diff --git a/.cocos-project.json b/.cocos-project.json new file mode 100644 index 0000000..18b6879 --- /dev/null +++ b/.cocos-project.json @@ -0,0 +1,5 @@ +{ + "engine_version": "cocos2d-x-3.17.2", + "has_native": true, + "project_type": "js" +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..70e19e6 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,154 @@ +#/**************************************************************************** +# Copyright (c) 2013 cocos2d-x.org +# Copyright (c) 2014 martell malone +# Copyright (c) 2015-2017 Chukong Technologies Inc. +# +# http://www.cocos2d-x.org +# +# 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. +# ****************************************************************************/ + +cmake_minimum_required(VERSION 3.6) + +set(APP_NAME Fbird) + +project(${APP_NAME}) + +set(RUNTIME_SRC_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/frameworks/runtime-src) +set(COCOS2DX_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/frameworks/cocos2d-x) +set(CMAKE_MODULE_PATH ${COCOS2DX_ROOT_PATH}/cmake/Modules/) + +include(CocosBuildSet) +set(BUILD_JS_LIBS ON) +add_subdirectory(${COCOS2DX_ROOT_PATH}/cocos ${ENGINE_BINARY_PATH}/cocos/core) + +# script and source files info, not need to compile +set(res_main_files + "${CMAKE_CURRENT_SOURCE_DIR}/main.js" + "${CMAKE_CURRENT_SOURCE_DIR}/project.json" + ) +set(res_res_folders + "${CMAKE_CURRENT_SOURCE_DIR}/res" + ) +set(res_src_folders + "${CMAKE_CURRENT_SOURCE_DIR}/src" + ) +set(res_script_folders + "${COCOS2DX_ROOT_PATH}/cocos/scripting/js-bindings/script" + ) +if(APPLE OR VS) + cocos_mark_multi_resources(res_main RES_TO "Resources" FILES ${res_main_files}) + cocos_mark_multi_resources(res_res RES_TO "Resources/res" FOLDERS ${res_res_folders}) + cocos_mark_multi_resources(res_src RES_TO "Resources/src" FOLDERS ${res_src_folders}) + cocos_mark_multi_resources(res_script RES_TO "Resources/script" FOLDERS ${res_script_folders}) + set(cc_common_res ${res_main} ${res_res} ${res_src} ${res_script}) +endif() + +# record sources, headers +set(GAME_SOURCE ${RUNTIME_SRC_ROOT}/Classes/AppDelegate.cpp) +set(GAME_HEADER ${RUNTIME_SRC_ROOT}/Classes/AppDelegate.h) + +if(ANDROID) + # change APP_NAME to the share library name for Android, it's value depend on AndroidManifest.xml + set(APP_NAME cocos2djs) + list(APPEND GAME_SOURCE ${RUNTIME_SRC_ROOT}/proj.android/app/jni/hellojavascript/main.cpp) +elseif(LINUX) + list(APPEND GAME_SOURCE ${RUNTIME_SRC_ROOT}/proj.linux/main.cpp) +elseif(WINDOWS) + set(WINDOWS_SRC ${RUNTIME_SRC_ROOT}/proj.win32/main.cpp) + list(APPEND WINDOWS_SRC ${RUNTIME_SRC_ROOT}/proj.win32/game.rc) + list(APPEND GAME_HEADER + ${RUNTIME_SRC_ROOT}/proj.win32/main.h + ${RUNTIME_SRC_ROOT}/proj.win32/resource.h + ) + list(APPEND GAME_SOURCE ${WINDOWS_SRC} ${cc_common_res}) +elseif(APPLE) + if(IOS) + list(APPEND GAME_HEADER + ${RUNTIME_SRC_ROOT}/proj.ios_mac/ios/AppController.h + ${RUNTIME_SRC_ROOT}/proj.ios_mac/ios/RootViewController.h + ) + set(APP_UI_RES + ${RUNTIME_SRC_ROOT}/proj.ios_mac/ios/LaunchScreen.storyboard + ${RUNTIME_SRC_ROOT}/proj.ios_mac/ios/LaunchScreenBackground.png + ${RUNTIME_SRC_ROOT}/proj.ios_mac/ios/Images.xcassets + ) + list(APPEND GAME_SOURCE + ${RUNTIME_SRC_ROOT}/proj.ios_mac/ios/main.m + ${RUNTIME_SRC_ROOT}/proj.ios_mac/ios/AppController.mm + ${RUNTIME_SRC_ROOT}/proj.ios_mac/ios/RootViewController.mm + ${RUNTIME_SRC_ROOT}/proj.ios_mac/ios/Prefix.pch + ${APP_UI_RES} + ) + elseif(MACOSX) + set(APP_UI_RES + ${RUNTIME_SRC_ROOT}/proj.ios_mac/mac/Icon.icns + ${RUNTIME_SRC_ROOT}/proj.ios_mac/mac/Info.plist + ) + list(APPEND GAME_SOURCE + ${RUNTIME_SRC_ROOT}/proj.ios_mac/mac/main.cpp + ${RUNTIME_SRC_ROOT}/proj.ios_mac/mac/Prefix.pch + ${APP_UI_RES} + ) + endif() + list(APPEND GAME_SOURCE ${cc_common_res}) +endif() + +set(APP_SRC ${GAME_HEADER} ${GAME_SOURCE}) + +# mark app complie info and libs info +if(NOT ANDROID) + add_executable(${APP_NAME} ${APP_SRC}) +else() + add_library(${APP_NAME} SHARED ${APP_SRC}) + add_subdirectory(${COCOS2DX_ROOT_PATH}/cocos/platform/android ${ENGINE_BINARY_PATH}/cocos/platform) + target_link_libraries(${APP_NAME} -Wl,--whole-archive cpp_android_spec -Wl,--no-whole-archive) + add_subdirectory(${COCOS2DX_ROOT_PATH}/cocos/scripting/js-bindings/proj.android ${ENGINE_BINARY_PATH}/cocos/js-android) + target_link_libraries(${APP_NAME} -Wl,--whole-archive js_android_spec -Wl,--no-whole-archive) +endif() + +target_link_libraries(${APP_NAME} jscocos2d) +target_include_directories(${APP_NAME} PRIVATE ${RUNTIME_SRC_ROOT}/Classes) + +# mark app resources, resource will be copy auto after mark +setup_cocos_app_config(${APP_NAME}) +if(APPLE) + set_target_properties(${APP_NAME} PROPERTIES RESOURCE "${APP_UI_RES}") + if(MACOSX) + set_target_properties(${APP_NAME} PROPERTIES + MACOSX_BUNDLE_INFO_PLIST "${RUNTIME_SRC_ROOT}/proj.ios_mac/mac/Info.plist" + ) + elseif(IOS) + cocos_pak_xcode(${APP_NAME} INFO_PLIST "iOSBundleInfo.plist.in") + set_xcode_property(${APP_NAME} ASSETCATALOG_COMPILER_APPICON_NAME "AppIcon") + set_xcode_property(${APP_NAME} DEVELOPMENT_TEAM "") + set_xcode_property(${APP_NAME} CODE_SIGN_IDENTITY "iPhone Developer") + endif() +elseif(WINDOWS) + cocos_copy_target_dll(${APP_NAME}) +endif() +# copy resource on linux or WINDOWS +if(LINUX OR WINDOWS) + set(APP_RES_DIR "$/Resources") + cocos_copy_target_res(${APP_NAME} COPY_TO ${APP_RES_DIR} FILES ${res_main_files}) + cocos_copy_target_res(${APP_NAME} COPY_TO ${APP_RES_DIR}/res FOLDERS ${res_res_folders}) + cocos_copy_target_res(${APP_NAME} COPY_TO ${APP_RES_DIR}/src FOLDERS ${res_src_folders}) + cocos_copy_target_res(${APP_NAME} COPY_TO ${APP_RES_DIR}/script FOLDERS ${res_script_folders}) +endif() + diff --git a/frameworks/cocos2d-html5/.gitignore b/frameworks/cocos2d-html5/.gitignore new file mode 100644 index 0000000..73b9c1a --- /dev/null +++ b/frameworks/cocos2d-html5/.gitignore @@ -0,0 +1,10 @@ +lib +/web.config +.idea +build +aspnet_client +node_modules +/tools/jsdoc_toolkit-2.4.0 +/package +/tools/jsdoc_toolkit/jsdoc_toolkit-2.4.0 +/.project diff --git a/frameworks/cocos2d-html5/Base64Images.js b/frameworks/cocos2d-html5/Base64Images.js new file mode 100644 index 0000000..0d98350 --- /dev/null +++ b/frameworks/cocos2d-html5/Base64Images.js @@ -0,0 +1,32 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +var cc = cc || {}; + +cc._loadingImage = "data:image/gif;base64,R0lGODlhEAAQALMNAD8/P7+/vyoqKlVVVX9/fxUVFUBAQGBgYMDAwC8vL5CQkP///wAAAP///wAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFAAANACwAAAAAEAAQAAAEO5DJSau9OOvNex0IMnDIsiCkiW6g6BmKYlBFkhSUEgQKlQCARG6nEBwOgl+QApMdCIRD7YZ5RjlGpCUCACH5BAUAAA0ALAAAAgAOAA4AAAQ6kLGB0JA4M7QW0hrngRllkYyhKAYqKUGguAws0ypLS8JxCLQDgXAIDg+FRKIA6v0SAECCBpXSkstMBAAh+QQFAAANACwAAAAACgAQAAAEOJDJORAac6K1kDSKYmydpASBUl0mqmRfaGTCcQgwcxDEke+9XO2WkxQSiUIuAQAkls0n7JgsWq8RACH5BAUAAA0ALAAAAAAOAA4AAAQ6kMlplDIzTxWC0oxwHALnDQgySAdBHNWFLAvCukc215JIZihVIZEogDIJACBxnCSXTcmwGK1ar1hrBAAh+QQFAAANACwAAAAAEAAKAAAEN5DJKc4RM+tDyNFTkSQF5xmKYmQJACTVpQSBwrpJNteZSGYoFWjIGCAQA2IGsVgglBOmEyoxIiMAIfkEBQAADQAsAgAAAA4ADgAABDmQSVZSKjPPBEDSGucJxyGA1XUQxAFma/tOpDlnhqIYN6MEAUXvF+zldrMBAjHoIRYLhBMqvSmZkggAIfkEBQAADQAsBgAAAAoAEAAABDeQyUmrnSWlYhMASfeFVbZdjHAcgnUQxOHCcqWylKEohqUEAYVkgEAMfkEJYrFA6HhKJsJCNFoiACH5BAUAAA0ALAIAAgAOAA4AAAQ3kMlJq704611SKloCAEk4lln3DQgyUMJxCBKyLAh1EMRR3wiDQmHY9SQslyIQUMRmlmVTIyRaIgA7"; + +cc._fpsImage = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAgCAYAAAD9qabkAAAKQ2lDQ1BJQ0MgcHJvZmlsZQAAeNqdU3dYk/cWPt/3ZQ9WQtjwsZdsgQAiI6wIyBBZohCSAGGEEBJAxYWIClYUFRGcSFXEgtUKSJ2I4qAouGdBiohai1VcOO4f3Ke1fXrv7e371/u855zn/M55zw+AERImkeaiagA5UoU8Otgfj09IxMm9gAIVSOAEIBDmy8JnBcUAAPADeXh+dLA//AGvbwACAHDVLiQSx+H/g7pQJlcAIJEA4CIS5wsBkFIAyC5UyBQAyBgAsFOzZAoAlAAAbHl8QiIAqg0A7PRJPgUA2KmT3BcA2KIcqQgAjQEAmShHJAJAuwBgVYFSLALAwgCgrEAiLgTArgGAWbYyRwKAvQUAdo5YkA9AYACAmUIszAAgOAIAQx4TzQMgTAOgMNK/4KlfcIW4SAEAwMuVzZdL0jMUuJXQGnfy8ODiIeLCbLFCYRcpEGYJ5CKcl5sjE0jnA0zODAAAGvnRwf44P5Dn5uTh5mbnbO/0xaL+a/BvIj4h8d/+vIwCBAAQTs/v2l/l5dYDcMcBsHW/a6lbANpWAGjf+V0z2wmgWgrQevmLeTj8QB6eoVDIPB0cCgsL7SViob0w44s+/zPhb+CLfvb8QB7+23rwAHGaQJmtwKOD/XFhbnauUo7nywRCMW735yP+x4V//Y4p0eI0sVwsFYrxWIm4UCJNx3m5UpFEIcmV4hLpfzLxH5b9CZN3DQCshk/ATrYHtctswH7uAQKLDljSdgBAfvMtjBoLkQAQZzQyefcAAJO/+Y9AKwEAzZek4wAAvOgYXKiUF0zGCAAARKCBKrBBBwzBFKzADpzBHbzAFwJhBkRADCTAPBBCBuSAHAqhGJZBGVTAOtgEtbADGqARmuEQtMExOA3n4BJcgetwFwZgGJ7CGLyGCQRByAgTYSE6iBFijtgizggXmY4EImFINJKApCDpiBRRIsXIcqQCqUJqkV1II/ItchQ5jVxA+pDbyCAyivyKvEcxlIGyUQPUAnVAuagfGorGoHPRdDQPXYCWomvRGrQePYC2oqfRS+h1dAB9io5jgNExDmaM2WFcjIdFYIlYGibHFmPlWDVWjzVjHVg3dhUbwJ5h7wgkAouAE+wIXoQQwmyCkJBHWExYQ6gl7CO0EroIVwmDhDHCJyKTqE+0JXoS+cR4YjqxkFhGrCbuIR4hniVeJw4TX5NIJA7JkuROCiElkDJJC0lrSNtILaRTpD7SEGmcTCbrkG3J3uQIsoCsIJeRt5APkE+S+8nD5LcUOsWI4kwJoiRSpJQSSjVlP+UEpZ8yQpmgqlHNqZ7UCKqIOp9aSW2gdlAvU4epEzR1miXNmxZDy6Qto9XQmmlnafdoL+l0ugndgx5Fl9CX0mvoB+nn6YP0dwwNhg2Dx0hiKBlrGXsZpxi3GS+ZTKYF05eZyFQw1zIbmWeYD5hvVVgq9ip8FZHKEpU6lVaVfpXnqlRVc1U/1XmqC1SrVQ+rXlZ9pkZVs1DjqQnUFqvVqR1Vu6k2rs5Sd1KPUM9RX6O+X/2C+mMNsoaFRqCGSKNUY7fGGY0hFsYyZfFYQtZyVgPrLGuYTWJbsvnsTHYF+xt2L3tMU0NzqmasZpFmneZxzQEOxrHg8DnZnErOIc4NznstAy0/LbHWaq1mrX6tN9p62r7aYu1y7Rbt69rvdXCdQJ0snfU6bTr3dQm6NrpRuoW623XP6j7TY+t56Qn1yvUO6d3RR/Vt9KP1F+rv1u/RHzcwNAg2kBlsMThj8MyQY+hrmGm40fCE4agRy2i6kcRoo9FJoye4Ju6HZ+M1eBc+ZqxvHGKsNN5l3Gs8YWJpMtukxKTF5L4pzZRrmma60bTTdMzMyCzcrNisyeyOOdWca55hvtm82/yNhaVFnMVKizaLx5balnzLBZZNlvesmFY+VnlW9VbXrEnWXOss623WV2xQG1ebDJs6m8u2qK2brcR2m23fFOIUjynSKfVTbtox7PzsCuya7AbtOfZh9iX2bfbPHcwcEh3WO3Q7fHJ0dcx2bHC866ThNMOpxKnD6VdnG2ehc53zNRemS5DLEpd2lxdTbaeKp26fesuV5RruutK10/Wjm7ub3K3ZbdTdzD3Ffav7TS6bG8ldwz3vQfTw91jicczjnaebp8LzkOcvXnZeWV77vR5Ps5wmntYwbcjbxFvgvct7YDo+PWX6zukDPsY+Ap96n4e+pr4i3z2+I37Wfpl+B/ye+zv6y/2P+L/hefIW8U4FYAHBAeUBvYEagbMDawMfBJkEpQc1BY0FuwYvDD4VQgwJDVkfcpNvwBfyG/ljM9xnLJrRFcoInRVaG/owzCZMHtYRjobPCN8Qfm+m+UzpzLYIiOBHbIi4H2kZmRf5fRQpKjKqLupRtFN0cXT3LNas5Fn7Z72O8Y+pjLk722q2cnZnrGpsUmxj7Ju4gLiquIF4h/hF8ZcSdBMkCe2J5MTYxD2J43MC52yaM5zkmlSWdGOu5dyiuRfm6c7Lnnc8WTVZkHw4hZgSl7I/5YMgQlAvGE/lp25NHRPyhJuFT0W+oo2iUbG3uEo8kuadVpX2ON07fUP6aIZPRnXGMwlPUit5kRmSuSPzTVZE1t6sz9lx2S05lJyUnKNSDWmWtCvXMLcot09mKyuTDeR55m3KG5OHyvfkI/lz89sVbIVM0aO0Uq5QDhZML6greFsYW3i4SL1IWtQz32b+6vkjC4IWfL2QsFC4sLPYuHhZ8eAiv0W7FiOLUxd3LjFdUrpkeGnw0n3LaMuylv1Q4lhSVfJqedzyjlKD0qWlQyuCVzSVqZTJy26u9Fq5YxVhlWRV72qX1VtWfyoXlV+scKyorviwRrjm4ldOX9V89Xlt2treSrfK7etI66Trbqz3Wb+vSr1qQdXQhvANrRvxjeUbX21K3nShemr1js20zcrNAzVhNe1bzLas2/KhNqP2ep1/XctW/a2rt77ZJtrWv913e/MOgx0VO97vlOy8tSt4V2u9RX31btLugt2PGmIbur/mft24R3dPxZ6Pe6V7B/ZF7+tqdG9s3K+/v7IJbVI2jR5IOnDlm4Bv2pvtmne1cFoqDsJB5cEn36Z8e+NQ6KHOw9zDzd+Zf7f1COtIeSvSOr91rC2jbaA9ob3v6IyjnR1eHUe+t/9+7zHjY3XHNY9XnqCdKD3x+eSCk+OnZKeenU4/PdSZ3Hn3TPyZa11RXb1nQ8+ePxd07ky3X/fJ897nj13wvHD0Ivdi2yW3S609rj1HfnD94UivW2/rZffL7Vc8rnT0Tes70e/Tf/pqwNVz1/jXLl2feb3vxuwbt24m3Ry4Jbr1+Hb27Rd3Cu5M3F16j3iv/L7a/eoH+g/qf7T+sWXAbeD4YMBgz8NZD+8OCYee/pT/04fh0kfMR9UjRiONj50fHxsNGr3yZM6T4aeypxPPyn5W/3nrc6vn3/3i+0vPWPzY8Av5i8+/rnmp83Lvq6mvOscjxx+8znk98ab8rc7bfe+477rfx70fmSj8QP5Q89H6Y8en0E/3Pud8/vwv94Tz+4A5JREAAAAGYktHRAD/AP8A/6C9p5MAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfcAgcQLxxUBNp/AAAQZ0lEQVR42u2be3QVVZbGv1N17829eRLyIKAEOiISEtPhJTJAYuyBDmhWjAEx4iAGBhxA4wABbVAMWUAeykMCM+HRTcBRWkNH2l5moS0LCCrQTkYeQWBQSCAIgYRXEpKbW/XNH5zS4noR7faPEeu31l0h4dSpvc+t/Z199jkFWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhY/H9D/MR9qfKnLj/00U71aqfJn9+HCkCR/Wk36ddsgyJ/1wF4fkDfqqm9/gPsUeTnVr6a2xlQfnxdI7zs0W7irzD17Ytb2WT7EeNv/r4ox1O3Quf2QP2pgt9utwfout4FQE8AVBSlnaRmfvAURQkg2RlAbwB9AThlW5L0GaiKojhJhgOIBqDa7XaPrusdPtr5kQwF0BVAAoBIABRCKDd5aFUhRDAAw57eAOwAhKIoupft3zoqhB1AqLwuHIBut9uFt02qqvqRDJR2dAEQJj/BAOjn56dqmma+xiaECAEQAWAggLsB6A6HQ2iaZggBhBAqgEAAnQB0kzaEmT4hAITT6VQ8Ho/HJAKKECJQtr8LwD1y/A1/vcdfEUIEyfZ9AcQbYvZ942Px88L2UwlJR0dH0EMPPbRj5syZPUeNGrXR7Xb/641xIwJ1XY9NSUlZm52dfW+XLl1w8uRJzJ8//+OGhoYJqqqe1TSt1Wsm9NN1PSIqKmr12rVrR5WUlHy1bdu2AQCumWc3IYRD1/UwVVXnFRQUTIuNjUVzczN2797dWFJSkq8oymZd15sAGAEnFEUJ1nX9nzIzM1dnZmZGh4SE4OTJk5g5c+Zf29vbp9pstrMej6fVOyhIhgAYU1hY+B+hoaGoqKg4XVlZea+XTULTNFdCQsLGiRMnPuR2u3UhBOV9eeDAAWXTpk095DUe6WsoyRE5OTlr0tLSAux2O/bs2cO5c+e+pijKUpIXSHaQVAGkvPLKK++6XK4OksJLCFlXV2cvKSlJBFAjhU+x2WwhHo9nUHp6+urMzMy7wsLCUF9fjxdffPHjxsbGiTab7WuPx9NiEutOuq4PyMjI+M+srKyYqKgoHD58GDNmzNjq8XhyVFU9b/q+LH7hBAEYu3PnTlZVVRFAGgCX6f/tAHoOHDjwa0p27txp/JO9e/f+QM7cipw9nfL3kQBKt2zZQpJ87rnn6mQmoHilw2EACs+cOUOSrK+vZ1NTE0nyo48+IoBpxswoBcMJ4Ndjx471kOTFixe5d+9ekqTH42H//v13A4jyzpAURfEH0H/OnDnthu1z5sw558MmFUCPWbNmnaMP3nrrLZoyDmP8Hl68eDFJ8siRI9/Yc+zYMQKYKdtAztrTrl27xptRXV1NAKMAOAyBBBA/Y8aMdpLs6Ojgxx9//E37+++//29yvFXppwvAwMcee8xjtDHsuXLlCqOjo//ia3wsfpkoALqFhoZuIckJEyackimm3dQmEMDUmpoakmRISMhhAHOHDx/eQJIbN24kgKEyMAHAFRMTs2XXrl1saWkhSZ0kp0+ffhrAr3wEW/S8efOukORLL72kA1gKYMPWrVtJkk899dRJAHeYrgsEsIQkjx8/TgDvAPjd448/3kaSb7zxBmUa7vC6z53BwcFbSHL9+vU6Sc6aNes8gF5ewWAH0PfVV18lSQL4DMBGIcQ6AKtcLleBFC2jXtFt8ODBe0iyoqKCAJYByC8qKmJDQwOzsrK+MAmqo1OnTveHhoa+GRkZ+XZkZOSWiIiIvzgcjk9mzpypkWRmZuZpmbYbGV4AgPnNzc1sa2sjgN0A5iQmJtaSZHl5OQHcb/K3s81mW0uSTU1NBFAFYFbfvn1Pk+Tbb79NAA8IIVzW42/hByA+Pz/fLR/2ZXIda05NI/z9/TeR5J49ewhgqlxTrtI0jY2NjQQw3zTLuWJiYjaUlJToS5Ys6fjkk080kwDEeAmADcA9GzZsIElGRUW9CyAWwLApU6Y0kOSKFSsog9QICGdERMTGsrIyZmVlEcC9AB4IDw/fTpLbtm0jgN94CUAnAJmVlZVcs2aNZ/LkyRdJcvbs2b4EwAkgZfPmzTxw4AABFAN4BkC6vFeUSewcAO5duXIlSTIhIaEawGMAxgKYAmAGgCS73e5vrKVk/yGythANYEhCQsIhkly+fDkBpKqqGmL6DgIALDKN/3yZpVWQZGVlJQE8aPI3KiMjo5okV61aRQAjAPQBMPfIkSN0u90EUCBtsPiFEwpgbn19PdetW2fM5N4zQ9ekpKQqkty0aRMBpMjiWM6JEydIkoqirJUFJ6iq6pAPVy8A6cZMehMBUACEuVyuFwG8HBwcPEIWx367ZMkSjSQXLVrUJouTRorrkAHdA8BdQogsAOsKCwtJkmPGjDkvMw2bDDo/ADEjRoz4XylyFbm5uY0mAbjLyyZ/AOOrq6tZVlbWsWDBgo69e/eyoqKCgwcPPg4gSQaoIRbp27dvN7KF+tLSUr28vJwFBQXtMpvpYRIM7+wrAkDeqVOnePbsWQIoNKfzpiXPg8uXLydJJicnNwF4f+nSpW6STEtLq5fjYwhk1wkTJtSQ5Ouvv04AqTKj+N2xY8dIkgEBAW/Ie1v8wncRegwZMmQvSfbr12+3Ua33WqPfOWbMmP0kWVpaSgCDZAqcfejQIWNZsEGKgvnh9gfQb9myZd8nAEJVVZtMkUNk8CcNHTq0liR1XWdYWNhmH1mJIme80OnTp18x1rp5eXkEsNJms92Fb7e/IgEsvHz5Mp999tkmAI/l5uZeMC0B7vEqqAYAyL106RJJsra2lpWVld+sucePH38ZQG+5NncBeOrgwYMkqbe3t/Po0aOsra011wAWyl0H7x0JJ4DE+fPnu0kyPT29DsDdUrBuyNKEEAkAdpw/f/6GeoEM8GUmfwEgPCIiopwkGxsbabPZPgOw6L777vvm4p49e26VGYjFLxUhhD+ApLKyMp44ccIoVnXybgbgzkcfffRzklyzZg0BDJYCMMmoCwQFBXkLgLGWvvcWAgBToSsKwNPTp09vMR7UuLi4rwH0lgU8c/Db5ezbeeTIkRWzZ8++aMxu+fn5BPCADBwHgP4LFy701NXVEUAJgAnPP/98kyxMNgHo53A4zH77BQQETMvPz7+Um5vbBuAlAFMSExPPmdbVL0qh8Acw8fDhw5SCchVAEYAVb775JknyhRdeaJYztHfxMwLAaqNwCGC2FArv8x0hAHKNLGPKlCme5OTk/Zs3bzb7O0wKiiG8KXl5ed8IxenTp0mSR48e1UmyW7duWywBuD2xyQcgFECgoih+8H1gyJgZV5Lkyy+/3CbTRIePtl2HDBmyw1QBHyGDdXZdXR1JUghRKkXBjOMHCoBdpr0L3nvvPZLkF198wejo6O0A4lVVDTb74HQ6AwD8Wq7Jh8rgGgDgQ13XjVR8qaxJuADMbmlpYXl5uV5UVNRWUFDgfv/993Vj/ZydnU1c37eHXML4S3viAcQqitJD2l104cIFY8lTKsXSBWBMVVWVcd9yed2A1NTUQ6Zl00CvLMMOoHdubm6zFIlWOf5+PsY/Kj09vdrU11QAwwGsv3jxIk21m2DZr10I0RXAuAcffPBgaWkpV69eTYfDcdiwUxY0w6xw+flX8L1xApjevXv3lREREaW6rofB93aPDUDQpEmTMgHgtddeqwBwEd/utZvpqK6uPgEAcXFxkA94NwB9unfvjrNnz4LklwDcf08iIqv66Zs2bXrl4YcfxooVKxAbG7uqrq5uAYA2TdOEqqpGYIi2tjbl6aeffu/YsWPv5uTk7JaC1wHg4Pnz542MwoVvTx+21dbWYvjw4WLixIl+2dnZ9lGjRgmSTE1NRUpKCkwFTGiaxtTU1OXTpk3707Bhw/6g67pDipnT4biuj7qut+Lbk3Vf1tTUXI9qu91Pjq1QFEUBgJaWFgBo8yGOQ8eNGxcAAOvXr/8QwBUfYygAKL169eoCABcuXACAWtn2hOGv0+kMNO1KiPDw8F4A4rZv3/7R1KlTR0+bNu1ht9u9r1+/fqitrQXJgwDarRC6/QjPzs4+QJIffPCB9/aQmSAA43ft2mW0e1QGoi8CAPyLsZccExNTC2BlRkbGRdOyYJCP2csBIN6UAZzCd7cBbQCijYp/dXU1ExMTz6SmptaMHj36f9LS0vYlJCRsl6mxIWSdu3fv/g5J7t+/nwC2AShMTk6+SJKff/45AWRLYbD7+fndAeDf5BJnLoCCyZMnt5JkdnZ2C4B/F0KEm1Pu+Pj4rST55ZdfEsBWAK+mpaVdMo3raDn7KwDuSEpK+m+S3LBhAwG8DuCtHTt2UBbpjgC408vvcFVV15HkuXPnjMp+p5uMf0RcXNyHJNnQ0EBVVfcCWBQXF3fG+Jv0yxABPwB5LS0tRmFxN4BlTzzxxGWSXLx4sS5F3GGFy+1Hp5SUlJq6ujoWFxdTpsZ2H+0iIyMj/0iSWVlZX5mr5jfJFroPGzasxlhTnjp1iiTZ3NxMl8tlrCd9pfa9SkpKSJI5OTmnZOageLUZZqxvfVFWVkZcPwdgNwnSCKPqb17jkmR8fPzfZMDZ5CRsFBmNI7h95s2b1yhT7/MAYmStwCx4vy0uLqa3v5qmEcCfvSr1QQAeXb16NY3Cm3HQ55133iGAp+SxZTNhKSkpfzUddkrFjYevzAQCeGjp0qXfsYckY2NjTwD4leGDLCL2HTdunNtoY+zWSHFcIHdsFCtcfuZ1vO9Eqs3m7/F47sb1k2qX/f3997W2tl7BjWfpBYDOzzzzzIVJkyZh0KBBCwEsB3AJvl9AETabLcDj8dwRFRW1ctasWb8JCgpSzp07d62wsPC/Wltb8xRFadR1/ZqPXYbgAQMGbI2Pjw/+6quv9ldVVT0r01ezuPRJSUn5Y9euXXVd11WzDaqq6kePHm3+7LPPRgO4KlNuxWazhXo8nuTk5OSXMjIyEl0uFxoaGtqKior+dPXq1VdUVT0jj7r68ieoT58+vx8yZMjdx48fP1JVVTVF9m20VW02WyfZf97YsWPjXS4X6urqWvPy8jYCWCyEuEDS8FdVFKWzruv//OSTTy5OTk7uqWkaPv3007qysrJ8RVH+LI8ym8/rB3Tu3HnRI488knLo0KG2ffv2ZQI4C98vP6mqqoZqmpaclpa2cOTIkX39/f3R0NDQUVxc/G5TU9PLqqrWa5rWLH1QVFUN0TStX1JSUvH48eP7BwYG4uDBg1cKCgpeBbBe2u+2Qug2EwD5N5sMPuNtMe8XP4TT6Qxoa2sbIGeXvUKIK7d4IISiKC5d1wPljOfA9bPwzYqiXNV13dd6Uqiq6qdpml2mpe02m63d4/G4vcTF5fF47LJf71nJA6BZVVW3pmntuPHlmAD5wk6Q9NnbHp9vHaqq6tA0zU/64PZhk1FfCZB9G/23ALiqKEqzD39tpvbGUqoFwFUhRLP3yzpCCDtJpxyXDulfG27+pqRR3DXsUWVd4Yq0x/taVQjhIhksC8L+ABpM9ljBf5sKwI8pIBr75L5E4vvu+UNeG/a+hv+AL7yFH8qPtOfHjtOP6V/Bja8D6z/B2Nys/1u9Xv33tLf4GfF/LC4GCJwByWIAAAAASUVORK5CYII="; + +cc._loaderImage = "data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAAlAAD/4QMpaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjAtYzA2MCA2MS4xMzQ3NzcsIDIwMTAvMDIvMTItMTc6MzI6MDAgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjM4MDBEMDY2QTU1MjExRTFBQTAzQjEzMUNFNzMxRkQwIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjM4MDBEMDY1QTU1MjExRTFBQTAzQjEzMUNFNzMxRkQwIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzUgV2luZG93cyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkU2RTk0OEM4OERCNDExRTE5NEUyRkE3M0M3QkE1NTlEIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkU2RTk0OEM5OERCNDExRTE5NEUyRkE3M0M3QkE1NTlEIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+/+4ADkFkb2JlAGTAAAAAAf/bAIQADQkJCQoJDQoKDRMMCwwTFhENDREWGhUVFhUVGhkUFhUVFhQZGR0fIB8dGScnKionJzk4ODg5QEBAQEBAQEBAQAEODAwOEA4RDw8RFA4RDhQVERISERUfFRUXFRUfKB0ZGRkZHSgjJiAgICYjLCwoKCwsNzc1NzdAQEBAQEBAQEBA/8AAEQgAyACgAwEiAAIRAQMRAf/EALAAAAEFAQEAAAAAAAAAAAAAAAQAAgMFBgcBAQEAAwEBAAAAAAAAAAAAAAAAAQMEAgUQAAIBAgIEBwoLBgQGAwAAAAECAwAEEQUhMRIGQVFxsTITFGGBwdEiQlKSMzWRoeFicqKyI1NzFYJjJDQWB9KjVCbxwkNkJWXik3QRAAIBAgMFBQcDBQEAAAAAAAABAhEDIRIEMUFRcTJhwVIUBZGhsSJyEzOB0ULhYpIjUxX/2gAMAwEAAhEDEQA/AMJSpUqAVKlXuFAeUq9wpUB5XuFe4V6ooDzZHDox0CnGMinzwl7Z8NajaHeoO3vmTBZBtp9YUIqTEV5ROxHKnWRnaU8VRMhFBUjpV7hSoSeUq9pUB5Sr2lhQHlKvcK8oBV7hSFSRrtaKAZs07YNPM1pG2xJIAw1jSeandry/8X4m8VCKkWwaWwam7Xl/4v1W8VLtmX/i/VbxUoKkWwakSM407tmX/i/VbxUmzGwjQsjdY41IARie/U0IbZO0kNtCXnOCkEBeFu4KI3Bs7DNb27ya+jDx3kJeEnpJJEcQVbWDsk17u5urd591ucZkWhym2Vnd9RkCDEpFxDRpbw0bunu5mlp2De2FMLYXOD2wB2xbOeraUcYGJ72mlSUiqzzdzMd3Z3mixltA2yzcK/NlHM1DQyRXce1HocdNOEfJXZ88y9ZojOqhiBszIRiHQ8Y4cK5TvHuzLljHNMqxNoDjLFraHHnjPxcNCGVbxEUzYNTx5jZSxhpW6qTzlwJ+DCvO2Zf+L9VvFSgqyHYNLYNTdssPxfibxUu15f8Ai/VPiqCakOwa82DU/a8v/F+JvFTDdWPBL8R8VKCvYRYV5UzoMAy6QdIIqI0B4KJtxiRQwou16QoGUkntH5Tz0RbZbmF2hktraSVBo2lUkY8tDye0flPPXTslVUyiyVRsjqUOA4yMT8dW2ram2m6UVTNq9S7EIyUVJydMTn/6DnP+im9Wl+g5z/opvVrpteEhQWY4AaSTwAVf5WPiZh/9S5/zj7zltzlmYWkfWXNvJDGTgGcYDHirR7i7mSbwXParsFMrgb7w6jKw/wCmnc9I14kF3vpvCljbMyWMOJL4aEiB8qU/ObUK7HYWVrl1pFZWiCOCBQqKOLjPGTrNZZqKbUXVHq2nNwTuJRk1VpbgXN8s7Rk5ym0UQQzhIG2NAjhxHWbI+gCBVjBBFbwxwQqEiiUJGg1BVGAFe7dV28WYLYZFmF2Th1UD7JGjymGyn1iK5OyzIBGB1HgrLZhamzumQAGJwSqnSCh1q3GOCodxt4cxurdcpzuN4cyhiWaF5Bg09udUmnWw1H/jV9nFuJ7Quo+8h8peThFA+047vduyMtk7fYqTl07YFdfUufMPzT5p71UdtlmYXaGS2t3mQHAsgxANdadYJopLe4QS2867EsZ4QfCNYrCFbjdDPmgkYyWFxgVf04ifJf6ScNdRUW1XBb6FU5TjF5EpSSrGu/s5lN+g5z/opvVpfoOc/wCim9WtdHnatvObJXDW7xLGhB8nrPaY9/HCr+tEdPCVaSeDoYLnqF63lzW4/PFSW3ecxbI84VSzWUwUaSdg0DXXK5nvAipnd6qgKvWnQO7pri9ZUEmm3Vl2j1kr8pRlFRyquBNZjGxQ/S56Y1S2fu9OVueon11Szahoou06QoQUXadIVCD2FJJ7R+U89dMydv8Axdn+TH9muZye0flPPXQstlK5Tbka1gUjlC1q0vVLkeb6r+O3Tx9xcY1nt8c0NrZCyiOE1108NYjGv1joo7Js1jzKyScYLIvkzL6LDwHXVJksH9Sb49dKNq0tj1jA6uriOCL+02FWX7iVtZX1/AzaHTyeoauKn2MX9W79zebiZCuR5MjSrhfXuEtwTrUeZH+yNfdrRNcxI6IzhXlJEak6WIGJ2Rw4ChWnChndtlVBLMdQA0k1gbXNMzzDfDLs6mjaPKppJbWwJ1bOwwxw43OnHh71YT3DpfWUJmFlb5jHHDdeXBHIsrRea5TSqvxqG04cNN62vetoCS4tre5mgnkGE9q+3DKOkuI2WX6LDQRRHWDh1UCtwj7QRg2wdl8Djgw1qe7XvW0BQ3kfZ7mSLgU+T9E6RVbnuVrnWVSWqj+Lt8ZbRuHEdKPkYVcZ2MJY5fSGyeVar45+rkWQHAqccalPE5km1htWK5nK4Wnt5FuUBUwOMG4nGkA/BXUrW4S6torlOjMgcd/xVn7rLo7zKs0uEjCNeSvdwoBhgsZxX1l2j36k3Lu+uyprdj5Vs5A+i/lD48a0aaVJOPi7jB6lbzWozpjB48pf1NDXNN4vfl7+Z4BXS65pvF78vfzPAK71XTHmZ/S/yT+jvJ7L3fHytz1E+upbL+Qj5W56jfXWRnsIYKLtekKEFGWvSFQgyjk9o/Keet3YthlMP/5x9msJJ7R+U89biyb/AMXEv7gD6tadL1T+kwepRrC39ZkLDMbiwMvUHRPG0bjlGg8ore/23sxBldxfMPLupNhT8yL/AORNZbdzJ484scytxgLqJY5LZj6Q2sV5G1Vud1mjjyG0ij0NEGSZToKyhjtqw4waztuiXA3qKTbSxltfGhbZlE95ZtZqxVbgiOZhrER9ph3Svk9+pJILZ4Y4DGBFCUMKjRsGPobPFhUfW0NJmljE2xJcIrcI2vFUEln1lRXd6lrazXT9GCNpD+yNqoI7mOVduNw6nzlOIoPOUa6yye1XXcbMR5GdQ3xY0BSbj31/FcTQZirJ+q431q7anbHCTZ72Bw7lbPrKBMcBWNNgbMBBh+bsjBdni0VJ1lARZs6yWiupxCuMDy6KpS2IwOo6DTr3Mre3e5tZZVUM4ZBjqOOJoWO4jkXajcOOMHGgDISvWIrdAkKR80+TzVl908bPPL3LzxOuHdifxVfiTAg92qI/w+/8gGgSyN/mR7XPVlp0lF/3L3mbVKtu5Hjbk/8AHE2Fc03i9+Xv5ngFdKNc13i9+Xv5ngFaNV0x5nn+l/kn9HeEWXu+PlbnqJ9dS2Xu9OVueon11kZ7CGCjLXpCgxRlr0hUIPYUcntH5Tz1s8vb+Bt1/dqPirGSe0flPPWusG/g4Py15q06XqlyMWvVYQ+ruI9xJOqzO9hOto/sP8tbGOFIrmWeM7IuMDMnAXXQJOUjQeOsJk0nY96ip0CYunrjaHx1t+srPJUbXBm2LrFPikwTOb+T+VhbZxGMrDXp83x1QSy2tucJpUjPETp+Cn5/ftaRvKvtp3Kx48HG3erHMzOxZiWZtLMdJNQSbbL71Vk6yynViOkqnEEfOWtPbXi3EQkGg6mXiNckjeSJxJGxR10qw0GtxuxmvbImD4CZMFlA4fRfv0BqesqqzTMZNMEDbIHtHH2QeCiZJSqMQdOGiue53mz3czQwsRbIcNHnkec3c4qAMuriz68gTIToxwOOnlp0MjxMJYW741Gs3RVldtbygE/dMcHX/moDaxTiWNZB53B3arb8/wC+4SOF4sf/AKxU9kcBsfOGHfoUHtG/RbzY5Die5HHhXdvavqiZ9Q8Jdlq4/gbKua7xe/L38zwCuhpf2Uk/Zo50kmwJKIdogDjw1VzzeL35e/meAVp1LTgqY4nn+mRauzqmqwrjzCLL3fHytz1E+upLL+Qj5W56jfXWRnroYKLtekKEFF2vSFQg9hSSe0flPPWosm/hIfoLzVl5PaPynnrRWb/w0X0F5q06XqlyM2sVYx5gmbFre/t71NY2T+0h8VbSO5SWNJUOKSAMp7jDGspmMPaLRlXS6eWve1/FRO7WYdbZm1Y/eW/R7qHxHRXGojlm3ulid6aVbaW+OALvgCLq2Hm9WxHKWqjhj6xsK1e8dm15l4niG1LZkswGsxtrPeOmsvayBJA1VItlWjptLuTdPMo7LtjRDq9naK4+WF9IrUW7BaHOljGqVHB7w2hzVoZt87d8vaNYSLl02CcRsDEbJbj71Uu7UBkvJ7/D7q2QoDxySaAO8MTXdxRVMpRp5XZOWdF/ms7R5XdyKfKWJsO/5PhrG5XlNxmEywW6bTnTxAAcJNbGSMXkM1pjgbiNo1PziPJ+Os7u7m/6ReM00ZOgxSpqYYHT3wRXMKN4ll9zUG4bQfNshu8sZVuEA2hirA4qe/VOwwrVbzbww5mI44UKRRYkbWG0S3JWctbd7u5WFfOOLHiUdJqmaipfLsIsObhWe001lMkMVvJNjhghIALMcBxCs7fxXQmkupx1bXDswGPlaTidVaEyKNXkoo4eBV+Sq7L7Vs9zcBgeyQ4GQ/MB1crmoim2orezqcowTuSeEY48jQ7oZX2PLzdyLhNd6RjrEY6I7+uspvH78vfzPAK6UAAAFGAGgAcArmu8Xvy9/M8ArTfio24RW5nnaG67uou3H/KPuqT2X8hHytz1G+upLL3enK3PUb66ys9RDBRdr0hQgou06QqEGUkntH5Tz1e238vF9BeaqKT2j8p56vbb+Xi+gvNWjTdUuRn1XTHmTh8KrJTJlt8t1CPIY44cGnpJVjTJYkmjaN9Ib4u7V923njTethRauZJV3PaW1rfLIiXEDYg6R4VYc9CXW7thfOZbKdbGZtLW8uPVY/u3GrkNUkM9zlcxUjbhfWOA90cRq4gv4LhdqN+VToNYWmnRm9NNVWNTyHc6VWBv8wt4YeHqm6xyPmroq1Z7WGFLSxTq7WLSuPSdjrkfumq5yHXDUeA92oO2SKpVumNAaoJLMXH3myp0rpJ4uKhc3tbDM5BMri1zAj79j7KTiY8TcdBpcsith0286o+sPCagEX9Pzg4zXUCp6QYse8oouCG3tk6m1BYv05W6T+IdyolxbHDAAa2OgDlNCz3ryN2WxBd5PJMg1t81eId2ukqnLlTBbfcuY+9uJLiRcvtPvHdsHK+cfRHcHDWsyawjyy0WBcDI3lTP6TeIcFV+S5OmXx9bJg1048o8Cj0V8Jq2DVu09nL80up7OxHi+oal3P8AXB/IsZS8T/YOV65zvCcc7vfzPAK3ivWCz445zeH954BXOr6I8yfSfyz+jvCLP3fHytz1G+upLP3fHytz1E+usbPaQ0UXadIUIKLtekKhB7Ckk9o/Keer22/l4/oLzVRSe0flPPV7b/y8X0F5q0abqlyM+q6Y8yQsBTDMor1o8aiaE1pbluMqS3sbLLHIhSRQyngqukhaJ9uBjo+H5aOa3ao2t34qouRlLajTalGP8v0IY8ylXQ+PKPFU/bYXOLPge6CKia0LaxTOxHu1Q7cuBd9yPEJ7TbjXKO8CajbMIF6CNIeNvJHjqIWJ7tSpYkalqVblwIdyG+RGXur0hXYJFxal+Dhq5y3slkv3Y2pD0pTr+QUClpJRUdo9XW4OLrTHtM16cZLLWkeC7y4jvlNEpcRtw1Ux27Ci448NZrTFy3nn3IQWxlgGrDZ3pza7/M8ArZo+ArF5171uvp+CqdV0R5l/psUrs2vB3hdl7vTlbnqJ9dS2Xu+PlbnqJ9dY2eshooq16QoQUXa9IVCD2FLJ7RuU89WNtmUSQqkgYMgw0accKrpPaPynnrZWG4Vi+VWmY5tnMWXG+XrIYnA0rhj0mdcTgdNdwnKDqjmduM1SRR/qlr8/4KX6pa8T/BVzDuLZXudRZblmbxXcPUNPc3KqCIwrbOzgrHEnHjoyD+3eSXkht7DeKG4umDGOJVUklfouThXfmbnZ7Cvy1vt9pmv1W1+d8FL9VteJvgq5yrcOGfLmzHN80iyyETPbptAEFo2ZG8pmUa1OFNn3Ky6W/sbDKM5hv5bx2WTZA+7RF2y52WOPJTzE+z2Dy1vt9pT/AKpacTerS/U7Tib1a04/t7kDXPY03jhN0W6sQ7K7W3q2dnrMccaDy/8At80kuZfqWYxWNtlcvUPPhiGYhWDeUy7IwYU8xPs9g8tb7faUn6pacTerTxm9oOBvVq3v9z927aynuId44LiWKNnjhAXF2UYhRg516qpsryjLr21665zFLSTaK9U2GOA87SwqY37knRU+BzOzags0s1Oyr+BKM6sxwP6tSDPLMen6vy0rvdm3Sxlu7K/S7WDDrFUDUTxgnTU826eXW7KlxmqQuwDBXUKcD+1Xee/wXuKX5XDGWLapSVcOyhEM/seJ/V+WnjeGx4pPV+Wkm6kKZlFay3Jlt7iFpYZY8ASVK6DjtDDA0f8A0Tl340/1f8Ndx8xJVWXB0KbktFFpNzdVXAC/qOwA0CQni2flrO3Vwbm5lnI2TKxbDirX/wBE5d+NcfV/wVR7xZPa5U9utvI8nWhmbbw0YEAYYAVxfhfy5rlKR4Fulu6X7mW1mzT8S4Yis/5CPlbnqJ9dSWfu9OVueon11mZvQ2i7XpChKKtekKhBlNJ7R+U89bDfGTb3a3ZX0Lcj6kdY+T2j8p560288m1kWQr6MJ+ylSAr+2cnV5renjs3H1loX+3j9XvbbtxLN9lqW4UnV5jdnjtXHxihtyZNjeSBu5J9k1BJe7xy7W5CJ/wCzuD/mTVTf2+fq97LJuLrPsNRueS7W6aJ/38x+vLVXuY+xvHaNxbf2GoCezf8A36j/APsSf8w1sLnqczTefJluYoLm5uo5F61sBshItP1cNFYe1f8A3ir/APfE/wCZUe9bB94r5jwuPsrQFhmG4l/Z2M17HdW90tuu3IkTHaCjWdIw0VVZdks9/C06yJFEp2dp+E1bbqybGTZ8vpQD7L1XRv8A7blT96Oda7tpNuuNE37Cq9KSisjyuUoxrStKllHbLlWTXsMs8chuSuwEPDqwoLe5y+YRE/gLzmqRekvKKtd4327yM/ulHxmrHJStySWVRyrjxKI2XC/CTlnlPPKTpTdFbP0L1bgrf5Lp0G3dPhQHwV0S1lzBsns3sESR8Crh9WAJGjSOKuU3E+zdZQ3oJh8IArdZXFDmOTpHa3i2+YrI2KtKy4ricBsBuHHgFXSo440+Wa2qqxjvM9uMoy+WvzWpLCWWWE28HxL6e43ojgkeSCBY1Ri5BGIUDT51cl3vm276BBqSEH4WbxV0tlkyXJcxTMb+OW6uY9mGHrCzDQwwAbTp2uKuTZ9N1uYsfRRR8WPhrm419mSSjRyiqxVK7y23B/ftuTm2oSdJyzNVw3BFn7vTlbnqF9dS2fu9OVueon11lZuQ2iLdsGFD05H2dNQGV0ntG5Tz1dWm9N1b2kVq8EVwsI2UaQaQOKhmitZGLOmk68DhSFvY+gfWNSAg7z3Qvo7yKCKIohiaNR5LKxx8qpxvjcqS0VpbxvwOAcRQPZ7D0G9Y0uz2HoH1jUCpLY7zXlpbm3eKO5QuzjrBqZji3x17PvNcyT288VvDBJbMWUovS2hslW7mFQ9nsPQPrGl2ew9A+saCod/WNxtbYsrfb17WBxx5ddD2281xC88klvDcSXEnWuzrqOGGC9zRUPZ7D0G9Y0uzWHoH1jQVCLreq6ntZbaO3it1mGy7RjTs1X2mYy20ZiCq8ZOODcdEdmsPQb1jS7PYegfWNdJuLqnQiSUlRqpFLmryxtH1Ma7Qw2gNNPOdSt0oI27p007s9h6B9Y0uz2HoH1jXX3Z+I4+1b8IJdX89xLHKQFMXQUahpxoiPN5P+onfU+A0/s9h6DesaXZ7D0D6xpG7OLbUtu0StW5JJx2bBsmbtiSiEk+cxoCWWSaVpZOk2vDVo0VYdnsPQb1jSNvZcCH1jSd2c+p1XAmFqEOmOPEfaH+BQd1ueo211IzrgFUYKNAAqI1WztCpUqVCRUqVKgFSpUqAVKlSoBUqVKgFSpUqAVKlSoBUqVKgFSpUqAVKlSoD/9k="; diff --git a/frameworks/cocos2d-html5/CCBoot.js b/frameworks/cocos2d-html5/CCBoot.js new file mode 100644 index 0000000..22c5049 --- /dev/null +++ b/frameworks/cocos2d-html5/CCBoot.js @@ -0,0 +1,2899 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2015 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The main namespace of Cocos2d-JS, all engine core classes, functions, properties and constants are defined in this namespace + * @namespace + * @name cc + */ +var cc = cc || {}; +cc._tmp = cc._tmp || {}; +cc._LogInfos = {}; + +var _p = window; +/** @expose */ +_p.gl; +/** @expose */ +_p.WebGLRenderingContext; +/** @expose */ +_p.DeviceOrientationEvent; +/** @expose */ +_p.DeviceMotionEvent; +/** @expose */ +_p.AudioContext; +if (!_p.AudioContext) { + /** @expose */ + _p.webkitAudioContext; +} +/** @expose */ +_p.mozAudioContext; +_p = Object.prototype; +/** @expose */ +_p._super; +/** @expose */ +_p.ctor; +_p = null; + +/** + * drawing primitive of game engine + * @type {cc.DrawingPrimitive} + */ +cc._drawingUtil = null; + +/** + * main Canvas 2D/3D Context of game engine + * @type {CanvasRenderingContext2D|WebGLRenderingContext} + */ +cc._renderContext = null; +cc._supportRender = false; + +/** + * Main canvas of game engine + * @type {HTMLCanvasElement} + */ +cc._canvas = null; + +/** + * The element contains the game canvas + * @type {HTMLDivElement} + */ +cc.container = null; +cc._gameDiv = null; + +window.ENABLE_IMAEG_POOL = true; + +/** + * Iterate over an object or an array, executing a function for each matched element. + * @param {object|array} obj + * @param {function} iterator + * @param {object} [context] + */ +cc.each = function (obj, iterator, context) { + if (!obj) + return; + if (obj instanceof Array) { + for (var i = 0, li = obj.length; i < li; i++) { + if (iterator.call(context, obj[i], i) === false) + return; + } + } else { + for (var key in obj) { + if (iterator.call(context, obj[key], key) === false) + return; + } + } +}; + +/** + * Copy all of the properties in source objects to target object and return the target object. + * @param {object} target + * @param {object} *sources + * @returns {object} + */ +cc.extend = function (target) { + var sources = arguments.length >= 2 ? Array.prototype.slice.call(arguments, 1) : []; + + cc.each(sources, function (src) { + for (var key in src) { + if (src.hasOwnProperty(key)) { + target[key] = src[key]; + } + } + }); + return target; +}; + +/** + * Another way to subclass: Using Google Closure. + * The following code was copied + pasted from goog.base / goog.inherits + * @function + * @param {Function} childCtor + * @param {Function} parentCtor + */ +cc.inherits = function (childCtor, parentCtor) { + function tempCtor() {} + tempCtor.prototype = parentCtor.prototype; + childCtor.superClass_ = parentCtor.prototype; + childCtor.prototype = new tempCtor(); + childCtor.prototype.constructor = childCtor; + + // Copy "static" method, but doesn't generate subclasses. + // for( var i in parentCtor ) { + // childCtor[ i ] = parentCtor[ i ]; + // } +}; + +/** + * Check the obj whether is function or not + * @param {*} obj + * @returns {boolean} + */ +cc.isFunction = function (obj) { + return typeof obj === 'function'; +}; + +/** + * Check the obj whether is number or not + * @param {*} obj + * @returns {boolean} + */ +cc.isNumber = function (obj) { + return typeof obj === 'number' || Object.prototype.toString.call(obj) === '[object Number]'; +}; + +/** + * Check the obj whether is string or not + * @param {*} obj + * @returns {boolean} + */ +cc.isString = function (obj) { + return typeof obj === 'string' || Object.prototype.toString.call(obj) === '[object String]'; +}; + +/** + * Check the obj whether is array or not + * @param {*} obj + * @returns {boolean} + */ +cc.isArray = function (obj) { + return Array.isArray(obj) || + (typeof obj === 'object' && Object.prototype.toString.call(obj) === '[object Array]'); +}; + +/** + * Check the obj whether is undefined or not + * @param {*} obj + * @returns {boolean} + */ +cc.isUndefined = function (obj) { + return typeof obj === 'undefined'; +}; + +/** + * Check the obj whether is object or not + * @param {*} obj + * @returns {boolean} + */ +cc.isObject = function (obj) { + return typeof obj === "object" && Object.prototype.toString.call(obj) === '[object Object]'; +}; + +/** + * Check the url whether cross origin + * @param {String} url + * @returns {boolean} + */ +cc.isCrossOrigin = function (url) { + if (!url) { + cc.log("invalid URL"); + return false; + } + var startIndex = url.indexOf("://"); + if (startIndex === -1) + return false; + + var endIndex = url.indexOf("/", startIndex + 3); + var urlOrigin = (endIndex === -1) ? url : url.substring(0, endIndex); + return urlOrigin !== location.origin; +}; + +//+++++++++++++++++++++++++something about async begin+++++++++++++++++++++++++++++++ +/** + * Async Pool class, a helper of cc.async + * @param {Object|Array} srcObj + * @param {Number} limit the limit of parallel number + * @param {function} iterator + * @param {function} onEnd + * @param {object} target + * @constructor + */ +cc.AsyncPool = function (srcObj, limit, iterator, onEnd, target) { + var self = this; + self._finished = false; + self._srcObj = srcObj; + self._limit = limit; + self._pool = []; + self._iterator = iterator; + self._iteratorTarget = target; + self._onEnd = onEnd; + self._onEndTarget = target; + self._results = srcObj instanceof Array ? [] : {}; + self._errors = srcObj instanceof Array ? [] : {}; + + cc.each(srcObj, function (value, index) { + self._pool.push({index: index, value: value}); + }); + + self.size = self._pool.length; + self.finishedSize = 0; + self._workingSize = 0; + + self._limit = self._limit || self.size; + + self.onIterator = function (iterator, target) { + self._iterator = iterator; + self._iteratorTarget = target; + }; + + self.onEnd = function (endCb, endCbTarget) { + self._onEnd = endCb; + self._onEndTarget = endCbTarget; + }; + + self._handleItem = function () { + var self = this; + if (self._pool.length === 0 || self._workingSize >= self._limit) + return; //return directly if the array's length = 0 or the working size great equal limit number + + var item = self._pool.shift(); + var value = item.value, index = item.index; + self._workingSize++; + self._iterator.call(self._iteratorTarget, value, index, + function (err, result) { + if (self._finished) { + return; + } + + if (err) { + self._errors[this.index] = err; + } + else { + self._results[this.index] = result; + } + + self.finishedSize++; + self._workingSize--; + if (self.finishedSize === self.size) { + var errors = self._errors.length === 0 ? null : self._errors; + self.onEnd(errors, self._results); + return; + } + self._handleItem(); + }.bind(item), + self); + }; + + self.flow = function () { + var self = this; + if (self._pool.length === 0) { + if (self._onEnd) + self._onEnd.call(self._onEndTarget, null, []); + return; + } + for (var i = 0; i < self._limit; i++) + self._handleItem(); + }; + + self.onEnd = function(errors, results) { + self._finished = true; + if (self._onEnd) { + var selector = self._onEnd; + var target = self._onEndTarget; + self._onEnd = null; + self._onEndTarget = null; + selector.call(target, errors, results); + } + }; +}; + +/** + * @class + */ +cc.async = /** @lends cc.async# */{ + /** + * Do tasks series. + * @param {Array|Object} tasks + * @param {function} [cb] callback + * @param {Object} [target] + * @return {cc.AsyncPool} + */ + series: function (tasks, cb, target) { + var asyncPool = new cc.AsyncPool(tasks, 1, function (func, index, cb1) { + func.call(target, cb1); + }, cb, target); + asyncPool.flow(); + return asyncPool; + }, + + /** + * Do tasks parallel. + * @param {Array|Object} tasks + * @param {function} cb callback + * @param {Object} [target] + * @return {cc.AsyncPool} + */ + parallel: function (tasks, cb, target) { + var asyncPool = new cc.AsyncPool(tasks, 0, function (func, index, cb1) { + func.call(target, cb1); + }, cb, target); + asyncPool.flow(); + return asyncPool; + }, + + /** + * Do tasks waterfall. + * @param {Array|Object} tasks + * @param {function} cb callback + * @param {Object} [target] + * @return {cc.AsyncPool} + */ + waterfall: function (tasks, cb, target) { + var args = []; + var lastResults = [null];//the array to store the last results + var asyncPool = new cc.AsyncPool(tasks, 1, + function (func, index, cb1) { + args.push(function (err) { + args = Array.prototype.slice.call(arguments, 1); + if (tasks.length - 1 === index) lastResults = lastResults.concat(args);//while the last task + cb1.apply(null, arguments); + }); + func.apply(target, args); + }, function (err) { + if (!cb) + return; + if (err) + return cb.call(target, err); + cb.apply(target, lastResults); + }); + asyncPool.flow(); + return asyncPool; + }, + + /** + * Do tasks by iterator. + * @param {Array|Object} tasks + * @param {function|Object} iterator + * @param {function} [callback] + * @param {Object} [target] + * @return {cc.AsyncPool} + */ + map: function (tasks, iterator, callback, target) { + var locIterator = iterator; + if (typeof(iterator) === "object") { + callback = iterator.cb; + target = iterator.iteratorTarget; + locIterator = iterator.iterator; + } + var asyncPool = new cc.AsyncPool(tasks, 0, locIterator, callback, target); + asyncPool.flow(); + return asyncPool; + }, + + /** + * Do tasks by iterator limit. + * @param {Array|Object} tasks + * @param {Number} limit + * @param {function} iterator + * @param {function} cb callback + * @param {Object} [target] + */ + mapLimit: function (tasks, limit, iterator, cb, target) { + var asyncPool = new cc.AsyncPool(tasks, limit, iterator, cb, target); + asyncPool.flow(); + return asyncPool; + } +}; +//+++++++++++++++++++++++++something about async end+++++++++++++++++++++++++++++++++ + +//+++++++++++++++++++++++++something about path begin++++++++++++++++++++++++++++++++ +/** + * @class + */ +cc.path = /** @lends cc.path# */{ + normalizeRE: /[^\.\/]+\/\.\.\//, + + /** + * Join strings to be a path. + * @example + cc.path.join("a", "b.png");//-->"a/b.png" + cc.path.join("a", "b", "c.png");//-->"a/b/c.png" + cc.path.join("a", "b");//-->"a/b" + cc.path.join("a", "b", "/");//-->"a/b/" + cc.path.join("a", "b/", "/");//-->"a/b/" + * @returns {string} + */ + join: function () { + var l = arguments.length; + var result = ""; + for (var i = 0; i < l; i++) { + result = (result + (result === "" ? "" : "/") + arguments[i]).replace(/(\/|\\\\)$/, ""); + } + return result; + }, + + /** + * Get the ext name of a path. + * @example + cc.path.extname("a/b.png");//-->".png" + cc.path.extname("a/b.png?a=1&b=2");//-->".png" + cc.path.extname("a/b");//-->null + cc.path.extname("a/b?a=1&b=2");//-->null + * @param {string} pathStr + * @returns {*} + */ + extname: function (pathStr) { + var temp = /(\.[^\.\/\?\\]*)(\?.*)?$/.exec(pathStr); + return temp ? temp[1] : null; + }, + + /** + * Get the main name of a file name + * @param {string} fileName + * @returns {string} + */ + mainFileName: function (fileName) { + if (fileName) { + var idx = fileName.lastIndexOf("."); + if (idx !== -1) + return fileName.substring(0, idx); + } + return fileName; + }, + + /** + * Get the file name of a file path. + * @example + cc.path.basename("a/b.png");//-->"b.png" + cc.path.basename("a/b.png?a=1&b=2");//-->"b.png" + cc.path.basename("a/b.png", ".png");//-->"b" + cc.path.basename("a/b.png?a=1&b=2", ".png");//-->"b" + cc.path.basename("a/b.png", ".txt");//-->"b.png" + * @param {string} pathStr + * @param {string} [extname] + * @returns {*} + */ + basename: function (pathStr, extname) { + var index = pathStr.indexOf("?"); + if (index > 0) pathStr = pathStr.substring(0, index); + var reg = /(\/|\\\\)([^(\/|\\\\)]+)$/g; + var result = reg.exec(pathStr.replace(/(\/|\\\\)$/, "")); + if (!result) return null; + var baseName = result[2]; + if (extname && pathStr.substring(pathStr.length - extname.length).toLowerCase() === extname.toLowerCase()) + return baseName.substring(0, baseName.length - extname.length); + return baseName; + }, + + /** + * Get dirname of a file path. + * @example + * unix + cc.path.driname("a/b/c.png");//-->"a/b" + cc.path.driname("a/b/c.png?a=1&b=2");//-->"a/b" + cc.path.dirname("a/b/");//-->"a/b" + cc.path.dirname("c.png");//-->"" + * windows + cc.path.driname("a\\b\\c.png");//-->"a\b" + cc.path.driname("a\\b\\c.png?a=1&b=2");//-->"a\b" + * @param {string} pathStr + * @returns {*} + */ + dirname: function (pathStr) { + return pathStr.replace(/((.*)(\/|\\|\\\\))?(.*?\..*$)?/, '$2'); + }, + + /** + * Change extname of a file path. + * @example + cc.path.changeExtname("a/b.png", ".plist");//-->"a/b.plist" + cc.path.changeExtname("a/b.png?a=1&b=2", ".plist");//-->"a/b.plist?a=1&b=2" + * @param {string} pathStr + * @param {string} [extname] + * @returns {string} + */ + changeExtname: function (pathStr, extname) { + extname = extname || ""; + var index = pathStr.indexOf("?"); + var tempStr = ""; + if (index > 0) { + tempStr = pathStr.substring(index); + pathStr = pathStr.substring(0, index); + } + index = pathStr.lastIndexOf("."); + if (index < 0) return pathStr + extname + tempStr; + return pathStr.substring(0, index) + extname + tempStr; + }, + /** + * Change file name of a file path. + * @example + cc.path.changeBasename("a/b/c.plist", "b.plist");//-->"a/b/b.plist" + cc.path.changeBasename("a/b/c.plist?a=1&b=2", "b.plist");//-->"a/b/b.plist?a=1&b=2" + cc.path.changeBasename("a/b/c.plist", ".png");//-->"a/b/c.png" + cc.path.changeBasename("a/b/c.plist", "b");//-->"a/b/b" + cc.path.changeBasename("a/b/c.plist", "b", true);//-->"a/b/b.plist" + * @param {String} pathStr + * @param {String} basename + * @param {Boolean} [isSameExt] + * @returns {string} + */ + changeBasename: function (pathStr, basename, isSameExt) { + if (basename.indexOf(".") === 0) return this.changeExtname(pathStr, basename); + var index = pathStr.indexOf("?"); + var tempStr = ""; + var ext = isSameExt ? this.extname(pathStr) : ""; + if (index > 0) { + tempStr = pathStr.substring(index); + pathStr = pathStr.substring(0, index); + } + index = pathStr.lastIndexOf("/"); + index = index <= 0 ? 0 : index + 1; + return pathStr.substring(0, index) + basename + ext + tempStr; + }, + //todo make public after verification + _normalize: function (url) { + var oldUrl = url = String(url); + + //removing all ../ + do { + oldUrl = url; + url = url.replace(this.normalizeRE, ""); + } while (oldUrl.length !== url.length); + return url; + } +}; +//+++++++++++++++++++++++++something about path end++++++++++++++++++++++++++++++++ + +//+++++++++++++++++++++++++something about loader start+++++++++++++++++++++++++++ +/** + * Resource loading management. Created by in CCBoot.js as a singleton + * cc.loader. + * @name cc.Loader + * @class + * @memberof cc + * @see cc.loader + */ + +var imagePool = { + _pool: new Array(10), + _MAX: 10, + _smallImg: "data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=", + + count: 0, + get: function () { + if (this.count > 0) { + this.count--; + var result = this._pool[this.count]; + this._pool[this.count] = null; + return result; + } + else { + return new Image(); + } + }, + put: function (img) { + var pool = this._pool; + if (img instanceof HTMLImageElement && this.count < this._MAX) { + img.src = this._smallImg; + pool[this.count] = img; + this.count++; + } + } +}; + +/** + * Singleton instance of cc.Loader. + * @name cc.loader + * @member {cc.Loader} + * @memberof cc + */ +cc.loader = (function () { + var _jsCache = {}, //cache for js + _register = {}, //register of loaders + _langPathCache = {}, //cache for lang path + _aliases = {}, //aliases for res url + _queue = {}, // Callback queue for resources already loading + _urlRegExp = new RegExp("^(?:https?|ftp)://\\S*$", "i"); + + return /** @lends cc.Loader# */{ + /** + * Root path of resources. + * @type {String} + */ + resPath: "", + + /** + * Root path of audio resources + * @type {String} + */ + audioPath: "", + + /** + * Cache for data loaded. + * @type {Object} + */ + cache: {}, + + /** + * Get XMLHttpRequest. + * @returns {XMLHttpRequest} + */ + getXMLHttpRequest: function () { + var xhr = window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject("MSXML2.XMLHTTP"); + xhr.timeout = 10000; + if (xhr.ontimeout === undefined) { + xhr._timeoutId = -1; + } + return xhr; + }, + + //@MODE_BEGIN DEV + + _getArgs4Js: function (args) { + var a0 = args[0], a1 = args[1], a2 = args[2], results = ["", null, null]; + + if (args.length === 1) { + results[1] = a0 instanceof Array ? a0 : [a0]; + } else if (args.length === 2) { + if (typeof a1 === "function") { + results[1] = a0 instanceof Array ? a0 : [a0]; + results[2] = a1; + } else { + results[0] = a0 || ""; + results[1] = a1 instanceof Array ? a1 : [a1]; + } + } else if (args.length === 3) { + results[0] = a0 || ""; + results[1] = a1 instanceof Array ? a1 : [a1]; + results[2] = a2; + } else throw new Error("arguments error to load js!"); + return results; + }, + + isLoading: function (url) { + return (_queue[url] !== undefined); + }, + + /** + * Load js files. + * If the third parameter doesn't exist, then the baseDir turns to be "". + * + * @param {string} [baseDir] The pre path for jsList or the list of js path. + * @param {array} jsList List of js path. + * @param {function} [cb] Callback function + * @returns {*} + */ + loadJs: function (baseDir, jsList, cb) { + var self = this, + args = self._getArgs4Js(arguments); + + var preDir = args[0], list = args[1], callback = args[2]; + if (navigator.userAgent.indexOf("Trident/5") > -1) { + self._loadJs4Dependency(preDir, list, 0, callback); + } else { + cc.async.map(list, function (item, index, cb1) { + var jsPath = cc.path.join(preDir, item); + if (_jsCache[jsPath]) return cb1(null); + self._createScript(jsPath, false, cb1); + }, callback); + } + }, + /** + * Load js width loading image. + * + * @param {string} [baseDir] + * @param {array} jsList + * @param {function} [cb] + */ + loadJsWithImg: function (baseDir, jsList, cb) { + var self = this, jsLoadingImg = self._loadJsImg(), + args = self._getArgs4Js(arguments); + this.loadJs(args[0], args[1], function (err) { + if (err) throw new Error(err); + jsLoadingImg.parentNode.removeChild(jsLoadingImg);//remove loading gif + if (args[2]) args[2](); + }); + }, + _createScript: function (jsPath, isAsync, cb) { + var d = document, self = this, s = document.createElement('script'); + s.async = isAsync; + _jsCache[jsPath] = true; + if (cc.game.config["noCache"] && typeof jsPath === "string") { + if (self._noCacheRex.test(jsPath)) + s.src = jsPath + "&_t=" + (new Date() - 0); + else + s.src = jsPath + "?_t=" + (new Date() - 0); + } else { + s.src = jsPath; + } + s.addEventListener('load', function () { + s.parentNode.removeChild(s); + this.removeEventListener('load', arguments.callee, false); + cb(); + }, false); + s.addEventListener('error', function () { + s.parentNode.removeChild(s); + cb("Load " + jsPath + " failed!"); + }, false); + d.body.appendChild(s); + }, + _loadJs4Dependency: function (baseDir, jsList, index, cb) { + if (index >= jsList.length) { + if (cb) cb(); + return; + } + var self = this; + self._createScript(cc.path.join(baseDir, jsList[index]), false, function (err) { + if (err) return cb(err); + self._loadJs4Dependency(baseDir, jsList, index + 1, cb); + }); + }, + _loadJsImg: function () { + var d = document, jsLoadingImg = d.getElementById("cocos2d_loadJsImg"); + if (!jsLoadingImg) { + jsLoadingImg = document.createElement('img'); + + if (cc._loadingImage) + jsLoadingImg.src = cc._loadingImage; + + var canvasNode = d.getElementById(cc.game.config["id"]); + canvasNode.style.backgroundColor = "transparent"; + canvasNode.parentNode.appendChild(jsLoadingImg); + + var canvasStyle = getComputedStyle ? getComputedStyle(canvasNode) : canvasNode.currentStyle; + if (!canvasStyle) + canvasStyle = {width: canvasNode.width, height: canvasNode.height}; + jsLoadingImg.style.left = canvasNode.offsetLeft + (parseFloat(canvasStyle.width) - jsLoadingImg.width) / 2 + "px"; + jsLoadingImg.style.top = canvasNode.offsetTop + (parseFloat(canvasStyle.height) - jsLoadingImg.height) / 2 + "px"; + jsLoadingImg.style.position = "absolute"; + } + return jsLoadingImg; + }, + //@MODE_END DEV + + /** + * Load a single resource as txt. + * @param {string} url + * @param {function} [cb] arguments are : err, txt + */ + loadTxt: function (url, cb) { + if (!cc._isNodeJs) { + var xhr = this.getXMLHttpRequest(), + errInfo = "load " + url + " failed!"; + xhr.open("GET", url, true); + if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) { + // IE-specific logic here + xhr.setRequestHeader("Accept-Charset", "utf-8"); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) + (xhr.status === 200||xhr.status === 0) ? cb(null, xhr.responseText) : cb({status:xhr.status, errorMessage:errInfo}, null); + }; + } else { + if (xhr.overrideMimeType) xhr.overrideMimeType("text\/plain; charset=utf-8"); + var loadCallback = function () { + xhr.removeEventListener('load', loadCallback); + xhr.removeEventListener('error', errorCallback); + if (xhr._timeoutId >= 0) { + clearTimeout(xhr._timeoutId); + } + else { + xhr.removeEventListener('timeout', timeoutCallback); + } + if (xhr.readyState === 4) { + (xhr.status === 200||xhr.status === 0) ? cb(null, xhr.responseText) : cb({status:xhr.status, errorMessage:errInfo}, null); + } + }; + var errorCallback = function () { + xhr.removeEventListener('load', loadCallback); + xhr.removeEventListener('error', errorCallback); + if (xhr._timeoutId >= 0) { + clearTimeout(xhr._timeoutId); + } + else { + xhr.removeEventListener('timeout', timeoutCallback); + } + cb({status: xhr.status, errorMessage: errInfo}, null); + }; + var timeoutCallback = function () { + xhr.removeEventListener('load', loadCallback); + xhr.removeEventListener('error', errorCallback); + if (xhr._timeoutId >= 0) { + clearTimeout(xhr._timeoutId); + } + else { + xhr.removeEventListener('timeout', timeoutCallback); + } + cb({status: xhr.status, errorMessage: "Request timeout: " + errInfo}, null); + }; + xhr.addEventListener('load', loadCallback); + xhr.addEventListener('error', errorCallback); + if (xhr.ontimeout === undefined) { + xhr._timeoutId = setTimeout(function () { + timeoutCallback(); + }, xhr.timeout); + } + else { + xhr.addEventListener('timeout', timeoutCallback); + } + } + xhr.send(null); + } else { + var fs = require("fs"); + fs.readFile(url, function (err, data) { + err ? cb(err) : cb(null, data.toString()); + }); + } + }, + + loadCsb: function(url, cb){ + var xhr = cc.loader.getXMLHttpRequest(), + errInfo = "load " + url + " failed!"; + xhr.open("GET", url, true); + xhr.responseType = "arraybuffer"; + + var loadCallback = function () { + xhr.removeEventListener('load', loadCallback); + xhr.removeEventListener('error', errorCallback); + if (xhr._timeoutId >= 0) { + clearTimeout(xhr._timeoutId); + } + else { + xhr.removeEventListener('timeout', timeoutCallback); + } + var arrayBuffer = xhr.response; // Note: not oReq.responseText + if (arrayBuffer) { + window.msg = arrayBuffer; + } + if (xhr.readyState === 4) { + (xhr.status === 200||xhr.status === 0) ? cb(null, xhr.response) : cb({status:xhr.status, errorMessage:errInfo}, null); + } + }; + var errorCallback = function(){ + xhr.removeEventListener('load', loadCallback); + xhr.removeEventListener('error', errorCallback); + if (xhr._timeoutId >= 0) { + clearTimeout(xhr._timeoutId); + } + else { + xhr.removeEventListener('timeout', timeoutCallback); + } + cb({status:xhr.status, errorMessage:errInfo}, null); + }; + var timeoutCallback = function () { + xhr.removeEventListener('load', loadCallback); + xhr.removeEventListener('error', errorCallback); + if (xhr._timeoutId >= 0) { + clearTimeout(xhr._timeoutId); + } + else { + xhr.removeEventListener('timeout', timeoutCallback); + } + cb({status: xhr.status, errorMessage: "Request timeout: " + errInfo}, null); + }; + xhr.addEventListener('load', loadCallback); + xhr.addEventListener('error', errorCallback); + if (xhr.ontimeout === undefined) { + xhr._timeoutId = setTimeout(function () { + timeoutCallback(); + }, xhr.timeout); + } + else { + xhr.addEventListener('timeout', timeoutCallback); + } + xhr.send(null); + }, + + /** + * Load a single resource as json. + * @param {string} url + * @param {function} [cb] arguments are : err, json + */ + loadJson: function (url, cb) { + this.loadTxt(url, function (err, txt) { + if (err) { + cb(err); + } + else { + try { + var result = JSON.parse(txt); + } + catch (e) { + throw new Error("parse json [" + url + "] failed : " + e); + return; + } + cb(null, result); + } + }); + }, + + _checkIsImageURL: function (url) { + var ext = /(\.png)|(\.jpg)|(\.bmp)|(\.jpeg)|(\.gif)/.exec(url); + return (ext != null); + }, + /** + * Load a single image. + * @param {!string} url + * @param {object} [option] + * @param {function} callback + * @returns {Image} + */ + loadImg: function (url, option, callback, img) { + var opt = { + isCrossOrigin: true + }; + if (callback !== undefined) + opt.isCrossOrigin = option.isCrossOrigin === undefined ? opt.isCrossOrigin : option.isCrossOrigin; + else if (option !== undefined) + callback = option; + + var texture = this.getRes(url); + if (texture) { + callback && callback(null, texture); + return null; + } + + var queue = _queue[url]; + if (queue) { + queue.callbacks.push(callback); + return queue.img; + } + + img = img || imagePool.get(); + if (opt.isCrossOrigin && location.origin !== "file://") + img.crossOrigin = "Anonymous"; + else + img.crossOrigin = null; + + var loadCallback = function () { + this.removeEventListener('load', loadCallback, false); + this.removeEventListener('error', errorCallback, false); + + var queue = _queue[url]; + if (queue) { + var callbacks = queue.callbacks; + for (var i = 0; i < callbacks.length; ++i) { + var cb = callbacks[i]; + if (cb) { + cb(null, img); + } + } + queue.img = null; + delete _queue[url]; + } + + if (window.ENABLE_IMAEG_POOL && cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + imagePool.put(img); + } + }; + + var self = this; + var errorCallback = function () { + this.removeEventListener('load', loadCallback, false); + this.removeEventListener('error', errorCallback, false); + + if (window.location.protocol !== 'https:' && img.crossOrigin && img.crossOrigin.toLowerCase() === "anonymous") { + opt.isCrossOrigin = false; + self.release(url); + cc.loader.loadImg(url, opt, callback, img); + } else { + var queue = _queue[url]; + if (queue) { + var callbacks = queue.callbacks; + for (var i = 0; i < callbacks.length; ++i) { + var cb = callbacks[i]; + if (cb) { + cb("load image failed"); + } + } + queue.img = null; + delete _queue[url]; + } + + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + imagePool.put(img); + } + } + }; + + _queue[url] = { + img: img, + callbacks: callback ? [callback] : [] + }; + + img.addEventListener("load", loadCallback); + img.addEventListener("error", errorCallback); + img.src = url; + return img; + }, + + /** + * Iterator function to load res + * @param {object} item + * @param {number} index + * @param {function} [cb] + * @returns {*} + * @private + */ + _loadResIterator: function (item, index, cb) { + var self = this, url = null; + var type = item.type; + if (type) { + type = "." + type.toLowerCase(); + url = item.src ? item.src : item.name + type; + } else { + url = item; + type = cc.path.extname(url); + } + + var obj = self.getRes(url); + if (obj) + return cb(null, obj); + var loader = null; + if (type) { + loader = _register[type.toLowerCase()]; + } + if (!loader) { + cc.error("loader for [" + type + "] doesn't exist!"); + return cb(); + } + var realUrl = url; + if (!_urlRegExp.test(url)) { + var basePath = loader.getBasePath ? loader.getBasePath() : self.resPath; + realUrl = self.getUrl(basePath, url); + } + + if (cc.game.config["noCache"] && typeof realUrl === "string") { + if (self._noCacheRex.test(realUrl)) + realUrl += "&_t=" + (new Date() - 0); + else + realUrl += "?_t=" + (new Date() - 0); + } + loader.load(realUrl, url, item, function (err, data) { + if (err) { + cc.log(err); + self.cache[url] = null; + delete self.cache[url]; + cb({status: 520, errorMessage: err}, null); + } else { + self.cache[url] = data; + cb(null, data); + } + }); + }, + _noCacheRex: /\?/, + + /** + * Get url with basePath. + * @param {string} basePath + * @param {string} [url] + * @returns {*} + */ + getUrl: function (basePath, url) { + var self = this, path = cc.path; + if (basePath !== undefined && url === undefined) { + url = basePath; + var type = path.extname(url); + type = type ? type.toLowerCase() : ""; + var loader = _register[type]; + if (!loader) + basePath = self.resPath; + else + basePath = loader.getBasePath ? loader.getBasePath() : self.resPath; + } + url = cc.path.join(basePath || "", url); + if (url.match(/[\/(\\\\)]lang[\/(\\\\)]/i)) { + if (_langPathCache[url]) + return _langPathCache[url]; + var extname = path.extname(url) || ""; + url = _langPathCache[url] = url.substring(0, url.length - extname.length) + "_" + cc.sys.language + extname; + } + return url; + }, + + /** + * Load resources then call the callback. + * @param {string} resources + * @param {function} [option] callback or trigger + * @param {function|Object} [loadCallback] + * @return {cc.AsyncPool} + */ + load: function (resources, option, loadCallback) { + var self = this; + var len = arguments.length; + if (len === 0) + throw new Error("arguments error!"); + + if (len === 3) { + if (typeof option === "function") { + if (typeof loadCallback === "function") + option = {trigger: option, cb: loadCallback}; + else + option = {cb: option, cbTarget: loadCallback}; + } + } else if (len === 2) { + if (typeof option === "function") + option = {cb: option}; + } else if (len === 1) { + option = {}; + } + + if (!(resources instanceof Array)) + resources = [resources]; + var asyncPool = new cc.AsyncPool( + resources, cc.CONCURRENCY_HTTP_REQUEST_COUNT, + function (value, index, AsyncPoolCallback, aPool) { + self._loadResIterator(value, index, function (err) { + var arr = Array.prototype.slice.call(arguments, 1); + if (option.trigger) + option.trigger.call(option.triggerTarget, arr[0], aPool.size, aPool.finishedSize); //call trigger + AsyncPoolCallback(err, arr[0]); + }); + }, + option.cb, option.cbTarget); + asyncPool.flow(); + return asyncPool; + }, + + _handleAliases: function (fileNames, cb) { + var self = this; + var resList = []; + for (var key in fileNames) { + var value = fileNames[key]; + _aliases[key] = value; + resList.push(value); + } + this.load(resList, cb); + }, + + /** + *

+ * Loads alias map from the contents of a filename.
+ *
+ * @note The plist file name should follow the format below:
+ *
+ *
+ *
+ *
+ * filenames
+ *
+ * sounds/click.wav
+ * sounds/click.caf
+ * sounds/endgame.wav
+ * sounds/endgame.caf
+ * sounds/gem-0.wav
+ * sounds/gem-0.caf
+ *

+ * metadata
+ *
+ * version
+ * 1
+ *

+ *

+ *

+ *

+ * @param {String} url The plist file name. + * @param {Function} [callback] + */ + loadAliases: function (url, callback) { + var self = this, dict = self.getRes(url); + if (!dict) { + self.load(url, function (err, results) { + self._handleAliases(results[0]["filenames"], callback); + }); + } else + self._handleAliases(dict["filenames"], callback); + }, + + /** + * Register a resource loader into loader. + * @param {string} extNames + * @param {function} loader + */ + register: function (extNames, loader) { + if (!extNames || !loader) return; + var self = this; + if (typeof extNames === "string") + return _register[extNames.trim().toLowerCase()] = loader; + for (var i = 0, li = extNames.length; i < li; i++) { + _register["." + extNames[i].trim().toLowerCase()] = loader; + } + }, + + /** + * Get resource data by url. + * @param url + * @returns {*} + */ + getRes: function (url) { + return this.cache[url] || this.cache[_aliases[url]]; + }, + + /** + * Get aliase by url. + * @param url + * @returns {*} + */ + _getAliase: function (url) { + return _aliases[url]; + }, + + /** + * Release the cache of resource by url. + * @param url + */ + release: function (url) { + var cache = this.cache; + var queue = _queue[url]; + if (queue) { + queue.img = null; + delete _queue[url]; + } + delete cache[url]; + delete cache[_aliases[url]]; + delete _aliases[url]; + }, + + /** + * Resource cache of all resources. + */ + releaseAll: function () { + var locCache = this.cache; + for (var key in locCache) + delete locCache[key]; + for (var key in _aliases) + delete _aliases[key]; + } + }; +})(); +//+++++++++++++++++++++++++something about loader end+++++++++++++++++++++++++++++ + +/** + * A string tool to construct a string with format string. + * for example: + * cc.formatStr("a: %d, b: %b", a, b); + * cc.formatStr(a, b, c); + * @returns {String} + */ +cc.formatStr = function () { + var args = arguments; + var l = args.length; + if (l < 1) + return ""; + + var str = args[0]; + var needToFormat = true; + if (typeof str === "object") { + needToFormat = false; + } + for (var i = 1; i < l; ++i) { + var arg = args[i]; + if (needToFormat) { + while (true) { + var result = null; + if (typeof arg === "number") { + result = str.match(/(%d)|(%s)/); + if (result) { + str = str.replace(/(%d)|(%s)/, arg); + break; + } + } + result = str.match(/%s/); + if (result) + str = str.replace(/%s/, arg); + else + str += " " + arg; + break; + } + } else + str += " " + arg; + } + return str; +}; + + +//+++++++++++++++++++++++++Engine initialization function begin+++++++++++++++++++++++++++ +(function () { + +var _tmpCanvas1 = document.createElement("canvas"), + _tmpCanvas2 = document.createElement("canvas"); + +cc.create3DContext = function (canvas, opt_attribs) { + var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"]; + var context = null; + for (var ii = 0; ii < names.length; ++ii) { + try { + context = canvas.getContext(names[ii], opt_attribs); + } catch (e) { + } + if (context) { + break; + } + } + return context; +}; + +var _initSys = function () { + /** + * System variables + * @namespace + * @name cc.sys + */ + cc.sys = {}; + var sys = cc.sys; + + /** + * English language code + * @memberof cc.sys + * @name LANGUAGE_ENGLISH + * @constant + * @type {Number} + */ + sys.LANGUAGE_ENGLISH = "en"; + + /** + * Chinese language code + * @memberof cc.sys + * @name LANGUAGE_CHINESE + * @constant + * @type {Number} + */ + sys.LANGUAGE_CHINESE = "zh"; + + /** + * French language code + * @memberof cc.sys + * @name LANGUAGE_FRENCH + * @constant + * @type {Number} + */ + sys.LANGUAGE_FRENCH = "fr"; + + /** + * Italian language code + * @memberof cc.sys + * @name LANGUAGE_ITALIAN + * @constant + * @type {Number} + */ + sys.LANGUAGE_ITALIAN = "it"; + + /** + * German language code + * @memberof cc.sys + * @name LANGUAGE_GERMAN + * @constant + * @type {Number} + */ + sys.LANGUAGE_GERMAN = "de"; + + /** + * Spanish language code + * @memberof cc.sys + * @name LANGUAGE_SPANISH + * @constant + * @type {Number} + */ + sys.LANGUAGE_SPANISH = "es"; + + /** + * Spanish language code + * @memberof cc.sys + * @name LANGUAGE_DUTCH + * @constant + * @type {Number} + */ + sys.LANGUAGE_DUTCH = "du"; + + /** + * Russian language code + * @memberof cc.sys + * @name LANGUAGE_RUSSIAN + * @constant + * @type {Number} + */ + sys.LANGUAGE_RUSSIAN = "ru"; + + /** + * Korean language code + * @memberof cc.sys + * @name LANGUAGE_KOREAN + * @constant + * @type {Number} + */ + sys.LANGUAGE_KOREAN = "ko"; + + /** + * Japanese language code + * @memberof cc.sys + * @name LANGUAGE_JAPANESE + * @constant + * @type {Number} + */ + sys.LANGUAGE_JAPANESE = "ja"; + + /** + * Hungarian language code + * @memberof cc.sys + * @name LANGUAGE_HUNGARIAN + * @constant + * @type {Number} + */ + sys.LANGUAGE_HUNGARIAN = "hu"; + + /** + * Portuguese language code + * @memberof cc.sys + * @name LANGUAGE_PORTUGUESE + * @constant + * @type {Number} + */ + sys.LANGUAGE_PORTUGUESE = "pt"; + + /** + * Arabic language code + * @memberof cc.sys + * @name LANGUAGE_ARABIC + * @constant + * @type {Number} + */ + sys.LANGUAGE_ARABIC = "ar"; + + /** + * Norwegian language code + * @memberof cc.sys + * @name LANGUAGE_NORWEGIAN + * @constant + * @type {Number} + */ + sys.LANGUAGE_NORWEGIAN = "no"; + + /** + * Polish language code + * @memberof cc.sys + * @name LANGUAGE_POLISH + * @constant + * @type {Number} + */ + sys.LANGUAGE_POLISH = "pl"; + + /** + * Unknown language code + * @memberof cc.sys + * @name LANGUAGE_UNKNOWN + * @constant + * @type {Number} + */ + sys.LANGUAGE_UNKNOWN = "unkonwn"; + + /** + * @memberof cc.sys + * @name OS_IOS + * @constant + * @type {string} + */ + sys.OS_IOS = "iOS"; + /** + * @memberof cc.sys + * @name OS_ANDROID + * @constant + * @type {string} + */ + sys.OS_ANDROID = "Android"; + /** + * @memberof cc.sys + * @name OS_WINDOWS + * @constant + * @type {string} + */ + sys.OS_WINDOWS = "Windows"; + /** + * @memberof cc.sys + * @name OS_MARMALADE + * @constant + * @type {string} + */ + sys.OS_MARMALADE = "Marmalade"; + /** + * @memberof cc.sys + * @name OS_LINUX + * @constant + * @type {string} + */ + sys.OS_LINUX = "Linux"; + /** + * @memberof cc.sys + * @name OS_BADA + * @constant + * @type {string} + */ + sys.OS_BADA = "Bada"; + /** + * @memberof cc.sys + * @name OS_BLACKBERRY + * @constant + * @type {string} + */ + sys.OS_BLACKBERRY = "Blackberry"; + /** + * @memberof cc.sys + * @name OS_OSX + * @constant + * @type {string} + */ + sys.OS_OSX = "OS X"; + /** + * @memberof cc.sys + * @name OS_WP8 + * @constant + * @type {string} + */ + sys.OS_WP8 = "WP8"; + /** + * @memberof cc.sys + * @name OS_WINRT + * @constant + * @type {string} + */ + sys.OS_WINRT = "WINRT"; + /** + * @memberof cc.sys + * @name OS_UNKNOWN + * @constant + * @type {string} + */ + sys.OS_UNKNOWN = "Unknown"; + + /** + * @memberof cc.sys + * @name UNKNOWN + * @constant + * @default + * @type {Number} + */ + sys.UNKNOWN = -1; + /** + * @memberof cc.sys + * @name WIN32 + * @constant + * @default + * @type {Number} + */ + sys.WIN32 = 0; + /** + * @memberof cc.sys + * @name LINUX + * @constant + * @default + * @type {Number} + */ + sys.LINUX = 1; + /** + * @memberof cc.sys + * @name MACOS + * @constant + * @default + * @type {Number} + */ + sys.MACOS = 2; + /** + * @memberof cc.sys + * @name ANDROID + * @constant + * @default + * @type {Number} + */ + sys.ANDROID = 3; + /** + * @memberof cc.sys + * @name IOS + * @constant + * @default + * @type {Number} + */ + sys.IPHONE = 4; + /** + * @memberof cc.sys + * @name IOS + * @constant + * @default + * @type {Number} + */ + sys.IPAD = 5; + /** + * @memberof cc.sys + * @name BLACKBERRY + * @constant + * @default + * @type {Number} + */ + sys.BLACKBERRY = 6; + /** + * @memberof cc.sys + * @name NACL + * @constant + * @default + * @type {Number} + */ + sys.NACL = 7; + /** + * @memberof cc.sys + * @name EMSCRIPTEN + * @constant + * @default + * @type {Number} + */ + sys.EMSCRIPTEN = 8; + /** + * @memberof cc.sys + * @name TIZEN + * @constant + * @default + * @type {Number} + */ + sys.TIZEN = 9; + /** + * @memberof cc.sys + * @name WINRT + * @constant + * @default + * @type {Number} + */ + sys.WINRT = 10; + /** + * @memberof cc.sys + * @name WP8 + * @constant + * @default + * @type {Number} + */ + sys.WP8 = 11; + /** + * @memberof cc.sys + * @name MOBILE_BROWSER + * @constant + * @default + * @type {Number} + */ + sys.MOBILE_BROWSER = 100; + /** + * @memberof cc.sys + * @name DESKTOP_BROWSER + * @constant + * @default + * @type {Number} + */ + sys.DESKTOP_BROWSER = 101; + + sys.BROWSER_TYPE_WECHAT = "wechat"; + sys.BROWSER_TYPE_ANDROID = "androidbrowser"; + sys.BROWSER_TYPE_IE = "ie"; + sys.BROWSER_TYPE_QQ_APP = "qq"; // QQ App + sys.BROWSER_TYPE_QQ = "qqbrowser"; + sys.BROWSER_TYPE_MOBILE_QQ = "mqqbrowser"; + sys.BROWSER_TYPE_UC = "ucbrowser"; + sys.BROWSER_TYPE_360 = "360browser"; + sys.BROWSER_TYPE_BAIDU_APP = "baiduboxapp"; + sys.BROWSER_TYPE_BAIDU = "baidubrowser"; + sys.BROWSER_TYPE_MAXTHON = "maxthon"; + sys.BROWSER_TYPE_OPERA = "opera"; + sys.BROWSER_TYPE_OUPENG = "oupeng"; + sys.BROWSER_TYPE_MIUI = "miuibrowser"; + sys.BROWSER_TYPE_FIREFOX = "firefox"; + sys.BROWSER_TYPE_SAFARI = "safari"; + sys.BROWSER_TYPE_CHROME = "chrome"; + sys.BROWSER_TYPE_LIEBAO = "liebao"; + sys.BROWSER_TYPE_QZONE = "qzone"; + sys.BROWSER_TYPE_SOUGOU = "sogou"; + sys.BROWSER_TYPE_UNKNOWN = "unknown"; + + /** + * Is native ? This is set to be true in jsb auto. + * @memberof cc.sys + * @name isNative + * @type {Boolean} + */ + sys.isNative = false; + + var win = window, nav = win.navigator, doc = document, docEle = doc.documentElement; + var ua = nav.userAgent.toLowerCase(); + + /** + * Indicate whether system is mobile system + * @memberof cc.sys + * @name isMobile + * @type {Boolean} + */ + sys.isMobile = /mobile|android|iphone|ipad/.test(ua); + + /** + * Indicate the running platform + * @memberof cc.sys + * @name platform + * @type {Number} + */ + sys.platform = sys.isMobile ? sys.MOBILE_BROWSER : sys.DESKTOP_BROWSER; + + var currLanguage = nav.language; + currLanguage = currLanguage ? currLanguage : nav.browserLanguage; + currLanguage = currLanguage ? currLanguage.split("-")[0] : sys.LANGUAGE_ENGLISH; + + /** + * Indicate the current language of the running system + * @memberof cc.sys + * @name language + * @type {String} + */ + sys.language = currLanguage; + + // Get the os of system + var isAndroid = false, iOS = false, osVersion = '', osMainVersion = 0; + var uaResult = /android (\d+(?:\.\d+)+)/i.exec(ua) || /android (\d+(?:\.\d+)+)/i.exec(nav.platform); + if (uaResult) { + isAndroid = true; + osVersion = uaResult[1] || ''; + osMainVersion = parseInt(osVersion) || 0; + } + uaResult = /(iPad|iPhone|iPod).*OS ((\d+_?){2,3})/i.exec(ua); + if (uaResult) { + iOS = true; + osVersion = uaResult[2] || ''; + osMainVersion = parseInt(osVersion) || 0; + } + else if (/(iPhone|iPad|iPod)/.exec(nav.platform)) { + iOS = true; + osVersion = ''; + osMainVersion = 0; + } + + var osName = sys.OS_UNKNOWN; + if (nav.appVersion.indexOf("Win") !== -1) osName = sys.OS_WINDOWS; + else if (iOS) osName = sys.OS_IOS; + else if (nav.appVersion.indexOf("Mac") !== -1) osName = sys.OS_OSX; + else if (nav.appVersion.indexOf("X11") !== -1 && nav.appVersion.indexOf("Linux") === -1) osName = sys.OS_UNIX; + else if (isAndroid) osName = sys.OS_ANDROID; + else if (nav.appVersion.indexOf("Linux") !== -1) osName = sys.OS_LINUX; + + /** + * Indicate the running os name + * @memberof cc.sys + * @name os + * @type {String} + */ + sys.os = osName; + /** + * Indicate the running os version string + * @memberof cc.sys + * @name osVersion + * @type {String} + */ + sys.osVersion = osVersion; + /** + * Indicate the running os main version number + * @memberof cc.sys + * @name osMainVersion + * @type {Number} + */ + sys.osMainVersion = osMainVersion; + + /** + * Indicate the running browser type + * @memberof cc.sys + * @name browserType + * @type {String} + */ + sys.browserType = sys.BROWSER_TYPE_UNKNOWN; + /* Determine the browser type */ + (function(){ + var typeReg1 = /micromessenger|mqqbrowser|sogou|qzone|liebao|ucbrowser|360 aphone|360browser|baiduboxapp|baidubrowser|maxthon|mxbrowser|trident|miuibrowser/i; + var typeReg2 = /qqbrowser|qq|chrome|safari|firefox|opr|oupeng|opera/i; + var browserTypes = typeReg1.exec(ua); + if(!browserTypes) browserTypes = typeReg2.exec(ua); + var browserType = browserTypes ? browserTypes[0] : sys.BROWSER_TYPE_UNKNOWN; + if (browserType === 'micromessenger') + browserType = sys.BROWSER_TYPE_WECHAT; + else if (browserType === "safari" && isAndroid) + browserType = sys.BROWSER_TYPE_ANDROID; + else if (browserType === "trident") + browserType = sys.BROWSER_TYPE_IE; + else if (browserType === "360 aphone") + browserType = sys.BROWSER_TYPE_360; + else if (browserType === "mxbrowser") + browserType = sys.BROWSER_TYPE_MAXTHON; + else if (browserType === "opr") + browserType = sys.BROWSER_TYPE_OPERA; + + sys.browserType = browserType; + })(); + + /** + * Indicate the running browser version + * @memberof cc.sys + * @name browserVersion + * @type {String} + */ + sys.browserVersion = ""; + /* Determine the browser version number */ + (function(){ + var versionReg1 = /(mqqbrowser|micromessenger|sogou|qzone|liebao|maxthon|mxbrowser|baidu)(mobile)?(browser)?\/?([\d.]+)/i; + var versionReg2 = /(msie |rv:|firefox|chrome|ucbrowser|qq|oupeng|opera|opr|safari|miui)(mobile)?(browser)?\/?([\d.]+)/i; + var tmp = ua.match(versionReg1); + if(!tmp) tmp = ua.match(versionReg2); + sys.browserVersion = tmp ? tmp[4] : ""; + })(); + + var w = window.innerWidth || document.documentElement.clientWidth; + var h = window.innerHeight || document.documentElement.clientHeight; + var ratio = window.devicePixelRatio || 1; + + /** + * Indicate the real pixel resolution of the whole game window + * @memberof cc.sys + * @name windowPixelResolution + * @type {Size} + */ + sys.windowPixelResolution = { + width: ratio * w, + height: ratio * h + }; + + sys._checkWebGLRenderMode = function () { + if (cc._renderType !== cc.game.RENDER_TYPE_WEBGL) + throw new Error("This feature supports WebGL render mode only."); + }; + + //Whether or not the Canvas BlendModes are supported. + sys._supportCanvasNewBlendModes = (function(){ + var canvas = _tmpCanvas1; + canvas.width = 1; + canvas.height = 1; + var context = canvas.getContext('2d'); + context.fillStyle = '#000'; + context.fillRect(0, 0, 1, 1); + context.globalCompositeOperation = 'multiply'; + + var canvas2 = _tmpCanvas2; + canvas2.width = 1; + canvas2.height = 1; + var context2 = canvas2.getContext('2d'); + context2.fillStyle = '#fff'; + context2.fillRect(0, 0, 1, 1); + context.drawImage(canvas2, 0, 0, 1, 1); + + return context.getImageData(0, 0, 1, 1).data[0] === 0; + })(); + + // Adjust mobile css settings + if (cc.sys.isMobile) { + var fontStyle = document.createElement("style"); + fontStyle.type = "text/css"; + document.body.appendChild(fontStyle); + + fontStyle.textContent = "body,canvas,div{ -moz-user-select: none;-webkit-user-select: none;-ms-user-select: none;-khtml-user-select: none;" + + "-webkit-tap-highlight-color:rgba(0,0,0,0);}"; + } + + /** + * cc.sys.localStorage is a local storage component. + * @memberof cc.sys + * @name localStorage + * @type {Object} + */ + try { + var localStorage = sys.localStorage = win.localStorage; + localStorage.setItem("storage", ""); + localStorage.removeItem("storage"); + localStorage = null; + } catch (e) { + var warn = function () { + cc.warn("Warning: localStorage isn't enabled. Please confirm browser cookie or privacy option"); + }; + sys.localStorage = { + getItem : warn, + setItem : warn, + removeItem : warn, + clear : warn + }; + } + + var _supportCanvas = !!_tmpCanvas1.getContext("2d"); + var _supportWebGL = false; + if (win.WebGLRenderingContext) { + var tmpCanvas = document.createElement("CANVAS"); + try{ + var context = cc.create3DContext(tmpCanvas); + if (context) { + _supportWebGL = true; + } + + if (_supportWebGL && sys.os === sys.OS_IOS && sys.osMainVersion === 9) { + // Not activating WebGL in iOS 9 UIWebView because it may crash when entering background + if (!window.indexedDB) { + _supportWebGL = false; + } + } + + if (_supportWebGL && sys.os === sys.OS_ANDROID) { + var browserVer = parseFloat(sys.browserVersion); + switch (sys.browserType) { + case sys.BROWSER_TYPE_MOBILE_QQ: + case sys.BROWSER_TYPE_BAIDU: + case sys.BROWSER_TYPE_BAIDU_APP: + // QQ & Baidu Brwoser 6.2+ (using blink kernel) + if (browserVer >= 6.2) { + _supportWebGL = true; + } + else { + _supportWebGL = false; + } + break; + case sys.BROWSER_TYPE_CHROME: + // Chrome on android supports WebGL from v.30 + if(browserVer >= 30.0) { + _supportWebGL = true; + } else { + _supportWebGL = false; + } + break; + case sys.BROWSER_TYPE_ANDROID: + // Android 5+ default browser + if (sys.osMainVersion && sys.osMainVersion >= 5) { + _supportWebGL = true; + } + break; + case sys.BROWSER_TYPE_UNKNOWN: + case sys.BROWSER_TYPE_360: + case sys.BROWSER_TYPE_MIUI: + case sys.BROWSER_TYPE_UC: + _supportWebGL = false; + } + } + } + catch (e) {} + tmpCanvas = null; + } + + /** + * The capabilities of the current platform + * @memberof cc.sys + * @name capabilities + * @type {Object} + */ + var capabilities = sys.capabilities = { + "canvas": _supportCanvas, + "opengl": _supportWebGL + }; + if (docEle['ontouchstart'] !== undefined || doc['ontouchstart'] !== undefined || nav.msPointerEnabled) + capabilities["touches"] = true; + if (docEle['onmouseup'] !== undefined) + capabilities["mouse"] = true; + if (docEle['onkeyup'] !== undefined) + capabilities["keyboard"] = true; + if (win.DeviceMotionEvent || win.DeviceOrientationEvent) + capabilities["accelerometer"] = true; + + /** + * Forces the garbage collection, only available in JSB + * @memberof cc.sys + * @name garbageCollect + * @function + */ + sys.garbageCollect = function () { + // N/A in cocos2d-html5 + }; + + /** + * Dumps rooted objects, only available in JSB + * @memberof cc.sys + * @name dumpRoot + * @function + */ + sys.dumpRoot = function () { + // N/A in cocos2d-html5 + }; + + /** + * Restart the JS VM, only available in JSB + * @memberof cc.sys + * @name restartVM + * @function + */ + sys.restartVM = function () { + // N/A in cocos2d-html5 + }; + + /** + * Clean a script in the JS VM, only available in JSB + * @memberof cc.sys + * @name cleanScript + * @param {String} jsfile + * @function + */ + sys.cleanScript = function (jsfile) { + // N/A in cocos2d-html5 + }; + + /** + * Check whether an object is valid, + * In web engine, it will return true if the object exist + * In native engine, it will return true if the JS object and the correspond native object are both valid + * @memberof cc.sys + * @name isObjectValid + * @param {Object} obj + * @return {boolean} Validity of the object + * @function + */ + sys.isObjectValid = function (obj) { + if (obj) return true; + else return false; + }; + + /** + * Dump system informations + * @memberof cc.sys + * @name dump + * @function + */ + sys.dump = function () { + var self = this; + var str = ""; + str += "isMobile : " + self.isMobile + "\r\n"; + str += "language : " + self.language + "\r\n"; + str += "browserType : " + self.browserType + "\r\n"; + str += "browserVersion : " + self.browserVersion + "\r\n"; + str += "capabilities : " + JSON.stringify(self.capabilities) + "\r\n"; + str += "os : " + self.os + "\r\n"; + str += "osVersion : " + self.osVersion + "\r\n"; + str += "platform : " + self.platform + "\r\n"; + str += "Using " + (cc._renderType === cc.game.RENDER_TYPE_WEBGL ? "WEBGL" : "CANVAS") + " renderer." + "\r\n"; + cc.log(str); + }; + + /** + * Open a url in browser + * @memberof cc.sys + * @name openURL + * @param {String} url + */ + sys.openURL = function(url){ + window.open(url); + }; + + /** + * Get the number of milliseconds elapsed since 1 January 1970 00:00:00 UTC. + * @memberof cc.sys + * @name now + * @return {Number} + */ + sys.now = function () { + if (Date.now) { + return Date.now(); + } + else { + return +(new Date); + } + }; +}; +_initSys(); + +_tmpCanvas1 = null; +_tmpCanvas2 = null; + +//to make sure the cc.log, cc.warn, cc.error and cc.assert would not throw error before init by debugger mode. +cc.log = cc.warn = cc.error = cc.assert = function () { +}; + +var _config = null, + //cache for js and module that has added into jsList to be loaded. + _jsAddedCache = {}, + _engineInitCalled = false, + _engineLoadedCallback = null; + +cc._engineLoaded = false; + +function _determineRenderType(config) { + var CONFIG_KEY = cc.game.CONFIG_KEY, + userRenderMode = parseInt(config[CONFIG_KEY.renderMode]) || 0; + + // Adjust RenderType + if (isNaN(userRenderMode) || userRenderMode > 2 || userRenderMode < 0) + config[CONFIG_KEY.renderMode] = 0; + + // Determine RenderType + cc._renderType = cc.game.RENDER_TYPE_CANVAS; + cc._supportRender = false; + + if (userRenderMode === 0) { + if (cc.sys.capabilities["opengl"]) { + cc._renderType = cc.game.RENDER_TYPE_WEBGL; + cc._supportRender = true; + } + else if (cc.sys.capabilities["canvas"]) { + cc._renderType = cc.game.RENDER_TYPE_CANVAS; + cc._supportRender = true; + } + } + else if (userRenderMode === 1 && cc.sys.capabilities["canvas"]) { + cc._renderType = cc.game.RENDER_TYPE_CANVAS; + cc._supportRender = true; + } + else if (userRenderMode === 2 && cc.sys.capabilities["opengl"]) { + cc._renderType = cc.game.RENDER_TYPE_WEBGL; + cc._supportRender = true; + } +} + +function _getJsListOfModule(moduleMap, moduleName, dir) { + if (_jsAddedCache[moduleName]) return null; + dir = dir || ""; + var jsList = []; + var tempList = moduleMap[moduleName]; + if (!tempList) throw new Error("can not find module [" + moduleName + "]"); + var ccPath = cc.path; + for (var i = 0, li = tempList.length; i < li; i++) { + var item = tempList[i]; + if (_jsAddedCache[item]) continue; + var extname = ccPath.extname(item); + if (!extname) { + var arr = _getJsListOfModule(moduleMap, item, dir); + if (arr) jsList = jsList.concat(arr); + } else if (extname.toLowerCase() === ".js") jsList.push(ccPath.join(dir, item)); + _jsAddedCache[item] = 1; + } + return jsList; +} + +function _afterEngineLoaded(config) { + if (cc._initDebugSetting) + cc._initDebugSetting(config[cc.game.CONFIG_KEY.debugMode]); + cc._engineLoaded = true; + console.log(cc.ENGINE_VERSION); + if (_engineLoadedCallback) _engineLoadedCallback(); +} + +function _load(config) { + var self = this; + var CONFIG_KEY = cc.game.CONFIG_KEY, engineDir = config[CONFIG_KEY.engineDir], loader = cc.loader; + + if (cc.Class) { + // Single file loaded + _afterEngineLoaded(config); + } else { + // Load cocos modules + var ccModulesPath = cc.path.join(engineDir, "moduleConfig.json"); + loader.loadJson(ccModulesPath, function (err, modulesJson) { + if (err) throw new Error(err); + var modules = config["modules"] || []; + var moduleMap = modulesJson["module"]; + var jsList = []; + if (cc.sys.capabilities["opengl"] && modules.indexOf("base4webgl") < 0) modules.splice(0, 0, "base4webgl"); + else if (modules.indexOf("core") < 0) modules.splice(0, 0, "core"); + for (var i = 0, li = modules.length; i < li; i++) { + var arr = _getJsListOfModule(moduleMap, modules[i], engineDir); + if (arr) jsList = jsList.concat(arr); + } + cc.loader.loadJsWithImg(jsList, function (err) { + if (err) throw err; + _afterEngineLoaded(config); + }); + }); + } +} + +function _windowLoaded() { + this.removeEventListener('load', _windowLoaded, false); + _load(cc.game.config); +} + +cc.initEngine = function (config, cb) { + if (_engineInitCalled) { + var previousCallback = _engineLoadedCallback; + _engineLoadedCallback = function () { + previousCallback && previousCallback(); + cb && cb(); + } + return; + } + + _engineLoadedCallback = cb; + + // Config uninitialized and given, initialize with it + if (!cc.game.config && config) { + cc.game.config = config; + } + // No config given and no config set before, load it + else if (!cc.game.config) { + cc.game._loadConfig(); + } + config = cc.game.config; + + _determineRenderType(config); + + document.body ? _load(config) : cc._addEventListener(window, 'load', _windowLoaded, false); + _engineInitCalled = true; +}; + +})(); +//+++++++++++++++++++++++++Engine initialization function end+++++++++++++++++++++++++++++ + +//+++++++++++++++++++++++++something about CCGame begin+++++++++++++++++++++++++++ +/** + * An object to boot the game. + * @class + * @name cc.game + * + */ +cc.game = /** @lends cc.game# */{ + /** + * Debug mode: No debugging. {@static} + * @const {Number} + * @static + */ + DEBUG_MODE_NONE: 0, + /** + * Debug mode: Info, warning, error to console. + * @const {Number} + * @static + */ + DEBUG_MODE_INFO: 1, + /** + * Debug mode: Warning, error to console. + * @const {Number} + * @static + */ + DEBUG_MODE_WARN: 2, + /** + * Debug mode: Error to console. + * @const {Number} + * @static + */ + DEBUG_MODE_ERROR: 3, + /** + * Debug mode: Info, warning, error to web page. + * @const {Number} + * @static + */ + DEBUG_MODE_INFO_FOR_WEB_PAGE: 4, + /** + * Debug mode: Warning, error to web page. + * @const {Number} + * @static + */ + DEBUG_MODE_WARN_FOR_WEB_PAGE: 5, + /** + * Debug mode: Error to web page. + * @const {Number} + * @static + */ + DEBUG_MODE_ERROR_FOR_WEB_PAGE: 6, + + /** + * Event that is fired when the game is hidden. + * @constant {String} + */ + EVENT_HIDE: "game_on_hide", + /** + * Event that is fired when the game is shown. + * @constant {String} + */ + EVENT_SHOW: "game_on_show", + /** + * Event that is fired when the game is resized. + * @constant {String} + */ + EVENT_RESIZE: "game_on_resize", + /** + * Event that is fired when the renderer is done being initialized. + * @constant {String} + */ + EVENT_RENDERER_INITED: "renderer_inited", + + /** @constant {Number} */ + RENDER_TYPE_CANVAS: 0, + /** @constant {Number} */ + RENDER_TYPE_WEBGL: 1, + /** @constant {Number} */ + RENDER_TYPE_OPENGL: 2, + + _eventHide: null, + _eventShow: null, + + /** + * Keys found in project.json. + * + * @constant + * @type {Object} + * + * @prop {String} engineDir - In debug mode, if you use the whole engine to develop your game, you should specify its relative path with "engineDir". + * @prop {String} modules - Defines which modules you will need in your game, it's useful only on web + * @prop {String} debugMode - Debug mode, see DEBUG_MODE_XXX constant definitions. + * @prop {String} exposeClassName - Expose class name to chrome debug tools + * @prop {String} showFPS - Left bottom corner fps information will show when "showFPS" equals true, otherwise it will be hide. + * @prop {String} frameRate - Sets the wanted frame rate for your game, but the real fps depends on your game implementation and the running environment. + * @prop {String} id - Sets the id of your canvas element on the web page, it's useful only on web. + * @prop {String} renderMode - Sets the renderer type, only useful on web, 0: Automatic, 1: Canvas, 2: WebGL + * @prop {String} jsList - Sets the list of js files in your game. + */ + CONFIG_KEY: { + width: "width", + height: "height", + engineDir: "engineDir", + modules: "modules", + debugMode: "debugMode", + exposeClassName: "exposeClassName", + showFPS: "showFPS", + frameRate: "frameRate", + id: "id", + renderMode: "renderMode", + jsList: "jsList" + }, + + // states + _paused: true,//whether the game is paused + _configLoaded: false,//whether config loaded + _prepareCalled: false,//whether the prepare function has been called + _prepared: false,//whether the engine has prepared + _rendererInitialized: false, + + _renderContext: null, + + _intervalId: null,//interval target of main + + _lastTime: null, + _frameTime: null, + + /** + * The outer frame of the game canvas, parent of cc.container + * @type {Object} + */ + frame: null, + /** + * The container of game canvas, equals to cc.container + * @type {Object} + */ + container: null, + /** + * The canvas of the game, equals to cc._canvas + * @type {Object} + */ + canvas: null, + + /** + * Config of game + * @type {Object} + */ + config: null, + + /** + * Callback when the scripts of engine have been load. + * @type {Function|null} + */ + onStart: null, + + /** + * Callback when game exits. + * @type {Function|null} + */ + onStop: null, + +//@Public Methods + +// @Game play control + /** + * Set frameRate of game. + * @param frameRate + */ + setFrameRate: function (frameRate) { + var self = this, config = self.config, CONFIG_KEY = self.CONFIG_KEY; + config[CONFIG_KEY.frameRate] = frameRate; + if (self._intervalId) + window.cancelAnimationFrame(self._intervalId); + self._intervalId = 0; + self._paused = true; + self._setAnimFrame(); + self._runMainLoop(); + }, + + /** + * Run the game frame by frame. + */ + step: function () { + cc.director.mainLoop(); + }, + + /** + * Pause the game. + */ + pause: function () { + if (this._paused) return; + this._paused = true; + // Pause audio engine + if (cc.audioEngine) { + cc.audioEngine._pausePlaying(); + } + // Pause main loop + if (this._intervalId) + window.cancelAnimationFrame(this._intervalId); + this._intervalId = 0; + }, + + /** + * Resume the game from pause. + */ + resume: function () { + if (!this._paused) return; + this._paused = false; + // Resume audio engine + if (cc.audioEngine) { + cc.audioEngine._resumePlaying(); + } + // Resume main loop + this._runMainLoop(); + }, + + /** + * Check whether the game is paused. + */ + isPaused: function () { + return this._paused; + }, + + /** + * Restart game. + */ + restart: function () { + cc.director.popToSceneStackLevel(0); + // Clean up audio + cc.audioEngine && cc.audioEngine.end(); + + cc.game.onStart(); + }, + + /** + * End game, it will close the game window + */ + end: function () { + close(); + }, + +// @Game loading + /** + * Prepare game. + * @param cb + */ + prepare: function (cb) { + var self = this, + config = self.config, + CONFIG_KEY = self.CONFIG_KEY; + + // Config loaded + if (!this._configLoaded) { + this._loadConfig(function () { + self.prepare(cb); + }); + return; + } + + // Already prepared + if (this._prepared) { + if (cb) cb(); + return; + } + // Prepare called, but not done yet + if (this._prepareCalled) { + return; + } + // Prepare never called and engine ready + if (cc._engineLoaded) { + this._prepareCalled = true; + + this._initRenderer(config[CONFIG_KEY.width], config[CONFIG_KEY.height]); + + /** + * cc.view is the shared view object. + * @type {cc.EGLView} + * @name cc.view + * @memberof cc + */ + cc.view = cc.EGLView._getInstance(); + + /** + * @type {cc.Director} + * @name cc.director + * @memberof cc + */ + cc.director = cc.Director._getInstance(); + if (cc.director.setOpenGLView) + cc.director.setOpenGLView(cc.view); + /** + * cc.winSize is the alias object for the size of the current game window. + * @type {cc.Size} + * @name cc.winSize + * @memberof cc + */ + cc.winSize = cc.director.getWinSize(); + + this._initEvents(); + + this._setAnimFrame(); + this._runMainLoop(); + + // Load game scripts + var jsList = config[CONFIG_KEY.jsList]; + if (jsList) { + cc.loader.loadJsWithImg(jsList, function (err) { + if (err) throw new Error(err); + self._prepared = true; + if (cb) cb(); + }); + } + else { + if (cb) cb(); + } + + return; + } + + // Engine not loaded yet + cc.initEngine(this.config, function () { + self.prepare(cb); + }); + }, + + /** + * Run game with configuration object and onStart function. + * @param {Object|Function} [config] Pass configuration object or onStart function + * @param {onStart} [onStart] onStart function to be executed after game initialized + */ + run: function (config, onStart) { + if (typeof config === 'function') { + cc.game.onStart = config; + } + else { + if (config) { + if (typeof config === 'string') { + if (!cc.game.config) this._loadConfig(); + cc.game.config[cc.game.CONFIG_KEY.id] = config; + } else { + cc.game.config = config; + } + } + if (typeof onStart === 'function') { + cc.game.onStart = onStart; + } + } + + this.prepare(cc.game.onStart && cc.game.onStart.bind(cc.game)); + }, + +//@Private Methods + +// @Time ticker section + _setAnimFrame: function () { + this._lastTime = new Date(); + var frameRate = cc.game.config[cc.game.CONFIG_KEY.frameRate]; + this._frameTime = 1000 / frameRate; + if (frameRate !== 60 && frameRate !== 30) { + window.requestAnimFrame = this._stTime; + window.cancelAnimationFrame = this._ctTime; + } + else { + window.requestAnimFrame = window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + this._stTime; + window.cancelAnimationFrame = window.cancelAnimationFrame || + window.cancelRequestAnimationFrame || + window.msCancelRequestAnimationFrame || + window.mozCancelRequestAnimationFrame || + window.oCancelRequestAnimationFrame || + window.webkitCancelRequestAnimationFrame || + window.msCancelAnimationFrame || + window.mozCancelAnimationFrame || + window.webkitCancelAnimationFrame || + window.oCancelAnimationFrame || + this._ctTime; + } + }, + _stTime: function (callback) { + var currTime = new Date().getTime(); + var timeToCall = Math.max(0, cc.game._frameTime - (currTime - cc.game._lastTime)); + var id = window.setTimeout(function() { callback(); }, + timeToCall); + cc.game._lastTime = currTime + timeToCall; + return id; + }, + _ctTime: function (id) { + window.clearTimeout(id); + }, + //Run game. + _runMainLoop: function () { + var self = this, callback, config = self.config, CONFIG_KEY = self.CONFIG_KEY, + director = cc.director, + skip = true, frameRate = config[CONFIG_KEY.frameRate]; + + director.setDisplayStats(config[CONFIG_KEY.showFPS]); + + callback = function () { + if (!self._paused) { + if (frameRate === 30) { + if (skip = !skip) { + self._intervalId = window.requestAnimFrame(callback); + return; + } + } + + director.mainLoop(); + self._intervalId = window.requestAnimFrame(callback); + } + }; + + self._intervalId = window.requestAnimFrame(callback); + self._paused = false; + }, + +// @Game loading section + _loadConfig: function (cb) { + // Load config + var config = this.config || document["ccConfig"]; + // Already loaded or Load from document.ccConfig + if (config) { + this._initConfig(config); + cb && cb(); + } + // Load from project.json + else { + var cocos_script = document.getElementsByTagName('script'); + for (var i = 0; i < cocos_script.length; i++) { + var _t = cocos_script[i].getAttribute('cocos'); + if (_t === '' || _t) { + break; + } + } + var self = this; + var loaded = function (err, txt) { + var data = JSON.parse(txt); + self._initConfig(data); + cb && cb(); + }; + var _src, txt, _resPath; + if (i < cocos_script.length) { + _src = cocos_script[i].src; + if (_src) { + _resPath = /(.*)\//.exec(_src)[0]; + cc.loader.resPath = _resPath; + _src = cc.path.join(_resPath, 'project.json'); + } + cc.loader.loadTxt(_src, loaded); + } + if (!txt) { + cc.loader.loadTxt("project.json", loaded); + } + } + }, + + _initConfig: function (config) { + var CONFIG_KEY = this.CONFIG_KEY, + modules = config[CONFIG_KEY.modules]; + + // Configs adjustment + config[CONFIG_KEY.showFPS] = typeof config[CONFIG_KEY.showFPS] === 'undefined' ? true : config[CONFIG_KEY.showFPS]; + config[CONFIG_KEY.engineDir] = config[CONFIG_KEY.engineDir] || "frameworks/cocos2d-html5"; + if (config[CONFIG_KEY.debugMode] == null) + config[CONFIG_KEY.debugMode] = 0; + config[CONFIG_KEY.exposeClassName] = !!config[CONFIG_KEY.exposeClassName]; + config[CONFIG_KEY.frameRate] = config[CONFIG_KEY.frameRate] || 60; + if (config[CONFIG_KEY.renderMode] == null) + config[CONFIG_KEY.renderMode] = 0; + if (config[CONFIG_KEY.registerSystemEvent] == null) + config[CONFIG_KEY.registerSystemEvent] = true; + + // Modules adjustment + if (modules && modules.indexOf("core") < 0) modules.splice(0, 0, "core"); + modules && (config[CONFIG_KEY.modules] = modules); + this.config = config; + this._configLoaded = true; + }, + + _initRenderer: function (width, height) { + // Avoid setup to be called twice. + if (this._rendererInitialized) return; + + if (!cc._supportRender) { + throw new Error("The renderer doesn't support the renderMode " + this.config[this.CONFIG_KEY.renderMode]); + } + + var el = this.config[cc.game.CONFIG_KEY.id], + win = window, + element = cc.$(el) || cc.$('#' + el), + localCanvas, localContainer, localConStyle; + + if (element.tagName === "CANVAS") { + width = width || element.width; + height = height || element.height; + + //it is already a canvas, we wrap it around with a div + this.canvas = cc._canvas = localCanvas = element; + this.container = cc.container = localContainer = document.createElement("DIV"); + if (localCanvas.parentNode) + localCanvas.parentNode.insertBefore(localContainer, localCanvas); + } else { + //we must make a new canvas and place into this element + if (element.tagName !== "DIV") { + cc.log("Warning: target element is not a DIV or CANVAS"); + } + width = width || element.clientWidth; + height = height || element.clientHeight; + this.canvas = cc._canvas = localCanvas = cc.$(document.createElement("CANVAS")); + this.container = cc.container = localContainer = document.createElement("DIV"); + element.appendChild(localContainer); + } + localContainer.setAttribute('id', 'Cocos2dGameContainer'); + localContainer.appendChild(localCanvas); + this.frame = (localContainer.parentNode === document.body) ? document.documentElement : localContainer.parentNode; + + localCanvas.addClass("gameCanvas"); + localCanvas.setAttribute("width", width || 480); + localCanvas.setAttribute("height", height || 320); + localCanvas.setAttribute("tabindex", 99); + + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + this._renderContext = cc._renderContext = cc.webglContext + = cc.create3DContext(localCanvas, { + 'stencil': true, + 'alpha': false + }); + } + // WebGL context created successfully + if (this._renderContext) { + cc.renderer = cc.rendererWebGL; + win.gl = this._renderContext; // global variable declared in CCMacro.js + cc.renderer.init(); + cc._drawingUtil = new cc.DrawingPrimitiveWebGL(this._renderContext); + cc.textureCache._initializingRenderer(); + cc.glExt = {}; + cc.glExt.instanced_arrays = win.gl.getExtension("ANGLE_instanced_arrays"); + cc.glExt.element_uint = win.gl.getExtension("OES_element_index_uint"); + } else { + cc._renderType = cc.game.RENDER_TYPE_CANVAS; + cc.renderer = cc.rendererCanvas; + this._renderContext = cc._renderContext = new cc.CanvasContextWrapper(localCanvas.getContext("2d")); + cc._drawingUtil = cc.DrawingPrimitiveCanvas ? new cc.DrawingPrimitiveCanvas(this._renderContext) : null; + } + + cc._gameDiv = localContainer; + cc.game.canvas.oncontextmenu = function () { + if (!cc._isContextMenuEnable) return false; + }; + + this.dispatchEvent(this.EVENT_RENDERER_INITED, true); + + this._rendererInitialized = true; + }, + + _initEvents: function () { + var win = window, hidden; + + this._eventHide = this._eventHide || new cc.EventCustom(this.EVENT_HIDE); + this._eventHide.setUserData(this); + this._eventShow = this._eventShow || new cc.EventCustom(this.EVENT_SHOW); + this._eventShow.setUserData(this); + + // register system events + if (this.config[this.CONFIG_KEY.registerSystemEvent]) + cc.inputManager.registerSystemEvent(this.canvas); + + if (!cc.isUndefined(document.hidden)) { + hidden = "hidden"; + } else if (!cc.isUndefined(document.mozHidden)) { + hidden = "mozHidden"; + } else if (!cc.isUndefined(document.msHidden)) { + hidden = "msHidden"; + } else if (!cc.isUndefined(document.webkitHidden)) { + hidden = "webkitHidden"; + } + + var changeList = [ + "visibilitychange", + "mozvisibilitychange", + "msvisibilitychange", + "webkitvisibilitychange", + "qbrowserVisibilityChange" + ]; + var onHidden = function () { + if (cc.eventManager && cc.game._eventHide) + cc.eventManager.dispatchEvent(cc.game._eventHide); + }; + var onShow = function () { + if (cc.eventManager && cc.game._eventShow) + cc.eventManager.dispatchEvent(cc.game._eventShow); + }; + + if (hidden) { + for (var i=0; i -1) { + win.onfocus = function(){ onShow() }; + } + + if ("onpageshow" in window && "onpagehide" in window) { + win.addEventListener("pagehide", onHidden, false); + win.addEventListener("pageshow", onShow, false); + } + + cc.eventManager.addCustomListener(cc.game.EVENT_HIDE, function () { + cc.game.pause(); + }); + cc.eventManager.addCustomListener(cc.game.EVENT_SHOW, function () { + cc.game.resume(); + }); + } +}; +//+++++++++++++++++++++++++something about CCGame end+++++++++++++++++++++++++++++ + +Function.prototype.bind = Function.prototype.bind || function (oThis) { + if (!cc.isFunction(this)) { + // closest thing possible to the ECMAScript 5 + // internal IsCallable function + throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); + } + + var aArgs = Array.prototype.slice.call(arguments, 1), + fToBind = this, + fNOP = function () {}, + fBound = function () { + return fToBind.apply(this instanceof fNOP && oThis + ? this + : oThis, + aArgs.concat(Array.prototype.slice.call(arguments))); + }; + + fNOP.prototype = this.prototype; + fBound.prototype = new fNOP(); + + return fBound; +}; diff --git a/frameworks/cocos2d-html5/CCDebugger.js b/frameworks/cocos2d-html5/CCDebugger.js new file mode 100644 index 0000000..7acfc1c --- /dev/null +++ b/frameworks/cocos2d-html5/CCDebugger.js @@ -0,0 +1,333 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +cc._LogInfos = { + ActionManager_addAction: "cc.ActionManager.addAction(): action must be non-null", + ActionManager_removeAction: "cocos2d: removeAction: Target not found", + ActionManager_removeActionByTag: "cc.ActionManager.removeActionByTag(): an invalid tag", + ActionManager_removeActionByTag_2: "cc.ActionManager.removeActionByTag(): target must be non-null", + ActionManager_getActionByTag: "cc.ActionManager.getActionByTag(): an invalid tag", + ActionManager_getActionByTag_2: "cocos2d : getActionByTag(tag = %s): Action not found", + + configuration_dumpInfo: "cocos2d: **** WARNING **** CC_ENABLE_PROFILERS is defined. Disable it when you finish profiling (from ccConfig.js)", + configuration_loadConfigFile: "Expected 'data' dict, but not found. Config file: %s", + configuration_loadConfigFile_2: "Please load the resource first : %s", + + Director_resume: "cocos2d: Director: Error in gettimeofday", + Director_setProjection: "cocos2d: Director: unrecognized projection", + Director_popToSceneStackLevel: "cocos2d: Director: unrecognized projection", + Director_popToSceneStackLevel_2: "cocos2d: Director: Error in gettimeofday", + Director_popScene: "running scene should not null", + Director_pushScene: "the scene should not null", + + arrayVerifyType: "element type is wrong!", + + Scheduler_scheduleCallbackForTarget: "CCSheduler#scheduleCallback. Callback already scheduled. Updating interval from:%s to %s", + Scheduler_scheduleCallbackForTarget_2: "cc.scheduler.scheduleCallbackForTarget(): callback_fn should be non-null.", + Scheduler_scheduleCallbackForTarget_3: "cc.scheduler.scheduleCallbackForTarget(): target should be non-null.", + Scheduler_pauseTarget: "cc.Scheduler.pauseTarget():target should be non-null", + Scheduler_resumeTarget: "cc.Scheduler.resumeTarget():target should be non-null", + Scheduler_isTargetPaused: "cc.Scheduler.isTargetPaused():target should be non-null", + + Node_getZOrder: "getZOrder is deprecated. Please use getLocalZOrder instead.", + Node_setZOrder: "setZOrder is deprecated. Please use setLocalZOrder instead.", + Node_getRotation: "RotationX != RotationY. Don't know which one to return", + Node_getScale: "ScaleX != ScaleY. Don't know which one to return", + Node_addChild: "An Node can't be added as a child of itself.", + Node_addChild_2: "child already added. It can't be added again", + Node_addChild_3: "child must be non-null", + Node_removeFromParentAndCleanup: "removeFromParentAndCleanup is deprecated. Use removeFromParent instead", + Node_boundingBox: "boundingBox is deprecated. Use getBoundingBox instead", + Node_removeChildByTag: "argument tag is an invalid tag", + Node_removeChildByTag_2: "cocos2d: removeChildByTag(tag = %s): child not found!", + Node_removeAllChildrenWithCleanup: "removeAllChildrenWithCleanup is deprecated. Use removeAllChildren instead", + Node_stopActionByTag: "cc.Node.stopActionBy(): argument tag an invalid tag", + Node_getActionByTag: "cc.Node.getActionByTag(): argument tag is an invalid tag", + Node_resumeSchedulerAndActions: "resumeSchedulerAndActions is deprecated, please use resume instead.", + Node_pauseSchedulerAndActions: "pauseSchedulerAndActions is deprecated, please use pause instead.", + Node__arrayMakeObjectsPerformSelector: "Unknown callback function", + Node_reorderChild: "cc.Node.reorderChild(): child must be non-null", + Node_reorderChild_2: "cc.Node.reorderChild(): this child is not in children list", + Node_runAction: "cc.Node.runAction(): action must be non-null", + Node_schedule: "callback function must be non-null", + Node_schedule_2: "interval must be positive", + Node_initWithTexture: "cocos2d: Could not initialize cc.AtlasNode. Invalid Texture.", + + AtlasNode_updateAtlasValues: "cc.AtlasNode.updateAtlasValues(): Shall be overridden in subclasses", + AtlasNode_initWithTileFile: "", + AtlasNode__initWithTexture: "cocos2d: Could not initialize cc.AtlasNode. Invalid Texture.", + + _EventListenerKeyboard_checkAvailable: "cc._EventListenerKeyboard.checkAvailable(): Invalid EventListenerKeyboard!", + _EventListenerTouchOneByOne_checkAvailable: "cc._EventListenerTouchOneByOne.checkAvailable(): Invalid EventListenerTouchOneByOne!", + _EventListenerTouchAllAtOnce_checkAvailable: "cc._EventListenerTouchAllAtOnce.checkAvailable(): Invalid EventListenerTouchAllAtOnce!", + _EventListenerAcceleration_checkAvailable: "cc._EventListenerAcceleration.checkAvailable(): _onAccelerationEvent must be non-nil", + + EventListener_create: "Invalid parameter.", + + __getListenerID: "Don't call this method if the event is for touch.", + + eventManager__forceAddEventListener: "Invalid scene graph priority!", + eventManager_addListener: "0 priority is forbidden for fixed priority since it's used for scene graph based priority.", + eventManager_removeListeners: "Invalid listener type!", + eventManager_setPriority: "Can't set fixed priority with scene graph based listener.", + eventManager_addListener_2: "Invalid parameters.", + eventManager_addListener_3: "listener must be a cc.EventListener object when adding a fixed priority listener", + eventManager_addListener_4: "The listener has been registered, please don't register it again.", + + LayerMultiplex_initWithLayers: "parameters should not be ending with null in Javascript", + LayerMultiplex_switchTo: "Invalid index in MultiplexLayer switchTo message", + LayerMultiplex_switchToAndReleaseMe: "Invalid index in MultiplexLayer switchTo message", + LayerMultiplex_addLayer: "cc.Layer.addLayer(): layer should be non-null", + + EGLView_setDesignResolutionSize: "Resolution not valid", + EGLView_setDesignResolutionSize_2: "should set resolutionPolicy", + + inputManager_handleTouchesBegin: "The touches is more than MAX_TOUCHES, nUnusedIndex = %s", + + swap: "cc.swap is being modified from original macro, please check usage", + checkGLErrorDebug: "WebGL error %s", + + animationCache__addAnimationsWithDictionary: "cocos2d: cc.AnimationCache: No animations were found in provided dictionary.", + animationCache__addAnimationsWithDictionary_2: "cc.AnimationCache. Invalid animation format", + animationCache_addAnimations: "cc.AnimationCache.addAnimations(): File could not be found", + animationCache__parseVersion1: "cocos2d: cc.AnimationCache: Animation '%s' found in dictionary without any frames - cannot add to animation cache.", + animationCache__parseVersion1_2: "cocos2d: cc.AnimationCache: Animation '%s' refers to frame '%s' which is not currently in the cc.SpriteFrameCache. This frame will not be added to the animation.", + animationCache__parseVersion1_3: "cocos2d: cc.AnimationCache: None of the frames for animation '%s' were found in the cc.SpriteFrameCache. Animation is not being added to the Animation Cache.", + animationCache__parseVersion1_4: "cocos2d: cc.AnimationCache: An animation in your dictionary refers to a frame which is not in the cc.SpriteFrameCache. Some or all of the frames for the animation '%s' may be missing.", + animationCache__parseVersion2: "cocos2d: CCAnimationCache: Animation '%s' found in dictionary without any frames - cannot add to animation cache.", + animationCache__parseVersion2_2: "cocos2d: cc.AnimationCache: Animation '%s' refers to frame '%s' which is not currently in the cc.SpriteFrameCache. This frame will not be added to the animation.", + animationCache_addAnimations_2: "cc.AnimationCache.addAnimations(): Invalid texture file name", + + Sprite_ignoreAnchorPointForPosition: "cc.Sprite.ignoreAnchorPointForPosition(): it is invalid in cc.Sprite when using SpriteBatchNode", + Sprite_setDisplayFrameWithAnimationName: "cc.Sprite.setDisplayFrameWithAnimationName(): Frame not found", + Sprite_setDisplayFrameWithAnimationName_2: "cc.Sprite.setDisplayFrameWithAnimationName(): Invalid frame index", + Sprite_setDisplayFrame: "setDisplayFrame is deprecated, please use setSpriteFrame instead.", + Sprite__updateBlendFunc: "cc.Sprite._updateBlendFunc(): _updateBlendFunc doesn't work when the sprite is rendered using a cc.CCSpriteBatchNode", + Sprite_initWithSpriteFrame: "cc.Sprite.initWithSpriteFrame(): spriteFrame should be non-null", + Sprite_initWithSpriteFrameName: "cc.Sprite.initWithSpriteFrameName(): spriteFrameName should be non-null", + Sprite_initWithSpriteFrameName1: " is null, please check.", + Sprite_initWithFile: "cc.Sprite.initWithFile(): filename should be non-null", + Sprite_setDisplayFrameWithAnimationName_3: "cc.Sprite.setDisplayFrameWithAnimationName(): animationName must be non-null", + Sprite_addChild: "cc.Sprite.addChild(): cc.Sprite only supports cc.Sprites as children when using cc.SpriteBatchNode", + Sprite_addChild_2: "cc.Sprite.addChild(): cc.Sprite only supports a sprite using same texture as children when using cc.SpriteBatchNode", + Sprite_addChild_3: "cc.Sprite.addChild(): child should be non-null", + Sprite_setTexture: "cc.Sprite.texture setter: Batched sprites should use the same texture as the batchnode", + Sprite_updateQuadFromSprite: "cc.SpriteBatchNode.updateQuadFromSprite(): cc.SpriteBatchNode only supports cc.Sprites as children", + Sprite_insertQuadFromSprite: "cc.SpriteBatchNode.insertQuadFromSprite(): cc.SpriteBatchNode only supports cc.Sprites as children", + Sprite_addChild_4: "cc.SpriteBatchNode.addChild(): cc.SpriteBatchNode only supports cc.Sprites as children", + Sprite_addChild_5: "cc.SpriteBatchNode.addChild(): cc.Sprite is not using the same texture", + Sprite_initWithTexture: "Sprite.initWithTexture(): Argument must be non-nil ", + Sprite_setSpriteFrame: "Invalid spriteFrameName", + Sprite_setTexture_2: "Invalid argument: cc.Sprite.texture setter expects a CCTexture2D.", + Sprite_updateQuadFromSprite_2: "cc.SpriteBatchNode.updateQuadFromSprite(): sprite should be non-null", + Sprite_insertQuadFromSprite_2: "cc.SpriteBatchNode.insertQuadFromSprite(): sprite should be non-null", + + SpriteBatchNode_addSpriteWithoutQuad: "cc.SpriteBatchNode.addQuadFromSprite(): SpriteBatchNode only supports cc.Sprites as children", + SpriteBatchNode_increaseAtlasCapacity: "cocos2d: CCSpriteBatchNode: resizing TextureAtlas capacity from %s to %s.", + SpriteBatchNode_increaseAtlasCapacity_2: "cocos2d: WARNING: Not enough memory to resize the atlas", + SpriteBatchNode_reorderChild: "cc.SpriteBatchNode.addChild(): Child doesn't belong to Sprite", + SpriteBatchNode_removeChild: "cc.SpriteBatchNode.addChild(): sprite batch node should contain the child", + SpriteBatchNode_addSpriteWithoutQuad_2: "cc.SpriteBatchNode.addQuadFromSprite(): child should be non-null", + SpriteBatchNode_reorderChild_2: "cc.SpriteBatchNode.addChild(): child should be non-null", + + spriteFrameCache__getFrameConfig: "cocos2d: WARNING: originalWidth/Height not found on the cc.SpriteFrame. AnchorPoint won't work as expected. Regenrate the .plist", + spriteFrameCache_addSpriteFrames: "cocos2d: WARNING: an alias with name %s already exists", + spriteFrameCache__checkConflict: "cocos2d: WARNING: Sprite frame: %s has already been added by another source, please fix name conflit", + spriteFrameCache_getSpriteFrame: "cocos2d: cc.SpriteFrameCahce: Frame %s not found", + spriteFrameCache__getFrameConfig_2: "Please load the resource first : %s", + spriteFrameCache_addSpriteFrames_2: "cc.SpriteFrameCache.addSpriteFrames(): plist should be non-null", + spriteFrameCache_addSpriteFrames_3: "Argument must be non-nil", + + CCSpriteBatchNode_updateQuadFromSprite: "cc.SpriteBatchNode.updateQuadFromSprite(): cc.SpriteBatchNode only supports cc.Sprites as children", + CCSpriteBatchNode_insertQuadFromSprite: "cc.SpriteBatchNode.insertQuadFromSprite(): cc.SpriteBatchNode only supports cc.Sprites as children", + CCSpriteBatchNode_addChild: "cc.SpriteBatchNode.addChild(): cc.SpriteBatchNode only supports cc.Sprites as children", + CCSpriteBatchNode_initWithTexture: "Sprite.initWithTexture(): Argument must be non-nil ", + CCSpriteBatchNode_addChild_2: "cc.Sprite.addChild(): child should be non-null", + CCSpriteBatchNode_setSpriteFrame: "Invalid spriteFrameName", + CCSpriteBatchNode_setTexture: "Invalid argument: cc.Sprite texture setter expects a CCTexture2D.", + CCSpriteBatchNode_updateQuadFromSprite_2: "cc.SpriteBatchNode.updateQuadFromSprite(): sprite should be non-null", + CCSpriteBatchNode_insertQuadFromSprite_2: "cc.SpriteBatchNode.insertQuadFromSprite(): sprite should be non-null", + CCSpriteBatchNode_addChild_3: "cc.SpriteBatchNode.addChild(): child should be non-null", + + TextureAtlas_initWithFile: "cocos2d: Could not open file: %s", + TextureAtlas_insertQuad: "cc.TextureAtlas.insertQuad(): invalid totalQuads", + TextureAtlas_initWithTexture: "cc.TextureAtlas.initWithTexture():texture should be non-null", + TextureAtlas_updateQuad: "cc.TextureAtlas.updateQuad(): quad should be non-null", + TextureAtlas_updateQuad_2: "cc.TextureAtlas.updateQuad(): Invalid index", + TextureAtlas_insertQuad_2: "cc.TextureAtlas.insertQuad(): Invalid index", + TextureAtlas_insertQuads: "cc.TextureAtlas.insertQuad(): Invalid index + amount", + TextureAtlas_insertQuadFromIndex: "cc.TextureAtlas.insertQuadFromIndex(): Invalid newIndex", + TextureAtlas_insertQuadFromIndex_2: "cc.TextureAtlas.insertQuadFromIndex(): Invalid fromIndex", + TextureAtlas_removeQuadAtIndex: "cc.TextureAtlas.removeQuadAtIndex(): Invalid index", + TextureAtlas_removeQuadsAtIndex: "cc.TextureAtlas.removeQuadsAtIndex(): index + amount out of bounds", + TextureAtlas_moveQuadsFromIndex: "cc.TextureAtlas.moveQuadsFromIndex(): move is out of bounds", + TextureAtlas_moveQuadsFromIndex_2: "cc.TextureAtlas.moveQuadsFromIndex(): Invalid newIndex", + TextureAtlas_moveQuadsFromIndex_3: "cc.TextureAtlas.moveQuadsFromIndex(): Invalid oldIndex", + + textureCache_addPVRTCImage: "TextureCache:addPVRTCImage does not support on HTML5", + textureCache_addETCImage: "TextureCache:addPVRTCImage does not support on HTML5", + textureCache_textureForKey: "textureForKey is deprecated. Please use getTextureForKey instead.", + textureCache_addPVRImage: "addPVRImage does not support on HTML5", + textureCache_addUIImage: "cocos2d: Couldn't add UIImage in TextureCache", + textureCache_dumpCachedTextureInfo: "cocos2d: '%s' id=%s %s x %s", + textureCache_dumpCachedTextureInfo_2: "cocos2d: '%s' id= HTMLCanvasElement %s x %s", + textureCache_dumpCachedTextureInfo_3: "cocos2d: TextureCache dumpDebugInfo: %s textures, HTMLCanvasElement for %s KB (%s MB)", + textureCache_addUIImage_2: "cc.Texture.addUIImage(): image should be non-null", + + Texture2D_initWithETCFile: "initWithETCFile does not support on HTML5", + Texture2D_initWithPVRFile: "initWithPVRFile does not support on HTML5", + Texture2D_initWithPVRTCData: "initWithPVRTCData does not support on HTML5", + Texture2D_addImage: "cc.Texture.addImage(): path should be non-null", + Texture2D_initWithImage: "cocos2d: cc.Texture2D. Can't create Texture. UIImage is nil", + Texture2D_initWithImage_2: "cocos2d: WARNING: Image (%s x %s) is bigger than the supported %s x %s", + Texture2D_initWithString: "initWithString isn't supported on cocos2d-html5", + Texture2D_initWithETCFile_2: "initWithETCFile does not support on HTML5", + Texture2D_initWithPVRFile_2: "initWithPVRFile does not support on HTML5", + Texture2D_initWithPVRTCData_2: "initWithPVRTCData does not support on HTML5", + Texture2D_bitsPerPixelForFormat: "bitsPerPixelForFormat: %s, cannot give useful result, it's a illegal pixel format", + Texture2D__initPremultipliedATextureWithImage: "cocos2d: cc.Texture2D: Using RGB565 texture since image has no alpha", + Texture2D_addImage_2: "cc.Texture.addImage(): path should be non-null", + Texture2D_initWithData: "NSInternalInconsistencyException", + + MissingFile: "Missing file: %s", + radiansToDegress: "cc.radiansToDegress() should be called cc.radiansToDegrees()", + RectWidth: "Rect width exceeds maximum margin: %s", + RectHeight: "Rect height exceeds maximum margin: %s", + + EventManager__updateListeners: "If program goes here, there should be event in dispatch.", + EventManager__updateListeners_2: "_inDispatch should be 1 here." +}; + +//+++++++++++++++++++++++++something about log start++++++++++++++++++++++++++++ +cc._logToWebPage = function (msg) { + if (!cc._canvas) + return; + + var logList = cc._logList; + var doc = document; + if (!logList) { + var logDiv = doc.createElement("Div"); + var logDivStyle = logDiv.style; + + logDiv.setAttribute("id", "logInfoDiv"); + cc._canvas.parentNode.appendChild(logDiv); + logDiv.setAttribute("width", "200"); + logDiv.setAttribute("height", cc._canvas.height); + logDivStyle.zIndex = "99999"; + logDivStyle.position = "absolute"; + logDivStyle.top = "0"; + logDivStyle.left = "0"; + + logList = cc._logList = doc.createElement("textarea"); + var logListStyle = logList.style; + + logList.setAttribute("rows", "20"); + logList.setAttribute("cols", "30"); + logList.setAttribute("disabled", true); + logDiv.appendChild(logList); + logListStyle.backgroundColor = "transparent"; + logListStyle.borderBottom = "1px solid #cccccc"; + logListStyle.borderRightWidth = "0px"; + logListStyle.borderLeftWidth = "0px"; + logListStyle.borderTopWidth = "0px"; + logListStyle.borderTopStyle = "none"; + logListStyle.borderRightStyle = "none"; + logListStyle.borderLeftStyle = "none"; + logListStyle.padding = "0px"; + logListStyle.margin = 0; + + } + logList.value = logList.value + msg + "\r\n"; + logList.scrollTop = logList.scrollHeight; +}; + +//to make sure the cc.log, cc.warn, cc.error and cc.assert would not throw error before init by debugger mode. +cc._formatString = function (arg) { + if (cc.isObject(arg)) { + try { + return JSON.stringify(arg); + } catch (err) { + return ""; + } + } else + return arg; +}; +/** + * Init Debug setting. + * @function + * @param {Number} mode + */ +cc._initDebugSetting = function (mode) { + var ccGame = cc.game; + if(mode === ccGame.DEBUG_MODE_NONE) + return; + + var locLog; + if(mode > ccGame.DEBUG_MODE_ERROR){ + //log to web page + locLog = cc._logToWebPage.bind(cc); + cc.error = function(){ + locLog("ERROR : " + cc.formatStr.apply(cc, arguments)); + }; + cc.assert = function(cond, msg) { + if (!cond && msg) { + for (var i = 2; i < arguments.length; i++) + msg = msg.replace(/(%s)|(%d)/, cc._formatString(arguments[i])); + locLog("Assert: " + msg); + } + }; + if(mode !== ccGame.DEBUG_MODE_ERROR_FOR_WEB_PAGE){ + cc.warn = function(){ + locLog("WARN : " + cc.formatStr.apply(cc, arguments)); + }; + } + if(mode === ccGame.DEBUG_MODE_INFO_FOR_WEB_PAGE){ + cc.log = function(){ + locLog(cc.formatStr.apply(cc, arguments)); + }; + } + } else if(console && console.log.apply){//console is null when user doesn't open dev tool on IE9 + //log to console + + cc.error = Function.prototype.bind.call(console.error, console); + //If console.assert is not support user throw Error msg on wrong condition + if (console.assert) { + cc.assert = Function.prototype.bind.call(console.assert, console); + } else { + cc.assert = function (cond, msg) { + if (!cond && msg) { + for (var i = 2; i < arguments.length; i++) + msg = msg.replace(/(%s)|(%d)/, cc._formatString(arguments[i])); + throw new Error(msg); + } + }; + } + if (mode !== ccGame.DEBUG_MODE_ERROR) + cc.warn = Function.prototype.bind.call(console.warn, console); + if (mode === ccGame.DEBUG_MODE_INFO) + cc.log = Function.prototype.bind.call(console.log, console); + } +}; +//+++++++++++++++++++++++++something about log end+++++++++++++++++++++++++++++ diff --git a/frameworks/cocos2d-html5/bower.json b/frameworks/cocos2d-html5/bower.json new file mode 100644 index 0000000..e97d56b --- /dev/null +++ b/frameworks/cocos2d-html5/bower.json @@ -0,0 +1,36 @@ +{ + "name": "cocos2d-html5", + "homepage": "http://www.cocos2d-x.org", + "authors": [ + "AUTHORS.txt" + ], + "description": "Cocos2d-html5 is a cross-platform 2D game engine written in Javascript, based on Cocos2d-X and licensed under MIT. It incorporates the same high level api as “Cocos2d JS-binding engine” and compatible with Cocos2d-X. It currently supports canvas and WebGL renderering.", + "main": "README.mdown", + "keywords": [ + "cocos2d-x", + "cocos2d", + "game", + "engine", + "opengl", + "cross", + "multi", + "platform", + "iphone", + "ipad", + "android", + "windows", + "metro", + "bada", + "marmalade", + "playbook" + ], + "license": "MIT", + "private": false, + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] +} diff --git a/frameworks/cocos2d-html5/cocos2d/actions/CCAction.js b/frameworks/cocos2d-html5/cocos2d/actions/CCAction.js new file mode 100644 index 0000000..e548782 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/actions/CCAction.js @@ -0,0 +1,694 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** Default Action tag + * @constant + * @type {Number} + * @default + */ +cc.ACTION_TAG_INVALID = -1; + +/** + * Base class for cc.Action objects. + * @class + * + * @extends cc.Class + * + * @property {cc.Node} target - The target will be set with the 'startWithTarget' method. When the 'stop' method is called, target will be set to nil. + * @property {cc.Node} originalTarget - The original target of the action. + * @property {Number} tag - The tag of the action, can be used to find the action. + */ +cc.Action = cc.Class.extend(/** @lends cc.Action# */{ + //***********variables************* + originalTarget: null, + target: null, + tag: cc.ACTION_TAG_INVALID, + + //**************Public Functions*********** + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + */ + ctor: function () { + this.originalTarget = null; + this.target = null; + this.tag = cc.ACTION_TAG_INVALID; + }, + + /** + * to copy object with deep copy. + * + * @deprecated since v3.0 please use .clone + * + * @return {cc.Action} + */ + copy: function () { + cc.log("copy is deprecated. Please use clone instead."); + return this.clone(); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @return {cc.Action} + */ + clone: function () { + var action = new cc.Action(); + action.originalTarget = null; + action.target = null; + action.tag = this.tag; + return action; + }, + + /** + * return true if the action has finished. + * + * @return {Boolean} + */ + isDone: function () { + return true; + }, + + /** + * called before the action start. It will also set the target. + * + * @param {cc.Node} target + */ + startWithTarget: function (target) { + this.originalTarget = target; + this.target = target; + }, + + /** + * called after the action has finished. It will set the 'target' to nil.
+ * IMPORTANT: You should never call "action stop" manually. Instead, use: "target.stopAction(action);" + */ + stop: function () { + this.target = null; + }, + + /** + * called every frame with it's delta time.
+ * DON'T override unless you know what you are doing. + * + * @param {Number} dt + */ + step: function (dt) { + cc.log("[Action step]. override me"); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update: function (dt) { + cc.log("[Action update]. override me"); + }, + + /** + * get the target. + * + * @return {cc.Node} + */ + getTarget: function () { + return this.target; + }, + + /** + * The action will modify the target properties. + * + * @param {cc.Node} target + */ + setTarget: function (target) { + this.target = target; + }, + + /** + * get the original target. + * + * @return {cc.Node} + */ + getOriginalTarget: function () { + return this.originalTarget; + }, + + /** + * Set the original target, since target can be nil.
+ * Is the target that were used to run the action.
+ * Unless you are doing something complex, like cc.ActionManager, you should NOT call this method.
+ * The target is 'assigned', it is not 'retained'.
+ * @param {cc.Node} originalTarget + */ + setOriginalTarget: function (originalTarget) { + this.originalTarget = originalTarget; + }, + + /** + * get tag number. + * @return {Number} + */ + getTag: function () { + return this.tag; + }, + + /** + * set tag number. + * @param {Number} tag + */ + setTag: function (tag) { + this.tag = tag; + }, + + /** + * Currently JavaScript Bindings (JSB), in some cases, needs to use retain and release. This is a bug in JSB,
+ * and the ugly workaround is to use retain/release. So, these 2 methods were added to be compatible with JSB.
+ * This is a hack, and should be removed once JSB fixes the retain/release bug. + */ + retain: function () { + }, + + /** + * Currently JavaScript Bindings (JSB), in some cases, needs to use retain and release. This is a bug in JSB,
+ * and the ugly workaround is to use retain/release. So, these 2 methods were added to be compatible with JSB.
+ * This is a hack, and should be removed once JSB fixes the retain/release bug. + */ + release: function () { + } +}); + +/** + * Allocates and initializes the action. + * + * @function cc.action + * @static + * @return {cc.Action} + * + * @example + * // return {cc.Action} + * var action = cc.action(); + */ +cc.action = function () { + return new cc.Action(); +}; + +/** + * Please use cc.action instead.
+ * Allocates and initializes the action. + * + * @deprecated since v3.0 please use cc.action() instead. + * @static + * @returns {cc.Action} + */ +cc.Action.create = cc.action; + + +/** + * Base class actions that do have a finite time duration.
+ * Possible actions:
+ * - An action with a duration of 0 seconds.
+ * - An action with a duration of 35.5 seconds. + * + * Infinite time actions are valid + * @class + * @extends cc.Action + */ +cc.FiniteTimeAction = cc.Action.extend(/** @lends cc.FiniteTimeAction# */{ + // duration in seconds + _duration: 0, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + */ + ctor: function () { + cc.Action.prototype.ctor.call(this); + this._duration = 0; + }, + + /** + * get duration of the action. (seconds) + * + * @return {Number} + */ + getDuration: function () { + return this._duration * (this._timesForRepeat || 1); + }, + + /** + * set duration of the action. (seconds) + * + * @param {Number} duration + */ + setDuration: function (duration) { + this._duration = duration; + }, + + /** + * Returns a reversed action.
+ * For example:
+ * - The action will be x coordinates of 0 move to 100.
+ * - The reversed action will be x of 100 move to 0. + * - Will be rewritten + * + * @return {?cc.Action} + */ + reverse: function () { + cc.log("cocos2d: FiniteTimeAction#reverse: Implement me"); + return null; + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @return {cc.FiniteTimeAction} + */ + clone: function () { + return new cc.FiniteTimeAction(); + } +}); + +/** + * Changes the speed of an action, making it take longer (speed > 1) + * or less (speed < 1) time.
+ * Useful to simulate 'slow motion' or 'fast forward' effect. + * + * @warning This action can't be Sequenceable because it is not an cc.IntervalAction + * @class + * @extends cc.Action + * @param {cc.ActionInterval} action + * @param {Number} speed + */ +cc.Speed = cc.Action.extend(/** @lends cc.Speed# */{ + _speed: 0.0, + _innerAction: null, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {cc.ActionInterval} action + * @param {Number} speed + */ + ctor: function (action, speed) { + cc.Action.prototype.ctor.call(this); + this._speed = 0; + this._innerAction = null; + + action && this.initWithAction(action, speed); + }, + + /** + * Gets the current running speed.
+ * Will get a percentage number, compared to the original speed. + * + * @return {Number} + */ + getSpeed: function () { + return this._speed; + }, + + /** + * alter the speed of the inner function in runtime. + * + * @param {Number} speed + */ + setSpeed: function (speed) { + this._speed = speed; + }, + + /** + * initializes the action. + * + * @param {cc.ActionInterval} action + * @param {Number} speed + * @return {Boolean} + */ + initWithAction: function (action, speed) { + if (!action) + throw new Error("cc.Speed.initWithAction(): action must be non nil"); + + this._innerAction = action; + this._speed = speed; + return true; + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.Speed} + */ + clone: function () { + var action = new cc.Speed(); + action.initWithAction(this._innerAction.clone(), this._speed); + return action; + }, + + /** + * called before the action start. It will also set the target. + * + * @param {cc.Node} target + */ + startWithTarget: function (target) { + cc.Action.prototype.startWithTarget.call(this, target); + this._innerAction.startWithTarget(target); + }, + + /** + * Stop the action. + */ + stop: function () { + this._innerAction.stop(); + cc.Action.prototype.stop.call(this); + }, + + /** + * called every frame with it's delta time.
+ * DON'T override unless you know what you are doing. + * + * @param {Number} dt + */ + step: function (dt) { + this._innerAction.step(dt * this._speed); + }, + + /** + * return true if the action has finished. + * + * @return {Boolean} + */ + isDone: function () { + return this._innerAction.isDone(); + }, + + /** + * returns a reversed action.
+ * For example:
+ * - The action will be x coordinates of 0 move to 100.
+ * - The reversed action will be x of 100 move to 0. + * - Will be rewritten + * + * @return {cc.Speed} + */ + reverse: function () { + return new cc.Speed(this._innerAction.reverse(), this._speed); + }, + + /** + * Set inner Action. + * @param {cc.ActionInterval} action + */ + setInnerAction: function (action) { + if (this._innerAction !== action) { + this._innerAction = action; + } + }, + + /** + * Get inner Action. + * + * @return {cc.ActionInterval} + */ + getInnerAction: function () { + return this._innerAction; + } +}); + +/** + * creates the speed action. + * + * @function cc.speed + * @param {cc.ActionInterval} action + * @param {Number} speed + * @return {cc.Speed} + */ +cc.speed = function (action, speed) { + return new cc.Speed(action, speed); +}; + +/** + * Please use cc.speed instead. + * creates the action. + * + * @param {cc.ActionInterval} action + * @param {Number} speed + * @return {cc.Speed} + * @static + * @deprecated since v3.0 please use cc.speed() instead. + */ +cc.Speed.create = cc.speed; + +/** + * cc.Follow is an action that "follows" a node. + * + * @example + * //example + * //Instead of using cc.Camera as a "follower", use this action instead. + * layer.runAction(cc.follow(hero)); + * + * @property {Number} leftBoundary - world leftBoundary. + * @property {Number} rightBoundary - world rightBoundary. + * @property {Number} topBoundary - world topBoundary. + * @property {Number} bottomBoundary - world bottomBoundary. + * + * @param {cc.Node} followedNode + * @param {cc.Rect} rect + * @example + * // creates the action with a set boundary + * var sprite = new cc.Sprite("spriteFileName"); + * var followAction = new cc.Follow(sprite, cc.rect(0, 0, s.width * 2 - 100, s.height)); + * this.runAction(followAction); + * + * // creates the action with no boundary set + * var sprite = new cc.Sprite("spriteFileName"); + * var followAction = new cc.Follow(sprite); + * this.runAction(followAction); + * + * @class + * @extends cc.Action + */ +cc.Follow = cc.Action.extend(/** @lends cc.Follow# */{ + // node to follow + _followedNode: null, + // whether camera should be limited to certain area + _boundarySet: false, + // if screen size is bigger than the boundary - update not needed + _boundaryFullyCovered: false, + // fast access to the screen dimensions + _halfScreenSize: null, + _fullScreenSize: null, + _worldRect: null, + + leftBoundary: 0.0, + rightBoundary: 0.0, + topBoundary: 0.0, + bottomBoundary: 0.0, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * creates the action with a set boundary.
+ * creates the action with no boundary set. + * @param {cc.Node} followedNode + * @param {cc.Rect} rect + */ + ctor: function (followedNode, rect) { + cc.Action.prototype.ctor.call(this); + this._followedNode = null; + this._boundarySet = false; + + this._boundaryFullyCovered = false; + this._halfScreenSize = null; + this._fullScreenSize = null; + + this.leftBoundary = 0.0; + this.rightBoundary = 0.0; + this.topBoundary = 0.0; + this.bottomBoundary = 0.0; + this._worldRect = cc.rect(0, 0, 0, 0); + + if (followedNode) + rect ? this.initWithTarget(followedNode, rect) + : this.initWithTarget(followedNode); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @return {cc.Follow} + */ + clone: function () { + var action = new cc.Follow(); + var locRect = this._worldRect; + var rect = new cc.Rect(locRect.x, locRect.y, locRect.width, locRect.height); + action.initWithTarget(this._followedNode, rect); + return action; + }, + + /** + * Get whether camera should be limited to certain area. + * + * @return {Boolean} + */ + isBoundarySet: function () { + return this._boundarySet; + }, + + /** + * alter behavior - turn on/off boundary. + * + * @param {Boolean} value + */ + setBoudarySet: function (value) { + this._boundarySet = value; + }, + + /** + * initializes the action with a set boundary. + * + * @param {cc.Node} followedNode + * @param {cc.Rect} [rect=] + * @return {Boolean} + */ + initWithTarget: function (followedNode, rect) { + if (!followedNode) + throw new Error("cc.Follow.initWithAction(): followedNode must be non nil"); + + var _this = this; + rect = rect || cc.rect(0, 0, 0, 0); + _this._followedNode = followedNode; + _this._worldRect = rect; + + _this._boundarySet = !cc._rectEqualToZero(rect); + + _this._boundaryFullyCovered = false; + + var winSize = cc.director.getWinSize(); + _this._fullScreenSize = cc.p(winSize.width, winSize.height); + _this._halfScreenSize = cc.pMult(_this._fullScreenSize, 0.5); + + if (_this._boundarySet) { + _this.leftBoundary = -((rect.x + rect.width) - _this._fullScreenSize.x); + _this.rightBoundary = -rect.x; + _this.topBoundary = -rect.y; + _this.bottomBoundary = -((rect.y + rect.height) - _this._fullScreenSize.y); + + if (_this.rightBoundary < _this.leftBoundary) { + // screen width is larger than world's boundary width + //set both in the middle of the world + _this.rightBoundary = _this.leftBoundary = (_this.leftBoundary + _this.rightBoundary) / 2; + } + if (_this.topBoundary < _this.bottomBoundary) { + // screen width is larger than world's boundary width + //set both in the middle of the world + _this.topBoundary = _this.bottomBoundary = (_this.topBoundary + _this.bottomBoundary) / 2; + } + + if ((_this.topBoundary === _this.bottomBoundary) && (_this.leftBoundary === _this.rightBoundary)) + _this._boundaryFullyCovered = true; + } + return true; + }, + + /** + * called every frame with it's delta time.
+ * DON'T override unless you know what you are doing. + * + * @param {Number} dt + */ + step: function (dt) { + var tempPosX = this._followedNode.x; + var tempPosY = this._followedNode.y; + tempPosX = this._halfScreenSize.x - tempPosX; + tempPosY = this._halfScreenSize.y - tempPosY; + + //TODO Temporary treatment - The dirtyFlag symbol error + this.target._renderCmd._dirtyFlag = 0; + + if (this._boundarySet) { + // whole map fits inside a single screen, no need to modify the position - unless map boundaries are increased + if (this._boundaryFullyCovered) + return; + + this.target.setPosition(cc.clampf(tempPosX, this.leftBoundary, this.rightBoundary), cc.clampf(tempPosY, this.bottomBoundary, this.topBoundary)); + } else { + this.target.setPosition(tempPosX, tempPosY); + } + }, + + /** + * Return true if the action has finished. + * + * @return {Boolean} + */ + isDone: function () { + return ( !this._followedNode.running ); + }, + + /** + * Stop the action. + */ + stop: function () { + this.target = null; + cc.Action.prototype.stop.call(this); + } +}); + +/** + * creates the action with a set boundary.
+ * creates the action with no boundary set. + * + * @function + * @param {cc.Node} followedNode + * @param {cc.Rect} rect + * @return {cc.Follow|Null} returns the cc.Follow object on success + * @example + * // example + * // creates the action with a set boundary + * var sprite = new cc.Sprite("spriteFileName"); + * var followAction = cc.follow(sprite, cc.rect(0, 0, s.width * 2 - 100, s.height)); + * this.runAction(followAction); + * + * // creates the action with no boundary set + * var sprite = new cc.Sprite("spriteFileName"); + * var followAction = cc.follow(sprite); + * this.runAction(followAction); + */ +cc.follow = function (followedNode, rect) { + return new cc.Follow(followedNode, rect); +}; + +/** + * Please use cc.follow instead. + * creates the action with a set boundary.
+ * creates the action with no boundary set. + * @param {cc.Node} followedNode + * @param {cc.Rect} rect + * @return {cc.Follow|Null} returns the cc.Follow object on success + * @static + * @deprecated since v3.0 please cc.follow() instead. + */ +cc.Follow.create = cc.follow; diff --git a/frameworks/cocos2d-html5/cocos2d/actions/CCActionCatmullRom.js b/frameworks/cocos2d-html5/cocos2d/actions/CCActionCatmullRom.js new file mode 100644 index 0000000..a17dca4 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/actions/CCActionCatmullRom.js @@ -0,0 +1,612 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2008 Radu Gruian + Copyright (c) 2011 Vit Valentin + + http://www.cocos2d-x.org + + 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. + + Orignal code by Radu Gruian: http://www.codeproject.com/Articles/30838/Overhauser-Catmull-Rom-Splines-for-Camera-Animatio.So + + Adapted to cocos2d-x by Vit Valentin + + Adapted from cocos2d-x to cocos2d-iphone by Ricardo Quesada + ****************************************************************************/ + +/** + * Returns the Cardinal Spline position for a given set of control points, tension and time.
+ * CatmullRom Spline formula.
+ * s(-ttt + 2tt - t)P1 + s(-ttt + tt)P2 + (2ttt - 3tt + 1)P2 + s(ttt - 2tt + t)P3 + (-2ttt + 3tt)P3 + s(ttt - tt)P4 + * + * @function + * @param {cc.Point} p0 + * @param {cc.Point} p1 + * @param {cc.Point} p2 + * @param {cc.Point} p3 + * @param {Number} tension + * @param {Number} t + * @param {cc.Point} [out] + * @return {cc.Point} + */ +cc.cardinalSplineAt = function (p0, p1, p2, p3, tension, t, out) { + var t2 = t * t; + var t3 = t2 * t; + + /* + * Formula: s(-ttt + 2tt - t)P1 + s(-ttt + tt)P2 + (2ttt - 3tt + 1)P2 + s(ttt - 2tt + t)P3 + (-2ttt + 3tt)P3 + s(ttt - tt)P4 + */ + var s = (1 - tension) / 2; + + var b1 = s * ((-t3 + (2 * t2)) - t); // s(-t3 + 2 t2 - t)P1 + var b2 = s * (-t3 + t2) + (2 * t3 - 3 * t2 + 1); // s(-t3 + t2)P2 + (2 t3 - 3 t2 + 1)P2 + var b3 = s * (t3 - 2 * t2 + t) + (-2 * t3 + 3 * t2); // s(t3 - 2 t2 + t)P3 + (-2 t3 + 3 t2)P3 + var b4 = s * (t3 - t2); // s(t3 - t2)P4 + + var x = (p0.x * b1 + p1.x * b2 + p2.x * b3 + p3.x * b4); + var y = (p0.y * b1 + p1.y * b2 + p2.y * b3 + p3.y * b4); + if (out !== undefined) { + out.x = x; + out.y = y; + } + else { + return cc.p(x, y); + } +}; + +/** + * returns a new copy of the array reversed. + * + * @return {Array} + */ +cc.reverseControlPoints = function (controlPoints) { + var newArray = []; + for (var i = controlPoints.length - 1; i >= 0; i--) { + newArray.push(cc.p(controlPoints[i].x, controlPoints[i].y)); + } + return newArray; +}; + + +/** + * returns a new clone of the controlPoints + * + * @param controlPoints + * @returns {Array} + */ +cc.cloneControlPoints = function (controlPoints) { + var newArray = []; + for (var i = 0; i < controlPoints.length; i++) + newArray.push(cc.p(controlPoints[i].x, controlPoints[i].y)); + return newArray; +}; + +/** + * returns a new clone of the controlPoints + * @deprecated since v3.0 please use cc.cloneControlPoints() instead. + * @param controlPoints + * @returns {Array} + */ +cc.copyControlPoints = cc.cloneControlPoints; + +/** + * returns a point from the array + * + * @param {Array} controlPoints + * @param {Number} pos + * @return {Array} + */ +cc.getControlPointAt = function (controlPoints, pos) { + var p = Math.min(controlPoints.length - 1, Math.max(pos, 0)); + return controlPoints[p]; +}; + +/** + * reverse the current control point array inline, without generating a new one
+ * + * @param controlPoints + */ +cc.reverseControlPointsInline = function (controlPoints) { + var len = controlPoints.length; + var mid = 0 | (len / 2); + for (var i = 0; i < mid; ++i) { + var temp = controlPoints[i]; + controlPoints[i] = controlPoints[len - i - 1]; + controlPoints[len - i - 1] = temp; + } +}; + + +/** + * Cardinal Spline path. {@link http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline} + * Absolute coordinates. + * + * @class + * @extends cc.ActionInterval + * @param {Number} duration + * @param {Array} points array of control points + * @param {Number} tension + * + * @example + * //create a cc.CardinalSplineTo + * var action1 = cc.cardinalSplineTo(3, array, 0); + */ +cc.CardinalSplineTo = cc.ActionInterval.extend(/** @lends cc.CardinalSplineTo# */{ + /** Array of control points */ + _points:null, + _deltaT:0, + _tension:0, + _previousPosition:null, + _accumulatedDiff:null, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Creates an action with a Cardinal Spline array of points and tension. + * @param {Number} duration + * @param {Array} points array of control points + * @param {Number} tension + */ + ctor: function (duration, points, tension) { + cc.ActionInterval.prototype.ctor.call(this); + + this._points = []; + tension !== undefined && this.initWithDuration(duration, points, tension); + }, + + /** + * initializes the action with a duration and an array of points + * + * @param {Number} duration + * @param {Array} points array of control points + * @param {Number} tension + * + * @return {Boolean} + */ + initWithDuration:function (duration, points, tension) { + if(!points || points.length === 0) + throw new Error("Invalid configuration. It must at least have one control point"); + + if (cc.ActionInterval.prototype.initWithDuration.call(this, duration)) { + this.setPoints(points); + this._tension = tension; + return true; + } + return false; + }, + + /** + * returns a new clone of the action + * + * @returns {cc.CardinalSplineTo} + */ + clone:function () { + var action = new cc.CardinalSplineTo(); + action.initWithDuration(this._duration, cc.copyControlPoints(this._points), this._tension); + return action; + }, + + /** + * called before the action start. It will also set the target. + * + * @param {cc.Node} target + */ + startWithTarget:function (target) { + cc.ActionInterval.prototype.startWithTarget.call(this, target); + // Issue #1441 from cocos2d-iphone + this._deltaT = 1 / (this._points.length - 1); + this._previousPosition = cc.p(this.target.getPositionX(), this.target.getPositionY()); + this._accumulatedDiff = cc.p(0, 0); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + dt = this._computeEaseTime(dt); + var p, lt; + var ps = this._points; + // eg. + // p..p..p..p..p..p..p + // 1..2..3..4..5..6..7 + // want p to be 1, 2, 3, 4, 5, 6 + if (dt === 1) { + p = ps.length - 1; + lt = 1; + } else { + var locDT = this._deltaT; + p = 0 | (dt / locDT); + lt = (dt - locDT * p) / locDT; + } + + var newPos = cc.cardinalSplineAt( + cc.getControlPointAt(ps, p - 1), + cc.getControlPointAt(ps, p - 0), + cc.getControlPointAt(ps, p + 1), + cc.getControlPointAt(ps, p + 2), + this._tension, lt); + + if (cc.ENABLE_STACKABLE_ACTIONS) { + var tempX, tempY; + tempX = this.target.getPositionX() - this._previousPosition.x; + tempY = this.target.getPositionY() - this._previousPosition.y; + if (tempX !== 0 || tempY !== 0) { + var locAccDiff = this._accumulatedDiff; + tempX = locAccDiff.x + tempX; + tempY = locAccDiff.y + tempY; + locAccDiff.x = tempX; + locAccDiff.y = tempY; + newPos.x += tempX; + newPos.y += tempY; + } + } + this.updatePosition(newPos); + }, + + /** + * reverse a new cc.CardinalSplineTo.
+ * Along the track of movement in the opposite. + * + * @return {cc.CardinalSplineTo} + */ + reverse:function () { + var reversePoints = cc.reverseControlPoints(this._points); + return cc.cardinalSplineTo(this._duration, reversePoints, this._tension); + }, + + /** + * update position of target + * + * @param {cc.Point} newPos + */ + updatePosition:function (newPos) { + this.target.setPosition(newPos); + this._previousPosition = newPos; + }, + + /** + * Points getter + * + * @return {Array} + */ + getPoints:function () { + return this._points; + }, + + /** + * Points setter + * + * @param {Array} points + */ + setPoints:function (points) { + this._points = points; + } +}); + +/** + * creates an action with a Cardinal Spline array of points and tension. + * + * @function + * @param {Number} duration + * @param {Array} points array of control points + * @param {Number} tension + * @return {cc.CardinalSplineTo} + * + * @example + * //create a cc.CardinalSplineTo + * var action1 = cc.cardinalSplineTo(3, array, 0); + */ +cc.cardinalSplineTo = function (duration, points, tension) { + return new cc.CardinalSplineTo(duration, points, tension); +}; + +/** + * Please use cc.cardinalSplineTo instead.
+ * creates an action with a Cardinal Spline array of points and tension + * + * @function + * @param {Number} duration + * @param {Array} points array of control points + * @param {Number} tension + * @return {cc.CardinalSplineTo} + * @static + * @deprecated since v3.0 please use cc.cardinalSplineTo(duration, points, tension) instead. + */ +cc.CardinalSplineTo.create = cc.cardinalSplineTo; + +/** + * Cardinal Spline path. {@link http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Cardinal_spline} + * Relative coordinates. + * + * @class + * @extends cc.CardinalSplineTo + * @param {Number} duration + * @param {Array} points + * @param {Number} tension + * + * @example + * //create a cc.CardinalSplineBy + * var action1 = cc.cardinalSplineBy(3, array, 0); + */ +cc.CardinalSplineBy = cc.CardinalSplineTo.extend(/** @lends cc.CardinalSplineBy# */{ + _startPosition:null, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * creates an action with a Cardinal Spline array of points and tension. + * @param {Number} duration + * @param {Array} points + * @param {Number} tension + */ + ctor:function (duration, points, tension) { + cc.CardinalSplineTo.prototype.ctor.call(this); + this._startPosition = cc.p(0, 0); + + tension !== undefined && this.initWithDuration(duration, points, tension); + }, + + /** + * called before the action start. It will also set the target. + * + * @param {cc.Node} target + */ + startWithTarget:function (target) { + cc.CardinalSplineTo.prototype.startWithTarget.call(this, target); + this._startPosition.x = target.getPositionX(); + this._startPosition.y = target.getPositionY(); + }, + + /** + * reverse a new cc.CardinalSplineBy + * + * @return {cc.CardinalSplineBy} + */ + reverse:function () { + var copyConfig = this._points.slice(); + var current; + // + // convert "absolutes" to "diffs" + // + var p = copyConfig[0]; + for (var i = 1; i < copyConfig.length; ++i) { + current = copyConfig[i]; + copyConfig[i] = cc.pSub(current, p); + p = current; + } + + // convert to "diffs" to "reverse absolute" + var reverseArray = cc.reverseControlPoints(copyConfig); + + // 1st element (which should be 0,0) should be here too + p = reverseArray[ reverseArray.length - 1 ]; + reverseArray.pop(); + + p.x = -p.x; + p.y = -p.y; + + reverseArray.unshift(p); + for (var i = 1; i < reverseArray.length; ++i) { + current = reverseArray[i]; + current.x = -current.x; + current.y = -current.y; + current.x += p.x; + current.y += p.y; + reverseArray[i] = current; + p = current; + } + return cc.cardinalSplineBy(this._duration, reverseArray, this._tension); + }, + + /** + * update position of target + * + * @param {cc.Point} newPos + */ + updatePosition:function (newPos) { + var pos = this._startPosition; + var posX = newPos.x + pos.x; + var posY = newPos.y + pos.y; + this._previousPosition.x = posX; + this._previousPosition.y = posY; + this.target.setPosition(posX, posY); + }, + + /** + * returns a new clone of the action + * + * @returns {cc.CardinalSplineBy} + */ + clone:function () { + var a = new cc.CardinalSplineBy(); + a.initWithDuration(this._duration, cc.copyControlPoints(this._points), this._tension); + return a; + } +}); + +/** + * creates an action with a Cardinal Spline array of points and tension. + * + * @function + * @param {Number} duration + * @param {Array} points + * @param {Number} tension + * + * @return {cc.CardinalSplineBy} + */ +cc.cardinalSplineBy = function (duration, points, tension) { + return new cc.CardinalSplineBy(duration, points, tension); +}; + +/** + * Please use cc.cardinalSplineBy instead. + * creates an action with a Cardinal Spline array of points and tension. + * @function + * @param {Number} duration + * @param {Array} points + * @param {Number} tension + * @return {cc.CardinalSplineBy} + * @static + * @deprecated since v3.0 please use cc.cardinalSplineBy(duration, points, tension); + */ +cc.CardinalSplineBy.create = cc.cardinalSplineBy; + +/** + * An action that moves the target with a CatmullRom curve to a destination point.
+ * A Catmull Rom is a Cardinal Spline with a tension of 0.5.
+ * {@link http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline} + * Absolute coordinates. + * + * @class + * @extends cc.CardinalSplineTo + * @param {Number} dt + * @param {Array} points + * + * @example + * var action1 = cc.catmullRomTo(3, array); + */ +cc.CatmullRomTo = cc.CardinalSplineTo.extend(/** @lends cc.CatmullRomTo# */{ + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * creates an action with a Cardinal Spline array of points and tension. + * @param {Number} dt + * @param {Array} points + */ + ctor: function(dt, points) { + points && this.initWithDuration(dt, points); + }, + + /** + * Initializes the action with a duration and an array of points + * + * @param {Number} dt + * @param {Array} points + */ + initWithDuration:function (dt, points) { + return cc.CardinalSplineTo.prototype.initWithDuration.call(this, dt, points, 0.5); + }, + + /** + * returns a new clone of the action + * @returns {cc.CatmullRomTo} + */ + clone:function () { + var action = new cc.CatmullRomTo(); + action.initWithDuration(this._duration, cc.copyControlPoints(this._points)); + return action; + } +}); + +/** + * creates an action with a Cardinal Spline array of points and tension. + * + * @function + * @param {Number} dt + * @param {Array} points + * @return {cc.CatmullRomTo} + * + * @example + * var action1 = cc.catmullRomTo(3, array); + */ +cc.catmullRomTo = function (dt, points) { + return new cc.CatmullRomTo(dt, points); +}; +/** + * Please use cc.catmullRomTo instead. + * creates an action with a Cardinal Spline array of points and tension. + * + * @param {Number} dt + * @param {Array} points + * @return {cc.CatmullRomTo} + * @static + * @deprecated since v3.0 please use cc.catmullRomTo(dt, points) instead. + */ +cc.CatmullRomTo.create = cc.catmullRomTo; + +/** + * An action that moves the target with a CatmullRom curve by a certain distance.
+ * A Catmull Rom is a Cardinal Spline with a tension of 0.5.
+ * http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline + * Relative coordinates. + * + * @class + * @extends cc.CardinalSplineBy + * @param {Number} dt + * @param {Array} points + * + * @example + * var action1 = cc.catmullRomBy(3, array); + */ +cc.CatmullRomBy = cc.CardinalSplineBy.extend({ + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Creates an action with a Cardinal Spline array of points and tension. + * @param {Number} dt + * @param {Array} points + */ + ctor: function(dt, points) { + cc.CardinalSplineBy.prototype.ctor.call(this); + points && this.initWithDuration(dt, points); + }, + + /** + * initializes the action with a duration and an array of points + * + * @function + * @param {Number} dt + * @param {Array} points + */ + initWithDuration:function (dt, points) { + return cc.CardinalSplineTo.prototype.initWithDuration.call(this, dt, points, 0.5); + }, + + /** + * returns a new clone of the action + * @returns {cc.CatmullRomBy} + */ + clone:function () { + var action = new cc.CatmullRomBy(); + action.initWithDuration(this._duration, cc.copyControlPoints(this._points)); + return action; + } +}); + +/** + * Creates an action with a Cardinal Spline array of points and tension + * @function + * @param {Number} dt + * @param {Array} points + * @return {cc.CatmullRomBy} + * @example + * var action1 = cc.catmullRomBy(3, array); + */ +cc.catmullRomBy = function (dt, points) { + return new cc.CatmullRomBy(dt, points); +}; +/** + * Please use cc.catmullRomBy instead + * Creates an action with a Cardinal Spline array of points and tension + * @static + * @deprecated since v3.0 please cc.catmullRomBy(dt, points) instead. + */ +cc.CatmullRomBy.create = cc.catmullRomBy; diff --git a/frameworks/cocos2d-html5/cocos2d/actions/CCActionEase.js b/frameworks/cocos2d-html5/cocos2d/actions/CCActionEase.js new file mode 100644 index 0000000..f6ddef0 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/actions/CCActionEase.js @@ -0,0 +1,3680 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * Base class for Easing actions + * @class + * @extends cc.ActionInterval + * @param {cc.ActionInterval} action + * + * @deprecated since v3.0 Does not recommend the use of the base object. + * + * @example + * var moveEase = new cc.ActionEase(action); + */ +cc.ActionEase = cc.ActionInterval.extend(/** @lends cc.ActionEase# */{ + _inner:null, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * creates the action of ActionEase. + * @param {cc.ActionInterval} action + */ + ctor: function (action) { + cc.ActionInterval.prototype.ctor.call(this); + action && this.initWithAction(action); + }, + + /** + * initializes the action + * + * @param {cc.ActionInterval} action + * @return {Boolean} + */ + initWithAction:function (action) { + if(!action) + throw new Error("cc.ActionEase.initWithAction(): action must be non nil"); + + if (this.initWithDuration(action.getDuration())) { + this._inner = action; + return true; + } + return false; + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.ActionEase} + */ + clone:function(){ + var action = new cc.ActionEase(); + action.initWithAction(this._inner.clone()); + return action; + }, + + /** + * called before the action start. It will also set the target. + * + * @param {cc.Node} target + */ + startWithTarget:function (target) { + cc.ActionInterval.prototype.startWithTarget.call(this, target); + this._inner.startWithTarget(this.target); + }, + + /** + * Stop the action. + */ + stop:function () { + this._inner.stop(); + cc.ActionInterval.prototype.stop.call(this); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + this._inner.update(dt); + }, + + /** + * Create new action to original operation effect opposite.
+ * For example:
+ * - The action will be x coordinates of 0 move to 100.
+ * - The reversed action will be x of 100 move to 0. + * - Will be rewritten + * @return {cc.ActionEase} + */ + reverse:function () { + return new cc.ActionEase(this._inner.reverse()); + }, + + /** + * Get inner Action. + * + * @return {cc.ActionInterval} + */ + getInnerAction:function(){ + return this._inner; + } +}); + +/** + * creates the action of ActionEase + * + * @param {cc.ActionInterval} action + * @return {cc.ActionEase} + * @example + * // example + * var moveEase = cc.actionEase(action); + */ +cc.actionEase = function (action) { + return new cc.ActionEase(action); +}; + +/** + * Please use cc.actionEase instead + * creates the action of ActionEase + * + * @param {cc.ActionInterval} action + * @return {cc.ActionEase} + * @static + * @deprecated since v3.0 please use cc.actionEase(action) instead. + */ +cc.ActionEase.create = cc.actionEase; + +/** + * Base class for Easing actions with rate parameters + * + * @class + * @extends cc.ActionEase + * @param {cc.ActionInterval} action + * @param {Number} rate + * + * @deprecated since v3.0 please cc.easeRateAction(action, 3.0); + * + * @example + * //The old usage + * cc.EaseRateAction.create(action, 3.0); + * //The new usage + * var moveEaseRateAction = cc.easeRateAction(action, 3.0); + */ +cc.EaseRateAction = cc.ActionEase.extend(/** @lends cc.EaseRateAction# */{ + _rate:0, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Creates the action with the inner action and the rate parameter. + * @param {cc.ActionInterval} action + * @param {Number} rate + */ + ctor: function(action, rate){ + cc.ActionEase.prototype.ctor.call(this); + + rate !== undefined && this.initWithAction(action, rate); + }, + + /** + * set rate value for the actions + * @param {Number} rate + */ + setRate:function (rate) { + this._rate = rate; + }, + + /** get rate value for the actions + * @return {Number} + */ + getRate:function () { + return this._rate; + }, + + /** + * Initializes the action with the inner action and the rate parameter + * @param {cc.ActionInterval} action + * @param {Number} rate + * @return {Boolean} + */ + initWithAction:function (action, rate) { + if (cc.ActionEase.prototype.initWithAction.call(this, action)) { + this._rate = rate; + return true; + } + return false; + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseRateAction} + */ + clone:function(){ + var action = new cc.EaseRateAction(); + action.initWithAction(this._inner.clone(), this._rate); + return action; + }, + + /** + * Create new action to original operation effect opposite.
+ * For example:
+ * - The action will be x coordinates of 0 move to 100.
+ * - The reversed action will be x of 100 move to 0. + * - Will be rewritten + * @return {cc.EaseRateAction} + */ + reverse:function () { + return new cc.EaseRateAction(this._inner.reverse(), 1 / this._rate); + } +}); + +/** + * Creates the action with the inner action and the rate parameter. + * + * @param {cc.ActionInterval} action + * @param {Number} rate + * @return {cc.EaseRateAction} + * @example + * // example + * var moveEaseRateAction = cc.easeRateAction(action, 3.0); + */ +cc.easeRateAction = function (action, rate) { + return new cc.EaseRateAction(action, rate); +}; + +/** + * Please use cc.easeRateAction instead.
+ * Creates the action with the inner action and the rate parameter. + * + * @param {cc.ActionInterval} action + * @param {Number} rate + * @return {cc.EaseRateAction} + * @static + * @deprecated since v3.0 please use cc.easeRateAction(action, rate) + * @example + * //The old usage + * cc.EaseRateAction.create(action, 3.0); + * //The new usage + * var moveEaseRateAction = cc.easeRateAction(action, 3.0); + */ +cc.EaseRateAction.create = cc.easeRateAction; + +/** + * cc.EaseIn action with a rate. From slow to fast. + * + * @class + * @extends cc.EaseRateAction + * + * @deprecated since v3.0 please use action.easing(cc.easeIn(3)); + * + * @example + * //The old usage + * cc.EaseIn.create(action, 3); + * //The new usage + * action.easing(cc.easeIn(3.0)); + */ +cc.EaseIn = cc.EaseRateAction.extend(/** @lends cc.EaseIn# */{ + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + this._inner.update(Math.pow(dt, this._rate)); + }, + + /** + * Create a cc.easeIn action. Opposite with the original motion trajectory. + * @return {cc.EaseIn} + */ + reverse:function () { + return new cc.EaseIn(this._inner.reverse(), 1 / this._rate); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseIn} + */ + clone:function(){ + var action = new cc.EaseIn(); + action.initWithAction(this._inner.clone(), this._rate); + return action; + } +}); + +/** + * Creates the action with the inner action and the rate parameter.
+ * From slow to fast. + * + * @static + * @deprecated since v3.0
Please use action.easing(cc.easeIn(3)) + * + * @example + * //The old usage + * cc.EaseIn.create(action, 3); + * //The new usage + * action.easing(cc.easeIn(3.0)); + * + * @param {cc.ActionInterval} action + * @param {Number} rate + * @return {cc.EaseIn} + */ +cc.EaseIn.create = function (action, rate) { + return new cc.EaseIn(action, rate); +}; + +/** + * Creates the action easing object with the rate parameter.
+ * From slow to fast. + * + * @function + * @param {Number} rate + * @return {Object} + * @example + * // example + * action.easing(cc.easeIn(3.0)); + */ +cc.easeIn = function (rate) { + return { + _rate: rate, + easing: function (dt) { + return Math.pow(dt, this._rate); + }, + reverse: function(){ + return cc.easeIn(1 / this._rate); + } + }; +}; + +/** + * cc.EaseOut action with a rate. From fast to slow. + * + * @class + * @extends cc.EaseRateAction + * + * @deprecated since v3.0 please use action.easing(cc.easeOut(3)) + * + * @example + * //The old usage + * cc.EaseOut.create(action, 3); + * //The new usage + * action.easing(cc.easeOut(3.0)); + */ +cc.EaseOut = cc.EaseRateAction.extend(/** @lends cc.EaseOut# */{ + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + this._inner.update(Math.pow(dt, 1 / this._rate)); + }, + + /** + * Create a cc.easeIn action. Opposite with the original motion trajectory. + * @return {cc.EaseOut} + */ + reverse:function () { + return new cc.EaseOut(this._inner.reverse(), 1 / this._rate); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseOut} + */ + clone:function(){ + var action = new cc.EaseOut(); + action.initWithAction(this._inner.clone(),this._rate); + return action; + } +}); + +/** + * Creates the action with the inner action and the rate parameter.
+ * From fast to slow. + * + * @static + * @deprecated since v3.0
Please use cc.easeOut instead. + * + * @example + * //The old usage + * cc.EaseOut.create(action, 3); + * //The new usage + * action.easing(cc.easeOut(3.0)); + * + * @param {cc.ActionInterval} action + * @param {Number} rate + * @return {cc.EaseOut} + */ +cc.EaseOut.create = function (action, rate) { + return new cc.EaseOut(action, rate); +}; + +/** + * Creates the action easing object with the rate parameter.
+ * From fast to slow. + * + * @function + * @param {Number} rate + * @return {Object} + * @example + * // example + * action.easing(cc.easeOut(3.0)); + */ +cc.easeOut = function (rate) { + return { + _rate: rate, + easing: function (dt) { + return Math.pow(dt, 1 / this._rate); + }, + reverse: function(){ + return cc.easeOut(1 / this._rate) + } + }; +}; + +/** + * cc.EaseInOut action with a rate.
+ * Slow to fast then to slow. + * @class + * @extends cc.EaseRateAction + * + * @deprecated since v3.0 please use action.easing(cc.easeInOut(3.0)) + * + * @example + * //The old usage + * cc.EaseInOut.create(action, 3); + * //The new usage + * action.easing(cc.easeInOut(3.0)); + */ +cc.EaseInOut = cc.EaseRateAction.extend(/** @lends cc.EaseInOut# */{ + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + dt *= 2; + if (dt < 1) + this._inner.update(0.5 * Math.pow(dt, this._rate)); + else + this._inner.update(1.0 - 0.5 * Math.pow(2 - dt, this._rate)); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseInOut} + */ + clone:function(){ + var action = new cc.EaseInOut(); + action.initWithAction(this._inner.clone(), this._rate); + return action; + }, + + /** + * Create a cc.EaseInOut action. Opposite with the original motion trajectory. + * @return {cc.EaseInOut} + */ + reverse:function () { + return new cc.EaseInOut(this._inner.reverse(), this._rate); + } +}); + +/** + * Creates the action with the inner action and the rate parameter. + * Slow to fast then to slow. + * @static + * @deprecated since v3.0
Please use action.easing(cc.easeInOut(3.0)) + * + * @example + * //The old usage + * cc.EaseInOut.create(action, 3); + * //The new usage + * action.easing(cc.easeInOut(3.0)); + * + * @param {cc.ActionInterval} action + * @param {Number} rate + * @return {cc.EaseInOut} + */ +cc.EaseInOut.create = function (action, rate) { + return new cc.EaseInOut(action, rate); +}; + +/** + * Creates the action easing object with the rate parameter.
+ * Slow to fast then to slow. + * @function + * @param {Number} rate + * @return {Object} + * + * @example + * //The new usage + * action.easing(cc.easeInOut(3.0)); + */ +cc.easeInOut = function (rate) { + return { + _rate: rate, + easing: function (dt) { + dt *= 2; + if (dt < 1) + return 0.5 * Math.pow(dt, this._rate); + else + return 1.0 - 0.5 * Math.pow(2 - dt, this._rate); + }, + reverse: function(){ + return cc.easeInOut(this._rate); + } + }; +}; + +/** + * cc.Ease Exponential In. Slow to Fast.
+ * Reference easeInExpo:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @class + * @extends cc.ActionEase + * + * @deprecated since v3.0 please action.easing(cc.easeExponentialIn()) + * + * @example + * //The old usage + * cc.EaseExponentialIn.create(action); + * //The new usage + * action.easing(cc.easeExponentialIn()); + */ +cc.EaseExponentialIn = cc.ActionEase.extend(/** @lends cc.EaseExponentialIn# */{ + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + this._inner.update(dt === 0 ? 0 : Math.pow(2, 10 * (dt - 1))); + }, + + /** + * Create a cc.EaseExponentialOut action. Opposite with the original motion trajectory. + * @return {cc.EaseExponentialOut} + */ + reverse:function () { + return new cc.EaseExponentialOut(this._inner.reverse()); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseExponentialIn} + */ + clone:function(){ + var action = new cc.EaseExponentialIn(); + action.initWithAction(this._inner.clone()); + return action; + } +}); + +/** + * Creates the action easing object with the rate parameter.
+ * Reference easeInExpo:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @static + * @deprecated since v3.0
Please use action.easing(cc.easeExponentialIn()) + * @param {cc.ActionInterval} action + * @return {cc.EaseExponentialIn} + * + * @example + * //The old usage + * cc.EaseExponentialIn.create(action); + * //The new usage + * action.easing(cc.easeExponentialIn()); + */ +cc.EaseExponentialIn.create = function (action) { + return new cc.EaseExponentialIn(action); +}; + +cc._easeExponentialInObj = { + easing: function(dt){ + return dt === 0 ? 0 : Math.pow(2, 10 * (dt - 1)); + }, + reverse: function(){ + return cc._easeExponentialOutObj; + } +}; + +/** + * Creates the action easing object with the rate parameter.
+ * Reference easeInExpo:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @function + * @return {Object} + * @example + * // example + * action.easing(cc.easeExponentialIn()); + */ +cc.easeExponentialIn = function(){ + return cc._easeExponentialInObj; +}; + +/** + * Ease Exponential Out.
+ * Reference easeOutExpo:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @class + * @extends cc.ActionEase + * + * @deprecated since v3.0 please use action.easing(cc.easeExponentialOut()) + * + * @example + * //The old usage + * cc.EaseExponentialOut.create(action); + * //The new usage + * action.easing(cc.easeExponentialOut()); + */ +cc.EaseExponentialOut = cc.ActionEase.extend(/** @lends cc.EaseExponentialOut# */{ + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + this._inner.update(dt === 1 ? 1 : (-(Math.pow(2, -10 * dt)) + 1)); + }, + + /** + * Create a cc.EaseExponentialIn action. Opposite with the original motion trajectory. + * @return {cc.EaseExponentialIn} + */ + reverse:function () { + return new cc.EaseExponentialIn(this._inner.reverse()); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseExponentialOut} + */ + clone:function(){ + var action = new cc.EaseExponentialOut(); + action.initWithAction(this._inner.clone()); + return action; + } +}); + +/** + * Creates the action easing object with the rate parameter.
+ * Reference easeOutExpo:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @static + * @deprecated since v3.0
Please use action.easing(cc.easeExponentialOut()) + * @param {cc.ActionInterval} action + * @return {Object} + * + * @example + * //The old usage + * cc.EaseExponentialOut.create(action); + * //The new usage + * action.easing(cc.easeExponentialOut()); + */ +cc.EaseExponentialOut.create = function (action) { + return new cc.EaseExponentialOut(action); +}; + +cc._easeExponentialOutObj = { + easing: function(dt){ + return dt === 1 ? 1 : (-(Math.pow(2, -10 * dt)) + 1); + }, + reverse: function(){ + return cc._easeExponentialInObj; + } +}; + +/** + * creates the action easing object.
+ * Reference easeOutExpo:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * + * @return {Object} + * @example + * // example + * action.easing(cc.easeExponentialOut()); + */ +cc.easeExponentialOut = function(){ + return cc._easeExponentialOutObj; +}; + +/** + * Ease Exponential InOut.
+ * Reference easeInOutExpo:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * + * @class + * @extends cc.ActionEase + * + * @deprecated since v3.0 please use action.easing(cc.easeExponentialInOut) + * + * @example + * //The old usage + * cc.EaseExponentialInOut.create(action); + * //The new usage + * action.easing(cc.easeExponentialInOut()); + */ +cc.EaseExponentialInOut = cc.ActionEase.extend(/** @lends cc.EaseExponentialInOut# */{ + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + if( dt !== 1 && dt !== 0) { + dt *= 2; + if (dt < 1) + dt = 0.5 * Math.pow(2, 10 * (dt - 1)); + else + dt = 0.5 * (-Math.pow(2, -10 * (dt - 1)) + 2); + } + this._inner.update(dt); + }, + + /** + * Create a cc.EaseExponentialInOut action. Opposite with the original motion trajectory. + * @return {cc.EaseExponentialInOut} + */ + reverse:function () { + return new cc.EaseExponentialInOut(this._inner.reverse()); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseExponentialInOut} + */ + clone:function(){ + var action = new cc.EaseExponentialInOut(); + action.initWithAction(this._inner.clone()); + return action; + } +}); + +/** + * creates an EaseExponentialInOut action.
+ * Reference easeInOutExpo:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @static + * @deprecated since v3.0
Please use action.easing(cc.easeExponentialInOut) + * @param {cc.ActionInterval} action + * @return {cc.EaseExponentialInOut} + * + * @example + * //The old usage + * cc.EaseExponentialInOut.create(action); + * //The new usage + * action.easing(cc.easeExponentialInOut()); + */ +cc.EaseExponentialInOut.create = function (action) { + return new cc.EaseExponentialInOut(action); +}; + +cc._easeExponentialInOutObj = { + easing: function(dt){ + if( dt !== 1 && dt !== 0) { + dt *= 2; + if (dt < 1) + return 0.5 * Math.pow(2, 10 * (dt - 1)); + else + return 0.5 * (-Math.pow(2, -10 * (dt - 1)) + 2); + } + return dt; + }, + reverse: function(){ + return cc._easeExponentialInOutObj; + } +}; + +/** + * creates an EaseExponentialInOut action easing object.
+ * Reference easeInOutExpo:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @function + * @return {Object} + * @example + * // example + * action.easing(cc.easeExponentialInOut()); + */ +cc.easeExponentialInOut = function(){ + return cc._easeExponentialInOutObj; +}; + +/** + * Ease Sine In.
+ * Reference easeInSine:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @class + * @extends cc.ActionEase + * + * @deprecated since v3.0 please use action.easing(cc.easeSineIn()) + * + * @example + * //The old usage + * cc.EaseSineIn.create(action); + * //The new usage + * action.easing(cc.easeSineIn()); + */ +cc.EaseSineIn = cc.ActionEase.extend(/** @lends cc.EaseSineIn# */{ + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + dt = dt===0 || dt===1 ? dt : -1 * Math.cos(dt * Math.PI / 2) + 1; + this._inner.update(dt); + }, + + /** + * Create a cc.EaseSineOut action. Opposite with the original motion trajectory. + * @return {cc.EaseSineOut} + */ + reverse:function () { + return new cc.EaseSineOut(this._inner.reverse()); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseSineIn} + */ + clone:function(){ + var action = new cc.EaseSineIn(); + action.initWithAction(this._inner.clone()); + return action; + } +}); + +/** + * creates an EaseSineIn action.
+ * Reference easeInSine:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @static + * @deprecated since v3.0
Please use action.easing(cc.easeSineIn()) + * @param {cc.ActionInterval} action + * @return {cc.EaseSineIn} + * + * @example + * //The old usage + * cc.EaseSineIn.create(action); + * //The new usage + * action.easing(cc.easeSineIn()); + */ +cc.EaseSineIn.create = function (action) { + return new cc.EaseSineIn(action); +}; + +cc._easeSineInObj = { + easing: function(dt){ + return (dt===0 || dt===1) ? dt : -1 * Math.cos(dt * Math.PI / 2) + 1; + }, + reverse: function(){ + return cc._easeSineOutObj; + } +}; +/** + * creates an EaseSineIn action.
+ * Reference easeInSine:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @function + * @return {Object} + * @example + * // example + * action.easing(cc.easeSineIn()); + */ +cc.easeSineIn = function(){ + return cc._easeSineInObj; +}; + +/** + * Ease Sine Out.
+ * Reference easeOutSine:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @class + * @extends cc.ActionEase + * + * @deprecated since v3.0 please use action.easing(cc.easeSineOut()) + * + * @example + * //The old usage + * cc.EaseSineOut.create(action); + * //The new usage + * action.easing(cc.easeSineOut()); + */ +cc.EaseSineOut = cc.ActionEase.extend(/** @lends cc.EaseSineOut# */{ + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + dt = dt===0 || dt===1 ? dt : Math.sin(dt * Math.PI / 2); + this._inner.update(dt); + }, + + /** + * Create a cc.EaseSineIn action. Opposite with the original motion trajectory. + * @return {cc.EaseSineIn} + */ + reverse:function () { + return new cc.EaseSineIn(this._inner.reverse()); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseSineOut} + */ + clone:function(){ + var action = new cc.EaseSineOut(); + action.initWithAction(this._inner.clone()); + return action; + } +}); + +/** + * Creates an EaseSineOut action.
+ * Reference easeOutSine:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @static + * @deprecated since v3.0
Please use action.easing(cc.easeSineOut()) + * @param {cc.ActionInterval} action + * @return {cc.EaseSineOut} + * + * @example + * //The old usage + * cc.EaseSineOut.create(action); + * //The new usage + * action.easing(cc.easeSineOut()); + */ +cc.EaseSineOut.create = function (action) { + return new cc.EaseSineOut(action); +}; + +cc._easeSineOutObj = { + easing: function(dt){ + return (dt===0 || dt===1) ? dt : Math.sin(dt * Math.PI / 2); + }, + reverse: function(){ + return cc._easeSineInObj; + } +}; + +/** + * Creates an EaseSineOut action easing object.
+ * Reference easeOutSine:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @function + * @return {Object} + * @example + * // example + * action.easing(cc.easeSineOut()); + */ +cc.easeSineOut = function(){ + return cc._easeSineOutObj; +}; + +/** + * Ease Sine InOut.
+ * Reference easeInOutSine:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @class + * @extends cc.ActionEase + * + * @deprecated since v3.0 please use action.easing(cc.easeSineInOut()) + * + * @example + * //The old usage + * cc.EaseSineInOut.create(action); + * //The new usage + * action.easing(cc.easeSineInOut()); + */ +cc.EaseSineInOut = cc.ActionEase.extend(/** @lends cc.EaseSineInOut# */{ + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + dt = dt===0 || dt===1 ? dt : -0.5 * (Math.cos(Math.PI * dt) - 1); + this._inner.update(dt); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseSineInOut} + */ + clone:function(){ + var action = new cc.EaseSineInOut(); + action.initWithAction(this._inner.clone()); + return action; + }, + + /** + * Create a cc.EaseSineInOut action. Opposite with the original motion trajectory. + * @return {cc.EaseSineInOut} + */ + reverse:function () { + return new cc.EaseSineInOut(this._inner.reverse()); + } +}); + +/** + * Creates the action.
+ * Reference easeInOutSine:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @static + * @param {cc.ActionInterval} action + * @return {cc.EaseSineInOut} + * @deprecated since v3.0
Please use action.easing(cc.easeSineInOut()) + * + * @example + * //The old usage + * cc.EaseSineInOut.create(action); + * //The new usage + * action.easing(cc.easeSineInOut()); + */ +cc.EaseSineInOut.create = function (action) { + return new cc.EaseSineInOut(action); +}; + +cc._easeSineInOutObj = { + easing: function(dt){ + return (dt === 0 || dt === 1) ? dt : -0.5 * (Math.cos(Math.PI * dt) - 1); + }, + reverse: function(){ + return cc._easeSineInOutObj; + } +}; + +/** + * creates the action easing object.
+ * Reference easeInOutSine:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @return {Object} + * @example + * // example + * action.easing(cc.easeSineInOut()); + */ +cc.easeSineInOut = function(){ + return cc._easeSineInOutObj; +}; + +/** + * Ease Elastic abstract class. + * @class + * @extends cc.ActionEase + * @param {cc.ActionInterval} action + * @param {Number} [period=0.3] + * + * @deprecated since v3.0 Does not recommend the use of the base object. + */ +cc.EaseElastic = cc.ActionEase.extend(/** @lends cc.EaseElastic# */{ + _period: 0.3, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Creates the action with the inner action and the period in radians (default is 0.3). + * @param {cc.ActionInterval} action + * @param {Number} [period=0.3] + */ + ctor:function(action, period){ + cc.ActionEase.prototype.ctor.call(this); + + action && this.initWithAction(action, period); + }, + + /** + * get period of the wave in radians. default is 0.3 + * @return {Number} + */ + getPeriod:function () { + return this._period; + }, + + /** + * set period of the wave in radians. + * @param {Number} period + */ + setPeriod:function (period) { + this._period = period; + }, + + /** + * Initializes the action with the inner action and the period in radians (default is 0.3) + * @param {cc.ActionInterval} action + * @param {Number} [period=0.3] + * @return {Boolean} + */ + initWithAction:function (action, period) { + cc.ActionEase.prototype.initWithAction.call(this, action); + this._period = (period == null) ? 0.3 : period; + return true; + }, + + /** + * Create a action. Opposite with the original motion trajectory.
+ * Will be overwrite. + * @return {?cc.Action} + */ + reverse:function () { + cc.log("cc.EaseElastic.reverse(): it should be overridden in subclass."); + return null; + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseElastic} + */ + clone:function(){ + var action = new cc.EaseElastic(); + action.initWithAction(this._inner.clone(), this._period); + return action; + } +}); + +/** + * Creates the action with the inner action and the period in radians (default is 0.3). + * @static + * @deprecated since v3.0 Does not recommend the use of the base object. + * @param {cc.ActionInterval} action + * @param {Number} [period=0.3] + * @return {cc.EaseElastic} + */ +cc.EaseElastic.create = function (action, period) { + return new cc.EaseElastic(action, period); +}; + +/** + * Ease Elastic In action.
+ * Reference easeInElastic:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @warning This action doesn't use a bijective function. Actions like Sequence might have an unexpected result when used with this action. + * @class + * @extends cc.EaseElastic + * + * @deprecated since v3.0 please use action.easing(cc.easeElasticIn()) + * + * @example + * //The old usage + * cc.EaseElasticIn.create(action, period); + * //The new usage + * action.easing(cc.easeElasticIn(period)); + */ +cc.EaseElasticIn = cc.EaseElastic.extend(/** @lends cc.EaseElasticIn# */{ + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + var newT = 0; + if (dt === 0 || dt === 1) { + newT = dt; + } else { + var s = this._period / 4; + dt = dt - 1; + newT = -Math.pow(2, 10 * dt) * Math.sin((dt - s) * Math.PI * 2 / this._period); + } + this._inner.update(newT); + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseElasticOut} + */ + reverse:function () { + return new cc.EaseElasticOut(this._inner.reverse(), this._period); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseElasticIn} + */ + clone:function(){ + var action = new cc.EaseElasticIn(); + action.initWithAction(this._inner.clone(), this._period); + return action; + } +}); + +/** + * Creates the action with the inner action and the period in radians (default is 0.3).
+ * Reference easeInElastic:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @deprecated since v3.0
Please use action.easing(cc.easeElasticIn(period)) + * + * @example + * //The old usage + * cc.EaseElasticIn.create(action, period); + * //The new usage + * action.easing(cc.easeElasticIn(period)); + * + * @param {cc.ActionInterval} action + * @param {Number} [period=0.3] + * @return {cc.EaseElasticIn} + */ +cc.EaseElasticIn.create = function (action, period) { + return new cc.EaseElasticIn(action, period); +}; + +//default ease elastic in object (period = 0.3) +cc._easeElasticInObj = { + easing:function(dt){ + if (dt === 0 || dt === 1) + return dt; + dt = dt - 1; + return -Math.pow(2, 10 * dt) * Math.sin((dt - (0.3 / 4)) * Math.PI * 2 / 0.3); + }, + reverse:function(){ + return cc._easeElasticOutObj; + } +}; + +/** + * Creates the action easing obejct with the period in radians (default is 0.3).
+ * Reference easeInElastic:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @function + * @param {Number} [period=0.3] + * @return {Object} + * @example + * // example + * action.easing(cc.easeElasticIn(3.0)); + */ +cc.easeElasticIn = function (period) { + if(period && period !== 0.3){ + return { + _period: period, + easing: function (dt) { + if (dt === 0 || dt === 1) + return dt; + dt = dt - 1; + return -Math.pow(2, 10 * dt) * Math.sin((dt - (this._period / 4)) * Math.PI * 2 / this._period); + }, + reverse:function () { + return cc.easeElasticOut(this._period); + } + }; + } + return cc._easeElasticInObj; +}; + +/** + * Ease Elastic Out action.
+ * Reference easeOutElastic:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @warning This action doesn't use a bijective function. Actions like Sequence might have an unexpected result when used with this action. + * @class + * @extends cc.EaseElastic + * + * @deprecated since v3.0
Please use action.easing(cc.easeElasticOut(period)) + * + * @example + * //The old usage + * cc.EaseElasticOut.create(action, period); + * //The new usage + * action.easing(cc.easeElasticOut(period)); + */ +cc.EaseElasticOut = cc.EaseElastic.extend(/** @lends cc.EaseElasticOut# */{ + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + var newT = 0; + if (dt === 0 || dt === 1) { + newT = dt; + } else { + var s = this._period / 4; + newT = Math.pow(2, -10 * dt) * Math.sin((dt - s) * Math.PI * 2 / this._period) + 1; + } + + this._inner.update(newT); + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseElasticIn} + */ + reverse:function () { + return new cc.EaseElasticIn(this._inner.reverse(), this._period); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseElasticOut} + */ + clone:function(){ + var action = new cc.EaseElasticOut(); + action.initWithAction(this._inner.clone(), this._period); + return action; + } +}); + +/** + * Creates the action with the inner action and the period in radians (default is 0.3).
+ * Reference easeOutElastic:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @deprecated since v3.0
Please use action.easing(cc.easeElasticOut(period)) + * @param {cc.ActionInterval} action + * @param {Number} [period=0.3] + * @return {cc.EaseElasticOut} + * + * @example + * //The old usage + * cc.EaseElasticOut.create(action, period); + * //The new usage + * action.easing(cc.easeElasticOut(period)); + */ +cc.EaseElasticOut.create = function (action, period) { + return new cc.EaseElasticOut(action, period); +}; + +//default ease elastic out object (period = 0.3) +cc._easeElasticOutObj = { + easing: function (dt) { + return (dt === 0 || dt === 1) ? dt : Math.pow(2, -10 * dt) * Math.sin((dt - (0.3 / 4)) * Math.PI * 2 / 0.3) + 1; + }, + reverse:function(){ + return cc._easeElasticInObj; + } +}; +/** + * Creates the action easing object with the period in radians (default is 0.3).
+ * Reference easeOutElastic:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @function + * @param {Number} [period=0.3] + * @return {Object} + * @example + * // example + * action.easing(cc.easeElasticOut(3.0)); + */ +cc.easeElasticOut = function (period) { + if(period && period !== 0.3){ + return { + _period: period, + easing: function (dt) { + return (dt === 0 || dt === 1) ? dt : Math.pow(2, -10 * dt) * Math.sin((dt - (this._period / 4)) * Math.PI * 2 / this._period) + 1; + }, + reverse:function(){ + return cc.easeElasticIn(this._period); + } + }; + } + return cc._easeElasticOutObj; +}; + +/** + * Ease Elastic InOut action.
+ * Reference easeInOutElastic:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @warning This action doesn't use a bijective function. Actions like Sequence might have an unexpected result when used with this action. + * @class + * @extends cc.EaseElastic + * + * @deprecated since v3.0 please use action.easing(cc.easeElasticInOut()) + * + * @example + * //The old usage + * cc.EaseElasticInOut.create(action, period); + * //The new usage + * action.easing(cc.easeElasticInOut(period)); + */ +cc.EaseElasticInOut = cc.EaseElastic.extend(/** @lends cc.EaseElasticInOut# */{ + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + var newT = 0; + var locPeriod = this._period; + if (dt === 0 || dt === 1) { + newT = dt; + } else { + dt = dt * 2; + if (!locPeriod) + locPeriod = this._period = 0.3 * 1.5; + + var s = locPeriod / 4; + dt = dt - 1; + if (dt < 0) + newT = -0.5 * Math.pow(2, 10 * dt) * Math.sin((dt - s) * Math.PI * 2 / locPeriod); + else + newT = Math.pow(2, -10 * dt) * Math.sin((dt - s) * Math.PI * 2 / locPeriod) * 0.5 + 1; + } + this._inner.update(newT); + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseElasticInOut} + */ + reverse:function () { + return new cc.EaseElasticInOut(this._inner.reverse(), this._period); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseElasticInOut} + */ + clone:function(){ + var action = new cc.EaseElasticInOut(); + action.initWithAction(this._inner.clone(), this._period); + return action; + } +}); + +/** + * Creates the action with the inner action and the period in radians (default is 0.3).
+ * Reference easeInOutElastic:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @deprecated since v3.0
Please use action.easing(cc.easeElasticInOut(period)) + * @param {cc.ActionInterval} action + * @param {Number} [period=0.3] + * @return {cc.EaseElasticInOut} + * + * @example + * //The old usage + * cc.EaseElasticInOut.create(action, period); + * //The new usage + * action.easing(cc.easeElasticInOut(period)); + */ +cc.EaseElasticInOut.create = function (action, period) { + return new cc.EaseElasticInOut(action, period); +}; + +/** + * Creates the action easing object with the period in radians (default is 0.3).
+ * Reference easeInOutElastic:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @function + * @param {Number} [period=0.3] + * @return {Object} + * @example + * // example + * action.easing(cc.easeElasticInOut(3.0)); + */ +cc.easeElasticInOut = function (period) { + period = period || 0.3; + return { + _period: period, + easing: function (dt) { + var newT = 0; + var locPeriod = this._period; + if (dt === 0 || dt === 1) { + newT = dt; + } else { + dt = dt * 2; + if (!locPeriod) + locPeriod = this._period = 0.3 * 1.5; + var s = locPeriod / 4; + dt = dt - 1; + if (dt < 0) + newT = -0.5 * Math.pow(2, 10 * dt) * Math.sin((dt - s) * Math.PI * 2 / locPeriod); + else + newT = Math.pow(2, -10 * dt) * Math.sin((dt - s) * Math.PI * 2 / locPeriod) * 0.5 + 1; + } + return newT; + }, + reverse: function(){ + return cc.easeElasticInOut(this._period); + } + }; +}; + +/** + * cc.EaseBounce abstract class. + * + * @deprecated since v3.0 Does not recommend the use of the base object. + * + * @class + * @extends cc.ActionEase + */ +cc.EaseBounce = cc.ActionEase.extend(/** @lends cc.EaseBounce# */{ + /** + * @param {Number} time1 + * @return {Number} + */ + bounceTime:function (time1) { + if (time1 < 1 / 2.75) { + return 7.5625 * time1 * time1; + } else if (time1 < 2 / 2.75) { + time1 -= 1.5 / 2.75; + return 7.5625 * time1 * time1 + 0.75; + } else if (time1 < 2.5 / 2.75) { + time1 -= 2.25 / 2.75; + return 7.5625 * time1 * time1 + 0.9375; + } + + time1 -= 2.625 / 2.75; + return 7.5625 * time1 * time1 + 0.984375; + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseBounce} + */ + clone:function(){ + var action = new cc.EaseBounce(); + action.initWithAction(this._inner.clone()); + return action; + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseBounce} + */ + reverse:function () { + return new cc.EaseBounce(this._inner.reverse()); + } +}); + +/** + * Creates an ease bounce action. + * @static + * @deprecated since v3.0 Does not recommend the use of the base object. + * @param {cc.ActionInterval} action + * @return {cc.EaseBounce} + */ +cc.EaseBounce.create = function (action) { + return new cc.EaseBounce(action); +}; + +/** + * cc.EaseBounceIn action.
+ * Eased bounce effect at the beginning. + * @warning This action doesn't use a bijective function. Actions like Sequence might have an unexpected result when used with this action. + * @class + * @extends cc.EaseBounce + * + * @deprecated since v3.0 please use action.easing(cc.easeBounceIn()) + * + * @example + * //The old usage + * cc.EaseBounceIn.create(action); + * //The new usage + * action.easing(cc.easeBounceIn()); + */ +cc.EaseBounceIn = cc.EaseBounce.extend(/** @lends cc.EaseBounceIn# */{ + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + var newT = 1 - this.bounceTime(1 - dt); + this._inner.update(newT); + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseBounceOut} + */ + reverse:function () { + return new cc.EaseBounceOut(this._inner.reverse()); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseBounceIn} + */ + clone:function(){ + var action = new cc.EaseBounceIn(); + action.initWithAction(this._inner.clone()); + return action; + } +}); + +/** + * Creates the action.
+ * Eased bounce effect at the beginning. + * @static + * @deprecated since v3.0
Please use action.easing(cc.easeBounceIn()) + * @param {cc.ActionInterval} action + * @return {cc.EaseBounceIn} + * + * @example + * //The old usage + * cc.EaseBounceIn.create(action); + * //The new usage + * action.easing(cc.easeBounceIn()); + */ +cc.EaseBounceIn.create = function (action) { + return new cc.EaseBounceIn(action); +}; + +cc._bounceTime = function (time1) { + if (time1 < 1 / 2.75) { + return 7.5625 * time1 * time1; + } else if (time1 < 2 / 2.75) { + time1 -= 1.5 / 2.75; + return 7.5625 * time1 * time1 + 0.75; + } else if (time1 < 2.5 / 2.75) { + time1 -= 2.25 / 2.75; + return 7.5625 * time1 * time1 + 0.9375; + } + + time1 -= 2.625 / 2.75; + return 7.5625 * time1 * time1 + 0.984375; +}; + +cc._easeBounceInObj = { + easing: function(dt){ + return 1 - cc._bounceTime(1 - dt); + }, + reverse: function(){ + return cc._easeBounceOutObj; + } +}; + +/** + * Creates the action easing object.
+ * Eased bounce effect at the beginning. + * @function + * @return {Object} + * @example + * // example + * action.easing(cc.easeBounceIn()); + */ +cc.easeBounceIn = function(){ + return cc._easeBounceInObj; +}; + +/** + * cc.EaseBounceOut action.
+ * Eased bounce effect at the ending. + * @warning This action doesn't use a bijective function. Actions like Sequence might have an unexpected result when used with this action. + * @class + * @extends cc.EaseBounce + * + * @deprecated since v3.0 please use action.easing(cc.easeBounceOut()) + * + * @example + * //The old usage + * cc.EaseBounceOut.create(action); + * //The new usage + * action.easing(cc.easeBounceOut()); + */ +cc.EaseBounceOut = cc.EaseBounce.extend(/** @lends cc.EaseBounceOut# */{ + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + var newT = this.bounceTime(dt); + this._inner.update(newT); + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseBounceIn} + */ + reverse:function () { + return new cc.EaseBounceIn(this._inner.reverse()); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseBounceOut} + */ + clone:function(){ + var action = new cc.EaseBounceOut(); + action.initWithAction(this._inner.clone()); + return action; + } +}); + +/** + * Creates the action.
+ * Eased bounce effect at the ending. + * @static + * @deprecated since v3.0 please use action.easing(cc.easeBounceOut()) + * @param {cc.ActionInterval} action + * @return {cc.EaseBounceOut} + * + * @example + * //The old usage + * cc.EaseBounceOut.create(action); + * //The new usage + * action.easing(cc.easeBounceOut()); + */ +cc.EaseBounceOut.create = function (action) { + return new cc.EaseBounceOut(action); +}; + +cc._easeBounceOutObj = { + easing: function(dt){ + return cc._bounceTime(dt); + }, + reverse:function () { + return cc._easeBounceInObj; + } +}; + +/** + * Creates the action easing object.
+ * Eased bounce effect at the ending. + * @function + * @return {Object} + * @example + * // example + * action.easing(cc.easeBounceOut()); + */ +cc.easeBounceOut = function(){ + return cc._easeBounceOutObj; +}; + +/** + * cc.EaseBounceInOut action.
+ * Eased bounce effect at the beginning and ending. + * @warning This action doesn't use a bijective function. Actions like Sequence might have an unexpected result when used with this action. + * @class + * @extends cc.EaseBounce + * + * @deprecated since v3.0
Please use acton.easing(cc.easeBounceInOut()) + * + * @example + * //The old usage + * cc.EaseBounceInOut.create(action); + * //The new usage + * action.easing(cc.easeBounceInOut()); + */ +cc.EaseBounceInOut = cc.EaseBounce.extend(/** @lends cc.EaseBounceInOut# */{ + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + var newT = 0; + if (dt < 0.5) { + dt = dt * 2; + newT = (1 - this.bounceTime(1 - dt)) * 0.5; + } else { + newT = this.bounceTime(dt * 2 - 1) * 0.5 + 0.5; + } + this._inner.update(newT); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseBounceInOut} + */ + clone:function(){ + var action = new cc.EaseBounceInOut(); + action.initWithAction(this._inner.clone()); + return action; + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseBounceInOut} + */ + reverse:function () { + return new cc.EaseBounceInOut(this._inner.reverse()); + } +}); + +/** + * Creates the action.
+ * Eased bounce effect at the beginning and ending. + * @static + * @deprecated since v3.0
Please use action.easing(cc.easeBounceInOut()) + * @param {cc.ActionInterval} action + * @return {cc.EaseBounceInOut} + * + * @example + * //The old usage + * cc.EaseBounceInOut.create(action); + * //The new usage + * action.easing(cc.easeBounceInOut()); + */ +cc.EaseBounceInOut.create = function (action) { + return new cc.EaseBounceInOut(action); +}; + +cc._easeBounceInOutObj = { + easing: function (time1) { + var newT; + if (time1 < 0.5) { + time1 = time1 * 2; + newT = (1 - cc._bounceTime(1 - time1)) * 0.5; + } else { + newT = cc._bounceTime(time1 * 2 - 1) * 0.5 + 0.5; + } + return newT; + }, + reverse: function(){ + return cc._easeBounceInOutObj; + } +}; + +/** + * Creates the action easing object.
+ * Eased bounce effect at the beginning and ending. + * @function + * @return {Object} + * @example + * // example + * action.easing(cc.easeBounceInOut()); + */ +cc.easeBounceInOut = function(){ + return cc._easeBounceInOutObj; +}; + +/** + * cc.EaseBackIn action.
+ * In the opposite direction to move slowly, and then accelerated to the right direction. + * @warning This action doesn't use a bijective function. Actions like Sequence might have an unexpected result when used with this action. + * @class + * @extends cc.ActionEase + * + * @deprecated since v3.0 please use action.easing(cc.easeBackIn()) + * + * @example + * //The old usage + * cc.EaseBackIn.create(action); + * //The new usage + * action.easing(cc.easeBackIn()); + */ +cc.EaseBackIn = cc.ActionEase.extend(/** @lends cc.EaseBackIn# */{ + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + var overshoot = 1.70158; + dt = dt===0 || dt===1 ? dt : dt * dt * ((overshoot + 1) * dt - overshoot); + this._inner.update(dt); + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseBackOut} + */ + reverse:function () { + return new cc.EaseBackOut(this._inner.reverse()); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseBackIn} + */ + clone:function(){ + var action = new cc.EaseBackIn(); + action.initWithAction(this._inner.clone()); + return action; + } +}); + + +/** + * Creates the cc.EaseBackIn.
+ * In the opposite direction to move slowly, and then accelerated to the right direction. + * @static + * @deprecated since v3.0
Please use action.easing(cc.easeBackIn()) + * @param {cc.ActionInterval} action + * @return {cc.EaseBackIn} + * + * @example + * //The old usage + * cc.EaseBackIn.create(action); + * //The new usage + * action.easing(cc.easeBackIn()); + */ +cc.EaseBackIn.create = function (action) { + return new cc.EaseBackIn(action); +}; + +cc._easeBackInObj = { + easing: function (time1) { + var overshoot = 1.70158; + return (time1===0 || time1===1) ? time1 : time1 * time1 * ((overshoot + 1) * time1 - overshoot); + }, + reverse: function(){ + return cc._easeBackOutObj; + } +}; + +/** + * Creates the action easing object.
+ * In the opposite direction to move slowly, and then accelerated to the right direction. + * @function + * @return {Object} + * @example + * // example + * action.easing(cc.easeBackIn()); + */ +cc.easeBackIn = function(){ + return cc._easeBackInObj; +}; + +/** + * cc.EaseBackOut action.
+ * Fast moving more than the finish, and then slowly back to the finish. + * @warning This action doesn't use a bijective function. Actions like Sequence might have an unexpected result when used with this action. + * @class + * @extends cc.ActionEase + * + * @deprecated since v3.0 please use action.easing(cc.easeBackOut()); + * + * @example + * //The old usage + * cc.EaseBackOut.create(action); + * //The new usage + * action.easing(cc.easeBackOut()); + */ +cc.EaseBackOut = cc.ActionEase.extend(/** @lends cc.EaseBackOut# */{ + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + var overshoot = 1.70158; + dt = dt - 1; + this._inner.update(dt * dt * ((overshoot + 1) * dt + overshoot) + 1); + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseBackIn} + */ + reverse:function () { + return new cc.EaseBackIn(this._inner.reverse()); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseBackOut} + */ + clone:function(){ + var action = new cc.EaseBackOut(); + action.initWithAction(this._inner.clone()); + return action; + } +}); + +/** + * Creates the action.
+ * Fast moving more than the finish, and then slowly back to the finish. + * @static + * @deprecated since v3.0
Please use action.easing(cc.easeBackOut()); + * @param {cc.ActionInterval} action + * @return {cc.EaseBackOut} + * + * @example + * //The old usage + * cc.EaseBackOut.create(action); + * //The new usage + * action.easing(cc.easeBackOut()); + */ +cc.EaseBackOut.create = function (action) { + return new cc.EaseBackOut(action); +}; + +cc._easeBackOutObj = { + easing: function (time1) { + var overshoot = 1.70158; + time1 = time1 - 1; + return time1 * time1 * ((overshoot + 1) * time1 + overshoot) + 1; + }, + reverse: function(){ + return cc._easeBackInObj; + } +}; + +/** + * Creates the action easing object.
+ * Fast moving more than the finish, and then slowly back to the finish. + * @function + * @return {Object} + * @example + * // example + * action.easing(cc.easeBackOut()); + */ +cc.easeBackOut = function(){ + return cc._easeBackOutObj; +}; + +/** + * cc.EaseBackInOut action.
+ * Beginning of cc.EaseBackIn. Ending of cc.EaseBackOut. + * @warning This action doesn't use a bijective function. Actions like Sequence might have an unexpected result when used with this action. + * @class + * @extends cc.ActionEase + * + * @deprecated since v3.0
Please use action.easing(cc.easeBackInOut()) + * + * @example + * //The old usage + * cc.EaseBackInOut.create(action); + * //The new usage + * action.easing(cc.easeBackInOut()); + */ +cc.EaseBackInOut = cc.ActionEase.extend(/** @lends cc.EaseBackInOut# */{ + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + var overshoot = 1.70158 * 1.525; + dt = dt * 2; + if (dt < 1) { + this._inner.update((dt * dt * ((overshoot + 1) * dt - overshoot)) / 2); + } else { + dt = dt - 2; + this._inner.update((dt * dt * ((overshoot + 1) * dt + overshoot)) / 2 + 1); + } + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseBackInOut} + */ + clone:function(){ + var action = new cc.EaseBackInOut(); + action.initWithAction(this._inner.clone()); + return action; + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseBackInOut} + */ + reverse:function () { + return new cc.EaseBackInOut(this._inner.reverse()); + } +}); + + +/** + * Creates the action.
+ * Beginning of cc.EaseBackIn. Ending of cc.EaseBackOut. + * @static + * @param {cc.ActionInterval} action + * @return {cc.EaseBackInOut} + * + * @deprecated since v3.0
Please use action.easing(cc.easeBackInOut()) + * + * @example + * //The old usage + * cc.EaseBackInOut.create(action); + * //The new usage + * action.easing(cc.easeBackInOut()); + */ +cc.EaseBackInOut.create = function (action) { + return new cc.EaseBackInOut(action); +}; + +cc._easeBackInOutObj = { + easing: function (time1) { + var overshoot = 1.70158 * 1.525; + time1 = time1 * 2; + if (time1 < 1) { + return (time1 * time1 * ((overshoot + 1) * time1 - overshoot)) / 2; + } else { + time1 = time1 - 2; + return (time1 * time1 * ((overshoot + 1) * time1 + overshoot)) / 2 + 1; + } + }, + reverse: function(){ + return cc._easeBackInOutObj; + } +}; + +/** + * Creates the action easing object.
+ * Beginning of cc.EaseBackIn. Ending of cc.EaseBackOut. + * @function + * @return {Object} + * @example + * // example + * action.easing(cc.easeBackInOut()); + */ +cc.easeBackInOut = function(){ + return cc._easeBackInOutObj; +}; + +/** + * cc.EaseBezierAction action.
+ * Manually set a 4 order Bessel curve.
+ * According to the set point, calculate the trajectory. + * @class + * @extends cc.ActionEase + * @param {cc.Action} action + * + * @deprecated since v3.0
Please use action.easing(cc.easeBezierAction()) + * + * @example + * //The old usage + * var action = cc.EaseBezierAction.create(action); + * action.setBezierParamer(0.5, 0.5, 1.0, 1.0); + * //The new usage + * action.easing(cc.easeBezierAction(0.5, 0.5, 1.0, 1.0)); + */ +cc.EaseBezierAction = cc.ActionEase.extend(/** @lends cc.EaseBezierAction# */{ + + _p0: null, + _p1: null, + _p2: null, + _p3: null, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Initialization requires the application of Bessel curve of action. + * @param {cc.Action} action + */ + ctor: function(action){ + cc.ActionEase.prototype.ctor.call(this, action); + }, + + _updateTime: function(a, b, c, d, t){ + return (Math.pow(1-t,3) * a + 3*t*(Math.pow(1-t,2))*b + 3*Math.pow(t,2)*(1-t)*c + Math.pow(t,3)*d ); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update: function(dt){ + var t = this._updateTime(this._p0, this._p1, this._p2, this._p3, dt); + this._inner.update(t); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseBezierAction} + */ + clone: function(){ + var action = new cc.EaseBezierAction(); + action.initWithAction(this._inner.clone()); + action.setBezierParamer(this._p0, this._p1, this._p2, this._p3); + return action; + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseBezierAction} + */ + reverse: function(){ + var action = new cc.EaseBezierAction(this._inner.reverse()); + action.setBezierParamer(this._p3, this._p2, this._p1, this._p0); + return action; + }, + + /** + * Set of 4 reference point + * @param p0 + * @param p1 + * @param p2 + * @param p3 + */ + setBezierParamer: function(p0, p1, p2, p3){ + this._p0 = p0 || 0; + this._p1 = p1 || 0; + this._p2 = p2 || 0; + this._p3 = p3 || 0; + } +}); + +/** + * Creates the action.
+ * After creating the cc.EaseBezierAction, also need to manually call setBezierParamer.
+ * According to the set point, calculate the trajectory. + * @static + * @param action + * @returns {cc.EaseBezierAction} + * + * @deprecated since v3.0
Please use action.easing(cc.easeBezierAction()) + * + * @example + * //The old usage + * var action = cc.EaseBezierAction.create(action); + * action.setBezierParamer(0.5, 0.5, 1.0, 1.0); + * //The new usage + * action.easing(cc.easeBezierAction(0.5, 0.5, 1.0, 1.0)); + */ +cc.EaseBezierAction.create = function(action){ + return new cc.EaseBezierAction(action); +}; + +/** + * Creates the action easing object.
+ * Into the 4 reference point.
+ * To calculate the motion curve. + * @param {Number} p0 The first bezier parameter + * @param {Number} p1 The second bezier parameter + * @param {Number} p2 The third bezier parameter + * @param {Number} p3 The fourth bezier parameter + * @returns {Object} + * @example + * // example + * action.easing(cc.easeBezierAction(0.5, 0.5, 1.0, 1.0)); + */ +cc.easeBezierAction = function(p0, p1, p2, p3){ + return { + easing: function(time){ + return cc.EaseBezierAction.prototype._updateTime(p0, p1, p2, p3, time); + }, + reverse: function(){ + return cc.easeBezierAction(p3, p2, p1, p0); + } + }; +}; + +/** + * cc.EaseQuadraticActionIn action.
+ * Reference easeInQuad:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @class + * @extends cc.ActionEase + * + * @deprecated since v3.0
Please use action.easing(cc.easeQuadraticAction()) + * + * @example + * //The old usage + * cc.EaseQuadraticActionIn.create(action); + * //The new usage + * action.easing(cc.easeQuadraticActionIn()); + */ +cc.EaseQuadraticActionIn = cc.ActionEase.extend(/** @lends cc.EaseQuadraticActionIn# */{ + + _updateTime: function(time){ + return Math.pow(time, 2); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update: function(dt){ + this._inner.update(this._updateTime(dt)); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseQuadraticActionIn} + */ + clone: function(){ + var action = new cc.EaseQuadraticActionIn(); + action.initWithAction(this._inner.clone()); + return action; + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseQuadraticActionIn} + */ + reverse: function(){ + return new cc.EaseQuadraticActionIn(this._inner.reverse()); + } + +}); + +/** + * Creates the cc.EaseQuadRaticActionIn.
+ * Reference easeInQuad:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @static + * @param action + * @returns {cc.EaseQuadraticActionIn} + * + * @deprecated since v3.0
Please use action.easing(cc.easeQuadraticAction()) + * + * @example + * //The old usage + * cc.EaseQuadraticActionIn.create(action); + * //The new usage + * action.easing(cc.easeQuadraticActionIn()); + */ +cc.EaseQuadraticActionIn.create = function(action){ + return new cc.EaseQuadraticActionIn(action); +}; + +cc._easeQuadraticActionIn = { + easing: cc.EaseQuadraticActionIn.prototype._updateTime, + reverse: function(){ + return cc._easeQuadraticActionIn; + } +}; + +/** + * Creates the action easing object.
+ * Reference easeInQuad:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @returns {Object} + * @example + * //example + * action.easing(cc.easeQuadraticActionIn()); + */ +cc.easeQuadraticActionIn = function(){ + return cc._easeQuadraticActionIn; +}; + +/** + * cc.EaseQuadraticActionIn action.
+ * Reference easeOutQuad:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @class + * @extends cc.ActionEase + * + * @deprecated since v3.0
Please use action.easing(cc.easeQuadraticActionOut()) + * + * @example + * //The old usage + * cc.EaseQuadraticActionOut.create(action); + * //The new usage + * action.easing(cc.easeQuadraticActionOut()); + */ +cc.EaseQuadraticActionOut = cc.ActionEase.extend(/** @lends cc.EaseQuadraticActionOut# */{ + + _updateTime: function(time){ + return -time*(time-2); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update: function(dt){ + this._inner.update(this._updateTime(dt)); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseQuadraticActionOut} + */ + clone: function(){ + var action = new cc.EaseQuadraticActionOut(); + action.initWithAction(); + return action; + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseQuadraticActionOut} + */ + reverse: function(){ + return new cc.EaseQuadraticActionOut(this._inner.reverse()); + } +}); + +/** + * Creates the action.
+ * Reference easeOutQuad:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @static + * @param action + * @returns {cc.EaseQuadraticActionOut} + * + * @deprecated since v3.0
Please use action.easing(cc.easeQuadraticActionOut()) + * + * @example + * //The old usage + * cc.EaseQuadraticActionOut.create(action); + * //The new usage + * action.easing(cc.easeQuadraticActionOut()); + */ +cc.EaseQuadraticActionOut.create = function(action){ + return new cc.EaseQuadraticActionOut(action); +}; + +cc._easeQuadraticActionOut = { + easing: cc.EaseQuadraticActionOut.prototype._updateTime, + reverse: function(){ + return cc._easeQuadraticActionOut; + } +}; +/** + * Creates the action easing object.
+ * Reference easeOutQuad:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @function + * @returns {Object} + * @example + * //example + * action.easing(cc.easeQuadraticActionOut()); + */ +cc.easeQuadraticActionOut = function(){ + return cc._easeQuadraticActionOut; +}; + +/** + * cc.EaseQuadraticActionInOut action.
+ * Reference easeInOutQuad:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @class + * @extends cc.ActionEase + * + * @deprecated since v3.0
Please use action.easing(cc.easeQuadraticActionInOut()) + * + * @example + * //The old usage + * cc.EaseQuadraticActionInOut.create(action); + * //The new usage + * action.easing(cc.easeQuadraticActionInOut()); + */ +cc.EaseQuadraticActionInOut = cc.ActionEase.extend(/** @lends cc.EaseQuadraticActionInOut# */{ + _updateTime: function(time){ + var resultTime = time; + time *= 2; + if(time < 1){ + resultTime = time * time * 0.5; + }else{ + --time; + resultTime = -0.5 * ( time * ( time - 2 ) - 1) + } + return resultTime; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update: function(dt){ + this._inner.update(this._updateTime(dt)); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseQuadraticActionInOut} + */ + clone: function(){ + var action = new cc.EaseQuadraticActionInOut(); + action.initWithAction(this._inner.clone()); + return action; + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseQuadraticActionInOut} + */ + reverse: function(){ + return new cc.EaseQuadraticActionInOut(this._inner.reverse()); + } +}); + +/** + * Creates the action.
+ * Reference easeInOutQuad:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @static + * + * @deprecated since v3.0
Please use action.easing(cc.easeQuadraticActionInOut()) + * + * @example + * //The old usage + * cc.EaseQuadraticActionInOut.create(action); + * //The new usage + * action.easing(cc.easeQuadraticActionInOut()); + * + * @param action + * @returns {cc.EaseQuadraticActionInOut} + */ +cc.EaseQuadraticActionInOut.create = function(action){ + return new cc.EaseQuadraticActionInOut(action); +}; + +cc._easeQuadraticActionInOut = { + easing: cc.EaseQuadraticActionInOut.prototype._updateTime, + reverse: function(){ + return cc._easeQuadraticActionInOut; + } +}; + +/** + * Creates the action easing object.
+ * Reference easeInOutQuad:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @function + * @returns {Object} + * @example + * //example + * action.easing(cc.easeQuadraticActionInOut()); + */ +cc.easeQuadraticActionInOut = function(){ + return cc._easeQuadraticActionInOut; +}; + +/** + * cc.EaseQuarticActionIn action.
+ * Reference easeInQuart:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @class + * @extends cc.ActionEase + * + * @deprecated since v3.0
Please use action.easing(cc.easeQuarticActionIn()); + * + * @example + * //The old usage + * cc.EaseQuarticActionIn.create(action); + * //The new usage + * action.easing(cc.easeQuarticActionIn()); + */ +cc.EaseQuarticActionIn = cc.ActionEase.extend(/** @lends cc.EaseQuarticActionIn# */{ + _updateTime: function(time){ + return time * time * time * time; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update: function(dt){ + this._inner.update(this._updateTime(dt)); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseQuarticActionIn} + */ + clone: function(){ + var action = new cc.EaseQuarticActionIn(); + action.initWithAction(this._inner.clone()); + return action; + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseQuarticActionIn} + */ + reverse: function(){ + return new cc.EaseQuarticActionIn(this._inner.reverse()); + } +}); + +/** + * Creates the action.
+ * Reference easeInQuart:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @static + * + * @deprecated since v3.0
Please use action.easing(cc.easeQuarticActionIn()); + * + * @example + * //The old usage + * cc.EaseQuarticActionIn.create(action); + * //The new usage + * action.easing(cc.easeQuarticActionIn()); + * + * @param action + * @returns {cc.EaseQuarticActionIn} + */ +cc.EaseQuarticActionIn.create = function(action){ + return new cc.EaseQuarticActionIn(action); +}; + +cc._easeQuarticActionIn = { + easing: cc.EaseQuarticActionIn.prototype._updateTime, + reverse: function(){ + return cc._easeQuarticActionIn; + } +}; +/** + * Creates the action easing object.
+ * Reference easeIntQuart:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @function + * @returns {Object} + * @example + * //example + * action.easing(cc.easeQuarticActionIn()); + */ +cc.easeQuarticActionIn = function(){ + return cc._easeQuarticActionIn; +}; + +/** + * cc.EaseQuarticActionOut action.
+ * Reference easeOutQuart:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @class + * @extends cc.ActionEase + * + * @deprecated since v3.0
Please use action.easing(cc.QuarticActionOut()); + * + * @example + * //The old usage + * cc.EaseQuarticActionOut.create(action); + * //The new usage + * action.easing(cc.EaseQuarticActionOut()); + */ +cc.EaseQuarticActionOut = cc.ActionEase.extend(/** @lends cc.EaseQuarticActionOut# */{ + _updateTime: function(time){ + time -= 1; + return -(time * time * time * time - 1); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update: function(dt){ + this._inner.update(this._updateTime(dt)); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseQuarticActionOut} + */ + clone: function(){ + var action = new cc.EaseQuarticActionOut(); + action.initWithAction(this._inner.clone()); + return action; + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseQuarticActionOut} + */ + reverse: function(){ + return new cc.EaseQuarticActionOut(this._inner.reverse()); + } +}); + +/** + * Creates the action.
+ * Reference easeOutQuart:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @static + * + * @deprecated since v3.0
Please use action.easing(cc.QuarticActionOut()); + * + * @example + * //The old usage + * cc.EaseQuarticActionOut.create(action); + * //The new usage + * action.easing(cc.EaseQuarticActionOut()); + * + * @param action + * @returns {cc.EaseQuarticActionOut} + */ +cc.EaseQuarticActionOut.create = function(action){ + return new cc.EaseQuarticActionOut(action); +}; + +cc._easeQuarticActionOut = { + easing: cc.EaseQuarticActionOut.prototype._updateTime, + reverse: function(){ + return cc._easeQuarticActionOut; + } +}; + +/** + * Creates the action easing object.
+ * Reference easeOutQuart:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @function + * @returns {Object} + * @example + * //example + * action.easing(cc.QuarticActionOut()); + */ +cc.easeQuarticActionOut = function(){ + return cc._easeQuarticActionOut; +}; + +/** + * cc.EaseQuarticActionInOut action.
+ * Reference easeInOutQuart:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @class + * @extends cc.ActionEase + * + * @deprecated since v3.0
Please use action.easing(cc.easeQuarticActionInOut()); + * + * @example + * //The old usage + * cc.EaseQuarticActionInOut.create(action); + * //The new usage + * action.easing(cc.easeQuarticActionInOut()); + */ +cc.EaseQuarticActionInOut = cc.ActionEase.extend(/** @lends cc.EaseQuarticActionInOut# */{ + _updateTime: function(time){ + time = time*2; + if (time < 1) + return 0.5 * time * time * time * time; + time -= 2; + return -0.5 * (time * time * time * time - 2); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update: function(dt){ + this._inner.update(this._updateTime(dt)); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseQuarticActionInOut} + */ + clone: function(){ + var action = new cc.EaseQuarticActionInOut(); + action.initWithAction(this._inner.clone()); + return action; + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseQuarticActionInOut} + */ + reverse: function(){ + return new cc.EaseQuarticActionInOut(this._inner.reverse()); + } +}); + +/** + * Creates the action.
+ * Reference easeInOutQuart:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @static + * + * @deprecated since v3.0
Please use action.easing(cc.easeQuarticActionInOut()); + * + * @example + * //The old usage + * cc.EaseQuarticActionInOut.create(action); + * //The new usage + * action.easing(cc.easeQuarticActionInOut()); + * + * @param action + * @returns {cc.EaseQuarticActionInOut} + */ +cc.EaseQuarticActionInOut.create = function(action){ + return new cc.EaseQuarticActionInOut(action); +}; + +cc._easeQuarticActionInOut = { + easing: cc.EaseQuarticActionInOut.prototype._updateTime, + reverse: function(){ + return cc._easeQuarticActionInOut; + } +}; +/** + * Creates the action easing object.
+ * Reference easeInOutQuart:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @function + * @returns {Object} + */ +cc.easeQuarticActionInOut = function(){ + return cc._easeQuarticActionInOut; +}; + +/** + * cc.EaseQuinticActionIn action.
+ * Reference easeInQuint:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @class + * @extends cc.ActionEase + * + * @deprecated since v3.0
Please use action.easing(cc.easeQuinticActionIn()); + * + * @example + * //The old usage + * cc.EaseQuinticActionIn.create(action); + * //The new usage + * action.easing(cc.easeQuinticActionIn()); + */ +cc.EaseQuinticActionIn = cc.ActionEase.extend(/** @lends cc.EaseQuinticActionIn# */{ + _updateTime: function(time){ + return time * time * time * time * time; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update: function(dt){ + this._inner.update(this._updateTime(dt)); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseQuinticActionIn} + */ + clone: function(){ + var action = new cc.EaseQuinticActionIn(); + action.initWithAction(this._inner.clone()); + return action; + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseQuinticActionIn} + */ + reverse: function(){ + return new cc.EaseQuinticActionIn(this._inner.reverse()); + } +}); + +/** + * Creates the action.
+ * Reference easeInQuint:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @static + * + * @deprecated since v3.0
Please use action.easing(cc.easeQuinticActionIn()); + * + * @example + * //The old usage + * cc.EaseQuinticActionIn.create(action); + * //The new usage + * action.easing(cc.easeQuinticActionIn()); + * + * @param action + * @returns {cc.EaseQuinticActionIn} + */ +cc.EaseQuinticActionIn.create = function(action){ + return new cc.EaseQuinticActionIn(action); +}; + +cc._easeQuinticActionIn = { + easing: cc.EaseQuinticActionIn.prototype._updateTime, + reverse: function(){ + return cc._easeQuinticActionIn; + } +}; + +/** + * Creates the action easing object.
+ * Reference easeInQuint:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @function + * @returns {Object} + * @example + * //example + * action.easing(cc.easeQuinticActionIn()); + */ +cc.easeQuinticActionIn = function(){ + return cc._easeQuinticActionIn; +}; + +/** + * cc.EaseQuinticActionOut action.
+ * Reference easeQuint:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @class + * @extends cc.ActionEase + * + * @deprecated since v3.0
Please use action.easing(cc.easeQuadraticActionOut()); + * + * @example + * //The old usage + * cc.EaseQuinticActionOut.create(action); + * //The new usage + * action.easing(cc.easeQuadraticActionOut()); + */ +cc.EaseQuinticActionOut = cc.ActionEase.extend(/** @lends cc.EaseQuinticActionOut# */{ + _updateTime: function(time){ + time -=1; + return (time * time * time * time * time + 1); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update: function(dt){ + this._inner.update(this._updateTime(dt)); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseQuinticActionOut} + */ + clone: function(){ + var action = new cc.EaseQuinticActionOut(); + action.initWithAction(this._inner.clone()); + return action; + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseQuinticActionOut} + */ + reverse: function(){ + return new cc.EaseQuinticActionOut(this._inner.reverse()); + } +}); + +/** + * Creates the action.
+ * Reference easeOutQuint:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @static + * + * @deprecated since v3.0
Please use action.easing(cc.easeQuadraticActionOut()); + * + * @example + * //The old usage + * cc.EaseQuinticActionOut.create(action); + * //The new usage + * action.easing(cc.easeQuadraticActionOut()); + * + * @param action + * @returns {cc.EaseQuinticActionOut} + */ +cc.EaseQuinticActionOut.create = function(action){ + return new cc.EaseQuinticActionOut(action); +}; + +cc._easeQuinticActionOut = { + easing: cc.EaseQuinticActionOut.prototype._updateTime, + reverse: function(){ + return cc._easeQuinticActionOut; + } +}; + +/** + * Creates the action easing object.
+ * Reference easeOutQuint:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @function + * @returns {Object} + * @example + * //example + * action.easing(cc.easeQuadraticActionOut()); + */ +cc.easeQuinticActionOut = function(){ + return cc._easeQuinticActionOut; +}; + +/** + * cc.EaseQuinticActionInOut action.
+ * Reference easeInOutQuint:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @class + * @extends cc.ActionEase + * + * @deprecated since v3.0
Please use action.easing(cc.easeQuinticActionInOut()); + * + * @example + * //The old usage + * cc.EaseQuinticActionInOut.create(action); + * //The new usage + * action.easing(cc.easeQuinticActionInOut()); + */ +cc.EaseQuinticActionInOut = cc.ActionEase.extend(/** @lends cc.EaseQuinticActionInOut# */{ + _updateTime: function(time){ + time = time*2; + if (time < 1) + return 0.5 * time * time * time * time * time; + time -= 2; + return 0.5 * (time * time * time * time * time + 2); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update: function(dt){ + this._inner.update(this._updateTime(dt)); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseQuinticActionInOut} + */ + clone: function(){ + var action = new cc.EaseQuinticActionInOut(); + action.initWithAction(this._inner.clone()); + return action; + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseQuinticActionInOut} + */ + reverse: function(){ + return new cc.EaseQuinticActionInOut(this._inner.reverse()); + } +}); + +/** + * Creates the action.
+ * Reference easeInOutQuint:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @static + * + * @deprecated since v3.0
Please use action.easing(cc.easeQuinticActionInOut()); + * + * @example + * //The old usage + * cc.EaseQuinticActionInOut.create(action); + * //The new usage + * action.easing(cc.easeQuinticActionInOut()); + * + * @param action + * @returns {cc.EaseQuinticActionInOut} + */ +cc.EaseQuinticActionInOut.create = function(action){ + return new cc.EaseQuinticActionInOut(action); +}; + +cc._easeQuinticActionInOut = { + easing: cc.EaseQuinticActionInOut.prototype._updateTime, + reverse: function(){ + return cc._easeQuinticActionInOut; + } +}; + +/** + * Creates the action easing object.
+ * Reference easeInOutQuint:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @function + * @returns {Object} + * @example + * //example + * action.easing(cc.easeQuinticActionInOut()); + */ +cc.easeQuinticActionInOut = function(){ + return cc._easeQuinticActionInOut; +}; + +/** + * cc.EaseCircleActionIn action.
+ * Reference easeInCirc:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @class + * @extends cc.ActionEase + * + * @deprecated since v3.0
Please use action.easing(cc.easeCircleActionIn()); + * + * @example + * //The old usage + * cc.EaseCircleActionIn.create(action); + * //The new usage + * action.easing(cc.easeCircleActionIn()); + */ +cc.EaseCircleActionIn = cc.ActionEase.extend(/** @lends cc.EaseCircleActionIn# */{ + _updateTime: function(time){ + return -1 * (Math.sqrt(1 - time * time) - 1); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update: function(dt){ + this._inner.update(this._updateTime(dt)); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseCircleActionIn} + */ + clone: function(){ + var action = new cc.EaseCircleActionIn(); + action.initWithAction(this._inner.clone()); + return action; + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseCircleActionIn} + */ + reverse: function(){ + return new cc.EaseCircleActionIn(this._inner.reverse()); + } +}); + +/** + * Creates the action.
+ * Reference easeInCirc:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @static + * + * @deprecated since v3.0
Please use action.easing(cc.easeCircleActionIn()); + * + * @example + * //The old usage + * cc.EaseCircleActionIn.create(action); + * //The new usage + * action.easing(cc.easeCircleActionIn()); + * + * @param action + * @returns {cc.EaseCircleActionIn} + */ +cc.EaseCircleActionIn.create = function(action){ + return new cc.EaseCircleActionIn(action); +}; + +cc._easeCircleActionIn = { + easing: cc.EaseCircleActionIn.prototype._updateTime, + reverse: function(){ + return cc._easeCircleActionIn; + } +}; + +/** + * Creates the action easing object.
+ * Reference easeInCirc:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @function + * @returns {Object} + * @example + * //example + * action.easing(cc.easeCircleActionIn()); + */ +cc.easeCircleActionIn = function(){ + return cc._easeCircleActionIn; +}; + +/** + * cc.EaseCircleActionOut action.
+ * Reference easeOutCirc:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @class + * @extends cc.ActionEase + * + * @deprecated since v3.0
Please use action.easing(cc.easeCircleActionOut()); + * + * @example + * //The old usage + * cc.EaseCircleActionOut.create(action); + * //The new usage + * action.easing(cc.easeCircleActionOut()); + */ +cc.EaseCircleActionOut = cc.ActionEase.extend(/** @lends cc.EaseCircleActionOut# */{ + _updateTime: function(time){ + time = time - 1; + return Math.sqrt(1 - time * time); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update: function(dt){ + this._inner.update(this._updateTime(dt)); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseCircleActionOut} + */ + clone: function(){ + var action = new cc.EaseCircleActionOut(); + action.initWithAction(this._inner.clone()); + return action; + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseCircleActionOut} + */ + reverse: function(){ + return new cc.EaseCircleActionOut(this._inner.reverse()); + } +}); + +/** + * Creates the action.
+ * Reference easeOutCirc:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @static + * + * @deprecated since v3.0
Please use action.easing(cc.easeCircleActionOut()); + * + * @example + * //The old usage + * cc.EaseCircleActionOut.create(action); + * //The new usage + * action.easing(cc.easeCircleActionOut()); + * + * @param action + * @returns {cc.EaseCircleActionOut} + */ +cc.EaseCircleActionOut.create = function(action){ + return new cc.EaseCircleActionOut(action); +}; + +cc._easeCircleActionOut = { + easing: cc.EaseCircleActionOut.prototype._updateTime, + reverse: function(){ + return cc._easeCircleActionOut; + } +}; + +/** + * Creates the action easing object.
+ * Reference easeOutCirc:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @function + * @returns {Object} + * @exampple + * //example + * actioneasing(cc.easeCircleActionOut()); + */ +cc.easeCircleActionOut = function(){ + return cc._easeCircleActionOut; +}; + +/** + * cc.EaseCircleActionInOut action.
+ * Reference easeInOutCirc:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @class + * @extends cc.ActionEase + * + * @deprecated since v3.0
Please use action.easing(cc.easeCircleActionInOut()); + * + * @example + * //The old usage + * cc.EaseCircleActionInOut.create(action); + * //The new usage + * action.easing(cc.easeCircleActionInOut()); + */ +cc.EaseCircleActionInOut = cc.ActionEase.extend(/** @lends cc.EaseCircleActionInOut# */{ + _updateTime: function(time){ + time = time * 2; + if (time < 1) + return -0.5 * (Math.sqrt(1 - time * time) - 1); + time -= 2; + return 0.5 * (Math.sqrt(1 - time * time) + 1); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update: function(dt){ + this._inner.update(this._updateTime(dt)); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseCircleActionInOut} + */ + clone: function(){ + var action = new cc.EaseCircleActionInOut(); + action.initWithAction(this._inner.clone()); + return action; + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseCircleActionInOut} + */ + reverse: function(){ + return new cc.EaseCircleActionInOut(this._inner.reverse()); + } +}); + +/** + * Creates the action.
+ * Reference easeInOutCirc:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @static + * + * @deprecated since v3.0
Please use action.easing(cc.easeCircleActionInOut()); + * + * @example + * //The old usage + * cc.EaseCircleActionInOut.create(action); + * //The new usage + * action.easing(cc.easeCircleActionInOut()); + * + * @param action + * @returns {cc.EaseCircleActionInOut} + */ +cc.EaseCircleActionInOut.create = function(action){ + return new cc.EaseCircleActionInOut(action); +}; + +cc._easeCircleActionInOut = { + easing: cc.EaseCircleActionInOut.prototype._updateTime, + reverse: function(){ + return cc._easeCircleActionInOut; + } +}; + +/** + * Creates the action easing object.
+ * Reference easeInOutCirc:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @function + * @returns {Object} + * @example + * //example + * action.easing(cc.easeCircleActionInOut()); + */ +cc.easeCircleActionInOut = function(){ + return cc._easeCircleActionInOut; +}; + +/** + * cc.EaseCubicActionIn action.
+ * Reference easeInCubic:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @class + * @extends cc.ActionEase + * + * @deprecated since v3.0
action.easing(cc.easeCubicActionIn()); + * + * @example + * //The old usage + * cc.EaseCubicActionIn.create(action); + * //The new usage + * action.easing(cc.easeCubicActionIn()); + */ +cc.EaseCubicActionIn = cc.ActionEase.extend(/** @lends cc.EaseCubicActionIn# */{ + _updateTime: function(time){ + return time * time * time; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update: function(dt){ + this._inner.update(this._updateTime(dt)); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseCubicActionIn} + */ + clone: function(){ + var action = new cc.EaseCubicActionIn(); + action.initWithAction(this._inner.clone()); + return action; + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseCubicActionIn} + */ + reverse: function(){ + return new cc.EaseCubicActionIn(this._inner.reverse()); + } +}); + +/** + * Creates the action.
+ * Reference easeInCubic:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @static + * + * @deprecated since v3.0
action.easing(cc.easeCubicActionIn()); + * + * @example + * //The old usage + * cc.EaseCubicActionIn.create(action); + * //The new usage + * action.easing(cc.easeCubicActionIn()); + * + * @param action + * @returns {cc.EaseCubicActionIn} + */ +cc.EaseCubicActionIn.create = function(action){ + return new cc.EaseCubicActionIn(action); +}; + +cc._easeCubicActionIn = { + easing: cc.EaseCubicActionIn.prototype._updateTime, + reverse: function(){ + return cc._easeCubicActionIn; + } +}; + +/** + * Creates the action easing object.
+ * Reference easeInCubic:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @function + * @returns {Object} + * @example + * //example + * action.easing(cc.easeCubicActionIn()); + */ +cc.easeCubicActionIn = function(){ + return cc._easeCubicActionIn; +}; + +/** + * cc.EaseCubicActionOut action.
+ * Reference easeOutCubic:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @class + * @extends cc.ActionEase + * + * @deprecated since v3.0
Please use action.easing(cc.easeCubicActionOut()); + * + * @example + * //The old usage + * cc.EaseCubicActionOut.create(action); + * //The new usage + * action.easing(cc.easeCubicActionOut()); + */ +cc.EaseCubicActionOut = cc.ActionEase.extend(/** @lends cc.EaseCubicActionOut# */{ + _updateTime: function(time){ + time -= 1; + return (time * time * time + 1); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update: function(dt){ + this._inner.update(this._updateTime(dt)); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseCubicActionOut} + */ + clone: function(){ + var action = new cc.EaseCubicActionOut(); + action.initWithAction(this._inner.clone()); + return action; + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseCubicActionOut} + */ + reverse: function(){ + return new cc.EaseCubicActionOut(this._inner.reverse()); + } +}); + +/** + * Creates the action.
+ * Reference easeOutCubic:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @static + * + * @deprecated since v3.0
Please use action.easing(cc.easeCubicActionOut()); + * + * @example + * //The old usage + * cc.EaseCubicActionOut.create(action); + * //The new usage + * action.easing(cc.easeCubicActionOut()); + * + * @param action + * @returns {cc.EaseCubicActionOut} + */ +cc.EaseCubicActionOut.create = function(action){ + return new cc.EaseCubicActionOut(action); +}; + +cc._easeCubicActionOut = { + easing: cc.EaseCubicActionOut.prototype._updateTime, + reverse: function(){ + return cc._easeCubicActionOut; + } +}; + +/** + * Creates the action easing object.
+ * Reference easeOutCubic:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @function + * @returns {Object} + * @example + * //example + * action.easing(cc.easeCubicActionOut()); + */ +cc.easeCubicActionOut = function(){ + return cc._easeCubicActionOut; +}; + +/** + * cc.EaseCubicActionInOut action.
+ * Reference easeInOutCubic:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @class + * @extends cc.ActionEase + * + * @deprecated since v3.0
Please use action.easing(cc.easeCubicActionInOut()); + * + * @example + * //The old usage + * cc.EaseCubicActionInOut.create(action); + * //The new usage + * action.easing(cc.easeCubicActionInOut()); + */ +cc.EaseCubicActionInOut = cc.ActionEase.extend(/** @lends cc.EaseCubicActionInOut# */{ + _updateTime: function(time){ + time = time*2; + if (time < 1) + return 0.5 * time * time * time; + time -= 2; + return 0.5 * (time * time * time + 2); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update: function(dt){ + this._inner.update(this._updateTime(dt)); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @returns {cc.EaseCubicActionInOut} + */ + clone: function(){ + var action = new cc.EaseCubicActionInOut(); + action.initWithAction(this._inner.clone()); + return action; + }, + + /** + * Create a action. Opposite with the original motion trajectory. + * @return {cc.EaseCubicActionInOut} + */ + reverse: function(){ + return new cc.EaseCubicActionInOut(this._inner.reverse()); + } +}); + +/** + * Creates the action.
+ * Reference easeInOutCubic:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @static + * + * @deprecated since v3.0
Please use action.easing(cc.easeCubicActionInOut()); + * + * @example + * //The old usage + * cc.EaseCubicActionInOut.create(action); + * //The new usage + * action.easing(cc.easeCubicActionInOut()); + * + * @param action + * @returns {cc.EaseCubicActionInOut} + */ +cc.EaseCubicActionInOut.create = function(action){ + return new cc.EaseCubicActionInOut(action); +}; + +cc._easeCubicActionInOut = { + easing: cc.EaseCubicActionInOut.prototype._updateTime, + reverse: function(){ + return cc._easeCubicActionInOut; + } +}; + +/** + * Creates the action easing object.
+ * Reference easeInOutCubic:
+ * {@link http://www.zhihu.com/question/21981571/answer/19925418} + * @function + * @returns {Object} + */ +cc.easeCubicActionInOut = function(){ + return cc._easeCubicActionInOut; +}; diff --git a/frameworks/cocos2d-html5/cocos2d/actions/CCActionInstant.js b/frameworks/cocos2d-html5/cocos2d/actions/CCActionInstant.js new file mode 100644 index 0000000..f1e73f9 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/actions/CCActionInstant.js @@ -0,0 +1,749 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * Instant actions are immediate actions. They don't have a duration like. + * the CCIntervalAction actions. + * @class + * @extends cc.FiniteTimeAction + */ +cc.ActionInstant = cc.FiniteTimeAction.extend(/** @lends cc.ActionInstant# */{ + /** + * return true if the action has finished. + * @return {Boolean} + */ + isDone:function () { + return true; + }, + + /** + * called every frame with it's delta time.
+ * DON'T override unless you know what you are doing. + * @param {Number} dt + */ + step:function (dt) { + this.update(1); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + //nothing + }, + + /** + * returns a reversed action.
+ * For example:
+ * - The action will be x coordinates of 0 move to 100.
+ * - The reversed action will be x of 100 move to 0. + * - Will be rewritten + * @returns {cc.Action} + */ + reverse:function(){ + return this.clone(); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @return {cc.FiniteTimeAction} + */ + clone:function(){ + return new cc.ActionInstant(); + } +}); + +/** + * Show the node. + * @class + * @extends cc.ActionInstant + */ +cc.Show = cc.ActionInstant.extend(/** @lends cc.Show# */{ + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + this.target.visible = true; + }, + + /** + * returns a reversed action.
+ * For example:
+ * - The action will be x coordinates of 0 move to 100.
+ * - The reversed action will be x of 100 move to 0. + * - Will be rewritten + * @returns {cc.Hide} + */ + reverse:function () { + return new cc.Hide(); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @return {cc.FiniteTimeAction} + */ + clone:function(){ + return new cc.Show(); + } +}); + +/** + * Show the Node. + * @function + * @return {cc.Show} + * @example + * // example + * var showAction = cc.show(); + */ +cc.show = function () { + return new cc.Show(); +}; + +/** + * Show the Node. Please use cc.show instead. + * @static + * @deprecated since v3.0
Please use cc.show instead. + * @return {cc.Show} + */ +cc.Show.create = cc.show; + +/** + * Hide the node. + * @class + * @extends cc.ActionInstant + */ +cc.Hide = cc.ActionInstant.extend(/** @lends cc.Hide# */{ + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + this.target.visible = false; + }, + + /** + * returns a reversed action.
+ * For example:
+ * - The action will be x coordinates of 0 move to 100.
+ * - The reversed action will be x of 100 move to 0. + * - Will be rewritten + * @returns {cc.Show} + */ + reverse:function () { + return new cc.Show(); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @return {cc.Hide} + */ + clone:function(){ + return new cc.Hide(); + } +}); + +/** + * Hide the node. + * @function + * @return {cc.Hide} + * @example + * // example + * var hideAction = cc.hide(); + */ +cc.hide = function () { + return new cc.Hide(); +}; + +/** + * Hide the node. Please use cc.hide instead. + * @static + * @deprecated since v3.0
Please use cc.hide instead. + * @return {cc.Hide} + * @example + * // example + * var hideAction = cc.hide(); + */ +cc.Hide.create = cc.hide; + +/** + * Toggles the visibility of a node. + * @class + * @extends cc.ActionInstant + */ +cc.ToggleVisibility = cc.ActionInstant.extend(/** @lends cc.ToggleVisibility# */{ + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + this.target.visible = !this.target.visible; + }, + + /** + * returns a reversed action. + * @returns {cc.ToggleVisibility} + */ + reverse:function () { + return new cc.ToggleVisibility(); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @return {cc.ToggleVisibility} + */ + clone:function(){ + return new cc.ToggleVisibility(); + } +}); + +/** + * Toggles the visibility of a node. + * @function + * @return {cc.ToggleVisibility} + * @example + * // example + * var toggleVisibilityAction = cc.toggleVisibility(); + */ +cc.toggleVisibility = function () { + return new cc.ToggleVisibility(); +}; + +/** + * Toggles the visibility of a node. Please use cc.toggleVisibility instead. + * @static + * @deprecated since v3.0
Please use cc.toggleVisibility instead. + * @return {cc.ToggleVisibility} + */ +cc.ToggleVisibility.create = cc.toggleVisibility; + +/** + * Delete self in the next frame. + * @class + * @extends cc.ActionInstant + * @param {Boolean} [isNeedCleanUp=true] + * + * @example + * // example + * var removeSelfAction = new cc.RemoveSelf(false); + */ +cc.RemoveSelf = cc.ActionInstant.extend({ + _isNeedCleanUp: true, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Create a RemoveSelf object with a flag indicate whether the target should be cleaned up while removing. + * @param {Boolean} [isNeedCleanUp=true] + */ + ctor:function(isNeedCleanUp){ + cc.FiniteTimeAction.prototype.ctor.call(this); + + isNeedCleanUp !== undefined && this.init(isNeedCleanUp); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function(dt){ + this.target.removeFromParent(this._isNeedCleanUp); + }, + + /** + * Initialization of the node, please do not call this function by yourself, you should pass the parameters to constructor to initialize it
. + * @param isNeedCleanUp + * @returns {boolean} + */ + init:function(isNeedCleanUp){ + this._isNeedCleanUp = isNeedCleanUp; + return true; + }, + + /** + * returns a reversed action. + */ + reverse:function(){ + return new cc.RemoveSelf(this._isNeedCleanUp); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @return {cc.RemoveSelf} + */ + clone:function(){ + return new cc.RemoveSelf(this._isNeedCleanUp); + } +}); + +/** + * Create a RemoveSelf object with a flag indicate whether the target should be cleaned up while removing. + * + * @function + * @param {Boolean} [isNeedCleanUp=true] + * @return {cc.RemoveSelf} + * + * @example + * // example + * var removeSelfAction = cc.removeSelf(); + */ +cc.removeSelf = function(isNeedCleanUp){ + return new cc.RemoveSelf(isNeedCleanUp); +}; + +/** + * Please use cc.removeSelf instead. + * Create a RemoveSelf object with a flag indicate whether the target should be cleaned up while removing. + * + * @static + * @deprecated since v3.0
Please use cc.removeSelf instead. + * @param {Boolean} [isNeedCleanUp=true] + * @return {cc.RemoveSelf} + */ +cc.RemoveSelf.create = cc.removeSelf; + +/** + * Flips the sprite horizontally. + * @class + * @extends cc.ActionInstant + * @param {Boolean} flip Indicate whether the target should be flipped or not + * + * @example + * var flipXAction = new cc.FlipX(true); + */ +cc.FlipX = cc.ActionInstant.extend(/** @lends cc.FlipX# */{ + _flippedX:false, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Create a FlipX action to flip or unflip the target. + * @param {Boolean} flip Indicate whether the target should be flipped or not + */ + ctor:function(flip){ + cc.FiniteTimeAction.prototype.ctor.call(this); + this._flippedX = false; + flip !== undefined && this.initWithFlipX(flip); + }, + + /** + * initializes the action with a set flipX. + * @param {Boolean} flip + * @return {Boolean} + */ + initWithFlipX:function (flip) { + this._flippedX = flip; + return true; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + this.target.flippedX = this._flippedX; + }, + + /** + * returns a reversed action. + * @return {cc.FlipX} + */ + reverse:function () { + return new cc.FlipX(!this._flippedX); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @return {cc.FiniteTimeAction} + */ + clone:function(){ + var action = new cc.FlipX(); + action.initWithFlipX(this._flippedX); + return action; + } +}); + +/** + * Create a FlipX action to flip or unflip the target. + * + * @function + * @param {Boolean} flip Indicate whether the target should be flipped or not + * @return {cc.FlipX} + * @example + * var flipXAction = cc.flipX(true); + */ +cc.flipX = function (flip) { + return new cc.FlipX(flip); +}; + +/** + * Plese use cc.flipX instead. + * Create a FlipX action to flip or unflip the target + * + * @static + * @deprecated since v3.0
Plese use cc.flipX instead. + * @param {Boolean} flip Indicate whether the target should be flipped or not + * @return {cc.FlipX} + */ +cc.FlipX.create = cc.flipX; + +/** + * Flips the sprite vertically + * @class + * @extends cc.ActionInstant + * @param {Boolean} flip + * @example + * var flipYAction = new cc.FlipY(true); + */ +cc.FlipY = cc.ActionInstant.extend(/** @lends cc.FlipY# */{ + _flippedY:false, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Create a FlipY action to flip or unflip the target. + * + * @param {Boolean} flip + */ + ctor: function(flip){ + cc.FiniteTimeAction.prototype.ctor.call(this); + this._flippedY = false; + + flip !== undefined && this.initWithFlipY(flip); + }, + + /** + * initializes the action with a set flipY. + * @param {Boolean} flip + * @return {Boolean} + */ + initWithFlipY:function (flip) { + this._flippedY = flip; + return true; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + this.target.flippedY = this._flippedY; + }, + + /** + * returns a reversed action. + * @return {cc.FlipY} + */ + reverse:function () { + return new cc.FlipY(!this._flippedY); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @return {cc.FlipY} + */ + clone:function(){ + var action = new cc.FlipY(); + action.initWithFlipY(this._flippedY); + return action; + } +}); + +/** + * Create a FlipY action to flip or unflip the target. + * + * @function + * @param {Boolean} flip + * @return {cc.FlipY} + * @example + * var flipYAction = cc.flipY(true); + */ +cc.flipY = function (flip) { + return new cc.FlipY(flip); +}; + +/** + * Please use cc.flipY instead + * Create a FlipY action to flip or unflip the target + * + * @static + * @deprecated since v3.0
Please use cc.flipY instead. + * @param {Boolean} flip + * @return {cc.FlipY} + */ +cc.FlipY.create = cc.flipY; + +/** + * Places the node in a certain position + * @class + * @extends cc.ActionInstant + * @param {cc.Point|Number} pos + * @param {Number} [y] + * @example + * var placeAction = new cc.Place(cc.p(200, 200)); + * var placeAction = new cc.Place(200, 200); + */ +cc.Place = cc.ActionInstant.extend(/** @lends cc.Place# */{ + _x: 0, + _y: 0, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Creates a Place action with a position. + * @param {cc.Point|Number} pos + * @param {Number} [y] + */ + ctor:function(pos, y){ + cc.FiniteTimeAction.prototype.ctor.call(this); + this._x = 0; + this._y = 0; + + if (pos !== undefined) { + if (pos.x !== undefined) { + y = pos.y; + pos = pos.x; + } + this.initWithPosition(pos, y); + } + }, + + /** + * Initializes a Place action with a position + * @param {number} x + * @param {number} y + * @return {Boolean} + */ + initWithPosition: function (x, y) { + this._x = x; + this._y = y; + return true; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + this.target.setPosition(this._x, this._y); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @return {cc.Place} + */ + clone:function(){ + var action = new cc.Place(); + action.initWithPosition(this._x, this._y); + return action; + } +}); + +/** + * Creates a Place action with a position. + * @function + * @param {cc.Point|Number} pos + * @param {Number} [y] + * @return {cc.Place} + * @example + * // example + * var placeAction = cc.place(cc.p(200, 200)); + * var placeAction = cc.place(200, 200); + */ +cc.place = function (pos, y) { + return new cc.Place(pos, y); +}; + +/** + * Please use cc.place instead. + * Creates a Place action with a position. + * @static + * @deprecated since v3.0
Please use cc.place instead. + * @param {cc.Point|Number} pos + * @param {Number} [y] + * @return {cc.Place} + */ +cc.Place.create = cc.place; + + +/** + * Calls a 'callback'. + * @class + * @extends cc.ActionInstant + * @param {function} selector + * @param {object|null} [selectorTarget] + * @param {*|null} [data] data for function, it accepts all data types. + * @example + * // example + * // CallFunc without data + * var finish = new cc.CallFunc(this.removeSprite, this); + * + * // CallFunc with data + * var finish = new cc.CallFunc(this.removeFromParentAndCleanup, this, true); + */ +cc.CallFunc = cc.ActionInstant.extend(/** @lends cc.CallFunc# */{ + _selectorTarget:null, + _function:null, + _data:null, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Creates a CallFunc action with the callback. + * @param {function} selector + * @param {object|null} [selectorTarget] + * @param {*|null} [data] data for function, it accepts all data types. + */ + ctor:function(selector, selectorTarget, data){ + cc.FiniteTimeAction.prototype.ctor.call(this); + + this.initWithFunction(selector, selectorTarget, data); + }, + + /** + * Initializes the action with a function or function and its target + * @param {function} selector + * @param {object|Null} selectorTarget + * @param {*|Null} [data] data for function, it accepts all data types. + * @return {Boolean} + */ + initWithFunction:function (selector, selectorTarget, data) { + if (selector) { + this._function = selector; + } + if (selectorTarget) { + this._selectorTarget = selectorTarget; + } + if (data !== undefined) { + this._data = data; + } + return true; + }, + + /** + * execute the function. + */ + execute:function () { + if (this._function) { + this._function.call(this._selectorTarget, this.target, this._data); + } + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + this.execute(); + }, + + /** + * Get selectorTarget. + * @return {object} + */ + getTargetCallback:function () { + return this._selectorTarget; + }, + + /** + * Set selectorTarget. + * @param {object} sel + */ + setTargetCallback:function (sel) { + if (sel !== this._selectorTarget) { + if (this._selectorTarget) + this._selectorTarget = null; + this._selectorTarget = sel; + } + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @return {cc.CallFunc} + */ + clone:function(){ + var action = new cc.CallFunc(); + action.initWithFunction(this._function, this._selectorTarget, this._data); + return action; + } +}); + +/** + * Creates the action with the callback + * @function + * @param {function} selector + * @param {object|null} [selectorTarget] + * @param {*|null} [data] data for function, it accepts all data types. + * @return {cc.CallFunc} + * @example + * // example + * // CallFunc without data + * var finish = cc.callFunc(this.removeSprite, this); + * + * // CallFunc with data + * var finish = cc.callFunc(this.removeFromParentAndCleanup, this._grossini, true); + */ +cc.callFunc = function (selector, selectorTarget, data) { + return new cc.CallFunc(selector, selectorTarget, data); +}; + +/** + * Please use cc.callFunc instead. + * Creates the action with the callback. + * @static + * @deprecated since v3.0
Please use cc.callFunc instead. + * @param {function} selector + * @param {object|null} [selectorTarget] + * @param {*|null} [data] data for function, it accepts all data types. + * @return {cc.CallFunc} + */ +cc.CallFunc.create = cc.callFunc; diff --git a/frameworks/cocos2d-html5/cocos2d/actions/CCActionInterval.js b/frameworks/cocos2d-html5/cocos2d/actions/CCActionInterval.js new file mode 100644 index 0000000..905d72b --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/actions/CCActionInterval.js @@ -0,0 +1,3578 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + *

An interval action is an action that takes place within a certain period of time.
+ * It has an start time, and a finish time. The finish time is the parameter
+ * duration plus the start time.

+ * + *

These CCActionInterval actions have some interesting properties, like:
+ * - They can run normally (default)
+ * - They can run reversed with the reverse method
+ * - They can run with the time altered with the Accelerate, AccelDeccel and Speed actions.

+ * + *

For example, you can simulate a Ping Pong effect running the action normally and
+ * then running it again in Reverse mode.

+ * + * @class + * @extends cc.FiniteTimeAction + * @param {Number} d duration in seconds + * @example + * var actionInterval = new cc.ActionInterval(3); + */ +cc.ActionInterval = cc.FiniteTimeAction.extend(/** @lends cc.ActionInterval# */{ + _elapsed: 0, + _firstTick: false, + _easeList: null, + _timesForRepeat: 1, + _repeatForever: false, + _repeatMethod: false,//Compatible with repeat class, Discard after can be deleted + _speed: 1, + _speedMethod: false,//Compatible with speed class, Discard after can be deleted + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {Number} d duration in seconds + */ + ctor: function (d) { + this._speed = 1; + this._timesForRepeat = 1; + this._repeatForever = false; + this.MAX_VALUE = 2; + this._repeatMethod = false;//Compatible with repeat class, Discard after can be deleted + this._speedMethod = false;//Compatible with repeat class, Discard after can be deleted + cc.FiniteTimeAction.prototype.ctor.call(this); + d !== undefined && this.initWithDuration(d); + }, + + /** + * How many seconds had elapsed since the actions started to run. + * @return {Number} + */ + getElapsed: function () { + return this._elapsed; + }, + + /** + * Initializes the action. + * @param {Number} d duration in seconds + * @return {Boolean} + */ + initWithDuration: function (d) { + this._duration = (d === 0) ? cc.FLT_EPSILON : d; + // prevent division by 0 + // This comparison could be in step:, but it might decrease the performance + // by 3% in heavy based action games. + this._elapsed = 0; + this._firstTick = true; + return true; + }, + + /** + * Returns true if the action has finished. + * @return {Boolean} + */ + isDone: function () { + return (this._elapsed >= this._duration); + }, + + /** + * Some additional parameters of cloning. + * @param {cc.Action} action + * @private + */ + _cloneDecoration: function (action) { + action._repeatForever = this._repeatForever; + action._speed = this._speed; + action._timesForRepeat = this._timesForRepeat; + action._easeList = this._easeList; + action._speedMethod = this._speedMethod; + action._repeatMethod = this._repeatMethod; + }, + + _reverseEaseList: function (action) { + if (this._easeList) { + action._easeList = []; + for (var i = 0; i < this._easeList.length; i++) { + action._easeList.push(this._easeList[i].reverse()); + } + } + }, + + /** + * Returns a new clone of the action. + * @returns {cc.ActionInterval} + */ + clone: function () { + var action = new cc.ActionInterval(this._duration); + this._cloneDecoration(action); + return action; + }, + + /** + * Implementation of ease motion. + * + * @example + * //example + * action.easing(cc.easeIn(3.0)); + * @param {Object} easeObj + * @returns {cc.ActionInterval} + */ + easing: function (easeObj) { + if (this._easeList) + this._easeList.length = 0; + else + this._easeList = []; + for (var i = 0; i < arguments.length; i++) + this._easeList.push(arguments[i]); + return this; + }, + + _computeEaseTime: function (dt) { + var locList = this._easeList; + if ((!locList) || (locList.length === 0)) + return dt; + for (var i = 0, n = locList.length; i < n; i++) + dt = locList[i].easing(dt); + return dt; + }, + + /** + * called every frame with it's delta time.
+ * DON'T override unless you know what you are doing. + * + * @param {Number} dt + */ + step: function (dt) { + if (this._firstTick) { + this._firstTick = false; + this._elapsed = 0; + } else + this._elapsed += dt; + + //this.update((1 > (this._elapsed / this._duration)) ? this._elapsed / this._duration : 1); + //this.update(Math.max(0, Math.min(1, this._elapsed / Math.max(this._duration, cc.FLT_EPSILON)))); + var t = this._elapsed / (this._duration > 0.0000001192092896 ? this._duration : 0.0000001192092896); + t = (1 > t ? t : 1); + this.update(t > 0 ? t : 0); + + //Compatible with repeat class, Discard after can be deleted (this._repeatMethod) + if (this._repeatMethod && this._timesForRepeat > 1 && this.isDone()) { + if (!this._repeatForever) { + this._timesForRepeat--; + } + //var diff = locInnerAction.getElapsed() - locInnerAction._duration; + this.startWithTarget(this.target); + // to prevent jerk. issue #390 ,1247 + //this._innerAction.step(0); + //this._innerAction.step(diff); + this.step(this._elapsed - this._duration); + + } + }, + + /** + * Start this action with target. + * @param {cc.Node} target + */ + startWithTarget: function (target) { + cc.Action.prototype.startWithTarget.call(this, target); + this._elapsed = 0; + this._firstTick = true; + }, + + /** + * returns a reversed action.
+ * Will be overwrite. + * + * @return {?cc.Action} + */ + reverse: function () { + cc.log("cc.IntervalAction: reverse not implemented."); + return null; + }, + + /** + * Set amplitude rate. + * @warning It should be overridden in subclass. + * @param {Number} amp + */ + setAmplitudeRate: function (amp) { + // Abstract class needs implementation + cc.log("cc.ActionInterval.setAmplitudeRate(): it should be overridden in subclass."); + }, + + /** + * Get amplitude rate. + * @warning It should be overridden in subclass. + * @return {Number} 0 + */ + getAmplitudeRate: function () { + // Abstract class needs implementation + cc.log("cc.ActionInterval.getAmplitudeRate(): it should be overridden in subclass."); + return 0; + }, + + /** + * Changes the speed of an action, making it take longer (speed>1) + * or less (speed<1) time.
+ * Useful to simulate 'slow motion' or 'fast forward' effect. + * + * @param speed + * @returns {cc.Action} + */ + speed: function (speed) { + if (speed <= 0) { + cc.log("The speed parameter error"); + return this; + } + + this._speedMethod = true;//Compatible with repeat class, Discard after can be deleted + this._speed *= speed; + return this; + }, + + /** + * Get this action speed. + * @return {Number} + */ + getSpeed: function () { + return this._speed; + }, + + /** + * Set this action speed. + * @param {Number} speed + * @returns {cc.ActionInterval} + */ + setSpeed: function (speed) { + this._speed = speed; + return this; + }, + + /** + * Repeats an action a number of times. + * To repeat an action forever use the CCRepeatForever action. + * @param times + * @returns {cc.ActionInterval} + */ + repeat: function (times) { + times = Math.round(times); + if (isNaN(times) || times < 1) { + cc.log("The repeat parameter error"); + return this; + } + this._repeatMethod = true;//Compatible with repeat class, Discard after can be deleted + this._timesForRepeat *= times; + return this; + }, + + /** + * Repeats an action for ever.
+ * To repeat the an action for a limited number of times use the Repeat action.
+ * @returns {cc.ActionInterval} + */ + repeatForever: function () { + this._repeatMethod = true;//Compatible with repeat class, Discard after can be deleted + this._timesForRepeat = this.MAX_VALUE; + this._repeatForever = true; + return this; + } +}); + +/** + * An interval action is an action that takes place within a certain period of time. + * @function + * @param {Number} d duration in seconds + * @return {cc.ActionInterval} + * @example + * // example + * var actionInterval = cc.actionInterval(3); + */ +cc.actionInterval = function (d) { + return new cc.ActionInterval(d); +}; + +/** + * Please use cc.actionInterval instead. + * An interval action is an action that takes place within a certain period of time. + * @static + * @deprecated since v3.0
Please use cc.actionInterval instead. + * @param {Number} d duration in seconds + * @return {cc.ActionInterval} + */ +cc.ActionInterval.create = cc.actionInterval; + +/** + * Runs actions sequentially, one after another. + * @class + * @extends cc.ActionInterval + * @param {Array|cc.FiniteTimeAction} tempArray + * @example + * // create sequence with actions + * var seq = new cc.Sequence(act1, act2); + * + * // create sequence with array + * var seq = new cc.Sequence(actArray); + */ +cc.Sequence = cc.ActionInterval.extend(/** @lends cc.Sequence# */{ + _actions: null, + _split: null, + _last: 0, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Create an array of sequenceable actions. + * @param {Array|cc.FiniteTimeAction} tempArray + */ + ctor: function (tempArray) { + cc.ActionInterval.prototype.ctor.call(this); + this._actions = []; + + var paramArray = (tempArray instanceof Array) ? tempArray : arguments; + var last = paramArray.length - 1; + if ((last >= 0) && (paramArray[last] == null)) + cc.log("parameters should not be ending with null in Javascript"); + + if (last >= 0) { + var prev = paramArray[0], action1; + for (var i = 1; i < last; i++) { + if (paramArray[i]) { + action1 = prev; + prev = cc.Sequence._actionOneTwo(action1, paramArray[i]); + } + } + this.initWithTwoActions(prev, paramArray[last]); + } + }, + + /** + * Initializes the action
+ * @param {cc.FiniteTimeAction} actionOne + * @param {cc.FiniteTimeAction} actionTwo + * @return {Boolean} + */ + initWithTwoActions: function (actionOne, actionTwo) { + if (!actionOne || !actionTwo) + throw new Error("cc.Sequence.initWithTwoActions(): arguments must all be non nil"); + + var d = actionOne._duration + actionTwo._duration; + this.initWithDuration(d); + + this._actions[0] = actionOne; + this._actions[1] = actionTwo; + return true; + }, + + /** + * returns a new clone of the action + * @returns {cc.Sequence} + */ + clone: function () { + var action = new cc.Sequence(); + this._cloneDecoration(action); + action.initWithTwoActions(this._actions[0].clone(), this._actions[1].clone()); + return action; + }, + + /** + * Start the action with target. + * @param {cc.Node} target + */ + startWithTarget: function (target) { + cc.ActionInterval.prototype.startWithTarget.call(this, target); + this._split = this._actions[0]._duration / this._duration; + this._last = -1; + }, + + /** + * stop the action. + */ + stop: function () { + // Issue #1305 + if (this._last !== -1) + this._actions[this._last].stop(); + cc.Action.prototype.stop.call(this); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @param {Number} dt + */ + update: function (dt) { + var new_t, found = 0; + var locSplit = this._split, locActions = this._actions, locLast = this._last, actionFound; + + dt = this._computeEaseTime(dt); + if (dt < locSplit) { + // action[0] + new_t = (locSplit !== 0) ? dt / locSplit : 1; + + if (found === 0 && locLast === 1) { + // Reverse mode ? + // XXX: Bug. this case doesn't contemplate when _last==-1, found=0 and in "reverse mode" + // since it will require a hack to know if an action is on reverse mode or not. + // "step" should be overriden, and the "reverseMode" value propagated to inner Sequences. + locActions[1].update(0); + locActions[1].stop(); + } + } else { + // action[1] + found = 1; + new_t = (locSplit === 1) ? 1 : (dt - locSplit) / (1 - locSplit); + + if (locLast === -1) { + // action[0] was skipped, execute it. + locActions[0].startWithTarget(this.target); + locActions[0].update(1); + locActions[0].stop(); + } + if (!locLast) { + // switching to action 1. stop action 0. + locActions[0].update(1); + locActions[0].stop(); + } + } + + actionFound = locActions[found]; + // Last action found and it is done. + if (locLast === found && actionFound.isDone()) + return; + + // Last action found and it is done + if (locLast !== found) + actionFound.startWithTarget(this.target); + + new_t = new_t * actionFound._timesForRepeat; + actionFound.update(new_t > 1 ? new_t % 1 : new_t); + this._last = found; + }, + + /** + * Returns a reversed action. + * @return {cc.Sequence} + */ + reverse: function () { + var action = cc.Sequence._actionOneTwo(this._actions[1].reverse(), this._actions[0].reverse()); + this._cloneDecoration(action); + this._reverseEaseList(action); + return action; + } +}); + +/** helper constructor to create an array of sequenceable actions + * @function + * @param {Array|cc.FiniteTimeAction} tempArray + * @return {cc.Sequence} + * @example + * // example + * // create sequence with actions + * var seq = cc.sequence(act1, act2); + * + * // create sequence with array + * var seq = cc.sequence(actArray); + * todo: It should be use new + */ +cc.sequence = function (/*Multiple Arguments*/tempArray) { + var paramArray = (tempArray instanceof Array) ? tempArray : arguments; + if ((paramArray.length > 0) && (paramArray[paramArray.length - 1] == null)) + cc.log("parameters should not be ending with null in Javascript"); + + var result, current, i, repeat; + while (paramArray && paramArray.length > 0) { + current = Array.prototype.shift.call(paramArray); + repeat = current._timesForRepeat || 1; + current._repeatMethod = false; + current._timesForRepeat = 1; + + i = 0; + if (!result) { + result = current; + i = 1; + } + + for (i; i < repeat; i++) { + result = cc.Sequence._actionOneTwo(result, current); + } + } + + return result; +}; + +/** + * Please use cc.sequence instead. + * helper constructor to create an array of sequenceable actions + * @static + * @deprecated since v3.0
Please use cc.sequence instead. + * @param {Array|cc.FiniteTimeAction} tempArray + * @return {cc.Sequence} + */ +cc.Sequence.create = cc.sequence; + +/** creates the action + * @param {cc.FiniteTimeAction} actionOne + * @param {cc.FiniteTimeAction} actionTwo + * @return {cc.Sequence} + * @private + */ +cc.Sequence._actionOneTwo = function (actionOne, actionTwo) { + var sequence = new cc.Sequence(); + sequence.initWithTwoActions(actionOne, actionTwo); + return sequence; +}; + +/** + * Repeats an action a number of times. + * To repeat an action forever use the CCRepeatForever action. + * @class + * @extends cc.ActionInterval + * @param {cc.FiniteTimeAction} action + * @param {Number} times + * @example + * var rep = new cc.Repeat(cc.sequence(jump2, jump1), 5); + */ +cc.Repeat = cc.ActionInterval.extend(/** @lends cc.Repeat# */{ + _times: 0, + _total: 0, + _nextDt: 0, + _actionInstant: false, + _innerAction: null, //CCFiniteTimeAction + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Creates a Repeat action. Times is an unsigned integer between 1 and pow(2,30). + * @param {cc.FiniteTimeAction} action + * @param {Number} times + */ + ctor: function (action, times) { + cc.ActionInterval.prototype.ctor.call(this); + + times !== undefined && this.initWithAction(action, times); + }, + + /** + * @param {cc.FiniteTimeAction} action + * @param {Number} times + * @return {Boolean} + */ + initWithAction: function (action, times) { + var duration = action._duration * times; + + if (this.initWithDuration(duration)) { + this._times = times; + this._innerAction = action; + if (action instanceof cc.ActionInstant) { + this._actionInstant = true; + this._times -= 1; + } + this._total = 0; + return true; + } + return false; + }, + + /** + * returns a new clone of the action + * @returns {cc.Repeat} + */ + clone: function () { + var action = new cc.Repeat(); + this._cloneDecoration(action); + action.initWithAction(this._innerAction.clone(), this._times); + return action; + }, + + /** + * Start the action with target. + * @param {cc.Node} target + */ + startWithTarget: function (target) { + this._total = 0; + this._nextDt = this._innerAction._duration / this._duration; + cc.ActionInterval.prototype.startWithTarget.call(this, target); + this._innerAction.startWithTarget(target); + }, + + /** + * stop the action + */ + stop: function () { + this._innerAction.stop(); + cc.Action.prototype.stop.call(this); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @param {Number} dt + */ + update: function (dt) { + dt = this._computeEaseTime(dt); + var locInnerAction = this._innerAction; + var locDuration = this._duration; + var locTimes = this._times; + var locNextDt = this._nextDt; + + if (dt >= locNextDt) { + while (dt > locNextDt && this._total < locTimes) { + locInnerAction.update(1); + this._total++; + locInnerAction.stop(); + locInnerAction.startWithTarget(this.target); + locNextDt += locInnerAction._duration / locDuration; + this._nextDt = locNextDt; + } + + // fix for issue #1288, incorrect end value of repeat + if (dt >= 1.0 && this._total < locTimes) + this._total++; + + // don't set a instant action back or update it, it has no use because it has no duration + if (!this._actionInstant) { + if (this._total === locTimes) { + locInnerAction.update(1); + locInnerAction.stop(); + } else { + // issue #390 prevent jerk, use right update + locInnerAction.update(dt - (locNextDt - locInnerAction._duration / locDuration)); + } + } + } else { + locInnerAction.update((dt * locTimes) % 1.0); + } + }, + + /** + * Return true if the action has finished. + * @return {Boolean} + */ + isDone: function () { + return this._total === this._times; + }, + + /** + * returns a reversed action. + * @return {cc.Repeat} + */ + reverse: function () { + var action = new cc.Repeat(this._innerAction.reverse(), this._times); + this._cloneDecoration(action); + this._reverseEaseList(action); + return action; + }, + + /** + * Set inner Action. + * @param {cc.FiniteTimeAction} action + */ + setInnerAction: function (action) { + if (this._innerAction !== action) { + this._innerAction = action; + } + }, + + /** + * Get inner Action. + * @return {cc.FiniteTimeAction} + */ + getInnerAction: function () { + return this._innerAction; + } +}); + +/** + * Creates a Repeat action. Times is an unsigned integer between 1 and pow(2,30) + * @function + * @param {cc.FiniteTimeAction} action + * @param {Number} times + * @return {cc.Repeat} + * @example + * // example + * var rep = cc.repeat(cc.sequence(jump2, jump1), 5); + */ +cc.repeat = function (action, times) { + return new cc.Repeat(action, times); +}; + +/** + * Please use cc.repeat instead + * Creates a Repeat action. Times is an unsigned integer between 1 and pow(2,30) + * @static + * @deprecated since v3.0
Please use cc.repeat instead. + * @param {cc.FiniteTimeAction} action + * @param {Number} times + * @return {cc.Repeat} + */ +cc.Repeat.create = cc.repeat; + + +/** Repeats an action for ever.
+ * To repeat the an action for a limited number of times use the Repeat action.
+ * @warning This action can't be Sequenceable because it is not an IntervalAction + * @class + * @extends cc.ActionInterval + * @param {cc.FiniteTimeAction} action + * @example + * var rep = new cc.RepeatForever(cc.sequence(jump2, jump1), 5); + */ +cc.RepeatForever = cc.ActionInterval.extend(/** @lends cc.RepeatForever# */{ + _innerAction: null, //CCActionInterval + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Create a acton which repeat forever. + * @param {cc.FiniteTimeAction} action + */ + ctor: function (action) { + cc.ActionInterval.prototype.ctor.call(this); + this._innerAction = null; + + action && this.initWithAction(action); + }, + + /** + * @param {cc.ActionInterval} action + * @return {Boolean} + */ + initWithAction: function (action) { + if (!action) + throw new Error("cc.RepeatForever.initWithAction(): action must be non null"); + + this._innerAction = action; + return true; + }, + + /** + * returns a new clone of the action + * @returns {cc.RepeatForever} + */ + clone: function () { + var action = new cc.RepeatForever(); + this._cloneDecoration(action); + action.initWithAction(this._innerAction.clone()); + return action; + }, + + /** + * Start the action with target. + * @param {cc.Node} target + */ + startWithTarget: function (target) { + cc.ActionInterval.prototype.startWithTarget.call(this, target); + this._innerAction.startWithTarget(target); + }, + + /** + * called every frame with it's delta time.
+ * DON'T override unless you know what you are doing. + * @param dt delta time in seconds + */ + step: function (dt) { + var locInnerAction = this._innerAction; + locInnerAction.step(dt); + if (locInnerAction.isDone()) { + //var diff = locInnerAction.getElapsed() - locInnerAction._duration; + locInnerAction.startWithTarget(this.target); + // to prevent jerk. issue #390 ,1247 + //this._innerAction.step(0); + //this._innerAction.step(diff); + locInnerAction.step(locInnerAction.getElapsed() - locInnerAction._duration); + } + }, + + /** + * Return true if the action has finished. + * @return {Boolean} + */ + isDone: function () { + return false; + }, + + /** + * Returns a reversed action. + * @return {cc.RepeatForever} + */ + reverse: function () { + var action = new cc.RepeatForever(this._innerAction.reverse()); + this._cloneDecoration(action); + this._reverseEaseList(action); + return action; + }, + + /** + * Set inner action. + * @param {cc.ActionInterval} action + */ + setInnerAction: function (action) { + if (this._innerAction !== action) { + this._innerAction = action; + } + }, + + /** + * Get inner action. + * @return {cc.ActionInterval} + */ + getInnerAction: function () { + return this._innerAction; + } +}); + +/** + * Create a acton which repeat forever + * @function + * @param {cc.FiniteTimeAction} action + * @return {cc.RepeatForever} + * @example + * // example + * var repeat = cc.repeatForever(cc.rotateBy(1.0, 360)); + */ +cc.repeatForever = function (action) { + return new cc.RepeatForever(action); +}; + +/** + * Please use cc.repeatForever instead + * Create a acton which repeat forever + * @static + * @deprecated since v3.0
Please use cc.repeatForever instead. + * @param {cc.FiniteTimeAction} action + * @return {cc.RepeatForever} + * @param {Array|cc.FiniteTimeAction} tempArray + * @example + * var action = new cc.Spawn(cc.jumpBy(2, cc.p(300, 0), 50, 4), cc.rotateBy(2, 720)); + */ +cc.RepeatForever.create = cc.repeatForever; + + +/** Spawn a new action immediately + * @class + * @extends cc.ActionInterval + */ +cc.Spawn = cc.ActionInterval.extend(/** @lends cc.Spawn# */{ + _one: null, + _two: null, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {Array|cc.FiniteTimeAction} tempArray + */ + ctor: function (tempArray) { + cc.ActionInterval.prototype.ctor.call(this); + this._one = null; + this._two = null; + + var i, paramArray, last; + if (tempArray instanceof Array) { + paramArray = tempArray; + } + else { + paramArray = new Array(arguments.length); + for (i = 0; i < arguments.length; ++i) { + paramArray[i] = arguments[i]; + } + } + last = paramArray.length - 1; + if ((last >= 0) && (paramArray[last] == null)) + cc.log("parameters should not be ending with null in Javascript"); + + if (last >= 0) { + var prev = paramArray[0], action1; + for (i = 1; i < last; i++) { + if (paramArray[i]) { + action1 = prev; + prev = cc.Spawn._actionOneTwo(action1, paramArray[i]); + } + } + this.initWithTwoActions(prev, paramArray[last]); + } + }, + + /** initializes the Spawn action with the 2 actions to spawn + * @param {cc.FiniteTimeAction} action1 + * @param {cc.FiniteTimeAction} action2 + * @return {Boolean} + */ + initWithTwoActions: function (action1, action2) { + if (!action1 || !action2) + throw new Error("cc.Spawn.initWithTwoActions(): arguments must all be non null"); + + var ret = false; + + var d1 = action1._duration; + var d2 = action2._duration; + + if (this.initWithDuration(Math.max(d1, d2))) { + this._one = action1; + this._two = action2; + + if (d1 > d2) { + this._two = cc.Sequence._actionOneTwo(action2, cc.delayTime(d1 - d2)); + } else if (d1 < d2) { + this._one = cc.Sequence._actionOneTwo(action1, cc.delayTime(d2 - d1)); + } + + ret = true; + } + return ret; + }, + + /** + * returns a new clone of the action + * @returns {cc.Spawn} + */ + clone: function () { + var action = new cc.Spawn(); + this._cloneDecoration(action); + action.initWithTwoActions(this._one.clone(), this._two.clone()); + return action; + }, + + /** + * Start the action with target. + * @param {cc.Node} target + */ + startWithTarget: function (target) { + cc.ActionInterval.prototype.startWithTarget.call(this, target); + this._one.startWithTarget(target); + this._two.startWithTarget(target); + }, + + /** + * Stop the action + */ + stop: function () { + this._one.stop(); + this._two.stop(); + cc.Action.prototype.stop.call(this); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @param {Number} dt + */ + update: function (dt) { + dt = this._computeEaseTime(dt); + if (this._one) + this._one.update(dt); + if (this._two) + this._two.update(dt); + }, + + /** + * Returns a reversed action. + * @return {cc.Spawn} + */ + reverse: function () { + var action = cc.Spawn._actionOneTwo(this._one.reverse(), this._two.reverse()); + this._cloneDecoration(action); + this._reverseEaseList(action); + return action; + } +}); + +/** + * Create a spawn action which runs several actions in parallel. + * @function + * @param {Array|cc.FiniteTimeAction}tempArray + * @return {cc.Spawn} + * @example + * // example + * var action = cc.spawn(cc.jumpBy(2, cc.p(300, 0), 50, 4), cc.rotateBy(2, 720)); + * todo:It should be the direct use new + */ +cc.spawn = function (/*Multiple Arguments*/tempArray) { + var paramArray = (tempArray instanceof Array) ? tempArray : arguments; + if ((paramArray.length > 0) && (paramArray[paramArray.length - 1] == null)) + cc.log("parameters should not be ending with null in Javascript"); + + var prev = paramArray[0]; + for (var i = 1; i < paramArray.length; i++) { + if (paramArray[i] != null) + prev = cc.Spawn._actionOneTwo(prev, paramArray[i]); + } + return prev; +}; + +/** + * Please use cc.spawn instead. + * Create a spawn action which runs several actions in parallel. + * @static + * @deprecated since v3.0
Please use cc.spawn instead. + * @param {Array|cc.FiniteTimeAction}tempArray + * @return {cc.Spawn} + */ +cc.Spawn.create = cc.spawn; + +/** + * @param {cc.FiniteTimeAction} action1 + * @param {cc.FiniteTimeAction} action2 + * @return {cc.Spawn} + * @private + */ +cc.Spawn._actionOneTwo = function (action1, action2) { + var pSpawn = new cc.Spawn(); + pSpawn.initWithTwoActions(action1, action2); + return pSpawn; +}; + + +/** + * Rotates a cc.Node object to a certain angle by modifying it's. + * rotation attribute.
+ * The direction will be decided by the shortest angle. + * @class + * @extends cc.ActionInterval + * @param {Number} duration duration in seconds + * @param {Number} deltaAngleX deltaAngleX in degrees. + * @param {Number} [deltaAngleY] deltaAngleY in degrees. + * @example + * var rotateTo = new cc.RotateTo(2, 61.0); + */ +cc.RotateTo = cc.ActionInterval.extend(/** @lends cc.RotateTo# */{ + _dstAngleX: 0, + _startAngleX: 0, + _diffAngleX: 0, + + _dstAngleY: 0, + _startAngleY: 0, + _diffAngleY: 0, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Creates a RotateTo action with x and y rotation angles. + * @param {Number} duration duration in seconds + * @param {Number} deltaAngleX deltaAngleX in degrees. + * @param {Number} [deltaAngleY] deltaAngleY in degrees. + */ + ctor: function (duration, deltaAngleX, deltaAngleY) { + cc.ActionInterval.prototype.ctor.call(this); + + deltaAngleX !== undefined && this.initWithDuration(duration, deltaAngleX, deltaAngleY); + }, + + /** + * Initializes the action. + * @param {Number} duration + * @param {Number} deltaAngleX + * @param {Number} deltaAngleY + * @return {Boolean} + */ + initWithDuration: function (duration, deltaAngleX, deltaAngleY) { + if (cc.ActionInterval.prototype.initWithDuration.call(this, duration)) { + this._dstAngleX = deltaAngleX || 0; + this._dstAngleY = deltaAngleY !== undefined ? deltaAngleY : this._dstAngleX; + return true; + } + return false; + }, + + /** + * returns a new clone of the action + * @returns {cc.RotateTo} + */ + clone: function () { + var action = new cc.RotateTo(); + this._cloneDecoration(action); + action.initWithDuration(this._duration, this._dstAngleX, this._dstAngleY); + return action; + }, + + /** + * Start the action with target. + * @param {cc.Node} target + */ + startWithTarget: function (target) { + cc.ActionInterval.prototype.startWithTarget.call(this, target); + + // Calculate X + var locStartAngleX = target.rotationX % 360.0; + var locDiffAngleX = this._dstAngleX - locStartAngleX; + if (locDiffAngleX > 180) + locDiffAngleX -= 360; + if (locDiffAngleX < -180) + locDiffAngleX += 360; + this._startAngleX = locStartAngleX; + this._diffAngleX = locDiffAngleX; + + // Calculate Y It's duplicated from calculating X since the rotation wrap should be the same + this._startAngleY = target.rotationY % 360.0; + var locDiffAngleY = this._dstAngleY - this._startAngleY; + if (locDiffAngleY > 180) + locDiffAngleY -= 360; + if (locDiffAngleY < -180) + locDiffAngleY += 360; + this._diffAngleY = locDiffAngleY; + }, + + /** + * RotateTo reverse not implemented. + * Will be overridden. + * @returns {cc.Action} + */ + reverse: function () { + cc.log("cc.RotateTo.reverse(): it should be overridden in subclass."); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @param {Number} dt + */ + update: function (dt) { + dt = this._computeEaseTime(dt); + if (this.target) { + this.target.rotationX = this._startAngleX + this._diffAngleX * dt; + this.target.rotationY = this._startAngleY + this._diffAngleY * dt; + } + } +}); + +/** + * Creates a RotateTo action with separate rotation angles. + * To specify the angle of rotation. + * @function + * @param {Number} duration duration in seconds + * @param {Number} deltaAngleX deltaAngleX in degrees. + * @param {Number} [deltaAngleY] deltaAngleY in degrees. + * @return {cc.RotateTo} + * @example + * // example + * var rotateTo = cc.rotateTo(2, 61.0); + */ +cc.rotateTo = function (duration, deltaAngleX, deltaAngleY) { + return new cc.RotateTo(duration, deltaAngleX, deltaAngleY); +}; + +/** + * Please use cc.rotateTo instead + * Creates a RotateTo action with separate rotation angles. + * To specify the angle of rotation. + * @static + * @deprecated since v3.0
Please use cc.rotateTo instead. + * @param {Number} duration duration in seconds + * @param {Number} deltaAngleX deltaAngleX in degrees. + * @param {Number} [deltaAngleY] deltaAngleY in degrees. + * @return {cc.RotateTo} + */ +cc.RotateTo.create = cc.rotateTo; + + +/** + * Rotates a cc.Node object clockwise a number of degrees by modifying it's rotation attribute. + * Relative to its properties to modify. + * @class + * @extends cc.ActionInterval + * @param {Number} duration duration in seconds + * @param {Number} deltaAngleX deltaAngleX in degrees + * @param {Number} [deltaAngleY] deltaAngleY in degrees + * @example + * var actionBy = new cc.RotateBy(2, 360); + */ +cc.RotateBy = cc.ActionInterval.extend(/** @lends cc.RotateBy# */{ + _angleX: 0, + _startAngleX: 0, + _angleY: 0, + _startAngleY: 0, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {Number} duration duration in seconds + * @param {Number} deltaAngleX deltaAngleX in degrees + * @param {Number} [deltaAngleY] deltaAngleY in degrees + */ + ctor: function (duration, deltaAngleX, deltaAngleY) { + cc.ActionInterval.prototype.ctor.call(this); + + deltaAngleX !== undefined && this.initWithDuration(duration, deltaAngleX, deltaAngleY); + }, + + /** + * Initializes the action. + * @param {Number} duration duration in seconds + * @param {Number} deltaAngleX deltaAngleX in degrees + * @param {Number} [deltaAngleY=] deltaAngleY in degrees + * @return {Boolean} + */ + initWithDuration: function (duration, deltaAngleX, deltaAngleY) { + if (cc.ActionInterval.prototype.initWithDuration.call(this, duration)) { + this._angleX = deltaAngleX || 0; + this._angleY = deltaAngleY || this._angleX; + return true; + } + return false; + }, + + /** + * returns a new clone of the action + * @returns {cc.RotateBy} + */ + clone: function () { + var action = new cc.RotateBy(); + this._cloneDecoration(action); + action.initWithDuration(this._duration, this._angleX, this._angleY); + return action; + }, + + /** + * Start the action with target. + * @param {cc.Node} target + */ + startWithTarget: function (target) { + cc.ActionInterval.prototype.startWithTarget.call(this, target); + this._startAngleX = target.rotationX; + this._startAngleY = target.rotationY; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @param {Number} dt + */ + update: function (dt) { + dt = this._computeEaseTime(dt); + if (this.target) { + this.target.rotationX = this._startAngleX + this._angleX * dt; + this.target.rotationY = this._startAngleY + this._angleY * dt; + } + }, + + /** + * Returns a reversed action. + * @return {cc.RotateBy} + */ + reverse: function () { + var action = new cc.RotateBy(this._duration, -this._angleX, -this._angleY); + this._cloneDecoration(action); + this._reverseEaseList(action); + return action; + } +}); + +/** + * Rotates a cc.Node object clockwise a number of degrees by modifying it's rotation attribute. + * Relative to its properties to modify. + * @function + * @param {Number} duration duration in seconds + * @param {Number} deltaAngleX deltaAngleX in degrees + * @param {Number} [deltaAngleY] deltaAngleY in degrees + * @return {cc.RotateBy} + * @example + * // example + * var actionBy = cc.rotateBy(2, 360); + */ +cc.rotateBy = function (duration, deltaAngleX, deltaAngleY) { + return new cc.RotateBy(duration, deltaAngleX, deltaAngleY); +}; +/** + * Please use cc.rotateBy instead. + * Rotates a cc.Node object clockwise a number of degrees by modifying it's rotation attribute. + * Relative to its properties to modify. + * @static + * @deprecated since v3.0
Please use cc.rotateBy instead. + * @param {Number} duration duration in seconds + * @param {Number} deltaAngleX deltaAngleX in degrees + * @param {Number} [deltaAngleY] deltaAngleY in degrees + * @return {cc.RotateBy} + */ +cc.RotateBy.create = cc.rotateBy; + + +/** + *

+ * Moves a CCNode object x,y pixels by modifying it's position attribute.
+ * x and y are relative to the position of the object.
+ * Several CCMoveBy actions can be concurrently called, and the resulting
+ * movement will be the sum of individual movements. + *

+ * @class + * @extends cc.ActionInterval + * @param {Number} duration duration in seconds + * @param {cc.Point|Number} deltaPos + * @param {Number} [deltaY] + * @example + * var actionBy = cc.moveBy(2, cc.p(windowSize.width - 40, windowSize.height - 40)); + */ +cc.MoveBy = cc.ActionInterval.extend(/** @lends cc.MoveBy# */{ + _positionDelta: null, + _startPosition: null, + _previousPosition: null, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {Number} duration duration in seconds + * @param {cc.Point|Number} deltaPos + * @param {Number} [deltaY] + */ + ctor: function (duration, deltaPos, deltaY) { + cc.ActionInterval.prototype.ctor.call(this); + + this._positionDelta = cc.p(0, 0); + this._startPosition = cc.p(0, 0); + this._previousPosition = cc.p(0, 0); + + deltaPos !== undefined && this.initWithDuration(duration, deltaPos, deltaY); + }, + + /** + * Initializes the action. + * @param {Number} duration duration in seconds + * @param {cc.Point} position + * @param {Number} [y] + * @return {Boolean} + */ + initWithDuration: function (duration, position, y) { + if (cc.ActionInterval.prototype.initWithDuration.call(this, duration)) { + if (position.x !== undefined) { + y = position.y; + position = position.x; + } + + this._positionDelta.x = position; + this._positionDelta.y = y; + return true; + } + return false; + }, + + /** + * returns a new clone of the action + * @returns {cc.MoveBy} + */ + clone: function () { + var action = new cc.MoveBy(); + this._cloneDecoration(action); + action.initWithDuration(this._duration, this._positionDelta); + return action; + }, + + /** + * Start the action with target. + * @param {cc.Node} target + */ + startWithTarget: function (target) { + cc.ActionInterval.prototype.startWithTarget.call(this, target); + var locPosX = target.getPositionX(); + var locPosY = target.getPositionY(); + this._previousPosition.x = locPosX; + this._previousPosition.y = locPosY; + this._startPosition.x = locPosX; + this._startPosition.y = locPosY; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @param {Number} dt + */ + update: function (dt) { + dt = this._computeEaseTime(dt); + if (this.target) { + var x = this._positionDelta.x * dt; + var y = this._positionDelta.y * dt; + var locStartPosition = this._startPosition; + if (cc.ENABLE_STACKABLE_ACTIONS) { + var targetX = this.target.getPositionX(); + var targetY = this.target.getPositionY(); + var locPreviousPosition = this._previousPosition; + + locStartPosition.x = locStartPosition.x + targetX - locPreviousPosition.x; + locStartPosition.y = locStartPosition.y + targetY - locPreviousPosition.y; + x = x + locStartPosition.x; + y = y + locStartPosition.y; + locPreviousPosition.x = x; + locPreviousPosition.y = y; + this.target.setPosition(x, y); + } else { + this.target.setPosition(locStartPosition.x + x, locStartPosition.y + y); + } + } + }, + + /** + * MoveTo reverse is not implemented + * @return {cc.MoveBy} + */ + reverse: function () { + var action = new cc.MoveBy(this._duration, cc.p(-this._positionDelta.x, -this._positionDelta.y)); + this._cloneDecoration(action); + this._reverseEaseList(action); + return action; + } +}); + +/** + * Create the action. + * Relative to its coordinate moves a certain distance. + * @function + * @param {Number} duration duration in seconds + * @param {cc.Point|Number} deltaPos + * @param {Number} deltaY + * @return {cc.MoveBy} + * @example + * // example + * var actionBy = cc.moveBy(2, cc.p(windowSize.width - 40, windowSize.height - 40)); + */ +cc.moveBy = function (duration, deltaPos, deltaY) { + return new cc.MoveBy(duration, deltaPos, deltaY); +}; +/** + * Please use cc.moveBy instead. + * Relative to its coordinate moves a certain distance. + * @static + * @deprecated since v3.0 please use cc.moveBy instead. + * @param {Number} duration duration in seconds + * @param {cc.Point|Number} deltaPos + * @param {Number} deltaY + * @return {cc.MoveBy} + */ +cc.MoveBy.create = cc.moveBy; + + +/** + * Moves a CCNode object to the position x,y. x and y are absolute coordinates by modifying it's position attribute.
+ * Several CCMoveTo actions can be concurrently called, and the resulting
+ * movement will be the sum of individual movements. + * @class + * @extends cc.MoveBy + * @param {Number} duration duration in seconds + * @param {cc.Point|Number} position + * @param {Number} y + * @example + * var actionTo = new cc.MoveTo(2, cc.p(80, 80)); + */ +cc.MoveTo = cc.MoveBy.extend(/** @lends cc.MoveTo# */{ + _endPosition: null, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {Number} duration duration in seconds + * @param {cc.Point|Number} position + * @param {Number} y + */ + ctor: function (duration, position, y) { + cc.MoveBy.prototype.ctor.call(this); + this._endPosition = cc.p(0, 0); + + position !== undefined && this.initWithDuration(duration, position, y); + }, + + /** + * Initializes the action. + * @param {Number} duration duration in seconds + * @param {cc.Point} position + * @param {Number} y + * @return {Boolean} + */ + initWithDuration: function (duration, position, y) { + if (cc.MoveBy.prototype.initWithDuration.call(this, duration, position, y)) { + if (position.x !== undefined) { + y = position.y; + position = position.x; + } + + this._endPosition.x = position; + this._endPosition.y = y; + return true; + } + return false; + }, + + /** + * returns a new clone of the action + * @returns {cc.MoveTo} + */ + clone: function () { + var action = new cc.MoveTo(); + this._cloneDecoration(action); + action.initWithDuration(this._duration, this._endPosition); + return action; + }, + + /** + * Start the action with target. + * @param {cc.Node} target + */ + startWithTarget: function (target) { + cc.MoveBy.prototype.startWithTarget.call(this, target); + this._positionDelta.x = this._endPosition.x - target.getPositionX(); + this._positionDelta.y = this._endPosition.y - target.getPositionY(); + } +}); + +/** + * Create new action. + * Moving to the specified coordinates. + * @function + * @param {Number} duration duration in seconds + * @param {cc.Point|Number} position + * @param {Number} y + * @return {cc.MoveTo} + * @example + * // example + * var actionTo = cc.moveTo(2, cc.p(80, 80)); + */ +cc.moveTo = function (duration, position, y) { + return new cc.MoveTo(duration, position, y); +}; +/** + * Please use cc.moveTo instead. + * Moving to the specified coordinates. + * @static + * @deprecated since v3.0
Please use cc.moveTo instead. + * @param {Number} duration duration in seconds + * @param {cc.Point|Number} position + * @param {Number} y + * @return {cc.MoveTo} + */ +cc.MoveTo.create = cc.moveTo; + +/** + * Skews a cc.Node object to given angles by modifying it's skewX and skewY attributes + * @class + * @extends cc.ActionInterval + * @param {Number} t time in seconds + * @param {Number} sx + * @param {Number} sy + * @example + * var actionTo = new cc.SkewTo(2, 37.2, -37.2); + */ +cc.SkewTo = cc.ActionInterval.extend(/** @lends cc.SkewTo# */{ + _skewX: 0, + _skewY: 0, + _startSkewX: 0, + _startSkewY: 0, + _endSkewX: 0, + _endSkewY: 0, + _deltaX: 0, + _deltaY: 0, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {Number} t time in seconds + * @param {Number} sx + * @param {Number} sy + */ + ctor: function (t, sx, sy) { + cc.ActionInterval.prototype.ctor.call(this); + + sy !== undefined && this.initWithDuration(t, sx, sy); + }, + + /** + * Initializes the action. + * @param {Number} t time in seconds + * @param {Number} sx + * @param {Number} sy + * @return {Boolean} + */ + initWithDuration: function (t, sx, sy) { + var ret = false; + if (cc.ActionInterval.prototype.initWithDuration.call(this, t)) { + this._endSkewX = sx; + this._endSkewY = sy; + ret = true; + } + return ret; + }, + + /** + * returns a new clone of the action + * @returns {cc.SkewTo} + */ + clone: function () { + var action = new cc.SkewTo(); + this._cloneDecoration(action); + action.initWithDuration(this._duration, this._endSkewX, this._endSkewY); + return action; + }, + + /** + * Start the action with target. + * @param {cc.Node} target + */ + startWithTarget: function (target) { + cc.ActionInterval.prototype.startWithTarget.call(this, target); + + this._startSkewX = target.skewX % 180; + this._deltaX = this._endSkewX - this._startSkewX; + if (this._deltaX > 180) + this._deltaX -= 360; + if (this._deltaX < -180) + this._deltaX += 360; + + this._startSkewY = target.skewY % 360; + this._deltaY = this._endSkewY - this._startSkewY; + if (this._deltaY > 180) + this._deltaY -= 360; + if (this._deltaY < -180) + this._deltaY += 360; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @param {Number} dt + */ + update: function (dt) { + dt = this._computeEaseTime(dt); + this.target.skewX = this._startSkewX + this._deltaX * dt; + this.target.skewY = this._startSkewY + this._deltaY * dt; + } +}); +/** + * Create new action. + * Skews a cc.Node object to given angles by modifying it's skewX and skewY attributes. + * Changes to the specified value. + * @function + * @param {Number} t time in seconds + * @param {Number} sx + * @param {Number} sy + * @return {cc.SkewTo} + * @example + * // example + * var actionTo = cc.skewTo(2, 37.2, -37.2); + */ +cc.skewTo = function (t, sx, sy) { + return new cc.SkewTo(t, sx, sy); +}; +/** + * Please use cc.skewTo instead. + * Skews a cc.Node object to given angles by modifying it's skewX and skewY attributes。 + * Changes to the specified value. + * @static + * @deprecated since v3.0
Please use cc.skewTo instead. + * @param {Number} t time in seconds + * @param {Number} sx + * @param {Number} sy + * @return {cc.SkewTo} + */ +cc.SkewTo.create = cc.skewTo; + +/** + * Skews a cc.Node object by skewX and skewY degrees. + * Relative to its attribute modification. + * @class + * @extends cc.SkewTo + * @param {Number} t time in seconds + * @param {Number} sx skew in degrees for X axis + * @param {Number} sy skew in degrees for Y axis + */ +cc.SkewBy = cc.SkewTo.extend(/** @lends cc.SkewBy# */{ + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {Number} t time in seconds + * @param {Number} sx skew in degrees for X axis + * @param {Number} sy skew in degrees for Y axis + */ + ctor: function (t, sx, sy) { + cc.SkewTo.prototype.ctor.call(this); + sy !== undefined && this.initWithDuration(t, sx, sy); + }, + + /** + * Initializes the action. + * @param {Number} t time in seconds + * @param {Number} deltaSkewX skew in degrees for X axis + * @param {Number} deltaSkewY skew in degrees for Y axis + * @return {Boolean} + */ + initWithDuration: function (t, deltaSkewX, deltaSkewY) { + var ret = false; + if (cc.SkewTo.prototype.initWithDuration.call(this, t, deltaSkewX, deltaSkewY)) { + this._skewX = deltaSkewX; + this._skewY = deltaSkewY; + ret = true; + } + return ret; + }, + + /** + * returns a new clone of the action + * @returns {cc.SkewBy} + */ + clone: function () { + var action = new cc.SkewBy(); + this._cloneDecoration(action); + action.initWithDuration(this._duration, this._skewX, this._skewY); + return action; + }, + + /** + * Start the action width target. + * @param {cc.Node} target + */ + startWithTarget: function (target) { + cc.SkewTo.prototype.startWithTarget.call(this, target); + this._deltaX = this._skewX; + this._deltaY = this._skewY; + this._endSkewX = this._startSkewX + this._deltaX; + this._endSkewY = this._startSkewY + this._deltaY; + }, + + /** + * Returns a reversed action. + * @return {cc.SkewBy} + */ + reverse: function () { + var action = new cc.SkewBy(this._duration, -this._skewX, -this._skewY); + this._cloneDecoration(action); + this._reverseEaseList(action); + return action; + } +}); + +/** + * Skews a cc.Node object by skewX and skewY degrees.
+ * Relative to its attribute modification. + * @function + * @param {Number} t time in seconds + * @param {Number} sx sx skew in degrees for X axis + * @param {Number} sy sy skew in degrees for Y axis + * @return {cc.SkewBy} + * @example + * // example + * var actionBy = cc.skewBy(2, 0, -90); + */ +cc.skewBy = function (t, sx, sy) { + return new cc.SkewBy(t, sx, sy); +}; +/** + * Please use cc.skewBy instead.
+ * Skews a cc.Node object by skewX and skewY degrees.
+ * Relative to its attribute modification. + * @static + * @deprecated since v3.0 please use cc.skewBy instead. + * @param {Number} t time in seconds + * @param {Number} sx sx skew in degrees for X axis + * @param {Number} sy sy skew in degrees for Y axis + * @return {cc.SkewBy} + */ +cc.SkewBy.create = cc.skewBy; + + +/** + * Moves a cc.Node object simulating a parabolic jump movement by modifying it's position attribute. + * Relative to its movement. + * @class + * @extends cc.ActionInterval + * @param {Number} duration + * @param {cc.Point|Number} position + * @param {Number} [y] + * @param {Number} height + * @param {Number} jumps + * @example + * var actionBy = new cc.JumpBy(2, cc.p(300, 0), 50, 4); + * var actionBy = new cc.JumpBy(2, 300, 0, 50, 4); + */ +cc.JumpBy = cc.ActionInterval.extend(/** @lends cc.JumpBy# */{ + _startPosition: null, + _delta: null, + _height: 0, + _jumps: 0, + _previousPosition: null, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {Number} duration + * @param {cc.Point|Number} position + * @param {Number} [y] + * @param {Number} height + * @param {Number} jumps + */ + ctor: function (duration, position, y, height, jumps) { + cc.ActionInterval.prototype.ctor.call(this); + this._startPosition = cc.p(0, 0); + this._previousPosition = cc.p(0, 0); + this._delta = cc.p(0, 0); + + height !== undefined && this.initWithDuration(duration, position, y, height, jumps); + }, + /** + * Initializes the action. + * @param {Number} duration + * @param {cc.Point|Number} position + * @param {Number} [y] + * @param {Number} height + * @param {Number} jumps + * @return {Boolean} + * @example + * actionBy.initWithDuration(2, cc.p(300, 0), 50, 4); + * actionBy.initWithDuration(2, 300, 0, 50, 4); + */ + initWithDuration: function (duration, position, y, height, jumps) { + if (cc.ActionInterval.prototype.initWithDuration.call(this, duration)) { + if (jumps === undefined) { + jumps = height; + height = y; + y = position.y; + position = position.x; + } + this._delta.x = position; + this._delta.y = y; + this._height = height; + this._jumps = jumps; + return true; + } + return false; + }, + + /** + * returns a new clone of the action + * @returns {cc.JumpBy} + */ + clone: function () { + var action = new cc.JumpBy(); + this._cloneDecoration(action); + action.initWithDuration(this._duration, this._delta, this._height, this._jumps); + return action; + }, + + /** + * Start the action with target. + * @param {cc.Node} target + */ + startWithTarget: function (target) { + cc.ActionInterval.prototype.startWithTarget.call(this, target); + var locPosX = target.getPositionX(); + var locPosY = target.getPositionY(); + this._previousPosition.x = locPosX; + this._previousPosition.y = locPosY; + this._startPosition.x = locPosX; + this._startPosition.y = locPosY; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @param {Number} dt + */ + update: function (dt) { + dt = this._computeEaseTime(dt); + if (this.target) { + var frac = dt * this._jumps % 1.0; + var y = this._height * 4 * frac * (1 - frac); + y += this._delta.y * dt; + + var x = this._delta.x * dt; + var locStartPosition = this._startPosition; + if (cc.ENABLE_STACKABLE_ACTIONS) { + var targetX = this.target.getPositionX(); + var targetY = this.target.getPositionY(); + var locPreviousPosition = this._previousPosition; + + locStartPosition.x = locStartPosition.x + targetX - locPreviousPosition.x; + locStartPosition.y = locStartPosition.y + targetY - locPreviousPosition.y; + x = x + locStartPosition.x; + y = y + locStartPosition.y; + locPreviousPosition.x = x; + locPreviousPosition.y = y; + this.target.setPosition(x, y); + } else { + this.target.setPosition(locStartPosition.x + x, locStartPosition.y + y); + } + } + }, + + /** + * Returns a reversed action. + * @return {cc.JumpBy} + */ + reverse: function () { + var action = new cc.JumpBy(this._duration, cc.p(-this._delta.x, -this._delta.y), this._height, this._jumps); + this._cloneDecoration(action); + this._reverseEaseList(action); + return action; + } +}); + +/** + * Moves a cc.Node object simulating a parabolic jump movement by modifying it's position attribute. + * Relative to its movement. + * @function + * @param {Number} duration + * @param {cc.Point|Number} position + * @param {Number} [y] + * @param {Number} height + * @param {Number} jumps + * @return {cc.JumpBy} + * @example + * // example + * var actionBy = cc.jumpBy(2, cc.p(300, 0), 50, 4); + * var actionBy = cc.jumpBy(2, 300, 0, 50, 4); + */ +cc.jumpBy = function (duration, position, y, height, jumps) { + return new cc.JumpBy(duration, position, y, height, jumps); +}; +/** + * Please use cc.jumpBy instead.
+ * Moves a cc.Node object simulating a parabolic jump movement by modifying it's position attribute.
+ * Relative to its movement. + * @static + * @deprecated since v3.0 please use cc.jumpBy instead. + * @param {Number} duration + * @param {cc.Point|Number} position + * @param {Number} [y] + * @param {Number} height + * @param {Number} jumps + * @return {cc.JumpBy} + */ +cc.JumpBy.create = cc.jumpBy; + +/** + * Moves a cc.Node object to a parabolic position simulating a jump movement by modifying it's position attribute.
+ * Jump to the specified location. + * @class + * @extends cc.JumpBy + * @param {Number} duration + * @param {cc.Point|Number} position + * @param {Number} [y] + * @param {Number} height + * @param {Number} jumps + * @example + * var actionTo = new cc.JumpTo(2, cc.p(300, 0), 50, 4); + * var actionTo = new cc.JumpTo(2, 300, 0, 50, 4); + */ +cc.JumpTo = cc.JumpBy.extend(/** @lends cc.JumpTo# */{ + _endPosition: null, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {Number} duration + * @param {cc.Point|Number} position + * @param {Number} [y] + * @param {Number} height + * @param {Number} jumps + */ + ctor: function (duration, position, y, height, jumps) { + cc.JumpBy.prototype.ctor.call(this); + this._endPosition = cc.p(0, 0); + + height !== undefined && this.initWithDuration(duration, position, y, height, jumps); + }, + /** + * Initializes the action. + * @param {Number} duration + * @param {cc.Point|Number} position + * @param {Number} [y] + * @param {Number} height + * @param {Number} jumps + * @return {Boolean} + * @example + * actionTo.initWithDuration(2, cc.p(300, 0), 50, 4); + * actionTo.initWithDuration(2, 300, 0, 50, 4); + */ + initWithDuration: function (duration, position, y, height, jumps) { + if (cc.JumpBy.prototype.initWithDuration.call(this, duration, position, y, height, jumps)) { + if (jumps === undefined) { + y = position.y; + position = position.x; + } + this._endPosition.x = position; + this._endPosition.y = y; + return true; + } + return false; + }, + /** + * Start the action with target. + * @param {cc.Node} target + */ + startWithTarget: function (target) { + cc.JumpBy.prototype.startWithTarget.call(this, target); + this._delta.x = this._endPosition.x - this._startPosition.x; + this._delta.y = this._endPosition.y - this._startPosition.y; + }, + + /** + * returns a new clone of the action + * @returns {cc.JumpTo} + */ + clone: function () { + var action = new cc.JumpTo(); + this._cloneDecoration(action); + action.initWithDuration(this._duration, this._endPosition, this._height, this._jumps); + return action; + } +}); + +/** + * Moves a cc.Node object to a parabolic position simulating a jump movement by modifying it's position attribute.
+ * Jump to the specified location. + * @function + * @param {Number} duration + * @param {cc.Point|Number} position + * @param {Number} [y] + * @param {Number} height + * @param {Number} jumps + * @return {cc.JumpTo} + * @example + * // example + * var actionTo = cc.jumpTo(2, cc.p(300, 300), 50, 4); + * var actionTo = cc.jumpTo(2, 300, 300, 50, 4); + */ +cc.jumpTo = function (duration, position, y, height, jumps) { + return new cc.JumpTo(duration, position, y, height, jumps); +}; +/** + * Please use cc.jumpTo instead. + * Moves a cc.Node object to a parabolic position simulating a jump movement by modifying it's position attribute.
+ * Jump to the specified location. + * @static + * @deprecated since v3.0 please use cc.jumpTo instead. + * @param {Number} duration + * @param {cc.Point|Number} position + * @param {Number} [y] + * @param {Number} height + * @param {Number} jumps + * @return {cc.JumpTo} + */ +cc.JumpTo.create = cc.jumpTo; + +/** + * @function + * @param {Number} a + * @param {Number} b + * @param {Number} c + * @param {Number} d + * @param {Number} t + * @return {Number} + */ +cc.bezierAt = function (a, b, c, d, t) { + return (Math.pow(1 - t, 3) * a + + 3 * t * (Math.pow(1 - t, 2)) * b + + 3 * Math.pow(t, 2) * (1 - t) * c + + Math.pow(t, 3) * d ); +}; + +/** An action that moves the target with a cubic Bezier curve by a certain distance. + * Relative to its movement. + * @class + * @extends cc.ActionInterval + * @param {Number} t time in seconds + * @param {Array} c Array of points + * @example + * var bezier = [cc.p(0, windowSize.height / 2), cc.p(300, -windowSize.height / 2), cc.p(300, 100)]; + * var bezierForward = new cc.BezierBy(3, bezier); + */ +cc.BezierBy = cc.ActionInterval.extend(/** @lends cc.BezierBy# */{ + _config: null, + _startPosition: null, + _previousPosition: null, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {Number} t time in seconds + * @param {Array} c Array of points + */ + ctor: function (t, c) { + cc.ActionInterval.prototype.ctor.call(this); + this._config = []; + this._startPosition = cc.p(0, 0); + this._previousPosition = cc.p(0, 0); + + c && this.initWithDuration(t, c); + }, + + /** + * Initializes the action. + * @param {Number} t time in seconds + * @param {Array} c Array of points + * @return {Boolean} + */ + initWithDuration: function (t, c) { + if (cc.ActionInterval.prototype.initWithDuration.call(this, t)) { + this._config = c; + return true; + } + return false; + }, + + /** + * returns a new clone of the action + * @returns {cc.BezierBy} + */ + clone: function () { + var action = new cc.BezierBy(); + this._cloneDecoration(action); + var newConfigs = []; + for (var i = 0; i < this._config.length; i++) { + var selConf = this._config[i]; + newConfigs.push(cc.p(selConf.x, selConf.y)); + } + action.initWithDuration(this._duration, newConfigs); + return action; + }, + + /** + * Start the action with target. + * @param {cc.Node} target + */ + startWithTarget: function (target) { + cc.ActionInterval.prototype.startWithTarget.call(this, target); + var locPosX = target.getPositionX(); + var locPosY = target.getPositionY(); + this._previousPosition.x = locPosX; + this._previousPosition.y = locPosY; + this._startPosition.x = locPosX; + this._startPosition.y = locPosY; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @param {Number} dt + */ + update: function (dt) { + dt = this._computeEaseTime(dt); + if (this.target) { + var locConfig = this._config; + var xa = 0; + var xb = locConfig[0].x; + var xc = locConfig[1].x; + var xd = locConfig[2].x; + + var ya = 0; + var yb = locConfig[0].y; + var yc = locConfig[1].y; + var yd = locConfig[2].y; + + var x = cc.bezierAt(xa, xb, xc, xd, dt); + var y = cc.bezierAt(ya, yb, yc, yd, dt); + + var locStartPosition = this._startPosition; + if (cc.ENABLE_STACKABLE_ACTIONS) { + var targetX = this.target.getPositionX(); + var targetY = this.target.getPositionY(); + var locPreviousPosition = this._previousPosition; + + locStartPosition.x = locStartPosition.x + targetX - locPreviousPosition.x; + locStartPosition.y = locStartPosition.y + targetY - locPreviousPosition.y; + x = x + locStartPosition.x; + y = y + locStartPosition.y; + locPreviousPosition.x = x; + locPreviousPosition.y = y; + this.target.setPosition(x, y); + } else { + this.target.setPosition(locStartPosition.x + x, locStartPosition.y + y); + } + } + }, + + /** + * Returns a reversed action. + * @return {cc.BezierBy} + */ + reverse: function () { + var locConfig = this._config; + var r = [ + cc.pAdd(locConfig[1], cc.pNeg(locConfig[2])), + cc.pAdd(locConfig[0], cc.pNeg(locConfig[2])), + cc.pNeg(locConfig[2])]; + var action = new cc.BezierBy(this._duration, r); + this._cloneDecoration(action); + this._reverseEaseList(action); + return action; + } +}); + +/** + * An action that moves the target with a cubic Bezier curve by a certain distance. + * Relative to its movement. + * @function + * @param {Number} t time in seconds + * @param {Array} c Array of points + * @return {cc.BezierBy} + * @example + * // example + * var bezier = [cc.p(0, windowSize.height / 2), cc.p(300, -windowSize.height / 2), cc.p(300, 100)]; + * var bezierForward = cc.bezierBy(3, bezier); + */ +cc.bezierBy = function (t, c) { + return new cc.BezierBy(t, c); +}; +/** + * Please use cc.bezierBy instead. + * An action that moves the target with a cubic Bezier curve by a certain distance. + * Relative to its movement. + * @static + * @deprecated since v3.0 please use cc.bezierBy instead. + * @param {Number} t time in seconds + * @param {Array} c Array of points + * @return {cc.BezierBy} + */ +cc.BezierBy.create = cc.bezierBy; + + +/** An action that moves the target with a cubic Bezier curve to a destination point. + * @class + * @extends cc.BezierBy + * @param {Number} t + * @param {Array} c array of points + * @example + * var bezier = [cc.p(0, windowSize.height / 2), cc.p(300, -windowSize.height / 2), cc.p(300, 100)]; + * var bezierTo = new cc.BezierTo(2, bezier); + */ +cc.BezierTo = cc.BezierBy.extend(/** @lends cc.BezierTo# */{ + _toConfig: null, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {Number} t + * @param {Array} c array of points + * var bezierTo = new cc.BezierTo(2, bezier); + */ + ctor: function (t, c) { + cc.BezierBy.prototype.ctor.call(this); + this._toConfig = []; + c && this.initWithDuration(t, c); + }, + + /** + * Initializes the action. + * @param {Number} t time in seconds + * @param {Array} c Array of points + * @return {Boolean} + */ + initWithDuration: function (t, c) { + if (cc.ActionInterval.prototype.initWithDuration.call(this, t)) { + this._toConfig = c; + return true; + } + return false; + }, + + /** + * returns a new clone of the action + * @returns {cc.BezierTo} + */ + clone: function () { + var action = new cc.BezierTo(); + this._cloneDecoration(action); + action.initWithDuration(this._duration, this._toConfig); + return action; + }, + + /** + * Start the action with target. + * @param {cc.Node} target + */ + startWithTarget: function (target) { + cc.BezierBy.prototype.startWithTarget.call(this, target); + var locStartPos = this._startPosition; + var locToConfig = this._toConfig; + var locConfig = this._config; + + locConfig[0] = cc.pSub(locToConfig[0], locStartPos); + locConfig[1] = cc.pSub(locToConfig[1], locStartPos); + locConfig[2] = cc.pSub(locToConfig[2], locStartPos); + } +}); +/** + * An action that moves the target with a cubic Bezier curve to a destination point. + * @function + * @param {Number} t + * @param {Array} c array of points + * @return {cc.BezierTo} + * @example + * // example + * var bezier = [cc.p(0, windowSize.height / 2), cc.p(300, -windowSize.height / 2), cc.p(300, 100)]; + * var bezierTo = cc.bezierTo(2, bezier); + */ +cc.bezierTo = function (t, c) { + return new cc.BezierTo(t, c); +}; +/** + * Please use cc.bezierTo instead + * @static + * @deprecated since v3.0 please use cc.bezierTo instead. + * @param {Number} t + * @param {Array} c array of points + * @return {cc.BezierTo} + */ +cc.BezierTo.create = cc.bezierTo; + + +/** Scales a cc.Node object to a zoom factor by modifying it's scale attribute. + * @warning This action doesn't support "reverse" + * @class + * @extends cc.ActionInterval + * @param {Number} duration + * @param {Number} sx scale parameter in X + * @param {Number} [sy] scale parameter in Y, if Null equal to sx + * @example + * // It scales to 0.5 in both X and Y. + * var actionTo = new cc.ScaleTo(2, 0.5); + * + * // It scales to 0.5 in x and 2 in Y + * var actionTo = new cc.ScaleTo(2, 0.5, 2); + */ +cc.ScaleTo = cc.ActionInterval.extend(/** @lends cc.ScaleTo# */{ + _scaleX: 1, + _scaleY: 1, + _startScaleX: 1, + _startScaleY: 1, + _endScaleX: 0, + _endScaleY: 0, + _deltaX: 0, + _deltaY: 0, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {Number} duration + * @param {Number} sx scale parameter in X + * @param {Number} [sy] scale parameter in Y, if Null equal to sx + */ + ctor: function (duration, sx, sy) { + cc.ActionInterval.prototype.ctor.call(this); + sx !== undefined && this.initWithDuration(duration, sx, sy); + }, + + /** + * Initializes the action. + * @param {Number} duration + * @param {Number} sx + * @param {Number} [sy=] + * @return {Boolean} + */ + initWithDuration: function (duration, sx, sy) { //function overload here + if (cc.ActionInterval.prototype.initWithDuration.call(this, duration)) { + this._endScaleX = sx; + this._endScaleY = (sy != null) ? sy : sx; + return true; + } + return false; + }, + + /** + * returns a new clone of the action + * @returns {cc.ScaleTo} + */ + clone: function () { + var action = new cc.ScaleTo(); + this._cloneDecoration(action); + action.initWithDuration(this._duration, this._endScaleX, this._endScaleY); + return action; + }, + + /** + * Start the action with target. + * @param {cc.Node} target + */ + startWithTarget: function (target) { + cc.ActionInterval.prototype.startWithTarget.call(this, target); + this._startScaleX = target.scaleX; + this._startScaleY = target.scaleY; + this._deltaX = this._endScaleX - this._startScaleX; + this._deltaY = this._endScaleY - this._startScaleY; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @param {Number} dt + */ + update: function (dt) { + dt = this._computeEaseTime(dt); + if (this.target) { + this.target.scaleX = this._startScaleX + this._deltaX * dt; + this.target.scaleY = this._startScaleY + this._deltaY * dt; + } + } +}); +/** + * Scales a cc.Node object to a zoom factor by modifying it's scale attribute. + * @function + * @param {Number} duration + * @param {Number} sx scale parameter in X + * @param {Number} [sy] scale parameter in Y, if Null equal to sx + * @return {cc.ScaleTo} + * @example + * // example + * // It scales to 0.5 in both X and Y. + * var actionTo = cc.scaleTo(2, 0.5); + * + * // It scales to 0.5 in x and 2 in Y + * var actionTo = cc.scaleTo(2, 0.5, 2); + */ +cc.scaleTo = function (duration, sx, sy) { //function overload + return new cc.ScaleTo(duration, sx, sy); +}; +/** + * Please use cc.scaleTo instead. + * Scales a cc.Node object to a zoom factor by modifying it's scale attribute. + * @static + * @deprecated since v3.0 please use cc.scaleTo instead. + * @param {Number} duration + * @param {Number} sx scale parameter in X + * @param {Number} [sy] scale parameter in Y, if Null equal to sx + * @return {cc.ScaleTo} + */ +cc.ScaleTo.create = cc.scaleTo; + + +/** Scales a cc.Node object a zoom factor by modifying it's scale attribute. + * Relative to its changes. + * @class + * @extends cc.ScaleTo + */ +cc.ScaleBy = cc.ScaleTo.extend(/** @lends cc.ScaleBy# */{ + /** + * Start the action with target. + * @param {cc.Node} target + */ + startWithTarget: function (target) { + cc.ScaleTo.prototype.startWithTarget.call(this, target); + this._deltaX = this._startScaleX * this._endScaleX - this._startScaleX; + this._deltaY = this._startScaleY * this._endScaleY - this._startScaleY; + }, + + /** + * Returns a reversed action. + * @return {cc.ScaleBy} + */ + reverse: function () { + var action = new cc.ScaleBy(this._duration, 1 / this._endScaleX, 1 / this._endScaleY); + this._cloneDecoration(action); + this._reverseEaseList(action); + return action; + }, + + /** + * returns a new clone of the action + * @returns {cc.ScaleBy} + */ + clone: function () { + var action = new cc.ScaleBy(); + this._cloneDecoration(action); + action.initWithDuration(this._duration, this._endScaleX, this._endScaleY); + return action; + } +}); +/** + * Scales a cc.Node object a zoom factor by modifying it's scale attribute. + * Relative to its changes. + * @function + * @param {Number} duration duration in seconds + * @param {Number} sx sx scale parameter in X + * @param {Number|Null} [sy=] sy scale parameter in Y, if Null equal to sx + * @return {cc.ScaleBy} + * @example + * // example without sy, it scales by 2 both in X and Y + * var actionBy = cc.scaleBy(2, 2); + * + * //example with sy, it scales by 0.25 in X and 4.5 in Y + * var actionBy2 = cc.scaleBy(2, 0.25, 4.5); + */ +cc.scaleBy = function (duration, sx, sy) { + return new cc.ScaleBy(duration, sx, sy); +}; +/** + * Please use cc.scaleBy instead. + * Scales a cc.Node object a zoom factor by modifying it's scale attribute. + * Relative to its changes. + * @static + * @deprecated since v3.0 please use cc.scaleBy() instead. + * @param {Number} duration duration in seconds + * @param {Number} sx sx scale parameter in X + * @param {Number|Null} [sy=] sy scale parameter in Y, if Null equal to sx + * @return {cc.ScaleBy} + */ +cc.ScaleBy.create = cc.scaleBy; + +/** Blinks a cc.Node object by modifying it's visible attribute + * @class + * @extends cc.ActionInterval + * @param {Number} duration duration in seconds + * @param {Number} blinks blinks in times + * @example + * var action = new cc.Blink(2, 10); + */ +cc.Blink = cc.ActionInterval.extend(/** @lends cc.Blink# */{ + _times: 0, + _originalState: false, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {Number} duration duration in seconds + * @param {Number} blinks blinks in times + */ + ctor: function (duration, blinks) { + cc.ActionInterval.prototype.ctor.call(this); + blinks !== undefined && this.initWithDuration(duration, blinks); + }, + + /** + * Initializes the action. + * @param {Number} duration duration in seconds + * @param {Number} blinks blinks in times + * @return {Boolean} + */ + initWithDuration: function (duration, blinks) { + if (cc.ActionInterval.prototype.initWithDuration.call(this, duration)) { + this._times = blinks; + return true; + } + return false; + }, + + /** + * returns a new clone of the action + * @returns {cc.Blink} + */ + clone: function () { + var action = new cc.Blink(); + this._cloneDecoration(action); + action.initWithDuration(this._duration, this._times); + return action; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @param {Number} dt time in seconds + */ + update: function (dt) { + dt = this._computeEaseTime(dt); + if (this.target && !this.isDone()) { + var slice = 1.0 / this._times; + var m = dt % slice; + this.target.visible = (m > (slice / 2)); + } + }, + + /** + * Start the action with target. + * @param {cc.Node} target + */ + startWithTarget: function (target) { + cc.ActionInterval.prototype.startWithTarget.call(this, target); + this._originalState = target.visible; + }, + + /** + * stop the action + */ + stop: function () { + this.target.visible = this._originalState; + cc.ActionInterval.prototype.stop.call(this); + }, + + /** + * Returns a reversed action. + * @return {cc.Blink} + */ + reverse: function () { + var action = new cc.Blink(this._duration, this._times); + this._cloneDecoration(action); + this._reverseEaseList(action); + return action; + } +}); +/** + * Blinks a cc.Node object by modifying it's visible attribute. + * @function + * @param {Number} duration duration in seconds + * @param blinks blinks in times + * @return {cc.Blink} + * @example + * // example + * var action = cc.blink(2, 10); + */ +cc.blink = function (duration, blinks) { + return new cc.Blink(duration, blinks); +}; +/** + * Please use cc.blink instead. + * Blinks a cc.Node object by modifying it's visible attribute. + * @static + * @deprecated since v3.0 please use cc.blink instead. + * @param {Number} duration duration in seconds + * @param blinks blinks in times + * @return {cc.Blink} + */ +cc.Blink.create = cc.blink; + +/** Fades an object that implements the cc.RGBAProtocol protocol. It modifies the opacity from the current value to a custom one. + * @warning This action doesn't support "reverse" + * @class + * @extends cc.ActionInterval + * @param {Number} duration + * @param {Number} opacity 0-255, 0 is transparent + * @example + * var action = new cc.FadeTo(1.0, 0); + */ +cc.FadeTo = cc.ActionInterval.extend(/** @lends cc.FadeTo# */{ + _toOpacity: 0, + _fromOpacity: 0, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {Number} duration + * @param {Number} opacity 0-255, 0 is transparent + */ + ctor: function (duration, opacity) { + cc.ActionInterval.prototype.ctor.call(this); + opacity !== undefined && this.initWithDuration(duration, opacity); + }, + + /** + * Initializes the action. + * @param {Number} duration duration in seconds + * @param {Number} opacity + * @return {Boolean} + */ + initWithDuration: function (duration, opacity) { + if (cc.ActionInterval.prototype.initWithDuration.call(this, duration)) { + this._toOpacity = opacity; + return true; + } + return false; + }, + + /** + * returns a new clone of the action + * @returns {cc.FadeTo} + */ + clone: function () { + var action = new cc.FadeTo(); + this._cloneDecoration(action); + action.initWithDuration(this._duration, this._toOpacity); + return action; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @param {Number} time time in seconds + */ + update: function (time) { + time = this._computeEaseTime(time); + var fromOpacity = this._fromOpacity !== undefined ? this._fromOpacity : 255; + this.target.opacity = fromOpacity + (this._toOpacity - fromOpacity) * time; + }, + + /** + * Start this action with target. + * @param {cc.Node} target + */ + startWithTarget: function (target) { + cc.ActionInterval.prototype.startWithTarget.call(this, target); + this._fromOpacity = target.opacity; + } +}); + +/** + * Fades an object that implements the cc.RGBAProtocol protocol. It modifies the opacity from the current value to a custom one. + * @function + * @param {Number} duration + * @param {Number} opacity 0-255, 0 is transparent + * @return {cc.FadeTo} + * @example + * // example + * var action = cc.fadeTo(1.0, 0); + */ +cc.fadeTo = function (duration, opacity) { + return new cc.FadeTo(duration, opacity); +}; +/** + * Please use cc.fadeTo instead. + * Fades an object that implements the cc.RGBAProtocol protocol. It modifies the opacity from the current value to a custom one. + * @static + * @deprecated since v3.0 please use cc.fadeTo instead. + * @param {Number} duration + * @param {Number} opacity 0-255, 0 is transparent + * @return {cc.FadeTo} + */ +cc.FadeTo.create = cc.fadeTo; + +/** Fades In an object that implements the cc.RGBAProtocol protocol. It modifies the opacity from 0 to 255.
+ * The "reverse" of this action is FadeOut + * @class + * @extends cc.FadeTo + * @param {Number} duration duration in seconds + */ +cc.FadeIn = cc.FadeTo.extend(/** @lends cc.FadeIn# */{ + _reverseAction: null, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {Number} duration duration in seconds + */ + ctor: function (duration) { + cc.FadeTo.prototype.ctor.call(this); + if (duration == null) + duration = 0; + this.initWithDuration(duration, 255); + }, + + /** + * Returns a reversed action. + * @return {cc.FadeOut} + */ + reverse: function () { + var action = new cc.FadeOut(); + action.initWithDuration(this._duration, 0); + this._cloneDecoration(action); + this._reverseEaseList(action); + return action; + }, + + /** + * returns a new clone of the action + * @returns {cc.FadeIn} + */ + clone: function () { + var action = new cc.FadeIn(); + this._cloneDecoration(action); + action.initWithDuration(this._duration, this._toOpacity); + return action; + }, + + /** + * Start the action with target. + * @param {cc.Node} target + */ + startWithTarget: function (target) { + if (this._reverseAction) + this._toOpacity = this._reverseAction._fromOpacity; + cc.FadeTo.prototype.startWithTarget.call(this, target); + } +}); + +/** + * Fades In an object that implements the cc.RGBAProtocol protocol. It modifies the opacity from 0 to 255. + * @function + * @param {Number} duration duration in seconds + * @return {cc.FadeIn} + * @example + * //example + * var action = cc.fadeIn(1.0); + */ +cc.fadeIn = function (duration) { + return new cc.FadeIn(duration); +}; +/** + * Please use cc.fadeIn instead. + * Fades In an object that implements the cc.RGBAProtocol protocol. It modifies the opacity from 0 to 255. + * @static + * @deprecated since v3.0 please use cc.fadeIn() instead. + * @param {Number} duration duration in seconds + * @return {cc.FadeIn} + */ +cc.FadeIn.create = cc.fadeIn; + + +/** Fades Out an object that implements the cc.RGBAProtocol protocol. It modifies the opacity from 255 to 0. + * The "reverse" of this action is FadeIn + * @class + * @extends cc.FadeTo + * @param {Number} duration duration in seconds + */ +cc.FadeOut = cc.FadeTo.extend(/** @lends cc.FadeOut# */{ + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {Number} duration duration in seconds + */ + ctor: function (duration) { + cc.FadeTo.prototype.ctor.call(this); + if (duration == null) + duration = 0; + this.initWithDuration(duration, 0); + }, + + /** + * Returns a reversed action. + * @return {cc.FadeIn} + */ + reverse: function () { + var action = new cc.FadeIn(); + action._reverseAction = this; + action.initWithDuration(this._duration, 255); + this._cloneDecoration(action); + this._reverseEaseList(action); + return action; + }, + + /** + * returns a new clone of the action + * @returns {cc.FadeOut} + */ + clone: function () { + var action = new cc.FadeOut(); + this._cloneDecoration(action); + action.initWithDuration(this._duration, this._toOpacity); + return action; + } +}); + +/** + * Fades Out an object that implements the cc.RGBAProtocol protocol. It modifies the opacity from 255 to 0. + * @function + * @param {Number} d duration in seconds + * @return {cc.FadeOut} + * @example + * // example + * var action = cc.fadeOut(1.0); + */ +cc.fadeOut = function (d) { + return new cc.FadeOut(d); +}; +/** + * Please use cc.fadeOut instead. + * Fades Out an object that implements the cc.RGBAProtocol protocol. It modifies the opacity from 255 to 0. + * @static + * @deprecated since v3.0 please use cc.fadeOut instead. + * @param {Number} d duration in seconds + * @return {cc.FadeOut} + */ +cc.FadeOut.create = cc.fadeOut; + +/** Tints a cc.Node that implements the cc.NodeRGB protocol from current tint to a custom one. + * @warning This action doesn't support "reverse" + * @class + * @extends cc.ActionInterval + * @param {Number} duration + * @param {Number} red 0-255 + * @param {Number} green 0-255 + * @param {Number} blue 0-255 + * @example + * var action = new cc.TintTo(2, 255, 0, 255); + */ +cc.TintTo = cc.ActionInterval.extend(/** @lends cc.TintTo# */{ + _to: null, + _from: null, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {Number} duration + * @param {Number} red 0-255 + * @param {Number} green 0-255 + * @param {Number} blue 0-255 + */ + ctor: function (duration, red, green, blue) { + cc.ActionInterval.prototype.ctor.call(this); + this._to = cc.color(0, 0, 0); + this._from = cc.color(0, 0, 0); + + blue !== undefined && this.initWithDuration(duration, red, green, blue); + }, + + /** + * Initializes the action. + * @param {Number} duration + * @param {Number} red 0-255 + * @param {Number} green 0-255 + * @param {Number} blue 0-255 + * @return {Boolean} + */ + initWithDuration: function (duration, red, green, blue) { + if (cc.ActionInterval.prototype.initWithDuration.call(this, duration)) { + this._to = cc.color(red, green, blue); + return true; + } + return false; + }, + + /** + * returns a new clone of the action + * @returns {cc.TintTo} + */ + clone: function () { + var action = new cc.TintTo(); + this._cloneDecoration(action); + var locTo = this._to; + action.initWithDuration(this._duration, locTo.r, locTo.g, locTo.b); + return action; + }, + + /** + * Start the action with target. + * @param {cc.Node} target + */ + startWithTarget: function (target) { + cc.ActionInterval.prototype.startWithTarget.call(this, target); + + this._from = this.target.color; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @param {Number} dt time in seconds + */ + update: function (dt) { + dt = this._computeEaseTime(dt); + var locFrom = this._from, locTo = this._to; + if (locFrom) { + this.target.setColor( + cc.color( + locFrom.r + (locTo.r - locFrom.r) * dt, + locFrom.g + (locTo.g - locFrom.g) * dt, + locFrom.b + (locTo.b - locFrom.b) * dt) + ); + } + } +}); + +/** + * Tints a cc.Node that implements the cc.NodeRGB protocol from current tint to a custom one. + * @function + * @param {Number} duration + * @param {Number} red 0-255 + * @param {Number} green 0-255 + * @param {Number} blue 0-255 + * @return {cc.TintTo} + * @example + * // example + * var action = cc.tintTo(2, 255, 0, 255); + */ +cc.tintTo = function (duration, red, green, blue) { + return new cc.TintTo(duration, red, green, blue); +}; +/** + * Please use cc.tintTo instead. + * Tints a cc.Node that implements the cc.NodeRGB protocol from current tint to a custom one. + * @static + * @deprecated since v3.0 please use cc.tintTo instead. + * @param {Number} duration + * @param {Number} red 0-255 + * @param {Number} green 0-255 + * @param {Number} blue 0-255 + * @return {cc.TintTo} + */ +cc.TintTo.create = cc.tintTo; + + +/** Tints a cc.Node that implements the cc.NodeRGB protocol from current tint to a custom one. + * Relative to their own color change. + * @class + * @extends cc.ActionInterval + * @param {Number} duration duration in seconds + * @param {Number} deltaRed + * @param {Number} deltaGreen + * @param {Number} deltaBlue + * @example + * var action = new cc.TintBy(2, -127, -255, -127); + */ +cc.TintBy = cc.ActionInterval.extend(/** @lends cc.TintBy# */{ + _deltaR: 0, + _deltaG: 0, + _deltaB: 0, + + _fromR: 0, + _fromG: 0, + _fromB: 0, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {Number} duration duration in seconds + * @param {Number} deltaRed + * @param {Number} deltaGreen + * @param {Number} deltaBlue + */ + ctor: function (duration, deltaRed, deltaGreen, deltaBlue) { + cc.ActionInterval.prototype.ctor.call(this); + deltaBlue !== undefined && this.initWithDuration(duration, deltaRed, deltaGreen, deltaBlue); + }, + + /** + * Initializes the action. + * @param {Number} duration + * @param {Number} deltaRed 0-255 + * @param {Number} deltaGreen 0-255 + * @param {Number} deltaBlue 0-255 + * @return {Boolean} + */ + initWithDuration: function (duration, deltaRed, deltaGreen, deltaBlue) { + if (cc.ActionInterval.prototype.initWithDuration.call(this, duration)) { + this._deltaR = deltaRed; + this._deltaG = deltaGreen; + this._deltaB = deltaBlue; + return true; + } + return false; + }, + + /** + * returns a new clone of the action + * @returns {cc.TintBy} + */ + clone: function () { + var action = new cc.TintBy(); + this._cloneDecoration(action); + action.initWithDuration(this._duration, this._deltaR, this._deltaG, this._deltaB); + return action; + }, + + /** + * Start the action with target. + * @param {cc.Node} target + */ + startWithTarget: function (target) { + cc.ActionInterval.prototype.startWithTarget.call(this, target); + + var color = target.color; + this._fromR = color.r; + this._fromG = color.g; + this._fromB = color.b; + + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @param {Number} dt time in seconds + */ + update: function (dt) { + dt = this._computeEaseTime(dt); + + this.target.color = cc.color(this._fromR + this._deltaR * dt, + this._fromG + this._deltaG * dt, + this._fromB + this._deltaB * dt); + + }, + + /** + * Returns a reversed action. + * @return {cc.TintBy} + */ + reverse: function () { + var action = new cc.TintBy(this._duration, -this._deltaR, -this._deltaG, -this._deltaB); + this._cloneDecoration(action); + this._reverseEaseList(action); + return action; + } +}); + +/** + * Tints a cc.Node that implements the cc.NodeRGB protocol from current tint to a custom one. + * Relative to their own color change. + * @function + * @param {Number} duration duration in seconds + * @param {Number} deltaRed + * @param {Number} deltaGreen + * @param {Number} deltaBlue + * @return {cc.TintBy} + * @example + * // example + * var action = cc.tintBy(2, -127, -255, -127); + */ +cc.tintBy = function (duration, deltaRed, deltaGreen, deltaBlue) { + return new cc.TintBy(duration, deltaRed, deltaGreen, deltaBlue); +}; +/** + * Please use cc.tintBy instead. + * Tints a cc.Node that implements the cc.NodeRGB protocol from current tint to a custom one. + * Relative to their own color change. + * @static + * @deprecated since v3.0 please use cc.tintBy instead. + * @param {Number} duration duration in seconds + * @param {Number} deltaRed + * @param {Number} deltaGreen + * @param {Number} deltaBlue + * @return {cc.TintBy} + */ +cc.TintBy.create = cc.tintBy; + +/** Delays the action a certain amount of seconds + * @class + * @extends cc.ActionInterval + */ +cc.DelayTime = cc.ActionInterval.extend(/** @lends cc.DelayTime# */{ + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * Will be overwrite. + * @param {Number} dt time in seconds + */ + update: function (dt) { + }, + + /** + * Returns a reversed action. + * @return {cc.DelayTime} + */ + reverse: function () { + var action = new cc.DelayTime(this._duration); + this._cloneDecoration(action); + this._reverseEaseList(action); + return action; + }, + + /** + * returns a new clone of the action + * @returns {cc.DelayTime} + */ + clone: function () { + var action = new cc.DelayTime(); + this._cloneDecoration(action); + action.initWithDuration(this._duration); + return action; + } +}); + +/** + * Delays the action a certain amount of seconds + * @function + * @param {Number} d duration in seconds + * @return {cc.DelayTime} + * @example + * // example + * var delay = cc.delayTime(1); + */ +cc.delayTime = function (d) { + return new cc.DelayTime(d); +}; +/** + * Please use cc.delayTime instead. + * Delays the action a certain amount of seconds + * @static + * @deprecated since v3.0 please use cc.delaTime instead. + * @param {Number} d duration in seconds + * @return {cc.DelayTime} + */ +cc.DelayTime.create = cc.delayTime; + +/** + *

+ * Executes an action in reverse order, from time=duration to time=0
+ * @warning Use this action carefully. This action is not sequenceable.
+ * Use it as the default "reversed" method of your own actions, but using it outside the "reversed"
+ * scope is not recommended. + *

+ * @class + * @extends cc.ActionInterval + * @param {cc.FiniteTimeAction} action + * @example + * var reverse = new cc.ReverseTime(this); + */ +cc.ReverseTime = cc.ActionInterval.extend(/** @lends cc.ReverseTime# */{ + _other: null, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {cc.FiniteTimeAction} action + */ + ctor: function (action) { + cc.ActionInterval.prototype.ctor.call(this); + this._other = null; + + action && this.initWithAction(action); + }, + + /** + * @param {cc.FiniteTimeAction} action + * @return {Boolean} + */ + initWithAction: function (action) { + if (!action) + throw new Error("cc.ReverseTime.initWithAction(): action must be non null"); + if (action === this._other) + throw new Error("cc.ReverseTime.initWithAction(): the action was already passed in."); + + if (cc.ActionInterval.prototype.initWithDuration.call(this, action._duration)) { + // Don't leak if action is reused + this._other = action; + return true; + } + return false; + }, + + /** + * returns a new clone of the action + * @returns {cc.ReverseTime} + */ + clone: function () { + var action = new cc.ReverseTime(); + this._cloneDecoration(action); + action.initWithAction(this._other.clone()); + return action; + }, + + /** + * Start the action with target. + * @param {cc.Node} target + */ + startWithTarget: function (target) { + cc.ActionInterval.prototype.startWithTarget.call(this, target); + this._other.startWithTarget(target); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @param {Number} dt time in seconds + */ + update: function (dt) { + dt = this._computeEaseTime(dt); + if (this._other) + this._other.update(1 - dt); + }, + + /** + * Returns a reversed action. + * @return {cc.ActionInterval} + */ + reverse: function () { + return this._other.clone(); + }, + + /** + * Stop the action + */ + stop: function () { + this._other.stop(); + cc.Action.prototype.stop.call(this); + } +}); + +/** + * Executes an action in reverse order, from time=duration to time=0. + * @function + * @param {cc.FiniteTimeAction} action + * @return {cc.ReverseTime} + * @example + * // example + * var reverse = cc.reverseTime(this); + */ +cc.reverseTime = function (action) { + return new cc.ReverseTime(action); +}; +/** + * Please use cc.reverseTime instead. + * Executes an action in reverse order, from time=duration to time=0. + * @static + * @deprecated since v3.0 please use cc.reverseTime instead. + * @param {cc.FiniteTimeAction} action + * @return {cc.ReverseTime} + */ +cc.ReverseTime.create = cc.reverseTime; + + +/** Animates a sprite given the name of an Animation + * @class + * @extends cc.ActionInterval + * @param {cc.Animation} animation + * @example + * // create the animation with animation + * var anim = new cc.Animate(dance_grey); + */ +cc.Animate = cc.ActionInterval.extend(/** @lends cc.Animate# */{ + _animation: null, + _nextFrame: 0, + _origFrame: null, + _executedLoops: 0, + _splitTimes: null, + _currFrameIndex: 0, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * create the animate with animation. + * @param {cc.Animation} animation + */ + ctor: function (animation) { + cc.ActionInterval.prototype.ctor.call(this); + this._splitTimes = []; + + animation && this.initWithAnimation(animation); + }, + + /** + * @return {cc.Animation} + */ + getAnimation: function () { + return this._animation; + }, + + /** + * @param {cc.Animation} animation + */ + setAnimation: function (animation) { + this._animation = animation; + }, + + /** + * Gets the index of sprite frame currently displayed. + * @return {Number} + */ + getCurrentFrameIndex: function () { + return this._currFrameIndex; + }, + + /** + * @param {cc.Animation} animation + * @return {Boolean} + */ + initWithAnimation: function (animation) { + if (!animation) + throw new Error("cc.Animate.initWithAnimation(): animation must be non-NULL"); + var singleDuration = animation.getDuration(); + if (this.initWithDuration(singleDuration * animation.getLoops())) { + this._nextFrame = 0; + this.setAnimation(animation); + + this._origFrame = null; + this._executedLoops = 0; + var locTimes = this._splitTimes; + locTimes.length = 0; + + var accumUnitsOfTime = 0; + var newUnitOfTimeValue = singleDuration / animation.getTotalDelayUnits(); + + var frames = animation.getFrames(); + cc.arrayVerifyType(frames, cc.AnimationFrame); + + for (var i = 0; i < frames.length; i++) { + var frame = frames[i]; + var value = (accumUnitsOfTime * newUnitOfTimeValue) / singleDuration; + accumUnitsOfTime += frame.getDelayUnits(); + locTimes.push(value); + } + return true; + } + return false; + }, + + /** + * returns a new clone of the action + * @returns {cc.Animate} + */ + clone: function () { + var action = new cc.Animate(); + this._cloneDecoration(action); + action.initWithAnimation(this._animation.clone()); + return action; + }, + + /** + * Start the action with target. + * @param {cc.Sprite} target + */ + startWithTarget: function (target) { + cc.ActionInterval.prototype.startWithTarget.call(this, target); + if (this._animation.getRestoreOriginalFrame()) + this._origFrame = target.getSpriteFrame(); + this._nextFrame = 0; + this._executedLoops = 0; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @param {Number} dt + */ + update: function (dt) { + dt = this._computeEaseTime(dt); + // if t==1, ignore. Animation should finish with t==1 + if (dt < 1.0) { + dt *= this._animation.getLoops(); + + // new loop? If so, reset frame counter + var loopNumber = 0 | dt; + if (loopNumber > this._executedLoops) { + this._nextFrame = 0; + this._executedLoops++; + } + + // new t for animations + dt = dt % 1.0; + } + + var frames = this._animation.getFrames(); + var numberOfFrames = frames.length, locSplitTimes = this._splitTimes; + for (var i = this._nextFrame; i < numberOfFrames; i++) { + if (locSplitTimes[i] <= dt) { + _currFrameIndex = i; + this.target.setSpriteFrame(frames[_currFrameIndex].getSpriteFrame()); + this._nextFrame = i + 1; + } else { + // Issue 1438. Could be more than one frame per tick, due to low frame rate or frame delta < 1/FPS + break; + } + } + }, + + /** + * Returns a reversed action. + * @return {cc.Animate} + */ + reverse: function () { + var locAnimation = this._animation; + var oldArray = locAnimation.getFrames(); + var newArray = []; + cc.arrayVerifyType(oldArray, cc.AnimationFrame); + if (oldArray.length > 0) { + for (var i = oldArray.length - 1; i >= 0; i--) { + var element = oldArray[i]; + if (!element) + break; + newArray.push(element.clone()); + } + } + var newAnim = new cc.Animation(newArray, locAnimation.getDelayPerUnit(), locAnimation.getLoops()); + newAnim.setRestoreOriginalFrame(locAnimation.getRestoreOriginalFrame()); + var action = new cc.Animate(newAnim); + this._cloneDecoration(action); + this._reverseEaseList(action); + + return action; + }, + + /** + * stop the action + */ + stop: function () { + if (this._animation.getRestoreOriginalFrame() && this.target) + this.target.setSpriteFrame(this._origFrame); + cc.Action.prototype.stop.call(this); + } +}); + +/** + * create the animate with animation + * @function + * @param {cc.Animation} animation + * @return {cc.Animate} + * @example + * // example + * // create the animation with animation + * var anim = cc.animate(dance_grey); + */ +cc.animate = function (animation) { + return new cc.Animate(animation); +}; +/** + * Please use cc.animate instead + * create the animate with animation + * @static + * @deprecated since v3.0 please use cc.animate instead. + * @param {cc.Animation} animation + * @return {cc.Animate} + */ +cc.Animate.create = cc.animate; + +/** + *

+ * Overrides the target of an action so that it always runs on the target
+ * specified at action creation rather than the one specified by runAction. + *

+ * @class + * @extends cc.ActionInterval + * @param {cc.Node} target + * @param {cc.FiniteTimeAction} action + */ +cc.TargetedAction = cc.ActionInterval.extend(/** @lends cc.TargetedAction# */{ + _action: null, + _forcedTarget: null, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Create an action with the specified action and forced target. + * @param {cc.Node} target + * @param {cc.FiniteTimeAction} action + */ + ctor: function (target, action) { + cc.ActionInterval.prototype.ctor.call(this); + action && this.initWithTarget(target, action); + }, + + /** + * Init an action with the specified action and forced target + * @param {cc.Node} target + * @param {cc.FiniteTimeAction} action + * @return {Boolean} + */ + initWithTarget: function (target, action) { + if (this.initWithDuration(action._duration)) { + this._forcedTarget = target; + this._action = action; + return true; + } + return false; + }, + + /** + * returns a new clone of the action + * @returns {cc.TargetedAction} + */ + clone: function () { + var action = new cc.TargetedAction(); + this._cloneDecoration(action); + action.initWithTarget(this._forcedTarget, this._action.clone()); + return action; + }, + + /** + * Start the action with target. + * @param {cc.Node} target + */ + startWithTarget: function (target) { + cc.ActionInterval.prototype.startWithTarget.call(this, target); + this._action.startWithTarget(this._forcedTarget); + }, + + /** + * stop the action + */ + stop: function () { + this._action.stop(); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @param {Number} dt + */ + update: function (dt) { + dt = this._computeEaseTime(dt); + this._action.update(dt); + }, + + /** + * return the target that the action will be forced to run with + * @return {cc.Node} + */ + getForcedTarget: function () { + return this._forcedTarget; + }, + + /** + * set the target that the action will be forced to run with + * @param {cc.Node} forcedTarget + */ + setForcedTarget: function (forcedTarget) { + if (this._forcedTarget !== forcedTarget) + this._forcedTarget = forcedTarget; + } +}); + +/** + * Create an action with the specified action and forced target + * @function + * @param {cc.Node} target + * @param {cc.FiniteTimeAction} action + * @return {cc.TargetedAction} + */ +cc.targetedAction = function (target, action) { + return new cc.TargetedAction(target, action); +}; +/** + * Please use cc.targetedAction instead + * Create an action with the specified action and forced target + * @static + * @deprecated since v3.0 please use cc.targetedAction instead. + * @param {cc.Node} target + * @param {cc.FiniteTimeAction} action + * @return {cc.TargetedAction} + */ +cc.TargetedAction.create = cc.targetedAction; diff --git a/frameworks/cocos2d-html5/cocos2d/actions/CCActionTween.js b/frameworks/cocos2d-html5/cocos2d/actions/CCActionTween.js new file mode 100644 index 0000000..daa6c36 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/actions/CCActionTween.js @@ -0,0 +1,167 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * + * @class + * @extends cc.Class + */ +cc.ActionTweenDelegate = cc.Class.extend(/** @lends cc.ActionTweenDelegate */{ + + /** + * Update Tween Action. + * @param value + * @param key + */ + updateTweenAction: function (value, key) { + } +}); + +/** + * cc.ActionTween + * cc.ActionTween is an action that lets you update any property of an object. + * + * @class + * @extends cc.ActionInterval + * @example + * //For example, if you want to modify the "width" property of a target from 200 to 300 in 2 seconds, then: + * var modifyWidth = cc.actionTween(2,"width",200,300) + * target.runAction(modifyWidth); + * + * //Another example: cc.ScaleTo action could be rewriten using cc.PropertyAction: + * // scaleA and scaleB are equivalents + * var scaleA = cc.scaleTo(2,3); + * var scaleB = cc.actionTween(2,"scale",1,3); + * @param {Number} duration + * @param {String} key + * @param {Number} from + * @param {Number} to + */ +cc.ActionTween = cc.ActionInterval.extend(/** @lends cc.ActionTween */{ + key: "", + from: 0, + to: 0, + delta: 0, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Creates an initializes the action with the property name (key), and the from and to parameters. + * @param {Number} duration + * @param {String} key + * @param {Number} from + * @param {Number} to + */ + ctor: function (duration, key, from, to) { + cc.ActionInterval.prototype.ctor.call(this); + this.key = ""; + + to !== undefined && this.initWithDuration(duration, key, from, to); + }, + + /** + * initializes the action with the property name (key), and the from and to parameters. + * @param {Number} duration + * @param {String} key + * @param {Number} from + * @param {Number} to + * @return {Boolean} + */ + initWithDuration: function (duration, key, from, to) { + if (cc.ActionInterval.prototype.initWithDuration.call(this, duration)) { + this.key = key; + this.to = to; + this.from = from; + return true; + } + return false; + }, + + /** + * Start this tween with target. + * @param {cc.ActionTweenDelegate} target + */ + startWithTarget: function (target) { + if (!target || !target.updateTweenAction) + throw new Error("cc.ActionTween.startWithTarget(): target must be non-null, and target must implement updateTweenAction function"); + cc.ActionInterval.prototype.startWithTarget.call(this, target); + this.delta = this.to - this.from; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update: function (dt) { + this.target.updateTweenAction(this.to - this.delta * (1 - dt), this.key); + }, + + /** + * returns a reversed action. + * @return {cc.ActionTween} + */ + reverse: function () { + return new cc.ActionTween(this.duration, this.key, this.to, this.from); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @return {cc.ActionTween} + */ + clone: function () { + var action = new cc.ActionTween(); + action.initWithDuration(this._duration, this.key, this.from, this.to); + return action; + } +}); + +/** + * Creates an initializes the action with the property name (key), and the from and to parameters. + * @function + * @param {Number} duration + * @param {String} key + * @param {Number} from + * @param {Number} to + * @return {cc.ActionTween} + */ +cc.actionTween = function (duration, key, from, to) { + return new cc.ActionTween(duration, key, from, to); +}; + +/** + * Please use cc.actionTween instead. + * Creates an initializes the action with the property name (key), and the from and to parameters. + * @static + * @deprecated since v3.0
Please use cc.actionTween instead. + * @param {Number} duration + * @param {String} key + * @param {Number} from + * @param {Number} to + * @return {cc.ActionTween} + */ +cc.ActionTween.create = cc.actionTween; diff --git a/frameworks/cocos2d-html5/cocos2d/actions3d/CCActionGrid.js b/frameworks/cocos2d-html5/cocos2d/actions3d/CCActionGrid.js new file mode 100644 index 0000000..61b90a1 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/actions3d/CCActionGrid.js @@ -0,0 +1,427 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * Base class for Grid actions + * @class + * @extends cc.ActionInterval + * @param {Number} duration + * @param {cc.Size} gridSize + */ +cc.GridAction = cc.ActionInterval.extend(/** @lends cc.GridAction# */{ + _gridSize:null, + _gridNodeTarget:null, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {Number} duration + * @param {cc.Size} gridSize + */ + ctor:function(duration, gridSize){ + cc.sys._checkWebGLRenderMode(); + cc.ActionInterval.prototype.ctor.call(this); + this._gridSize = cc.size(0,0); + + gridSize && this.initWithDuration(duration, gridSize); + }, + + _cacheTargetAsGridNode:function (target) { + this._gridNodeTarget = target; + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @return {cc.ActionInterval} + */ + clone:function(){ + var action = new cc.GridAction(); + var locGridSize = this._gridSize; + action.initWithDuration(this._duration, cc.size(locGridSize.width, locGridSize.height)); + return action; + }, + + /** + * called before the action start. It will also set the target. + * + * @param {cc.Node} target + */ + startWithTarget:function (target) { + cc.ActionInterval.prototype.startWithTarget.call(this, target); + cc.renderer.childrenOrderDirty = true; + this._cacheTargetAsGridNode(target); + + var newGrid = this.getGrid(); + + var targetGrid = this._gridNodeTarget.getGrid(); + if (targetGrid && targetGrid.getReuseGrid() > 0) { + var locGridSize = targetGrid.getGridSize(); + if (targetGrid.isActive() && (locGridSize.width === this._gridSize.width) && (locGridSize.height === this._gridSize.height)) + targetGrid.reuse(); + } else { + if (targetGrid && targetGrid.isActive()) + targetGrid.setActive(false); + this._gridNodeTarget.setGrid(newGrid); + this._gridNodeTarget.getGrid().setActive(true); + } + }, + + /** + * Create a cc.ReverseTime action. Opposite with the original motion trajectory. + * @return {cc.ReverseTime} + */ + reverse:function () { + return new cc.ReverseTime(this); + }, + + /** + * Initializes the action with size and duration. + * @param {Number} duration + * @param {cc.Size} gridSize + * @return {Boolean} + */ + initWithDuration:function (duration, gridSize) { + if (cc.ActionInterval.prototype.initWithDuration.call(this, duration)) { + this._gridSize.width = gridSize.width; + this._gridSize.height = gridSize.height; + return true; + } + return false; + }, + + /** + * Returns the grid. + * @return {cc.GridBase} + */ + getGrid:function () { + // Abstract class needs implementation + cc.log("cc.GridAction.getGrid(): it should be overridden in subclass."); + } +}); + +/** + * creates the action with size and duration + * @function + * @param {Number} duration + * @param {cc.Size} gridSize + * @return {cc.GridAction} + */ +cc.gridAction = function (duration, gridSize) { + return new cc.GridAction(duration, gridSize); +}; + +/** + * Please use cc.gridAction instead.
+ * Creates the action with size and duration. + * @param {Number} duration + * @param {cc.Size} gridSize + * @return {cc.GridAction} + * @static + * @deprecated since v3.0
Please use cc.gridAction instead. + */ +cc.GridAction.create = cc.gridAction; + +/** + * Base class for cc.Grid3D actions.
+ * Grid3D actions can modify a non-tiled grid. + * @class + * @extends cc.GridAction + */ +cc.Grid3DAction = cc.GridAction.extend(/** @lends cc.Grid3DAction# */{ + + /** + * returns the grid + * @return {cc.Grid3D} + */ + getGrid:function () { + return new cc.Grid3D(this._gridSize, undefined, undefined, this._gridNodeTarget.getGridRect()); + }, + + /** + * get rect of the grid + * @return {cc.Rect} rect + */ + getGridRect:function () { + return this._gridNodeTarget.getGridRect(); + }, + + /** + * returns the vertex than belongs to certain position in the grid.
+ * It will be deprecated in future, please use getVertex instead. + * @param {cc.Point} position + * @return {cc.Vertex3F} + */ + vertex:function (position) { + return this.getVertex(position); + }, + + /** + * returns the vertex than belongs to certain position in the grid + * @param {cc.Point} position + * @return {cc.Vertex3F} + */ + getVertex: function(position){ + return this.target.grid.getVertex(position); + }, + + /** + * returns the non-transformed vertex than belongs to certain position in the grid
+ * It will be deprecated in future, please use getVertex instead. + * @param {cc.Point} position + * @return {cc.Vertex3F} + */ + originalVertex:function (position) { + return this.getOriginalVertex(position); + }, + + /** + * returns the non-transformed vertex that belongs to certain position in the grid + * @param {cc.Point} position + * @return {cc.Vertex3F} + */ + getOriginalVertex:function (position) { + return this.target.grid.originalVertex(position); + }, + + /** + * sets a new vertex to a certain position of the grid + * @param {cc.Point} position + * @param {cc.Vertex3F} vertex + */ + setVertex:function (position, vertex) { + this.target.grid.setVertex(position, vertex); + } +}); + +/** + * creates the action with size and duration + * @function + * @param {Number} duration + * @param {cc.Size} gridSize + * @return {cc.Grid3DAction} + */ +cc.grid3DAction = function (duration, gridSize) { + return new cc.Grid3DAction(duration, gridSize); +}; +/** + * Please use cc.grid3DAction instead.
+ * creates the action with size and duration.
+ * @param {Number} duration + * @param {cc.Size} gridSize + * @return {cc.Grid3DAction} + * @static + * @deprecated since v3.0
Please use cc.grid3DAction instead. + */ +cc.Grid3DAction.create = cc.grid3DAction; + +/** + * Base class for cc.TiledGrid3D actions. + * @class + * @extends cc.GridAction + */ +cc.TiledGrid3DAction = cc.GridAction.extend(/** @lends cc.TiledGrid3DAction# */{ + + /** + * returns the tile that belongs to a certain position of the grid
+ * It will be deprecated in future, please use getTile instead. + * @param {cc.Point} position + * @return {cc.Quad3} + */ + tile:function (position) { + return this.getTile(position); + }, + + /** + * returns the tile that belongs to a certain position of the grid + * @param {cc.Point} position + * @return {cc.Quad3} + */ + getTile:function (position) { + return this.target.grid.tile(position); + }, + + /** + * returns the non-transformed tile that belongs to a certain position of the grid
+ * It will be deprecated in future, please use getOriginalTile instead. + * @param {cc.Point} position + * @return {cc.Quad3} + */ + originalTile:function (position) { + return this.getOriginalTile(position); + }, + + /** + * returns the non-transformed tile that belongs to a certain position of the grid + * @param {cc.Point} position + * @return {cc.Quad3} + */ + getOriginalTile:function (position) { + return this.target.grid.originalTile(position); + }, + + /** + * sets a new tile to a certain position of the grid + * @param {cc.Point} position + * @param {cc.Quad3} coords + */ + setTile:function (position, coords) { + this.target.grid.setTile(position, coords); + }, + + /** + * returns the grid + * @return {cc.TiledGrid3D} + */ + getGrid:function () { + return new cc.TiledGrid3D(this._gridSize, undefined, undefined, this._gridNodeTarget.getGridRect()); + } +}); + +/** + * Creates the action with duration and grid size + * @function + * @param {Number} duration + * @param {cc.Size} gridSize + * @return {cc.TiledGrid3DAction} + */ +cc.tiledGrid3DAction = function (duration, gridSize) { + return new cc.TiledGrid3DAction(duration, gridSize); +}; + +/** + * Please use cc.tiledGrid3DAction instead + * Creates the action with duration and grid size + * @param {Number} duration + * @param {cc.Size} gridSize + * @return {cc.TiledGrid3DAction} + * @static + * @deprecated since v3.0
Please use cc.tiledGrid3DAction instead. + */ +cc.TiledGrid3DAction.create = cc.tiledGrid3DAction; + +/** + *

+ * cc.StopGrid action.
+ * @warning Don't call this action if another grid action is active.
+ * Call if you want to remove the the grid effect. Example:
+ * cc.sequence(Lens.action(...), cc.stopGrid(...), null);
+ *

+ * @class + * @extends cc.ActionInstant + */ +cc.StopGrid = cc.ActionInstant.extend(/** @lends cc.StopGrid# */{ + + /** + * called before the action start. It will also set the target. + * + * @param {cc.Node} target + */ + startWithTarget:function (target) { + cc.ActionInstant.prototype.startWithTarget.call(this, target); + cc.renderer.childrenOrderDirty = true; + var grid = this.target.grid; + if (grid && grid.isActive()) + grid.setActive(false); + } +}); + +/** + * Allocates and initializes the action + * @function + * @return {cc.StopGrid} + */ +cc.stopGrid = function () { + return new cc.StopGrid(); +}; +/** + * Please use cc.stopGrid instead + * Allocates and initializes the action + * @return {cc.StopGrid} + * @static + * @deprecated since v3.0
Please use cc.stopGrid instead. + */ +cc.StopGrid.create = cc.stopGrid; + +/** + * cc.ReuseGrid action + * @class + * @extends cc.ActionInstant + * @param {Number} times + */ +cc.ReuseGrid = cc.ActionInstant.extend(/** @lends cc.ReuseGrid# */{ + _times:null, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {Number} times + */ + ctor: function(times) { + cc.ActionInstant.prototype.ctor.call(this); + times !== undefined && this.initWithTimes(times); + }, + + /** + * initializes an action with the number of times that the current grid will be reused + * @param {Number} times + * @return {Boolean} + */ + initWithTimes:function (times) { + this._times = times; + return true; + }, + + /** + * called before the action start. It will also set the target. + * + * @param {cc.Node} target + */ + startWithTarget:function (target) { + cc.ActionInstant.prototype.startWithTarget.call(this, target); + cc.renderer.childrenOrderDirty = true; + if (this.target.grid && this.target.grid.isActive()) + this.target.grid.setReuseGrid(this.target.grid.getReuseGrid() + this._times); + } +}); + +/** + * creates an action with the number of times that the current grid will be reused + * @function + * @param {Number} times + * @return {cc.ReuseGrid} + */ +cc.reuseGrid = function (times) { + return new cc.ReuseGrid(times); +}; +/** + * Please use cc.reuseGrid instead + * creates an action with the number of times that the current grid will be reused + * @param {Number} times + * @return {cc.ReuseGrid} + * @static + * @deprecated since v3.0
Please use cc.reuseGrid instead. + */ +cc.ReuseGrid.create = cc.reuseGrid; diff --git a/frameworks/cocos2d-html5/cocos2d/actions3d/CCActionGrid3D.js b/frameworks/cocos2d-html5/cocos2d/actions3d/CCActionGrid3D.js new file mode 100644 index 0000000..31be9c2 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/actions3d/CCActionGrid3D.js @@ -0,0 +1,1257 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * cc.Waves3D action.
+ * Reference the test cases (Effects Advanced Test) + * @class + * @extends cc.Grid3DAction + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} waves + * @param {Number} amplitude + */ +cc.Waves3D = cc.Grid3DAction.extend(/** @lends cc.Waves3D# */{ + _waves: 0, + _amplitude: 0, + _amplitudeRate: 0, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Create a wave 3d action with duration, grid size, waves and amplitude. + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} waves + * @param {Number} amplitude + */ + ctor:function (duration, gridSize, waves, amplitude) { + cc.GridAction.prototype.ctor.call(this); + amplitude !== undefined && this.initWithDuration(duration, gridSize, waves, amplitude); + }, + + /** + * get Amplitude + * @return {Number} + */ + getAmplitude:function () { + return this._amplitude; + }, + + /** + * set Amplitude + * @param {Number} amplitude + */ + setAmplitude:function (amplitude) { + this._amplitude = amplitude; + }, + + /** + * get Amplitude Rate + * @return {Number} + */ + getAmplitudeRate:function () { + return this._amplitudeRate; + }, + + /** + * set Amplitude Rate + * @param {Number} amplitudeRate + */ + setAmplitudeRate:function (amplitudeRate) { + this._amplitudeRate = amplitudeRate; + }, + + /** + * initializes an action with duration, grid size, waves and amplitude + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} waves + * @param {Number} amplitude + * @return {Boolean} + */ + initWithDuration:function (duration, gridSize, waves, amplitude) { + if (cc.Grid3DAction.prototype.initWithDuration.call(this, duration, gridSize)) { + this._waves = waves; + this._amplitude = amplitude; + this._amplitudeRate = 1.0; + return true; + } + return false; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + var locGridSize = this._gridSize; + var locAmplitude = this._amplitude, locPos = cc.p(0, 0); + var locAmplitudeRate = this._amplitudeRate, locWaves = this._waves; + for (var i = 0; i < locGridSize.width + 1; ++i) { + for (var j = 0; j < locGridSize.height + 1; ++j) { + locPos.x = i; + locPos.y = j; + var v = this.originalVertex(locPos); + v.z += (Math.sin(Math.PI * dt * locWaves * 2 + (v.y + v.x) * 0.01) * locAmplitude * locAmplitudeRate); + //cc.log("v.z offset is" + (Math.sin(Math.PI * dt * this._waves * 2 + (v.y + v.x) * 0.01) * this._amplitude * this._amplitudeRate)); + this.setVertex(locPos, v); + } + } + } +}); + +/** + * Create a wave 3d action with duration, grid size, waves and amplitude. + * @function + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} waves + * @param {Number} amplitude + */ +cc.waves3D = function (duration, gridSize, waves, amplitude) { + return new cc.Waves3D(duration, gridSize, waves, amplitude); +}; +/** + * Please use cc.waves3D instead.
+ * Create a wave 3d action with duration, grid size, waves and amplitude. + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} waves + * @param {Number} amplitude + * @static + * @deprecated since v3.0
Please use cc.waves3D instead. + */ +cc.Waves3D.create = cc.waves3D; + +/** + * cc.FlipX3D action.
+ * Flip around.
+ * Reference the test cases (Effects Test) + * @class + * @extends cc.Grid3DAction + * @param {Number} duration + */ +cc.FlipX3D = cc.Grid3DAction.extend(/** @lends cc.FlipX3D# */{ + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Create a Flip X 3D action with duration. + * @param {Number} duration + */ + ctor: function(duration) { + if (duration !== undefined) + cc.GridAction.prototype.ctor.call(this, duration, cc.size(1, 1)); + else cc.GridAction.prototype.ctor.call(this); + }, + + /** + * initializes the action with duration + * @param {Number} duration + * @return {Boolean} + */ + initWithDuration:function (duration) { + return cc.Grid3DAction.prototype.initWithDuration.call(this, duration, cc.size(1, 1)); + }, + + /** + * initializes the action with gridSize and duration + * @param {cc.Size} gridSize + * @param {Number} duration + * @return {Boolean} + */ + initWithSize:function (gridSize, duration) { + if (gridSize.width !== 1 || gridSize.height !== 1) { + // Grid size must be (1,1) + cc.log("Grid size must be (1,1)"); + return false; + } + return cc.Grid3DAction.prototype.initWithDuration.call(this, duration, gridSize); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + var angle = Math.PI * dt; // 180 degrees + var mz = Math.sin(angle); + angle = angle / 2.0; // x calculates degrees from 0 to 90 + var mx = Math.cos(angle); + + var diff = new cc.Vertex3F(); + var tempVer = cc.p(0, 0); + tempVer.x = tempVer.y = 1; + var v0 = this.originalVertex(tempVer); + tempVer.x = tempVer.y = 0; + var v1 = this.originalVertex(tempVer); + + var x0 = v0.x; + var x1 = v1.x; + var x; + var a, b, c, d; + + if (x0 > x1) { + // Normal Grid + a = cc.p(0, 0); + b = cc.p(0, 1); + c = cc.p(1, 0); + d = cc.p(1, 1); + x = x0; + } else { + // Reversed Grid + c = cc.p(0, 0); + d = cc.p(0, 1); + a = cc.p(1, 0); + b = cc.p(1, 1); + x = x1; + } + + diff.x = ( x - x * mx ); + diff.z = Math.abs(parseFloat((x * mz) / 4.0)); + + // bottom-left + var v = this.originalVertex(a); + v.x = diff.x; + v.z += diff.z; + this.setVertex(a, v); + + // upper-left + v = this.originalVertex(b); + v.x = diff.x; + v.z += diff.z; + this.setVertex(b, v); + + // bottom-right + v = this.originalVertex(c); + v.x -= diff.x; + v.z -= diff.z; + this.setVertex(c, v); + + // upper-right + v = this.originalVertex(d); + v.x -= diff.x; + v.z -= diff.z; + this.setVertex(d, v); + } +}); + +/** + * Create a Flip X 3D action with duration.
+ * Flip around. + * @function + * @param {Number} duration + * @return {cc.FlipX3D} + */ +cc.flipX3D = function (duration) { + return new cc.FlipX3D(duration); +}; + +/** + * Please use cc.flipX3D instead.
+ * Create a Flip X 3D action with duration.
+ * Flip around. + * @param {Number} duration + * @return {cc.FlipX3D} + * @static + * @deprecated since v3.0
Please use cc.flipX3D instead. + */ +cc.FlipX3D.create = cc.flipX3D; + +/** + * cc.FlipY3D action.
+ * Upside down.
+ * Reference the test cases (Effects Test) + * @class + * @extends cc.FlipX3D + * @param {Number} duration + */ +cc.FlipY3D = cc.FlipX3D.extend(/** @lends cc.FlipY3D# */{ + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Create a flip Y 3d action with duration. + * @param {Number} duration + */ + ctor: function(duration) { + if (duration !== undefined) + cc.GridAction.prototype.ctor.call(this, duration, cc.size(1, 1)); + else cc.GridAction.prototype.ctor.call(this); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + var angle = Math.PI * dt; // 180 degrees + var mz = Math.sin(angle); + angle = angle / 2.0; // x calculates degrees from 0 to 90 + var my = Math.cos(angle); + + var diff = new cc.Vertex3F(); + + var tempP = cc.p(0, 0); + tempP.x = tempP.y = 1; + var v0 = this.originalVertex(tempP); + tempP.x = tempP.y = 0; + var v1 = this.originalVertex(tempP); + + var y0 = v0.y; + var y1 = v1.y; + var y; + var a, b, c, d; + + if (y0 > y1) { + // Normal Grid + a = cc.p(0, 0); + b = cc.p(0, 1); + c = cc.p(1, 0); + d = cc.p(1, 1); + y = y0; + } else { + // Reversed Grid + b = cc.p(0, 0); + a = cc.p(0, 1); + d = cc.p(1, 0); + c = cc.p(1, 1); + y = y1; + } + + diff.y = y - y * my; + diff.z = Math.abs(parseFloat(y * mz) / 4.0); + + // bottom-left + var v = this.originalVertex(a); + v.y = diff.y; + v.z += diff.z; + this.setVertex(a, v); + + // upper-left + v = this.originalVertex(b); + v.y -= diff.y; + v.z -= diff.z; + this.setVertex(b, v); + + // bottom-right + v = this.originalVertex(c); + v.y = diff.y; + v.z += diff.z; + this.setVertex(c, v); + + // upper-right + v = this.originalVertex(d); + v.y -= diff.y; + v.z -= diff.z; + this.setVertex(d, v); + } +}); + +/** + * Create a flip Y 3d action with duration.
+ * Upside down. + * @function + * @param {Number} duration + * @return {cc.FlipY3D} + */ +cc.flipY3D = function (duration) { + return new cc.FlipY3D(duration); +}; + +/** + * Please use cc.flipY3D instead.
+ * Create a flip Y 3d action with duration. + * @param {Number} duration + * @return {cc.FlipY3D} + * @static + * @deprecated since v3.0
Please use cc.flipY3D instead. + */ +cc.FlipY3D.create = cc.flipY3D; + +/** + * cc.Lens3D action.
+ * Upside down.
+ * Reference the test cases (Effects Test) + * @class + * @extends cc.Grid3DAction + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {cc.Point} position + * @param {Number} radius + */ +cc.Lens3D = cc.Grid3DAction.extend(/** @lends cc.Lens3D# */{ + //lens center position + _position:null, + _radius:0, + //lens effect. Defaults to 0.7 - 0 means no effect, 1 is very strong effect + _lensEffect:0, + //lens is concave. (true = concave, false = convex) default is convex i.e. false + _concave:false, + _dirty:false, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * creates a lens 3d action with center position, radius. + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {cc.Point} position + * @param {Number} radius + */ + ctor:function (duration, gridSize, position, radius) { + cc.GridAction.prototype.ctor.call(this); + + this._position = cc.p(0, 0); + radius !== undefined && this.initWithDuration(duration, gridSize, position, radius); + }, + + /** + * Get lens center position + * @return {Number} + */ + getLensEffect:function () { + return this._lensEffect; + }, + + /** + * Set lens center position + * @param {Number} lensEffect + */ + setLensEffect:function (lensEffect) { + this._lensEffect = lensEffect; + }, + + /** + * Set whether lens is concave + * @param {Boolean} concave + */ + setConcave:function (concave) { + this._concave = concave; + }, + + /** + * get Position + * @return {cc.Point} + */ + getPosition:function () { + return this._position; + }, + + /** + * set Position + * @param {cc.Point} position + */ + setPosition:function (position) { + if (!cc.pointEqualToPoint(position, this._position)) { + this._position.x = position.x; + this._position.y = position.y; + this._dirty = true; + } + }, + + /** + * initializes the action with center position, radius, a grid size and duration + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {cc.Point} position + * @param {Number} radius + * @return {Boolean} + */ + initWithDuration:function (duration, gridSize, position, radius) { + if (cc.Grid3DAction.prototype.initWithDuration.call(this, duration, gridSize)) { + this.setPosition(position); + this._radius = radius; + this._lensEffect = 0.7; + this._dirty = true; + return true; + } + return false; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + if (this._dirty) { + var locGridSizeWidth = this._gridSize.width, locGridSizeHeight = this._gridSize.height; + var locRadius = this._radius, locLensEffect = this._lensEffect; + var locPos = cc.p(0, 0); + var vect = cc.p(0, 0); + var v, r, l, new_r, pre_log; + for (var i = 0; i < locGridSizeWidth + 1; ++i) { + for (var j = 0; j < locGridSizeHeight + 1; ++j) { + locPos.x = i; + locPos.y = j; + v = this.originalVertex(locPos); + vect.x = this._position.x - v.x; + vect.y = this._position.y - v.y; + r = cc.pLength(vect); + + if (r < locRadius) { + r = locRadius - r; + pre_log = r / locRadius; + if (pre_log === 0) + pre_log = 0.001; + + l = Math.log(pre_log) * locLensEffect; + new_r = Math.exp(l) * locRadius; + + r = cc.pLength(vect); + if (r > 0) { + vect.x = vect.x / r; + vect.y = vect.y / r; + + vect.x = vect.x * new_r; + vect.y = vect.y * new_r; + v.z += cc.pLength(vect) * locLensEffect; + } + } + this.setVertex(locPos, v); + } + } + this._dirty = false; + } + } +}); + +/** + * creates a lens 3d action with center position, radius + * @function + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {cc.Point} position + * @param {Number} radius + * @return {cc.Lens3D} + */ +cc.lens3D = function (duration, gridSize, position, radius) { + return new cc.Lens3D(duration, gridSize, position, radius); +}; + +/** + * Please use cc.lens3D instead + * creates a lens 3d action with center position, radius + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {cc.Point} position + * @param {Number} radius + * @return {cc.Lens3D} + * @static + * @deprecated since v3.0
Please use cc.lens3D instead. + */ +cc.Lens3D.create = cc.lens3D; + +/** + * cc.Ripple3D action.
+ * Reference the test cases (Effects Test) + * @class + * @extends cc.Grid3DAction + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {cc.Point} position + * @param {Number} radius + * @param {Number} waves + * @param {Number} amplitude + */ +cc.Ripple3D = cc.Grid3DAction.extend(/** @lends cc.Ripple3D# */{ + /* center position */ + _position: null, + _radius: 0, + _waves: 0, + _amplitude: 0, + _amplitudeRate: 0, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * creates a ripple 3d action with radius, number of waves, amplitude. + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {cc.Point} position + * @param {Number} radius + * @param {Number} waves + * @param {Number} amplitude + */ + ctor:function (duration, gridSize, position, radius, waves, amplitude) { + cc.GridAction.prototype.ctor.call(this); + + this._position = cc.p(0, 0); + amplitude !== undefined && this.initWithDuration(duration, gridSize, position, radius, waves, amplitude); + }, + + /** + * get center position + * @return {cc.Point} + */ + getPosition:function () { + return this._position; + }, + + /** + * set center position + * @param {cc.Point} position + */ + setPosition:function (position) { + this._position.x = position.x; + this._position.y = position.y; + }, + + /** + * get Amplitude + * @return {Number} + */ + getAmplitude:function () { + return this._amplitude; + }, + + /** + * set Amplitude + * @param {Number} amplitude + */ + setAmplitude:function (amplitude) { + this._amplitude = amplitude; + }, + + /** + * get Amplitude rate + * @return {*} + */ + getAmplitudeRate:function () { + return this._amplitudeRate; + }, + + /** + * get amplitude rate + * @param {Number} amplitudeRate + */ + setAmplitudeRate:function (amplitudeRate) { + this._amplitudeRate = amplitudeRate; + }, + + /** + * initializes the action with radius, number of waves, amplitude, a grid size and duration + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {cc.Point} position + * @param {Number} radius + * @param {Number} waves + * @param {Number} amplitude + * @return {Boolean} + */ + initWithDuration:function (duration, gridSize, position, radius, waves, amplitude) { + if (cc.Grid3DAction.prototype.initWithDuration.call(this, duration, gridSize)) { + this.setPosition(position); + this._radius = radius; + this._waves = waves; + this._amplitude = amplitude; + this._amplitudeRate = 1.0; + return true; + } + return false; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + var locGridSizeWidth = this._gridSize.width, locGridSizeHeight = this._gridSize.height; + var locPos = cc.p(0, 0), locRadius = this._radius; + var locWaves = this._waves, locAmplitude = this._amplitude, locAmplitudeRate = this._amplitudeRate; + var v, r, tempPos = cc.p(0, 0); + for (var i = 0; i < (locGridSizeWidth + 1); ++i) { + for (var j = 0; j < (locGridSizeHeight + 1); ++j) { + locPos.x = i; + locPos.y = j; + v = this.originalVertex(locPos); + + tempPos.x = this._position.x - v.x; + tempPos.y = this._position.y - v.y; + r = cc.pLength(tempPos); + + if (r < locRadius) { + r = locRadius - r; + var rate = Math.pow(r / locRadius, 2); + v.z += (Math.sin(dt * Math.PI * locWaves * 2 + r * 0.1) * locAmplitude * locAmplitudeRate * rate); + } + this.setVertex(locPos, v); + } + } + } +}); + +/** + * creates a ripple 3d action with radius, number of waves, amplitude + * @function + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {cc.Point} position + * @param {Number} radius + * @param {Number} waves + * @param {Number} amplitude + * @return {cc.Ripple3D} + */ +cc.ripple3D = function (duration, gridSize, position, radius, waves, amplitude) { + return new cc.Ripple3D(duration, gridSize, position, radius, waves, amplitude); +}; + +/** + * Please use cc.ripple3D instead + * creates a ripple 3d action with radius, number of waves, amplitude + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {cc.Point} position + * @param {Number} radius + * @param {Number} waves + * @param {Number} amplitude + * @return {cc.Ripple3D} + * @static + * @deprecated since v3.0
Please use cc.ripple3D instead. + */ +cc.Ripple3D.create = cc.ripple3D; + +/** + * cc.Shaky3D action.
+ * Reference the test cases (Effects Test) + * @class + * @extends cc.Grid3DAction + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} range + * @param {Boolean} shakeZ + */ +cc.Shaky3D = cc.Grid3DAction.extend(/** @lends cc.Shaky3D# */{ + _randRange: 0, + _shakeZ: false, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Create a shaky3d action with a range, shake Z vertices. + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} range + * @param {Boolean} shakeZ + */ + ctor:function (duration, gridSize, range, shakeZ) { + cc.GridAction.prototype.ctor.call(this); + shakeZ !== undefined && this.initWithDuration(duration, gridSize, range, shakeZ); + }, + + /** + * initializes the action with a range, shake Z vertices, a grid and duration + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} range + * @param {Boolean} shakeZ + * @return {Boolean} + */ + initWithDuration:function (duration, gridSize, range, shakeZ) { + if (cc.Grid3DAction.prototype.initWithDuration.call(this, duration, gridSize)) { + this._randRange = range; + this._shakeZ = shakeZ; + return true; + } + return false; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + var locGridSizeWidth = this._gridSize.width, locGridSizeHeight = this._gridSize.height; + var locRandRange = this._randRange, locShakeZ = this._shakeZ, locP = cc.p(0, 0); + var v; + for (var i = 0; i < (locGridSizeWidth + 1); ++i) { + for (var j = 0; j < (locGridSizeHeight + 1); ++j) { + locP.x = i; + locP.y = j; + v = this.originalVertex(locP); + v.x += (cc.rand() % (locRandRange * 2)) - locRandRange; + v.y += (cc.rand() % (locRandRange * 2)) - locRandRange; + if (locShakeZ) + v.z += (cc.rand() % (locRandRange * 2)) - locRandRange; + this.setVertex(locP, v); + } + } + } +}); + +/** + * creates the action with a range, shake Z vertices, a grid and duration + * @function + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} range + * @param {Boolean} shakeZ + * @return {cc.Shaky3D} + */ +cc.shaky3D = function (duration, gridSize, range, shakeZ) { + return new cc.Shaky3D(duration, gridSize, range, shakeZ); +}; + +/** + * Please use cc.shaky3D instead + * creates the action with a range, shake Z vertices, a grid and duration + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} range + * @param {Boolean} shakeZ + * @return {cc.Shaky3D} + * @static + * @deprecated since v3.0
Please use cc.shaky3D instead. + */ +cc.Shaky3D.create = cc.shaky3D; + +/** + * cc.Liquid action.
+ * Reference the test cases (Effects Test) + * @class + * @extends cc.Grid3DAction + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} waves + * @param {Number} amplitude + */ +cc.Liquid = cc.Grid3DAction.extend(/** @lends cc.Liquid# */{ + _waves: 0, + _amplitude: 0, + _amplitudeRate: 0, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Create a liquid action with amplitude, a grid and duration. + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} waves + * @param {Number} amplitude + */ + ctor: function (duration, gridSize, waves, amplitude) { + cc.GridAction.prototype.ctor.call(this); + amplitude !== undefined && this.initWithDuration(duration, gridSize, waves, amplitude); + }, + + /** + * get amplitude + * @return {Number} + */ + getAmplitude:function () { + return this._amplitude; + }, + + /** + * set amplitude + * @param {Number} amplitude + */ + setAmplitude:function (amplitude) { + this._amplitude = amplitude; + }, + + /** + * get amplitude rate + * @return {Number} + */ + getAmplitudeRate:function () { + return this._amplitudeRate; + }, + + /** + * set amplitude rate + * @param {Number} amplitudeRate + */ + setAmplitudeRate:function (amplitudeRate) { + this._amplitudeRate = amplitudeRate; + }, + + /** + * initializes the action with amplitude, a grid and duration + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} waves + * @param {Number} amplitude + * @return {Boolean} + */ + initWithDuration:function (duration, gridSize, waves, amplitude) { + if (cc.Grid3DAction.prototype.initWithDuration.call(this, duration, gridSize)) { + this._waves = waves; + this._amplitude = amplitude; + this._amplitudeRate = 1.0; + return true; + } + return false; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + var locSizeWidth = this._gridSize.width, locSizeHeight = this._gridSize.height, locPos = cc.p(0, 0); + var locWaves = this._waves, locAmplitude = this._amplitude, locAmplitudeRate = this._amplitudeRate; + var v; + for (var i = 1; i < locSizeWidth; ++i) { + for (var j = 1; j < locSizeHeight; ++j) { + locPos.x = i; + locPos.y = j; + v = this.originalVertex(locPos); + v.x = (v.x + (Math.sin(dt * Math.PI * locWaves * 2 + v.x * .01) * locAmplitude * locAmplitudeRate)); + v.y = (v.y + (Math.sin(dt * Math.PI * locWaves * 2 + v.y * .01) * locAmplitude * locAmplitudeRate)); + this.setVertex(locPos, v); + } + } + } +}); + +/** + * creates the action with amplitude, a grid and duration + * @function + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} waves + * @param {Number} amplitude + * @return {cc.Liquid} + */ +cc.liquid = function (duration, gridSize, waves, amplitude) { + return new cc.Liquid(duration, gridSize, waves, amplitude); +}; + +/** + * Please use cc.liquid instead + * creates the action with amplitude, a grid and duration + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} waves + * @param {Number} amplitude + * @return {cc.Liquid} + * @static + * @deprecated since v3.0
Please use cc.liquid instead. + */ +cc.Liquid.create = cc.liquid; + +/** + * cc.Waves action.
+ * Reference the test cases (Effects Test) + * @class + * @extends cc.Grid3DAction + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} waves + * @param {Number} amplitude + * @param {Boolean} horizontal + * @param {Boolean} vertical + */ +cc.Waves = cc.Grid3DAction.extend(/** @lends cc.Waves# */{ + _waves: 0, + _amplitude: 0, + _amplitudeRate: 0, + _vertical: false, + _horizontal: false, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Create a wave action with amplitude, horizontal sin, vertical sin, a grid and duration. + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} waves + * @param {Number} amplitude + * @param {Boolean} horizontal + * @param {Boolean} vertical + */ + ctor: function (duration, gridSize, waves, amplitude, horizontal, vertical) { + cc.GridAction.prototype.ctor.call(this); + vertical !== undefined && this.initWithDuration(duration, gridSize, waves, amplitude, horizontal, vertical); + }, + + /** + * get amplitude + * @return {Number} + */ + getAmplitude:function () { + return this._amplitude; + }, + + /** + * set amplitude + * @param {Number} amplitude + */ + setAmplitude:function (amplitude) { + this._amplitude = amplitude; + }, + + /** + * get amplitude rate + * @return {Number} + */ + getAmplitudeRate:function () { + return this._amplitudeRate; + }, + + /** + * set amplitude rate + * @param {Number} amplitudeRate + */ + setAmplitudeRate:function (amplitudeRate) { + this._amplitudeRate = amplitudeRate; + }, + + /** + * initializes the action with amplitude, horizontal sin, vertical sin, a grid and duration + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} waves + * @param {Number} amplitude + * @param {Boolean} horizontal + * @param {Boolean} vertical + * @return {Boolean} + */ + initWithDuration:function (duration, gridSize, waves, amplitude, horizontal, vertical) { + if (cc.Grid3DAction.prototype.initWithDuration.call(this, duration, gridSize)) { + this._waves = waves; + this._amplitude = amplitude; + this._amplitudeRate = 1.0; + this._horizontal = horizontal; + this._vertical = vertical; + return true; + } + return false; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + var locSizeWidth = this._gridSize.width, locSizeHeight = this._gridSize.height, locPos = cc.p(0, 0); + var locVertical = this._vertical, locHorizontal = this._horizontal; + var locWaves = this._waves, locAmplitude = this._amplitude, locAmplitudeRate = this._amplitudeRate; + var v; + for (var i = 0; i < locSizeWidth + 1; ++i) { + for (var j = 0; j < locSizeHeight + 1; ++j) { + locPos.x = i; + locPos.y = j; + v = this.originalVertex(locPos); + if (locVertical) + v.x = (v.x + (Math.sin(dt * Math.PI * locWaves * 2 + v.y * .01) * locAmplitude * locAmplitudeRate)); + if (locHorizontal) + v.y = (v.y + (Math.sin(dt * Math.PI * locWaves * 2 + v.x * .01) * locAmplitude * locAmplitudeRate)); + this.setVertex(locPos, v); + } + } + } +}); + +/** + * initializes the action with amplitude, horizontal sin, vertical sin, a grid and duration + * @function + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} waves + * @param {Number} amplitude + * @param {Boolean} horizontal + * @param {Boolean} vertical + * @return {cc.Waves} + */ +cc.waves = function (duration, gridSize, waves, amplitude, horizontal, vertical) { + return new cc.Waves(duration, gridSize, waves, amplitude, horizontal, vertical); +}; + +/** + * Please use cc.waves instead + * initializes the action with amplitude, horizontal sin, vertical sin, a grid and duration + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} waves + * @param {Number} amplitude + * @param {Boolean} horizontal + * @param {Boolean} vertical + * @return {cc.Waves} + * @static + * @deprecated since v3.0
Please use cc.waves instead. + */ +cc.Waves.create = cc.waves; + +/** @brief */ +/** + * cc.Twirl action.
+ * Reference the test cases (Effects Test) + * @class + * @extends cc.Grid3DAction + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {cc.Point} position + * @param {Number} twirls + * @param {Number} amplitude + */ +cc.Twirl = cc.Grid3DAction.extend(/** @lends cc.Twirl# */{ + /* twirl center */ + _position: null, + _twirls: 0, + _amplitude: 0, + _amplitudeRate: 0, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Create a grid 3d action with center position, number of twirls, amplitude, a grid size and duration. + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {cc.Point} position + * @param {Number} twirls + * @param {Number} amplitude + */ + ctor:function (duration, gridSize, position, twirls, amplitude) { + cc.GridAction.prototype.ctor.call(this); + + this._position = cc.p(0, 0); + amplitude !== undefined && this.initWithDuration(duration, gridSize, position, twirls, amplitude); + }, + + /** + * get twirl center + * @return {cc.Point} + */ + getPosition:function () { + return this._position; + }, + + /** + * set twirl center + * @param {cc.Point} position + */ + setPosition:function (position) { + this._position.x = position.x; + this._position.y = position.y; + }, + + /** + * get amplitude + * @return {Number} + */ + getAmplitude:function () { + return this._amplitude; + }, + + /** + * set amplitude + * @param {Number} amplitude + */ + setAmplitude:function (amplitude) { + this._amplitude = amplitude; + }, + + /** + * get amplitude rate + * @return {Number} + */ + getAmplitudeRate:function () { + return this._amplitudeRate; + }, + + /** + * set amplitude rate + * @param {Number} amplitudeRate + */ + setAmplitudeRate:function (amplitudeRate) { + this._amplitudeRate = amplitudeRate; + }, + + /** initializes the action with center position, number of twirls, amplitude, a grid size and duration */ + initWithDuration:function (duration, gridSize, position, twirls, amplitude) { + if (cc.Grid3DAction.prototype.initWithDuration.call(this, duration, gridSize)) { + this.setPosition(position); + this._twirls = twirls; + this._amplitude = amplitude; + this._amplitudeRate = 1.0; + return true; + } + return false; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * + * @param {Number} dt + */ + update:function (dt) { + var c = this._position; + var locSizeWidth = this._gridSize.width, locSizeHeight = this._gridSize.height, locPos = cc.p(0, 0); + var amp = 0.1 * this._amplitude * this._amplitudeRate; + var locTwirls = this._twirls; + var v, a, dX, dY, avg = cc.p(0, 0); + for (var i = 0; i < (locSizeWidth + 1); ++i) { + for (var j = 0; j < (locSizeHeight + 1); ++j) { + locPos.x = i; + locPos.y = j; + v = this.originalVertex(locPos); + + avg.x = i - (locSizeWidth / 2.0); + avg.y = j - (locSizeHeight / 2.0); + + a = cc.pLength(avg) * Math.cos(Math.PI / 2.0 + dt * Math.PI * locTwirls * 2) * amp; + + dX = Math.sin(a) * (v.y - c.y) + Math.cos(a) * (v.x - c.x); + dY = Math.cos(a) * (v.y - c.y) - Math.sin(a) * (v.x - c.x); + + v.x = c.x + dX; + v.y = c.y + dY; + + this.setVertex(locPos, v); + } + } + } +}); + +/** + * creates the action with center position, number of twirls, amplitude, a grid size and duration + * @function + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {cc.Point} position + * @param {Number} twirls + * @param {Number} amplitude + * @return {cc.Twirl} + */ +cc.twirl = function (duration, gridSize, position, twirls, amplitude) { + return new cc.Twirl(duration, gridSize, position, twirls, amplitude); +}; + +/** + * Please use cc.twirl instead + * creates the action with center position, number of twirls, amplitude, a grid size and duration + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {cc.Point} position + * @param {Number} twirls + * @param {Number} amplitude + * @return {cc.Twirl} + * @static + * @deprecated since v3.0
Please use cc.twirl instead. + */ +cc.Twirl.create = cc.twirl; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/cocos2d/actions3d/CCActionPageTurn3D.js b/frameworks/cocos2d-html5/cocos2d/actions3d/CCActionPageTurn3D.js new file mode 100644 index 0000000..ae7313b --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/actions3d/CCActionPageTurn3D.js @@ -0,0 +1,130 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + *

+ * This action simulates a page turn from the bottom right hand corner of the screen.
+ * It's not much use by itself but is used by the PageTurnTransition.
+ *
+ * Based on an original paper by L Hong et al.
+ * http://www.parc.com/publication/1638/turning-pages-of-3d-electronic-books.html + *

+ * @class + * @extends cc.Grid3DAction + */ +cc.PageTurn3D = cc.Grid3DAction.extend(/** @lends cc.PageTurn3D# */{ + getGrid: function(){ + var result = new cc.Grid3D(this._gridSize, undefined, undefined, this._gridNodeTarget.getGridRect()); + result.setNeedDepthTestForBlit(true); + return result; + }, + + clone: function(){ + var ret = new cc.PageTurn3D(); + ret.initWithDuration(this._duration, this._gridSize); + return ret; + }, + + /** + * Update each tick
+ * Time is the percentage of the way through the duration + */ + update:function (time) { + var tt = Math.max(0, time - 0.25); + var deltaAy = (tt * tt * 500); + var ay = -100 - deltaAy; + + var deltaTheta = Math.sqrt(time); + var theta = deltaTheta>0.5?Math.PI/2 *deltaTheta : Math.PI/2*(1-deltaTheta); + var rotateByYAxis = (2-time)*Math.PI; + + var sinTheta = Math.sin(theta); + var cosTheta = Math.cos(theta); + + var locGridSize = this._gridSize; + var locVer = cc.p(0, 0); + for (var i = 0; i <= locGridSize.width; ++i) { + for (var j = 0; j <= locGridSize.height; ++j) { + locVer.x = i; + locVer.y = j; + // Get original vertex + var p = this.getOriginalVertex(locVer); + + p.x -= this.getGridRect().x; + var R = Math.sqrt((p.x * p.x) + ((p.y - ay) * (p.y - ay))); + var r = R * sinTheta; + var alpha = Math.asin(p.x / R); + var beta = alpha / sinTheta; + var cosBeta = Math.cos(beta); + + // If beta > PI then we've wrapped around the cone + // Reduce the radius to stop these points interfering with others + if (beta <= Math.PI) + p.x = ( r * Math.sin(beta)); + else + p.x = 0; //Force X = 0 to stop wrapped points + + p.y = ( R + ay - ( r * (1 - cosBeta) * sinTheta)); + + // We scale z here to avoid the animation being + // too much bigger than the screen due to perspectve transform + p.z = (r * ( 1 - cosBeta ) * cosTheta);// "100" didn't work for + p.x = p.z * Math.sin(rotateByYAxis) + p.x * Math.cos(rotateByYAxis); + p.z = p.z * Math.cos(rotateByYAxis) - p.x * Math.cos(rotateByYAxis); + p.z/= 7; + // Stop z coord from dropping beneath underlying page in a transition + // issue #751 + if (p.z < 0.5) + p.z = 0.5; + + // Set new coords + p.x+= this.getGridRect().x; + this.setVertex(locVer, p); + } + } + } +}); + +/** + * create PageTurn3D action + * @function + * @param {Number} duration + * @param {cc.Size} gridSize + * @return {cc.PageTurn3D} + */ +cc.pageTurn3D = function (duration, gridSize) { + return new cc.PageTurn3D(duration, gridSize); +}; +/** + * Please use cc.pageTurn3D instead + * create PageTurn3D action + * @param {Number} duration + * @param {cc.Size} gridSize + * @return {cc.PageTurn3D} + * @static + * @deprecated since v3.0 please use cc.pageTurn3D instead. + */ +cc.PageTurn3D.create = cc.pageTurn3D; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/cocos2d/actions3d/CCActionTiledGrid.js b/frameworks/cocos2d-html5/cocos2d/actions3d/CCActionTiledGrid.js new file mode 100644 index 0000000..67dffc5 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/actions3d/CCActionTiledGrid.js @@ -0,0 +1,1300 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * cc.ShakyTiles3D action.
+ * Reference the test cases (Effects Test) + * @class + * @extends cc.TiledGrid3DAction + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} range + * @param {Boolean} shakeZ + */ +cc.ShakyTiles3D = cc.TiledGrid3DAction.extend(/** @lends cc.ShakyTiles3D# */{ + _randRange:0, + _shakeZ:false, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Creates the action with a range, whether or not to shake Z vertices, a grid size, and duration. + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} range + * @param {Boolean} shakeZ + */ + ctor:function (duration, gridSize, range, shakeZ) { + cc.GridAction.prototype.ctor.call(this); + shakeZ !== undefined && this.initWithDuration(duration, gridSize, range, shakeZ); + }, + + /** + * Initializes the action with a range, whether or not to shake Z vertices, a grid size, and duration. + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} range + * @param {Boolean} shakeZ + * @return {Boolean} + */ + initWithDuration:function (duration, gridSize, range, shakeZ) { + if (cc.TiledGrid3DAction.prototype.initWithDuration.call(this, duration, gridSize)) { + this._randRange = range; + this._shakeZ = shakeZ; + return true; + } + return false; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval.
+ * @param {Number} dt + */ + update:function (dt) { + var locGridSize = this._gridSize, locRandRange = this._randRange; + var locPos = cc.p(0, 0); + for (var i = 0; i < locGridSize.width; ++i) { + for (var j = 0; j < locGridSize.height; ++j) { + locPos.x = i; + locPos.y = j; + var coords = this.originalTile(locPos); + + // X + coords.bl.x += ( cc.rand() % (locRandRange * 2) ) - locRandRange; + coords.br.x += ( cc.rand() % (locRandRange * 2) ) - locRandRange; + coords.tl.x += ( cc.rand() % (locRandRange * 2) ) - locRandRange; + coords.tr.x += ( cc.rand() % (locRandRange * 2) ) - locRandRange; + + // Y + coords.bl.y += ( cc.rand() % (locRandRange * 2) ) - locRandRange; + coords.br.y += ( cc.rand() % (locRandRange * 2) ) - locRandRange; + coords.tl.y += ( cc.rand() % (locRandRange * 2) ) - locRandRange; + coords.tr.y += ( cc.rand() % (locRandRange * 2) ) - locRandRange; + + if (this._shakeZ) { + coords.bl.z += ( cc.rand() % (locRandRange * 2) ) - locRandRange; + coords.br.z += ( cc.rand() % (locRandRange * 2) ) - locRandRange; + coords.tl.z += ( cc.rand() % (locRandRange * 2) ) - locRandRange; + coords.tr.z += ( cc.rand() % (locRandRange * 2) ) - locRandRange; + } + + this.setTile(locPos, coords); + } + } + } +}); + +/** + * Creates the action with a range, whether or not to shake Z vertices, a grid size, and duration.
+ * Reference the test cases (Effects Test) + * @function + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} range + * @param {Boolean} shakeZ + * @return {cc.ShakyTiles3D} + */ +cc.shakyTiles3D = function (duration, gridSize, range, shakeZ) { + return new cc.ShakyTiles3D(duration, gridSize, range, shakeZ); +}; + +/** + * Please use cc.shakyTiles3D instead.
+ * creates the action with a range, whether or not to shake Z vertices, a grid size, and duration.
+ * Reference the test cases (Effects Test) + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} range + * @param {Boolean} shakeZ + * @return {cc.ShakyTiles3D} + * @static + * @deprecated since v3.0
Please use cc.shakyTiles3D instead. + */ +cc.ShakyTiles3D.create = cc.shakyTiles3D; + +/** + * cc.ShatteredTiles3D action.
+ * Reference the test cases (Effects Test) + * @class + * @extends cc.TiledGrid3DAction + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} range + * @param {Boolean} shatterZ + */ +cc.ShatteredTiles3D = cc.TiledGrid3DAction.extend(/** @lends cc.ShatteredTiles3D# */{ + _randRange:0, + _once:false, + _shatterZ:false, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Creates the action with a range, whether of not to shatter Z vertices, a grid size and duration. + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} range + * @param {Boolean} shatterZ + */ + ctor:function (duration, gridSize, range, shatterZ) { + cc.GridAction.prototype.ctor.call(this); + shatterZ !== undefined && this.initWithDuration(duration, gridSize, range, shatterZ); + }, + + /** + * Initializes the action with a range, whether or not to shatter Z vertices, a grid size and duration.
+ * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} range + * @param {Boolean} shatterZ + * @return {Boolean} + */ + initWithDuration:function (duration, gridSize, range, shatterZ) { + if (cc.TiledGrid3DAction.prototype.initWithDuration.call(this, duration, gridSize)) { + this._once = false; + this._randRange = range; + this._shatterZ = shatterZ; + return true; + } + return false; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval.
+ * @param {Number} dt + */ + update:function (dt) { + if (this._once === false) { + var locGridSize = this._gridSize, locRandRange = this._randRange; + var coords, locPos = cc.p(0, 0); + for (var i = 0; i < locGridSize.width; ++i) { + for (var j = 0; j < locGridSize.height; ++j) { + locPos.x = i; + locPos.y = j; + coords = this.originalTile(locPos); + + // X + coords.bl.x += ( cc.rand() % (locRandRange * 2) ) - locRandRange; + coords.br.x += ( cc.rand() % (locRandRange * 2) ) - locRandRange; + coords.tl.x += ( cc.rand() % (locRandRange * 2) ) - locRandRange; + coords.tr.x += ( cc.rand() % (locRandRange * 2) ) - locRandRange; + + // Y + coords.bl.y += ( cc.rand() % (locRandRange * 2) ) - locRandRange; + coords.br.y += ( cc.rand() % (locRandRange * 2) ) - locRandRange; + coords.tl.y += ( cc.rand() % (locRandRange * 2) ) - locRandRange; + coords.tr.y += ( cc.rand() % (locRandRange * 2) ) - locRandRange; + + if (this._shatterZ) { + coords.bl.z += ( cc.rand() % (locRandRange * 2) ) - locRandRange; + coords.br.z += ( cc.rand() % (locRandRange * 2) ) - locRandRange; + coords.tl.z += ( cc.rand() % (locRandRange * 2) ) - locRandRange; + coords.tr.z += ( cc.rand() % (locRandRange * 2) ) - locRandRange; + } + this.setTile(locPos, coords); + } + } + this._once = true; + } + } +}); + +/** + * Creates the action with a range, whether of not to shatter Z vertices, a grid size and duration.
+ * Reference the test cases (Effects Test) + * @function + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} range + * @param {Boolean} shatterZ + * @return {cc.ShatteredTiles3D} + */ +cc.shatteredTiles3D = function (duration, gridSize, range, shatterZ) { + return new cc.ShatteredTiles3D(duration, gridSize, range, shatterZ); +}; + +/** + * Please use cc.shatteredTiles3D instead.
+ * Creates the action with a range, whether of not to shatter Z vertices, a grid size and duration.
+ * Reference the test cases (Effects Test) + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} range + * @param {Boolean} shatterZ + * @return {cc.ShatteredTiles3D} + * @static + * @deprecated since v3.0
Please use cc.shatteredTiles3D instead. + */ +cc.ShatteredTiles3D.create = cc.shatteredTiles3D; + +/** + * A Tile composed of position, startPosition and delta. + * @Class + * @constructor + * @param {cc.Point} [position=cc.p(0,0)] + * @param {cc.Point} [startPosition=cc.p(0,0)] + * @param {cc.Size} [delta=cc.p(0,0)] + */ +cc.Tile = function (position, startPosition, delta) { + this.position = position || cc.p(0,0); + this.startPosition = startPosition || cc.p(0,0); + this.delta = delta || cc.p(0,0); +}; + +/** + * cc.ShuffleTiles action, Shuffle the tiles in random order.
+ * Reference the test cases (Effects Test) + * @class + * @extends cc.TiledGrid3DAction + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} seed + */ +cc.ShuffleTiles = cc.TiledGrid3DAction.extend(/** @lends cc.ShuffleTiles# */{ + _seed:0, + _tilesCount:0, + _tilesOrder:null, + _tiles:null, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Creates the action with a random seed, the grid size and the duration. + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} seed + */ + ctor:function (duration, gridSize, seed) { + cc.GridAction.prototype.ctor.call(this); + this._tilesOrder = []; + this._tiles = []; + + seed !== undefined && this.initWithDuration(duration, gridSize, seed); + }, + + /** + * Initializes the action with a random seed, the grid size and the duration. + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} seed + * @return {Boolean} + */ + initWithDuration:function (duration, gridSize, seed) { + if (cc.TiledGrid3DAction.prototype.initWithDuration.call(this, duration, gridSize)) { + this._seed = seed; + this._tilesOrder.length = 0; + this._tiles.length = 0; + return true; + } + return false; + }, + + /** + * Shuffle + * @param {Array} array + * @param {Number} len + */ + shuffle:function (array, len) { + for (var i = len - 1; i >= 0; i--) { + var j = 0 | (cc.rand() % (i + 1)); + var v = array[i]; + array[i] = array[j]; + array[j] = v; + } + }, + + /** + * Get Delta + * @param {cc.Size} pos + */ + getDelta:function (pos) { + var locGridSize = this._gridSize; + var idx = pos.width * locGridSize.height + pos.height; + return cc.size(((this._tilesOrder[idx] / locGridSize.height) - pos.width), + ((this._tilesOrder[idx] % locGridSize.height) - pos.height)); + }, + + /** + * Place Tile + * @param {cc.Point} pos + * @param {cc.Tile} tile + */ + placeTile:function (pos, tile) { + var coords = this.originalTile(pos); + + var step = this.target.grid.getStep(); + var locPosition = tile.position; + coords.bl.x += (locPosition.x * step.x); + coords.bl.y += (locPosition.y * step.y); + + coords.br.x += (locPosition.x * step.x); + coords.br.y += (locPosition.y * step.y); + + coords.tl.x += (locPosition.x * step.x); + coords.tl.y += (locPosition.y * step.y); + + coords.tr.x += (locPosition.x * step.x); + coords.tr.y += (locPosition.y * step.y); + + this.setTile(pos, coords); + }, + + /** + * Start with target + * @param {cc.Node} target + */ + startWithTarget:function (target) { + cc.TiledGrid3DAction.prototype.startWithTarget.call(this, target); + var locGridSize = this._gridSize; + + this._tilesCount = locGridSize.width * locGridSize.height; + var locTilesOrder = this._tilesOrder; + locTilesOrder.length = 0; + + /** + * Use k to loop. Because m_nTilesCount is unsigned int, + * and i is used later for int. + */ + for (var k = 0; k < this._tilesCount; ++k) + locTilesOrder[k] = k; + this.shuffle(locTilesOrder, this._tilesCount); + + var locTiles = this._tiles ; + locTiles.length = 0; + var tileIndex = 0, tempSize = cc.size(0,0); + for (var i = 0; i < locGridSize.width; ++i) { + for (var j = 0; j < locGridSize.height; ++j) { + locTiles[tileIndex] = new cc.Tile(); + locTiles[tileIndex].position = cc.p(i, j); + locTiles[tileIndex].startPosition = cc.p(i, j); + tempSize.width = i; + tempSize.height = j; + locTiles[tileIndex].delta = this.getDelta(tempSize); + ++tileIndex; + } + } + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @param {Number} dt + */ + update:function (dt) { + var tileIndex = 0, locGridSize = this._gridSize, locTiles = this._tiles; + var selTile, locPos = cc.p(0, 0); + for (var i = 0; i < locGridSize.width; ++i) { + for (var j = 0; j < locGridSize.height; ++j) { + locPos.x = i; + locPos.y = j; + selTile = locTiles[tileIndex]; + selTile.position.x = selTile.delta.width * dt; + selTile.position.y = selTile.delta.height * dt; + this.placeTile(locPos, selTile); + ++tileIndex; + } + } + } +}); + +/** + * Creates the action with a random seed, the grid size and the duration.
+ * Reference the test cases (Effects Test) + * @function + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} seed + * @return {cc.ShuffleTiles} + */ +cc.shuffleTiles = function (duration, gridSize, seed) { + return new cc.ShuffleTiles(duration, gridSize, seed); +}; + +/** + * Please use cc.shuffleTiles instead.
+ * Creates the action with a random seed, the grid size and the duration.
+ * Reference the test cases (Effects Test) + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} seed + * @return {cc.ShuffleTiles} + * @static + * @deprecated since v3.0
Please use cc.shuffleTiles instead. + */ +cc.ShuffleTiles.create = cc.shuffleTiles; + +/** + * cc.FadeOutTRTiles action. Fades out the tiles in a Top-Right direction.
+ * Reference the test cases (Effects Test) + * @class + * @extends cc.TiledGrid3DAction + */ +cc.FadeOutTRTiles = cc.TiledGrid3DAction.extend(/** @lends cc.FadeOutTRTiles# */{ + /** + * Test function + * @param {cc.Point} pos + * @param {Number} time + */ + testFunc:function (pos, time) { + var locX = this._gridSize.width * time; + var locY = this._gridSize.height * time; + if (locX === this._gridSize.width && locY === this._gridSize.height) return 0.0; + if ((locX + locY) === 0.0) + return 1.0; + return Math.pow((pos.x + pos.y) / (locX + locY), 6); + }, + + /** + * Turn on Tile + * @param {cc.Point} pos + */ + turnOnTile:function (pos) { + this.setTile(pos, this.originalTile(pos)); + }, + + /** + * Turn Off Tile + * @param {cc.Point} pos + */ + turnOffTile:function (pos) { + this.setTile(pos, new cc.Quad3()); + }, + + /** + * Transform tile + * @param {cc.Point} pos + * @param {Number} distance + */ + transformTile:function (pos, distance) { + var coords = this.originalTile(pos); + var step = this.target.grid.getStep(); + + coords.bl.x += (step.x / 2) * (1.0 - distance); + coords.bl.y += (step.y / 2) * (1.0 - distance); + + coords.br.x -= (step.x / 2) * (1.0 - distance); + coords.br.y += (step.y / 2) * (1.0 - distance); + + coords.tl.x += (step.x / 2) * (1.0 - distance); + coords.tl.y -= (step.y / 2) * (1.0 - distance); + + coords.tr.x -= (step.x / 2) * (1.0 - distance); + coords.tr.y -= (step.y / 2) * (1.0 - distance); + + this.setTile(pos, coords); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @param {Number} dt + */ + update:function (dt) { + var locGridSize = this._gridSize; + var locPos = cc.p(0, 0), distance; + for (var i = 0; i < locGridSize.width; ++i) { + for (var j = 0; j < locGridSize.height; ++j) { + locPos.x = i; + locPos.y = j; + distance = this.testFunc(locPos, dt); + if (distance === 0) + this.turnOffTile(locPos); + else if (distance < 1) + this.transformTile(locPos, distance); + else + this.turnOnTile(locPos); + } + } + } +}); + +/** + * Creates the action with the grid size and the duration.
+ * Reference the test cases (Effects Test) + * @function + * @param duration + * @param gridSize + * @return {cc.FadeOutTRTiles} + */ +cc.fadeOutTRTiles = function (duration, gridSize) { + return new cc.FadeOutTRTiles(duration, gridSize); +}; + +/** + * Please use cc.fadeOutTRTiles instead.
+ * Creates the action with the grid size and the duration.
+ * Reference the test cases (Effects Test) + * @param duration + * @param gridSize + * @return {cc.FadeOutTRTiles} + * @static + * @deprecated since v3.0
Please use cc.fadeOutTRTiles instead. + */ +cc.FadeOutTRTiles.create = cc.fadeOutTRTiles; + +/** + * cc.FadeOutBLTiles action. Fades out the tiles in a Bottom-Left direction.
+ * Reference the test cases (Effects Test) + * @class + * @extends cc.FadeOutTRTiles + */ +cc.FadeOutBLTiles = cc.FadeOutTRTiles.extend(/** @lends cc.FadeOutBLTiles# */{ + /** + * Test function + * @param {cc.Point} pos + * @param {Number} time + */ + testFunc:function (pos, time) { + var locX = this._gridSize.width * (1.0 - time); + var locY = this._gridSize.height * (1.0 - time); + if ((locX + locY) === 0) + return 0.0; + if ((pos.x + pos.y) === 0) + return 1.0; + + return Math.pow((locX + locY) / (pos.x + pos.y), 6); + } +}); + +/** + * Creates the action with the grid size and the duration.
+ * Reference the test cases (Effects Test) + * @function + * @param duration + * @param gridSize + * @return {cc.FadeOutBLTiles} + */ +cc.fadeOutBLTiles = function (duration, gridSize) { + return new cc.FadeOutBLTiles(duration, gridSize); +}; + +/** + * Please use cc.fadeOutBLTiles instead.
+ * Creates the action with the grid size and the duration.
+ * Reference the test cases (Effects Test) + * @param duration + * @param gridSize + * @return {cc.FadeOutBLTiles} + * @static + * @deprecated since v3.0
Please use cc.fadeOutBLTiles instead. + */ +cc.FadeOutBLTiles.create = cc.fadeOutBLTiles; + +/** + * cc.FadeOutUpTiles action. Fades out the tiles in upwards direction.
+ * Reference the test cases (Effects Test) + * @class + * @extends cc.FadeOutTRTiles + */ +cc.FadeOutUpTiles = cc.FadeOutTRTiles.extend(/** @lends cc.FadeOutUpTiles# */{ + /** + * Test function + * @param {cc.Point} pos + * @param {Number} time + */ + testFunc:function (pos, time) { + var locY = this._gridSize.height * time; + if( locY === this._gridSize.height) return 0.0; + if (locY === 0.0) return 1.0; + return Math.pow(pos.y / locY, 6); + }, + + transformTile:function (pos, distance) { + var coords = this.originalTile(pos); + var step = this.target.grid.getStep(); + + coords.bl.y += (step.y / 2) * (1.0 - distance); + coords.br.y += (step.y / 2) * (1.0 - distance); + coords.tl.y -= (step.y / 2) * (1.0 - distance); + coords.tr.y -= (step.y / 2) * (1.0 - distance); + + this.setTile(pos, coords); + } +}); + +/** + * Creates the action with the grid size and the duration.
+ * Reference the test cases (Effects Test) + * @function + * @param {Number} duration + * @param {cc.Size} gridSize + * @return {cc.FadeOutUpTiles} + */ +cc.fadeOutUpTiles = function (duration, gridSize) { + return new cc.FadeOutUpTiles(duration, gridSize); +}; + +/** + * Please use cc.fadeOutUpTiles instead.
+ * Creates the action with the grid size and the duration.
+ * Reference the test cases (Effects Test) + * @param {Number} duration + * @param {cc.Size} gridSize + * @return {cc.FadeOutUpTiles} + * @static + * @deprecated since v3.0
Please use cc.fadeOutUpTiles instead. + */ +cc.FadeOutUpTiles.create = cc.fadeOutUpTiles; + +/** + * cc.FadeOutDownTiles action. Fades out the tiles in downwards direction.
+ * Reference the test cases (Effects Test) + * @class + * @extends cc.FadeOutUpTiles + */ +cc.FadeOutDownTiles = cc.FadeOutUpTiles.extend(/** @lends cc.FadeOutDownTiles# */{ + /** + * Test function + * @param {cc.Point} pos + * @param {Number} time + */ + testFunc:function (pos, time) { + var locY = this._gridSize.height * (1.0 - time); + if( locY === 0.0 ) return 0.0; + if (pos.y === 0) return 1.0; + return Math.pow(locY / pos.y, 6); + } +}); + +/** + * Creates the action with the grid size and the duration.
+ * Reference the test cases (Effects Test) + * @function + * @param {Number} duration + * @param {cc.Size} gridSize + * @return {cc.FadeOutDownTiles} + */ +cc.fadeOutDownTiles = function (duration, gridSize) { + return new cc.FadeOutDownTiles(duration, gridSize); +}; +/** + * Please use cc.fadeOutDownTiles instead.
+ * Creates the action with the grid size and the duration.
+ * Reference the test cases (Effects Test) + * @param {Number} duration + * @param {cc.Size} gridSize + * @return {cc.FadeOutDownTiles} + * @static + * @deprecated since v3.0
Please use cc.fadeOutDownTiles instead. + */ +cc.FadeOutDownTiles.create = cc.fadeOutDownTiles; + +/** + * cc.TurnOffTiles action.
+ * Turn off the files in random order.
+ * Reference the test cases (Effects Test) + * @class + * @extends cc.TiledGrid3DAction + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number|Null} [seed=0] + * @example + * // turnOffTiles without seed + * var toff = new cc.TurnOffTiles(this._duration, cc.size(x, y)); + * + * // turnOffTiles with seed + * var toff = new cc.TurnOffTiles(this._duration, cc.size(x, y), 0); + */ +cc.TurnOffTiles = cc.TiledGrid3DAction.extend(/** @lends cc.TurnOffTiles# */{ + _seed:null, + _tilesCount:0, + _tilesOrder:null, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Creates the action with a random seed, the grid size and the duration. + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number|Null} [seed=0] + */ + ctor:function (duration, gridSize, seed) { + cc.GridAction.prototype.ctor.call(this); + this._tilesOrder = []; + + gridSize !== undefined && this.initWithDuration(duration, gridSize, seed); + }, + + /** + * Initializes the action with a random seed, the grid size and the duration. + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number|Null} [seed=0] + * @return {Boolean} + */ + initWithDuration:function (duration, gridSize, seed) { + if (cc.TiledGrid3DAction.prototype.initWithDuration.call(this, duration, gridSize)) { + this._seed = seed || 0; + this._tilesOrder.length = 0; + return true; + } + return false; + }, + + /** + * Shuffle + * @param {Array} array + * @param {Number} len + */ + shuffle:function (array, len) { + for (var i = len - 1; i >= 0; i--) { + var j = 0 | (cc.rand() % (i + 1)); + var v = array[i]; + array[i] = array[j]; + array[j] = v; + } + }, + + /** + * Turn on tile. + * @param {cc.Point} pos + */ + turnOnTile:function (pos) { + this.setTile(pos, this.originalTile(pos)); + }, + + /** + * Turn off title. + * @param {cc.Point} pos + */ + turnOffTile:function (pos) { + this.setTile(pos, new cc.Quad3()); + }, + + /** + * called before the action start. It will also set the target. + * @param {cc.Node} target + */ + startWithTarget:function (target) { + cc.TiledGrid3DAction.prototype.startWithTarget.call(this, target); + + this._tilesCount = this._gridSize.width * this._gridSize.height; + var locTilesOrder = this._tilesOrder; + locTilesOrder.length = 0; + for (var i = 0; i < this._tilesCount; ++i) + locTilesOrder[i] = i; + this.shuffle(locTilesOrder, this._tilesCount); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @param {Number} dt + */ + update:function (dt) { + var l = 0 | (dt * this._tilesCount), locGridSize = this._gridSize; + var t,tilePos = cc.p(0,0), locTilesOrder = this._tilesOrder; + for (var i = 0; i < this._tilesCount; i++) { + t = locTilesOrder[i]; + tilePos.x = 0 | (t / locGridSize.height); + tilePos.y = t % (0 | locGridSize.height); + if (i < l) + this.turnOffTile(tilePos); + else + this.turnOnTile(tilePos); + } + } +}); + +/** + * Creates the action with a random seed, the grid size and the duration.
+ * Reference the test cases (Effects Test) + * @function + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number|Null} [seed=0] + * @return {cc.TurnOffTiles} + * @example + * // example + * // turnOffTiles without seed + * var toff = cc.turnOffTiles(this._duration, cc.size(x, y)); + * + * // turnOffTiles with seed + * var toff = cc.turnOffTiles(this._duration, cc.size(x, y), 0); + */ +cc.turnOffTiles = function (duration, gridSize, seed) { + return new cc.TurnOffTiles(duration, gridSize, seed); +}; +/** + * Please use cc.turnOffTiles instead.
+ * Creates the action with a random seed, the grid size and the duration.
+ * Reference the test cases (Effects Test) + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number|Null} [seed=0] + * @return {cc.TurnOffTiles} + * @static + * @deprecated since v3.0
Please use cc.turnOffTiles instead. + */ +cc.TurnOffTiles.create = cc.turnOffTiles; + +/** + * cc.WavesTiles3D action.
+ * Reference the test cases (Effects Test) + * @class + * @extends cc.TiledGrid3DAction + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} waves + * @param {Number} amplitude + */ +cc.WavesTiles3D = cc.TiledGrid3DAction.extend(/** @lends cc.WavesTiles3D# */{ + _waves:0, + _amplitude:0, + _amplitudeRate:0, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * creates the action with a number of waves, the waves amplitude, the grid size and the duration. + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} waves + * @param {Number} amplitude + */ + ctor:function (duration, gridSize, waves, amplitude) { + cc.GridAction.prototype.ctor.call(this); + amplitude !== undefined && this.initWithDuration(duration, gridSize, waves, amplitude); + }, + + /** + * get amplitude of waves + * @return {Number} + */ + getAmplitude:function () { + return this._amplitude; + }, + + /** + * set amplitude of waves + * @param {Number} amplitude + */ + setAmplitude:function (amplitude) { + this._amplitude = amplitude; + }, + + /** + * get amplitude rate of waves + * @return {Number} + */ + getAmplitudeRate:function () { + return this._amplitudeRate; + }, + + /** + * set amplitude rate of waves + * @param {Number} amplitudeRate + */ + setAmplitudeRate:function (amplitudeRate) { + this._amplitudeRate = amplitudeRate; + }, + + /** + * initializes the action with a number of waves, the waves amplitude, the grid size and the duration + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} waves + * @param {Number} amplitude + * @return {Boolean} + */ + initWithDuration:function (duration, gridSize, waves, amplitude) { + if (cc.TiledGrid3DAction.prototype.initWithDuration.call(this, duration, gridSize)) { + this._waves = waves; + this._amplitude = amplitude; + this._amplitudeRate = 1.0; + return true; + } + return false; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @param {Number} dt + */ + update:function (dt) { + var locGridSize = this._gridSize, locWaves = this._waves, locAmplitude = this._amplitude, locAmplitudeRate = this._amplitudeRate; + var locPos = cc.p(0, 0), coords; + for (var i = 0; i < locGridSize.width; i++) { + for (var j = 0; j < locGridSize.height; j++) { + locPos.x = i; + locPos.y = j; + coords = this.originalTile(locPos); + coords.bl.z = (Math.sin(dt * Math.PI * locWaves * 2 + + (coords.bl.y + coords.bl.x) * 0.01) * locAmplitude * locAmplitudeRate); + coords.br.z = coords.bl.z; + coords.tl.z = coords.bl.z; + coords.tr.z = coords.bl.z; + this.setTile(locPos, coords); + } + } + } +}); + +/** + * creates the action with a number of waves, the waves amplitude, the grid size and the duration.
+ * Reference the test cases (Effects Test) + * @function + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} waves + * @param {Number} amplitude + * @return {cc.WavesTiles3D} + */ +cc.wavesTiles3D = function (duration, gridSize, waves, amplitude) { + return new cc.WavesTiles3D(duration, gridSize, waves, amplitude); +}; +/** + * Please use cc.wavesTiles3D instead + * creates the action with a number of waves, the waves amplitude, the grid size and the duration.
+ * Reference the test cases (Effects Test) + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} waves + * @param {Number} amplitude + * @return {cc.WavesTiles3D} + * @static + * @deprecated since v3.0
Please use cc.wavesTiles3D instead. + */ +cc.WavesTiles3D.create = cc.wavesTiles3D; + +/** + * cc.JumpTiles3D action. A sin function is executed to move the tiles across the Z axis.
+ * Reference the test cases (Effects Test) + * @class + * @extends cc.TiledGrid3DAction + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} numberOfJumps + * @param {Number} amplitude + */ +cc.JumpTiles3D = cc.TiledGrid3DAction.extend(/** @lends cc.JumpTiles3D# */{ + _jumps:0, + _amplitude:0, + _amplitudeRate:0, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * creates the action with the number of jumps, the sin amplitude, the grid size and the duration. + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} numberOfJumps + * @param {Number} amplitude + */ + ctor:function (duration, gridSize, numberOfJumps, amplitude) { + cc.GridAction.prototype.ctor.call(this); + amplitude !== undefined && this.initWithDuration(duration, gridSize, numberOfJumps, amplitude); + }, + + /** + * get amplitude of the sin + * @return {Number} + */ + getAmplitude:function () { + return this._amplitude; + }, + + /** + * set amplitude of the sin + * @param {Number} amplitude + */ + setAmplitude:function (amplitude) { + this._amplitude = amplitude; + }, + + /** + * get amplitude rate + * @return {Number} + */ + getAmplitudeRate:function () { + return this._amplitudeRate; + }, + + /** + * set amplitude rate + * @param amplitudeRate + */ + setAmplitudeRate:function (amplitudeRate) { + this._amplitudeRate = amplitudeRate; + }, + + /** + * initializes the action with the number of jumps, the sin amplitude, the grid size and the duration + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} numberOfJumps + * @param {Number} amplitude + */ + initWithDuration:function (duration, gridSize, numberOfJumps, amplitude) { + if (cc.TiledGrid3DAction.prototype.initWithDuration.call(this, duration, gridSize)) { + this._jumps = numberOfJumps; + this._amplitude = amplitude; + this._amplitudeRate = 1.0; + return true; + } + return false; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @param {Number} dt + */ + update:function (dt) { + var sinz = (Math.sin(Math.PI * dt * this._jumps * 2) * this._amplitude * this._amplitudeRate ); + var sinz2 = (Math.sin(Math.PI * (dt * this._jumps * 2 + 1)) * this._amplitude * this._amplitudeRate ); + + var locGridSize = this._gridSize; + var locGrid = this.target.grid; + var coords, locPos = cc.p(0, 0); + for (var i = 0; i < locGridSize.width; i++) { + for (var j = 0; j < locGridSize.height; j++) { + locPos.x = i; + locPos.y = j; + //hack for html5 + //var coords = this.originalTile(cc.p(i, j)); + coords = locGrid.originalTile(locPos); + + if (((i + j) % 2) === 0) { + coords.bl.z += sinz; + coords.br.z += sinz; + coords.tl.z += sinz; + coords.tr.z += sinz; + } else { + coords.bl.z += sinz2; + coords.br.z += sinz2; + coords.tl.z += sinz2; + coords.tr.z += sinz2; + } + //hack for html5 + //this.setTile(cc.p(i, j), coords); + locGrid.setTile(locPos, coords); + } + } + } +}); + +/** + * creates the action with the number of jumps, the sin amplitude, the grid size and the duration.
+ * Reference the test cases (Effects Test) + * @function + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} numberOfJumps + * @param {Number} amplitude + * @return {cc.JumpTiles3D} + */ +cc.jumpTiles3D = function (duration, gridSize, numberOfJumps, amplitude) { + return new cc.JumpTiles3D(duration, gridSize, numberOfJumps, amplitude); +}; + +/** + * Please use cc.jumpTiles3D instead + * creates the action with the number of jumps, the sin amplitude, the grid size and the duration.
+ * Reference the test cases (Effects Test) + * @param {Number} duration + * @param {cc.Size} gridSize + * @param {Number} numberOfJumps + * @param {Number} amplitude + * @return {cc.JumpTiles3D} + * @static + * @deprecated since v3.0
Please use cc.jumpTiles3D instead. + */ +cc.JumpTiles3D.create = cc.jumpTiles3D; + +/** + * cc.SplitRows action.
+ * Reference the test cases (Effects Test) + * @class + * @extends cc.TiledGrid3DAction + * @param {Number} duration + * @param {Number} rows + */ +cc.SplitRows = cc.TiledGrid3DAction.extend(/** @lends cc.SplitRows# */{ + _rows:0, + _winSize:null, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * creates the action with the number of rows to split and the duration. + * @param {Number} duration + * @param {Number} rows + */ + ctor:function (duration, rows) { + cc.GridAction.prototype.ctor.call(this); + rows !== undefined && this.initWithDuration(duration, rows); + }, + + /** + * initializes the action with the number of rows to split and the duration + * @param {Number} duration + * @param {Number} rows + * @return {Boolean} + */ + initWithDuration:function (duration, rows) { + this._rows = rows; + return cc.TiledGrid3DAction.prototype.initWithDuration.call(this, duration, cc.size(1, rows)); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @param {Number} dt + */ + update:function (dt) { + var locGridSize = this._gridSize, locWinSizeWidth = this._winSize.width; + var coords, direction, locPos = cc.p(0, 0); + for (var j = 0; j < locGridSize.height; ++j) { + locPos.y = j; + coords = this.originalTile(locPos); + direction = 1; + + if ((j % 2 ) === 0) + direction = -1; + + coords.bl.x += direction * locWinSizeWidth * dt; + coords.br.x += direction * locWinSizeWidth * dt; + coords.tl.x += direction * locWinSizeWidth * dt; + coords.tr.x += direction * locWinSizeWidth * dt; + + this.setTile(locPos, coords); + } + }, + + /** + * called before the action start. It will also set the target. + * @param {cc.Node} target + */ + startWithTarget:function (target) { + cc.TiledGrid3DAction.prototype.startWithTarget.call(this, target); + this._winSize = cc.director.getWinSizeInPixels(); + } +}); + +/** + * creates the action with the number of rows to split and the duration.
+ * Reference the test cases (Effects Test) + * @function + * @param {Number} duration + * @param {Number} rows + * @return {cc.SplitRows} + */ +cc.splitRows = function (duration, rows) { + return new cc.SplitRows(duration, rows); +}; + +/** + * Please use cc.splitRows instead + * creates the action with the number of rows to split and the duration.
+ * Reference the test cases (Effects Test) + * @param {Number} duration + * @param {Number} rows + * @return {cc.SplitRows} + * @static + * @deprecated since v3.0
Please use cc.splitRows instead. + */ +cc.SplitRows.create = cc.splitRows; + +/** + * cc.SplitCols action.
+ * Reference the test cases (Effects Test) + * @class + * @extends cc.TiledGrid3DAction + * @param {Number} duration + * @param {Number} cols + */ +cc.SplitCols = cc.TiledGrid3DAction.extend(/** @lends cc.SplitCols# */{ + _cols:0, + _winSize:null, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Creates the action with the number of columns to split and the duration. + * @param {Number} duration + * @param {Number} cols + */ + ctor:function (duration, cols) { + cc.GridAction.prototype.ctor.call(this); + cols !== undefined && this.initWithDuration(duration, cols); + }, + /** + * initializes the action with the number of columns to split and the duration + * @param {Number} duration + * @param {Number} cols + * @return {Boolean} + */ + initWithDuration:function (duration, cols) { + this._cols = cols; + return cc.TiledGrid3DAction.prototype.initWithDuration.call(this, duration, cc.size(cols, 1)); + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @param {Number} dt + */ + update:function (dt) { + var locGridSizeWidth = this._gridSize.width, locWinSizeHeight = this._winSize.height; + var coords, direction, locPos = cc.p(0, 0); + for (var i = 0; i < locGridSizeWidth; ++i) { + locPos.x = i; + coords = this.originalTile(locPos); + direction = 1; + + if ((i % 2 ) === 0) + direction = -1; + + coords.bl.y += direction * locWinSizeHeight * dt; + coords.br.y += direction * locWinSizeHeight * dt; + coords.tl.y += direction * locWinSizeHeight * dt; + coords.tr.y += direction * locWinSizeHeight * dt; + + this.setTile(locPos, coords); + } + cc.renderer.childrenOrderDirty = true; + }, + + /** + * called before the action start. It will also set the target. + * @param {cc.Node} target + */ + startWithTarget:function (target) { + cc.TiledGrid3DAction.prototype.startWithTarget.call(this, target); + this._winSize = cc.director.getWinSizeInPixels(); + } +}); + +/** + * creates the action with the number of columns to split and the duration.
+ * Reference the test cases (Effects Test) + * @function + * @param {Number} duration + * @param {Number} cols + * @return {cc.SplitCols} + */ +cc.splitCols = function (duration, cols) { + return new cc.SplitCols(duration, cols); +}; + +/** + * Please use cc.splitCols instead. + * creates the action with the number of columns to split and the duration.
+ * Reference the test cases (Effects Test) + * @param {Number} duration + * @param {Number} cols + * @return {cc.SplitCols} + * @static + * @deprecated since v3.0
Please use cc.splitCols instead. + */ +cc.SplitCols.create = cc.splitCols; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/cocos2d/audio/CCAudio.js b/frameworks/cocos2d-html5/cocos2d/audio/CCAudio.js new file mode 100644 index 0000000..7762bf4 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/audio/CCAudio.js @@ -0,0 +1,941 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * Audio support in the browser + * + * MULTI_CHANNEL : Multiple audio while playing - If it doesn't, you can only play background music + * WEB_AUDIO : Support for WebAudio - Support W3C WebAudio standards, all of the audio can be played + * AUTOPLAY : Supports auto-play audio - if Don‘t support it, On a touch detecting background music canvas, and then replay + * REPLAY_AFTER_TOUCH : The first music will fail, must be replay after touchstart + * USE_EMPTIED_EVENT : Whether to use the emptied event to replace load callback + * DELAY_CREATE_CTX : delay created the context object - only webAudio + * NEED_MANUAL_LOOP : loop attribute failure, need to perform loop manually + * + * May be modifications for a few browser version + */ +(function () { + + var DEBUG = false; + + var sys = cc.sys; + var version = sys.browserVersion; + + // check if browser supports Web Audio + // check Web Audio's context + var supportWebAudio = !!(window.AudioContext || window.webkitAudioContext || window.mozAudioContext); + + var support = {ONLY_ONE: false, WEB_AUDIO: supportWebAudio, DELAY_CREATE_CTX: false, ONE_SOURCE: false}; + + if (sys.browserType === sys.BROWSER_TYPE_FIREFOX) { + support.DELAY_CREATE_CTX = true; + support.USE_LOADER_EVENT = 'canplay'; + } + + if (sys.os === sys.OS_IOS) { + support.USE_LOADER_EVENT = 'loadedmetadata'; + } + + if (sys.os === sys.OS_ANDROID) { + if (sys.browserType === sys.BROWSER_TYPE_UC) { + support.ONE_SOURCE = true; + } + } + + window.__audioSupport = support; + + if (DEBUG) { + setTimeout(function () { + cc.log("browse type: " + sys.browserType); + cc.log("browse version: " + version); + cc.log("MULTI_CHANNEL: " + window.__audioSupport.MULTI_CHANNEL); + cc.log("WEB_AUDIO: " + window.__audioSupport.WEB_AUDIO); + cc.log("AUTOPLAY: " + window.__audioSupport.AUTOPLAY); + }, 0); + } + +})(); + +/** + * Encapsulate DOM and webAudio + */ +cc.Audio = cc.Class.extend({ + interruptPlay: false, + src: null, + _element: null, + _AUDIO_TYPE: "AUDIO", + + ctor: function (url) { + this.src = url; + }, + + setBuffer: function (buffer) { + this._AUDIO_TYPE = "WEBAUDIO"; + this._element = new cc.Audio.WebAudio(buffer); + }, + + setElement: function (element) { + this._AUDIO_TYPE = "AUDIO"; + this._element = element; + + // Prevent partial browser from playing after the end does not reset the paused tag + // Will cause the player to judge the status of the error + element.addEventListener('ended', function () { + if (!element.loop) { + element.paused = true; + } + }); + }, + + play: function (offset, loop) { + if (!this._element) { + this.interruptPlay = false; + return; + } + this._element.loop = loop; + this._element.play(); + if (this._AUDIO_TYPE === 'AUDIO' && this._element.paused) { + this.stop(); + cc.Audio.touchPlayList.push({ loop: loop, offset: offset, audio: this._element }); + } + + if (cc.Audio.bindTouch === false) { + cc.Audio.bindTouch = true; + // Listen to the touchstart body event and play the audio when necessary. + cc.game.canvas.addEventListener('touchstart', cc.Audio.touchStart); + } + }, + + getPlaying: function () { + if (!this._element) return true; + return !this._element.paused; + }, + + stop: function () { + if (!this._element) { + this.interruptPlay = true; + return; + } + this._element.pause(); + try { + this._element.currentTime = 0; + } catch (err) { + } + }, + + pause: function () { + if (!this._element) { + this.interruptPlay = true; + return; + } + this._element.pause(); + }, + + resume: function () { + if (!this._element) { + this.interruptPlay = false; + return; + } + this._element.play(); + }, + + setVolume: function (volume) { + if (!this._element) return; + this._element.volume = volume; + }, + + getVolume: function () { + if (!this._element) return; + return this._element.volume; + }, + + cloneNode: function () { + var audio = new cc.Audio(this.src); + if (this._AUDIO_TYPE === "AUDIO") { + var elem = document.createElement("audio"); + var sources = elem.getElementsByTagName('source'); + for (var i = 0; i < sources.length; i++) { + elem.appendChild(sources[i]); + } + elem.src = this.src; + audio.setElement(elem); + } else { + audio.setBuffer(this._element.buffer); + } + return audio; + } +}); + +cc.Audio.touchPlayList = [ + //{ offset: 0, audio: audio } +]; + +cc.Audio.bindTouch = false; +cc.Audio.touchStart = function () { + var list = cc.Audio.touchPlayList; + var item = null; + while (item = list.pop()) { + item.audio.loop = !!item.loop; + item.audio.play(item.offset); + } +}; + +cc.Audio.WebAudio = function (buffer) { + this.buffer = buffer; + this.context = cc.Audio._context; + + var volume = this.context['createGain'](); + volume['gain'].value = 1; + volume['connect'](this.context['destination']); + this._volume = volume; + + this._loop = false; + + // The time stamp on the audio time axis when the recording begins to play. + this._startTime = -1; + // Record the currently playing Source + this._currentSource = null; + // Record the time has been played + this.playedLength = 0; + + this._currextTimer = null; +}; + +cc.Audio.WebAudio.prototype = { + constructor: cc.Audio.WebAudio, + + get paused() { + // If the current audio is a loop, then paused is false + if (this._currentSource && this._currentSource.loop) + return false; + + // StartTime does not have value, as the default -1, it does not begin to play + if (this._startTime === -1) + return true; + + // currentTime - startTime > durationTime + return this.context.currentTime - this._startTime > this.buffer.duration; + }, + set paused(bool) { + }, + + get loop() { + return this._loop; + }, + set loop(bool) { + return this._loop = bool; + }, + + get volume() { + return this._volume['gain'].value; + }, + set volume(num) { + return this._volume['gain'].value = num; + }, + + get currentTime() { + return this.playedLength; + }, + set currentTime(num) { + return this.playedLength = num; + }, + + play: function (offset) { + + // If repeat play, you need to stop before an audio + if (this._currentSource && !this.paused) { + this._currentSource.stop(0); + this.playedLength = 0; + } + + var audio = this.context["createBufferSource"](); + audio.buffer = this.buffer; + audio["connect"](this._volume); + audio.loop = this._loop; + + this._startTime = this.context.currentTime; + offset = offset || this.playedLength; + + var duration = this.buffer.duration; + if (!this._loop) { + if (audio.start) + audio.start(0, offset, duration - offset); + else if (audio["notoGrainOn"]) + audio["noteGrainOn"](0, offset, duration - offset); + else + audio["noteOn"](0, offset, duration - offset); + } else { + if (audio.start) + audio.start(0); + else if (audio["notoGrainOn"]) + audio["noteGrainOn"](0); + else + audio["noteOn"](0); + } + + this._currentSource = audio; + + // If the current audio context time stamp is 0 + // There may be a need to touch events before you can actually start playing audio + // So here to add a timer to determine whether the real start playing audio, if not, then the incoming touchPlay queue + if (this.context.currentTime === 0) { + var self = this; + clearTimeout(this._currextTimer); + this._currextTimer = setTimeout(function () { + if (self.context.currentTime === 0) { + cc.Audio.touchPlayList.push({ + offset: offset, + audio: self + }); + } + }, 10); + } + }, + pause: function () { + // Record the time the current has been played + this.playedLength = this.context.currentTime - this._startTime; + //If the duration of playedLendth exceeds the audio, you should take the remainder + this.playedLength %= this.buffer.duration; + var audio = this._currentSource; + this._currentSource = null; + this._startTime = -1; + if (audio) + audio.stop(0); + } +}; + +(function (polyfill) { + + var SWA = polyfill.WEB_AUDIO, SWB = polyfill.ONLY_ONE; + + var support = []; + + (function () { + var audio = document.createElement("audio"); + if (audio.canPlayType) { + var ogg = audio.canPlayType('audio/ogg; codecs="vorbis"'); + if (ogg && ogg !== "") support.push(".ogg"); + var mp3 = audio.canPlayType("audio/mpeg"); + if (mp3 && mp3 !== "") support.push(".mp3"); + var wav = audio.canPlayType('audio/wav; codecs="1"'); + if (wav && wav !== "") support.push(".wav"); + var mp4 = audio.canPlayType("audio/mp4"); + if (mp4 && mp4 !== "") support.push(".mp4"); + var m4a = audio.canPlayType("audio/x-m4a"); + if (m4a && m4a !== "") support.push(".m4a"); + } + })(); + try { + if (SWA) { + var context = new (window.AudioContext || window.webkitAudioContext || window.mozAudioContext)(); + cc.Audio._context = context; + // check context integrity + if ( + !context["createBufferSource"] || + !context["createGain"] || + !context["destination"] || + !context["decodeAudioData"] + ) { + throw 'context is incomplete'; + } + if (polyfill.DELAY_CREATE_CTX) + setTimeout(function () { + context = new (window.AudioContext || window.webkitAudioContext || window.mozAudioContext)(); + cc.Audio._context = context; + }, 0); + } + } catch (error) { + SWA = false; + cc.log("browser don't support web audio"); + } + + var loader = { + + cache: {}, + + useWebAudio: true, + + loadBuffer: function (url, cb) { + if (!SWA) return; // WebAudio Buffer + + var request = cc.loader.getXMLHttpRequest(); + request.open("GET", url, true); + request.responseType = "arraybuffer"; + + // Our asynchronous callback + request.onload = function () { + if (request._timeoutId >= 0) { + clearTimeout(request._timeoutId); + } + context["decodeAudioData"](request.response, function (buffer) { + //success + cb(null, buffer); + //audio.setBuffer(buffer); + }, function () { + //error + cb('decode error - ' + url); + }); + }; + + request.onerror = function () { + cb('request error - ' + url); + }; + if (request.ontimeout === undefined) { + request._timeoutId = setTimeout(function () { + request.ontimeout(); + }, request.timeout); + } + request.ontimeout = function () { + cb('request timeout - ' + url); + }; + + request.send(); + }, + + load: function (realUrl, url, res, cb) { + + if (support.length === 0) + return cb("can not support audio!"); + + var audio = cc.loader.getRes(url); + if (audio) + return cb(null, audio); + + if (cc.loader.audioPath) + realUrl = cc.path.join(cc.loader.audioPath, realUrl); + + var extname = cc.path.extname(realUrl); + + var typeList = [extname]; + for (var i = 0; i < support.length; i++) { + if (extname !== support[i]) { + typeList.push(support[i]); + } + } + + audio = new cc.Audio(realUrl); + cc.loader.cache[url] = audio; + this.loadAudioFromExtList(realUrl, typeList, audio, cb); + return audio; + }, + + loadAudioFromExtList: function (realUrl, typeList, audio, cb) { + if (typeList.length === 0) { + var ERRSTR = "can not found the resource of audio! Last match url is : "; + ERRSTR += realUrl.replace(/\.(.*)?$/, "("); + support.forEach(function (ext) { + ERRSTR += ext + "|"; + }); + ERRSTR = ERRSTR.replace(/\|$/, ")"); + return cb({status: 520, errorMessage: ERRSTR}, null); + } + + if (SWA && this.useWebAudio) { + this.loadBuffer(realUrl, function (error, buffer) { + if (error) + cc.log(error); + + if (buffer) + audio.setBuffer(buffer); + + cb(null, audio); + }); + return; + } + + var num = polyfill.ONE_SOURCE ? 1 : typeList.length; + + // 加载统一使用dom + var dom = document.createElement('audio'); + for (var i = 0; i < num; i++) { + var source = document.createElement('source'); + source.src = cc.path.changeExtname(realUrl, typeList[i]); + dom.appendChild(source); + } + + audio.setElement(dom); + + var timer = setTimeout(function () { + if (dom.readyState === 0) { + failure(); + } else { + success(); + } + }, 8000); + + var success = function () { + dom.removeEventListener("canplaythrough", success, false); + dom.removeEventListener("error", failure, false); + dom.removeEventListener("emptied", success, false); + if (polyfill.USE_LOADER_EVENT) + dom.removeEventListener(polyfill.USE_LOADER_EVENT, success, false); + clearTimeout(timer); + cb(null, audio); + }; + var failure = function () { + cc.log('load audio failure - ' + realUrl); + success(); + }; + dom.addEventListener("canplaythrough", success, false); + dom.addEventListener("error", failure, false); + if (polyfill.USE_LOADER_EVENT) + dom.addEventListener(polyfill.USE_LOADER_EVENT, success, false); + } + }; + cc.loader.register(["mp3", "ogg", "wav", "mp4", "m4a"], loader); + + /** + * cc.audioEngine is the singleton object, it provide simple audio APIs. + * @namespace + */ + cc.audioEngine = { + _currMusic: null, + _musicVolume: 1, + + features: polyfill, + + /** + * Indicates whether any background music can be played or not. + * @returns {boolean} true if the background music is playing, otherwise false + */ + willPlayMusic: function () { + return false; + }, + + /** + * Play music. + * @param {String} url The path of the music file without filename extension. + * @param {Boolean} loop Whether the music loop or not. + * @example + * //example + * cc.audioEngine.playMusic(path, false); + */ + playMusic: function(url, loop){ + var bgMusic = this._currMusic; + if (bgMusic && bgMusic.getPlaying()) { + bgMusic.stop(); + } + var musicVolume = this._musicVolume; + var audio = cc.loader.getRes(url); + if (!audio) { + cc.loader.load(url, function () { + if (!audio.getPlaying() && !audio.interruptPlay) { + audio.setVolume(musicVolume); + audio.play(0, loop || false); + } + }); + audio = cc.loader.getRes(url); + } + audio.setVolume(musicVolume); + audio.play(0, loop || false); + + this._currMusic = audio; + }, + + /** + * Stop playing music. + * @param {Boolean} [releaseData] If release the music data or not.As default value is false. + * @example + * //example + * cc.audioEngine.stopMusic(); + */ + stopMusic: function(releaseData){ + var audio = this._currMusic; + if (audio) { + var list = cc.Audio.touchPlayList; + for (var i=list.length-1; i>=0; --i) { + if (this[i] && this[i].audio === audio._element) + list.splice(i, 1); + } + + audio.stop(); + this._currMusic = null; + if (releaseData) + cc.loader.release(audio.src); + } + }, + + /** + * Pause playing music. + * @example + * //example + * cc.audioEngine.pauseMusic(); + */ + pauseMusic: function () { + var audio = this._currMusic; + if (audio) + audio.pause(); + }, + + /** + * Resume playing music. + * @example + * //example + * cc.audioEngine.resumeMusic(); + */ + resumeMusic: function () { + var audio = this._currMusic; + if (audio) + audio.resume(); + }, + + /** + * Rewind playing music. + * @example + * //example + * cc.audioEngine.rewindMusic(); + */ + rewindMusic: function () { + var audio = this._currMusic; + if (audio) { + audio.stop(); + audio.play(); + } + }, + + /** + * The volume of the music max value is 1.0,the min value is 0.0 . + * @return {Number} + * @example + * //example + * var volume = cc.audioEngine.getMusicVolume(); + */ + getMusicVolume: function () { + return this._musicVolume; + }, + + /** + * Set the volume of music. + * @param {Number} volume Volume must be in 0.0~1.0 . + * @example + * //example + * cc.audioEngine.setMusicVolume(0.5); + */ + setMusicVolume: function (volume) { + volume = volume - 0; + if (isNaN(volume)) volume = 1; + if (volume > 1) volume = 1; + if (volume < 0) volume = 0; + + this._musicVolume = volume; + var audio = this._currMusic; + if (audio) { + audio.setVolume(volume); + } + }, + + /** + * Whether the music is playing. + * @return {Boolean} If is playing return true,or return false. + * @example + * //example + * if (cc.audioEngine.isMusicPlaying()) { + * cc.log("music is playing"); + * } + * else { + * cc.log("music is not playing"); + * } + */ + isMusicPlaying: function () { + var audio = this._currMusic; + if (audio) { + return audio.getPlaying(); + } else { + return false; + } + }, + + _audioPool: {}, + _maxAudioInstance: 10, + _effectVolume: 1, + /** + * Play sound effect. + * @param {String} url The path of the sound effect with filename extension. + * @param {Boolean} loop Whether to loop the effect playing, default value is false + * @return {Number|null} the audio id + * @example + * //example + * var soundId = cc.audioEngine.playEffect(path); + */ + playEffect: function (url, loop) { + + if (SWB && this._currMusic && this._currMusic.getPlaying()) { + cc.log('Browser is only allowed to play one audio'); + return null; + } + + var effectList = this._audioPool[url]; + if (!effectList) { + effectList = this._audioPool[url] = []; + } + + for (var i = 0; i < effectList.length; i++) { + if (!effectList[i].getPlaying()) { + break; + } + } + + if (!SWA && i > this._maxAudioInstance) { + var first = effectList.shift(); + first.stop(); + effectList.push(first); + i = effectList.length - 1; + // cc.log("Error: %s greater than %d", url, this._maxAudioInstance); + } + + var audio; + if (effectList[i]) { + audio = effectList[i]; + audio.setVolume(this._effectVolume); + audio.play(0, loop || false); + return audio; + } + + audio = cc.loader.getRes(url); + + if (audio && SWA && audio._AUDIO_TYPE === 'AUDIO') { + cc.loader.release(url); + audio = null; + } + + if (audio) { + + if (SWA && audio._AUDIO_TYPE === 'AUDIO') { + loader.loadBuffer(url, function (error, buffer) { + audio.setBuffer(buffer); + audio.setVolume(cc.audioEngine._effectVolume); + if (!audio.getPlaying()) + audio.play(0, loop || false); + }); + } else { + audio = audio.cloneNode(); + audio.setVolume(this._effectVolume); + audio.play(0, loop || false); + effectList.push(audio); + return audio; + } + + } + + var cache = loader.useWebAudio; + loader.useWebAudio = true; + cc.loader.load(url, function (audio) { + audio = cc.loader.getRes(url); + audio = audio.cloneNode(); + audio.setVolume(cc.audioEngine._effectVolume); + audio.play(0, loop || false); + effectList.push(audio); + }); + loader.useWebAudio = cache; + + return audio; + }, + + /** + * Set the volume of sound effects. + * @param {Number} volume Volume must be in 0.0~1.0 . + * @example + * //example + * cc.audioEngine.setEffectsVolume(0.5); + */ + setEffectsVolume: function (volume) { + volume = volume - 0; + if (isNaN(volume)) volume = 1; + if (volume > 1) volume = 1; + if (volume < 0) volume = 0; + + this._effectVolume = volume; + var audioPool = this._audioPool; + for (var p in audioPool) { + var audioList = audioPool[p]; + if (Array.isArray(audioList)) + for (var i = 0; i < audioList.length; i++) { + audioList[i].setVolume(volume); + } + } + }, + + /** + * The volume of the effects max value is 1.0,the min value is 0.0 . + * @return {Number} + * @example + * //example + * var effectVolume = cc.audioEngine.getEffectsVolume(); + */ + getEffectsVolume: function () { + return this._effectVolume; + }, + + /** + * Pause playing sound effect. + * @param {Number} audio The return value of function playEffect. + * @example + * //example + * cc.audioEngine.pauseEffect(audioID); + */ + pauseEffect: function (audio) { + if (audio) { + audio.pause(); + } + }, + + /** + * Pause all playing sound effect. + * @example + * //example + * cc.audioEngine.pauseAllEffects(); + */ + pauseAllEffects: function () { + var ap = this._audioPool; + for (var p in ap) { + var list = ap[p]; + for (var i = 0; i < ap[p].length; i++) { + if (list[i].getPlaying()) { + list[i].pause(); + } + } + } + }, + + /** + * Resume playing sound effect. + * @param {Number} audio The return value of function playEffect. + * @audioID + * //example + * cc.audioEngine.resumeEffect(audioID); + */ + resumeEffect: function (audio) { + if (audio) + audio.resume(); + }, + + /** + * Resume all playing sound effect + * @example + * //example + * cc.audioEngine.resumeAllEffects(); + */ + resumeAllEffects: function () { + var ap = this._audioPool; + for (var p in ap) { + var list = ap[p]; + for (var i = 0; i < ap[p].length; i++) { + list[i].resume(); + } + } + }, + + /** + * Stop playing sound effect. + * @param {Number} audio The return value of function playEffect. + * @example + * //example + * cc.audioEngine.stopEffect(audioID); + */ + stopEffect: function (audio) { + if (audio) { + audio.stop(); + } + }, + + /** + * Stop all playing sound effects. + * @example + * //example + * cc.audioEngine.stopAllEffects(); + */ + stopAllEffects: function () { + var ap = this._audioPool; + for (var p in ap) { + var list = ap[p]; + for (var i = 0; i < list.length; i++) { + list[i].stop(); + } + list.length = 0; + } + ap.length = 0; + }, + + /** + * Unload the preloaded effect from internal buffer + * @param {String} url + * @example + * //example + * cc.audioEngine.unloadEffect(EFFECT_FILE); + */ + unloadEffect: function (url) { + if (!url) { + return; + } + + cc.loader.release(url); + var pool = this._audioPool[url]; + if (pool) { + for (var i = 0; i < pool.length; i++) { + pool[i].stop(); + } + pool.length = 0; + } + delete this._audioPool[url]; + }, + + /** + * End music and effects. + */ + end: function () { + this.stopMusic(); + this.stopAllEffects(); + }, + + _pauseCache: [], + _pausePlaying: function () { + var bgMusic = this._currMusic; + if (bgMusic && bgMusic.getPlaying()) { + bgMusic.pause(); + this._pauseCache.push(bgMusic); + } + var ap = this._audioPool; + for (var p in ap) { + var list = ap[p]; + for (var i = 0; i < ap[p].length; i++) { + if (list[i].getPlaying()) { + list[i].pause(); + this._pauseCache.push(list[i]); + } + } + } + }, + + _resumePlaying: function () { + var list = this._pauseCache; + for (var i = 0; i < list.length; i++) { + list[i].resume(); + } + list.length = 0; + } + }; + +})(window.__audioSupport); diff --git a/frameworks/cocos2d-html5/cocos2d/clipping-nodes/CCClippingNode.js b/frameworks/cocos2d-html5/cocos2d/clipping-nodes/CCClippingNode.js new file mode 100644 index 0000000..7214ea1 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/clipping-nodes/CCClippingNode.js @@ -0,0 +1,247 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2012 Pierre-David Bélanger + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * the value of stencil bits. + * @type Number + */ +cc.stencilBits = -1; + +/** + *

+ * cc.ClippingNode is a subclass of cc.Node.
+ * It draws its content (children) clipped using a stencil.
+ * The stencil is an other cc.Node that will not be drawn.
+ * The clipping is done using the alpha part of the stencil (adjusted with an alphaThreshold). + *

+ * @class + * @extends cc.Node + * @param {cc.Node} [stencil=null] + * + * @property {Number} alphaThreshold - Threshold for alpha value. + * @property {Boolean} inverted - Indicate whether in inverted mode. + * @property {cc.Node} stencil - he cc.Node to use as a stencil to do the clipping. + */ +cc.ClippingNode = cc.Node.extend(/** @lends cc.ClippingNode# */{ + inverted: false, + _alphaThreshold: 0, + + _stencil: null, + _className: "ClippingNode", + + _originStencilProgram: null, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {cc.Node} [stencil=null] + */ + ctor: function (stencil) { + stencil = stencil || null; + cc.Node.prototype.ctor.call(this); + this._stencil = stencil; + if (stencil) { + this._originStencilProgram = stencil.getShaderProgram(); + } + this.alphaThreshold = 1; + this.inverted = false; + this._renderCmd.initStencilBits(); + }, + + /** + *

+ * Event callback that is invoked every time when node enters the 'stage'.
+ * If the CCNode enters the 'stage' with a transition, this event is called when the transition starts.
+ * During onEnter you can't access a "sister/brother" node.
+ * If you override onEnter, you must call its parent's onEnter function with this._super(). + *

+ * @function + */ + onEnter: function () { + cc.Node.prototype.onEnter.call(this); + if (this._stencil) + this._stencil._performRecursive(cc.Node._stateCallbackType.onEnter); + }, + + /** + *

+ * Event callback that is invoked when the node enters in the 'stage'.
+ * If the node enters the 'stage' with a transition, this event is called when the transition finishes.
+ * If you override onEnterTransitionDidFinish, you shall call its parent's onEnterTransitionDidFinish with this._super() + *

+ * @function + */ + onEnterTransitionDidFinish: function () { + cc.Node.prototype.onEnterTransitionDidFinish.call(this); + if (this._stencil) + this._stencil._performRecursive(cc.Node._stateCallbackType.onEnterTransitionDidFinish); + }, + + /** + *

+ * callback that is called every time the node leaves the 'stage'.
+ * If the node leaves the 'stage' with a transition, this callback is called when the transition starts.
+ * If you override onExitTransitionDidStart, you shall call its parent's onExitTransitionDidStart with this._super() + *

+ * @function + */ + onExitTransitionDidStart: function () { + this._stencil._performRecursive(cc.Node._stateCallbackType.onExitTransitionDidStart); + cc.Node.prototype.onExitTransitionDidStart.call(this); + }, + + /** + *

+ * callback that is called every time the node leaves the 'stage'.
+ * If the node leaves the 'stage' with a transition, this callback is called when the transition finishes.
+ * During onExit you can't access a sibling node.
+ * If you override onExit, you shall call its parent's onExit with this._super(). + *

+ * @function + */ + onExit: function () { + this._stencil._performRecursive(cc.Node._stateCallbackType.onExit); + cc.Node.prototype.onExit.call(this); + }, + + visit: function (parent) { + this._renderCmd.clippingVisit(parent && parent._renderCmd); + }, + + _visitChildren: function () { + var renderer = cc.renderer; + if (this._reorderChildDirty) { + this.sortAllChildren(); + } + var children = this._children, child; + for (var i = 0, len = children.length; i < len; i++) { + child = children[i]; + if (child && child._visible) { + child.visit(this); + } + } + this._renderCmd._dirtyFlag = 0; + }, + + /** + *

+ * The alpha threshold.
+ * The content is drawn only where the stencil have pixel with alpha greater than the alphaThreshold.
+ * Should be a float between 0 and 1.
+ * This default to 1 (so alpha test is disabled). + *

+ * @return {Number} + */ + getAlphaThreshold: function () { + return this._alphaThreshold; + }, + + /** + * set alpha threshold. + * @param {Number} alphaThreshold + */ + setAlphaThreshold: function (alphaThreshold) { + if (alphaThreshold === 1 && alphaThreshold !== this._alphaThreshold) { + // should reset program used by _stencil + this._renderCmd.resetProgramByStencil(); + } + this._alphaThreshold = alphaThreshold; + }, + + /** + *

+ * Inverted. If this is set to YES,
+ * the stencil is inverted, so the content is drawn where the stencil is NOT drawn.
+ * This default to NO. + *

+ * @return {Boolean} + */ + isInverted: function () { + return this.inverted; + }, + + /** + * set whether or not invert of stencil + * @param {Boolean} inverted + */ + setInverted: function (inverted) { + this.inverted = inverted; + }, + + /** + * The cc.Node to use as a stencil to do the clipping.
+ * The stencil node will be retained. This default to nil. + * @return {cc.Node} + */ + getStencil: function () { + return this._stencil; + }, + + /** + * Set stencil. + * @function + * @param {cc.Node} stencil + */ + setStencil: function (stencil) { + if (this._stencil === stencil) + return; + if (stencil) + this._originStencilProgram = stencil.getShaderProgram(); + this._renderCmd.setStencil(stencil); + }, + + _createRenderCmd: function () { + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return new cc.ClippingNode.CanvasRenderCmd(this); + else + return new cc.ClippingNode.WebGLRenderCmd(this); + } +}); + +var _p = cc.ClippingNode.prototype; + +// Extended properties +/** @expose */ +_p.stencil; +cc.defineGetterSetter(_p, "stencil", _p.getStencil, _p.setStencil); +/** @expose */ +_p.alphaThreshold; +cc.defineGetterSetter(_p, "alphaThreshold", _p.getAlphaThreshold, _p.setAlphaThreshold); + + +/** + * Creates and initializes a clipping node with an other node as its stencil.
+ * The stencil node will be retained. + * @deprecated since v3.0, please use "new cc.ClippingNode(stencil)" instead + * @param {cc.Node} [stencil=null] + * @return {cc.ClippingNode} + * @example + * //example + * new cc.ClippingNode(stencil); + */ +cc.ClippingNode.create = function (stencil) { + return new cc.ClippingNode(stencil); +}; diff --git a/frameworks/cocos2d-html5/cocos2d/clipping-nodes/CCClippingNodeCanvasRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/clipping-nodes/CCClippingNodeCanvasRenderCmd.js new file mode 100644 index 0000000..de49899 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/clipping-nodes/CCClippingNodeCanvasRenderCmd.js @@ -0,0 +1,223 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +//-------------------------- ClippingNode's canvas render cmd -------------------------------- +(function () { + cc.ClippingNode.CanvasRenderCmd = function (renderable) { + this._rootCtor(renderable); + this._needDraw = false; + + this._godhelpme = false; + this._clipElemType = false; + + this._rendererSaveCmd = new cc.CustomRenderCmd(this, this._saveCmdCallback); + this._rendererClipCmd = new cc.CustomRenderCmd(this, this._clipCmdCallback); + this._rendererRestoreCmd = new cc.CustomRenderCmd(this, this._restoreCmdCallback); + }; + var proto = cc.ClippingNode.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + proto.constructor = cc.ClippingNode.CanvasRenderCmd; + + proto.resetProgramByStencil = function () { + + }; + + proto.initStencilBits = function () { + }; + + proto.setStencil = function (stencil) { + if (stencil == null) + return; + + this._node._stencil = stencil; + + // For shape stencil, rewrite the draw of stencil ,only init the clip path and draw nothing. + //else + if (stencil instanceof cc.DrawNode) { + if (stencil._buffer) { + for (var i = 0; i < stencil._buffer.length; i++) { + stencil._buffer[i].isFill = false; + stencil._buffer[i].isStroke = false; + } + } + + stencil._renderCmd.rendering = function (ctx, scaleX, scaleY) { + //make it do nothing and draw it in clipp render command + return; + }; + + stencil._renderCmd._canUseDirtyRegion = true; + this._rendererSaveCmd._canUseDirtyRegion = true; + this._rendererClipCmd._canUseDirtyRegion = true; + this._rendererRestoreCmd._canUseDirtyRegion = true; + + } else { + stencil._parent = this._node; + } + }; + + proto._saveCmdCallback = function (ctx, scaleX, scaleY) { + var wrapper = ctx || cc._renderContext, context = wrapper.getContext(); + + if (this._clipElemType) { + var locCache = cc.ClippingNode.CanvasRenderCmd._getSharedCache(); + var canvas = context.canvas; + locCache.width = canvas.width; + locCache.height = canvas.height; //note: on some browser, it can't clear the canvas, e.g. baidu + var locCacheCtx = locCache.getContext("2d"); + locCacheCtx.drawImage(canvas, 0, 0); //save the result to shareCache canvas + } else { + wrapper.save(); + //Because drawNode's content size is zero + wrapper.setTransform(this._worldTransform, scaleX, scaleY); + + if (this._node.inverted) { + context.beginPath(); //save for clip + context.rect(0, 0, context.canvas.width, -context.canvas.height); + context.clip(); + } + } + }; + + proto._setStencilCompositionOperation = function (stencil) { + if (!stencil) + return; + var node = this._node; + if (stencil._renderCmd && stencil._renderCmd._blendFuncStr) //it is a hack way. + stencil._renderCmd._blendFuncStr = (node.inverted ? "destination-out" : "destination-in"); + + if (!stencil._children) + return; + var children = stencil._children; + for (var i = 0, len = children.length; i < len; i++) { + this._setStencilCompositionOperation(children[i]); + } + }; + + proto._clipCmdCallback = function (ctx) { + var node = this._node; + var wrapper = ctx || cc._renderContext, context = wrapper.getContext(); + + if (this._clipElemType) { + //hack + this._setStencilCompositionOperation(node._stencil); + } else { + var stencil = this._node._stencil; + if (stencil instanceof cc.DrawNode) { + context.beginPath(); + var t = stencil._renderCmd._transform; + context.transform(t.a, t.b, t.c, t.d, t.tx, -t.ty); + for (var i = 0; i < stencil._buffer.length; i++) { + var vertices = stencil._buffer[i].verts; + //TODO: need support circle etc + //cc.assert(cc.vertexListIsClockwise(vertices), + // "Only clockwise polygons should be used as stencil"); + + var firstPoint = vertices[0]; + context.moveTo(firstPoint.x, -firstPoint.y); + for (var j = vertices.length - 1; j > 0; j--) + context.lineTo(vertices[j].x, -vertices[j].y); + } + } + context.clip(); + } + }; + + proto._restoreCmdCallback = function (ctx) { + var locCache = cc.ClippingNode.CanvasRenderCmd._getSharedCache(); + var wrapper = ctx || cc._renderContext, context = wrapper.getContext(); + if (this._clipElemType) { + // Redraw the cached canvas, so that the clipped area shows the background etc. + context.save(); + context.setTransform(1, 0, 0, 1, 0, 0); + context.globalCompositeOperation = "destination-over"; + context.drawImage(locCache, 0, 0); + context.restore(); + this._dirtyFlag = 0; + } else { + wrapper.restore(); //use for restore clip operation + } + }; + + proto.transform = function (parentCmd, recursive) { + this.originTransform(parentCmd, recursive); + var node = this._node; + if (node._stencil && node._stencil._renderCmd) { + node._stencil._renderCmd.transform(this, true); + node._stencil._dirtyFlag &= ~cc.Node._dirtyFlags.transformDirty; + } + }; + + proto._cangodhelpme = function (godhelpme) { + if (godhelpme === true || godhelpme === false) + cc.ClippingNode.CanvasRenderCmd.prototype._godhelpme = godhelpme; + return cc.ClippingNode.CanvasRenderCmd.prototype._godhelpme; + }; + + proto.clippingVisit = function (parentCmd) { + var node = this._node; + parentCmd = parentCmd || this.getParentRenderCmd(); + this.visit(parentCmd); + + // Composition mode, costy but support texture stencil + this._clipElemType = !(!this._cangodhelpme() && node._stencil instanceof cc.DrawNode); + if (!node._stencil || !node._stencil.visible) { + if (this.inverted) + node._visitChildren(); // draw everything + return; + } + + cc.renderer.pushRenderCommand(this._rendererSaveCmd); + if (this._clipElemType) { + // Draw everything first using node visit function + node._visitChildren(); + } else { + node._stencil.visit(node); + } + cc.renderer.pushRenderCommand(this._rendererClipCmd); + + if (this._clipElemType) { + node._stencil.visit(node); + } else { + // Clip mode doesn't support recursive stencil, so once we used a clip stencil, + // so if it has ClippingNode as a child, the child must uses composition stencil. + this._cangodhelpme(true); + var children = node._children; + var i, len = children.length; + if (len > 0) { + node.sortAllChildren(); + for (i = 0; i < len; i++) + children[i].visit(node); + } + this._cangodhelpme(false); + } + + cc.renderer.pushRenderCommand(this._rendererRestoreCmd); + this._dirtyFlag = 0; + }; + + cc.ClippingNode.CanvasRenderCmd._sharedCache = null; + cc.ClippingNode.CanvasRenderCmd._getSharedCache = function () { + return (cc.ClippingNode.CanvasRenderCmd._sharedCache) || (cc.ClippingNode.CanvasRenderCmd._sharedCache = document.createElement("canvas")); + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/clipping-nodes/CCClippingNodeWebGLRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/clipping-nodes/CCClippingNodeWebGLRenderCmd.js new file mode 100644 index 0000000..6d8956a --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/clipping-nodes/CCClippingNodeWebGLRenderCmd.js @@ -0,0 +1,207 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +function setProgram (node, program) { + node.shaderProgram = program; + + var children = node.children; + if (!children) + return; + + for (var i = 0; i < children.length; i++) + setProgram(children[i], program); +} + +// ------------------------------- ClippingNode's WebGL render cmd ------------------------------ +(function () { + cc.ClippingNode.WebGLRenderCmd = function (renderable) { + this._rootCtor(renderable); + this._needDraw = false; + + this._beforeVisitCmd = new cc.CustomRenderCmd(this, this._onBeforeVisit); + this._afterDrawStencilCmd = new cc.CustomRenderCmd(this, this._onAfterDrawStencil); + this._afterVisitCmd = new cc.CustomRenderCmd(this, this._onAfterVisit); + + this._currentStencilEnabled = null; + this._mask_layer_le = null; + }; + + var proto = cc.ClippingNode.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + proto.constructor = cc.ClippingNode.WebGLRenderCmd; + + cc.ClippingNode.WebGLRenderCmd._init_once = null; + cc.ClippingNode.WebGLRenderCmd._visit_once = null; + cc.ClippingNode.WebGLRenderCmd._layer = -1; + + proto.initStencilBits = function () { + // get (only once) the number of bits of the stencil buffer + cc.ClippingNode.WebGLRenderCmd._init_once = true; + if (cc.ClippingNode.WebGLRenderCmd._init_once) { + cc.stencilBits = cc._renderContext.getParameter(cc._renderContext.STENCIL_BITS); + if (cc.stencilBits <= 0) + cc.log("Stencil buffer is not enabled."); + cc.ClippingNode.WebGLRenderCmd._init_once = false; + } + }; + + proto.transform = function (parentCmd, recursive) { + var node = this._node; + this.originTransform(parentCmd, recursive); + if (node._stencil) { + node._stencil._renderCmd.transform(this, true); + node._stencil._dirtyFlag &= ~cc.Node._dirtyFlags.transformDirty; + } + }; + + proto.clippingVisit = function (parentCmd) { + var node = this._node; + parentCmd = parentCmd || this.getParentRenderCmd(); + this.visit(parentCmd); + + // if stencil buffer disabled + if (cc.stencilBits < 1) { + // draw everything, as if there were no stencil + node._visitChildren(); + return; + } + + if (!node._stencil || !node._stencil.visible) { + if (node.inverted) + node._visitChildren(); // draw everything + return; + } + + if (cc.ClippingNode.WebGLRenderCmd._layer + 1 === cc.stencilBits) { + cc.ClippingNode.WebGLRenderCmd._visit_once = true; + if (cc.ClippingNode.WebGLRenderCmd._visit_once) { + cc.log("Nesting more than " + cc.stencilBits + "stencils is not supported. Everything will be drawn without stencil for this node and its children."); + cc.ClippingNode.WebGLRenderCmd._visit_once = false; + } + // draw everything, as if there were no stencil + node._visitChildren(); + return; + } + + cc.renderer.pushRenderCommand(this._beforeVisitCmd); + + // node._stencil._stackMatrix = node._stackMatrix; + node._stencil.visit(node); + + cc.renderer.pushRenderCommand(this._afterDrawStencilCmd); + + // draw (according to the stencil test func) this node and its children + var locChildren = node._children; + if (locChildren && locChildren.length > 0) { + var childLen = locChildren.length; + node.sortAllChildren(); + // draw children zOrder < 0 + for (var i = 0; i < childLen; i++) { + locChildren[i].visit(node); + } + } + + cc.renderer.pushRenderCommand(this._afterVisitCmd); + + this._dirtyFlag = 0; + }; + + proto.setStencil = function (stencil) { + var node = this._node; + if (node._stencil) + node._stencil._parent = null; + node._stencil = stencil; + if (node._stencil) + node._stencil._parent = node; + }; + + proto.resetProgramByStencil = function () { + var node = this._node; + if (node._stencil) { + var program = node._originStencilProgram; + setProgram(node._stencil, program); + } + }; + + proto._onBeforeVisit = function (ctx) { + var gl = ctx || cc._renderContext, node = this._node; + cc.ClippingNode.WebGLRenderCmd._layer++; + + // mask of the current layer (ie: for layer 3: 00000100) + var mask_layer = 0x1 << cc.ClippingNode.WebGLRenderCmd._layer; + // mask of all layers less than the current (ie: for layer 3: 00000011) + var mask_layer_l = mask_layer - 1; + // mask of all layers less than or equal to the current (ie: for layer 3: 00000111) + //var mask_layer_le = mask_layer | mask_layer_l; + this._mask_layer_le = mask_layer | mask_layer_l; + // manually save the stencil state + this._currentStencilEnabled = gl.isEnabled(gl.STENCIL_TEST); + + gl.clear(gl.DEPTH_BUFFER_BIT); + // enable stencil use + gl.enable(gl.STENCIL_TEST); + + gl.depthMask(false); + + gl.stencilFunc(gl.NEVER, mask_layer, mask_layer); + gl.stencilOp(gl.REPLACE, gl.KEEP, gl.KEEP); + + gl.stencilMask(mask_layer); + gl.clear(gl.STENCIL_BUFFER_BIT); + + if (node.alphaThreshold < 1) { //TODO desktop + var program = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLORALPHATEST); + // set our alphaThreshold + cc.glUseProgram(program.getProgram()); + program.setUniformLocationWith1f(cc.UNIFORM_ALPHA_TEST_VALUE_S, node.alphaThreshold); + program.setUniformLocationWithMatrix4fv(cc.UNIFORM_MVMATRIX_S, cc.renderer.mat4Identity.mat); + cc.setProgram(node._stencil, program); + } + }; + + proto._onAfterDrawStencil = function (ctx) { + var gl = ctx || cc._renderContext; + gl.depthMask(true); + gl.stencilFunc(!this._node.inverted ? gl.EQUAL : gl.NOTEQUAL, this._mask_layer_le, this._mask_layer_le); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); + }; + + proto._onAfterVisit = function (ctx) { + var gl = ctx || cc._renderContext; + + cc.ClippingNode.WebGLRenderCmd._layer--; + + if (this._currentStencilEnabled) { + var mask_layer = 0x1 << cc.ClippingNode.WebGLRenderCmd._layer; + var mask_layer_l = mask_layer - 1; + var mask_layer_le = mask_layer | mask_layer_l; + + gl.stencilMask(mask_layer); + gl.stencilFunc(gl.EQUAL, mask_layer_le, mask_layer_le); + } + else { + gl.disable(gl.STENCIL_TEST); + + } + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/compression/ZipUtils.js b/frameworks/cocos2d-html5/cocos2d/compression/ZipUtils.js new file mode 100644 index 0000000..ccecd63 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/compression/ZipUtils.js @@ -0,0 +1,82 @@ +/*-- + Copyright 2009-2010 by Stefan Rusterholz. + All rights reserved. + You can choose between MIT and BSD-3-Clause license. License file will be added later. + --*/ + +/** + * mixin cc.Codec + */ +cc.Codec = {name:'Jacob__Codec'}; + +/** + * Unpack a gzipped byte array + * @param {Array} input Byte array + * @returns {String} Unpacked byte string + */ +cc.unzip = function () { + return cc.Codec.GZip.gunzip.apply(cc.Codec.GZip, arguments); +}; + +/** + * Unpack a gzipped byte string encoded as base64 + * @param {String} input Byte string encoded as base64 + * @returns {String} Unpacked byte string + */ +cc.unzipBase64 = function () { + var tmpInput = cc.Codec.Base64.decode.apply(cc.Codec.Base64, arguments); + return cc.Codec.GZip.gunzip.apply(cc.Codec.GZip, [tmpInput]); +}; + +/** + * Unpack a gzipped byte string encoded as base64 + * @param {String} input Byte string encoded as base64 + * @param {Number} bytes Bytes per array item + * @returns {Array} Unpacked byte array + */ +cc.unzipBase64AsArray = function (input, bytes) { + bytes = bytes || 1; + + var dec = this.unzipBase64(input), + ar = [], i, j, len; + for (i = 0, len = dec.length / bytes; i < len; i++) { + ar[i] = 0; + for (j = bytes - 1; j >= 0; --j) { + ar[i] += dec.charCodeAt((i * bytes) + j) << (j * 8); + } + } + return ar; +}; + +/** + * Unpack a gzipped byte array + * @param {Array} input Byte array + * @param {Number} bytes Bytes per array item + * @returns {Array} Unpacked byte array + */ +cc.unzipAsArray = function (input, bytes) { + bytes = bytes || 1; + + var dec = this.unzip(input), + ar = [], i, j, len; + for (i = 0, len = dec.length / bytes; i < len; i++) { + ar[i] = 0; + for (j = bytes - 1; j >= 0; --j) { + ar[i] += dec.charCodeAt((i * bytes) + j) << (j * 8); + } + } + return ar; +}; + +/** + * string to array + * @param {String} input + * @returns {Array} array + */ +cc.StringToArray = function (input) { + var tmp = input.split(","), ar = [], i; + for (i = 0; i < tmp.length; i++) { + ar.push(parseInt(tmp[i])); + } + return ar; +}; diff --git a/frameworks/cocos2d-html5/cocos2d/compression/base64.js b/frameworks/cocos2d-html5/cocos2d/compression/base64.js new file mode 100644 index 0000000..adb0d68 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/compression/base64.js @@ -0,0 +1,95 @@ +/*-- + Copyright 2009-2010 by Stefan Rusterholz. + All rights reserved. + You can choose between MIT and BSD-3-Clause license. License file will be added later. + --*/ + +/** + * mixin cc.Codec.Base64 + */ +cc.Codec.Base64 = {name:'Jacob__Codec__Base64'}; + +cc.Codec.Base64._keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + +/** + *

+ * cc.Codec.Base64.decode(input[, unicode=false]) -> String (http://en.wikipedia.org/wiki/Base64). + *

+ * @function + * @param {String} input The base64 encoded string to decode + * @return {String} Decodes a base64 encoded String + * @example + * //decode string + * cc.Codec.Base64.decode("U29tZSBTdHJpbmc="); // => "Some String" + */ +cc.Codec.Base64.decode = function Jacob__Codec__Base64__decode(input) { + var output = [], + chr1, chr2, chr3, + enc1, enc2, enc3, enc4, + i = 0; + + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + + while (i < input.length) { + enc1 = this._keyStr.indexOf(input.charAt(i++)); + enc2 = this._keyStr.indexOf(input.charAt(i++)); + enc3 = this._keyStr.indexOf(input.charAt(i++)); + enc4 = this._keyStr.indexOf(input.charAt(i++)); + + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + + output.push(String.fromCharCode(chr1)); + + if (enc3 !== 64) { + output.push(String.fromCharCode(chr2)); + } + if (enc4 !== 64) { + output.push(String.fromCharCode(chr3)); + } + } + + output = output.join(''); + + return output; +}; + +/** + *

+ * Converts an input string encoded in base64 to an array of integers whose
+ * values represent the decoded string's characters' bytes. + *

+ * @function + * @param {String} input The String to convert to an array of Integers + * @param {Number} bytes + * @return {Array} + * @example + * //decode string to array + * var decodeArr = cc.Codec.Base64.decodeAsArray("U29tZSBTdHJpbmc="); + */ +cc.Codec.Base64.decodeAsArray = function Jacob__Codec__Base64___decodeAsArray(input, bytes) { + var dec = this.decode(input), + ar = [], i, j, len; + for (i = 0, len = dec.length / bytes; i < len; i++) { + ar[i] = 0; + for (j = bytes - 1; j >= 0; --j) { + ar[i] += dec.charCodeAt((i * bytes) + j) << (j * 8); + } + } + + return ar; +}; + +cc.uint8ArrayToUint32Array = function(uint8Arr){ + if(uint8Arr.length % 4 !== 0) + return null; + + var arrLen = uint8Arr.length /4; + var retArr = window.Uint32Array? new Uint32Array(arrLen) : []; + for(var i = 0; i < arrLen; i++){ + var offset = i * 4; + retArr[i] = uint8Arr[offset] + uint8Arr[offset + 1] * (1 << 8) + uint8Arr[offset + 2] * (1 << 16) + uint8Arr[offset + 3] * (1<<24); + } + return retArr; +}; diff --git a/frameworks/cocos2d-html5/cocos2d/compression/gzip.js b/frameworks/cocos2d-html5/cocos2d/compression/gzip.js new file mode 100644 index 0000000..baed633 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/compression/gzip.js @@ -0,0 +1,731 @@ +/*-- + Copyright 2009-2010 by Stefan Rusterholz. + All rights reserved. + You can choose between MIT and BSD-3-Clause license. License file will be added later. + --*/ + +/** + * See cc.Codec.GZip.gunzip. + * @param {Array | String} data The bytestream to decompress + * Constructor + */ +cc.Codec.GZip = function Jacob__GZip(data) { + this.data = data; + + this.debug = false; + this.gpflags = undefined; + this.files = 0; + this.unzipped = []; + this.buf32k = new Array(32768); + this.bIdx = 0; + this.modeZIP = false; + this.bytepos = 0; + this.bb = 1; + this.bits = 0; + this.nameBuf = []; + this.fileout = undefined; + this.literalTree = new Array(cc.Codec.GZip.LITERALS); + this.distanceTree = new Array(32); + this.treepos = 0; + this.Places = null; + this.len = 0; + this.fpos = new Array(17); + this.fpos[0] = 0; + this.flens = undefined; + this.fmax = undefined; +}; + +/** + * Unzips the gzipped data of the 'data' argument. + * @param string The bytestream to decompress. Either an array of Integers between 0 and 255, or a String. + * @return {String} + */ +cc.Codec.GZip.gunzip = function (string) { + if (string.constructor === Array) { + } else if (string.constructor === String) { + } + var gzip = new cc.Codec.GZip(string); + return gzip.gunzip()[0][0]; +}; + +cc.Codec.GZip.HufNode = function () { + this.b0 = 0; + this.b1 = 0; + this.jump = null; + this.jumppos = -1; +}; + +/** + * @constant + * @type Number + */ +cc.Codec.GZip.LITERALS = 288; +/** + * @constant + * @type Number + */ +cc.Codec.GZip.NAMEMAX = 256; + +cc.Codec.GZip.bitReverse = [ + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff +]; +cc.Codec.GZip.cplens = [ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 +]; +cc.Codec.GZip.cplext = [ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99 +]; +/* 99==invalid */ +cc.Codec.GZip.cpdist = [ + 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0007, 0x0009, 0x000d, + 0x0011, 0x0019, 0x0021, 0x0031, 0x0041, 0x0061, 0x0081, 0x00c1, + 0x0101, 0x0181, 0x0201, 0x0301, 0x0401, 0x0601, 0x0801, 0x0c01, + 0x1001, 0x1801, 0x2001, 0x3001, 0x4001, 0x6001 +]; +cc.Codec.GZip.cpdext = [ + 0, 0, 0, 0, 1, 1, 2, 2, + 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, + 11, 11, 12, 12, 13, 13 +]; +cc.Codec.GZip.border = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; + + +/** + * gunzip + * @return {Array} + */ +cc.Codec.GZip.prototype.gunzip = function () { + this.outputArr = []; + + //convertToByteArray(input); + //if (this.debug) alert(this.data); + + this.nextFile(); + return this.unzipped; +}; + +cc.Codec.GZip.prototype.readByte = function () { + this.bits += 8; + if (this.bytepos < this.data.length) { + //return this.data[this.bytepos++]; // Array + return this.data.charCodeAt(this.bytepos++); + } else { + return -1; + } +}; + +cc.Codec.GZip.prototype.byteAlign = function () { + this.bb = 1; +}; + +cc.Codec.GZip.prototype.readBit = function () { + var carry; + this.bits++; + carry = (this.bb & 1); + this.bb >>= 1; + if (this.bb === 0) { + this.bb = this.readByte(); + carry = (this.bb & 1); + this.bb = (this.bb >> 1) | 0x80; + } + return carry; +}; + +cc.Codec.GZip.prototype.readBits = function (a) { + var res = 0, + i = a; + + while (i--) res = (res << 1) | this.readBit(); + if (a) res = cc.Codec.GZip.bitReverse[res] >> (8 - a); + + return res; +}; + +cc.Codec.GZip.prototype.flushBuffer = function () { + this.bIdx = 0; +}; + +cc.Codec.GZip.prototype.addBuffer = function (a) { + this.buf32k[this.bIdx++] = a; + this.outputArr.push(String.fromCharCode(a)); + if (this.bIdx === 0x8000) this.bIdx = 0; +}; + +cc.Codec.GZip.prototype.IsPat = function () { + while (1) { + if (this.fpos[this.len] >= this.fmax) return -1; + if (this.flens[this.fpos[this.len]] === this.len) return this.fpos[this.len]++; + this.fpos[this.len]++; + } +}; + +cc.Codec.GZip.prototype.Rec = function () { + var curplace = this.Places[this.treepos]; + var tmp; + //if (this.debug) document.write("
len:"+this.len+" treepos:"+this.treepos); + if (this.len === 17) { //war 17 + return -1; + } + this.treepos++; + this.len++; + + tmp = this.IsPat(); + //if (this.debug) document.write("
IsPat "+tmp); + if (tmp >= 0) { + curplace.b0 = tmp; + /* leaf cell for 0-bit */ + //if (this.debug) document.write("
b0 "+curplace.b0); + } else { + /* Not a Leaf cell */ + curplace.b0 = 0x8000; + //if (this.debug) document.write("
b0 "+curplace.b0); + if (this.Rec()) return -1; + } + tmp = this.IsPat(); + if (tmp >= 0) { + curplace.b1 = tmp; + /* leaf cell for 1-bit */ + //if (this.debug) document.write("
b1 "+curplace.b1); + curplace.jump = null; + /* Just for the display routine */ + } else { + /* Not a Leaf cell */ + curplace.b1 = 0x8000; + //if (this.debug) document.write("
b1 "+curplace.b1); + curplace.jump = this.Places[this.treepos]; + curplace.jumppos = this.treepos; + if (this.Rec()) return -1; + } + this.len--; + return 0; +}; + +cc.Codec.GZip.prototype.CreateTree = function (currentTree, numval, lengths, show) { + var i; + /* Create the Huffman decode tree/table */ + //if (this.debug) document.write("currentTree "+currentTree+" numval "+numval+" lengths "+lengths+" show "+show); + this.Places = currentTree; + this.treepos = 0; + this.flens = lengths; + this.fmax = numval; + for (i = 0; i < 17; i++) this.fpos[i] = 0; + this.len = 0; + if (this.Rec()) { + //if (this.debug) alert("invalid huffman tree\n"); + return -1; + } + // if (this.debug) { + // document.write('
Tree: '+this.Places.length); + // for (var a=0;a<32;a++){ + // document.write("Places["+a+"].b0="+this.Places[a].b0+"
"); + // document.write("Places["+a+"].b1="+this.Places[a].b1+"
"); + // } + // } + + return 0; +}; + +cc.Codec.GZip.prototype.DecodeValue = function (currentTree) { + var len, i, + xtreepos = 0, + X = currentTree[xtreepos], + b; + + /* decode one symbol of the data */ + while (1) { + b = this.readBit(); + // if (this.debug) document.write("b="+b); + if (b) { + if (!(X.b1 & 0x8000)) { + // if (this.debug) document.write("ret1"); + return X.b1; + /* If leaf node, return data */ + } + X = X.jump; + len = currentTree.length; + for (i = 0; i < len; i++) { + if (currentTree[i] === X) { + xtreepos = i; + break; + } + } + } else { + if (!(X.b0 & 0x8000)) { + // if (this.debug) document.write("ret2"); + return X.b0; + /* If leaf node, return data */ + } + xtreepos++; + X = currentTree[xtreepos]; + } + } + // if (this.debug) document.write("ret3"); + + return -1; +}; + +cc.Codec.GZip.prototype.DeflateLoop = function () { + var last, c, type, i, len; + do { + last = this.readBit(); + type = this.readBits(2); + + if (type === 0) { + var blockLen, cSum; + + // Stored + this.byteAlign(); + blockLen = this.readByte(); + blockLen |= (this.readByte() << 8); + + cSum = this.readByte(); + cSum |= (this.readByte() << 8); + + if (((blockLen ^ ~cSum) & 0xffff)) { + document.write("BlockLen checksum mismatch\n"); // FIXME: use throw + } + while (blockLen--) { + c = this.readByte(); + this.addBuffer(c); + } + } else if (type === 1) { + var j; + + /* Fixed Huffman tables -- fixed decode routine */ + while (1) { + /* + 256 0000000 0 + : : : + 279 0010111 23 + 0 00110000 48 + : : : + 143 10111111 191 + 280 11000000 192 + : : : + 287 11000111 199 + 144 110010000 400 + : : : + 255 111111111 511 + + Note the bit order! + */ + j = (cc.Codec.GZip.bitReverse[this.readBits(7)] >> 1); + if (j > 23) { + j = (j << 1) | this.readBit(); + /* 48..255 */ + + if (j > 199) { /* 200..255 */ + j -= 128; + /* 72..127 */ + j = (j << 1) | this.readBit(); + /* 144..255 << */ + } else { /* 48..199 */ + j -= 48; + /* 0..151 */ + if (j > 143) { + j = j + 136; + /* 280..287 << */ + /* 0..143 << */ + } + } + } else { /* 0..23 */ + j += 256; + /* 256..279 << */ + } + if (j < 256) { + this.addBuffer(j); + } else if (j === 256) { + /* EOF */ + break; // FIXME: make this the loop-condition + } else { + var len, dist; + + j -= 256 + 1; + /* bytes + EOF */ + len = this.readBits(cc.Codec.GZip.cplext[j]) + cc.Codec.GZip.cplens[j]; + + j = cc.Codec.GZip.bitReverse[this.readBits(5)] >> 3; + if (cc.Codec.GZip.cpdext[j] > 8) { + dist = this.readBits(8); + dist |= (this.readBits(cc.Codec.GZip.cpdext[j] - 8) << 8); + } else { + dist = this.readBits(cc.Codec.GZip.cpdext[j]); + } + dist += cc.Codec.GZip.cpdist[j]; + + for (j = 0; j < len; j++) { + var c = this.buf32k[(this.bIdx - dist) & 0x7fff]; + this.addBuffer(c); + } + } + } // while + + } else if (type === 2) { + var j, n, literalCodes, distCodes, lenCodes; + var ll = new Array(288 + 32); // "static" just to preserve stack + + // Dynamic Huffman tables + + literalCodes = 257 + this.readBits(5); + distCodes = 1 + this.readBits(5); + lenCodes = 4 + this.readBits(4); + for (j = 0; j < 19; j++) { + ll[j] = 0; + } + + // Get the decode tree code lengths + + for (j = 0; j < lenCodes; j++) { + ll[cc.Codec.GZip.border[j]] = this.readBits(3); + } + len = this.distanceTree.length; + for (i = 0; i < len; i++) this.distanceTree[i] = new cc.Codec.GZip.HufNode(); + if (this.CreateTree(this.distanceTree, 19, ll, 0)) { + this.flushBuffer(); + return 1; + } + // if (this.debug) { + // document.write("
distanceTree"); + // for(var a=0;a"+this.distanceTree[a].b0+" "+this.distanceTree[a].b1+" "+this.distanceTree[a].jump+" "+this.distanceTree[a].jumppos); + // } + // } + + //read in literal and distance code lengths + n = literalCodes + distCodes; + i = 0; + var z = -1; + // if (this.debug) document.write("
n="+n+" bits: "+this.bits+"
"); + while (i < n) { + z++; + j = this.DecodeValue(this.distanceTree); + // if (this.debug) document.write("
"+z+" i:"+i+" decode: "+j+" bits "+this.bits+"
"); + if (j < 16) { // length of code in bits (0..15) + ll[i++] = j; + } else if (j === 16) { // repeat last length 3 to 6 times + var l; + j = 3 + this.readBits(2); + if (i + j > n) { + this.flushBuffer(); + return 1; + } + l = i ? ll[i - 1] : 0; + while (j--) { + ll[i++] = l; + } + } else { + if (j === 17) { // 3 to 10 zero length codes + j = 3 + this.readBits(3); + } else { // j == 18: 11 to 138 zero length codes + j = 11 + this.readBits(7); + } + if (i + j > n) { + this.flushBuffer(); + return 1; + } + while (j--) { + ll[i++] = 0; + } + } + } // while + + // Can overwrite tree decode tree as it is not used anymore + len = this.literalTree.length; + for (i = 0; i < len; i++) + this.literalTree[i] = new cc.Codec.GZip.HufNode(); + if (this.CreateTree(this.literalTree, literalCodes, ll, 0)) { + this.flushBuffer(); + return 1; + } + len = this.literalTree.length; + for (i = 0; i < len; i++) this.distanceTree[i] = new cc.Codec.GZip.HufNode(); + var ll2 = new Array(); + for (i = literalCodes; i < ll.length; i++) ll2[i - literalCodes] = ll[i]; + if (this.CreateTree(this.distanceTree, distCodes, ll2, 0)) { + this.flushBuffer(); + return 1; + } + // if (this.debug) document.write("
literalTree"); + while (1) { + j = this.DecodeValue(this.literalTree); + if (j >= 256) { // In C64: if carry set + var len, dist; + j -= 256; + if (j === 0) { + // EOF + break; + } + j--; + len = this.readBits(cc.Codec.GZip.cplext[j]) + cc.Codec.GZip.cplens[j]; + + j = this.DecodeValue(this.distanceTree); + if (cc.Codec.GZip.cpdext[j] > 8) { + dist = this.readBits(8); + dist |= (this.readBits(cc.Codec.GZip.cpdext[j] - 8) << 8); + } else { + dist = this.readBits(cc.Codec.GZip.cpdext[j]); + } + dist += cc.Codec.GZip.cpdist[j]; + while (len--) { + var c = this.buf32k[(this.bIdx - dist) & 0x7fff]; + this.addBuffer(c); + } + } else { + this.addBuffer(j); + } + } // while + } + } while (!last); + this.flushBuffer(); + + this.byteAlign(); + return 0; +}; + +cc.Codec.GZip.prototype.unzipFile = function (name) { + var i; + this.gunzip(); + for (i = 0; i < this.unzipped.length; i++) { + if (this.unzipped[i][1] === name) { + return this.unzipped[i][0]; + } + } +}; + +cc.Codec.GZip.prototype.nextFile = function () { + // if (this.debug) alert("NEXTFILE"); + + this.outputArr = []; + this.modeZIP = false; + + var tmp = []; + tmp[0] = this.readByte(); + tmp[1] = this.readByte(); + // if (this.debug) alert("type: "+tmp[0]+" "+tmp[1]); + + if (tmp[0] === 0x78 && tmp[1] === 0xda) { //GZIP + // if (this.debug) alert("GEONExT-GZIP"); + this.DeflateLoop(); + // if (this.debug) alert(this.outputArr.join('')); + this.unzipped[this.files] = [this.outputArr.join(''), "geonext.gxt"]; + this.files++; + } + if (tmp[0] === 0x1f && tmp[1] === 0x8b) { //GZIP + // if (this.debug) alert("GZIP"); + this.skipdir(); + // if (this.debug) alert(this.outputArr.join('')); + this.unzipped[this.files] = [this.outputArr.join(''), "file"]; + this.files++; + } + if (tmp[0] === 0x50 && tmp[1] === 0x4b) { //ZIP + this.modeZIP = true; + tmp[2] = this.readByte(); + tmp[3] = this.readByte(); + if (tmp[2] === 0x03 && tmp[3] === 0x04) { + //MODE_ZIP + tmp[0] = this.readByte(); + tmp[1] = this.readByte(); + // if (this.debug) alert("ZIP-Version: "+tmp[1]+" "+tmp[0]/10+"."+tmp[0]%10); + + this.gpflags = this.readByte(); + this.gpflags |= (this.readByte() << 8); + // if (this.debug) alert("gpflags: "+this.gpflags); + + var method = this.readByte(); + method |= (this.readByte() << 8); + // if (this.debug) alert("method: "+method); + + this.readByte(); + this.readByte(); + this.readByte(); + this.readByte(); + +// var crc = this.readByte(); +// crc |= (this.readByte()<<8); +// crc |= (this.readByte()<<16); +// crc |= (this.readByte()<<24); + + var compSize = this.readByte(); + compSize |= (this.readByte() << 8); + compSize |= (this.readByte() << 16); + compSize |= (this.readByte() << 24); + + var size = this.readByte(); + size |= (this.readByte() << 8); + size |= (this.readByte() << 16); + size |= (this.readByte() << 24); + + // if (this.debug) alert("local CRC: "+crc+"\nlocal Size: "+size+"\nlocal CompSize: "+compSize); + + var filelen = this.readByte(); + filelen |= (this.readByte() << 8); + + var extralen = this.readByte(); + extralen |= (this.readByte() << 8); + + // if (this.debug) alert("filelen "+filelen); + i = 0; + this.nameBuf = []; + while (filelen--) { + var c = this.readByte(); + if (c === "/" | c === ":") { + i = 0; + } else if (i < cc.Codec.GZip.NAMEMAX - 1) { + this.nameBuf[i++] = String.fromCharCode(c); + } + } + // if (this.debug) alert("nameBuf: "+this.nameBuf); + + if (!this.fileout) this.fileout = this.nameBuf; + + var i = 0; + while (i < extralen) { + c = this.readByte(); + i++; + } + + // if (size = 0 && this.fileOut.charAt(this.fileout.length-1)=="/"){ + // //skipdir + // // if (this.debug) alert("skipdir"); + // } + if (method === 8) { + this.DeflateLoop(); + // if (this.debug) alert(this.outputArr.join('')); + this.unzipped[this.files] = [this.outputArr.join(''), this.nameBuf.join('')]; + this.files++; + } + this.skipdir(); + } + } +}; + +cc.Codec.GZip.prototype.skipdir = function () { + var tmp = []; + var compSize, size, os, i, c; + + if ((this.gpflags & 8)) { + tmp[0] = this.readByte(); + tmp[1] = this.readByte(); + tmp[2] = this.readByte(); + tmp[3] = this.readByte(); + +// if (tmp[0] == 0x50 && tmp[1] == 0x4b && tmp[2] == 0x07 && tmp[3] == 0x08) { +// crc = this.readByte(); +// crc |= (this.readByte()<<8); +// crc |= (this.readByte()<<16); +// crc |= (this.readByte()<<24); +// } else { +// crc = tmp[0] | (tmp[1]<<8) | (tmp[2]<<16) | (tmp[3]<<24); +// } + + compSize = this.readByte(); + compSize |= (this.readByte() << 8); + compSize |= (this.readByte() << 16); + compSize |= (this.readByte() << 24); + + size = this.readByte(); + size |= (this.readByte() << 8); + size |= (this.readByte() << 16); + size |= (this.readByte() << 24); + } + + if (this.modeZIP) this.nextFile(); + + tmp[0] = this.readByte(); + if (tmp[0] !== 8) { + // if (this.debug) alert("Unknown compression method!"); + return 0; + } + + this.gpflags = this.readByte(); + // if (this.debug && (this.gpflags & ~(0x1f))) alert("Unknown flags set!"); + + this.readByte(); + this.readByte(); + this.readByte(); + this.readByte(); + + this.readByte(); + os = this.readByte(); + + if ((this.gpflags & 4)) { + tmp[0] = this.readByte(); + tmp[2] = this.readByte(); + this.len = tmp[0] + 256 * tmp[1]; + // if (this.debug) alert("Extra field size: "+this.len); + for (i = 0; i < this.len; i++) + this.readByte(); + } + + if ((this.gpflags & 8)) { + i = 0; + this.nameBuf = []; + while (c = this.readByte()) { + if (c === "7" || c === ":") + i = 0; + if (i < cc.Codec.GZip.NAMEMAX - 1) + this.nameBuf[i++] = c; + } + //this.nameBuf[i] = "\0"; + // if (this.debug) alert("original file name: "+this.nameBuf); + } + + if ((this.gpflags & 16)) { + while (c = this.readByte()) { // FIXME: looks like they read to the end of the stream, should be doable more efficiently + //FILE COMMENT + } + } + + if ((this.gpflags & 2)) { + this.readByte(); + this.readByte(); + } + + this.DeflateLoop(); + +// crc = this.readByte(); +// crc |= (this.readByte()<<8); +// crc |= (this.readByte()<<16); +// crc |= (this.readByte()<<24); + + size = this.readByte(); + size |= (this.readByte() << 8); + size |= (this.readByte() << 16); + size |= (this.readByte() << 24); + + if (this.modeZIP) this.nextFile(); +}; diff --git a/frameworks/cocos2d-html5/cocos2d/compression/zlib.min.js b/frameworks/cocos2d-html5/cocos2d/compression/zlib.min.js new file mode 100644 index 0000000..6839e7b --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/compression/zlib.min.js @@ -0,0 +1,55 @@ +/** @license zlib.js 2012 - imaya [ https://github.com/imaya/zlib.js ] The MIT License */ +(function() {'use strict';function i(a){throw a;}var r=void 0,v=!0,aa=this;function y(a,c){var b=a.split("."),e=aa;!(b[0]in e)&&e.execScript&&e.execScript("var "+b[0]);for(var f;b.length&&(f=b.shift());)!b.length&&c!==r?e[f]=c:e=e[f]?e[f]:e[f]={}};var H="undefined"!==typeof Uint8Array&&"undefined"!==typeof Uint16Array&&"undefined"!==typeof Uint32Array;function ba(a){if("string"===typeof a){var c=a.split(""),b,e;b=0;for(e=c.length;b>>0;a=c}for(var f=1,d=0,g=a.length,h,m=0;0>>0};function J(a,c){this.index="number"===typeof c?c:0;this.i=0;this.buffer=a instanceof(H?Uint8Array:Array)?a:new (H?Uint8Array:Array)(32768);2*this.buffer.length<=this.index&&i(Error("invalid index"));this.buffer.length<=this.index&&this.f()}J.prototype.f=function(){var a=this.buffer,c,b=a.length,e=new (H?Uint8Array:Array)(b<<1);if(H)e.set(a);else for(c=0;c>>8&255]<<16|N[a>>>16&255]<<8|N[a>>>24&255])>>32-c:N[a]>>8-c);if(8>c+d)g=g<>c-h-1&1,8===++d&&(d=0,e[f++]=N[g],g=0,f===e.length&&(e=this.f()));e[f]=g;this.buffer=e;this.i=d;this.index=f};J.prototype.finish=function(){var a=this.buffer,c=this.index,b;0ha;++ha){for(var R=ha,ia=R,ja=7,R=R>>>1;R;R>>>=1)ia<<=1,ia|=R&1,--ja;ca[ha]=(ia<>>0}var N=ca;var ka=[0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759, +2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977, +2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755, +2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956, +3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270, +936918E3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117];H&&new Uint32Array(ka);function la(a){this.buffer=new (H?Uint16Array:Array)(2*a);this.length=0}la.prototype.getParent=function(a){return 2*((a-2)/4|0)};la.prototype.push=function(a,c){var b,e,f=this.buffer,d;b=this.length;f[this.length++]=c;for(f[this.length++]=a;0f[e])d=f[b],f[b]=f[e],f[e]=d,d=f[b+1],f[b+1]=f[e+1],f[e+1]=d,b=e;else break;return this.length}; +la.prototype.pop=function(){var a,c,b=this.buffer,e,f,d;c=b[0];a=b[1];this.length-=2;b[0]=b[this.length];b[1]=b[this.length+1];for(d=0;;){f=2*d+2;if(f>=this.length)break;f+2b[f]&&(f+=2);if(b[f]>b[d])e=b[d],b[d]=b[f],b[f]=e,e=b[d+1],b[d+1]=b[f+1],b[f+1]=e;else break;d=f}return{index:a,value:c,length:this.length}};function S(a){var c=a.length,b=0,e=Number.POSITIVE_INFINITY,f,d,g,h,m,j,s,n,l;for(n=0;nb&&(b=a[n]),a[n]>=1;for(l=j;lT;T++)switch(v){case 143>=T:ra.push([T+48,8]);break;case 255>=T:ra.push([T-144+400,9]);break;case 279>=T:ra.push([T-256+0,7]);break;case 287>=T:ra.push([T-280+192,8]);break;default:i("invalid literal: "+T)} +ma.prototype.n=function(){var a,c,b,e,f=this.input;switch(this.h){case 0:b=0;for(e=f.length;b>>8&255;l[q++]=j&255;l[q++]=j>>>8&255;if(H)l.set(d,q),q+=d.length,l=l.subarray(0,q);else{s=0;for(n=d.length;sw)for(;0w?w:138,G>w-3&&G=G?(L[I++]=17,L[I++]=G-3,P[17]++):(L[I++]=18,L[I++]=G-11,P[18]++),w-=G;else if(L[I++]=M[u],P[M[u]]++,w--,3>w)for(;0w?w:6,G>w-3&&GF;F++)va[F]=na[da[F]];for(C=19;4=a:return[265,a-11,1];case 14>=a:return[266,a-13,1];case 16>=a:return[267,a-15,1];case 18>=a:return[268,a-17,1];case 22>=a:return[269,a-19,2];case 26>=a:return[270,a-23,2];case 30>=a:return[271,a-27,2];case 34>=a:return[272,a- +31,2];case 42>=a:return[273,a-35,3];case 50>=a:return[274,a-43,3];case 58>=a:return[275,a-51,3];case 66>=a:return[276,a-59,3];case 82>=a:return[277,a-67,4];case 98>=a:return[278,a-83,4];case 114>=a:return[279,a-99,4];case 130>=a:return[280,a-115,4];case 162>=a:return[281,a-131,5];case 194>=a:return[282,a-163,5];case 226>=a:return[283,a-195,5];case 257>=a:return[284,a-227,5];case 258===a:return[285,a-258,0];default:i("invalid length: "+a)}}var Aa=[],za,Ba; +for(za=3;258>=za;za++)Ba=xa(),Aa[za]=Ba[2]<<24|Ba[1]<<16|Ba[0];var Ca=H?new Uint32Array(Aa):Aa; +function sa(a,c){function b(a,c){var b=a.G,d=[],e=0,f;f=Ca[a.length];d[e++]=f&65535;d[e++]=f>>16&255;d[e++]=f>>24;var g;switch(v){case 1===b:g=[0,b-1,0];break;case 2===b:g=[1,b-2,0];break;case 3===b:g=[2,b-3,0];break;case 4===b:g=[3,b-4,0];break;case 6>=b:g=[4,b-5,1];break;case 8>=b:g=[5,b-7,1];break;case 12>=b:g=[6,b-9,2];break;case 16>=b:g=[7,b-13,2];break;case 24>=b:g=[8,b-17,3];break;case 32>=b:g=[9,b-25,3];break;case 48>=b:g=[10,b-33,4];break;case 64>=b:g=[11,b-49,4];break;case 96>=b:g=[12,b- +65,5];break;case 128>=b:g=[13,b-97,5];break;case 192>=b:g=[14,b-129,6];break;case 256>=b:g=[15,b-193,6];break;case 384>=b:g=[16,b-257,7];break;case 512>=b:g=[17,b-385,7];break;case 768>=b:g=[18,b-513,8];break;case 1024>=b:g=[19,b-769,8];break;case 1536>=b:g=[20,b-1025,9];break;case 2048>=b:g=[21,b-1537,9];break;case 3072>=b:g=[22,b-2049,10];break;case 4096>=b:g=[23,b-3073,10];break;case 6144>=b:g=[24,b-4097,11];break;case 8192>=b:g=[25,b-6145,11];break;case 12288>=b:g=[26,b-8193,12];break;case 16384>= +b:g=[27,b-12289,12];break;case 24576>=b:g=[28,b-16385,13];break;case 32768>=b:g=[29,b-24577,13];break;default:i("invalid distance")}f=g;d[e++]=f[0];d[e++]=f[1];d[e++]=f[2];var h,j;h=0;for(j=d.length;h=d;)t[d++]=0;for(d=0;29>=d;)z[d++]=0}t[256]=1;e=0;for(f=c.length;e=f){n&&b(n,-1);d=0;for(g=f-e;dp&&e+pk&&(B=x,k=p);if(258===p)break}s=new wa(k,e-B);n?n.length2*l[k-1]+q[k]&&(l[k]=2*l[k-1]+q[k]),t[k]=Array(l[k]),z[k]=Array(l[k]);for(B=0;Bh[B]?(t[k][p]=D,z[k][p]=n,C+=2): +(t[k][p]=h[B],z[k][p]=B,++B);K[k]=0;1===q[k]&&b(k)}m=E;j=0;for(s=g.length;j1<f&&i("undercommitted");d=0;for(g=a.length;d>>=1}return c};function Da(a,c){this.input=a;this.a=new (H?Uint8Array:Array)(32768);this.h=U.j;var b={},e;if((c||!(c={}))&&"number"===typeof c.compressionType)this.h=c.compressionType;for(e in c)b[e]=c[e];b.outputBuffer=this.a;this.z=new ma(this.input,b)}var U=qa; +Da.prototype.n=function(){var a,c,b,e,f,d,g,h=0;g=this.a;a=Ea;switch(a){case Ea:c=Math.LOG2E*Math.log(32768)-8;break;default:i(Error("invalid compression method"))}b=c<<4|a;g[h++]=b;switch(a){case Ea:switch(this.h){case U.NONE:f=0;break;case U.r:f=1;break;case U.j:f=2;break;default:i(Error("unsupported compression type"))}break;default:i(Error("invalid compression method"))}e=f<<6|0;g[h++]=e|31-(256*b+e)%31;d=ba(this.input);this.z.b=h;g=this.z.n();h=g.length;H&&(g=new Uint8Array(g.buffer),g.length<= +h+4&&(this.a=new Uint8Array(g.length+4),this.a.set(g),g=this.a),g=g.subarray(0,h+4));g[h++]=d>>24&255;g[h++]=d>>16&255;g[h++]=d>>8&255;g[h++]=d&255;return g};y("Zlib.Deflate",Da);y("Zlib.Deflate.compress",function(a,c){return(new Da(a,c)).n()});y("Zlib.Deflate.CompressionType",U);y("Zlib.Deflate.CompressionType.NONE",U.NONE);y("Zlib.Deflate.CompressionType.FIXED",U.r);y("Zlib.Deflate.CompressionType.DYNAMIC",U.j);function V(a,c){this.k=[];this.l=32768;this.e=this.g=this.c=this.q=0;this.input=H?new Uint8Array(a):a;this.s=!1;this.m=Fa;this.B=!1;if(c||!(c={}))c.index&&(this.c=c.index),c.bufferSize&&(this.l=c.bufferSize),c.bufferType&&(this.m=c.bufferType),c.resize&&(this.B=c.resize);switch(this.m){case Ga:this.b=32768;this.a=new (H?Uint8Array:Array)(32768+this.l+258);break;case Fa:this.b=0;this.a=new (H?Uint8Array:Array)(this.l);this.f=this.J;this.t=this.H;this.o=this.I;break;default:i(Error("invalid inflate mode"))}} +var Ga=0,Fa=1,Ha={D:Ga,C:Fa}; +V.prototype.p=function(){for(;!this.s;){var a=X(this,3);a&1&&(this.s=v);a>>>=1;switch(a){case 0:var c=this.input,b=this.c,e=this.a,f=this.b,d=r,g=r,h=r,m=e.length,j=r;this.e=this.g=0;d=c[b++];d===r&&i(Error("invalid uncompressed block header: LEN (first byte)"));g=d;d=c[b++];d===r&&i(Error("invalid uncompressed block header: LEN (second byte)"));g|=d<<8;d=c[b++];d===r&&i(Error("invalid uncompressed block header: NLEN (first byte)"));h=d;d=c[b++];d===r&&i(Error("invalid uncompressed block header: NLEN (second byte)"));h|= +d<<8;g===~h&&i(Error("invalid uncompressed block header: length verify"));b+g>c.length&&i(Error("input buffer is broken"));switch(this.m){case Ga:for(;f+g>e.length;){j=m-f;g-=j;if(H)e.set(c.subarray(b,b+j),f),f+=j,b+=j;else for(;j--;)e[f++]=c[b++];this.b=f;e=this.f();f=this.b}break;case Fa:for(;f+g>e.length;)e=this.f({v:2});break;default:i(Error("invalid inflate mode"))}if(H)e.set(c.subarray(b,b+g),f),f+=g,b+=g;else for(;g--;)e[f++]=c[b++];this.c=b;this.b=f;this.a=e;break;case 1:this.o(Ia,Ja);break; +case 2:Ka(this);break;default:i(Error("unknown BTYPE: "+a))}}return this.t()}; +var La=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],Za=H?new Uint16Array(La):La,$a=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,258,258],ab=H?new Uint16Array($a):$a,bb=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0],cb=H?new Uint8Array(bb):bb,db=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577],eb=H?new Uint16Array(db):db,fb=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10, +10,11,11,12,12,13,13],gb=H?new Uint8Array(fb):fb,hb=new (H?Uint8Array:Array)(288),Y,ib;Y=0;for(ib=hb.length;Y=Y?8:255>=Y?9:279>=Y?7:8;var Ia=S(hb),jb=new (H?Uint8Array:Array)(30),kb,lb;kb=0;for(lb=jb.length;kb>>c;a.e=e-c;a.c=d;return g} +function mb(a,c){for(var b=a.g,e=a.e,f=a.input,d=a.c,g=c[0],h=c[1],m,j,s;e>>16;a.g=b>>s;a.e=e-s;a.c=d;return j&65535} +function Ka(a){function c(a,b,c){var d,e,f,g;for(g=0;gd)e>=f&&(this.b=e,b=this.f(),e=this.b),b[e++]=d;else{g=d-257;m=ab[g];0=f&&(this.b=e,b=this.f(),e=this.b);for(;m--;)b[e]=b[e++-h]}for(;8<=this.e;)this.e-=8,this.c--;this.b=e}; +V.prototype.I=function(a,c){var b=this.a,e=this.b;this.u=a;for(var f=b.length,d,g,h,m;256!==(d=mb(this,a));)if(256>d)e>=f&&(b=this.f(),f=b.length),b[e++]=d;else{g=d-257;m=ab[g];0f&&(b=this.f(),f=b.length);for(;m--;)b[e]=b[e++-h]}for(;8<=this.e;)this.e-=8,this.c--;this.b=e}; +V.prototype.f=function(){var a=new (H?Uint8Array:Array)(this.b-32768),c=this.b-32768,b,e,f=this.a;if(H)a.set(f.subarray(32768,a.length));else{b=0;for(e=a.length;bb;++b)f[b]=f[c+b];this.b=32768;return f}; +V.prototype.J=function(a){var c,b=this.input.length/this.c+1|0,e,f,d,g=this.input,h=this.a;a&&("number"===typeof a.v&&(b=a.v),"number"===typeof a.F&&(b+=a.F));2>b?(e=(g.length-this.c)/this.u[2],d=258*(e/2)|0,f=dc&&(this.a.length=c),a=this.a);return this.buffer=a};function nb(a,c){var b,e;this.input=a;this.c=0;if(c||!(c={}))c.index&&(this.c=c.index),c.verify&&(this.M=c.verify);b=a[this.c++];e=a[this.c++];switch(b&15){case Ea:this.method=Ea;break;default:i(Error("unsupported compression method"))}0!==((b<<8)+e)%31&&i(Error("invalid fcheck flag:"+((b<<8)+e)%31));e&32&&i(Error("fdict flag is not supported"));this.A=new V(a,{index:this.c,bufferSize:c.bufferSize,bufferType:c.bufferType,resize:c.resize})} +nb.prototype.p=function(){var a=this.input,c,b;c=this.A.p();this.c=this.A.c;this.M&&(b=(a[this.c++]<<24|a[this.c++]<<16|a[this.c++]<<8|a[this.c++])>>>0,b!==ba(c)&&i(Error("invalid adler-32 checksum")));return c};y("Zlib.Inflate",nb);y("Zlib.Inflate.BufferType",Ha);Ha.ADAPTIVE=Ha.C;Ha.BLOCK=Ha.D;y("Zlib.Inflate.prototype.decompress",nb.prototype.p);var ob=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];H&&new Uint16Array(ob);var pb=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,258,258];H&&new Uint16Array(pb);var qb=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0];H&&new Uint8Array(qb);var rb=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577];H&&new Uint16Array(rb); +var sb=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13];H&&new Uint8Array(sb);var tb=new (H?Uint8Array:Array)(288),Z,ub;Z=0;for(ub=tb.length;Z=Z?8:255>=Z?9:279>=Z?7:8;S(tb);var vb=new (H?Uint8Array:Array)(30),wb,xb;wb=0;for(xb=vb.length;wb + * Normally you won't need to use this class directly. 99% of the cases you will use the CCNode interface, + * which uses this class's singleton object. + * But there are some cases where you might need to use this class.
+ * Examples:
+ * - When you want to run an action where the target is different from a CCNode.
+ * - When you want to pause / resume the actions
+ * @class + * @extends cc.Class + * @example + * var mng = new cc.ActionManager(); + */ +cc.ActionManager = cc.Class.extend(/** @lends cc.ActionManager# */{ + _elementPool: [], + + _searchElementByTarget:function (arr, target) { + for (var k = 0; k < arr.length; k++) { + if (target === arr[k].target) + return arr[k]; + } + return null; + }, + + ctor:function () { + this._hashTargets = {}; + this._arrayTargets = []; + this._currentTarget = null; + }, + + _getElement: function (target, paused) { + var element = this._elementPool.pop(); + if (!element) { + element = new cc.HashElement(); + } + element.target = target; + element.paused = !!paused; + return element; + }, + + _putElement: function (element) { + element.actions.length = 0; + element.actionIndex = 0; + element.currentAction = null; + element.paused = false; + element.target = null; + element.lock = false; + this._elementPool.push(element); + }, + + /** Adds an action with a target. + * If the target is already present, then the action will be added to the existing target. + * If the target is not present, a new instance of this target will be created either paused or not, and the action will be added to the newly created target. + * When the target is paused, the queued actions won't be 'ticked'. + * @param {cc.Action} action + * @param {cc.Node} target + * @param {Boolean} paused + */ + addAction:function (action, target, paused) { + if(!action) + throw new Error("cc.ActionManager.addAction(): action must be non-null"); + if(!target) + throw new Error("cc.ActionManager.addAction(): target must be non-null"); + + //check if the action target already exists + var element = this._hashTargets[target.__instanceId]; + //if doesn't exists, create a hashelement and push in mpTargets + if (!element) { + element = this._getElement(target, paused); + this._hashTargets[target.__instanceId] = element; + this._arrayTargets.push(element); + } + else if (!element.actions) { + element.actions = []; + } + + element.actions.push(action); + action.startWithTarget(target); + }, + + /** + * Removes all actions from all the targets. + */ + removeAllActions:function () { + var locTargets = this._arrayTargets; + for (var i = 0; i < locTargets.length; i++) { + var element = locTargets[i]; + if (element) + this.removeAllActionsFromTarget(element.target, true); + } + }, + /** Removes all actions from a certain target.
+ * All the actions that belongs to the target will be removed. + * @param {object} target + * @param {boolean} forceDelete + */ + removeAllActionsFromTarget:function (target, forceDelete) { + // explicit null handling + if (target == null) + return; + var element = this._hashTargets[target.__instanceId]; + if (element) { + element.actions.length = 0; + this._deleteHashElement(element); + } + }, + /** Removes an action given an action reference. + * @param {cc.Action} action + */ + removeAction:function (action) { + // explicit null handling + if (action == null) + return; + var target = action.getOriginalTarget(); + var element = this._hashTargets[target.__instanceId]; + + if (element) { + for (var i = 0; i < element.actions.length; i++) { + if (element.actions[i] === action) { + element.actions.splice(i, 1); + // update actionIndex in case we are in tick. looping over the actions + if (element.actionIndex >= i) + element.actionIndex--; + break; + } + } + } else { + cc.log(cc._LogInfos.ActionManager_removeAction); + } + }, + + /** Removes an action given its tag and the target + * @param {Number} tag + * @param {object} target + */ + removeActionByTag:function (tag, target) { + if(tag === cc.ACTION_TAG_INVALID) + cc.log(cc._LogInfos.ActionManager_addAction); + + cc.assert(target, cc._LogInfos.ActionManager_addAction); + + var element = this._hashTargets[target.__instanceId]; + + if (element) { + var limit = element.actions.length; + for (var i = 0; i < limit; ++i) { + var action = element.actions[i]; + if (action && action.getTag() === tag && action.getOriginalTarget() === target) { + this._removeActionAtIndex(i, element); + break; + } + } + } + }, + + /** Gets an action given its tag an a target + * @param {Number} tag + * @param {object} target + * @return {cc.Action|Null} return the Action with the given tag on success + */ + getActionByTag:function (tag, target) { + if(tag === cc.ACTION_TAG_INVALID) + cc.log(cc._LogInfos.ActionManager_getActionByTag); + + var element = this._hashTargets[target.__instanceId]; + if (element) { + if (element.actions != null) { + for (var i = 0; i < element.actions.length; ++i) { + var action = element.actions[i]; + if (action && action.getTag() === tag) + return action; + } + } + cc.log(cc._LogInfos.ActionManager_getActionByTag_2, tag); + } + return null; + }, + + + /** Returns the numbers of actions that are running in a certain target.
+ * Composable actions are counted as 1 action.
+ * Example:
+ * - If you are running 1 Sequence of 7 actions, it will return 1.
+ * - If you are running 7 Sequences of 2 actions, it will return 7. + * @param {object} target + * @return {Number} + */ + numberOfRunningActionsInTarget:function (target) { + var element = this._hashTargets[target.__instanceId]; + if (element) + return (element.actions) ? element.actions.length : 0; + + return 0; + }, + /** Pauses the target: all running actions and newly added actions will be paused. + * @param {object} target + */ + pauseTarget:function (target) { + var element = this._hashTargets[target.__instanceId]; + if (element) + element.paused = true; + }, + /** Resumes the target. All queued actions will be resumed. + * @param {object} target + */ + resumeTarget:function (target) { + var element = this._hashTargets[target.__instanceId]; + if (element) + element.paused = false; + }, + + /** + * Pauses all running actions, returning a list of targets whose actions were paused. + * @return {Array} a list of targets whose actions were paused. + */ + pauseAllRunningActions:function(){ + var idsWithActions = []; + var locTargets = this._arrayTargets; + for(var i = 0; i< locTargets.length; i++){ + var element = locTargets[i]; + if(element && !element.paused){ + element.paused = true; + idsWithActions.push(element.target); + } + } + return idsWithActions; + }, + + /** + * Resume a set of targets (convenience function to reverse a pauseAllRunningActions call) + * @param {Array} targetsToResume + */ + resumeTargets:function(targetsToResume){ + if (!targetsToResume) + return; + + for (var i = 0; i< targetsToResume.length; i++) { + if(targetsToResume[i]) + this.resumeTarget(targetsToResume[i]); + } + }, + + /** purges the shared action manager. It releases the retained instance.
+ * because it uses this, so it can not be static + */ + purgeSharedManager:function () { + cc.director.getScheduler().unscheduleUpdate(this); + }, + + //protected + _removeActionAtIndex:function (index, element) { + var action = element.actions[index]; + + element.actions.splice(index, 1); + + // update actionIndex in case we are in tick. looping over the actions + if (element.actionIndex >= index) + element.actionIndex--; + + if (element.actions.length === 0) { + this._deleteHashElement(element); + } + }, + + _deleteHashElement:function (element) { + var ret = false; + if (element && !element.lock) { + if (this._hashTargets[element.target.__instanceId]) { + delete this._hashTargets[element.target.__instanceId]; + var targets = this._arrayTargets; + for (var i = 0, l = targets.length; i < l; i++) { + if (targets[i] === element) { + targets.splice(i, 1); + break; + } + } + this._putElement(element); + ret = true; + } + } + return ret; + }, + + /** + * @param {Number} dt delta time in seconds + */ + update:function (dt) { + var locTargets = this._arrayTargets , locCurrTarget; + for (var elt = 0; elt < locTargets.length; elt++) { + this._currentTarget = locTargets[elt]; + locCurrTarget = this._currentTarget; + if (!locCurrTarget.paused && locCurrTarget.actions) { + locCurrTarget.lock = true; + // The 'actions' CCMutableArray may change while inside this loop. + for (locCurrTarget.actionIndex = 0; locCurrTarget.actionIndex < locCurrTarget.actions.length; locCurrTarget.actionIndex++) { + locCurrTarget.currentAction = locCurrTarget.actions[locCurrTarget.actionIndex]; + if (!locCurrTarget.currentAction) + continue; + + //use for speed + locCurrTarget.currentAction.step(dt * ( locCurrTarget.currentAction._speedMethod ? locCurrTarget.currentAction._speed : 1 ) ); + + if (locCurrTarget.currentAction && locCurrTarget.currentAction.isDone()) { + locCurrTarget.currentAction.stop(); + var action = locCurrTarget.currentAction; + locCurrTarget.currentAction = null; + this.removeAction(action); + } + + locCurrTarget.currentAction = null; + } + locCurrTarget.lock = false; + } + // only delete currentTarget if no actions were scheduled during the cycle (issue #481) + if (locCurrTarget.actions.length === 0) { + this._deleteHashElement(locCurrTarget) && elt--; + } + } + } +}); diff --git a/frameworks/cocos2d-html5/cocos2d/core/CCConfiguration.js b/frameworks/cocos2d-html5/cocos2d/core/CCConfiguration.js new file mode 100644 index 0000000..db58df2 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/CCConfiguration.js @@ -0,0 +1,294 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * cc.configuration is a singleton object which contains some openGL variables + * @class + * @name cc.configuration + * @example + * var textureSize = cc.configuration.getMaxTextureSize(); + */ +cc.configuration = /** @lends cc.configuration# */{ + // Type constants + /* + * ERROR type + * @public + * @const + * @type {Number} + */ + ERROR:0, + + /* + * STRING type + * @public + * @const + * @type {Number} + */ + STRING:1, + + /* + * INT type + * @public + * @const + * @type {Number} + */ + INT:2, + + /* + * DOUBLE type + * @public + * @const + * @type {Number} + */ + DOUBLE:3, + + /* + * BOOLEAN type + * @public + * @const + * @type {Number} + */ + BOOLEAN:4, + + _maxTextureSize:0, + _maxModelviewStackDepth:0, + _supportsPVRTC:false, + _supportsNPOT:false, + _supportsBGRA8888:false, + _supportsDiscardFramebuffer:false, + _supportsShareableVAO:false, + _maxSamplesAllowed:0, + _maxTextureUnits:0, + _GlExtensions:"", + _valueDict:{}, + + _inited: false, + + _init:function () { + var locValueDict = this._valueDict; + locValueDict["cocos2d.x.version"] = cc.ENGINE_VERSION; + locValueDict["cocos2d.x.compiled_with_profiler"] = false; + locValueDict["cocos2d.x.compiled_with_gl_state_cache"] = cc.ENABLE_GL_STATE_CACHE; + this._inited = true; + }, + + /** + * OpenGL Max texture size. + * @return {Number} + */ + getMaxTextureSize:function () { + return this._maxTextureSize; + }, + + /** + * OpenGL Max Modelview Stack Depth. + * @return {Number} + */ + getMaxModelviewStackDepth:function () { + return this._maxModelviewStackDepth; + }, + + /** + * returns the maximum texture units + * @return {Number} + */ + getMaxTextureUnits:function () { + return this._maxTextureUnits; + }, + + /** + * Whether or not the GPU supports NPOT (Non Power Of Two) textures. + * OpenGL ES 2.0 already supports NPOT (iOS). + * @return {Boolean} + */ + supportsNPOT:function () { + return this._supportsNPOT; + }, + + /** + * Whether or not PVR Texture Compressed is supported + * @return {Boolean} + */ + supportsPVRTC: function () { + return this._supportsPVRTC; + }, + + /** + * Whether or not ETC Texture Compressed is supported + * @return {Boolean} + */ + supportsETC: function() { + return false; + }, + + /** + * Whether or not S3TC Texture Compressed is supported + * @return {Boolean} + */ + supportsS3TC: function() { + return false; + }, + + /** + * Whether or not ATITC Texture Compressed is supported + * @return {Boolean} + */ + supportsATITC: function() { + return false; + }, + + /** + * Whether or not BGRA8888 textures are supported. + * @return {Boolean} + */ + supportsBGRA8888:function () { + return this._supportsBGRA8888; + }, + + /** + * Whether or not glDiscardFramebufferEXT is supported + * @return {Boolean} + */ + supportsDiscardFramebuffer:function () { + return this._supportsDiscardFramebuffer; + }, + + /** + * Whether or not shareable VAOs are supported. + * @return {Boolean} + */ + supportsShareableVAO:function () { + return this._supportsShareableVAO; + }, + + /** + * returns whether or not an OpenGL is supported + * @param {String} searchName + */ + checkForGLExtension:function (searchName) { + return this._GlExtensions.indexOf(searchName) > -1; + }, + + /** + * Returns the value of a given key. If the key is not found, it will return the default value + * @param {String} key + * @param {String|Bool|Number|Object} [default_value=null] + * @returns {String|Bool|Number|Object} + */ + getValue: function(key, default_value){ + if(!this._inited) + this._init(); + var locValueDict = this._valueDict; + if(locValueDict[key]) + return locValueDict[key]; + return default_value; + }, + + /** + * Sets a new key/value pair in the configuration dictionary + * @param {string} key + * @param {String|Bool|Number|Object} value + */ + setValue: function(key, value){ + this._valueDict[key] = value; + }, + + /** + * Dumps the current configuration on the console + */ + dumpInfo: function(){ + if(cc.ENABLE_GL_STATE_CACHE === 0){ + cc.log(""); + cc.log(cc._LogInfos.configuration_dumpInfo); + cc.log("") + } + }, + + /** + * gathers OpenGL / GPU information + */ + gatherGPUInfo: function(){ + if(cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return; + + if(!this._inited) + this._init(); + var gl = cc._renderContext; + var locValueDict = this._valueDict; + locValueDict["gl.vendor"] = gl.getParameter(gl.VENDOR); + locValueDict["gl.renderer"] = gl.getParameter(gl.RENDERER); + locValueDict["gl.version"] = gl.getParameter(gl.VERSION); + + this._GlExtensions = ""; + var extArr = gl.getSupportedExtensions(); + for (var i = 0; i < extArr.length; i++) + this._GlExtensions += extArr[i] + " "; + + this._maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); + locValueDict["gl.max_texture_size"] = this._maxTextureSize; + this._maxTextureUnits = gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS); + locValueDict["gl.max_texture_units"] = this._maxTextureUnits; + + this._supportsPVRTC = this.checkForGLExtension("GL_IMG_texture_compression_pvrtc"); + locValueDict["gl.supports_PVRTC"] = this._supportsPVRTC; + + this._supportsNPOT = false; //true; + locValueDict["gl.supports_NPOT"] = this._supportsNPOT; + + this._supportsBGRA8888 = this.checkForGLExtension("GL_IMG_texture_format_BGRA888"); + locValueDict["gl.supports_BGRA8888"] = this._supportsBGRA8888; + + this._supportsDiscardFramebuffer = this.checkForGLExtension("GL_EXT_discard_framebuffer"); + locValueDict["gl.supports_discard_framebuffer"] = this._supportsDiscardFramebuffer; + + this._supportsShareableVAO = this.checkForGLExtension("vertex_array_object"); + locValueDict["gl.supports_vertex_array_object"] = this._supportsShareableVAO; + + cc.checkGLErrorDebug(); + }, + + /** + * Loads a config file. If the keys are already present, then they are going to be replaced. Otherwise the new keys are added. + * @param {string} url + */ + loadConfigFile: function( url){ + if(!this._inited) + this._init(); + var dict = cc.loader.getRes(url); + if(!dict) throw new Error("Please load the resource first : " + url); + cc.assert(dict, cc._LogInfos.configuration_loadConfigFile_2, url); + + var getDatas = dict["data"]; + if(!getDatas){ + cc.log(cc._LogInfos.configuration_loadConfigFile, url); + return; + } + + // Add all keys in the existing dictionary + for(var selKey in getDatas) + this._valueDict[selKey] = getDatas[selKey]; + } +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/cocos2d/core/CCDirector.js b/frameworks/cocos2d-html5/cocos2d/core/CCDirector.js new file mode 100644 index 0000000..b8a021f --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/CCDirector.js @@ -0,0 +1,946 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +cc.g_NumberOfDraws = 0; + +//---------------------------------------------------------------------------------------------------------------------- + +/** + *

+ * ATTENTION: USE cc.director INSTEAD OF cc.Director.
+ * cc.director is a singleton object which manage your game's logic flow.
+ * Since the cc.director is a singleton, you don't need to call any constructor or create functions,
+ * the standard way to use it is by calling:
+ * - cc.director.methodName();
+ * + * It creates and handle the main Window and manages how and when to execute the Scenes.
+ *
+ * The cc.director is also responsible for:
+ * - initializing the OpenGL context
+ * - setting the OpenGL pixel format (default on is RGB565)
+ * - setting the OpenGL pixel format (default on is RGB565)
+ * - setting the OpenGL buffer depth (default one is 0-bit)
+ * - setting the color for clear screen (default one is BLACK)
+ * - setting the projection (default one is 3D)
+ * - setting the orientation (default one is Portrait)
+ *
+ *
+ * The cc.director also sets the default OpenGL context:
+ * - GL_TEXTURE_2D is enabled
+ * - GL_VERTEX_ARRAY is enabled
+ * - GL_COLOR_ARRAY is enabled
+ * - GL_TEXTURE_COORD_ARRAY is enabled
+ *

+ *

+ * cc.director also synchronizes timers with the refresh rate of the display.
+ * Features and Limitations:
+ * - Scheduled timers & drawing are synchronizes with the refresh rate of the display
+ * - Only supports animation intervals of 1/60 1/30 & 1/15
+ *

+ * @class + * @name cc.Director + */ +cc.Director = cc.Class.extend(/** @lends cc.Director# */{ + //Variables + _landscape: false, + _nextDeltaTimeZero: false, + _paused: false, + _purgeDirectorInNextLoop: false, + _sendCleanupToScene: false, + _animationInterval: 0.0, + _oldAnimationInterval: 0.0, + _projection: 0, + _contentScaleFactor: 1.0, + + _deltaTime: 0.0, + + _winSizeInPoints: null, + + _lastUpdate: null, + _nextScene: null, + _notificationNode: null, + _openGLView: null, + _scenesStack: null, + _projectionDelegate: null, + _runningScene: null, + + _totalFrames: 0, + _secondsPerFrame: 0, + + _dirtyRegion: null, + + _scheduler: null, + _actionManager: null, + _eventProjectionChanged: null, + _eventAfterUpdate: null, + _eventAfterVisit: null, + _eventAfterDraw: null, + + ctor: function () { + var self = this; + self._lastUpdate = Date.now(); + cc.eventManager.addCustomListener(cc.game.EVENT_SHOW, function () { + self._lastUpdate = Date.now(); + }); + }, + + init: function () { + // scenes + this._oldAnimationInterval = this._animationInterval = 1.0 / cc.defaultFPS; + this._scenesStack = []; + // Set default projection (3D) + this._projection = cc.Director.PROJECTION_DEFAULT; + // projection delegate if "Custom" projection is used + this._projectionDelegate = null; + + // FPS + this._totalFrames = 0; + this._lastUpdate = Date.now(); + + //Paused? + this._paused = false; + + //purge? + this._purgeDirectorInNextLoop = false; + + this._winSizeInPoints = cc.size(0, 0); + + this._openGLView = null; + this._contentScaleFactor = 1.0; + + //scheduler + this._scheduler = new cc.Scheduler(); + //action manager + if (cc.ActionManager) { + this._actionManager = new cc.ActionManager(); + this._scheduler.scheduleUpdate(this._actionManager, cc.Scheduler.PRIORITY_SYSTEM, false); + } else { + this._actionManager = null; + } + + this._eventAfterUpdate = new cc.EventCustom(cc.Director.EVENT_AFTER_UPDATE); + this._eventAfterUpdate.setUserData(this); + this._eventAfterVisit = new cc.EventCustom(cc.Director.EVENT_AFTER_VISIT); + this._eventAfterVisit.setUserData(this); + this._eventAfterDraw = new cc.EventCustom(cc.Director.EVENT_AFTER_DRAW); + this._eventAfterDraw.setUserData(this); + this._eventProjectionChanged = new cc.EventCustom(cc.Director.EVENT_PROJECTION_CHANGED); + this._eventProjectionChanged.setUserData(this); + + return true; + }, + + /** + * calculates delta time since last time it was called + */ + calculateDeltaTime: function () { + var now = Date.now(); + + // new delta time. + if (this._nextDeltaTimeZero) { + this._deltaTime = 0; + this._nextDeltaTimeZero = false; + } else { + this._deltaTime = (now - this._lastUpdate) / 1000; + } + + if ((cc.game.config[cc.game.CONFIG_KEY.debugMode] > 0) && (this._deltaTime > 0.2)) + this._deltaTime = 1 / 60.0; + + this._lastUpdate = now; + }, + + /** + * Converts a view coordinate to an WebGL coordinate
+ * Useful to convert (multi) touches coordinates to the current layout (portrait or landscape)
+ * Implementation can be found in CCDirectorWebGL + * @function + * @param {cc.Point} uiPoint + * @return {cc.Point} + */ + convertToGL: function (uiPoint) { + var docElem = document.documentElement; + var view = cc.view; + var box = docElem.getBoundingClientRect(); + box.left += window.pageXOffset - docElem.clientLeft; + box.top += window.pageYOffset - docElem.clientTop; + var x = view._devicePixelRatio * (uiPoint.x - box.left); + var y = view._devicePixelRatio * (box.top + box.height - uiPoint.y); + return view._isRotated ? {x: view._viewPortRect.width - y, y: x} : {x: x, y: y}; + }, + + /** + * Converts an WebGL coordinate to a view coordinate
+ * Useful to convert node points to window points for calls such as glScissor
+ * Implementation can be found in CCDirectorWebGL + * @function + * @param {cc.Point} glPoint + * @return {cc.Point} + */ + convertToUI: function (glPoint) { + var docElem = document.documentElement; + var view = cc.view; + var box = docElem.getBoundingClientRect(); + box.left += window.pageXOffset - docElem.clientLeft; + box.top += window.pageYOffset - docElem.clientTop; + var uiPoint = {x: 0, y: 0}; + if (view._isRotated) { + uiPoint.x = box.left + glPoint.y / view._devicePixelRatio; + uiPoint.y = box.top + box.height - (view._viewPortRect.width - glPoint.x) / view._devicePixelRatio; + } + else { + uiPoint.x = box.left + glPoint.x / view._devicePixelRatio; + uiPoint.y = box.top + box.height - glPoint.y / view._devicePixelRatio; + } + return uiPoint; + }, + + /** + * Draw the scene. This method is called every frame. Don't call it manually. + */ + drawScene: function () { + var renderer = cc.renderer; + + // calculate "global" dt + this.calculateDeltaTime(); + + //tick before glClear: issue #533 + if (!this._paused) { + this._scheduler.update(this._deltaTime); + cc.eventManager.dispatchEvent(this._eventAfterUpdate); + } + + /* to avoid flickr, nextScene MUST be here: after tick and before draw. + XXX: Which bug is this one. It seems that it can't be reproduced with v0.9 */ + if (this._nextScene) { + this.setNextScene(); + } + + // draw the scene + if (this._runningScene) { + if (renderer.childrenOrderDirty) { + cc.renderer.clearRenderCommands(); + cc.renderer.assignedZ = 0; + this._runningScene._renderCmd._curLevel = 0; //level start from 0; + this._runningScene.visit(); + renderer.resetFlag(); + } + else if (renderer.transformDirty()) { + renderer.transform(); + } + } + + renderer.clear(); + + // draw the notifications node + if (this._notificationNode) + this._notificationNode.visit(); + + cc.eventManager.dispatchEvent(this._eventAfterVisit); + cc.g_NumberOfDraws = 0; + + renderer.rendering(cc._renderContext); + this._totalFrames++; + + cc.eventManager.dispatchEvent(this._eventAfterDraw); + cc.eventManager.frameUpdateListeners(); + + this._calculateMPF(); + }, + + /** + * End the life of director in the next frame + */ + end: function () { + this._purgeDirectorInNextLoop = true; + }, + + /** + * Returns the size in pixels of the surface. It could be different than the screen size.
+ * High-res devices might have a higher surface size than the screen size. + * @return {Number} + */ + getContentScaleFactor: function () { + return this._contentScaleFactor; + }, + + /** + * This object will be visited after the main scene is visited.
+ * This object MUST implement the "visit" selector.
+ * Useful to hook a notification object + * @return {cc.Node} + */ + getNotificationNode: function () { + return this._notificationNode; + }, + + /** + * Returns the size of the WebGL view in points.
+ * It takes into account any possible rotation (device orientation) of the window + * @return {cc.Size} + */ + getWinSize: function () { + return cc.size(this._winSizeInPoints); + }, + + /** + * Returns the size of the OpenGL view in pixels.
+ * It takes into account any possible rotation (device orientation) of the window.
+ * On Mac winSize and winSizeInPixels return the same value. + * @return {cc.Size} + */ + getWinSizeInPixels: function () { + return cc.size(this._winSizeInPoints.width * this._contentScaleFactor, this._winSizeInPoints.height * this._contentScaleFactor); + }, + + /** + * getVisibleSize/getVisibleOrigin move to CCDirectorWebGL/CCDirectorCanvas + * getZEye move to CCDirectorWebGL + */ + + /** + * Returns the visible size of the running scene + * @function + * @return {cc.Size} + */ + getVisibleSize: null, + + /** + * Returns the visible origin of the running scene + * @function + * @return {cc.Point} + */ + getVisibleOrigin: null, + + /** + * Returns the z eye, only available in WebGL mode + * @function + * @return {Number} + */ + getZEye: null, + + /** + * Pause the director's ticker + */ + pause: function () { + if (this._paused) + return; + + this._oldAnimationInterval = this._animationInterval; + // when paused, don't consume CPU + this.setAnimationInterval(1 / 4.0); + this._paused = true; + }, + + /** + * Pops out a scene from the queue.
+ * This scene will replace the running one.
+ * The running scene will be deleted. If there are no more scenes in the stack the execution is terminated.
+ * ONLY call it if there is a running scene. + */ + popScene: function () { + + cc.assert(this._runningScene, cc._LogInfos.Director_popScene); + + this._scenesStack.pop(); + var c = this._scenesStack.length; + + if (c === 0) + this.end(); + else { + this._sendCleanupToScene = true; + this._nextScene = this._scenesStack[c - 1]; + } + }, + + /** + * Removes cached all cocos2d cached data. It will purge the cc.textureCache, cc.spriteFrameCache, cc.animationCache + */ + purgeCachedData: function () { + cc.animationCache._clear(); + cc.spriteFrameCache._clear(); + cc.textureCache._clear(); + }, + + /** + * Purge the cc.director itself, including unschedule all schedule, remove all event listeners, clean up and exit the running scene, stops all animations, clear cached data. + */ + purgeDirector: function () { + //cleanup scheduler + this.getScheduler().unscheduleAll(); + + // Disable event dispatching + if (cc.eventManager) + cc.eventManager.setEnabled(false); + + // don't release the event handlers + // They are needed in case the director is run again + + if (this._runningScene) { + this._runningScene._performRecursive(cc.Node._stateCallbackType.onExitTransitionDidStart); + this._runningScene._performRecursive(cc.Node._stateCallbackType.onExit); + this._runningScene._performRecursive(cc.Node._stateCallbackType.cleanup); + } + + this._runningScene = null; + this._nextScene = null; + + // remove all objects, but don't release it. + // runScene might be executed after 'end'. + this._scenesStack.length = 0; + + this.stopAnimation(); + + // Clear all caches + this.purgeCachedData(); + + cc.checkGLErrorDebug(); + }, + + /** + * Suspends the execution of the running scene, pushing it on the stack of suspended scenes.
+ * The new scene will be executed.
+ * Try to avoid big stacks of pushed scenes to reduce memory allocation.
+ * ONLY call it if there is a running scene. + * @param {cc.Scene} scene + */ + pushScene: function (scene) { + + cc.assert(scene, cc._LogInfos.Director_pushScene); + + this._sendCleanupToScene = false; + + this._scenesStack.push(scene); + this._nextScene = scene; + }, + + /** + * Run a scene. Replaces the running scene with a new one or enter the first scene. + * @param {cc.Scene} scene + */ + runScene: function (scene) { + + cc.assert(scene, cc._LogInfos.Director_pushScene); + + if (!this._runningScene) { + //start scene + this.pushScene(scene); + this.startAnimation(); + } else { + //replace scene + var i = this._scenesStack.length; + if (i === 0) { + this._sendCleanupToScene = true; + this._scenesStack[i] = scene; + this._nextScene = scene; + } else { + this._sendCleanupToScene = true; + this._scenesStack[i - 1] = scene; + this._nextScene = scene; + } + } + }, + + /** + * Resume director after pause, if the current scene is not paused, nothing will happen. + */ + resume: function () { + if (!this._paused) { + return; + } + + this.setAnimationInterval(this._oldAnimationInterval); + this._lastUpdate = Date.now(); + if (!this._lastUpdate) { + cc.log(cc._LogInfos.Director_resume); + } + + this._paused = false; + this._deltaTime = 0; + }, + + /** + * The size in pixels of the surface. It could be different than the screen size.
+ * High-res devices might have a higher surface size than the screen size. + * @param {Number} scaleFactor + */ + setContentScaleFactor: function (scaleFactor) { + if (scaleFactor !== this._contentScaleFactor) { + this._contentScaleFactor = scaleFactor; + } + }, + + /** + * Enables or disables WebGL depth test.
+ * Implementation can be found in CCDirectorCanvas.js/CCDirectorWebGL.js + * @function + * @param {Boolean} on + */ + setDepthTest: null, + + /** + * set color for clear screen.
+ * Implementation can be found in CCDirectorCanvas.js/CCDirectorWebGL.js + * @function + * @param {cc.Color} clearColor + */ + setClearColor: null, + /** + * Sets the default values based on the CCConfiguration info + */ + setDefaultValues: function () { + + }, + + /** + * Sets whether next delta time equals to zero + * @param {Boolean} nextDeltaTimeZero + */ + setNextDeltaTimeZero: function (nextDeltaTimeZero) { + this._nextDeltaTimeZero = nextDeltaTimeZero; + }, + + /** + * Starts the registered next scene + */ + setNextScene: function () { + var runningIsTransition = false, newIsTransition = false; + if (cc.TransitionScene) { + runningIsTransition = this._runningScene ? this._runningScene instanceof cc.TransitionScene : false; + newIsTransition = this._nextScene ? this._nextScene instanceof cc.TransitionScene : false; + } + + // If it is not a transition, call onExit/cleanup + if (!newIsTransition) { + var locRunningScene = this._runningScene; + if (locRunningScene) { + locRunningScene._performRecursive(cc.Node._stateCallbackType.onExitTransitionDidStart); + locRunningScene._performRecursive(cc.Node._stateCallbackType.onExit); + } + + // issue #709. the root node (scene) should receive the cleanup message too + // otherwise it might be leaked. + if (this._sendCleanupToScene && locRunningScene) + locRunningScene._performRecursive(cc.Node._stateCallbackType.cleanup); + } + + this._runningScene = this._nextScene; + cc.renderer.childrenOrderDirty = true; + + this._nextScene = null; + if ((!runningIsTransition) && (this._runningScene !== null)) { + this._runningScene._performRecursive(cc.Node._stateCallbackType.onEnter); + this._runningScene._performRecursive(cc.Node._stateCallbackType.onEnterTransitionDidFinish); + } + }, + + /** + * Sets Notification Node + * @param {cc.Node} node + */ + setNotificationNode: function (node) { + cc.renderer.childrenOrderDirty = true; + if (this._notificationNode) { + this._notificationNode._performRecursive(cc.Node._stateCallbackType.onExitTransitionDidStart); + this._notificationNode._performRecursive(cc.Node._stateCallbackType.onExit); + this._notificationNode._performRecursive(cc.Node._stateCallbackType.cleanup); + } + this._notificationNode = node; + if (!node) + return; + this._notificationNode._performRecursive(cc.Node._stateCallbackType.onEnter); + this._notificationNode._performRecursive(cc.Node._stateCallbackType.onEnterTransitionDidFinish); + }, + + /** + * Returns the cc.director delegate. + * @return {cc.DirectorDelegate} + */ + getDelegate: function () { + return this._projectionDelegate; + }, + + /** + * Sets the cc.director delegate. It shall implement the CCDirectorDelegate protocol + * @return {cc.DirectorDelegate} + */ + setDelegate: function (delegate) { + this._projectionDelegate = delegate; + }, + + /** + * Sets the view, where everything is rendered, do not call this function.
+ * Implementation can be found in CCDirectorCanvas.js/CCDirectorWebGL.js. + * @function + * @param {cc.view} openGLView + */ + setOpenGLView: null, + + /** + * Sets an OpenGL projection.
+ * Implementation can be found in CCDirectorCanvas.js/CCDirectorWebGL.js. + * @function + * @param {Number} projection + */ + setProjection: null, + + /** + * Update the view port.
+ * Implementation can be found in CCDirectorCanvas.js/CCDirectorWebGL.js. + * @function + */ + setViewport: null, + + /** + * Get the CCEGLView, where everything is rendered.
+ * Implementation can be found in CCDirectorCanvas.js/CCDirectorWebGL.js. + * @function + * @return {cc.view} + */ + getOpenGLView: null, + + /** + * Sets an OpenGL projection.
+ * Implementation can be found in CCDirectorCanvas.js/CCDirectorWebGL.js. + * @function + * @return {Number} + */ + getProjection: null, + + /** + * Enables/disables OpenGL alpha blending.
+ * Implementation can be found in CCDirectorCanvas.js/CCDirectorWebGL.js. + * @function + * @param {Boolean} on + */ + setAlphaBlending: null, + + /** + * Returns whether or not the replaced scene will receive the cleanup message.
+ * If the new scene is pushed, then the old scene won't receive the "cleanup" message.
+ * If the new scene replaces the old one, the it will receive the "cleanup" message. + * @return {Boolean} + */ + isSendCleanupToScene: function () { + return this._sendCleanupToScene; + }, + + /** + * Returns current running Scene. Director can only run one Scene at the time + * @return {cc.Scene} + */ + getRunningScene: function () { + return this._runningScene; + }, + + /** + * Returns the FPS value + * @return {Number} + */ + getAnimationInterval: function () { + return this._animationInterval; + }, + + /** + * Returns whether or not to display the FPS informations + * @return {Boolean} + */ + isDisplayStats: function () { + return cc.profiler ? cc.profiler.isShowingStats() : false; + }, + + /** + * Sets whether display the FPS on the bottom-left corner + * @param {Boolean} displayStats + */ + setDisplayStats: function (displayStats) { + if (cc.profiler) { + displayStats ? cc.profiler.showStats() : cc.profiler.hideStats(); + } + }, + + /** + * Returns seconds per frame + * @return {Number} + */ + getSecondsPerFrame: function () { + return this._secondsPerFrame; + }, + + /** + * Returns whether next delta time equals to zero + * @return {Boolean} + */ + isNextDeltaTimeZero: function () { + return this._nextDeltaTimeZero; + }, + + /** + * Returns whether or not the Director is paused + * @return {Boolean} + */ + isPaused: function () { + return this._paused; + }, + + /** + * Returns how many frames were called since the director started + * @return {Number} + */ + getTotalFrames: function () { + return this._totalFrames; + }, + + /** + * Pops out all scenes from the queue until the root scene in the queue.
+ * This scene will replace the running one.
+ * Internally it will call "popToSceneStackLevel(1)" + */ + popToRootScene: function () { + this.popToSceneStackLevel(1); + }, + + /** + * Pops out all scenes from the queue until it reaches "level".
+ * If level is 0, it will end the director.
+ * If level is 1, it will pop all scenes until it reaches to root scene.
+ * If level is <= than the current stack level, it won't do anything. + * @param {Number} level + */ + popToSceneStackLevel: function (level) { + cc.assert(this._runningScene, cc._LogInfos.Director_popToSceneStackLevel_2); + + var locScenesStack = this._scenesStack; + var c = locScenesStack.length; + + if (level === 0) { + this.end(); + return; + } + // stack overflow + if (level >= c) + return; + + // pop stack until reaching desired level + while (c > level) { + var current = locScenesStack.pop(); + if (current.running) { + current._performRecursive(cc.Node._stateCallbackType.onExitTransitionDidStart); + current._performRecursive(cc.Node._stateCallbackType.onExit); + } + current._performRecursive(cc.Node._stateCallbackType.cleanup); + c--; + } + this._nextScene = locScenesStack[locScenesStack.length - 1]; + this._sendCleanupToScene = true; + }, + + /** + * Returns the cc.Scheduler associated with this director + * @return {cc.Scheduler} + */ + getScheduler: function () { + return this._scheduler; + }, + + /** + * Sets the cc.Scheduler associated with this director + * @param {cc.Scheduler} scheduler + */ + setScheduler: function (scheduler) { + if (this._scheduler !== scheduler) { + this._scheduler = scheduler; + } + }, + + /** + * Returns the cc.ActionManager associated with this director + * @return {cc.ActionManager} + */ + getActionManager: function () { + return this._actionManager; + }, + /** + * Sets the cc.ActionManager associated with this director + * @param {cc.ActionManager} actionManager + */ + setActionManager: function (actionManager) { + if (this._actionManager !== actionManager) { + this._actionManager = actionManager; + } + }, + + /** + * Returns the delta time since last frame + * @return {Number} + */ + getDeltaTime: function () { + return this._deltaTime; + }, + + _calculateMPF: function () { + var now = Date.now(); + this._secondsPerFrame = (now - this._lastUpdate) / 1000; + } +}); + +/** + * The event projection changed of cc.Director + * @constant + * @type {string} + * @example + * cc.eventManager.addCustomListener(cc.Director.EVENT_PROJECTION_CHANGED, function(event) { + * cc.log("Projection changed."); + * }); + */ +cc.Director.EVENT_PROJECTION_CHANGED = "director_projection_changed"; + +/** + * The event after update of cc.Director + * @constant + * @type {string} + * @example + * cc.eventManager.addCustomListener(cc.Director.EVENT_AFTER_UPDATE, function(event) { + * cc.log("after update event."); + * }); + */ +cc.Director.EVENT_AFTER_UPDATE = "director_after_update"; + +/** + * The event after visit of cc.Director + * @constant + * @type {string} + * @example + * cc.eventManager.addCustomListener(cc.Director.EVENT_AFTER_VISIT, function(event) { + * cc.log("after visit event."); + * }); + */ +cc.Director.EVENT_AFTER_VISIT = "director_after_visit"; + +/** + * The event after draw of cc.Director + * @constant + * @type {string} + * @example + * cc.eventManager.addCustomListener(cc.Director.EVENT_AFTER_DRAW, function(event) { + * cc.log("after draw event."); + * }); + */ +cc.Director.EVENT_AFTER_DRAW = "director_after_draw"; + +/*************************************************** + * implementation of DisplayLinkDirector + **************************************************/ +cc.DisplayLinkDirector = cc.Director.extend(/** @lends cc.Director# */{ + invalid: false, + + /** + * Starts Animation + */ + startAnimation: function () { + this._nextDeltaTimeZero = true; + this.invalid = false; + }, + + /** + * Run main loop of director + */ + mainLoop: function () { + if (this._purgeDirectorInNextLoop) { + this._purgeDirectorInNextLoop = false; + this.purgeDirector(); + } + else if (!this.invalid) { + this.drawScene(); + } + }, + + /** + * Stops animation + */ + stopAnimation: function () { + this.invalid = true; + }, + + /** + * Sets animation interval + * @param {Number} value the animation interval desired + */ + setAnimationInterval: function (value) { + this._animationInterval = value; + if (!this.invalid) { + this.stopAnimation(); + this.startAnimation(); + } + } +}); + +cc.Director.sharedDirector = null; +cc.Director.firstUseDirector = true; + +cc.Director._getInstance = function () { + if (cc.Director.firstUseDirector) { + cc.Director.firstUseDirector = false; + cc.Director.sharedDirector = new cc.DisplayLinkDirector(); + cc.Director.sharedDirector.init(); + } + return cc.Director.sharedDirector; +}; + +/** + * Default fps is 60 + * @type {Number} + */ +cc.defaultFPS = 60; + +//Possible OpenGL projections used by director +/** + * Constant for 2D projection (orthogonal projection) + * @constant + * @type {Number} + */ +cc.Director.PROJECTION_2D = 0; + +/** + * Constant for 3D projection with a fovy=60, znear=0.5f and zfar=1500. + * @constant + * @type {Number} + */ +cc.Director.PROJECTION_3D = 1; + +/** + * Constant for custom projection, if cc.Director's projection set to it, it calls "updateProjection" on the projection delegate. + * @constant + * @type {Number} + */ +cc.Director.PROJECTION_CUSTOM = 3; + +/** + * Constant for default projection of cc.Director, default projection is 2D projection + * @constant + * @type {Number} + */ +cc.Director.PROJECTION_DEFAULT = cc.Director.PROJECTION_3D; diff --git a/frameworks/cocos2d-html5/cocos2d/core/CCDirectorCanvas.js b/frameworks/cocos2d-html5/cocos2d/core/CCDirectorCanvas.js new file mode 100644 index 0000000..12deec5 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/CCDirectorCanvas.js @@ -0,0 +1,82 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +cc.game.addEventListener(cc.game.EVENT_RENDERER_INITED, function () { + + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) { + var _p = cc.Director.prototype; + + _p.getProjection = function (projection) { + return this._projection; + }; + + _p.setProjection = function (projection) { + this._projection = projection; + cc.eventManager.dispatchEvent(this._eventProjectionChanged); + }; + + _p.setDepthTest = function () { + }; + + _p.setClearColor = function (clearColor) { + cc.renderer._clearColor = clearColor; + cc.renderer._clearFillStyle = 'rgb(' + clearColor.r + ',' + clearColor.g + ',' + clearColor.b +')' ; + }; + + _p.setOpenGLView = function (openGLView) { + // set size + this._winSizeInPoints.width = cc._canvas.width; //this._openGLView.getDesignResolutionSize(); + this._winSizeInPoints.height = cc._canvas.height; + this._openGLView = openGLView || cc.view; + if (cc.eventManager) + cc.eventManager.setEnabled(true); + }; + + _p.getVisibleSize = function () { + //if (this._openGLView) { + //return this._openGLView.getVisibleSize(); + //} else { + return this.getWinSize(); + //} + }; + + _p.getVisibleOrigin = function () { + //if (this._openGLView) { + //return this._openGLView.getVisibleOrigin(); + //} else { + return cc.p(0, 0); + //} + }; + } else { + cc.Director._fpsImage = new Image(); + cc.Director._fpsImage.addEventListener("load", function () { + cc.Director._fpsImageLoaded = true; + }); + if (cc._fpsImage) { + cc.Director._fpsImage.src = cc._fpsImage; + } + } +}); diff --git a/frameworks/cocos2d-html5/cocos2d/core/CCDirectorWebGL.js b/frameworks/cocos2d-html5/cocos2d/core/CCDirectorWebGL.js new file mode 100644 index 0000000..50ae30b --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/CCDirectorWebGL.js @@ -0,0 +1,215 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +cc.game.addEventListener(cc.game.EVENT_RENDERER_INITED, function () { + + // Do nothing under other render mode + if (cc._renderType !== cc.game.RENDER_TYPE_WEBGL) { + return; + } + + /** + * OpenGL projection protocol + * @class + * @extends cc.Class + */ + cc.DirectorDelegate = cc.Class.extend(/** @lends cc.DirectorDelegate# */{ + /** + * Called by CCDirector when the projection is updated, and "custom" projection is used + */ + updateProjection: function () { + } + }); + + var _p = cc.Director.prototype; + + var recursiveChild = function(node){ + if(node && node._renderCmd){ + node._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + var i, children = node._children; + for(i=0; i 0); + this._repeat = repeat; + this._runForever = (this._repeat === cc.REPEAT_FOREVER); + return true; + }, + /** + * @return {Number} returns interval of timer + */ + getInterval : function(){return this._interval;}, + /** + * @param {Number} interval set interval in seconds + */ + setInterval : function(interval){this._interval = interval;}, + + /** + * triggers the timer + * @param {Number} dt delta time + */ + update:function (dt) { + if (this._elapsed === -1) { + this._elapsed = 0; + this._timesExecuted = 0; + } else { + this._elapsed += dt; + if (this._runForever && !this._useDelay) {//standard timer usage + if (this._elapsed >= this._interval) { + this.trigger(); + this._elapsed = 0; + } + } else {//advanced usage + if (this._useDelay) { + if (this._elapsed >= this._delay) { + this.trigger(); + + this._elapsed -= this._delay; + this._timesExecuted += 1; + this._useDelay = false; + } + } else { + if (this._elapsed >= this._interval) { + this.trigger(); + + this._elapsed = 0; + this._timesExecuted += 1; + } + } + + if (this._callback && !this._runForever && this._timesExecuted > this._repeat) + this.cancel(); + } + } + }, + + getCallback: function(){ + return this._callback; + }, + + getKey: function(){ + return this._key; + }, + + trigger: function () { + if (this._target && this._callback){ + this._callback.call(this._target, this._elapsed); + } + }, + + cancel: function () { + //override + this._scheduler.unschedule(this._callback, this._target); + } +}, CallbackTimer.prototype); + +var _timers = []; +CallbackTimer.get = function () { + return _timers.pop() || new CallbackTimer(); +}; +CallbackTimer.put = function (timer) { + timer._scheduler = null; + timer._elapsed = -1; + timer._runForever = false; + timer._useDelay = false; + timer._timesExecuted = 0; + timer._repeat = 0; + timer._delay = 0; + timer._interval = 0; + timer._target = null; + timer._callback = null; + timer._key = null; + if (_timers.length < MAX_POOL_SIZE) + _timers.push(timer); +}; + +/** + *

+ * Scheduler is responsible of triggering the scheduled callbacks.
+ * You should not use NSTimer. Instead use this class.
+ *
+ * There are 2 different types of callbacks (selectors):
+ * - update callback: the 'update' callback will be called every frame. You can customize the priority.
+ * - custom callback: A custom callback will be called every frame, or with a custom interval of time
+ *
+ * The 'custom selectors' should be avoided when possible. It is faster, and consumes less memory to use the 'update callback'. * + *

+ * @class + * @extends cc.Class + * + * @example + * //register a schedule to scheduler + * cc.director.getScheduler().schedule(callback, this, interval, !this._isRunning); + */ +cc.Scheduler = cc.Class.extend(/** @lends cc.Scheduler# */{ + _timeScale:1.0, + + //_updates : null, //_updates[0] list of priority < 0, _updates[1] list of priority == 0, _updates[2] list of priority > 0, + _updatesNegList: null, + _updates0List: null, + _updatesPosList: null, + + _hashForTimers:null, //Used for "selectors with interval" + _arrayForTimers:null, //Speed up indexing + _hashForUpdates:null, // hash used to fetch quickly the list entries for pause,delete,etc + //_arrayForUpdates:null, //Speed up indexing + + _currentTarget:null, + _currentTargetSalvaged:false, + _updateHashLocked:false, //If true unschedule will not remove anything from a hash. Elements will only be marked for deletion. + + + ctor:function () { + this._timeScale = 1.0; + this._updatesNegList = []; + this._updates0List = []; + this._updatesPosList = []; + + this._hashForUpdates = {}; + this._hashForTimers = {}; + this._currentTarget = null; + this._currentTargetSalvaged = false; + this._updateHashLocked = false; + + this._arrayForTimers = []; + //this._arrayForUpdates = []; + + }, + + //-----------------------private method---------------------- + + _schedulePerFrame: function(callback, target, priority, paused){ + var hashElement = this._hashForUpdates[target.__instanceId]; + if (hashElement && hashElement.entry){ + // check if priority has changed + if (hashElement.entry.priority !== priority){ + if (this._updateHashLocked){ + cc.log("warning: you CANNOT change update priority in scheduled function"); + hashElement.entry.markedForDeletion = false; + hashElement.entry.paused = paused; + return; + }else{ + // will be added again outside if (hashElement). + this.unscheduleUpdate(target); + } + }else{ + hashElement.entry.markedForDeletion = false; + hashElement.entry.paused = paused; + return; + } + } + + // most of the updates are going to be 0, that's why there + // is an special list for updates with priority 0 + if (priority === 0){ + this._appendIn(this._updates0List, callback, target, paused); + }else if (priority < 0){ + this._priorityIn(this._updatesNegList, callback, target, priority, paused); + }else{ + // priority > 0 + this._priorityIn(this._updatesPosList, callback, target, priority, paused); + } + }, + + _removeHashElement:function (element) { + delete this._hashForTimers[element.target.__instanceId]; + var arr = this._arrayForTimers; + for (var i = 0, l = arr.length; i < l; i++) { + if (arr[i] === element) { + arr.splice(i, 1); + break; + } + } + HashTimerEntry.put(element); + }, + + _removeUpdateFromHash:function (entry) { + var self = this; + var element = self._hashForUpdates[entry.target.__instanceId]; + if (element) { + // Remove list entry from list + var list = element.list, listEntry = element.entry; + for (var i = 0, l = list.length; i < l; i++) { + if (list[i] === listEntry) { + list.splice(i, 1); + break; + } + } + + delete self._hashForUpdates[element.target.__instanceId]; + ListEntry.put(listEntry); + HashUpdateEntry.put(element); + } + }, + + _priorityIn:function (ppList, callback, target, priority, paused) { + var self = this, + listElement = ListEntry.get(null, null, callback, target, priority, paused, false); + + // empey list ? + if (!ppList) { + ppList = []; + ppList.push(listElement); + } else { + var index2Insert = ppList.length - 1; + for(var i = 0; i <= index2Insert; i++){ + if (priority < ppList[i].priority) { + index2Insert = i; + break; + } + } + ppList.splice(i, 0, listElement); + } + + //update hash entry for quick access + self._hashForUpdates[target.__instanceId] = HashUpdateEntry.get(ppList, listElement, target, null); + + return ppList; + }, + + _appendIn:function (ppList, callback, target, paused) { + var self = this, + listElement = ListEntry.get(null, null, callback, target, 0, paused, false); + ppList.push(listElement); + + //update hash entry for quicker access + self._hashForUpdates[target.__instanceId] = HashUpdateEntry.get(ppList, listElement, target, null, null); + }, + + //-----------------------public method------------------------- + /** + *

+ * Modifies the time of all scheduled callbacks.
+ * You can use this property to create a 'slow motion' or 'fast forward' effect.
+ * Default is 1.0. To create a 'slow motion' effect, use values below 1.0.
+ * To create a 'fast forward' effect, use values higher than 1.0.
+ * @warning It will affect EVERY scheduled selector / action. + *

+ * @param {Number} timeScale + */ + setTimeScale:function (timeScale) { + this._timeScale = timeScale; + }, + + /** + * Returns time scale of scheduler + * @return {Number} + */ + getTimeScale:function () { + return this._timeScale; + }, + + /** + * 'update' the scheduler. (You should NEVER call this method, unless you know what you are doing.) + * @param {Number} dt delta time + */ + update:function (dt) { + this._updateHashLocked = true; + if(this._timeScale !== 1) + dt *= this._timeScale; + + var i, list, len, entry; + + for(i=0,list=this._updatesNegList, len = list.length; i + * The scheduled method will be called every 'interval' seconds.
+ * If paused is YES, then it won't be called until it is resumed.
+ * If 'interval' is 0, it will be called every frame, but if so, it recommended to use 'scheduleUpdateForTarget:' instead.
+ * If the callback function is already scheduled, then only the interval parameter will be updated without re-scheduling it again.
+ * repeat let the action be repeated repeat + 1 times, use cc.REPEAT_FOREVER to let the action run continuously
+ * delay is the amount of time the action will wait before it'll start
+ *

+ * @deprecated since v3.4 please use .schedule + * @param {cc.Class} target + * @param {function} callback_fn + * @param {Number} interval + * @param {Number} repeat + * @param {Number} delay + * @param {Boolean} paused + * @example + * //register a schedule to scheduler + * cc.director.getScheduler().scheduleCallbackForTarget(this, function, interval, repeat, delay, !this._isRunning ); + */ + scheduleCallbackForTarget: function(target, callback_fn, interval, repeat, delay, paused){ + //cc.log("scheduleCallbackForTarget is deprecated. Please use schedule."); + this.schedule(callback_fn, target, interval, repeat, delay, paused, target.__instanceId + ""); + }, + + schedule: function (callback, target, interval, repeat, delay, paused, key) { + var isSelector = false; + if (typeof callback !== "function") { + var tmp = callback; + callback = target; + target = tmp; + isSelector = true; + } + //callback, target, interval, repeat, delay, paused, key + //callback, target, interval, paused, key + if(arguments.length === 4 || arguments.length === 5){ + key = delay; + paused = repeat; + repeat = cc.REPEAT_FOREVER; + delay = 0; + } + if (key === undefined) { + key = target.__instanceId + ""; + } + + cc.assert(target, cc._LogInfos.Scheduler_scheduleCallbackForTarget_3); + + var element = this._hashForTimers[target.__instanceId]; + + if (!element) { + // Is this the 1st element ? Then set the pause level to all the callback_fns of this target + element = HashTimerEntry.get(null, target, 0, null, null, paused); + this._arrayForTimers.push(element); + this._hashForTimers[target.__instanceId] = element; + } else { + cc.assert(element.paused === paused, ""); + } + + var timer, i; + if (element.timers == null) { + element.timers = []; + } else { + for (i = 0; i < element.timers.length; i++) { + timer = element.timers[i]; + if (callback === timer._callback) { + cc.log(cc._LogInfos.Scheduler_scheduleCallbackForTarget, timer.getInterval().toFixed(4), interval.toFixed(4)); + timer._interval = interval; + return; + } + } + } + + timer = CallbackTimer.get(); + timer.initWithCallback(this, callback, target, interval, repeat, delay, key); + element.timers.push(timer); + }, + + scheduleUpdate: function(target, priority, paused){ + this._schedulePerFrame(function(dt){ + target.update(dt); + }, target, priority, paused); + }, + + _getUnscheduleMark: function(key, timer){ + //key, callback + switch (typeof key){ + case "number": + case "string": + return key === timer._key; + case "function": + return key === timer._callback; + } + }, + unschedule: function (key, target) { + //key, target + //selector, target + //callback, target - This is in order to increase compatibility + + // explicity handle nil arguments when removing an object + if (!target || !key) + return; + + var self = this, element = self._hashForTimers[target.__instanceId]; + if (element) { + var timers = element.timers; + for(var i = 0, li = timers.length; i < li; i++){ + var timer = timers[i]; + if (this._getUnscheduleMark(key, timer)) { + if ((timer === element.currentTimer) && (!element.currentTimerSalvaged)) { + element.currentTimerSalvaged = true; + } + timers.splice(i, 1); + CallbackTimer.put(timer); + //update timerIndex in case we are in tick;, looping over the actions + if (element.timerIndex >= i) { + element.timerIndex--; + } + + if (timers.length === 0) { + if (self._currentTarget === element) { + self._currentTargetSalvaged = true; + } else { + self._removeHashElement(element); + } + } + return; + } + } + } + }, + + unscheduleUpdate: function (target) { + if (!target) + return; + + var element = this._hashForUpdates[target.__instanceId]; + + if (element) { + if (this._updateHashLocked) { + element.entry.markedForDeletion = true; + } else { + this._removeUpdateFromHash(element.entry); + } + } + }, + + unscheduleAllForTarget: function (target) { + // explicit nullptr handling + if (!target){ + return; + } + + // Custom Selectors + var element = this._hashForTimers[target.__instanceId]; + + if (element) { + var timers = element.timers; + if (timers.indexOf(element.currentTimer) > -1 && + (!element.currentTimerSalvaged)) { + element.currentTimerSalvaged = true; + } + for (var i = 0, l = timers.length; i < l; i++) { + CallbackTimer.put(timers[i]); + } + timers.length = 0; + + if (this._currentTarget === element){ + this._currentTargetSalvaged = true; + }else{ + this._removeHashElement(element); + } + } + + // update selector + this.unscheduleUpdate(target); + }, + + unscheduleAll: function(){ + this.unscheduleAllWithMinPriority(cc.Scheduler.PRIORITY_SYSTEM); + }, + + unscheduleAllWithMinPriority: function(minPriority){ + // Custom Selectors + var i, element, arr = this._arrayForTimers; + for(i=arr.length-1; i>=0; i--){ + element = arr[i]; + this.unscheduleAllForTarget(element.target); + } + + // Updates selectors + var entry; + var temp_length = 0; + if(minPriority < 0){ + for(i=0; i= minPriority) + this.unscheduleUpdate(entry.target); + if (temp_length == this._updatesNegList.length) + i++; + } + } + + if(minPriority <= 0){ + for(i=0; i= minPriority) + this.unscheduleUpdate(entry.target); + if (temp_length == this._updatesPosList.length) + i++; + } + }, + + isScheduled: function(callback, target){ + //key, target + //selector, target + cc.assert(callback, "Argument callback must not be empty"); + cc.assert(target, "Argument target must be non-nullptr"); + + var element = this._hashForTimers[target.__instanceId]; + + if (!element) { + return false; + } + + if (element.timers == null){ + return false; + } + else { + var timers = element.timers; + for (var i = 0; i < timers.length; ++i) { + var timer = timers[i]; + + if (callback === timer._callback){ + return true; + } + } + return false; + } + }, + + /** + *

+ * Pause all selectors from all targets.
+ * You should NEVER call this method, unless you know what you are doing. + *

+ */ + pauseAllTargets:function () { + return this.pauseAllTargetsWithMinPriority(cc.Scheduler.PRIORITY_SYSTEM); + }, + + /** + * Pause all selectors from all targets with a minimum priority.
+ * You should only call this with kCCPriorityNonSystemMin or higher. + * @param {Number} minPriority + */ + pauseAllTargetsWithMinPriority:function (minPriority) { + var idsWithSelectors = []; + + var self = this, element, locArrayForTimers = self._arrayForTimers; + var i, li; + // Custom Selectors + for(i = 0, li = locArrayForTimers.length; i < li; i++){ + element = locArrayForTimers[i]; + if (element) { + element.paused = true; + idsWithSelectors.push(element.target); + } + } + + var entry; + if(minPriority < 0){ + for(i=0; i= minPriority){ + entry.paused = true; + idsWithSelectors.push(entry.target); + } + } + } + } + + if(minPriority <= 0){ + for(i=0; i= minPriority){ + entry.paused = true; + idsWithSelectors.push(entry.target); + } + } + } + + return idsWithSelectors; + }, + + /** + * Resume selectors on a set of targets.
+ * This can be useful for undoing a call to pauseAllCallbacks. + * @param {Array} targetsToResume + */ + resumeTargets:function (targetsToResume) { + if (!targetsToResume) + return; + + for (var i = 0; i < targetsToResume.length; i++) { + this.resumeTarget(targetsToResume[i]); + } + }, + + /** + *

+ * Pauses the target.
+ * All scheduled selectors/update for a given target won't be 'ticked' until the target is resumed.
+ * If the target is not present, nothing happens. + *

+ * @param {cc.Class} target + */ + pauseTarget:function (target) { + + cc.assert(target, cc._LogInfos.Scheduler_pauseTarget); + + //customer selectors + var self = this, element = self._hashForTimers[target.__instanceId]; + if (element) { + element.paused = true; + } + + //update callback + var elementUpdate = self._hashForUpdates[target.__instanceId]; + if (elementUpdate) { + elementUpdate.entry.paused = true; + } + }, + + /** + * Resumes the target.
+ * The 'target' will be unpaused, so all schedule selectors/update will be 'ticked' again.
+ * If the target is not present, nothing happens. + * @param {cc.Class} target + */ + resumeTarget:function (target) { + + cc.assert(target, cc._LogInfos.Scheduler_resumeTarget); + + // custom selectors + var self = this, element = self._hashForTimers[target.__instanceId]; + + if (element) { + element.paused = false; + } + + //update callback + var elementUpdate = self._hashForUpdates[target.__instanceId]; + + if (elementUpdate) { + elementUpdate.entry.paused = false; + } + }, + + /** + * Returns whether or not the target is paused + * @param {cc.Class} target + * @return {Boolean} + */ + isTargetPaused:function (target) { + + cc.assert(target, cc._LogInfos.Scheduler_isTargetPaused); + + // Custom selectors + var element = this._hashForTimers[target.__instanceId]; + if (element) { + return element.paused; + } + var elementUpdate = this._hashForUpdates[target.__instanceId]; + if (elementUpdate) { + return elementUpdate.entry.paused; + } + return false; + }, + + /** + *

+ * Schedules the 'update' callback_fn for a given target with a given priority.
+ * The 'update' callback_fn will be called every frame.
+ * The lower the priority, the earlier it is called. + *

+ * @deprecated since v3.4 please use .scheduleUpdate + * @param {cc.Class} target + * @param {Number} priority + * @param {Boolean} paused + * @example + * //register this object to scheduler + * cc.director.getScheduler().scheduleUpdateForTarget(this, priority, !this._isRunning ); + */ + scheduleUpdateForTarget: function(target, priority, paused){ + //cc.log("scheduleUpdateForTarget is deprecated. Please use scheduleUpdate."); + this.scheduleUpdate(target, priority, paused); + }, + + /** + *

+ * Unschedule a callback function for a given target.
+ * If you want to unschedule the "update", use unscheudleUpdateForTarget. + *

+ * @deprecated since v3.4 please use .unschedule + * @param {cc.Class} target + * @param {function} callback callback[Function] or key[String] + * @example + * //unschedule a callback of target + * cc.director.getScheduler().unscheduleCallbackForTarget(function, this); + */ + unscheduleCallbackForTarget:function (target, callback) { + //cc.log("unscheduleCallbackForTarget is deprecated. Please use unschedule."); + this.unschedule(callback, target); + }, + + /** + * Unschedules the update callback function for a given target + * @param {cc.Class} target + * @deprecated since v3.4 please use .unschedule + * @example + * //unschedules the "update" method. + * cc.director.getScheduler().unscheduleUpdateForTarget(this); + */ + unscheduleUpdateForTarget:function (target) { + //cc.log("unscheduleUpdateForTarget is deprecated. Please use unschedule."); + this.unscheduleUpdate(target); + }, + + /** + * Unschedules all function callbacks for a given target. This also includes the "update" callback function. + * @deprecated since v3.4 please use .unscheduleAll + * @param {cc.Class} target + */ + unscheduleAllCallbacksForTarget: function(target){ + //cc.log("unscheduleAllCallbacksForTarget is deprecated. Please use unscheduleAll."); + this.unschedule(target.__instanceId + "", target); + }, + + /** + *

+ * Unschedules all function callbacks from all targets.
+ * You should NEVER call this method, unless you know what you are doing. + *

+ * @deprecated since v3.4 please use .unscheduleAllWithMinPriority + */ + unscheduleAllCallbacks: function(){ + //cc.log("unscheduleAllCallbacks is deprecated. Please use unscheduleAll."); + this.unscheduleAllWithMinPriority(cc.Scheduler.PRIORITY_SYSTEM); + }, + + /** + *

+ * Unschedules all function callbacks from all targets with a minimum priority.
+ * You should only call this with kCCPriorityNonSystemMin or higher. + *

+ * @deprecated since v3.4 please use .unscheduleAllWithMinPriority + * @param {Number} minPriority + */ + unscheduleAllCallbacksWithMinPriority:function (minPriority) { + //cc.log("unscheduleAllCallbacksWithMinPriority is deprecated. Please use unscheduleAllWithMinPriority."); + this.unscheduleAllWithMinPriority(minPriority); + } +}); + +/** + * Priority level reserved for system services. + * @constant + * @type Number + */ +cc.Scheduler.PRIORITY_SYSTEM = (-2147483647 - 1); + +/** + * Minimum priority level for user scheduling. + * @constant + * @type Number + */ +cc.Scheduler.PRIORITY_NON_SYSTEM = cc.Scheduler.PRIORITY_SYSTEM + 1; + +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/core/base-nodes/BaseNodesPropertyDefine.js b/frameworks/cocos2d-html5/cocos2d/core/base-nodes/BaseNodesPropertyDefine.js new file mode 100644 index 0000000..2b2639c --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/base-nodes/BaseNodesPropertyDefine.js @@ -0,0 +1,127 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +cc._tmp.PrototypeCCNode = function () { + + var _p = cc.Node.prototype; + + cc.defineGetterSetter(_p, "x", _p.getPositionX, _p.setPositionX); + cc.defineGetterSetter(_p, "y", _p.getPositionY, _p.setPositionY); + /** @expose */ + _p.width; + cc.defineGetterSetter(_p, "width", _p._getWidth, _p._setWidth); + /** @expose */ + _p.height; + cc.defineGetterSetter(_p, "height", _p._getHeight, _p._setHeight); + /** @expose */ + _p.anchorX; + cc.defineGetterSetter(_p, "anchorX", _p._getAnchorX, _p._setAnchorX); + /** @expose */ + _p.anchorY; + cc.defineGetterSetter(_p, "anchorY", _p._getAnchorY, _p._setAnchorY); + /** @expose */ + _p.skewX; + cc.defineGetterSetter(_p, "skewX", _p.getSkewX, _p.setSkewX); + /** @expose */ + _p.skewY; + cc.defineGetterSetter(_p, "skewY", _p.getSkewY, _p.setSkewY); + /** @expose */ + _p.zIndex; + cc.defineGetterSetter(_p, "zIndex", _p.getLocalZOrder, _p.setLocalZOrder); + /** @expose */ + _p.vertexZ; + cc.defineGetterSetter(_p, "vertexZ", _p.getVertexZ, _p.setVertexZ); + /** @expose */ + _p.rotation; + cc.defineGetterSetter(_p, "rotation", _p.getRotation, _p.setRotation); + /** @expose */ + _p.rotationX; + cc.defineGetterSetter(_p, "rotationX", _p.getRotationX, _p.setRotationX); + /** @expose */ + _p.rotationY; + cc.defineGetterSetter(_p, "rotationY", _p.getRotationY, _p.setRotationY); + /** @expose */ + _p.scale; + cc.defineGetterSetter(_p, "scale", _p.getScale, _p.setScale); + /** @expose */ + _p.scaleX; + cc.defineGetterSetter(_p, "scaleX", _p.getScaleX, _p.setScaleX); + /** @expose */ + _p.scaleY; + cc.defineGetterSetter(_p, "scaleY", _p.getScaleY, _p.setScaleY); + /** @expose */ + _p.children; + cc.defineGetterSetter(_p, "children", _p.getChildren); + /** @expose */ + _p.childrenCount; + cc.defineGetterSetter(_p, "childrenCount", _p.getChildrenCount); + /** @expose */ + _p.parent; + cc.defineGetterSetter(_p, "parent", _p.getParent, _p.setParent); + /** @expose */ + _p.visible; + cc.defineGetterSetter(_p, "visible", _p.isVisible, _p.setVisible); + /** @expose */ + _p.running; + cc.defineGetterSetter(_p, "running", _p.isRunning); + /** @expose */ + _p.ignoreAnchor; + cc.defineGetterSetter(_p, "ignoreAnchor", _p.isIgnoreAnchorPointForPosition, _p.ignoreAnchorPointForPosition); + /** @expose */ + _p.tag; + /** @expose */ + _p.userData; + /** @expose */ + _p.userObject; + /** @expose */ + _p.arrivalOrder; + /** @expose */ + _p.actionManager; + cc.defineGetterSetter(_p, "actionManager", _p.getActionManager, _p.setActionManager); + /** @expose */ + _p.scheduler; + cc.defineGetterSetter(_p, "scheduler", _p.getScheduler, _p.setScheduler); + //cc.defineGetterSetter(_p, "boundingBox", _p.getBoundingBox); + /** @expose */ + _p.shaderProgram; + cc.defineGetterSetter(_p, "shaderProgram", _p.getShaderProgram, _p.setShaderProgram); + + /** @expose */ + _p.opacity; + cc.defineGetterSetter(_p, "opacity", _p.getOpacity, _p.setOpacity); + /** @expose */ + _p.opacityModifyRGB; + cc.defineGetterSetter(_p, "opacityModifyRGB", _p.isOpacityModifyRGB); + /** @expose */ + _p.cascadeOpacity; + cc.defineGetterSetter(_p, "cascadeOpacity", _p.isCascadeOpacityEnabled, _p.setCascadeOpacityEnabled); + /** @expose */ + _p.color; + cc.defineGetterSetter(_p, "color", _p.getColor, _p.setColor); + /** @expose */ + _p.cascadeColor; + cc.defineGetterSetter(_p, "cascadeColor", _p.isCascadeColorEnabled, _p.setCascadeColorEnabled); +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/cocos2d/core/base-nodes/CCAtlasNode.js b/frameworks/cocos2d-html5/cocos2d/core/base-nodes/CCAtlasNode.js new file mode 100644 index 0000000..336ab7c --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/base-nodes/CCAtlasNode.js @@ -0,0 +1,295 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + *

cc.AtlasNode is a subclass of cc.Node, it knows how to render a TextureAtlas object.

+ * + *

If you are going to render a TextureAtlas consider subclassing cc.AtlasNode (or a subclass of cc.AtlasNode)

+ * + *

All features from cc.Node are valid

+ * + *

You can create a cc.AtlasNode with an Atlas file, the width, the height of each item and the quantity of items to render

+ * + * @class + * @extends cc.Node + * + * @param {String} tile + * @param {Number} tileWidth + * @param {Number} tileHeight + * @param {Number} itemsToRender + * @example + * var node = new cc.AtlasNode("pathOfTile", 16, 16, 1); + * + * @property {cc.Texture2D} texture - Current used texture + * @property {cc.TextureAtlas} textureAtlas - Texture atlas for cc.AtlasNode + * @property {Number} quadsToDraw - Number of quads to draw + */ +cc.AtlasNode = cc.Node.extend(/** @lends cc.AtlasNode# */{ + textureAtlas: null, + quadsToDraw: 0, + + //! chars per row + _itemsPerRow: 0, + //! chars per column + _itemsPerColumn: 0, + //! width of each char + _itemWidth: 0, + //! height of each char + _itemHeight: 0, + + // protocol variables + _opacityModifyRGB: false, + _blendFunc: null, + + // This variable is only used for CCLabelAtlas FPS display. So plz don't modify its value. + _ignoreContentScaleFactor: false, + _className: "AtlasNode", + + _texture: null, + _textureForCanvas: null, + + /** + *

Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.

+ * @param {String} tile + * @param {Number} tileWidth + * @param {Number} tileHeight + * @param {Number} itemsToRender + */ + ctor: function (tile, tileWidth, tileHeight, itemsToRender) { + cc.Node.prototype.ctor.call(this); + this._blendFunc = {src: cc.BLEND_SRC, dst: cc.BLEND_DST}; + this._ignoreContentScaleFactor = false; + itemsToRender !== undefined && this.initWithTileFile(tile, tileWidth, tileHeight, itemsToRender); + }, + + _createRenderCmd: function(){ + if(cc._renderType === cc.game.RENDER_TYPE_CANVAS) + this._renderCmd = new cc.AtlasNode.CanvasRenderCmd(this); + else + this._renderCmd = new cc.AtlasNode.WebGLRenderCmd(this); + }, + + /** + * Updates the Atlas (indexed vertex array). + * Empty implementation, shall be overridden in subclasses + * @function + */ + updateAtlasValues: function () { + cc.log(cc._LogInfos.AtlasNode_updateAtlasValues); + }, + + /** + * Get color value of the atlas node + * @function + * @return {cc.Color} + */ + getColor: function () { + if (this._opacityModifyRGB) + return this._renderCmd._colorUnmodified; + return cc.Node.prototype.getColor.call(this); + }, + + /** + * Set whether color should be changed with the opacity value, + * if true, node color will change while opacity changes. + * @function + * @param {Boolean} value + */ + setOpacityModifyRGB: function (value) { + var oldColor = this.color; + this._opacityModifyRGB = value; + this.setColor(oldColor); + }, + + /** + * Get whether color should be changed with the opacity value + * @function + * @return {Boolean} + */ + isOpacityModifyRGB: function () { + return this._opacityModifyRGB; + }, + + /** + * Get node's blend function + * @function + * @return {cc.BlendFunc} + */ + getBlendFunc: function () { + return this._blendFunc; + }, + + /** + * Set node's blend function + * This function accept either cc.BlendFunc object or source value and destination value + * @function + * @param {Number | cc.BlendFunc} src + * @param {Number} dst + */ + setBlendFunc: function (src, dst) { + if (dst === undefined) + this._blendFunc = src; + else + this._blendFunc = {src: src, dst: dst}; + }, + + /** + * Set the atlas texture + * @function + * @param {cc.TextureAtlas} value The texture + */ + setTextureAtlas: function (value) { + this.textureAtlas = value; + }, + + /** + * Get the atlas texture + * @function + * @return {cc.TextureAtlas} + */ + getTextureAtlas: function () { + return this.textureAtlas; + }, + + /** + * Get the number of quads to be rendered + * @function + * @return {Number} + */ + getQuadsToDraw: function () { + return this.quadsToDraw; + }, + + /** + * Set the number of quads to be rendered + * @function + * @param {Number} quadsToDraw + */ + setQuadsToDraw: function (quadsToDraw) { + this.quadsToDraw = quadsToDraw; + }, + + /** + * Initializes an cc.AtlasNode object with an atlas texture file name, the width, the height of each tile and the quantity of tiles to render + * @function + * @param {String} tile The atlas texture file name + * @param {Number} tileWidth The width of each tile + * @param {Number} tileHeight The height of each tile + * @param {Number} itemsToRender The quantity of tiles to be rendered + * @return {Boolean} + */ + initWithTileFile: function (tile, tileWidth, tileHeight, itemsToRender) { + if (!tile) + throw new Error("cc.AtlasNode.initWithTileFile(): title should not be null"); + var texture = cc.textureCache.addImage(tile); + return this.initWithTexture(texture, tileWidth, tileHeight, itemsToRender); + }, + + /** + * Initializes an CCAtlasNode with an atlas texture, the width, the height of each tile and the quantity of tiles to render + * @function + * @param {cc.Texture2D} texture The atlas texture + * @param {Number} tileWidth The width of each tile + * @param {Number} tileHeight The height of each tile + * @param {Number} itemsToRender The quantity of tiles to be rendered + * @return {Boolean} + */ + initWithTexture: function(texture, tileWidth, tileHeight, itemsToRender){ + return this._renderCmd.initWithTexture(texture, tileWidth, tileHeight, itemsToRender); + }, + + /** + * Set node's color + * @function + * @param {cc.Color} color Color object created with cc.color(r, g, b). + */ + setColor: function(color){ + this._renderCmd.setColor(color); + }, + + /** + * Set node's opacity + * @function + * @param {Number} opacity The opacity value + */ + setOpacity: function (opacity) { + this._renderCmd.setOpacity(opacity); + }, + + /** + * Get the current texture + * @function + * @return {cc.Texture2D} + */ + getTexture: function(){ + return this._texture; + }, + + /** + * Replace the current texture with a new one + * @function + * @param {cc.Texture2D} texture The new texture + */ + setTexture: function(texture){ + this._texture = texture; + }, + + _setIgnoreContentScaleFactor: function (ignoreContentScaleFactor) { + this._ignoreContentScaleFactor = ignoreContentScaleFactor; + } +}); + + +var _p = cc.AtlasNode.prototype; +// Override properties +cc.defineGetterSetter(_p, "opacity", _p.getOpacity, _p.setOpacity); +cc.defineGetterSetter(_p, "color", _p.getColor, _p.setColor); + +// Extended properties +/** @expose */ +_p.texture; +cc.defineGetterSetter(_p, "texture", _p.getTexture, _p.setTexture); +/** @expose */ +_p.textureAtlas; +/** @expose */ +_p.quadsToDraw; + +cc.EventHelper.prototype.apply(_p); + +/** + * Creates a cc.AtlasNode with an Atlas file the width and height of each item and the quantity of items to render + * @deprecated since v3.0, please use new construction instead + * @function + * @static + * @param {String} tile + * @param {Number} tileWidth + * @param {Number} tileHeight + * @param {Number} itemsToRender + * @return {cc.AtlasNode} + */ +cc.AtlasNode.create = function (tile, tileWidth, tileHeight, itemsToRender) { + return new cc.AtlasNode(tile, tileWidth, tileHeight, itemsToRender); +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/cocos2d/core/base-nodes/CCAtlasNodeCanvasRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/core/base-nodes/CCAtlasNodeCanvasRenderCmd.js new file mode 100644 index 0000000..4fb324f --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/base-nodes/CCAtlasNodeCanvasRenderCmd.js @@ -0,0 +1,91 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * cc.AtlasNode's rendering objects of Canvas + */ +(function () { + cc.AtlasNode.CanvasRenderCmd = function (renderableObject) { + this._rootCtor(renderableObject); + this._needDraw = false; + this._colorUnmodified = cc.color.WHITE; + this._textureToRender = null; + }; + + var proto = cc.AtlasNode.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + proto.constructor = cc.AtlasNode.CanvasRenderCmd; + + proto.initWithTexture = function (texture, tileWidth, tileHeight, itemsToRender) { + var node = this._node; + node._itemWidth = tileWidth; + node._itemHeight = tileHeight; + + node._opacityModifyRGB = true; + node._texture = texture; + if (!node._texture) { + cc.log(cc._LogInfos.AtlasNode__initWithTexture); + return false; + } + this._textureToRender = texture; + this._calculateMaxItems(); + + node.quadsToDraw = itemsToRender; + return true; + }; + + proto.setColor = function (color3) { + var node = this._node; + var locRealColor = node._realColor; + if ((locRealColor.r === color3.r) && (locRealColor.g === color3.g) && (locRealColor.b === color3.b)) + return; + this._colorUnmodified = color3; + this._changeTextureColor(); + }; + + proto._changeTextureColor = function () { + var node = this._node; + var texture = node._texture, + color = this._colorUnmodified, + element = texture.getHtmlElementObj(); + var textureRect = cc.rect(0, 0, element.width, element.height); + if (texture === this._textureToRender) + this._textureToRender = texture._generateColorTexture(color.r, color.g, color.b, textureRect); + else + texture._generateColorTexture(color.r, color.g, color.b, textureRect, this._textureToRender.getHtmlElementObj()); + }; + + proto.setOpacity = function (opacity) { + var node = this._node; + cc.Node.prototype.setOpacity.call(node, opacity); + }; + + proto._calculateMaxItems = function () { + var node = this._node; + var selTexture = node._texture; + var size = selTexture.getContentSize(); + + node._itemsPerColumn = 0 | (size.height / node._itemHeight); + node._itemsPerRow = 0 | (size.width / node._itemWidth); + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/core/base-nodes/CCAtlasNodeWebGLRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/core/base-nodes/CCAtlasNodeWebGLRenderCmd.js new file mode 100644 index 0000000..5810d00 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/base-nodes/CCAtlasNodeWebGLRenderCmd.js @@ -0,0 +1,159 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * cc.AtlasNode's rendering objects of WebGL + */ +(function () { + cc.AtlasNode.WebGLRenderCmd = function (renderableObject) { + this._rootCtor(renderableObject); + this._needDraw = true; + this._textureAtlas = null; + this._colorUnmodified = cc.color.WHITE; + this._colorF32Array = null; + this._uniformColor = null; + + this._matrix = new cc.math.Matrix4(); + this._matrix.identity(); + + //shader stuff + this._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURE_UCOLOR); + this._uniformColor = cc._renderContext.getUniformLocation(this._shaderProgram.getProgram(), "u_color"); + }; + + var proto = cc.AtlasNode.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + proto.constructor = cc.AtlasNode.WebGLRenderCmd; + + proto._updateBlendFunc = function () { + var node = this._node; + if (!this._textureAtlas.texture.hasPremultipliedAlpha()) { + node._blendFunc.src = cc.SRC_ALPHA; + node._blendFunc.dst = cc.ONE_MINUS_SRC_ALPHA; + } + }; + + proto._updateOpacityModifyRGB = function () { + this._node._opacityModifyRGB = this._textureAtlas.texture.hasPremultipliedAlpha(); + }; + + proto.rendering = function (ctx) { + var context = ctx || cc._renderContext, node = this._node; + + var wt = this._worldTransform; + this._matrix.mat[0] = wt.a; + this._matrix.mat[4] = wt.c; + this._matrix.mat[12] = wt.tx; + this._matrix.mat[1] = wt.b; + this._matrix.mat[5] = wt.d; + this._matrix.mat[13] = wt.ty; + + this._glProgramState.apply(this._matrix); + + cc.glBlendFunc(node._blendFunc.src, node._blendFunc.dst); + if (this._uniformColor && this._colorF32Array) { + context.uniform4fv(this._uniformColor, this._colorF32Array); + this._textureAtlas.drawNumberOfQuads(node.quadsToDraw, 0); + } + }; + + proto.initWithTexture = function (texture, tileWidth, tileHeight, itemsToRender) { + var node = this._node; + node._itemWidth = tileWidth; + node._itemHeight = tileHeight; + this._colorUnmodified = cc.color.WHITE; + node._opacityModifyRGB = true; + + node._blendFunc.src = cc.BLEND_SRC; + node._blendFunc.dst = cc.BLEND_DST; + + var locRealColor = node._realColor; + this._colorF32Array = new Float32Array([locRealColor.r / 255.0, locRealColor.g / 255.0, locRealColor.b / 255.0, node._realOpacity / 255.0]); + this._textureAtlas = new cc.TextureAtlas(); + this._textureAtlas.initWithTexture(texture, itemsToRender); + + if (!this._textureAtlas) { + cc.log(cc._LogInfos.AtlasNode__initWithTexture); + return false; + } + + this._updateBlendFunc(); + this._updateOpacityModifyRGB(); + this._calculateMaxItems(); + node.quadsToDraw = itemsToRender; + + return true; + }; + + proto.setColor = function (color3) { + var temp = cc.color(color3.r, color3.g, color3.b), node = this._node; + this._colorUnmodified = color3; + var locDisplayedOpacity = this._displayedOpacity; + if (node._opacityModifyRGB) { + temp.r = temp.r * locDisplayedOpacity / 255; + temp.g = temp.g * locDisplayedOpacity / 255; + temp.b = temp.b * locDisplayedOpacity / 255; + } + cc.Node.prototype.setColor.call(node, temp); + }; + + proto.setOpacity = function (opacity) { + var node = this._node; + cc.Node.prototype.setOpacity.call(node, opacity); + // special opacity for premultiplied textures + if (node._opacityModifyRGB) { + node.color = this._colorUnmodified; + } + }; + + proto._updateColor = function () { + if (this._colorF32Array) { + var locDisplayedColor = this._displayedColor; + this._colorF32Array[0] = locDisplayedColor.r / 255.0; + this._colorF32Array[1] = locDisplayedColor.g / 255.0; + this._colorF32Array[2] = locDisplayedColor.b / 255.0; + this._colorF32Array[3] = this._displayedOpacity / 255.0; + } + }; + + proto.getTexture = function () { + return this._textureAtlas.texture; + }; + + proto.setTexture = function (texture) { + this._textureAtlas.texture = texture; + this._updateBlendFunc(); + this._updateOpacityModifyRGB(); + }; + + proto._calculateMaxItems = function () { + var node = this._node; + var selTexture = this._textureAtlas.texture; + var size = selTexture.getContentSize(); + if (node._ignoreContentScaleFactor) + size = selTexture.getContentSizeInPixels(); + + node._itemsPerColumn = 0 | (size.height / node._itemHeight); + node._itemsPerRow = 0 | (size.width / node._itemWidth); + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/core/base-nodes/CCNode.js b/frameworks/cocos2d-html5/cocos2d/core/base-nodes/CCNode.js new file mode 100644 index 0000000..9d3e6f1 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/base-nodes/CCNode.js @@ -0,0 +1,2604 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * Default Node tag + * @constant + * @type Number + */ +cc.NODE_TAG_INVALID = -1; + +/** + * XXX: Yes, nodes might have a sort problem once every 15 days if the game runs at 60 FPS and each frame sprites are reordered. + */ +cc.s_globalOrderOfArrival = 1; + +/** + *

cc.Node is the root class of all node. Anything that gets drawn or contains things that get drawn is a cc.Node.
+ * The most popular cc.Nodes are: cc.Scene, cc.Layer, cc.Sprite, cc.Menu.

+ * + *

The main features of a cc.Node are:
+ * - They can contain other cc.Node nodes (addChild, getChildByTag, removeChild, etc)
+ * - They can schedule periodic callback (schedule, unschedule, etc)
+ * - They can execute actions (runAction, stopAction, etc)

+ * + *

Some cc.Node nodes provide extra functionality for them or their children.

+ * + *

Subclassing a cc.Node usually means (one/all) of:
+ * - overriding constructor function "ctor" to initialize resources and schedule callbacks
+ * - create callbacks to handle the advancement of time

+ * + *

Features of cc.Node:
+ * - position
+ * - scale (x, y)
+ * - rotation (in degrees, clockwise)
+ * - anchor point
+ * - size
+ * - color
+ * - opacity
+ * - visible
+ * - z-order
+ * - WebGL z position

+ * + *

Default values:
+ * - rotation: 0
+ * - position: (x=0,y=0)
+ * - scale: (x=1,y=1)
+ * - contentSize: (x=0,y=0)
+ * - anchorPoint: (x=0,y=0)
+ * - color: (r=255,g=255,b=255)
+ * - opacity: 255

+ * + *

Limitations:
+ * - A cc.Node is a "void" object. It doesn't have a texture

+ * + *

Order in transformations with grid disabled
+ * -# The node will be translated (position)
+ * -# The node will be rotated (rotation)
+ * -# The node will be scaled (scale)
+ * + *

Order in transformations with grid enabled
+ * -# The node will be translated (position)
+ * -# The node will be rotated (rotation)
+ * -# The node will be scaled (scale)
+ * -# The grid will capture the screen
+ * -# The grid will render the captured screen

+ * + * @class + * @extends cc.Class + * + * @property {Number} x - x axis position of node + * @property {Number} y - y axis position of node + * @property {Number} width - Width of node + * @property {Number} height - Height of node + * @property {Number} anchorX - Anchor point's position on x axis + * @property {Number} anchorY - Anchor point's position on y axis + * @property {Boolean} ignoreAnchor - Indicate whether ignore the anchor point property for positioning + * @property {Number} skewX - Skew x + * @property {Number} skewY - Skew y + * @property {Number} zIndex - Z order in depth which stands for the drawing order + * @property {Number} vertexZ - WebGL Z vertex of this node, z order works OK if all the nodes uses the same openGL Z vertex + * @property {Number} rotation - Rotation of node + * @property {Number} rotationX - Rotation on x axis + * @property {Number} rotationY - Rotation on y axis + * @property {Number} scale - Scale of node + * @property {Number} scaleX - Scale on x axis + * @property {Number} scaleY - Scale on y axis + * @property {Boolean} visible - Indicate whether node is visible or not + * @property {cc.Color} color - Color of node, default value is white: (255, 255, 255) + * @property {Boolean} cascadeColor - Indicate whether node's color value affect its child nodes, default value is false + * @property {Number} opacity - Opacity of node, default value is 255 + * @property {Boolean} opacityModifyRGB - Indicate whether opacity affect the color value, default value is false + * @property {Boolean} cascadeOpacity - Indicate whether node's opacity value affect its child nodes, default value is false + * @property {Array} children - <@readonly> All children nodes + * @property {Number} childrenCount - <@readonly> Number of children + * @property {cc.Node} parent - Parent node + * @property {Boolean} running - <@readonly> Indicate whether node is running or not + * @property {Number} tag - Tag of node + * @property {Object} userData - Custom user data + * @property {Object} userObject - User assigned CCObject, similar to userData, but instead of holding a void* it holds an id + * @property {Number} arrivalOrder - The arrival order, indicates which children is added previously + * @property {cc.ActionManager} actionManager - The CCActionManager object that is used by all actions. + * @property {cc.Scheduler} scheduler - cc.Scheduler used to schedule all "updates" and timers. + * @property {cc.GridBase} grid - grid object that is used when applying effects + * @property {cc.GLProgram} shaderProgram - The shader program currently used for this node + * @property {Number} glServerState - The state of OpenGL server side + */ +cc.Node = cc.Class.extend(/** @lends cc.Node# */{ + _localZOrder: 0, ///< Local order (relative to its siblings) used to sort the node + _globalZOrder: 0, ///< Global order used to sort the node + _vertexZ: 0.0, + _customZ: NaN, + + _rotationX: 0, + _rotationY: 0.0, + _scaleX: 1.0, + _scaleY: 1.0, + _position: null, + + _normalizedPosition:null, + _usingNormalizedPosition: false, + _normalizedPositionDirty: false, + + _skewX: 0.0, + _skewY: 0.0, + // children (lazy allocs), + _children: null, + // lazy alloc, + _visible: true, + _anchorPoint: null, + _contentSize: null, + _running: false, + _parent: null, + + // "whole screen" objects. like Scenes and Layers, should set _ignoreAnchorPointForPosition to true + _ignoreAnchorPointForPosition: false, + tag: cc.NODE_TAG_INVALID, + // userData is always initialized as nil + userData: null, + userObject: null, + + //since 2.0 api + _reorderChildDirty: false, + arrivalOrder: 0, + + _actionManager: null, + _scheduler: null, + + _additionalTransformDirty: false, + _additionalTransform: null, + _componentContainer: null, + _isTransitionFinished: false, + + _className: "Node", + _showNode: false, + _name: "", ///Properties configuration function
+ * All properties in attrs will be set to the node,
+ * when the setter of the node is available,
+ * the property will be set via setter function.
+ *

+ * @function + * @param {Object} attrs Properties to be set to node + */ + attr: function (attrs) { + for (var key in attrs) { + this[key] = attrs[key]; + } + }, + + /** + *

Returns the skew degrees in X
+ * The X skew angle of the node in degrees.
+ * This angle describes the shear distortion in the X direction.
+ * Thus, it is the angle between the Y axis and the left edge of the shape
+ * The default skewX angle is 0. Positive values distort the node in a CW direction.
+ *

+ * @function + * @return {Number} The X skew angle of the node in degrees. + */ + getSkewX: function () { + return this._skewX; + }, + + /** + *

+ * Changes the X skew angle of the node in degrees.
+ *
+ * This angle describes the shear distortion in the X direction.
+ * Thus, it is the angle between the Y axis and the left edge of the shape
+ * The default skewX angle is 0. Positive values distort the node in a CW direction. + *

+ * @function + * @param {Number} newSkewX The X skew angle of the node in degrees. + */ + setSkewX: function (newSkewX) { + this._skewX = newSkewX; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }, + + /** + *

Returns the skew degrees in Y
+ * The Y skew angle of the node in degrees.
+ * This angle describes the shear distortion in the Y direction.
+ * Thus, it is the angle between the X axis and the bottom edge of the shape
+ * The default skewY angle is 0. Positive values distort the node in a CCW direction.
+ *

+ * @function + * @return {Number} The Y skew angle of the node in degrees. + */ + getSkewY: function () { + return this._skewY; + }, + + /** + *

+ * Changes the Y skew angle of the node in degrees.
+ *
+ * This angle describes the shear distortion in the Y direction.
+ * Thus, it is the angle between the X axis and the bottom edge of the shape
+ * The default skewY angle is 0. Positive values distort the node in a CCW direction.
+ *

+ * @function + * @param {Number} newSkewY The Y skew angle of the node in degrees. + */ + setSkewY: function (newSkewY) { + this._skewY = newSkewY; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }, + + /** + *

LocalZOrder is the 'key' used to sort the node relative to its siblings.
+ *
+ * The Node's parent will sort all its children based ont the LocalZOrder value.
+ * If two nodes have the same LocalZOrder, then the node that was added first to the children's array
+ * will be in front of the other node in the array.
+ *
+ * Also, the Scene Graph is traversed using the "In-Order" tree traversal algorithm ( http://en.wikipedia.org/wiki/Tree_traversal#In-order ) + *
+ * And Nodes that have LocalZOder values < 0 are the "left" subtree
+ * While Nodes with LocalZOder >=0 are the "right" subtree.

+ * @function + * @param {Number} localZOrder + */ + setLocalZOrder: function (localZOrder) { + if (localZOrder === this._localZOrder) + return; + if (this._parent) + this._parent.reorderChild(this, localZOrder); + else + this._localZOrder = localZOrder; + cc.eventManager._setDirtyForNode(this); + }, + + //Helper function used by `setLocalZOrder`. Don't use it unless you know what you are doing. + _setLocalZOrder: function (localZOrder) { + this._localZOrder = localZOrder; + }, + + /** + * Returns the local Z order of this node. + * @function + * @returns {Number} The local (relative to its siblings) Z order. + */ + getLocalZOrder: function () { + return this._localZOrder; + }, + + /** + * Returns z order of this node + * @function + * @return {Number} + * @deprecated since 3.0, please use getLocalZOrder instead + */ + getZOrder: function () { + cc.log(cc._LogInfos.Node_getZOrder); + return this.getLocalZOrder(); + }, + + /** + *

+ * Sets the Z order which stands for the drawing order, and reorder this node in its parent's children array.
+ *
+ * The Z order of node is relative to its "brothers": children of the same parent.
+ * It's nothing to do with OpenGL's z vertex. This one only affects the draw order of nodes in cocos2d.
+ * The larger number it is, the later this node will be drawn in each message loop.
+ * Please refer to setVertexZ(float) for the difference. + *

+ * @function + * @param {Number} z Z order of this node. + * @deprecated since 3.0, please use setLocalZOrder instead + */ + setZOrder: function (z) { + cc.log(cc._LogInfos.Node_setZOrder); + this.setLocalZOrder(z); + }, + + /** + *

Defines the oder in which the nodes are renderer.
+ * Nodes that have a Global Z Order lower, are renderer first.
+ *
+ * In case two or more nodes have the same Global Z Order, the oder is not guaranteed.
+ * The only exception if the Nodes have a Global Z Order == 0. In that case, the Scene Graph order is used.
+ *
+ * By default, all nodes have a Global Z Order = 0. That means that by default, the Scene Graph order is used to render the nodes.
+ *
+ * Global Z Order is useful when you need to render nodes in an order different than the Scene Graph order.
+ *
+ * Limitations: Global Z Order can't be used used by Nodes that have SpriteBatchNode as one of their ancestors.
+ * And if ClippingNode is one of the ancestors, then "global Z order" will be relative to the ClippingNode.

+ * @function + * @param {Number} globalZOrder + */ + setGlobalZOrder: function (globalZOrder) { + if (this._globalZOrder !== globalZOrder) { + this._globalZOrder = globalZOrder; + cc.eventManager._setDirtyForNode(this); + } + }, + + /** + * Return the Node's Global Z Order. + * @function + * @returns {number} The node's global Z order + */ + getGlobalZOrder: function () { + return this._globalZOrder; + }, + + /** + * Returns WebGL Z vertex of this node. + * @function + * @return {Number} WebGL Z vertex of this node + */ + getVertexZ: function () { + return this._vertexZ; + }, + + /** + *

+ * Sets the real WebGL Z vertex.
+ *
+ * Differences between openGL Z vertex and cocos2d Z order:
+ * - WebGL Z modifies the Z vertex, and not the Z order in the relation between parent-children
+ * - WebGL Z might require to set 2D projection
+ * - cocos2d Z order works OK if all the nodes uses the same WebGL Z vertex. eg: vertexZ = 0
+ *
+ * @warning Use it at your own risk since it might break the cocos2d parent-children z order + *

+ * @function + * @param {Number} Var + */ + setVertexZ: function (Var) { + this._customZ = this._vertexZ = Var; + }, + + /** + * Returns the rotation (angle) of the node in degrees. 0 is the default rotation angle. Positive values rotate node clockwise. + * @function + * @return {Number} The rotation of the node in degrees. + */ + getRotation: function () { + if (this._rotationX !== this._rotationY) + cc.log(cc._LogInfos.Node_getRotation); + return this._rotationX; + }, + + /** + *

+ * Sets the rotation (angle) of the node in degrees.
+ *
+ * 0 is the default rotation angle.
+ * Positive values rotate node clockwise, and negative values for anti-clockwise. + *

+ * @function + * @param {Number} newRotation The rotation of the node in degrees. + */ + setRotation: function (newRotation) { + this._rotationX = this._rotationY = newRotation; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }, + + /** + * Returns the X axis rotation (angle) which represent a horizontal rotational skew of the node in degrees.
+ * 0 is the default rotation angle. Positive values rotate node clockwise
+ * (support only in WebGL rendering mode) + * @function + * @return {Number} The X rotation in degrees. + */ + getRotationX: function () { + return this._rotationX; + }, + + /** + *

+ * Sets the X rotation (angle) of the node in degrees which performs a horizontal rotational skew.
+ * (support only in WebGL rendering mode)
+ * 0 is the default rotation angle.
+ * Positive values rotate node clockwise, and negative values for anti-clockwise. + *

+ * @param {Number} rotationX The X rotation in degrees which performs a horizontal rotational skew. + */ + setRotationX: function (rotationX) { + this._rotationX = rotationX; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }, + + /** + * Returns the Y axis rotation (angle) which represent a vertical rotational skew of the node in degrees.
+ * 0 is the default rotation angle. Positive values rotate node clockwise
+ * (support only in WebGL rendering mode) + * @function + * @return {Number} The Y rotation in degrees. + */ + getRotationY: function () { + return this._rotationY; + }, + + /** + *

+ * Sets the Y rotation (angle) of the node in degrees which performs a vertical rotational skew.
+ * (support only in WebGL rendering mode)
+ * 0 is the default rotation angle.
+ * Positive values rotate node clockwise, and negative values for anti-clockwise. + *

+ * @param rotationY The Y rotation in degrees. + */ + setRotationY: function (rotationY) { + this._rotationY = rotationY; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }, + + /** + * Returns the scale factor of the node. + * @warning: Assertion will fail when _scaleX != _scaleY. + * @function + * @return {Number} The scale factor + */ + getScale: function () { + if (this._scaleX !== this._scaleY) + cc.log(cc._LogInfos.Node_getScale); + return this._scaleX; + }, + + /** + * Sets the scale factor of the node. 1.0 is the default scale factor. This function can modify the X and Y scale at the same time. + * @function + * @param {Number} scale or scaleX value + * @param {Number} [scaleY=] + */ + setScale: function (scale, scaleY) { + this._scaleX = scale; + this._scaleY = (scaleY || scaleY === 0) ? scaleY : scale; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }, + + /** + * Returns the scale factor on X axis of this node + * @function + * @return {Number} The scale factor on X axis. + */ + getScaleX: function () { + return this._scaleX; + }, + + /** + *

+ * Changes the scale factor on X axis of this node
+ * The default value is 1.0 if you haven't changed it before + *

+ * @function + * @param {Number} newScaleX The scale factor on X axis. + */ + setScaleX: function (newScaleX) { + this._scaleX = newScaleX; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }, + + /** + * Returns the scale factor on Y axis of this node + * @function + * @return {Number} The scale factor on Y axis. + */ + getScaleY: function () { + return this._scaleY; + }, + + /** + *

+ * Changes the scale factor on Y axis of this node
+ * The Default value is 1.0 if you haven't changed it before. + *

+ * @function + * @param {Number} newScaleY The scale factor on Y axis. + */ + setScaleY: function (newScaleY) { + this._scaleY = newScaleY; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }, + + /** + *

+ * Changes the position (x,y) of the node in cocos2d coordinates.
+ * The original point (0,0) is at the left-bottom corner of screen.
+ * Usually we use cc.p(x,y) to compose CCPoint object.
+ * and Passing two numbers (x,y) is more efficient than passing CCPoint object. + *

+ * @function + * @param {cc.Point|Number} newPosOrxValue The position (x,y) of the node in coordinates or the X coordinate for position + * @param {Number} [yValue] Y coordinate for position + * @example + * var size = cc.winSize; + * node.setPosition(size.width/2, size.height/2); + */ + setPosition: function (newPosOrxValue, yValue) { + var locPosition = this._position; + if (yValue === undefined) { + if (locPosition.x === newPosOrxValue.x && locPosition.y === newPosOrxValue.y) + return; + locPosition.x = newPosOrxValue.x; + locPosition.y = newPosOrxValue.y; + } else { + if (locPosition.x === newPosOrxValue && locPosition.y === yValue) + return; + locPosition.x = newPosOrxValue; + locPosition.y = yValue; + } + this._usingNormalizedPosition = false; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }, + + /** + *

+ * Sets the position (x,y) using values between 0 and 1.
+ * The positions in pixels is calculated like the following:
+ * _position = _normalizedPosition * parent.getContentSize() + *

+ * @param {cc.Point|Number} posOrX + * @param {Number} [y] + */ + setNormalizedPosition: function(posOrX, y){ + var locPosition = this._normalizedPosition; + if (y === undefined) { + locPosition.x = posOrX.x; + locPosition.y = posOrX.y; + } else { + locPosition.x = posOrX; + locPosition.y = y; + } + this._normalizedPositionDirty = this._usingNormalizedPosition = true; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }, + + /** + *

Returns a copy of the position (x,y) of the node in cocos2d coordinates. (0,0) is the left-bottom corner.

+ * @function + * @return {cc.Point} The position (x,y) of the node in OpenGL coordinates + */ + getPosition: function () { + return cc.p(this._position); + }, + + /** + * returns the normalized position + * @returns {cc.Point} + */ + getNormalizedPosition: function(){ + return cc.p(this._normalizedPosition); + }, + + /** + *

Returns the x axis position of the node in cocos2d coordinates.

+ * @function + * @return {Number} + */ + getPositionX: function () { + return this._position.x; + }, + + /** + *

Sets the x axis position of the node in cocos2d coordinates.

+ * @function + * @param {Number} x The new position in x axis + */ + setPositionX: function (x) { + this._position.x = x; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }, + + /** + *

Returns the y axis position of the node in cocos2d coordinates.

+ * @function + * @return {Number} + */ + getPositionY: function () { + return this._position.y; + }, + + /** + *

Sets the y axis position of the node in cocos2d coordinates.

+ * @function + * @param {Number} y The new position in y axis + */ + setPositionY: function (y) { + this._position.y = y; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }, + + /** + * Returns the amount of children. + * @function + * @return {Number} The amount of children. + */ + getChildrenCount: function () { + return this._children.length; + }, + + /** + * Returns an array of all children
+ * Composing a "tree" structure is a very important feature of CCNode + * @function + * @return {Array} An array of children + * @example + * //This sample code traverses all children nodes, and set their position to (0,0) + * var allChildren = parent.getChildren(); + * for(var i = 0; i< allChildren.length; i++) { + * allChildren[i].setPosition(0,0); + * } + */ + getChildren: function () { + return this._children; + }, + + /** + * Returns if the node is visible + * @function + * @see cc.Node#setVisible + * @return {Boolean} true if the node is visible, false if the node is hidden. + */ + isVisible: function () { + return this._visible; + }, + + /** + * Sets whether the node is visible
+ * The default value is true + * @function + * @param {Boolean} visible Pass true to make the node visible, false to hide the node. + */ + setVisible: function (visible) { + if (this._visible !== visible) { + this._visible = visible; + //if(visible) + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + cc.renderer.childrenOrderDirty = true; + } + }, + + /** + *

Returns a copy of the anchor point.
+ * Anchor point is the point around which all transformations and positioning manipulations take place.
+ * It's like a pin in the node where it is "attached" to its parent.
+ * The anchorPoint is normalized, like a percentage. (0,0) means the bottom-left corner and (1,1) means the top-right corner.
+ * But you can use values higher than (1,1) and lower than (0,0) too.
+ * The default anchor point is (0.5,0.5), so it starts at the center of the node.

+ * @function + * @return {cc.Point} The anchor point of node. + */ + getAnchorPoint: function () { + return cc.p(this._anchorPoint); + }, + + /** + *

+ * Sets the anchor point in percent.
+ *
+ * anchor point is the point around which all transformations and positioning manipulations take place.
+ * It's like a pin in the node where it is "attached" to its parent.
+ * The anchorPoint is normalized, like a percentage. (0,0) means the bottom-left corner and (1,1) means the top-right corner.
+ * But you can use values higher than (1,1) and lower than (0,0) too.
+ * The default anchor point is (0.5,0.5), so it starts at the center of the node. + *

+ * @function + * @param {cc.Point|Number} point The anchor point of node or The x axis anchor of node. + * @param {Number} [y] The y axis anchor of node. + */ + setAnchorPoint: function (point, y) { + var locAnchorPoint = this._anchorPoint; + if (y === undefined) { + if ((point.x === locAnchorPoint.x) && (point.y === locAnchorPoint.y)) + return; + locAnchorPoint.x = point.x; + locAnchorPoint.y = point.y; + } else { + if ((point === locAnchorPoint.x) && (y === locAnchorPoint.y)) + return; + locAnchorPoint.x = point; + locAnchorPoint.y = y; + } + this._renderCmd._updateAnchorPointInPoint(); + }, + + _getAnchorX: function () { + return this._anchorPoint.x; + }, + _setAnchorX: function (x) { + if (this._anchorPoint.x === x) return; + this._anchorPoint.x = x; + this._renderCmd._updateAnchorPointInPoint(); + }, + _getAnchorY: function () { + return this._anchorPoint.y; + }, + _setAnchorY: function (y) { + if (this._anchorPoint.y === y) return; + this._anchorPoint.y = y; + this._renderCmd._updateAnchorPointInPoint(); + }, + + /** + * Returns a copy of the anchor point in absolute pixels.
+ * you can only read it. If you wish to modify it, use setAnchorPoint + * @see cc.Node#getAnchorPoint + * @function + * @return {cc.Point} The anchor point in absolute pixels. + */ + getAnchorPointInPoints: function () { + return this._renderCmd.getAnchorPointInPoints(); + }, + + _getWidth: function () { + return this._contentSize.width; + }, + _setWidth: function (width) { + this._contentSize.width = width; + this._renderCmd._updateAnchorPointInPoint(); + }, + _getHeight: function () { + return this._contentSize.height; + }, + _setHeight: function (height) { + this._contentSize.height = height; + this._renderCmd._updateAnchorPointInPoint(); + }, + + /** + *

Returns a copy the untransformed size of the node.
+ * The contentSize remains the same no matter the node is scaled or rotated.
+ * All nodes has a size. Layer and Scene has the same size of the screen by default.

+ * @function + * @return {cc.Size} The untransformed size of the node. + */ + getContentSize: function () { + return cc.size(this._contentSize); + }, + + /** + *

+ * Sets the untransformed size of the node.
+ *
+ * The contentSize remains the same no matter the node is scaled or rotated.
+ * All nodes has a size. Layer and Scene has the same size of the screen. + *

+ * @function + * @param {cc.Size|Number} size The untransformed size of the node or The untransformed size's width of the node. + * @param {Number} [height] The untransformed size's height of the node. + */ + setContentSize: function (size, height) { + var locContentSize = this._contentSize; + if (height === undefined) { + if ((size.width === locContentSize.width) && (size.height === locContentSize.height)) + return; + locContentSize.width = size.width; + locContentSize.height = size.height; + } else { + if ((size === locContentSize.width) && (height === locContentSize.height)) + return; + locContentSize.width = size; + locContentSize.height = height; + } + this._renderCmd._updateAnchorPointInPoint(); + }, + + /** + *

+ * Returns whether or not the node accepts event callbacks.
+ * Running means the node accept event callbacks like onEnter(), onExit(), update() + *

+ * @function + * @return {Boolean} Whether or not the node is running. + */ + isRunning: function () { + return this._running; + }, + + /** + * Returns a reference to the parent node + * @function + * @return {cc.Node} A reference to the parent node + */ + getParent: function () { + return this._parent; + }, + + /** + * Sets the parent node + * @param {cc.Node} parent A reference to the parent node + */ + setParent: function (parent) { + this._parent = parent; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }, + + /** + * Returns whether the anchor point will be ignored when you position this node.
+ * When anchor point ignored, position will be calculated based on the origin point (0, 0) in parent's coordinates. + * @function + * @see cc.Node#ignoreAnchorPointForPosition + * @return {Boolean} true if the anchor point will be ignored when you position this node. + */ + isIgnoreAnchorPointForPosition: function () { + return this._ignoreAnchorPointForPosition; + }, + + /** + *

+ * Sets whether the anchor point will be ignored when you position this node.
+ * When anchor point ignored, position will be calculated based on the origin point (0, 0) in parent's coordinates.
+ * This is an internal method, only used by CCLayer and CCScene. Don't call it outside framework.
+ * The default value is false, while in CCLayer and CCScene are true + *

+ * @function + * @param {Boolean} newValue true if anchor point will be ignored when you position this node + */ + ignoreAnchorPointForPosition: function (newValue) { + if (newValue !== this._ignoreAnchorPointForPosition) { + this._ignoreAnchorPointForPosition = newValue; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + } + }, + + /** + * Returns a tag that is used to identify the node easily. + * @function + * @return {Number} An integer that identifies the node. + * @example + * //You can set tags to node then identify them easily. + * // set tags + * node1.setTag(TAG_PLAYER); + * node2.setTag(TAG_MONSTER); + * node3.setTag(TAG_BOSS); + * parent.addChild(node1); + * parent.addChild(node2); + * parent.addChild(node3); + * // identify by tags + * var allChildren = parent.getChildren(); + * for(var i = 0; i < allChildren.length; i++){ + * switch(node.getTag()) { + * case TAG_PLAYER: + * break; + * case TAG_MONSTER: + * break; + * case TAG_BOSS: + * break; + * } + * } + */ + getTag: function () { + return this.tag; + }, + + /** + * Changes the tag that is used to identify the node easily.
+ * Please refer to getTag for the sample code. + * @function + * @see cc.Node#getTag + * @param {Number} tag A integer that identifies the node. + */ + setTag: function (tag) { + this.tag = tag; + }, + + /** + * Changes the name that is used to identify the node easily. + * @function + * @param {String} name + */ + setName: function (name) { + this._name = name; + }, + + /** + * Returns a string that is used to identify the node. + * @function + * @returns {string} A string that identifies the node. + */ + getName: function () { + return this._name; + }, + + /** + *

+ * Returns a custom user data pointer
+ * You can set everything in UserData pointer, a data block, a structure or an object. + *

+ * @function + * @return {object} A custom user data pointer + */ + getUserData: function () { + return this.userData; + }, + + /** + *

+ * Sets a custom user data reference
+ * You can set everything in UserData reference, a data block, a structure or an object, etc. + *

+ * @function + * @warning Don't forget to release the memory manually in JSB, especially before you change this data pointer, and before this node is autoreleased. + * @param {object} Var A custom user data + */ + setUserData: function (Var) { + this.userData = Var; + }, + + /** + * Returns a user assigned cocos2d object.
+ * Similar to userData, but instead of holding all kinds of data it can only hold a cocos2d object + * @function + * @return {object} A user assigned CCObject + */ + getUserObject: function () { + return this.userObject; + }, + + /** + *

+ * Sets a user assigned cocos2d object
+ * Similar to UserData, but instead of holding all kinds of data it can only hold a cocos2d object
+ * In JSB, the UserObject will be retained once in this method, and the previous UserObject (if existed) will be release.
+ * The UserObject will be released in CCNode's destruction. + *

+ * @param {object} newValue A user cocos2d object + */ + setUserObject: function (newValue) { + if (this.userObject !== newValue) + this.userObject = newValue; + }, + + + /** + * Returns the arrival order, indicates which children should be added previously. + * @function + * @return {Number} The arrival order. + */ + getOrderOfArrival: function () { + return this.arrivalOrder; + }, + + /** + *

+ * Sets the arrival order when this node has a same ZOrder with other children.
+ *
+ * A node which called addChild subsequently will take a larger arrival order,
+ * If two children have the same Z order, the child with larger arrival order will be drawn later. + *

+ * @function + * @warning This method is used internally for zOrder sorting, don't change this manually + * @param {Number} Var The arrival order. + */ + setOrderOfArrival: function (Var) { + this.arrivalOrder = Var; + }, + + /** + *

Returns the CCActionManager object that is used by all actions.
+ * (IMPORTANT: If you set a new cc.ActionManager, then previously created actions are going to be removed.)

+ * @function + * @see cc.Node#setActionManager + * @return {cc.ActionManager} A CCActionManager object. + */ + getActionManager: function () { + return this._actionManager || cc.director.getActionManager(); + }, + + /** + *

Sets the cc.ActionManager object that is used by all actions.

+ * @function + * @warning If you set a new CCActionManager, then previously created actions will be removed. + * @param {cc.ActionManager} actionManager A CCActionManager object that is used by all actions. + */ + setActionManager: function (actionManager) { + if (this._actionManager !== actionManager) { + this.stopAllActions(); + this._actionManager = actionManager; + } + }, + + /** + *

+ * Returns the cc.Scheduler object used to schedule all "updates" and timers. + *

+ * @function + * @return {cc.Scheduler} A CCScheduler object. + */ + getScheduler: function () { + return this._scheduler || cc.director.getScheduler(); + }, + + /** + *

+ * Sets a CCScheduler object that is used to schedule all "updates" and timers.
+ * IMPORTANT: If you set a new cc.Scheduler, then previously created timers/update are going to be removed. + *

+ * @function + * @warning If you set a new CCScheduler, then previously created timers/update are going to be removed. + * @param scheduler A cc.Scheduler object that is used to schedule all "update" and timers. + */ + setScheduler: function (scheduler) { + if (this._scheduler !== scheduler) { + this.unscheduleAllCallbacks(); + this._scheduler = scheduler; + } + }, + + /** + * Returns a "local" axis aligned bounding box of the node.
+ * @deprecated since v3.0, please use getBoundingBox instead + * @return {cc.Rect} + */ + boundingBox: function () { + cc.log(cc._LogInfos.Node_boundingBox); + return this.getBoundingBox(); + }, + + /** + * Returns a "local" axis aligned bounding box of the node.
+ * The returned box is relative only to its parent. + * @function + * @return {cc.Rect} The calculated bounding box of the node + */ + getBoundingBox: function () { + var rect = cc.rect(0, 0, this._contentSize.width, this._contentSize.height); + return cc._rectApplyAffineTransformIn(rect, this.getNodeToParentTransform()); + }, + + /** + * Stops all running actions and schedulers + * @function + */ + cleanup: function () { + // actions + this.stopAllActions(); + this.unscheduleAllCallbacks(); + + // event + cc.eventManager.removeListeners(this); + }, + + // composition: GET + /** + * Returns a child from the container given its tag + * @function + * @param {Number} aTag An identifier to find the child node. + * @return {cc.Node} a CCNode object whose tag equals to the input parameter + */ + getChildByTag: function (aTag) { + var __children = this._children; + if (__children !== null) { + for (var i = 0; i < __children.length; i++) { + var node = __children[i]; + if (node && node.tag === aTag) + return node; + } + } + return null; + }, + + /** + * Returns a child from the container given its name + * @function + * @param {String} name A name to find the child node. + * @return {cc.Node} a CCNode object whose name equals to the input parameter + */ + getChildByName: function (name) { + if (!name) { + cc.log("Invalid name"); + return null; + } + + var locChildren = this._children; + for (var i = 0, len = locChildren.length; i < len; i++) { + if (locChildren[i]._name === name) + return locChildren[i]; + } + return null; + }, + + // composition: ADD + + /**

"add" logic MUST only be in this method

+ * + *

If the child is added to a 'running' node, then 'onEnter' and 'onEnterTransitionDidFinish' will be called immediately.

+ * @function + * @param {cc.Node} child A child node + * @param {Number} [localZOrder=] Z order for drawing priority. Please refer to setZOrder(int) + * @param {Number|String} [tag=] An integer or a name to identify the node easily. Please refer to setTag(int) and setName(string) + */ + addChild: function (child, localZOrder, tag) { + localZOrder = localZOrder === undefined ? child._localZOrder : localZOrder; + var name, setTag = false; + if (tag === undefined) { + name = child._name; + } else if (typeof tag === 'string') { + name = tag; + tag = undefined; + } else if (typeof tag === 'number') { + setTag = true; + name = ""; + } + + cc.assert(child, cc._LogInfos.Node_addChild_3); + cc.assert(child._parent === null, "child already added. It can't be added again"); + + this._addChildHelper(child, localZOrder, tag, name, setTag); + }, + + _addChildHelper: function (child, localZOrder, tag, name, setTag) { + if (!this._children) + this._children = []; + + this._insertChild(child, localZOrder); + if (setTag) + child.setTag(tag); + else + child.setName(name); + + child.setParent(this); + child.setOrderOfArrival(cc.s_globalOrderOfArrival++); + + if (this._running) { + child._performRecursive(cc.Node._stateCallbackType.onEnter); + // prevent onEnterTransitionDidFinish to be called twice when a node is added in onEnter + if (this._isTransitionFinished) + child._performRecursive(cc.Node._stateCallbackType.onEnterTransitionDidFinish); + } + child._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + if (this._cascadeColorEnabled) + child._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.colorDirty); + if (this._cascadeOpacityEnabled) + child._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.opacityDirty); + }, + + // composition: REMOVE + /** + * Remove itself from its parent node. If cleanup is true, then also remove all actions and callbacks.
+ * If the cleanup parameter is not passed, it will force a cleanup.
+ * If the node orphan, then nothing happens. + * @function + * @param {Boolean} [cleanup=true] true if all actions and callbacks on this node should be removed, false otherwise. + * @see cc.Node#removeFromParentAndCleanup + */ + removeFromParent: function (cleanup) { + if (this._parent) { + if (cleanup === undefined) + cleanup = true; + this._parent.removeChild(this, cleanup); + } + }, + + /** + * Removes this node itself from its parent node.
+ * If the node orphan, then nothing happens. + * @deprecated since v3.0, please use removeFromParent() instead + * @param {Boolean} [cleanup=true] true if all actions and callbacks on this node should be removed, false otherwise. + */ + removeFromParentAndCleanup: function (cleanup) { + cc.log(cc._LogInfos.Node_removeFromParentAndCleanup); + this.removeFromParent(cleanup); + }, + + /**

Removes a child from the container. It will also cleanup all running actions depending on the cleanup parameter.

+ * If the cleanup parameter is not passed, it will force a cleanup.
+ *

"remove" logic MUST only be on this method
+ * If a class wants to extend the 'removeChild' behavior it only needs
+ * to override this method

+ * @function + * @param {cc.Node} child The child node which will be removed. + * @param {Boolean} [cleanup=true] true if all running actions and callbacks on the child node will be cleanup, false otherwise. + */ + removeChild: function (child, cleanup) { + // explicit nil handling + if (this._children.length === 0) + return; + + if (cleanup === undefined) + cleanup = true; + if (this._children.indexOf(child) > -1) + this._detachChild(child, cleanup); + + //this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.visibleDirty); + cc.renderer.childrenOrderDirty = true; + }, + + /** + * Removes a child from the container by tag value. It will also cleanup all running actions depending on the cleanup parameter. + * If the cleanup parameter is not passed, it will force a cleanup.
+ * @function + * @param {Number} tag An integer number that identifies a child node + * @param {Boolean} [cleanup=true] true if all running actions and callbacks on the child node will be cleanup, false otherwise. + * @see cc.Node#removeChildByTag + */ + removeChildByTag: function (tag, cleanup) { + if (tag === cc.NODE_TAG_INVALID) + cc.log(cc._LogInfos.Node_removeChildByTag); + + var child = this.getChildByTag(tag); + if (!child) + cc.log(cc._LogInfos.Node_removeChildByTag_2, tag); + else + this.removeChild(child, cleanup); + }, + + /** + * Removes all children from the container and do a cleanup all running actions depending on the cleanup parameter. + * @param {Boolean} [cleanup=true] + */ + removeAllChildrenWithCleanup: function (cleanup) { + this.removeAllChildren(cleanup); + }, + + /** + * Removes all children from the container and do a cleanup all running actions depending on the cleanup parameter.
+ * If the cleanup parameter is not passed, it will force a cleanup.
+ * @function + * @param {Boolean} [cleanup=true] true if all running actions on all children nodes should be cleanup, false otherwise. + */ + removeAllChildren: function (cleanup) { + // not using detachChild improves speed here + var __children = this._children; + if (__children !== null) { + if (cleanup === undefined) + cleanup = true; + for (var i = 0; i < __children.length; i++) { + var node = __children[i]; + if (node) { + if (this._running) { + node._performRecursive(cc.Node._stateCallbackType.onExitTransitionDidStart); + node._performRecursive(cc.Node._stateCallbackType.onExit); + } + + // If you don't do cleanup, the node's actions will not get removed and the + if (cleanup) + node._performRecursive(cc.Node._stateCallbackType.cleanup); + + // set parent nil at the end + node.parent = null; + node._renderCmd.detachFromParent(); + } + } + this._children.length = 0; + cc.renderer.childrenOrderDirty = true; + } + }, + + _detachChild: function (child, doCleanup) { + // IMPORTANT: + // -1st do onExit + // -2nd cleanup + if (this._running) { + child._performRecursive(cc.Node._stateCallbackType.onExitTransitionDidStart); + child._performRecursive(cc.Node._stateCallbackType.onExit); + } + + // If you don't do cleanup, the child's actions will not get removed and the + if (doCleanup) + child._performRecursive(cc.Node._stateCallbackType.cleanup); + + // set parent nil at the end + child.parent = null; + child._renderCmd.detachFromParent(); + cc.arrayRemoveObject(this._children, child); + }, + + _insertChild: function (child, z) { + cc.renderer.childrenOrderDirty = this._reorderChildDirty = true; + this._children.push(child); + child._setLocalZOrder(z); + }, + + setNodeDirty: function () { + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }, + + /** Reorders a child according to a new z value.
+ * The child MUST be already added. + * @function + * @param {cc.Node} child An already added child node. It MUST be already added. + * @param {Number} zOrder Z order for drawing priority. Please refer to setZOrder(int) + */ + reorderChild: function (child, zOrder) { + cc.assert(child, cc._LogInfos.Node_reorderChild); + if (this._children.indexOf(child) === -1) { + cc.log(cc._LogInfos.Node_reorderChild_2); + return; + } + cc.renderer.childrenOrderDirty = this._reorderChildDirty = true; + child.arrivalOrder = cc.s_globalOrderOfArrival; + cc.s_globalOrderOfArrival++; + child._setLocalZOrder(zOrder); + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.orderDirty); + }, + + /** + *

+ * Sorts the children array once before drawing, instead of every time when a child is added or reordered.
+ * This approach can improves the performance massively. + *

+ * @function + * @note Don't call this manually unless a child added needs to be removed in the same frame + */ + sortAllChildren: function () { + if (this._reorderChildDirty) { + var _children = this._children; + + // insertion sort + var len = _children.length, i, j, tmp; + for (i = 1; i < len; i++) { + tmp = _children[i]; + j = i - 1; + + //continue moving element downwards while zOrder is smaller or when zOrder is the same but mutatedIndex is smaller + while (j >= 0) { + if (tmp._localZOrder < _children[j]._localZOrder) { + _children[j + 1] = _children[j]; + } else if (tmp._localZOrder === _children[j]._localZOrder && tmp.arrivalOrder < _children[j].arrivalOrder) { + _children[j + 1] = _children[j]; + } else { + break; + } + j--; + } + _children[j + 1] = tmp; + } + + //don't need to check children recursively, that's done in visit of each child + this._reorderChildDirty = false; + } + }, + + /** + * Render function using the canvas 2d context or WebGL context, internal usage only, please do not call this function + * @function + * @param {CanvasRenderingContext2D | WebGLRenderingContext} ctx The render context + */ + draw: function (ctx) { + // override me + // Only use- this function to draw your staff. + // DON'T draw your stuff outside this method + }, + + // Internal use only, do not call it by yourself, + transformAncestors: function () { + if (this._parent !== null) { + this._parent.transformAncestors(); + this._parent.transform(); + } + }, + + //scene management + /** + *

+ * Event callback that is invoked every time when CCNode enters the 'stage'.
+ * If the CCNode enters the 'stage' with a transition, this event is called when the transition starts.
+ * During onEnter you can't access a "sister/brother" node.
+ * If you override onEnter, you must call its parent's onEnter function with this._super(). + *

+ * @function + */ + onEnter: function () { + this._isTransitionFinished = false; + this._running = true;//should be running before resumeSchedule + this.resume(); + }, + + _performRecursive: function (callbackType) { + var nodeCallbackType = cc.Node._stateCallbackType; + if (callbackType >= nodeCallbackType.max) { + return; + } + + var index = 0; + var children, child, curr, i, len; + var stack = cc.Node._performStacks[cc.Node._performing]; + if (!stack) { + stack = []; + cc.Node._performStacks.push(stack); + } + stack.length = 0; + cc.Node._performing++; + curr = stack[0] = this; + while (curr) { + // Walk through children + children = curr._children; + if (children && children.length > 0) { + for (i = 0, len = children.length; i < len; ++i) { + child = children[i]; + stack.push(child); + } + } + children = curr._protectedChildren; + if (children && children.length > 0) { + for (i = 0, len = children.length; i < len; ++i) { + child = children[i]; + stack.push(child); + } + } + + index++; + curr = stack[index]; + } + for (i = stack.length - 1; i >= 0; --i) { + curr = stack[i]; + stack[i] = null; + if (!curr) continue; + + // Perform actual action + switch (callbackType) { + case nodeCallbackType.onEnter: + curr.onEnter(); + break; + case nodeCallbackType.onExit: + curr.onExit(); + break; + case nodeCallbackType.onEnterTransitionDidFinish: + curr.onEnterTransitionDidFinish(); + break; + case nodeCallbackType.cleanup: + curr.cleanup(); + break; + case nodeCallbackType.onExitTransitionDidStart: + curr.onExitTransitionDidStart(); + break; + } + } + cc.Node._performing--; + }, + + /** + *

+ * Event callback that is invoked when the CCNode enters in the 'stage'.
+ * If the CCNode enters the 'stage' with a transition, this event is called when the transition finishes.
+ * If you override onEnterTransitionDidFinish, you shall call its parent's onEnterTransitionDidFinish with this._super() + *

+ * @function + */ + onEnterTransitionDidFinish: function () { + this._isTransitionFinished = true; + }, + + /** + *

callback that is called every time the cc.Node leaves the 'stage'.
+ * If the cc.Node leaves the 'stage' with a transition, this callback is called when the transition starts.
+ * If you override onExitTransitionDidStart, you shall call its parent's onExitTransitionDidStart with this._super()

+ * @function + */ + onExitTransitionDidStart: function () { + }, + + /** + *

+ * callback that is called every time the cc.Node leaves the 'stage'.
+ * If the cc.Node leaves the 'stage' with a transition, this callback is called when the transition finishes.
+ * During onExit you can't access a sibling node.
+ * If you override onExit, you shall call its parent's onExit with this._super(). + *

+ * @function + */ + onExit: function () { + this._running = false; + this.pause(); + this.removeAllComponents(); + }, + + // actions + /** + * Executes an action, and returns the action that is executed.
+ * The node becomes the action's target. Refer to cc.Action's getTarget() + * @function + * @warning Starting from v0.8 actions don't retain their target anymore. + * @param {cc.Action} action + * @return {cc.Action} An Action pointer + */ + runAction: function (action) { + cc.assert(action, cc._LogInfos.Node_runAction); + + this.actionManager.addAction(action, this, !this._running); + return action; + }, + + /** + * Stops and removes all actions from the running action list . + * @function + */ + stopAllActions: function () { + this.actionManager && this.actionManager.removeAllActionsFromTarget(this); + }, + + /** + * Stops and removes an action from the running action list. + * @function + * @param {cc.Action} action An action object to be removed. + */ + stopAction: function (action) { + this.actionManager.removeAction(action); + }, + + /** + * Removes an action from the running action list by its tag. + * @function + * @param {Number} tag A tag that indicates the action to be removed. + */ + stopActionByTag: function (tag) { + if (tag === cc.ACTION_TAG_INVALID) { + cc.log(cc._LogInfos.Node_stopActionByTag); + return; + } + this.actionManager.removeActionByTag(tag, this); + }, + + /** + * Returns an action from the running action list by its tag. + * @function + * @see cc.Node#getTag and cc.Node#setTag + * @param {Number} tag + * @return {cc.Action} The action object with the given tag. + */ + getActionByTag: function (tag) { + if (tag === cc.ACTION_TAG_INVALID) { + cc.log(cc._LogInfos.Node_getActionByTag); + return null; + } + return this.actionManager.getActionByTag(tag, this); + }, + + /**

Returns the numbers of actions that are running plus the ones that are schedule to run (actions in actionsToAdd and actions arrays).
+ * Composable actions are counted as 1 action. Example:
+ * If you are running 1 Sequence of 7 actions, it will return 1.
+ * If you are running 7 Sequences of 2 actions, it will return 7.

+ * @function + * @return {Number} The number of actions that are running plus the ones that are schedule to run + */ + getNumberOfRunningActions: function () { + return this.actionManager.numberOfRunningActionsInTarget(this); + }, + + // cc.Node - Callbacks + // timers + /** + *

schedules the "update" method.
+ * It will use the order number 0. This method will be called every frame.
+ * Scheduled methods with a lower order value will be called before the ones that have a higher order value.
+ * Only one "update" method could be scheduled per node.

+ * @function + */ + scheduleUpdate: function () { + this.scheduleUpdateWithPriority(0); + }, + + /** + *

+ * schedules the "update" callback function with a custom priority. + * This callback function will be called every frame.
+ * Scheduled callback functions with a lower priority will be called before the ones that have a higher value.
+ * Only one "update" callback function could be scheduled per node (You can't have 2 'update' callback functions).
+ *

+ * @function + * @param {Number} priority + */ + scheduleUpdateWithPriority: function (priority) { + this.scheduler.scheduleUpdate(this, priority, !this._running); + }, + + /** + * Unschedules the "update" method. + * @function + * @see cc.Node#scheduleUpdate + */ + unscheduleUpdate: function () { + this.scheduler.unscheduleUpdate(this); + }, + + /** + *

Schedules a custom selector.
+ * If the selector is already scheduled, then the interval parameter will be updated without scheduling it again.

+ * @function + * @param {function} callback A function wrapped as a selector + * @param {Number} interval Tick interval in seconds. 0 means tick every frame. If interval = 0, it's recommended to use scheduleUpdate() instead. + * @param {Number} repeat The selector will be executed (repeat + 1) times, you can use kCCRepeatForever for tick infinitely. + * @param {Number} delay The amount of time that the first tick will wait before execution. + * @param {String} key The only string identifying the callback + */ + schedule: function (callback, interval, repeat, delay, key) { + var len = arguments.length; + if (typeof callback === "function") { + //callback, interval, repeat, delay, key + if (len === 1) { + //callback + interval = 0; + repeat = cc.REPEAT_FOREVER; + delay = 0; + key = this.__instanceId; + } else if (len === 2) { + if (typeof interval === "number") { + //callback, interval + repeat = cc.REPEAT_FOREVER; + delay = 0; + key = this.__instanceId; + } else { + //callback, key + key = interval; + interval = 0; + repeat = cc.REPEAT_FOREVER; + delay = 0; + } + } else if (len === 3) { + if (typeof repeat === "string") { + //callback, interval, key + key = repeat; + repeat = cc.REPEAT_FOREVER; + } else { + //callback, interval, repeat + key = this.__instanceId; + } + delay = 0; + } else if (len === 4) { + key = this.__instanceId; + } + } else { + //selector + //selector, interval + //selector, interval, repeat, delay + if (len === 1) { + interval = 0; + repeat = cc.REPEAT_FOREVER; + delay = 0; + } else if (len === 2) { + repeat = cc.REPEAT_FOREVER; + delay = 0; + } + } + + cc.assert(callback, cc._LogInfos.Node_schedule); + cc.assert(interval >= 0, cc._LogInfos.Node_schedule_2); + + interval = interval || 0; + repeat = isNaN(repeat) ? cc.REPEAT_FOREVER : repeat; + delay = delay || 0; + + this.scheduler.schedule(callback, this, interval, repeat, delay, !this._running, key); + }, + + /** + * Schedules a callback function that runs only once, with a delay of 0 or larger + * @function + * @see cc.Node#schedule + * @param {function} callback A function wrapped as a selector + * @param {Number} delay The amount of time that the first tick will wait before execution. + * @param {String} key The only string identifying the callback + */ + scheduleOnce: function (callback, delay, key) { + //selector, delay + //callback, delay, key + if (key === undefined) + key = this.__instanceId; + this.schedule(callback, 0, 0, delay, key); + }, + + /** + * unschedules a custom callback function. + * @function + * @see cc.Node#schedule + * @param {function} callback_fn A function wrapped as a selector + */ + unschedule: function (callback_fn) { + //key + //selector + if (!callback_fn) + return; + + this.scheduler.unschedule(callback_fn, this); + }, + + /** + *

unschedule all scheduled callback functions: custom callback functions, and the 'update' callback function.
+ * Actions are not affected by this method.

+ * @function + */ + unscheduleAllCallbacks: function () { + this.scheduler.unscheduleAllForTarget(this); + }, + + /** + * Resumes all scheduled selectors and actions.
+ * This method is called internally by onEnter + * @function + * @deprecated since v3.0, please use resume() instead + */ + resumeSchedulerAndActions: function () { + cc.log(cc._LogInfos.Node_resumeSchedulerAndActions); + this.resume(); + }, + + /** + *

Resumes all scheduled selectors and actions.
+ * This method is called internally by onEnter

+ */ + resume: function () { + this.scheduler.resumeTarget(this); + this.actionManager && this.actionManager.resumeTarget(this); + cc.eventManager.resumeTarget(this); + }, + + /** + *

Pauses all scheduled selectors and actions.
+ * This method is called internally by onExit

+ * @deprecated since v3.0, please use pause instead + * @function + */ + pauseSchedulerAndActions: function () { + cc.log(cc._LogInfos.Node_pauseSchedulerAndActions); + this.pause(); + }, + + /** + *

Pauses all scheduled selectors and actions.
+ * This method is called internally by onExit

+ * @function + */ + pause: function () { + this.scheduler.pauseTarget(this); + this.actionManager && this.actionManager.pauseTarget(this); + cc.eventManager.pauseTarget(this); + }, + + /** + *

Sets the additional transform.
+ * The additional transform will be concatenated at the end of getNodeToParentTransform.
+ * It could be used to simulate `parent-child` relationship between two nodes (e.g. one is in BatchNode, another isn't).
+ *

+ * @function + * @param {cc.AffineTransform} additionalTransform The additional transform + * @example + * // create a batchNode + * var batch = new cc.SpriteBatchNode("Icon-114.png"); + * this.addChild(batch); + * + * // create two sprites, spriteA will be added to batchNode, they are using different textures. + * var spriteA = new cc.Sprite(batch->getTexture()); + * var spriteB = new cc.Sprite("Icon-72.png"); + * + * batch.addChild(spriteA); + * + * // We can't make spriteB as spriteA's child since they use different textures. So just add it to layer. + * // But we want to simulate `parent-child` relationship for these two node. + * this.addChild(spriteB); + * + * //position + * spriteA.setPosition(ccp(200, 200)); + * + * // Gets the spriteA's transform. + * var t = spriteA.getNodeToParentTransform(); + * + * // Sets the additional transform to spriteB, spriteB's position will based on its pseudo parent i.e. spriteA. + * spriteB.setAdditionalTransform(t); + * + * //scale + * spriteA.setScale(2); + * + * // Gets the spriteA's transform. + * t = spriteA.getNodeToParentTransform(); + * + * // Sets the additional transform to spriteB, spriteB's scale will based on its pseudo parent i.e. spriteA. + * spriteB.setAdditionalTransform(t); + * + * //rotation + * spriteA.setRotation(20); + * + * // Gets the spriteA's transform. + * t = spriteA.getNodeToParentTransform(); + * + * // Sets the additional transform to spriteB, spriteB's rotation will based on its pseudo parent i.e. spriteA. + * spriteB.setAdditionalTransform(t); + */ + setAdditionalTransform: function (additionalTransform) { + if(additionalTransform === undefined) + return this._additionalTransformDirty = false; + this._additionalTransform = additionalTransform; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + this._additionalTransformDirty = true; + }, + + /** + * Returns the matrix that transform parent's space coordinates to the node's (local) space coordinates.
+ * The matrix is in Pixels. + * @function + * @return {cc.AffineTransform} + */ + getParentToNodeTransform: function () { + return this._renderCmd.getParentToNodeTransform(); + }, + + /** + * @function + * @deprecated since v3.0, please use getParentToNodeTransform instead + */ + parentToNodeTransform: function () { + return this.getParentToNodeTransform(); + }, + + /** + * Returns the world affine transform matrix. The matrix is in Pixels. + * @function + * @return {cc.AffineTransform} + */ + getNodeToWorldTransform: function () { + var t = this.getNodeToParentTransform(); + for (var p = this._parent; p !== null; p = p.parent) + t = cc.affineTransformConcat(t, p.getNodeToParentTransform()); + return t; + }, + + /** + * @function + * @deprecated since v3.0, please use getNodeToWorldTransform instead + */ + nodeToWorldTransform: function () { + return this.getNodeToWorldTransform(); + }, + + /** + * Returns the inverse world affine transform matrix. The matrix is in Pixels. + * @function + * @return {cc.AffineTransform} + */ + getWorldToNodeTransform: function () { + return cc.affineTransformInvert(this.getNodeToWorldTransform()); + }, + + /** + * @function + * @deprecated since v3.0, please use getWorldToNodeTransform instead + */ + worldToNodeTransform: function () { + return this.getWorldToNodeTransform(); + }, + + /** + * Converts a Point to node (local) space coordinates. The result is in Points. + * @function + * @param {cc.Point} worldPoint + * @return {cc.Point} + */ + convertToNodeSpace: function (worldPoint) { + return cc.pointApplyAffineTransform(worldPoint, this.getWorldToNodeTransform()); + }, + + /** + * Converts a Point to world space coordinates. The result is in Points. + * @function + * @param {cc.Point} nodePoint + * @return {cc.Point} + */ + convertToWorldSpace: function (nodePoint) { + nodePoint = nodePoint || cc.p(0, 0); + return cc.pointApplyAffineTransform(nodePoint, this.getNodeToWorldTransform()); + }, + + /** + * Converts a Point to node (local) space coordinates. The result is in Points.
+ * treating the returned/received node point as anchor relative. + * @function + * @param {cc.Point} worldPoint + * @return {cc.Point} + */ + convertToNodeSpaceAR: function (worldPoint) { + return cc.pSub(this.convertToNodeSpace(worldPoint), this._renderCmd.getAnchorPointInPoints()); + }, + + /** + * Converts a local Point to world space coordinates.The result is in Points.
+ * treating the returned/received node point as anchor relative. + * @function + * @param {cc.Point} nodePoint + * @return {cc.Point} + */ + convertToWorldSpaceAR: function (nodePoint) { + nodePoint = nodePoint || cc.p(0, 0); + var pt = cc.pAdd(nodePoint, this._renderCmd.getAnchorPointInPoints()); + return this.convertToWorldSpace(pt); + }, + + _convertToWindowSpace: function (nodePoint) { + var worldPoint = this.convertToWorldSpace(nodePoint); + return cc.director.convertToUI(worldPoint); + }, + + /** convenience methods which take a cc.Touch instead of cc.Point + * @function + * @param {cc.Touch} touch The touch object + * @return {cc.Point} + */ + convertTouchToNodeSpace: function (touch) { + var point = touch.getLocation(); + return this.convertToNodeSpace(point); + }, + + /** + * converts a cc.Touch (world coordinates) into a local coordinate. This method is AR (Anchor Relative). + * @function + * @param {cc.Touch} touch The touch object + * @return {cc.Point} + */ + convertTouchToNodeSpaceAR: function (touch) { + var point = cc.director.convertToGL(touch.getLocation()); + return this.convertToNodeSpaceAR(point); + }, + + /** + * Update will be called automatically every frame if "scheduleUpdate" is called when the node is "live".
+ * The default behavior is to invoke the visit function of node's componentContainer.
+ * Override me to implement your own update logic. + * @function + * @param {Number} dt Delta time since last update + */ + update: function (dt) { + if (this._componentContainer && !this._componentContainer.isEmpty()) + this._componentContainer.visit(dt); + }, + + /** + *

+ * Calls children's updateTransform() method recursively.
+ *
+ * This method is moved from CCSprite, so it's no longer specific to CCSprite.
+ * As the result, you apply CCSpriteBatchNode's optimization on your customed CCNode.
+ * e.g., batchNode->addChild(myCustomNode), while you can only addChild(sprite) before. + *

+ * @function + */ + updateTransform: function () { + var children = this._children, node; + for (var i = 0; i < children.length; i++) { + node = children[i]; + if (node) + node.updateTransform(); + } + }, + + /** + *

Currently JavaScript Bindings (JSB), in some cases, needs to use retain and release. This is a bug in JSB, + * and the ugly workaround is to use retain/release. So, these 2 methods were added to be compatible with JSB. + * This is a hack, and should be removed once JSB fixes the retain/release bug
+ * You will need to retain an object if you created an engine object and haven't added it into the scene graph during the same frame.
+ * Otherwise, JSB's native autorelease pool will consider this object a useless one and release it directly,
+ * when you want to use it later, a "Invalid Native Object" error will be raised.
+ * The retain function can increase a reference count for the native object to avoid it being released,
+ * you need to manually invoke release function when you think this object is no longer needed, otherwise, there will be memory learks.
+ * retain and release function call should be paired in developer's game code.

+ * @function + * @see cc.Node#release + */ + retain: function () { + }, + /** + *

Currently JavaScript Bindings (JSB), in some cases, needs to use retain and release. This is a bug in JSB, + * and the ugly workaround is to use retain/release. So, these 2 methods were added to be compatible with JSB. + * This is a hack, and should be removed once JSB fixes the retain/release bug
+ * You will need to retain an object if you created an engine object and haven't added it into the scene graph during the same frame.
+ * Otherwise, JSB's native autorelease pool will consider this object a useless one and release it directly,
+ * when you want to use it later, a "Invalid Native Object" error will be raised.
+ * The retain function can increase a reference count for the native object to avoid it being released,
+ * you need to manually invoke release function when you think this object is no longer needed, otherwise, there will be memory learks.
+ * retain and release function call should be paired in developer's game code.

+ * @function + * @see cc.Node#retain + */ + release: function () { + }, + + /** + * Returns a component identified by the name given. + * @function + * @param {String} name The name to search for + * @return {cc.Component} The component found + */ + getComponent: function (name) { + if(this._componentContainer) + return this._componentContainer.getComponent(name); + return null; + }, + + /** + * Adds a component to the node's component container. + * @function + * @param {cc.Component} component + */ + addComponent: function (component) { + if(this._componentContainer) + this._componentContainer.add(component); + }, + + /** + * Removes a component identified by the given name or removes the component object given + * @function + * @param {String|cc.Component} component + */ + removeComponent: function (component) { + if(this._componentContainer) + return this._componentContainer.remove(component); + return false; + }, + + /** + * Removes all components of cc.Node, it called when cc.Node is exiting from stage. + * @function + */ + removeAllComponents: function () { + if(this._componentContainer) + this._componentContainer.removeAll(); + }, + + grid: null, + + /** + * Recursive method that visit its children and draw them + * @function + * @param {cc.Node} parent + */ + visit: function (parent) { + var cmd = this._renderCmd, parentCmd = parent ? parent._renderCmd : null; + + // quick return if not visible + if (!this._visible) { + cmd._propagateFlagsDown(parentCmd); + return; + } + + var renderer = cc.renderer; + cmd.visit(parentCmd); + + var i, children = this._children, len = children.length, child; + if (len > 0) { + if (this._reorderChildDirty) { + this.sortAllChildren(); + } + // draw children zOrder < 0 + for (i = 0; i < len; i++) { + child = children[i]; + if (child._localZOrder < 0) { + child.visit(this); + } + else { + break; + } + } + + renderer.pushRenderCommand(cmd); + for (; i < len; i++) { + children[i].visit(this); + } + } else { + renderer.pushRenderCommand(cmd); + } + cmd._dirtyFlag = 0; + }, + + /** + * Performs view-matrix transformation based on position, scale, rotation and other attributes. + * @function + * @param {cc.Node.RenderCmd} parentCmd parent's render command + * @param {boolean} recursive whether call its children's transform + */ + transform: function (parentCmd, recursive) { + this._renderCmd.transform(parentCmd, recursive); + }, + + /** + *

Returns the matrix that transform the node's (local) space coordinates into the parent's space coordinates.
+ * The matrix is in Pixels.

+ * @function + * @return {cc.AffineTransform} + * @deprecated since v3.0, please use getNodeToParentTransform instead + */ + nodeToParentTransform: function () { + return this.getNodeToParentTransform(); + }, + + /** + * Returns the matrix that transform the node's (local) space coordinates into the parent's space coordinates.
+ * The matrix is in Pixels. + * @function + * @return {cc.AffineTransform} The affine transform object + */ + getNodeToParentTransform: function (ancestor) { + var t = this._renderCmd.getNodeToParentTransform(); + if (ancestor) { + var T = {a: t.a, b: t.b, c: t.c, d: t.d, tx: t.tx, ty: t.ty}; + for (var p = this._parent; p != null && p != ancestor; p = p.getParent()) { + cc.affineTransformConcatIn(T, p.getNodeToParentTransform()); + } + return T; + } else { + return t; + } + }, + + getNodeToParentAffineTransform: function (ancestor) { + return this.getNodeToParentTransform(ancestor); + }, + + /** + * Returns null + * @function + * @return {null} + * @deprecated since v3.0, no alternative function + */ + getCamera: function () { + return null; + }, + + /** + *

Returns a grid object that is used when applying effects.
+ * This function have been deprecated, please use cc.NodeGrid to run grid actions

+ * @function + * @return {cc.GridBase} A CCGrid object that is used when applying effects + * @deprecated since v3.0, no alternative function + */ + getGrid: function () { + return this.grid; + }, + + /** + *

Changes a grid object that is used when applying effects
+ * This function have been deprecated, please use cc.NodeGrid to run grid actions

+ * @function + * @param {cc.GridBase} grid A CCGrid object that is used when applying effects + * @deprecated since v3.0, no alternative function + */ + setGrid: function (grid) { + this.grid = grid; + }, + + /** + * Return the shader program currently used for this node + * @function + * @return {cc.GLProgram} The shader program currently used for this node + */ + getShaderProgram: function () { + return this._renderCmd.getShaderProgram(); + }, + + /** + *

+ * Sets the shader program for this node + * + * Since v2.0, each rendering node must set its shader program. + * It should be set in initialize phase. + *

+ * @function + * @param {cc.GLProgram} newShaderProgram The shader program which fetches from CCShaderCache. + * @example + * node.setGLProgram(cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR)); + */ + setShaderProgram: function (newShaderProgram) { + this._renderCmd.setShaderProgram(newShaderProgram); + }, + + setGLProgramState: function (glProgramState) { + this._renderCmd.setGLProgramState(glProgramState); + }, + + getGLProgramState: function () { + return this._renderCmd.getGLProgramState(); + }, + + /** + * Returns the state of OpenGL server side. + * @function + * @return {Number} The state of OpenGL server side. + * @deprecated since v3.0, no need anymore + */ + getGLServerState: function () { + return 0; + }, + + /** + * Sets the state of OpenGL server side. + * @function + * @param {Number} state The state of OpenGL server side. + * @deprecated since v3.0, no need anymore + */ + setGLServerState: function (state) { + }, + + /** + * Returns a "world" axis aligned bounding box of the node. + * @function + * @return {cc.Rect} + */ + getBoundingBoxToWorld: function () { + var rect = cc.rect(0, 0, this._contentSize.width, this._contentSize.height); + var trans = this.getNodeToWorldTransform(); + rect = cc.rectApplyAffineTransform(rect, trans); + + //query child's BoundingBox + if (!this._children) + return rect; + + var locChildren = this._children; + for (var i = 0; i < locChildren.length; i++) { + var child = locChildren[i]; + if (child && child._visible) { + var childRect = child._getBoundingBoxToCurrentNode(trans); + if (childRect) + rect = cc.rectUnion(rect, childRect); + } + } + return rect; + }, + + _getBoundingBoxToCurrentNode: function (parentTransform) { + var rect = cc.rect(0, 0, this._contentSize.width, this._contentSize.height); + var trans = (parentTransform === undefined) ? this.getNodeToParentTransform() : cc.affineTransformConcat(this.getNodeToParentTransform(), parentTransform); + rect = cc.rectApplyAffineTransform(rect, trans); + + //query child's BoundingBox + if (!this._children) + return rect; + + var locChildren = this._children; + for (var i = 0; i < locChildren.length; i++) { + var child = locChildren[i]; + if (child && child._visible) { + var childRect = child._getBoundingBoxToCurrentNode(trans); + if (childRect) + rect = cc.rectUnion(rect, childRect); + } + } + return rect; + }, + + /** + * Returns the opacity of Node + * @function + * @returns {number} opacity + */ + getOpacity: function () { + return this._realOpacity; + }, + + /** + * Returns the displayed opacity of Node, + * the difference between displayed opacity and opacity is that displayed opacity is calculated based on opacity and parent node's opacity when cascade opacity enabled. + * @function + * @returns {number} displayed opacity + */ + getDisplayedOpacity: function () { + return this._renderCmd.getDisplayedOpacity(); + }, + + /** + * Sets the opacity of Node + * @function + * @param {Number} opacity + */ + setOpacity: function (opacity) { + this._realOpacity = opacity; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.opacityDirty); + }, + + /** + * Update displayed opacity + * @function + * @param {Number} parentOpacity + */ + updateDisplayedOpacity: function (parentOpacity) { + //TODO this API shouldn't be public. + this._renderCmd._updateDisplayOpacity(parentOpacity); + }, + + /** + * Returns whether node's opacity value affect its child nodes. + * @function + * @returns {boolean} + */ + isCascadeOpacityEnabled: function () { + return this._cascadeOpacityEnabled; + }, + + /** + * Enable or disable cascade opacity, if cascade enabled, child nodes' opacity will be the multiplication of parent opacity and its own opacity. + * @function + * @param {boolean} cascadeOpacityEnabled + */ + setCascadeOpacityEnabled: function (cascadeOpacityEnabled) { + if (this._cascadeOpacityEnabled === cascadeOpacityEnabled) + return; + this._cascadeOpacityEnabled = cascadeOpacityEnabled; + this._renderCmd.setCascadeOpacityEnabledDirty(); + }, + + /** + * Returns the color of Node + * @function + * @returns {cc.Color} + */ + getColor: function () { + var locRealColor = this._realColor; + return cc.color(locRealColor.r, locRealColor.g, locRealColor.b, locRealColor.a); + }, + + /** + * Returns the displayed color of Node, + * the difference between displayed color and color is that displayed color is calculated based on color and parent node's color when cascade color enabled. + * @function + * @returns {cc.Color} + */ + getDisplayedColor: function () { + return this._renderCmd.getDisplayedColor(); + }, + + /** + *

Sets the color of Node.
+ * When color doesn't include opacity value like cc.color(128,128,128), this function only change the color.
+ * When color include opacity like cc.color(128,128,128,100), then this function will change the color and the opacity.

+ * @function + * @param {cc.Color} color The new color given + */ + setColor: function (color) { + var locRealColor = this._realColor; + locRealColor.r = color.r; + locRealColor.g = color.g; + locRealColor.b = color.b; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.colorDirty); + }, + + /** + * Update the displayed color of Node + * @function + * @param {cc.Color} parentColor + */ + updateDisplayedColor: function (parentColor) { + //TODO this API shouldn't be public. + this._renderCmd._updateDisplayColor(parentColor); + }, + + /** + * Returns whether node's color value affect its child nodes. + * @function + * @returns {boolean} + */ + isCascadeColorEnabled: function () { + return this._cascadeColorEnabled; + }, + + /** + * Enable or disable cascade color, if cascade enabled, child nodes' opacity will be the cascade value of parent color and its own color. + * @param {boolean} cascadeColorEnabled + */ + setCascadeColorEnabled: function (cascadeColorEnabled) { + if (this._cascadeColorEnabled === cascadeColorEnabled) + return; + this._cascadeColorEnabled = cascadeColorEnabled; + this._renderCmd.setCascadeColorEnabledDirty(); + }, + + /** + * Set whether color should be changed with the opacity value, + * useless in cc.Node, but this function is override in some class to have such behavior. + * @function + * @param {Boolean} opacityValue + */ + setOpacityModifyRGB: function (opacityValue) { + }, + + /** + * Get whether color should be changed with the opacity value + * @function + * @return {Boolean} + */ + isOpacityModifyRGB: function () { + return false; + }, + + _createRenderCmd: function () { + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return new cc.Node.CanvasRenderCmd(this); + else + return new cc.Node.WebGLRenderCmd(this); + }, + + /** Search the children of the receiving node to perform processing for nodes which share a name. + * + * @param name The name to search for, supports c++11 regular expression. + * Search syntax options: + * `//`: Can only be placed at the begin of the search string. This indicates that it will search recursively. + * `..`: The search should move up to the node's parent. Can only be placed at the end of string. + * `/` : When placed anywhere but the start of the search string, this indicates that the search should move to the node's children. + * + * @code + * enumerateChildren("//MyName", ...): This searches the children recursively and matches any node with the name `MyName`. + * enumerateChildren("[[:alnum:]]+", ...): This search string matches every node of its children. + * enumerateChildren("A[[:digit:]]", ...): This searches the node's children and returns any child named `A0`, `A1`, ..., `A9`. + * enumerateChildren("Abby/Normal", ...): This searches the node's grandchildren and returns any node whose name is `Normal` + * and whose parent is named `Abby`. + * enumerateChildren("//Abby/Normal", ...): This searches recursively and returns any node whose name is `Normal` and whose + * parent is named `Abby`. + * @endcode + * + * @warning Only support alpha or number for name, and not support unicode. + * + * @param callback A callback function to execute on nodes that match the `name` parameter. The function takes the following arguments: + * `node` + * A node that matches the name + * And returns a boolean result. Your callback can return `true` to terminate the enumeration. + * + */ + enumerateChildren: function (name, callback) { + cc.assert(name && name.length != 0, "Invalid name"); + cc.assert(callback != null, "Invalid callback function"); + + var length = name.length; + var subStrStartPos = 0; + var subStrlength = length; + + // Starts with '//'? + var searchRecursively = false; + if (length > 2 && name[0] === "/" && name[1] === "/") { + searchRecursively = true; + subStrStartPos = 2; + subStrlength -= 2; + } + + var searchFromParent = false; + if (length > 3 && name[length - 3] === "/" && name[length - 2] === "." && name[length - 1] === ".") { + searchFromParent = true; + subStrlength -= 3; + } + + var newName = name.substr(subStrStartPos, subStrlength); + + if (searchFromParent) + newName = "[[:alnum:]]+/" + newName; + + if (searchRecursively) + this.doEnumerateRecursive(this, newName, callback); + else + this.doEnumerate(newName, callback); + }, + + doEnumerateRecursive: function (node, name, callback) { + var ret = false; + if (node.doEnumerate(name, callback)) { + ret = true; + } else { + var child, + children = node.getChildren(), + length = children.length; + // search its children + for (var i = 0; i < length; i++) { + child = children[i]; + if (this.doEnumerateRecursive(child, name, callback)) { + ret = true; + break; + } + } + } + return ret; + }, + + doEnumerate: function (name, callback) { + // name may be xxx/yyy, should find its parent + var pos = name.indexOf('/'); + var searchName = name; + var needRecursive = false; + if (pos !== -1) { + searchName = name.substr(0, pos); + //name.erase(0, pos+1); + needRecursive = true; + } + + var ret = false; + var child, + children = this._children, + length = children.length; + for (var i = 0; i < length; i++) { + child = children[i]; + if (child._name.indexOf(searchName) !== -1) { + if (!needRecursive) { + // terminate enumeration if callback return true + if (callback(child)) { + ret = true; + break; + } + } else { + ret = child.doEnumerate(name, callback); + if (ret) + break; + } + } + } + + return ret; + } +}); + +/** + * Allocates and initializes a node. + * @deprecated since v3.0, please use new construction instead. + * @see cc.Node + * @return {cc.Node} + */ +cc.Node.create = function () { + return new cc.Node(); +}; + +cc.Node._stateCallbackType = { + onEnter: 1, + onExit: 2, + cleanup: 3, + onEnterTransitionDidFinish: 4, + onExitTransitionDidStart: 5, + max: 6 +}; +cc.Node._performStacks = [[]]; +cc.Node._performing = 0; + +cc.assert(cc.isFunction(cc._tmp.PrototypeCCNode), cc._LogInfos.MissingFile, "BaseNodesPropertyDefine.js"); +cc._tmp.PrototypeCCNode(); +delete cc._tmp.PrototypeCCNode; diff --git a/frameworks/cocos2d-html5/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js new file mode 100644 index 0000000..43c46d0 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js @@ -0,0 +1,657 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +//---------------------- Customer render cmd -------------------- +cc.CustomRenderCmd = function (target, func) { + this._needDraw = true; + this._target = target; + this._callback = func; +}; +cc.CustomRenderCmd.prototype.rendering = function (ctx, scaleX, scaleY) { + if (!this._callback) + return; + this._callback.call(this._target, ctx, scaleX, scaleY); +}; +cc.CustomRenderCmd.prototype.needDraw = function () { + return this._needDraw; +}; + +var dirtyFlags = cc.Node._dirtyFlags = { + transformDirty: 1 << 0, visibleDirty: 1 << 1, colorDirty: 1 << 2, opacityDirty: 1 << 3, cacheDirty: 1 << 4, + orderDirty: 1 << 5, textDirty: 1 << 6, gradientDirty: 1 << 7, textureDirty: 1 << 8, + contentDirty: 1 << 9, + COUNT: 10, + all: (1 << 10) - 1 +}; + +var ONE_DEGREE = Math.PI / 180; + +function transformChildTree(root) { + var index = 1; + var children, child, curr, parentCmd, i, len; + var stack = cc.Node._performStacks[cc.Node._performing]; + if (!stack) { + stack = []; + cc.Node._performStacks.push(stack); + } + stack.length = 0; + cc.Node._performing++; + stack[0] = root; + while (index) { + index--; + curr = stack[index]; + // Avoid memory leak + stack[index] = null; + if (!curr) continue; + children = curr._children; + if (children && children.length > 0) { + parentCmd = curr._renderCmd; + for (i = 0, len = children.length; i < len; ++i) { + child = children[i]; + stack[index] = child; + index++; + child._renderCmd.transform(parentCmd); + } + } + var pChildren = curr._protectedChildren; + if (pChildren && pChildren.length > 0) { + parentCmd = curr._renderCmd; + for (i = 0, len = pChildren.length; i < len; ++i) { + child = pChildren[i]; + stack[index] = child; + index++; + child._renderCmd.transform(parentCmd); + } + } + } + cc.Node._performing--; +} + +//-------------------------Base ------------------------- +cc.Node.RenderCmd = function (renderable) { + this._node = renderable; + this._anchorPointInPoints = {x: 0, y: 0}; + this._displayedColor = cc.color(255, 255, 255, 255); +}; + +cc.Node.RenderCmd.prototype = { + constructor: cc.Node.RenderCmd, + + _needDraw: false, + _dirtyFlag: 1, + _curLevel: -1, + + _displayedOpacity: 255, + _cascadeColorEnabledDirty: false, + _cascadeOpacityEnabledDirty: false, + + _transform: null, + _worldTransform: null, + _inverse: null, + + needDraw: function () { + return this._needDraw; + }, + + getAnchorPointInPoints: function () { + return cc.p(this._anchorPointInPoints); + }, + + getDisplayedColor: function () { + var tmpColor = this._displayedColor; + return cc.color(tmpColor.r, tmpColor.g, tmpColor.b, tmpColor.a); + }, + + getDisplayedOpacity: function () { + return this._displayedOpacity; + }, + + setCascadeColorEnabledDirty: function () { + this._cascadeColorEnabledDirty = true; + this.setDirtyFlag(cc.Node._dirtyFlags.colorDirty); + }, + + setCascadeOpacityEnabledDirty: function () { + this._cascadeOpacityEnabledDirty = true; + this.setDirtyFlag(cc.Node._dirtyFlags.opacityDirty); + }, + + getParentToNodeTransform: function () { + if (!this._inverse) { + this._inverse = {a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0}; + } + if (this._dirtyFlag & cc.Node._dirtyFlags.transformDirty) { + cc.affineTransformInvertOut(this.getNodeToParentTransform(), this._inverse); + } + return this._inverse; + }, + + detachFromParent: function () { + }, + + _updateAnchorPointInPoint: function () { + var locAPP = this._anchorPointInPoints, locSize = this._node._contentSize, locAnchorPoint = this._node._anchorPoint; + locAPP.x = locSize.width * locAnchorPoint.x; + locAPP.y = locSize.height * locAnchorPoint.y; + this.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }, + + setDirtyFlag: function (dirtyFlag) { + if (this._dirtyFlag === 0 && dirtyFlag !== 0) + cc.renderer.pushDirtyNode(this); + this._dirtyFlag |= dirtyFlag; + }, + + getParentRenderCmd: function () { + if (this._node && this._node._parent && this._node._parent._renderCmd) + return this._node._parent._renderCmd; + return null; + }, + + transform: function (parentCmd, recursive) { + if (!this._transform) { + this._transform = {a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0}; + this._worldTransform = {a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0}; + } + + var node = this._node, + pt = parentCmd ? parentCmd._worldTransform : null, + t = this._transform, + wt = this._worldTransform; //get the world transform + + if (node._usingNormalizedPosition && node._parent) { + var conSize = node._parent._contentSize; + node._position.x = node._normalizedPosition.x * conSize.width; + node._position.y = node._normalizedPosition.y * conSize.height; + node._normalizedPositionDirty = false; + } + + var hasRotation = node._rotationX || node._rotationY; + var hasSkew = node._skewX || node._skewY; + var sx = node._scaleX, sy = node._scaleY; + var appX = this._anchorPointInPoints.x, appY = this._anchorPointInPoints.y; + var a = 1, b = 0, c = 0, d = 1; + if (hasRotation || hasSkew) { + // position + t.tx = node._position.x; + t.ty = node._position.y; + + // rotation + if (hasRotation) { + var rotationRadiansX = node._rotationX * ONE_DEGREE; + c = Math.sin(rotationRadiansX); + d = Math.cos(rotationRadiansX); + if (node._rotationY === node._rotationX) { + a = d; + b = -c; + } + else { + var rotationRadiansY = node._rotationY * ONE_DEGREE; + a = Math.cos(rotationRadiansY); + b = -Math.sin(rotationRadiansY); + } + } + + // scale + t.a = a *= sx; + t.b = b *= sx; + t.c = c *= sy; + t.d = d *= sy; + + // skew + if (hasSkew) { + var skx = Math.tan(node._skewX * ONE_DEGREE); + var sky = Math.tan(node._skewY * ONE_DEGREE); + if (skx === Infinity) + skx = 99999999; + if (sky === Infinity) + sky = 99999999; + t.a = a + c * sky; + t.b = b + d * sky; + t.c = c + a * skx; + t.d = d + b * skx; + } + + if (appX || appY) { + t.tx -= t.a * appX + t.c * appY; + t.ty -= t.b * appX + t.d * appY; + // adjust anchorPoint + if (node._ignoreAnchorPointForPosition) { + t.tx += appX; + t.ty += appY; + } + } + + if (node._additionalTransformDirty) { + cc.affineTransformConcatIn(t, node._additionalTransform); + } + + if (pt) { + // cc.AffineTransformConcat is incorrect at get world transform + wt.a = t.a * pt.a + t.b * pt.c; //a + wt.b = t.a * pt.b + t.b * pt.d; //b + wt.c = t.c * pt.a + t.d * pt.c; //c + wt.d = t.c * pt.b + t.d * pt.d; //d + wt.tx = pt.a * t.tx + pt.c * t.ty + pt.tx; + wt.ty = pt.d * t.ty + pt.ty + pt.b * t.tx; + } else { + wt.a = t.a; + wt.b = t.b; + wt.c = t.c; + wt.d = t.d; + wt.tx = t.tx; + wt.ty = t.ty; + } + } + else { + t.a = sx; + t.b = 0; + t.c = 0; + t.d = sy; + t.tx = node._position.x; + t.ty = node._position.y; + + if (appX || appY) { + t.tx -= t.a * appX; + t.ty -= t.d * appY; + // adjust anchorPoint + if (node._ignoreAnchorPointForPosition) { + t.tx += appX; + t.ty += appY; + } + } + + if (node._additionalTransformDirty) { + cc.affineTransformConcatIn(t, node._additionalTransform); + } + + if (pt) { + wt.a = t.a * pt.a + t.b * pt.c; + wt.b = t.a * pt.b + t.b * pt.d; + wt.c = t.c * pt.a + t.d * pt.c; + wt.d = t.c * pt.b + t.d * pt.d; + wt.tx = t.tx * pt.a + t.ty * pt.c + pt.tx; + wt.ty = t.tx * pt.b + t.ty * pt.d + pt.ty; + } else { + wt.a = t.a; + wt.b = t.b; + wt.c = t.c; + wt.d = t.d; + wt.tx = t.tx; + wt.ty = t.ty; + } + } + + if (this._updateCurrentRegions) { + this._updateCurrentRegions(); + this._notifyRegionStatus && this._notifyRegionStatus(cc.Node.CanvasRenderCmd.RegionStatus.DirtyDouble); + } + + if (recursive) { + transformChildTree(node); + } + + this._cacheDirty = true; + }, + + getNodeToParentTransform: function () { + if (!this._transform || this._dirtyFlag & cc.Node._dirtyFlags.transformDirty) { + this.transform(); + } + return this._transform; + }, + + visit: function (parentCmd) { + var node = this._node, renderer = cc.renderer; + + parentCmd = parentCmd || this.getParentRenderCmd(); + if (parentCmd) + this._curLevel = parentCmd._curLevel + 1; + + if (isNaN(node._customZ)) { + node._vertexZ = renderer.assignedZ; + renderer.assignedZ += renderer.assignedZStep; + } + + this._syncStatus(parentCmd); + }, + + _updateDisplayColor: function (parentColor) { + var node = this._node; + var locDispColor = this._displayedColor, locRealColor = node._realColor; + var i, len, selChildren, item; + this._notifyRegionStatus && this._notifyRegionStatus(cc.Node.CanvasRenderCmd.RegionStatus.Dirty); + if (this._cascadeColorEnabledDirty && !node._cascadeColorEnabled) { + locDispColor.r = locRealColor.r; + locDispColor.g = locRealColor.g; + locDispColor.b = locRealColor.b; + var whiteColor = new cc.Color(255, 255, 255, 255); + selChildren = node._children; + for (i = 0, len = selChildren.length; i < len; i++) { + item = selChildren[i]; + if (item && item._renderCmd) + item._renderCmd._updateDisplayColor(whiteColor); + } + this._cascadeColorEnabledDirty = false; + } else { + if (parentColor === undefined) { + var locParent = node._parent; + if (locParent && locParent._cascadeColorEnabled) + parentColor = locParent.getDisplayedColor(); + else + parentColor = cc.color.WHITE; + } + locDispColor.r = 0 | (locRealColor.r * parentColor.r / 255.0); + locDispColor.g = 0 | (locRealColor.g * parentColor.g / 255.0); + locDispColor.b = 0 | (locRealColor.b * parentColor.b / 255.0); + if (node._cascadeColorEnabled) { + selChildren = node._children; + for (i = 0, len = selChildren.length; i < len; i++) { + item = selChildren[i]; + if (item && item._renderCmd) { + item._renderCmd._updateDisplayColor(locDispColor); + item._renderCmd._updateColor(); + } + } + } + } + this._dirtyFlag &= ~dirtyFlags.colorDirty; + }, + + _updateDisplayOpacity: function (parentOpacity) { + var node = this._node; + var i, len, selChildren, item; + this._notifyRegionStatus && this._notifyRegionStatus(cc.Node.CanvasRenderCmd.RegionStatus.Dirty); + if (this._cascadeOpacityEnabledDirty && !node._cascadeOpacityEnabled) { + this._displayedOpacity = node._realOpacity; + selChildren = node._children; + for (i = 0, len = selChildren.length; i < len; i++) { + item = selChildren[i]; + if (item && item._renderCmd) + item._renderCmd._updateDisplayOpacity(255); + } + this._cascadeOpacityEnabledDirty = false; + } else { + if (parentOpacity === undefined) { + var locParent = node._parent; + parentOpacity = 255; + if (locParent && locParent._cascadeOpacityEnabled) + parentOpacity = locParent.getDisplayedOpacity(); + } + this._displayedOpacity = node._realOpacity * parentOpacity / 255.0; + if (node._cascadeOpacityEnabled) { + selChildren = node._children; + for (i = 0, len = selChildren.length; i < len; i++) { + item = selChildren[i]; + if (item && item._renderCmd) { + item._renderCmd._updateDisplayOpacity(this._displayedOpacity); + item._renderCmd._updateColor(); + } + } + } + } + this._dirtyFlag &= ~dirtyFlags.opacityDirty; + }, + + _syncDisplayColor: function (parentColor) { + var node = this._node, locDispColor = this._displayedColor, locRealColor = node._realColor; + if (parentColor === undefined) { + var locParent = node._parent; + if (locParent && locParent._cascadeColorEnabled) + parentColor = locParent.getDisplayedColor(); + else + parentColor = cc.color.WHITE; + } + locDispColor.r = 0 | (locRealColor.r * parentColor.r / 255.0); + locDispColor.g = 0 | (locRealColor.g * parentColor.g / 255.0); + locDispColor.b = 0 | (locRealColor.b * parentColor.b / 255.0); + }, + + _syncDisplayOpacity: function (parentOpacity) { + var node = this._node; + if (parentOpacity === undefined) { + var locParent = node._parent; + parentOpacity = 255; + if (locParent && locParent._cascadeOpacityEnabled) + parentOpacity = locParent.getDisplayedOpacity(); + } + this._displayedOpacity = node._realOpacity * parentOpacity / 255.0; + }, + + _updateColor: function () { + }, + + _propagateFlagsDown: function (parentCmd) { + var locFlag = this._dirtyFlag; + var parentNode = parentCmd ? parentCmd._node : null; + + if(parentNode && parentNode._cascadeColorEnabled && (parentCmd._dirtyFlag & dirtyFlags.colorDirty)) + locFlag |= dirtyFlags.colorDirty; + + if(parentNode && parentNode._cascadeOpacityEnabled && (parentCmd._dirtyFlag & dirtyFlags.opacityDirty)) + locFlag |= dirtyFlags.opacityDirty; + + if(parentCmd && (parentCmd._dirtyFlag & dirtyFlags.transformDirty)) + locFlag |= dirtyFlags.transformDirty; + + this._dirtyFlag = locFlag; + }, + + updateStatus: function () { + var locFlag = this._dirtyFlag; + var colorDirty = locFlag & dirtyFlags.colorDirty, + opacityDirty = locFlag & dirtyFlags.opacityDirty; + + if (locFlag & dirtyFlags.contentDirty) { + this._notifyRegionStatus && this._notifyRegionStatus(cc.Node.CanvasRenderCmd.RegionStatus.Dirty); + this._dirtyFlag &= ~dirtyFlags.contentDirty; + } + + if (colorDirty) + this._updateDisplayColor(); + + if (opacityDirty) + this._updateDisplayOpacity(); + + if (colorDirty || opacityDirty) + this._updateColor(); + + if (locFlag & dirtyFlags.transformDirty) { + //update the transform + this.transform(this.getParentRenderCmd(), true); + this._dirtyFlag &= ~dirtyFlags.transformDirty; + } + + if (locFlag & dirtyFlags.orderDirty) + this._dirtyFlag &= ~dirtyFlags.orderDirty; + }, + + _syncStatus: function (parentCmd) { + // In the visit logic does not restore the _dirtyFlag + // Because child elements need parent's _dirtyFlag to change himself + var locFlag = this._dirtyFlag, parentNode = parentCmd ? parentCmd._node : null; + + // There is a possibility: + // The parent element changed color, child element not change + // This will cause the parent element changed color + // But while the child element does not enter the circulation + // Here will be reset state in last + // In order the child elements get the parent state + if (parentNode && parentNode._cascadeColorEnabled && (parentCmd._dirtyFlag & dirtyFlags.colorDirty)) + locFlag |= dirtyFlags.colorDirty; + + if (parentNode && parentNode._cascadeOpacityEnabled && (parentCmd._dirtyFlag & dirtyFlags.opacityDirty)) + locFlag |= dirtyFlags.opacityDirty; + + if (parentCmd && (parentCmd._dirtyFlag & dirtyFlags.transformDirty)) + locFlag |= dirtyFlags.transformDirty; + + this._dirtyFlag = locFlag; + + var colorDirty = locFlag & dirtyFlags.colorDirty, + opacityDirty = locFlag & dirtyFlags.opacityDirty; + + if (colorDirty) + //update the color + this._syncDisplayColor(); + + if (opacityDirty) + //update the opacity + this._syncDisplayOpacity(); + + if (colorDirty || opacityDirty) + this._updateColor(); + + if (locFlag & dirtyFlags.transformDirty) + //update the transform + this.transform(parentCmd); + + if (locFlag & dirtyFlags.orderDirty) + this._dirtyFlag &= ~dirtyFlags.orderDirty; + }, + + setShaderProgram: function (shaderProgram) { + //do nothing. + }, + + getShaderProgram: function () { + return null; + }, + + getGLProgramState: function () { + return null; + }, + + setGLProgramState: function (glProgramState) { + // do nothing + }, +}; + +cc.Node.RenderCmd.prototype.originTransform = cc.Node.RenderCmd.prototype.transform; +cc.Node.RenderCmd.prototype.originUpdateStatus = cc.Node.RenderCmd.prototype.updateStatus; +cc.Node.RenderCmd.prototype._originSyncStatus = cc.Node.RenderCmd.prototype._syncStatus; + +//-----------------------Canvas --------------------------- + +(function () { +//The cc.Node's render command for Canvas + cc.Node.CanvasRenderCmd = function (renderable) { + this._node = renderable; + this._anchorPointInPoints = {x: 0, y: 0}; + this._displayedColor = cc.color(255, 255, 255, 255); + this._cachedParent = null; + this._cacheDirty = false; + this._currentRegion = new cc.Region(); + this._oldRegion = new cc.Region(); + this._regionFlag = 0; + this._canUseDirtyRegion = false; + }; + + cc.Node.CanvasRenderCmd.RegionStatus = { + NotDirty: 0, //the region is not dirty + Dirty: 1, //the region is dirty, because of color, opacity or context + DirtyDouble: 2 //the region is moved, the old and the new one need considered when rendering + }; + + var proto = cc.Node.CanvasRenderCmd.prototype = Object.create(cc.Node.RenderCmd.prototype); + proto.constructor = cc.Node.CanvasRenderCmd; + proto._rootCtor = cc.Node.CanvasRenderCmd; + + proto._notifyRegionStatus = function (status) { + if (this._needDraw && this._regionFlag < status) { + this._regionFlag = status; + } + }; + + var localBB = new cc.Rect(); + proto.getLocalBB = function () { + var node = this._node; + localBB.x = localBB.y = 0; + localBB.width = node._contentSize.width; + localBB.height = node._contentSize.height; + return localBB; + }; + + proto._updateCurrentRegions = function () { + var temp = this._currentRegion; + this._currentRegion = this._oldRegion; + this._oldRegion = temp; + //hittest will call the transform, and set region flag to DirtyDouble, and the changes need to be considered for rendering + if (cc.Node.CanvasRenderCmd.RegionStatus.DirtyDouble === this._regionFlag && (!this._currentRegion.isEmpty())) { + this._oldRegion.union(this._currentRegion); + } + this._currentRegion.updateRegion(this.getLocalBB(), this._worldTransform); + }; + + proto.setDirtyFlag = function (dirtyFlag, child) { + cc.Node.RenderCmd.prototype.setDirtyFlag.call(this, dirtyFlag, child); + this._setCacheDirty(child); //TODO it should remove from here. + if (this._cachedParent) + this._cachedParent.setDirtyFlag(dirtyFlag, true); + }; + + proto._setCacheDirty = function () { + if (this._cacheDirty === false) { + this._cacheDirty = true; + var cachedP = this._cachedParent; + cachedP && cachedP !== this && cachedP._setNodeDirtyForCache && cachedP._setNodeDirtyForCache(); + } + }; + + proto._setCachedParent = function (cachedParent) { + if (this._cachedParent === cachedParent) + return; + + this._cachedParent = cachedParent; + var children = this._node._children; + for (var i = 0, len = children.length; i < len; i++) + children[i]._renderCmd._setCachedParent(cachedParent); + }; + + proto.detachFromParent = function () { + this._cachedParent = null; + var selChildren = this._node._children, item; + for (var i = 0, len = selChildren.length; i < len; i++) { + item = selChildren[i]; + if (item && item._renderCmd) + item._renderCmd.detachFromParent(); + } + }; + + //util functions + cc.Node.CanvasRenderCmd._getCompositeOperationByBlendFunc = function (blendFunc) { + if (!blendFunc) + return "source-over"; + else { + if (( blendFunc.src === cc.SRC_ALPHA && blendFunc.dst === cc.ONE) || (blendFunc.src === cc.ONE && blendFunc.dst === cc.ONE)) + return "lighter"; + else if (blendFunc.src === cc.ZERO && blendFunc.dst === cc.SRC_ALPHA) + return "destination-in"; + else if (blendFunc.src === cc.ZERO && blendFunc.dst === cc.ONE_MINUS_SRC_ALPHA) + return "destination-out"; + else + return "source-over"; + } + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/core/base-nodes/CCNodeWebGLRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/core/base-nodes/CCNodeWebGLRenderCmd.js new file mode 100644 index 0000000..7680017 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/base-nodes/CCNodeWebGLRenderCmd.js @@ -0,0 +1,65 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ +// ------------------------------ The cc.Node's render command for WebGL ---------------------------------- +(function () { + cc.Node.WebGLRenderCmd = function (renderable) { + this._node = renderable; + this._anchorPointInPoints = {x: 0, y: 0}; + this._displayedColor = cc.color(255, 255, 255, 255); + this._glProgramState = null; + }; + + var proto = cc.Node.WebGLRenderCmd.prototype = Object.create(cc.Node.RenderCmd.prototype); + proto.constructor = cc.Node.WebGLRenderCmd; + proto._rootCtor = cc.Node.WebGLRenderCmd; + + proto._updateColor = function () { + }; + + proto.setShaderProgram = function (shaderProgram) { + this._glProgramState = cc.GLProgramState.getOrCreateWithGLProgram(shaderProgram); + }; + + proto.getShaderProgram = function () { + return this._glProgramState ? this._glProgramState.getGLProgram() : null; + }; + + proto.getGLProgramState = function () { + return this._glProgramState; + }; + + proto.setGLProgramState = function (glProgramState) { + this._glProgramState = glProgramState; + }; + + // Use a property getter/setter for backwards compatability, and + // to ease the transition from using glPrograms directly, to + // using glProgramStates. + Object.defineProperty(proto, '_shaderProgram', { + set: function (value) { this.setShaderProgram(value); }, + get: function () { return this.getShaderProgram(); } + }); + /** @expose */ + proto._shaderProgram; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/core/cocoa/CCAffineTransform.js b/frameworks/cocos2d-html5/cocos2d/core/cocoa/CCAffineTransform.js new file mode 100644 index 0000000..8ff0957 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/cocoa/CCAffineTransform.js @@ -0,0 +1,305 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + *

cc.AffineTransform class represent an affine transform matrix. It's composed basically by translation, rotation, scale transformations.
+ * Please do not use its constructor directly, use cc.affineTransformMake alias function instead. + *

+ * @class cc.AffineTransform + * @param {Number} a + * @param {Number} b + * @param {Number} c + * @param {Number} d + * @param {Number} tx + * @param {Number} ty + * @see cc.affineTransformMake + */ +cc.AffineTransform = function (a, b, c, d, tx, ty) { + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.tx = tx; + this.ty = ty; +}; + +/** + * Create a cc.AffineTransform object with all contents in the matrix + * @function + * + * @param {Number} a + * @param {Number} b + * @param {Number} c + * @param {Number} d + * @param {Number} tx + * @param {Number} ty + * @return {cc.AffineTransform} + */ +cc.affineTransformMake = function (a, b, c, d, tx, ty) { + return {a: a, b: b, c: c, d: d, tx: tx, ty: ty}; +}; + +/** + * Apply the affine transformation on a point. + * @function + * + * @param {cc.Point|Number} point or x + * @param {cc.AffineTransform|Number} transOrY transform matrix or y + * @param {cc.AffineTransform} t transform matrix or y + * @return {cc.Point} + */ +cc.pointApplyAffineTransform = function (point, transOrY, t) { + var x, y; + if (t === undefined) { + t = transOrY; + x = point.x; + y = point.y; + } else { + x = point; + y = transOrY; + } + return {x: t.a * x + t.c * y + t.tx, y: t.b * x + t.d * y + t.ty}; +}; + +cc._pointApplyAffineTransform = function (x, y, t) { //it will remove. + return cc.pointApplyAffineTransform(x, y, t); +}; + +/** + * Apply the affine transformation on a size. + * @function + * + * @param {cc.Size} size + * @param {cc.AffineTransform} t + * @return {cc.Size} + */ +cc.sizeApplyAffineTransform = function (size, t) { + return {width: t.a * size.width + t.c * size.height, height: t.b * size.width + t.d * size.height}; +}; + +/** + *

Create a identity transformation matrix:
+ * [ 1, 0, 0,
+ * 0, 1, 0 ]

+ * @function + * + * @return {cc.AffineTransform} + */ +cc.affineTransformMakeIdentity = function () { + return {a: 1.0, b: 0.0, c: 0.0, d: 1.0, tx: 0.0, ty: 0.0}; +}; + +/** + *

Create a identity transformation matrix:
+ * [ 1, 0, 0,
+ * 0, 1, 0 ]

+ * @function + * + * @return {cc.AffineTransform} + * @deprecated since v3.0, please use cc.affineTransformMakeIdentity() instead + * @see cc.affineTransformMakeIdentity + */ +cc.affineTransformIdentity = function () { + return {a: 1.0, b: 0.0, c: 0.0, d: 1.0, tx: 0.0, ty: 0.0}; +}; + +/** + * Apply the affine transformation on a rect. + * @function + * + * @param {cc.Rect} rect + * @param {cc.AffineTransform} anAffineTransform + * @return {cc.Rect} + */ +cc.rectApplyAffineTransform = function (rect, anAffineTransform) { + var top = cc.rectGetMinY(rect); + var left = cc.rectGetMinX(rect); + var right = cc.rectGetMaxX(rect); + var bottom = cc.rectGetMaxY(rect); + + var topLeft = cc.pointApplyAffineTransform(left, top, anAffineTransform); + var topRight = cc.pointApplyAffineTransform(right, top, anAffineTransform); + var bottomLeft = cc.pointApplyAffineTransform(left, bottom, anAffineTransform); + var bottomRight = cc.pointApplyAffineTransform(right, bottom, anAffineTransform); + + var minX = Math.min(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x); + var maxX = Math.max(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x); + var minY = Math.min(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y); + var maxY = Math.max(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y); + + return cc.rect(minX, minY, (maxX - minX), (maxY - minY)); +}; + +cc._rectApplyAffineTransformIn = function (rect, anAffineTransform) { + var top = cc.rectGetMinY(rect); + var left = cc.rectGetMinX(rect); + var right = cc.rectGetMaxX(rect); + var bottom = cc.rectGetMaxY(rect); + + var topLeft = cc.pointApplyAffineTransform(left, top, anAffineTransform); + var topRight = cc.pointApplyAffineTransform(right, top, anAffineTransform); + var bottomLeft = cc.pointApplyAffineTransform(left, bottom, anAffineTransform); + var bottomRight = cc.pointApplyAffineTransform(right, bottom, anAffineTransform); + + var minX = Math.min(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x); + var maxX = Math.max(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x); + var minY = Math.min(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y); + var maxY = Math.max(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y); + + rect.x = minX; + rect.y = minY; + rect.width = maxX - minX; + rect.height = maxY - minY; + return rect; +}; + +/** + * Create a new affine transformation with a base transformation matrix and a translation based on it. + * @function + * + * @param {cc.AffineTransform} t The base affine transform object + * @param {Number} tx The translation on x axis + * @param {Number} ty The translation on y axis + * @return {cc.AffineTransform} + */ +cc.affineTransformTranslate = function (t, tx, ty) { + return { + a: t.a, + b: t.b, + c: t.c, + d: t.d, + tx: t.tx + t.a * tx + t.c * ty, + ty: t.ty + t.b * tx + t.d * ty + }; +}; + +/** + * Create a new affine transformation with a base transformation matrix and a scale based on it. + * @function + * @param {cc.AffineTransform} t The base affine transform object + * @param {Number} sx The scale on x axis + * @param {Number} sy The scale on y axis + * @return {cc.AffineTransform} + */ +cc.affineTransformScale = function (t, sx, sy) { + return {a: t.a * sx, b: t.b * sx, c: t.c * sy, d: t.d * sy, tx: t.tx, ty: t.ty}; +}; + +/** + * Create a new affine transformation with a base transformation matrix and a rotation based on it. + * @function + * @param {cc.AffineTransform} aTransform The base affine transform object + * @param {Number} anAngle The angle to rotate + * @return {cc.AffineTransform} + */ +cc.affineTransformRotate = function (aTransform, anAngle) { + var fSin = Math.sin(anAngle); + var fCos = Math.cos(anAngle); + + return { + a: aTransform.a * fCos + aTransform.c * fSin, + b: aTransform.b * fCos + aTransform.d * fSin, + c: aTransform.c * fCos - aTransform.a * fSin, + d: aTransform.d * fCos - aTransform.b * fSin, + tx: aTransform.tx, + ty: aTransform.ty + }; +}; + +/** + * Concatenate a transform matrix to another and return the result:
+ * t' = t1 * t2 + * @function + * @param {cc.AffineTransform} t1 The first transform object + * @param {cc.AffineTransform} t2 The transform object to concatenate + * @return {cc.AffineTransform} The result of concatenation + */ +cc.affineTransformConcat = function (t1, t2) { + return { + a: t1.a * t2.a + t1.b * t2.c, //a + b: t1.a * t2.b + t1.b * t2.d, //b + c: t1.c * t2.a + t1.d * t2.c, //c + d: t1.c * t2.b + t1.d * t2.d, //d + tx: t1.tx * t2.a + t1.ty * t2.c + t2.tx, //tx + ty: t1.tx * t2.b + t1.ty * t2.d + t2.ty + }; //ty +}; + +/** + * Concatenate a transform matrix to another
+ * The results are reflected in the first matrix.
+ * t' = t1 * t2 + * @function + * @param {cc.AffineTransform} t1 The first transform object + * @param {cc.AffineTransform} t2 The transform object to concatenate + * @return {cc.AffineTransform} The result of concatenation + */ +cc.affineTransformConcatIn = function (t1, t2) { + var a = t1.a, b = t1.b, c = t1.c, d = t1.d, tx = t1.tx, ty = t1.ty; + t1.a = a * t2.a + b * t2.c; + t1.b = a * t2.b + b * t2.d; + t1.c = c * t2.a + d * t2.c; + t1.d = c * t2.b + d * t2.d; + t1.tx = tx * t2.a + ty * t2.c + t2.tx; + t1.ty = tx * t2.b + ty * t2.d + t2.ty; + return t1; +}; + +/** + * Return true if an affine transform equals to another, false otherwise. + * @function + * @param {cc.AffineTransform} t1 + * @param {cc.AffineTransform} t2 + * @return {Boolean} + */ +cc.affineTransformEqualToTransform = function (t1, t2) { + return ((t1.a === t2.a) && (t1.b === t2.b) && (t1.c === t2.c) && (t1.d === t2.d) && (t1.tx === t2.tx) && (t1.ty === t2.ty)); +}; + +/** + * Get the invert transform of an AffineTransform object + * @function + * @param {cc.AffineTransform} t + * @return {cc.AffineTransform} The inverted transform object + */ +cc.affineTransformInvert = function (t) { + var determinant = 1 / (t.a * t.d - t.b * t.c); + return { + a: determinant * t.d, b: -determinant * t.b, c: -determinant * t.c, d: determinant * t.a, + tx: determinant * (t.c * t.ty - t.d * t.tx), ty: determinant * (t.b * t.tx - t.a * t.ty) + }; +}; + +cc.affineTransformInvertOut = function (t, out) { + var a = t.a, b = t.b, c = t.c, d = t.d; + var determinant = 1 / (a * d - b * c); + out.a = determinant * d; + out.b = -determinant * b; + out.c = -determinant * c; + out.d = determinant * a; + out.tx = determinant * (c * t.ty - d * t.tx); + out.ty = determinant * (b * t.tx - a * t.ty); +}; diff --git a/frameworks/cocos2d-html5/cocos2d/core/cocoa/CCGeometry.js b/frameworks/cocos2d-html5/cocos2d/core/cocoa/CCGeometry.js new file mode 100644 index 0000000..a3690f2 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/cocoa/CCGeometry.js @@ -0,0 +1,338 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * cc.Point is the class for point object, please do not use its constructor to create points, use cc.p() alias function instead. + * @class cc.Point + * @param {Number} x + * @param {Number} y + * + * @property x {Number} + * @property y {Number} + * @see cc.p + */ +cc.Point = function (x, y) { + this.x = x || 0; + this.y = y || 0; +}; + +/** + * Helper function that creates a cc.Point. + * @function + * @param {Number|cc.Point} x a Number or a size object + * @param {Number} y + * @return {cc.Point} + * @example + * var point1 = cc.p(); + * var point2 = cc.p(100, 100); + * var point3 = cc.p(point2); + * var point4 = cc.p({x: 100, y: 100}); + */ +cc.p = function (x, y) { + // This can actually make use of "hidden classes" in JITs and thus decrease + // memory usage and overall performance drastically + // return cc.p(x, y); + // but this one will instead flood the heap with newly allocated hash maps + // giving little room for optimization by the JIT, + // note: we have tested this item on Chrome and firefox, it is faster than cc.p(x, y) + if (x === undefined) + return {x: 0, y: 0}; + if (y === undefined) + return {x: x.x, y: x.y}; + return {x: x, y: y}; +}; + +/** + * Check whether a point's value equals to another + * @function + * @param {cc.Point} point1 + * @param {cc.Point} point2 + * @return {Boolean} + */ +cc.pointEqualToPoint = function (point1, point2) { + return point1 && point2 && (point1.x === point2.x) && (point1.y === point2.y); +}; + + +/** + * cc.Size is the class for size object, please do not use its constructor to create sizes, use cc.size() alias function instead. + * @class cc.Size + * @param {Number} width + * @param {Number} height + * @property {Number} width + * @property {Number} height + * @see cc.size + */ +cc.Size = function (width, height) { + this.width = width || 0; + this.height = height || 0; +}; + +/** + * Helper function that creates a cc.Size. + * @function + * @param {Number|cc.Size} w width or a size object + * @param {Number} h height + * @return {cc.Size} + * @example + * var size1 = cc.size(); + * var size2 = cc.size(100,100); + * var size3 = cc.size(size2); + * var size4 = cc.size({width: 100, height: 100}); + */ +cc.size = function (w, h) { + // This can actually make use of "hidden classes" in JITs and thus decrease + // memory usage and overall performance drastically + //return cc.size(w, h); + // but this one will instead flood the heap with newly allocated hash maps + // giving little room for optimization by the JIT + // note: we have tested this item on Chrome and firefox, it is faster than cc.size(w, h) + if (w === undefined) + return {width: 0, height: 0}; + if (h === undefined) + return {width: w.width, height: w.height}; + return {width: w, height: h}; +}; + +/** + * Check whether a point's value equals to another + * @function + * @param {cc.Size} size1 + * @param {cc.Size} size2 + * @return {Boolean} + */ +cc.sizeEqualToSize = function (size1, size2) { + return (size1 && size2 && (size1.width === size2.width) && (size1.height === size2.height)); +}; + + +/** + * cc.Rect is the class for rect object, please do not use its constructor to create rects, use cc.rect() alias function instead. + * @class cc.Rect + * @param {Number} x + * @param {Number} y + * @param {Number} width + * @param {Number} height + * + * @property {Number} x + * @property {Number} y + * @property {Number} width + * @property {Number} height + * + * @see cc.rect + */ +cc.Rect = function (x, y, width, height) { + this.x = x||0; + this.y = y||0; + this.width = width||0; + this.height = height||0; +}; + +/** + * Helper function that creates a cc.Rect. + * @function + * @param {Number|cc.Rect} x a number or a rect object + * @param {Number} y + * @param {Number} w + * @param {Number} h + * @returns {cc.Rect} + * @example + * var rect1 = cc.rect(); + * var rect2 = cc.rect(100,100,100,100); + * var rect3 = cc.rect(rect2); + * var rect4 = cc.rect({x: 100, y: 100, width: 100, height: 100}); + */ +cc.rect = function (x, y, w, h) { + if (x === undefined) + return {x: 0, y: 0, width: 0, height: 0}; + if (y === undefined) + return {x: x.x, y: x.y, width: x.width, height: x.height}; + return {x: x, y: y, width: w, height: h }; +}; + +/** + * Check whether a rect's value equals to another + * @function + * @param {cc.Rect} rect1 + * @param {cc.Rect} rect2 + * @return {Boolean} + */ +cc.rectEqualToRect = function (rect1, rect2) { + return rect1 && rect2 && (rect1.x === rect2.x) && (rect1.y === rect2.y) && (rect1.width === rect2.width) && (rect1.height === rect2.height); +}; + +cc._rectEqualToZero = function(rect){ + return rect && (rect.x === 0) && (rect.y === 0) && (rect.width === 0) && (rect.height === 0); +}; + +/** + * Check whether the rect1 contains rect2 + * @function + * @param {cc.Rect} rect1 + * @param {cc.Rect} rect2 + * @return {Boolean} + */ +cc.rectContainsRect = function (rect1, rect2) { + if (!rect1 || !rect2) + return false; + return !((rect1.x >= rect2.x) || (rect1.y >= rect2.y) || + ( rect1.x + rect1.width <= rect2.x + rect2.width) || + ( rect1.y + rect1.height <= rect2.y + rect2.height)); +}; + +/** + * Returns the rightmost x-value of a rect + * @function + * @param {cc.Rect} rect + * @return {Number} The rightmost x value + */ +cc.rectGetMaxX = function (rect) { + return (rect.x + rect.width); +}; + +/** + * Return the midpoint x-value of a rect + * @function + * @param {cc.Rect} rect + * @return {Number} The midpoint x value + */ +cc.rectGetMidX = function (rect) { + return (rect.x + rect.width / 2.0); +}; +/** + * Returns the leftmost x-value of a rect + * @function + * @param {cc.Rect} rect + * @return {Number} The leftmost x value + */ +cc.rectGetMinX = function (rect) { + return rect.x; +}; + +/** + * Return the topmost y-value of a rect + * @function + * @param {cc.Rect} rect + * @return {Number} The topmost y value + */ +cc.rectGetMaxY = function (rect) { + return(rect.y + rect.height); +}; + +/** + * Return the midpoint y-value of `rect' + * @function + * @param {cc.Rect} rect + * @return {Number} The midpoint y value + */ +cc.rectGetMidY = function (rect) { + return rect.y + rect.height / 2.0; +}; + +/** + * Return the bottommost y-value of a rect + * @function + * @param {cc.Rect} rect + * @return {Number} The bottommost y value + */ +cc.rectGetMinY = function (rect) { + return rect.y; +}; + +/** + * Check whether a rect contains a point + * @function + * @param {cc.Rect} rect + * @param {cc.Point} point + * @return {Boolean} + */ +cc.rectContainsPoint = function (rect, point) { + return (point.x >= cc.rectGetMinX(rect) && point.x <= cc.rectGetMaxX(rect) && + point.y >= cc.rectGetMinY(rect) && point.y <= cc.rectGetMaxY(rect)) ; +}; + +/** + * Check whether a rect intersect with another + * @function + * @param {cc.Rect} rectA + * @param {cc.Rect} rectB + * @return {Boolean} + */ +cc.rectIntersectsRect = function (ra, rb) { + var maxax = ra.x + ra.width, + maxay = ra.y + ra.height, + maxbx = rb.x + rb.width, + maxby = rb.y + rb.height; + return !(maxax < rb.x || maxbx < ra.x || maxay < rb.y || maxby < ra.y); +}; + +/** + * Check whether a rect overlaps another + * @function + * @param {cc.Rect} rectA + * @param {cc.Rect} rectB + * @return {Boolean} + */ +cc.rectOverlapsRect = function (rectA, rectB) { + return !((rectA.x + rectA.width < rectB.x) || + (rectB.x + rectB.width < rectA.x) || + (rectA.y + rectA.height < rectB.y) || + (rectB.y + rectB.height < rectA.y)); +}; + +/** + * Returns the smallest rectangle that contains the two source rectangles. + * @function + * @param {cc.Rect} rectA + * @param {cc.Rect} rectB + * @return {cc.Rect} + */ +cc.rectUnion = function (rectA, rectB) { + var rect = cc.rect(0, 0, 0, 0); + rect.x = Math.min(rectA.x, rectB.x); + rect.y = Math.min(rectA.y, rectB.y); + rect.width = Math.max(rectA.x + rectA.width, rectB.x + rectB.width) - rect.x; + rect.height = Math.max(rectA.y + rectA.height, rectB.y + rectB.height) - rect.y; + return rect; +}; + +/** + * Returns the overlapping portion of 2 rectangles + * @function + * @param {cc.Rect} rectA + * @param {cc.Rect} rectB + * @return {cc.Rect} + */ +cc.rectIntersection = function (rectA, rectB) { + var intersection = cc.rect( + Math.max(cc.rectGetMinX(rectA), cc.rectGetMinX(rectB)), + Math.max(cc.rectGetMinY(rectA), cc.rectGetMinY(rectB)), + 0, 0); + + intersection.width = Math.min(cc.rectGetMaxX(rectA), cc.rectGetMaxX(rectB)) - cc.rectGetMinX(intersection); + intersection.height = Math.min(cc.rectGetMaxY(rectA), cc.rectGetMaxY(rectB)) - cc.rectGetMinY(intersection); + return intersection; +}; diff --git a/frameworks/cocos2d-html5/cocos2d/core/cocos2d_externs.js b/frameworks/cocos2d-html5/cocos2d/core/cocos2d_externs.js new file mode 100644 index 0000000..97c8397 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/cocos2d_externs.js @@ -0,0 +1,66 @@ +/** + * @fileoverview Java Script Builtins for windows properties. + * + * @externs + */ + + /** + * @see https://cocos2d-x.org + */ + +/** + * cocos2d-html5-only. + * @type {string} + */ +CSSProperties.prototype._super; + +/** + * cocos2d-html5-only. We need this because the cc.Class.extend's new + * infrastructure requires it. + * @type {string} + */ +CSSProperties.prototype.ctor; + +/** + * cocos2d-html5-only. + * @type {string} + */ +CSSProperties.prototype.Inflate; + +/** + * cocos2d-html5-only. + * @type {string} + */ +CSSProperties.prototype.decompress; + +/** + * Accelerometer api + * cocos2d-html5-only. + * @type {string} + */ +CSSProperties.prototype.DeviceOrientationEvent; +CSSProperties.prototype.DeviceMotionEvent; +CSSProperties.prototype.accelerationIncludingGravity; +CSSProperties.prototype.gamma; +CSSProperties.prototype.beta; +CSSProperties.prototype.alpha; + + +var gl = gl || {}; +CSSProperties.prototype.gl; + +CSSProperties.prototype.AudioContext; +CSSProperties.prototype.webkitAudioContext; +CSSProperties.prototype.mozAudioContext; +CSSProperties.prototype.createBufferSource; +CSSProperties.prototype.createGain; +CSSProperties.prototype.createGainNode; +CSSProperties.prototype.destination; +CSSProperties.prototype.decodeAudioData; +CSSProperties.prototype.gain; +CSSProperties.prototype.connect; +CSSProperties.prototype.playbackState; +CSSProperties.prototype.noteGrainOn; +CSSProperties.prototype.noteOn; + + diff --git a/frameworks/cocos2d-html5/cocos2d/core/event-manager/CCEvent.js b/frameworks/cocos2d-html5/cocos2d/core/event-manager/CCEvent.js new file mode 100644 index 0000000..fa5c47f --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/event-manager/CCEvent.js @@ -0,0 +1,449 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * Base class of all kinds of events. + * @class + * @extends cc.Class + */ +cc.Event = cc.Class.extend(/** @lends cc.Event# */{ + _type: 0, // Event type + _isStopped: false, //< whether the event has been stopped. + _currentTarget: null, //< Current target + + _setCurrentTarget: function (target) { + this._currentTarget = target; + }, + + ctor: function (type) { + this._type = type; + }, + + /** + * Gets the event type + * @function + * @returns {Number} + */ + getType: function () { + return this._type; + }, + + /** + * Stops propagation for current event + * @function + */ + stopPropagation: function () { + this._isStopped = true; + }, + + /** + * Checks whether the event has been stopped + * @function + * @returns {boolean} + */ + isStopped: function () { + return this._isStopped; + }, + + /** + *

+ * Gets current target of the event
+ * note: It only be available when the event listener is associated with node.
+ * It returns 0 when the listener is associated with fixed priority. + *

+ * @function + * @returns {cc.Node} The target with which the event associates. + */ + getCurrentTarget: function () { + return this._currentTarget; + } +}); + +//event type +/** + * The type code of Touch event. + * @constant + * @type {number} + */ +cc.Event.TOUCH = 0; +/** + * The type code of Keyboard event. + * @constant + * @type {number} + */ +cc.Event.KEYBOARD = 1; +/** + * The type code of Acceleration event. + * @constant + * @type {number} + */ +cc.Event.ACCELERATION = 2; +/** + * The type code of Mouse event. + * @constant + * @type {number} + */ +cc.Event.MOUSE = 3; +/** + * The type code of UI focus event. + * @constant + * @type {number} + */ +cc.Event.FOCUS = 4; +/** + * The type code of Custom event. + * @constant + * @type {number} + */ +cc.Event.CUSTOM = 6; + +/** + * The Custom event + * @class + * @extends cc.Event + */ +cc.EventCustom = cc.Event.extend(/** @lends cc.EventCustom# */{ + _eventName: null, + _userData: null, // User data + + ctor: function (eventName) { + cc.Event.prototype.ctor.call(this, cc.Event.CUSTOM); + this._eventName = eventName; + }, + + /** + * Sets user data + * @param {*} data + */ + setUserData: function (data) { + this._userData = data; + }, + + /** + * Gets user data + * @returns {*} + */ + getUserData: function () { + return this._userData; + }, + + /** + * Gets event name + * @returns {String} + */ + getEventName: function () { + return this._eventName; + } +}); + +/** + * The mouse event + * @class + * @extends cc.Event + */ +cc.EventMouse = cc.Event.extend(/** @lends cc.EventMouse# */{ + _eventType: 0, + _button: 0, + _x: 0, + _y: 0, + _prevX: 0, + _prevY: 0, + _scrollX: 0, + _scrollY: 0, + + ctor: function (eventType) { + cc.Event.prototype.ctor.call(this, cc.Event.MOUSE); + this._eventType = eventType; + }, + + /** + * Sets scroll data + * @param {number} scrollX + * @param {number} scrollY + */ + setScrollData: function (scrollX, scrollY) { + this._scrollX = scrollX; + this._scrollY = scrollY; + }, + + /** + * Returns the x axis scroll value + * @returns {number} + */ + getScrollX: function () { + return this._scrollX; + }, + + /** + * Returns the y axis scroll value + * @returns {number} + */ + getScrollY: function () { + return this._scrollY; + }, + + /** + * Sets cursor location + * @param {number} x + * @param {number} y + */ + setLocation: function (x, y) { + this._x = x; + this._y = y; + }, + + /** + * Returns cursor location + * @return {cc.Point} location + */ + getLocation: function () { + return {x: this._x, y: this._y}; + }, + + /** + * Returns the current cursor location in screen coordinates + * @return {cc.Point} + */ + getLocationInView: function() { + return {x: this._x, y: cc.view._designResolutionSize.height - this._y}; + }, + + _setPrevCursor: function (x, y) { + this._prevX = x; + this._prevY = y; + }, + + /** + * Returns the delta distance from the previous location to current location + * @return {cc.Point} + */ + getDelta: function () { + return {x: this._x - this._prevX, y: this._y - this._prevY}; + }, + + /** + * Returns the X axis delta distance from the previous location to current location + * @return {Number} + */ + getDeltaX: function () { + return this._x - this._prevX; + }, + + /** + * Returns the Y axis delta distance from the previous location to current location + * @return {Number} + */ + getDeltaY: function () { + return this._y - this._prevY; + }, + + /** + * Sets mouse button + * @param {number} button + */ + setButton: function (button) { + this._button = button; + }, + + /** + * Returns mouse button + * @returns {number} + */ + getButton: function () { + return this._button; + }, + + /** + * Returns location X axis data + * @returns {number} + */ + getLocationX: function () { + return this._x; + }, + + /** + * Returns location Y axis data + * @returns {number} + */ + getLocationY: function () { + return this._y; + } +}); + +//Different types of MouseEvent +/** + * The none event code of mouse event. + * @constant + * @type {number} + */ +cc.EventMouse.NONE = 0; +/** + * The event type code of mouse down event. + * @constant + * @type {number} + */ +cc.EventMouse.DOWN = 1; +/** + * The event type code of mouse up event. + * @constant + * @type {number} + */ +cc.EventMouse.UP = 2; +/** + * The event type code of mouse move event. + * @constant + * @type {number} + */ +cc.EventMouse.MOVE = 3; +/** + * The event type code of mouse scroll event. + * @constant + * @type {number} + */ +cc.EventMouse.SCROLL = 4; + +/** + * The tag of Mouse left button + * @constant + * @type {Number} + */ +cc.EventMouse.BUTTON_LEFT = 0; + +/** + * The tag of Mouse right button (The right button number is 2 on browser) + * @constant + * @type {Number} + */ +cc.EventMouse.BUTTON_RIGHT = 2; + +/** + * The tag of Mouse middle button (The right button number is 1 on browser) + * @constant + * @type {Number} + */ +cc.EventMouse.BUTTON_MIDDLE = 1; + +/** + * The tag of Mouse button 4 + * @constant + * @type {Number} + */ +cc.EventMouse.BUTTON_4 = 3; + +/** + * The tag of Mouse button 5 + * @constant + * @type {Number} + */ +cc.EventMouse.BUTTON_5 = 4; + +/** + * The tag of Mouse button 6 + * @constant + * @type {Number} + */ +cc.EventMouse.BUTTON_6 = 5; + +/** + * The tag of Mouse button 7 + * @constant + * @type {Number} + */ +cc.EventMouse.BUTTON_7 = 6; + +/** + * The tag of Mouse button 8 + * @constant + * @type {Number} + */ +cc.EventMouse.BUTTON_8 = 7; + +/** + * The touch event + * @class + * @extends cc.Event + */ +cc.EventTouch = cc.Event.extend(/** @lends cc.EventTouch# */{ + _eventCode: 0, + _touches: null, + + ctor: function (arr) { + cc.Event.prototype.ctor.call(this, cc.Event.TOUCH); + this._touches = arr || []; + }, + + /** + * Returns event code + * @returns {number} + */ + getEventCode: function () { + return this._eventCode; + }, + + /** + * Returns touches of event + * @returns {Array} + */ + getTouches: function () { + return this._touches; + }, + + _setEventCode: function (eventCode) { + this._eventCode = eventCode; + }, + + _setTouches: function (touches) { + this._touches = touches; + } +}); + +/** + * The maximum touch numbers + * @constant + * @type {Number} + */ +cc.EventTouch.MAX_TOUCHES = 5; + +cc.EventTouch.EventCode = {BEGAN: 0, MOVED: 1, ENDED: 2, CANCELLED: 3}; + +/** + * Focus change event for UI widget + * @class + * @extends cc.Event + */ +cc.EventFocus = cc.Event.extend(/** @lends cc.EventTouch# */{ + _widgetGetFocus: null, + _widgetLoseFocus: null, + /** + * Constructor function. + * @param {ccui.Widget} widgetLoseFocus + * @param {ccui.Widget} widgetGetFocus + */ + ctor: function(widgetLoseFocus, widgetGetFocus){ + cc.Event.prototype.ctor.call(this, cc.Event.FOCUS); + this._widgetGetFocus = widgetGetFocus; + this._widgetLoseFocus = widgetLoseFocus; + } +}); \ No newline at end of file diff --git a/frameworks/cocos2d-html5/cocos2d/core/event-manager/CCEventExtension.js b/frameworks/cocos2d-html5/cocos2d/core/event-manager/CCEventExtension.js new file mode 100644 index 0000000..a14968f --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/event-manager/CCEventExtension.js @@ -0,0 +1,126 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The acceleration event + * @class + * @extends cc.Event + */ +cc.EventAcceleration = cc.Event.extend(/** @lends cc.EventAcceleration# */{ + _acc: null, + ctor: function (acc) { + cc.Event.prototype.ctor.call(this, cc.Event.ACCELERATION); + this._acc = acc; + } +}); + +/** + * The keyboard event + * @class + * @extends cc.Event + */ +cc.EventKeyboard = cc.Event.extend(/** @lends cc.EventKeyboard# */{ + _keyCode: 0, + _isPressed: false, + ctor: function (keyCode, isPressed) { + cc.Event.prototype.ctor.call(this, cc.Event.KEYBOARD); + this._keyCode = keyCode; + this._isPressed = isPressed; + } +}); + + +//Acceleration +cc._EventListenerAcceleration = cc.EventListener.extend({ + _onAccelerationEvent: null, + + ctor: function (callback) { + this._onAccelerationEvent = callback; + var selfPointer = this; + var listener = function (event) { + selfPointer._onAccelerationEvent(event._acc, event); + }; + cc.EventListener.prototype.ctor.call(this, cc.EventListener.ACCELERATION, cc._EventListenerAcceleration.LISTENER_ID, listener); + }, + + checkAvailable: function () { + + cc.assert(this._onAccelerationEvent, cc._LogInfos._EventListenerAcceleration_checkAvailable); + + return true; + }, + + clone: function () { + return new cc._EventListenerAcceleration(this._onAccelerationEvent); + } +}); + +cc._EventListenerAcceleration.LISTENER_ID = "__cc_acceleration"; + +cc._EventListenerAcceleration.create = function (callback) { + return new cc._EventListenerAcceleration(callback); +}; + + +//Keyboard +cc._EventListenerKeyboard = cc.EventListener.extend({ + onKeyPressed: null, + onKeyReleased: null, + + ctor: function () { + var selfPointer = this; + var listener = function (event) { + if (event._isPressed) { + if (selfPointer.onKeyPressed) + selfPointer.onKeyPressed(event._keyCode, event); + } else { + if (selfPointer.onKeyReleased) + selfPointer.onKeyReleased(event._keyCode, event); + } + }; + cc.EventListener.prototype.ctor.call(this, cc.EventListener.KEYBOARD, cc._EventListenerKeyboard.LISTENER_ID, listener); + }, + + clone: function () { + var eventListener = new cc._EventListenerKeyboard(); + eventListener.onKeyPressed = this.onKeyPressed; + eventListener.onKeyReleased = this.onKeyReleased; + return eventListener; + }, + + checkAvailable: function () { + if (this.onKeyPressed === null && this.onKeyReleased === null) { + cc.log(cc._LogInfos._EventListenerKeyboard_checkAvailable); + return false; + } + return true; + } +}); + +cc._EventListenerKeyboard.LISTENER_ID = "__cc_keyboard"; + +cc._EventListenerKeyboard.create = function () { + return new cc._EventListenerKeyboard(); +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/cocos2d/core/event-manager/CCEventHelper.js b/frameworks/cocos2d-html5/cocos2d/core/event-manager/CCEventHelper.js new file mode 100644 index 0000000..b005e71 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/event-manager/CCEventHelper.js @@ -0,0 +1,138 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2015 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +// The event helper +cc.EventHelper = function () { +}; + +cc.EventHelper.prototype = { + constructor: cc.EventHelper, + + apply: function (object) { + object.addEventListener = cc.EventHelper.prototype.addEventListener; + object.hasEventListener = cc.EventHelper.prototype.hasEventListener; + object.removeEventListener = cc.EventHelper.prototype.removeEventListener; + object.dispatchEvent = cc.EventHelper.prototype.dispatchEvent; + }, + + addEventListener: function (type, listener, target) { + //check 'type' status, if the status is ready, dispatch event next frame + if (type === "load" && this._textureLoaded) { //only load event checked. + setTimeout(function () { + listener.call(target); + }, 0); + return; + } + + if (this._listeners === undefined) + this._listeners = {}; + + var listeners = this._listeners; + if (listeners[type] === undefined) + listeners[type] = []; + + if (!this.hasEventListener(type, listener, target)) + listeners[type].push({callback: listener, eventTarget: target}); + }, + + hasEventListener: function (type, listener, target) { + if (this._listeners === undefined) + return false; + + var listeners = this._listeners; + if (listeners[type] !== undefined) { + for (var i = 0, len = listeners.length; i < len; i++) { + var selListener = listeners[i]; + if (selListener.callback === listener && selListener.eventTarget === target) + return true; + } + } + return false; + }, + + removeEventListener: function (type, listener, target) { + if (this._listeners === undefined) + return; + + var listeners = this._listeners; + var listenerArray = listeners[type]; + + if (listenerArray !== undefined) { + for (var i = 0; i < listenerArray.length;) { + var selListener = listenerArray[i]; + if (selListener.eventTarget === target && selListener.callback === listener) + listenerArray.splice(i, 1); + else + i++ + } + } + }, + + removeEventTarget: function (type, listener, target) { + if (this._listeners === undefined) + return; + + var listeners = this._listeners; + var listenerArray = listeners[type]; + + if (listenerArray !== undefined) { + for (var i = 0; i < listenerArray.length;) { + var selListener = listenerArray[i]; + if (selListener.eventTarget === target) + listenerArray.splice(i, 1); + else + i++ + } + } + }, + + dispatchEvent: function (event, clearAfterDispatch) { + if (this._listeners === undefined) + return; + + if (clearAfterDispatch == null) + clearAfterDispatch = true; + var listeners = this._listeners; + var listenerArray = listeners[event]; + + if (listenerArray !== undefined) { + var array = []; + var length = listenerArray.length; + + for (var i = 0; i < length; i++) { + array[i] = listenerArray[i]; + } + + for (i = 0; i < length; i++) { + array[i].callback.call(array[i].eventTarget, this); + } + + if (clearAfterDispatch) + listenerArray.length = 0; + } + } +}; + +cc.EventHelper.prototype.apply(cc.game); diff --git a/frameworks/cocos2d-html5/cocos2d/core/event-manager/CCEventListener.js b/frameworks/cocos2d-html5/cocos2d/core/event-manager/CCEventListener.js new file mode 100644 index 0000000..c970c43 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/event-manager/CCEventListener.js @@ -0,0 +1,514 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + *

+ * The base class of event listener.
+ * If you need custom listener which with different callback, you need to inherit this class.
+ * For instance, you could refer to EventListenerAcceleration, EventListenerKeyboard,
+ * EventListenerTouchOneByOne, EventListenerCustom. + *

+ * @class + * @extends cc.Class + */ +cc.EventListener = cc.Class.extend(/** @lends cc.EventListener# */{ + _onEvent: null, // Event callback function + _type: 0, // Event listener type + _listenerID: null, // Event listener ID + _registered: false, // Whether the listener has been added to dispatcher. + + _fixedPriority: 0, // The higher the number, the higher the priority, 0 is for scene graph base priority. + _node: null, // scene graph based priority + _paused: true, // Whether the listener is paused + _isEnabled: true, // Whether the listener is enabled + + /** + * Initializes event with type and callback function + * @param {number} type + * @param {string} listenerID + * @param {function} callback + */ + ctor: function (type, listenerID, callback) { + this._onEvent = callback; + this._type = type || 0; + this._listenerID = listenerID || ""; + }, + + /** + *

+ * Sets paused state for the listener + * The paused state is only used for scene graph priority listeners. + * `EventDispatcher::resumeAllEventListenersForTarget(node)` will set the paused state to `true`, + * while `EventDispatcher::pauseAllEventListenersForTarget(node)` will set it to `false`. + * @note 1) Fixed priority listeners will never get paused. If a fixed priority doesn't want to receive events, + * call `setEnabled(false)` instead. + * 2) In `Node`'s onEnter and onExit, the `paused state` of the listeners which associated with that node will be automatically updated. + *

+ * @param {boolean} paused + * @private + */ + _setPaused: function (paused) { + this._paused = paused; + }, + + /** + * Checks whether the listener is paused + * @returns {boolean} + * @private + */ + _isPaused: function () { + return this._paused; + }, + + /** + * Marks the listener was registered by EventDispatcher + * @param {boolean} registered + * @private + */ + _setRegistered: function (registered) { + this._registered = registered; + }, + + /** + * Checks whether the listener was registered by EventDispatcher + * @returns {boolean} + * @private + */ + _isRegistered: function () { + return this._registered; + }, + + /** + * Gets the type of this listener + * @note It's different from `EventType`, e.g. TouchEvent has two kinds of event listeners - EventListenerOneByOne, EventListenerAllAtOnce + * @returns {number} + * @private + */ + _getType: function () { + return this._type; + }, + + /** + * Gets the listener ID of this listener + * When event is being dispatched, listener ID is used as key for searching listeners according to event type. + * @returns {string} + * @private + */ + _getListenerID: function () { + return this._listenerID; + }, + + /** + * Sets the fixed priority for this listener + * @note This method is only used for `fixed priority listeners`, it needs to access a non-zero value. 0 is reserved for scene graph priority listeners + * @param {number} fixedPriority + * @private + */ + _setFixedPriority: function (fixedPriority) { + this._fixedPriority = fixedPriority; + }, + + /** + * Gets the fixed priority of this listener + * @returns {number} 0 if it's a scene graph priority listener, non-zero for fixed priority listener + * @private + */ + _getFixedPriority: function () { + return this._fixedPriority; + }, + + /** + * Sets scene graph priority for this listener + * @param {cc.Node} node + * @private + */ + _setSceneGraphPriority: function (node) { + this._node = node; + }, + + /** + * Gets scene graph priority of this listener + * @returns {cc.Node} if it's a fixed priority listener, non-null for scene graph priority listener + * @private + */ + _getSceneGraphPriority: function () { + return this._node; + }, + + /** + * Checks whether the listener is available. + * @returns {boolean} + */ + checkAvailable: function () { + return this._onEvent !== null; + }, + + /** + * Clones the listener, its subclasses have to override this method. + * @returns {cc.EventListener} + */ + clone: function () { + return null; + }, + + /** + * Enables or disables the listener + * @note Only listeners with `enabled` state will be able to receive events. + * When an listener was initialized, it's enabled by default. + * An event listener can receive events when it is enabled and is not paused. + * paused state is always false when it is a fixed priority listener. + * @param {boolean} enabled + */ + setEnabled: function(enabled){ + this._isEnabled = enabled; + }, + + /** + * Checks whether the listener is enabled + * @returns {boolean} + */ + isEnabled: function(){ + return this._isEnabled; + }, + + /** + *

Currently JavaScript Bindings (JSB), in some cases, needs to use retain and release. This is a bug in JSB, + * and the ugly workaround is to use retain/release. So, these 2 methods were added to be compatible with JSB. + * This is a hack, and should be removed once JSB fixes the retain/release bug
+ * You will need to retain an object if you created a listener and haven't added it any target node during the same frame.
+ * Otherwise, JSB's native autorelease pool will consider this object a useless one and release it directly,
+ * when you want to use it later, a "Invalid Native Object" error will be raised.
+ * The retain function can increase a reference count for the native object to avoid it being released,
+ * you need to manually invoke release function when you think this object is no longer needed, otherwise, there will be memory learks.
+ * retain and release function call should be paired in developer's game code.

+ * @function + * @see cc.EventListener#release + */ + retain:function () { + }, + /** + *

Currently JavaScript Bindings (JSB), in some cases, needs to use retain and release. This is a bug in JSB, + * and the ugly workaround is to use retain/release. So, these 2 methods were added to be compatible with JSB. + * This is a hack, and should be removed once JSB fixes the retain/release bug
+ * You will need to retain an object if you created a listener and haven't added it any target node during the same frame.
+ * Otherwise, JSB's native autorelease pool will consider this object a useless one and release it directly,
+ * when you want to use it later, a "Invalid Native Object" error will be raised.
+ * The retain function can increase a reference count for the native object to avoid it being released,
+ * you need to manually invoke release function when you think this object is no longer needed, otherwise, there will be memory learks.
+ * retain and release function call should be paired in developer's game code.

+ * @function + * @see cc.EventListener#retain + */ + release:function () { + } +}); + +// event listener type +/** + * The type code of unknown event listener. + * @constant + * @type {number} + */ +cc.EventListener.UNKNOWN = 0; +/** + * The type code of one by one touch event listener. + * @constant + * @type {number} + */ +cc.EventListener.TOUCH_ONE_BY_ONE = 1; +/** + * The type code of all at once touch event listener. + * @constant + * @type {number} + */ +cc.EventListener.TOUCH_ALL_AT_ONCE = 2; +/** + * The type code of keyboard event listener. + * @constant + * @type {number} + */ +cc.EventListener.KEYBOARD = 3; +/** + * The type code of mouse event listener. + * @constant + * @type {number} + */ +cc.EventListener.MOUSE = 4; +/** + * The type code of acceleration event listener. + * @constant + * @type {number} + */ +cc.EventListener.ACCELERATION = 6; +/** + * The type code of Focus change event listener. + * @constant + * @type {number} + */ +cc.EventListener.FOCUS = 7; +/** + * The type code of custom event listener. + * @constant + * @type {number} + */ +cc.EventListener.CUSTOM = 8; + +cc._EventListenerCustom = cc.EventListener.extend({ + _onCustomEvent: null, + ctor: function (listenerId, callback, target) { + this._onCustomEvent = callback; + this._target = target; + + cc.EventListener.prototype.ctor.call(this, cc.EventListener.CUSTOM, listenerId, this._callback); + }, + + _callback: function (event) { + if (this._onCustomEvent !== null) + this._onCustomEvent.call(this._target, event); + }, + + checkAvailable: function () { + return (cc.EventListener.prototype.checkAvailable.call(this) && this._onCustomEvent !== null); + }, + + clone: function () { + return new cc._EventListenerCustom(this._listenerID, this._onCustomEvent); + } +}); + +cc._EventListenerCustom.create = function (eventName, callback) { + return new cc._EventListenerCustom(eventName, callback); +}; + +cc._EventListenerMouse = cc.EventListener.extend({ + onMouseDown: null, + onMouseUp: null, + onMouseMove: null, + onMouseScroll: null, + + ctor: function () { + cc.EventListener.prototype.ctor.call(this, cc.EventListener.MOUSE, cc._EventListenerMouse.LISTENER_ID, this._callback); + }, + + _callback: function (event) { + var eventType = cc.EventMouse; + switch (event._eventType) { + case eventType.DOWN: + if (this.onMouseDown) + this.onMouseDown(event); + break; + case eventType.UP: + if (this.onMouseUp) + this.onMouseUp(event); + break; + case eventType.MOVE: + if (this.onMouseMove) + this.onMouseMove(event); + break; + case eventType.SCROLL: + if (this.onMouseScroll) + this.onMouseScroll(event); + break; + default: + break; + } + }, + + clone: function () { + var eventListener = new cc._EventListenerMouse(); + eventListener.onMouseDown = this.onMouseDown; + eventListener.onMouseUp = this.onMouseUp; + eventListener.onMouseMove = this.onMouseMove; + eventListener.onMouseScroll = this.onMouseScroll; + return eventListener; + }, + + checkAvailable: function () { + return true; + } +}); + +cc._EventListenerMouse.LISTENER_ID = "__cc_mouse"; + +cc._EventListenerMouse.create = function () { + return new cc._EventListenerMouse(); +}; + +cc._EventListenerTouchOneByOne = cc.EventListener.extend({ + _claimedTouches: null, + swallowTouches: false, + onTouchBegan: null, + onTouchMoved: null, + onTouchEnded: null, + onTouchCancelled: null, + + ctor: function () { + cc.EventListener.prototype.ctor.call(this, cc.EventListener.TOUCH_ONE_BY_ONE, cc._EventListenerTouchOneByOne.LISTENER_ID, null); + this._claimedTouches = []; + }, + + setSwallowTouches: function (needSwallow) { + this.swallowTouches = needSwallow; + }, + + isSwallowTouches: function(){ + return this.swallowTouches; + }, + + clone: function () { + var eventListener = new cc._EventListenerTouchOneByOne(); + eventListener.onTouchBegan = this.onTouchBegan; + eventListener.onTouchMoved = this.onTouchMoved; + eventListener.onTouchEnded = this.onTouchEnded; + eventListener.onTouchCancelled = this.onTouchCancelled; + eventListener.swallowTouches = this.swallowTouches; + return eventListener; + }, + + checkAvailable: function () { + if(!this.onTouchBegan){ + cc.log(cc._LogInfos._EventListenerTouchOneByOne_checkAvailable); + return false; + } + return true; + } +}); + +cc._EventListenerTouchOneByOne.LISTENER_ID = "__cc_touch_one_by_one"; + +cc._EventListenerTouchOneByOne.create = function () { + return new cc._EventListenerTouchOneByOne(); +}; + +cc._EventListenerTouchAllAtOnce = cc.EventListener.extend({ + onTouchesBegan: null, + onTouchesMoved: null, + onTouchesEnded: null, + onTouchesCancelled: null, + + ctor: function(){ + cc.EventListener.prototype.ctor.call(this, cc.EventListener.TOUCH_ALL_AT_ONCE, cc._EventListenerTouchAllAtOnce.LISTENER_ID, null); + }, + + clone: function(){ + var eventListener = new cc._EventListenerTouchAllAtOnce(); + eventListener.onTouchesBegan = this.onTouchesBegan; + eventListener.onTouchesMoved = this.onTouchesMoved; + eventListener.onTouchesEnded = this.onTouchesEnded; + eventListener.onTouchesCancelled = this.onTouchesCancelled; + return eventListener; + }, + + checkAvailable: function(){ + if (this.onTouchesBegan === null && this.onTouchesMoved === null + && this.onTouchesEnded === null && this.onTouchesCancelled === null) { + cc.log(cc._LogInfos._EventListenerTouchAllAtOnce_checkAvailable); + return false; + } + return true; + } +}); + +cc._EventListenerTouchAllAtOnce.LISTENER_ID = "__cc_touch_all_at_once"; + +cc._EventListenerTouchAllAtOnce.create = function(){ + return new cc._EventListenerTouchAllAtOnce(); +}; + +/** + * Create a EventListener object by json object + * @function + * @static + * @param {object} argObj a json object + * @returns {cc.EventListener} + * todo: It should be the direct use new + * @example + * cc.EventListener.create({ + * event: cc.EventListener.TOUCH_ONE_BY_ONE, + * swallowTouches: true, + * onTouchBegan: function (touch, event) { + * //do something + * return true; + * } + * }); + */ +cc.EventListener.create = function(argObj){ + + cc.assert(argObj&&argObj.event, cc._LogInfos.EventListener_create); + + var listenerType = argObj.event; + delete argObj.event; + + var listener = null; + if(listenerType === cc.EventListener.TOUCH_ONE_BY_ONE) + listener = new cc._EventListenerTouchOneByOne(); + else if(listenerType === cc.EventListener.TOUCH_ALL_AT_ONCE) + listener = new cc._EventListenerTouchAllAtOnce(); + else if(listenerType === cc.EventListener.MOUSE) + listener = new cc._EventListenerMouse(); + else if(listenerType === cc.EventListener.CUSTOM){ + listener = new cc._EventListenerCustom(argObj.eventName, argObj.callback); + delete argObj.eventName; + delete argObj.callback; + } else if(listenerType === cc.EventListener.KEYBOARD) + listener = new cc._EventListenerKeyboard(); + else if(listenerType === cc.EventListener.ACCELERATION){ + listener = new cc._EventListenerAcceleration(argObj.callback); + delete argObj.callback; + } else if(listenerType === cc.EventListener.FOCUS) + listener = new cc._EventListenerFocus(); + + for(var key in argObj) { + listener[key] = argObj[key]; + } + + return listener; +}; + +cc._EventListenerFocus = cc.EventListener.extend({ + clone: function(){ + var listener = new cc._EventListenerFocus(); + listener.onFocusChanged = this.onFocusChanged; + return listener; + }, + checkAvailable: function(){ + if(!this.onFocusChanged){ + cc.log("Invalid EventListenerFocus!"); + return false; + } + return true; + }, + onFocusChanged: null, + ctor: function(){ + cc.EventListener.prototype.ctor.call(this, cc.EventListener.FOCUS, cc._EventListenerFocus.LISTENER_ID, this._callback); + }, + _callback: function (event) { + if (this.onFocusChanged) { + this.onFocusChanged(event._widgetLoseFocus, event._widgetGetFocus); + } + } +}); + +cc._EventListenerFocus.LISTENER_ID = "__cc_focus_event"; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/cocos2d/core/event-manager/CCEventManager.js b/frameworks/cocos2d-html5/cocos2d/core/event-manager/CCEventManager.js new file mode 100644 index 0000000..7f54f03 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/event-manager/CCEventManager.js @@ -0,0 +1,1016 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2015 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + +/** + * @ignore + */ +cc._EventListenerVector = cc.Class.extend({ + _fixedListeners: null, + _sceneGraphListeners: null, + gt0Index: 0, + + ctor: function () { + this._fixedListeners = []; + this._sceneGraphListeners = []; + }, + + size: function () { + return this._fixedListeners.length + this._sceneGraphListeners.length; + }, + + empty: function () { + return (this._fixedListeners.length === 0) && (this._sceneGraphListeners.length === 0); + }, + + push: function (listener) { + if (listener._getFixedPriority() === 0) + this._sceneGraphListeners.push(listener); + else + this._fixedListeners.push(listener); + }, + + clearSceneGraphListeners: function () { + this._sceneGraphListeners.length = 0; + }, + + clearFixedListeners: function () { + this._fixedListeners.length = 0; + }, + + clear: function () { + this._sceneGraphListeners.length = 0; + this._fixedListeners.length = 0; + }, + + getFixedPriorityListeners: function () { + return this._fixedListeners; + }, + + getSceneGraphPriorityListeners: function () { + return this._sceneGraphListeners; + } +}); + +function __getListenerID (event) { + var eventType = cc.Event, getType = event._type; + if (getType === eventType.ACCELERATION) + return cc._EventListenerAcceleration.LISTENER_ID; + if (getType === eventType.CUSTOM) + return event._eventName; + if (getType === eventType.KEYBOARD) + return cc._EventListenerKeyboard.LISTENER_ID; + if (getType === eventType.MOUSE) + return cc._EventListenerMouse.LISTENER_ID; + if (getType === eventType.FOCUS) + return cc._EventListenerFocus.LISTENER_ID; + if (getType === eventType.TOUCH) { + // Touch listener is very special, it contains two kinds of listeners, EventListenerTouchOneByOne and EventListenerTouchAllAtOnce. + // return UNKNOWN instead. + cc.log(cc._LogInfos.__getListenerID); + } + return ""; +} + +/** + *

+ * cc.eventManager is a singleton object which manages event listener subscriptions and event dispatching.
+ *
+ * The EventListener list is managed in such way so that event listeners can be added and removed
+ * while events are being dispatched. + *

+ * @class + * @name cc.eventManager + */ +cc.eventManager = /** @lends cc.eventManager# */{ + //Priority dirty flag + DIRTY_NONE: 0, + DIRTY_FIXED_PRIORITY: 1 << 0, + DIRTY_SCENE_GRAPH_PRIORITY: 1 << 1, + DIRTY_ALL: 3, + + _listenersMap: {}, + _priorityDirtyFlagMap: {}, + _nodeListenersMap: {}, + _nodePriorityMap: {}, + _globalZOrderNodeMap: {}, + _toAddedListeners: [], + _toRemovedListeners: [], + _dirtyNodes: [], + _inDispatch: 0, + _isEnabled: false, + _nodePriorityIndex: 0, + + _internalCustomListenerIDs: [cc.game.EVENT_HIDE, cc.game.EVENT_SHOW], + + _setDirtyForNode: function (node) { + // Mark the node dirty only when there is an event listener associated with it. + if (this._nodeListenersMap[node.__instanceId] != null) + this._dirtyNodes.push(node); + var _children = node.getChildren(); + for(var i = 0, len = _children.length; i < len; i++) + this._setDirtyForNode(_children[i]); + }, + + /** + * Pauses all listeners which are associated the specified target. + * @param {cc.Node} node + * @param {Boolean} [recursive=false] + */ + pauseTarget: function (node, recursive) { + var listeners = this._nodeListenersMap[node.__instanceId], i, len; + if (listeners) { + for (i = 0, len = listeners.length; i < len; i++) + listeners[i]._setPaused(true); + } + if (recursive === true) { + var locChildren = node.getChildren(); + for (i = 0, len = locChildren.length; i < len; i++) + this.pauseTarget(locChildren[i], true); + } + }, + + /** + * Resumes all listeners which are associated the specified target. + * @param {cc.Node} node + * @param {Boolean} [recursive=false] + */ + resumeTarget: function (node, recursive) { + var listeners = this._nodeListenersMap[node.__instanceId], i, len; + if (listeners) { + for (i = 0, len = listeners.length; i < len; i++) + listeners[i]._setPaused(false); + } + this._setDirtyForNode(node); + if (recursive === true) { + var locChildren = node.getChildren(); + for (i = 0, len = locChildren.length; i< len; i++) + this.resumeTarget(locChildren[i], true); + } + }, + + _addListener: function (listener) { + if (this._inDispatch === 0) + this._forceAddEventListener(listener); + else + this._toAddedListeners.push(listener); + }, + + _forceAddEventListener: function (listener) { + var listenerID = listener._getListenerID(); + var listeners = this._listenersMap[listenerID]; + if (!listeners) { + listeners = new cc._EventListenerVector(); + this._listenersMap[listenerID] = listeners; + } + listeners.push(listener); + + if (listener._getFixedPriority() === 0) { + this._setDirty(listenerID, this.DIRTY_SCENE_GRAPH_PRIORITY); + + var node = listener._getSceneGraphPriority(); + if (node === null) + cc.log(cc._LogInfos.eventManager__forceAddEventListener); + + this._associateNodeAndEventListener(node, listener); + if (node.isRunning()) + this.resumeTarget(node); + } else + this._setDirty(listenerID, this.DIRTY_FIXED_PRIORITY); + }, + + _getListeners: function (listenerID) { + return this._listenersMap[listenerID]; + }, + + _updateDirtyFlagForSceneGraph: function () { + if (this._dirtyNodes.length === 0) + return; + + var locDirtyNodes = this._dirtyNodes, selListeners, selListener, locNodeListenersMap = this._nodeListenersMap; + for (var i = 0, len = locDirtyNodes.length; i < len; i++) { + selListeners = locNodeListenersMap[locDirtyNodes[i].__instanceId]; + if (selListeners) { + for (var j = 0, listenersLen = selListeners.length; j < listenersLen; j++) { + selListener = selListeners[j]; + if (selListener) + this._setDirty(selListener._getListenerID(), this.DIRTY_SCENE_GRAPH_PRIORITY); + } + } + } + this._dirtyNodes.length = 0; + }, + + _removeAllListenersInVector: function (listenerVector) { + if (!listenerVector) + return; + var selListener; + for (var i = 0; i < listenerVector.length;) { + selListener = listenerVector[i]; + selListener._setRegistered(false); + if (selListener._getSceneGraphPriority() != null) { + this._dissociateNodeAndEventListener(selListener._getSceneGraphPriority(), selListener); + selListener._setSceneGraphPriority(null); // NULL out the node pointer so we don't have any dangling pointers to destroyed nodes. + } + + if (this._inDispatch === 0) + cc.arrayRemoveObject(listenerVector, selListener); + else + ++i; + } + }, + + _removeListenersForListenerID: function (listenerID) { + var listeners = this._listenersMap[listenerID], i; + if (listeners) { + var fixedPriorityListeners = listeners.getFixedPriorityListeners(); + var sceneGraphPriorityListeners = listeners.getSceneGraphPriorityListeners(); + + this._removeAllListenersInVector(sceneGraphPriorityListeners); + this._removeAllListenersInVector(fixedPriorityListeners); + + // Remove the dirty flag according the 'listenerID'. + // No need to check whether the dispatcher is dispatching event. + delete this._priorityDirtyFlagMap[listenerID]; + + if (!this._inDispatch) { + listeners.clear(); + } + delete this._listenersMap[listenerID]; + } + + var locToAddedListeners = this._toAddedListeners, listener; + for (i = 0; i < locToAddedListeners.length;) { + listener = locToAddedListeners[i]; + if (listener && listener._getListenerID() === listenerID) + cc.arrayRemoveObject(locToAddedListeners, listener); + else + ++i; + } + }, + + _sortEventListeners: function (listenerID) { + var dirtyFlag = this.DIRTY_NONE, locFlagMap = this._priorityDirtyFlagMap; + if (locFlagMap[listenerID]) + dirtyFlag = locFlagMap[listenerID]; + + if (dirtyFlag !== this.DIRTY_NONE) { + // Clear the dirty flag first, if `rootNode` is null, then set its dirty flag of scene graph priority + locFlagMap[listenerID] = this.DIRTY_NONE; + + if (dirtyFlag & this.DIRTY_FIXED_PRIORITY) + this._sortListenersOfFixedPriority(listenerID); + + if (dirtyFlag & this.DIRTY_SCENE_GRAPH_PRIORITY) { + var rootNode = cc.director.getRunningScene(); + if (rootNode) + this._sortListenersOfSceneGraphPriority(listenerID, rootNode); + else + locFlagMap[listenerID] = this.DIRTY_SCENE_GRAPH_PRIORITY; + } + } + }, + + _sortListenersOfSceneGraphPriority: function (listenerID, rootNode) { + var listeners = this._getListeners(listenerID); + if (!listeners) + return; + + var sceneGraphListener = listeners.getSceneGraphPriorityListeners(); + if (!sceneGraphListener || sceneGraphListener.length === 0) + return; + + // Reset priority index + this._nodePriorityIndex = 0; + this._nodePriorityMap = {}; + + this._visitTarget(rootNode, true); + + // After sort: priority < 0, > 0 + listeners.getSceneGraphPriorityListeners().sort(this._sortEventListenersOfSceneGraphPriorityDes); + }, + + _sortEventListenersOfSceneGraphPriorityDes: function (l1, l2) { + var locNodePriorityMap = cc.eventManager._nodePriorityMap, node1 = l1._getSceneGraphPriority(), + node2 = l2._getSceneGraphPriority(); + if (!l2 || !node2 || !locNodePriorityMap[node2.__instanceId]) + return -1; + else if (!l1 || !node1 || !locNodePriorityMap[node1.__instanceId]) + return 1; + return locNodePriorityMap[l2._getSceneGraphPriority().__instanceId] - locNodePriorityMap[l1._getSceneGraphPriority().__instanceId]; + }, + + _sortListenersOfFixedPriority: function (listenerID) { + var listeners = this._listenersMap[listenerID]; + if (!listeners) + return; + + var fixedListeners = listeners.getFixedPriorityListeners(); + if (!fixedListeners || fixedListeners.length === 0) + return; + // After sort: priority < 0, > 0 + fixedListeners.sort(this._sortListenersOfFixedPriorityAsc); + + // FIXME: Should use binary search + var index = 0; + for (var len = fixedListeners.length; index < len;) { + if (fixedListeners[index]._getFixedPriority() >= 0) + break; + ++index; + } + listeners.gt0Index = index; + }, + + _sortListenersOfFixedPriorityAsc: function (l1, l2) { + return l1._getFixedPriority() - l2._getFixedPriority(); + }, + + _onUpdateListeners: function (listeners) { + var fixedPriorityListeners = listeners.getFixedPriorityListeners(); + var sceneGraphPriorityListeners = listeners.getSceneGraphPriorityListeners(); + var i, selListener, idx, toRemovedListeners = this._toRemovedListeners; + + if (sceneGraphPriorityListeners) { + for (i = 0; i < sceneGraphPriorityListeners.length;) { + selListener = sceneGraphPriorityListeners[i]; + if (!selListener._isRegistered()) { + cc.arrayRemoveObject(sceneGraphPriorityListeners, selListener); + // if item in toRemove list, remove it from the list + idx = toRemovedListeners.indexOf(selListener); + if(idx !== -1) + toRemovedListeners.splice(idx, 1); + } else + ++i; + } + } + + if (fixedPriorityListeners) { + for (i = 0; i < fixedPriorityListeners.length;) { + selListener = fixedPriorityListeners[i]; + if (!selListener._isRegistered()) { + cc.arrayRemoveObject(fixedPriorityListeners, selListener); + // if item in toRemove list, remove it from the list + idx = toRemovedListeners.indexOf(selListener); + if(idx !== -1) + toRemovedListeners.splice(idx, 1); + } else + ++i; + } + } + + if (sceneGraphPriorityListeners && sceneGraphPriorityListeners.length === 0) + listeners.clearSceneGraphListeners(); + + if (fixedPriorityListeners && fixedPriorityListeners.length === 0) + listeners.clearFixedListeners(); + }, + + frameUpdateListeners: function () { + var locListenersMap = this._listenersMap, locPriorityDirtyFlagMap = this._priorityDirtyFlagMap; + for (var selKey in locListenersMap) { + if (locListenersMap[selKey].empty()) { + delete locPriorityDirtyFlagMap[selKey]; + delete locListenersMap[selKey]; + } + } + + var locToAddedListeners = this._toAddedListeners; + if (locToAddedListeners.length !== 0) { + for (var i = 0, len = locToAddedListeners.length; i < len; i++) + this._forceAddEventListener(locToAddedListeners[i]); + locToAddedListeners.length = 0; + } + if (this._toRemovedListeners.length !== 0) { + this._cleanToRemovedListeners(); + } + }, + + _updateTouchListeners: function (event) { + var locInDispatch = this._inDispatch; + cc.assert(locInDispatch > 0, cc._LogInfos.EventManager__updateListeners); + + if (locInDispatch > 1) + return; + + var listeners; + listeners = this._listenersMap[cc._EventListenerTouchOneByOne.LISTENER_ID]; + if (listeners) { + this._onUpdateListeners(listeners); + } + listeners = this._listenersMap[cc._EventListenerTouchAllAtOnce.LISTENER_ID]; + if (listeners) { + this._onUpdateListeners(listeners); + } + + cc.assert(locInDispatch === 1, cc._LogInfos.EventManager__updateListeners_2); + + var locToAddedListeners = this._toAddedListeners; + if (locToAddedListeners.length !== 0) { + for (var i = 0, len = locToAddedListeners.length; i < len; i++) + this._forceAddEventListener(locToAddedListeners[i]); + locToAddedListeners.length = 0; + } + if (this._toRemovedListeners.length !== 0) { + this._cleanToRemovedListeners(); + } + }, + + //Remove all listeners in _toRemoveListeners list and cleanup + _cleanToRemovedListeners: function () { + var toRemovedListeners = this._toRemovedListeners; + for (var i = 0; i < toRemovedListeners.length; i++) { + var selListener = toRemovedListeners[i]; + var listeners = this._listenersMap[selListener._getListenerID()]; + if (!listeners) + continue; + + var idx, fixedPriorityListeners = listeners.getFixedPriorityListeners(), + sceneGraphPriorityListeners = listeners.getSceneGraphPriorityListeners(); + + if (sceneGraphPriorityListeners) { + idx = sceneGraphPriorityListeners.indexOf(selListener); + if (idx !== -1) { + sceneGraphPriorityListeners.splice(idx, 1); + } + } + if (fixedPriorityListeners) { + idx = fixedPriorityListeners.indexOf(selListener); + if (idx !== -1) { + fixedPriorityListeners.splice(idx, 1); + } + } + } + toRemovedListeners.length = 0; + }, + + _onTouchEventCallback: function (listener, argsObj) { + // Skip if the listener was removed. + if (!listener._isRegistered) + return false; + + var event = argsObj.event, selTouch = argsObj.selTouch; + event._setCurrentTarget(listener._node); + + var isClaimed = false, removedIdx; + var getCode = event.getEventCode(), eventCode = cc.EventTouch.EventCode; + if (getCode === eventCode.BEGAN) { + if (listener.onTouchBegan) { + isClaimed = listener.onTouchBegan(selTouch, event); + if (isClaimed && listener._registered) + listener._claimedTouches.push(selTouch); + } + } else if (listener._claimedTouches.length > 0 + && ((removedIdx = listener._claimedTouches.indexOf(selTouch)) !== -1)) { + isClaimed = true; + if (getCode === eventCode.MOVED && listener.onTouchMoved) { + listener.onTouchMoved(selTouch, event); + } else if (getCode === eventCode.ENDED) { + if (listener.onTouchEnded) + listener.onTouchEnded(selTouch, event); + if (listener._registered) + listener._claimedTouches.splice(removedIdx, 1); + } else if (getCode === eventCode.CANCELLED) { + if (listener.onTouchCancelled) + listener.onTouchCancelled(selTouch, event); + if (listener._registered) + listener._claimedTouches.splice(removedIdx, 1); + } + } + + // If the event was stopped, return directly. + if (event.isStopped()) { + cc.eventManager._updateTouchListeners(event); + return true; + } + + if (isClaimed && listener._registered && listener.swallowTouches) { + if (argsObj.needsMutableSet) + argsObj.touches.splice(selTouch, 1); + return true; + } + return false; + }, + + _dispatchTouchEvent: function (event) { + this._sortEventListeners(cc._EventListenerTouchOneByOne.LISTENER_ID); + this._sortEventListeners(cc._EventListenerTouchAllAtOnce.LISTENER_ID); + + var oneByOneListeners = this._getListeners(cc._EventListenerTouchOneByOne.LISTENER_ID); + var allAtOnceListeners = this._getListeners(cc._EventListenerTouchAllAtOnce.LISTENER_ID); + + // If there aren't any touch listeners, return directly. + if (null === oneByOneListeners && null === allAtOnceListeners) + return; + + var originalTouches = event.getTouches(), mutableTouches = cc.copyArray(originalTouches); + var oneByOneArgsObj = {event: event, needsMutableSet: (oneByOneListeners && allAtOnceListeners), touches: mutableTouches, selTouch: null}; + + // + // process the target handlers 1st + // + if (oneByOneListeners) { + for (var i = 0; i < originalTouches.length; i++) { + oneByOneArgsObj.selTouch = originalTouches[i]; + this._dispatchEventToListeners(oneByOneListeners, this._onTouchEventCallback, oneByOneArgsObj); + if (event.isStopped()) + return; + } + } + + // + // process standard handlers 2nd + // + if (allAtOnceListeners && mutableTouches.length > 0) { + this._dispatchEventToListeners(allAtOnceListeners, this._onTouchesEventCallback, {event: event, touches: mutableTouches}); + if (event.isStopped()) + return; + } + this._updateTouchListeners(event); + }, + + _onTouchesEventCallback: function (listener, callbackParams) { + // Skip if the listener was removed. + if (!listener._registered) + return false; + + var eventCode = cc.EventTouch.EventCode, event = callbackParams.event, touches = callbackParams.touches, getCode = event.getEventCode(); + event._setCurrentTarget(listener._node); + if (getCode === eventCode.BEGAN && listener.onTouchesBegan) + listener.onTouchesBegan(touches, event); + else if (getCode === eventCode.MOVED && listener.onTouchesMoved) + listener.onTouchesMoved(touches, event); + else if (getCode === eventCode.ENDED && listener.onTouchesEnded) + listener.onTouchesEnded(touches, event); + else if (getCode === eventCode.CANCELLED && listener.onTouchesCancelled) + listener.onTouchesCancelled(touches, event); + + // If the event was stopped, return directly. + if (event.isStopped()) { + cc.eventManager._updateTouchListeners(event); + return true; + } + return false; + }, + + _associateNodeAndEventListener: function (node, listener) { + var listeners = this._nodeListenersMap[node.__instanceId]; + if (!listeners) { + listeners = []; + this._nodeListenersMap[node.__instanceId] = listeners; + } + listeners.push(listener); + }, + + _dissociateNodeAndEventListener: function (node, listener) { + var listeners = this._nodeListenersMap[node.__instanceId]; + if (listeners) { + cc.arrayRemoveObject(listeners, listener); + if (listeners.length === 0) + delete this._nodeListenersMap[node.__instanceId]; + } + }, + + _dispatchEventToListeners: function (listeners, onEvent, eventOrArgs) { + var shouldStopPropagation = false; + var fixedPriorityListeners = listeners.getFixedPriorityListeners(); + var sceneGraphPriorityListeners = listeners.getSceneGraphPriorityListeners(); + + var i = 0, j, selListener; + if (fixedPriorityListeners) { // priority < 0 + if (fixedPriorityListeners.length !== 0) { + for (; i < listeners.gt0Index; ++i) { + selListener = fixedPriorityListeners[i]; + if (selListener.isEnabled() && !selListener._isPaused() && selListener._isRegistered() && onEvent(selListener, eventOrArgs)) { + shouldStopPropagation = true; + break; + } + } + } + } + + if (sceneGraphPriorityListeners && !shouldStopPropagation) { // priority == 0, scene graph priority + for (j = 0; j < sceneGraphPriorityListeners.length; j++) { + selListener = sceneGraphPriorityListeners[j]; + if (selListener.isEnabled() && !selListener._isPaused() && selListener._isRegistered() && onEvent(selListener, eventOrArgs)) { + shouldStopPropagation = true; + break; + } + } + } + + if (fixedPriorityListeners && !shouldStopPropagation) { // priority > 0 + for (; i < fixedPriorityListeners.length; ++i) { + selListener = fixedPriorityListeners[i]; + if (selListener.isEnabled() && !selListener._isPaused() && selListener._isRegistered() && onEvent(selListener, eventOrArgs)) { + shouldStopPropagation = true; + break; + } + } + } + }, + + _setDirty: function (listenerID, flag) { + var locDirtyFlagMap = this._priorityDirtyFlagMap; + if (locDirtyFlagMap[listenerID] == null) + locDirtyFlagMap[listenerID] = flag; + else + locDirtyFlagMap[listenerID] = flag | locDirtyFlagMap[listenerID]; + }, + + _visitTarget: function (node, isRootNode) { + var children = node.getChildren(), i = 0; + var childrenCount = children.length, locGlobalZOrderNodeMap = this._globalZOrderNodeMap, locNodeListenersMap = this._nodeListenersMap; + + if (childrenCount > 0) { + var child; + // visit children zOrder < 0 + for (; i < childrenCount; i++) { + child = children[i]; + if (child && child.getLocalZOrder() < 0) + this._visitTarget(child, false); + else + break; + } + + if (locNodeListenersMap[node.__instanceId] != null) { + if (!locGlobalZOrderNodeMap[node.getGlobalZOrder()]) + locGlobalZOrderNodeMap[node.getGlobalZOrder()] = []; + locGlobalZOrderNodeMap[node.getGlobalZOrder()].push(node.__instanceId); + } + + for (; i < childrenCount; i++) { + child = children[i]; + if (child) + this._visitTarget(child, false); + } + } else { + if (locNodeListenersMap[node.__instanceId] != null) { + if (!locGlobalZOrderNodeMap[node.getGlobalZOrder()]) + locGlobalZOrderNodeMap[node.getGlobalZOrder()] = []; + locGlobalZOrderNodeMap[node.getGlobalZOrder()].push(node.__instanceId); + } + } + + if (isRootNode) { + var globalZOrders = []; + for (var selKey in locGlobalZOrderNodeMap) + globalZOrders.push(selKey); + + globalZOrders.sort(this._sortNumberAsc); + + var zOrdersLen = globalZOrders.length, selZOrders, j, locNodePriorityMap = this._nodePriorityMap; + for (i = 0; i < zOrdersLen; i++) { + selZOrders = locGlobalZOrderNodeMap[globalZOrders[i]]; + for (j = 0; j < selZOrders.length; j++) + locNodePriorityMap[selZOrders[j]] = ++this._nodePriorityIndex; + } + this._globalZOrderNodeMap = {}; + } + }, + + _sortNumberAsc: function (a, b) { + return a - b; + }, + + /** + *

+ * Adds a event listener for a specified event.
+ * if the parameter "nodeOrPriority" is a node, it means to add a event listener for a specified event with the priority of scene graph.
+ * if the parameter "nodeOrPriority" is a Number, it means to add a event listener for a specified event with the fixed priority.
+ *

+ * @param {cc.EventListener|Object} listener The listener of a specified event or a object of some event parameters. + * @param {cc.Node|Number} nodeOrPriority The priority of the listener is based on the draw order of this node or fixedPriority The fixed priority of the listener. + * @note The priority of scene graph will be fixed value 0. So the order of listener item in the vector will be ' <0, scene graph (0 priority), >0'. + * A lower priority will be called before the ones that have a higher value. 0 priority is forbidden for fixed priority since it's used for scene graph based priority. + * The listener must be a cc.EventListener object when adding a fixed priority listener, because we can't remove a fixed priority listener without the listener handler, + * except calls removeAllListeners(). + * @return {cc.EventListener} Return the listener. Needed in order to remove the event from the dispatcher. + */ + addListener: function (listener, nodeOrPriority) { + cc.assert(listener && nodeOrPriority, cc._LogInfos.eventManager_addListener_2); + if (!(listener instanceof cc.EventListener)) { + cc.assert(!cc.isNumber(nodeOrPriority), cc._LogInfos.eventManager_addListener_3); + listener = cc.EventListener.create(listener); + } else { + if (listener._isRegistered()) { + cc.log(cc._LogInfos.eventManager_addListener_4); + return; + } + } + + if (!listener.checkAvailable()) + return; + + if (cc.isNumber(nodeOrPriority)) { + if (nodeOrPriority === 0) { + cc.log(cc._LogInfos.eventManager_addListener); + return; + } + + listener._setSceneGraphPriority(null); + listener._setFixedPriority(nodeOrPriority); + listener._setRegistered(true); + listener._setPaused(false); + this._addListener(listener); + } else { + listener._setSceneGraphPriority(nodeOrPriority); + listener._setFixedPriority(0); + listener._setRegistered(true); + this._addListener(listener); + } + + return listener; + }, + + /** + * Adds a Custom event listener. It will use a fixed priority of 1. + * @param {string} eventName + * @param {function} callback + * @return {cc.EventListener} the generated event. Needed in order to remove the event from the dispatcher + */ + addCustomListener: function (eventName, callback, target) { + var listener = new cc._EventListenerCustom(eventName, callback, target); + this.addListener(listener, 1); + return listener; + }, + + /** + * Remove a listener + * @param {cc.EventListener} listener an event listener or a registered node target + */ + removeListener: function (listener) { + if (listener == null) + return; + + var isFound, locListener = this._listenersMap; + for (var selKey in locListener) { + var listeners = locListener[selKey]; + var fixedPriorityListeners = listeners.getFixedPriorityListeners(), sceneGraphPriorityListeners = listeners.getSceneGraphPriorityListeners(); + + isFound = this._removeListenerInVector(sceneGraphPriorityListeners, listener); + if (isFound){ + // fixed #4160: Dirty flag need to be updated after listeners were removed. + this._setDirty(listener._getListenerID(), this.DIRTY_SCENE_GRAPH_PRIORITY); + }else{ + isFound = this._removeListenerInVector(fixedPriorityListeners, listener); + if (isFound) + this._setDirty(listener._getListenerID(), this.DIRTY_FIXED_PRIORITY); + } + + if (listeners.empty()) { + delete this._priorityDirtyFlagMap[listener._getListenerID()]; + delete locListener[selKey]; + } + + if (isFound) + break; + } + + if (!isFound) { + var locToAddedListeners = this._toAddedListeners; + for (var i = 0, len = locToAddedListeners.length; i < len; i++) { + var selListener = locToAddedListeners[i]; + if (selListener === listener) { + cc.arrayRemoveObject(locToAddedListeners, selListener); + selListener._setRegistered(false); + break; + } + } + } + }, + + _removeListenerInCallback: function (listeners, callback) { + if (listeners == null) + return false; + + for (var i = 0, len = listeners.length; i < len; i++) { + var selListener = listeners[i]; + if (selListener._onCustomEvent === callback || selListener._onEvent === callback) { + selListener._setRegistered(false); + if (selListener._getSceneGraphPriority() != null) { + this._dissociateNodeAndEventListener(selListener._getSceneGraphPriority(), selListener); + selListener._setSceneGraphPriority(null); // NULL out the node pointer so we don't have any dangling pointers to destroyed nodes. + } + + if (this._inDispatch === 0) + cc.arrayRemoveObject(listeners, selListener); + return true; + } + } + return false; + }, + + _removeListenerInVector: function (listeners, listener) { + if (listeners == null) + return false; + + for (var i = 0, len = listeners.length; i < len; i++) { + var selListener = listeners[i]; + if (selListener === listener) { + selListener._setRegistered(false); + if (selListener._getSceneGraphPriority() != null) { + this._dissociateNodeAndEventListener(selListener._getSceneGraphPriority(), selListener); + selListener._setSceneGraphPriority(null); // NULL out the node pointer so we don't have any dangling pointers to destroyed nodes. + } + + if (this._inDispatch === 0) + cc.arrayRemoveObject(listeners, selListener); + else + this._toRemovedListeners.push(selListener); + return true; + } + } + return false; + }, + + /** + * Removes all listeners with the same event listener type or removes all listeners of a node + * @param {Number|cc.Node} listenerType listenerType or a node + * @param {Boolean} [recursive=false] + */ + removeListeners: function (listenerType, recursive) { + var _t = this; + if (listenerType instanceof cc.Node) { + // Ensure the node is removed from these immediately also. + // Don't want any dangling pointers or the possibility of dealing with deleted objects.. + delete _t._nodePriorityMap[listenerType.__instanceId]; + cc.arrayRemoveObject(_t._dirtyNodes, listenerType); + var listeners = _t._nodeListenersMap[listenerType.__instanceId], i; + if (listeners) { + var listenersCopy = cc.copyArray(listeners); + for (i = 0; i < listenersCopy.length; i++) + _t.removeListener(listenersCopy[i]); + listenersCopy.length = 0; + } + + // Bug fix: ensure there are no references to the node in the list of listeners to be added. + // If we find any listeners associated with the destroyed node in this list then remove them. + // This is to catch the scenario where the node gets destroyed before it's listener + // is added into the event dispatcher fully. This could happen if a node registers a listener + // and gets destroyed while we are dispatching an event (touch etc.) + var locToAddedListeners = _t._toAddedListeners; + for (i = 0; i < locToAddedListeners.length; ) { + var listener = locToAddedListeners[i]; + if (listener._getSceneGraphPriority() === listenerType) { + listener._setSceneGraphPriority(null); // Ensure no dangling ptr to the target node. + listener._setRegistered(false); + locToAddedListeners.splice(i, 1); + } else + ++i; + } + + if (recursive === true) { + var locChildren = listenerType.getChildren(), len; + for (i = 0, len = locChildren.length; i< len; i++) + _t.removeListeners(locChildren[i], true); + } + } else { + if (listenerType === cc.EventListener.TOUCH_ONE_BY_ONE) + _t._removeListenersForListenerID(cc._EventListenerTouchOneByOne.LISTENER_ID); + else if (listenerType === cc.EventListener.TOUCH_ALL_AT_ONCE) + _t._removeListenersForListenerID(cc._EventListenerTouchAllAtOnce.LISTENER_ID); + else if (listenerType === cc.EventListener.MOUSE) + _t._removeListenersForListenerID(cc._EventListenerMouse.LISTENER_ID); + else if (listenerType === cc.EventListener.ACCELERATION) + _t._removeListenersForListenerID(cc._EventListenerAcceleration.LISTENER_ID); + else if (listenerType === cc.EventListener.KEYBOARD) + _t._removeListenersForListenerID(cc._EventListenerKeyboard.LISTENER_ID); + else + cc.log(cc._LogInfos.eventManager_removeListeners); + } + }, + + /** + * Removes all custom listeners with the same event name + * @param {string} customEventName + */ + removeCustomListeners: function (customEventName) { + this._removeListenersForListenerID(customEventName); + }, + + /** + * Removes all listeners + */ + removeAllListeners: function () { + var locListeners = this._listenersMap, locInternalCustomEventIDs = this._internalCustomListenerIDs; + for (var selKey in locListeners) { + if (locInternalCustomEventIDs.indexOf(selKey) === -1) + this._removeListenersForListenerID(selKey); + } + }, + + /** + * Sets listener's priority with fixed value. + * @param {cc.EventListener} listener + * @param {Number} fixedPriority + */ + setPriority: function (listener, fixedPriority) { + if (listener == null) + return; + + var locListeners = this._listenersMap; + for (var selKey in locListeners) { + var selListeners = locListeners[selKey]; + var fixedPriorityListeners = selListeners.getFixedPriorityListeners(); + if (fixedPriorityListeners) { + var found = fixedPriorityListeners.indexOf(listener); + if (found !== -1) { + if (listener._getSceneGraphPriority() != null) + cc.log(cc._LogInfos.eventManager_setPriority); + if (listener._getFixedPriority() !== fixedPriority) { + listener._setFixedPriority(fixedPriority); + this._setDirty(listener._getListenerID(), this.DIRTY_FIXED_PRIORITY); + } + return; + } + } + } + }, + + /** + * Whether to enable dispatching events + * @param {boolean} enabled + */ + setEnabled: function (enabled) { + this._isEnabled = enabled; + }, + + /** + * Checks whether dispatching events is enabled + * @returns {boolean} + */ + isEnabled: function () { + return this._isEnabled; + }, + + /** + * Dispatches the event, also removes all EventListeners marked for deletion from the event dispatcher list. + * @param {cc.Event} event + */ + dispatchEvent: function (event) { + if (!this._isEnabled) + return; + + this._updateDirtyFlagForSceneGraph(); + this._inDispatch++; + if (!event || !event.getType) + throw new Error("event is undefined"); + if (event._type === cc.Event.TOUCH) { + this._dispatchTouchEvent(event); + this._inDispatch--; + return; + } + + var listenerID = __getListenerID(event); + this._sortEventListeners(listenerID); + var selListeners = this._listenersMap[listenerID]; + if (selListeners) { + this._dispatchEventToListeners(selListeners, this._onListenerCallback, event); + this._onUpdateListeners(selListeners); + } + + this._inDispatch--; + }, + + _onListenerCallback: function (listener, event) { + event._setCurrentTarget(listener._getSceneGraphPriority()); + listener._onEvent(event); + return event.isStopped(); + }, + + /** + * Dispatches a Custom Event with a event name an optional user data + * @param {string} eventName + * @param {*} optionalUserData + */ + dispatchCustomEvent: function (eventName, optionalUserData) { + var ev = new cc.EventCustom(eventName); + ev.setUserData(optionalUserData); + this.dispatchEvent(ev); + } +}; + +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/core/event-manager/CCTouch.js b/frameworks/cocos2d-html5/cocos2d/core/event-manager/CCTouch.js new file mode 100644 index 0000000..3c8ea79 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/event-manager/CCTouch.js @@ -0,0 +1,176 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The touch event class + * @class + * @extends cc.Class + * + * @param {Number} x + * @param {Number} y + * @param {Number} id + */ +cc.Touch = cc.Class.extend(/** @lends cc.Touch# */{ + _lastModified: 0, + _point:null, + _prevPoint:null, + _id:0, + _startPointCaptured: false, + _startPoint:null, + + ctor:function (x, y, id) { + this.setTouchInfo(id, x, y); + }, + + /** + * Returns the current touch location in OpenGL coordinates + * @return {cc.Point} + */ + getLocation:function () { + //TODO + //return cc.director.convertToGL(this._point); + return {x: this._point.x, y: this._point.y}; + }, + + /** + * Returns X axis location value + * @returns {number} + */ + getLocationX: function () { + return this._point.x; + }, + + /** + * Returns Y axis location value + * @returns {number} + */ + getLocationY: function () { + return this._point.y; + }, + + /** + * Returns the previous touch location in OpenGL coordinates + * @return {cc.Point} + */ + getPreviousLocation:function () { + //TODO + //return cc.director.convertToGL(this._prevPoint); + return {x: this._prevPoint.x, y: this._prevPoint.y}; + }, + + /** + * Returns the start touch location in OpenGL coordinates + * @returns {cc.Point} + */ + getStartLocation: function() { + //TODO + //return cc.director.convertToGL(this._startPoint); + return {x: this._startPoint.x, y: this._startPoint.y}; + }, + + /** + * Returns the delta distance from the previous touche to the current one in screen coordinates + * @return {cc.Point} + */ + getDelta:function () { + return cc.pSub(this._point, this._prevPoint); + }, + + /** + * Returns the current touch location in screen coordinates + * @return {cc.Point} + */ + getLocationInView: function() { + return {x: this._point.x, y: this._point.y}; + }, + + /** + * Returns the previous touch location in screen coordinates + * @return {cc.Point} + */ + getPreviousLocationInView: function(){ + return {x: this._prevPoint.x, y: this._prevPoint.y}; + }, + + /** + * Returns the start touch location in screen coordinates + * @return {cc.Point} + */ + getStartLocationInView: function(){ + return {x: this._startPoint.x, y: this._startPoint.y}; + }, + + /** + * Returns the id of cc.Touch + * @return {Number} + */ + getID:function () { + return this._id; + }, + + /** + * Returns the id of cc.Touch + * @return {Number} + * @deprecated since v3.0, please use getID() instead + */ + getId:function () { + cc.log("getId is deprecated. Please use getID instead."); + return this._id; + }, + + /** + * Sets information to touch + * @param {Number} id + * @param {Number} x + * @param {Number} y + */ + setTouchInfo:function (id, x, y) { + this._prevPoint = this._point; + this._point = cc.p(x || 0, y || 0); + this._id = id; + if (!this._startPointCaptured) { + this._startPoint = cc.p(this._point); + cc.view._convertPointWithScale(this._startPoint); + this._startPointCaptured = true; + } + }, + + _setPoint: function(x, y){ + if(y === undefined){ + this._point.x = x.x; + this._point.y = x.y; + }else{ + this._point.x = x; + this._point.y = y; + } + }, + + _setPrevPoint:function (x, y) { + if(y === undefined) + this._prevPoint = cc.p(x.x, x.y); + else + this._prevPoint = cc.p(x || 0, y || 0); + } +}); \ No newline at end of file diff --git a/frameworks/cocos2d-html5/cocos2d/core/labelttf/CCLabelTTF.js b/frameworks/cocos2d-html5/cocos2d/core/labelttf/CCLabelTTF.js new file mode 100644 index 0000000..ac98810 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/labelttf/CCLabelTTF.js @@ -0,0 +1,974 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + *

cc.LabelTTF is a subclass of cc.TextureNode that knows how to render text labels with system font or a ttf font file
+ * All features from cc.Sprite are valid in cc.LabelTTF
+ * cc.LabelTTF objects are slow for js-binding on mobile devices.
+ * Consider using cc.LabelAtlas or cc.LabelBMFont instead.
+ * You can create a cc.LabelTTF from a font name, alignment, dimension and font size or a cc.FontDefinition object.

+ * @class + * @extends cc.Sprite + * + * @param {String} text + * @param {String|cc.FontDefinition} [fontName="Arial"] + * @param {Number} [fontSize=16] + * @param {cc.Size} [dimensions=cc.size(0,0)] + * @param {Number} [hAlignment=cc.TEXT_ALIGNMENT_LEFT] + * @param {Number} [vAlignment=cc.VERTICAL_TEXT_ALIGNMENT_TOP] + * @example + * var myLabel = new cc.LabelTTF('label text', 'Times New Roman', 32, cc.size(320,32), cc.TEXT_ALIGNMENT_LEFT); + * + * var fontDef = new cc.FontDefinition(); + * fontDef.fontName = "Arial"; + * fontDef.fontSize = "32"; + * var myLabel = new cc.LabelTTF('label text', fontDef); + * + * @property {String} string - Content string of label + * @property {Number} textAlign - Horizontal Alignment of label: cc.TEXT_ALIGNMENT_LEFT|cc.TEXT_ALIGNMENT_CENTER|cc.TEXT_ALIGNMENT_RIGHT + * @property {Number} verticalAlign - Vertical Alignment of label: cc.VERTICAL_TEXT_ALIGNMENT_TOP|cc.VERTICAL_TEXT_ALIGNMENT_CENTER|cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM + * @property {Number} fontSize - Font size of label + * @property {String} fontName - Font name of label + * @property {String} font - The label font with a style string: e.g. "18px Verdana" + * @property {Number} boundingWidth - Width of the bounding box of label, the real content width is limited by boundingWidth + * @property {Number} boundingHeight - Height of the bounding box of label, the real content height is limited by boundingHeight + * @property {cc.Color} fillStyle - The fill color + * @property {cc.Color} strokeStyle - The stroke color + * @property {Number} lineWidth - The line width for stroke + * @property {Number} shadowOffsetX - The x axis offset of shadow + * @property {Number} shadowOffsetY - The y axis offset of shadow + * @property {Number} shadowOpacity - The opacity of shadow + * @property {Number} shadowBlur - The blur size of shadow + */ +cc.LabelTTF = cc.Sprite.extend(/** @lends cc.LabelTTF# */{ + _dimensions: null, + _hAlignment: cc.TEXT_ALIGNMENT_CENTER, + _vAlignment: cc.VERTICAL_TEXT_ALIGNMENT_TOP, + _fontName: null, + _fontSize: 0.0, + _string: "", + _originalText: null, + _onCacheCanvasMode: true, + + // font shadow + _shadowEnabled: false, + _shadowOffset: null, + _shadowOpacity: 0, + _shadowBlur: 0, + _shadowColor: null, + + // font stroke + _strokeEnabled: false, + _strokeColor: null, + _strokeSize: 0, + + // font tint + _textFillColor: null, + + _strokeShadowOffsetX: 0, + _strokeShadowOffsetY: 0, + _needUpdateTexture: false, + + _lineWidths: null, + _className: "LabelTTF", + + //for web + _fontStyle: "normal", + _fontWeight: "normal", + _lineHeight: "normal", + + /** + * Initializes the cc.LabelTTF with a font name, alignment, dimension and font size, do not call it by yourself, + * you should pass the correct arguments in constructor to initialize the label. + * @param {String} label string + * @param {String} fontName + * @param {Number} fontSize + * @param {cc.Size} [dimensions=] + * @param {Number} [hAlignment=] + * @param {Number} [vAlignment=] + * @return {Boolean} return false on error + */ + initWithString: function (label, fontName, fontSize, dimensions, hAlignment, vAlignment) { + var strInfo; + if (label) + strInfo = label + ""; + else + strInfo = ""; + + fontSize = fontSize || 16; + dimensions = dimensions || cc.size(0, 0/*fontSize*/); + hAlignment = hAlignment || cc.TEXT_ALIGNMENT_LEFT; + vAlignment = vAlignment || cc.VERTICAL_TEXT_ALIGNMENT_TOP; + + this._opacityModifyRGB = false; + this._dimensions = cc.size(dimensions.width, dimensions.height); + this._fontName = fontName || "Arial"; + this._hAlignment = hAlignment; + this._vAlignment = vAlignment; + + this._fontSize = fontSize; + this._renderCmd._setFontStyle(this._fontName, fontSize, this._fontStyle, this._fontWeight); + this.string = strInfo; + this._renderCmd._setColorsString(); + this._renderCmd._updateTexture(); + this._setUpdateTextureDirty(); + + // Needed for high dpi text. + // In order to render it crisp, we request devicePixelRatio times the + // font size and scale it down 1/devicePixelRatio. + this._scaleX = this._scaleY = 1 / cc.view.getDevicePixelRatio(); + return true; + }, + + _setUpdateTextureDirty: function () { + this._needUpdateTexture = true; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.textDirty); + }, + + ctor: function (text, fontName, fontSize, dimensions, hAlignment, vAlignment) { + cc.Sprite.prototype.ctor.call(this); + + this._dimensions = cc.size(0, 0); + this._hAlignment = cc.TEXT_ALIGNMENT_LEFT; + this._vAlignment = cc.VERTICAL_TEXT_ALIGNMENT_TOP; + this._opacityModifyRGB = false; + this._fontName = "Arial"; + + this._shadowEnabled = false; + this._shadowOffset = cc.p(0, 0); + this._shadowOpacity = 0; + this._shadowBlur = 0; + + this._strokeEnabled = false; + this._strokeColor = cc.color(255, 255, 255, 255); + this._strokeSize = 0; + + this._textFillColor = cc.color(255, 255, 255, 255); + this._strokeShadowOffsetX = 0; + this._strokeShadowOffsetY = 0; + this._needUpdateTexture = false; + + this._lineWidths = []; + this._renderCmd._setColorsString(); + this._textureLoaded = true; + + if (fontName && fontName instanceof cc.FontDefinition) { + this.initWithStringAndTextDefinition(text, fontName); + } else { + cc.LabelTTF.prototype.initWithString.call(this, text, fontName, fontSize, dimensions, hAlignment, vAlignment); + } + }, + + init: function () { + return this.initWithString(" ", this._fontName, this._fontSize); + }, + + description: function () { + return ""; + }, + + getLineHeight: function () { + return !this._lineHeight || this._lineHeight.charAt ? + this._renderCmd._getFontClientHeight() : + this._lineHeight || this._renderCmd._getFontClientHeight(); + }, + + setLineHeight: function (lineHeight) { + this._lineHeight = lineHeight; + }, + + /** + * Returns the text of the label + * @return {String} + */ + getString: function () { + return this._string; + }, + + /** + * Returns Horizontal Alignment of cc.LabelTTF + * @return {cc.TEXT_ALIGNMENT_LEFT|cc.TEXT_ALIGNMENT_CENTER|cc.TEXT_ALIGNMENT_RIGHT} + */ + getHorizontalAlignment: function () { + return this._hAlignment; + }, + + /** + * Returns Vertical Alignment of cc.LabelTTF + * @return {cc.VERTICAL_TEXT_ALIGNMENT_TOP|cc.VERTICAL_TEXT_ALIGNMENT_CENTER|cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM} + */ + getVerticalAlignment: function () { + return this._vAlignment; + }, + + /** + * Returns the dimensions of cc.LabelTTF, the dimension is the maximum size of the label, set it so that label will automatically change lines when necessary. + * @see cc.LabelTTF#setDimensions, cc.LabelTTF#boundingWidth and cc.LabelTTF#boundingHeight + * @return {cc.Size} + */ + getDimensions: function () { + return cc.size(this._dimensions); + }, + + /** + * Returns font size of cc.LabelTTF + * @return {Number} + */ + getFontSize: function () { + return this._fontSize; + }, + + /** + * Returns font name of cc.LabelTTF + * @return {String} + */ + getFontName: function () { + return this._fontName; + }, + + /** + * Initializes the CCLabelTTF with a font name, alignment, dimension and font size, do not call it by yourself, you should pass the correct arguments in constructor to initialize the label. + * @param {String} text + * @param {cc.FontDefinition} textDefinition + * @return {Boolean} + */ + initWithStringAndTextDefinition: function (text, textDefinition) { + // prepare everything needed to render the label + this._updateWithTextDefinition(textDefinition, false); + // set the string + this.string = text; + return true; + }, + + /** + * Sets the text definition used by this label + * @param {cc.FontDefinition} theDefinition + */ + setTextDefinition: function (theDefinition) { + if (theDefinition) + this._updateWithTextDefinition(theDefinition, true); + }, + + /** + * Extract the text definition used by this label + * @return {cc.FontDefinition} + */ + getTextDefinition: function () { + return this._prepareTextDefinition(false); + }, + + /** + * Enable or disable shadow for the label + * @param {cc.Color | Number} a Color or The x axis offset of the shadow + * @param {cc.Size | Number} b Size or The y axis offset of the shadow + * @param {Number} c The blur size of the shadow or The opacity of the shadow (0 to 1) + * @param {null | Number} d Null or The blur size of the shadow + * @example + * old: + * labelttf.enableShadow(shadowOffsetX, shadowOffsetY, shadowOpacity, shadowBlur); + * new: + * labelttf.enableShadow(shadowColor, offset, blurRadius); + */ + enableShadow: function (a, b, c, d) { + if (a.r != null && a.g != null && a.b != null && a.a != null) { + this._enableShadow(a, b, c); + } else { + this._enableShadowNoneColor(a, b, c, d); + } + }, + + _enableShadowNoneColor: function (shadowOffsetX, shadowOffsetY, shadowOpacity, shadowBlur) { + shadowOpacity = shadowOpacity || 0.5; + if (false === this._shadowEnabled) + this._shadowEnabled = true; + + var locShadowOffset = this._shadowOffset; + if (locShadowOffset && (locShadowOffset.x !== shadowOffsetX) || (locShadowOffset._y !== shadowOffsetY)) { + locShadowOffset.x = shadowOffsetX; + locShadowOffset.y = shadowOffsetY; + } + + if (this._shadowOpacity !== shadowOpacity) { + this._shadowOpacity = shadowOpacity; + } + this._renderCmd._setColorsString(); + + if (this._shadowBlur !== shadowBlur) + this._shadowBlur = shadowBlur; + this._setUpdateTextureDirty(); + }, + + _enableShadow: function (shadowColor, offset, blurRadius) { + if (!this._shadowColor) { + this._shadowColor = cc.color(255, 255, 255, 128); + } + this._shadowColor.r = shadowColor.r; + this._shadowColor.g = shadowColor.g; + this._shadowColor.b = shadowColor.b; + + var x, y, a, b; + x = offset.width || offset.x || 0; + y = offset.height || offset.y || 0; + a = (shadowColor.a != null) ? (shadowColor.a / 255) : 0.5; + b = blurRadius; + + this._enableShadowNoneColor(x, y, a, b); + }, + + _getShadowOffsetX: function () { + return this._shadowOffset.x; + }, + _setShadowOffsetX: function (x) { + if (false === this._shadowEnabled) + this._shadowEnabled = true; + + if (this._shadowOffset.x !== x) { + this._shadowOffset.x = x; + this._setUpdateTextureDirty(); + } + }, + + _getShadowOffsetY: function () { + return this._shadowOffset._y; + }, + _setShadowOffsetY: function (y) { + if (false === this._shadowEnabled) + this._shadowEnabled = true; + + if (this._shadowOffset._y !== y) { + this._shadowOffset._y = y; + this._setUpdateTextureDirty(); + } + }, + + _getShadowOffset: function () { + return cc.p(this._shadowOffset.x, this._shadowOffset.y); + }, + _setShadowOffset: function (offset) { + if (false === this._shadowEnabled) + this._shadowEnabled = true; + + if (this._shadowOffset.x !== offset.x || this._shadowOffset.y !== offset.y) { + this._shadowOffset.x = offset.x; + this._shadowOffset.y = offset.y; + this._setUpdateTextureDirty(); + } + }, + + _getShadowOpacity: function () { + return this._shadowOpacity; + }, + _setShadowOpacity: function (shadowOpacity) { + if (false === this._shadowEnabled) + this._shadowEnabled = true; + + if (this._shadowOpacity !== shadowOpacity) { + this._shadowOpacity = shadowOpacity; + this._renderCmd._setColorsString(); + this._setUpdateTextureDirty(); + } + }, + + _getShadowBlur: function () { + return this._shadowBlur; + }, + _setShadowBlur: function (shadowBlur) { + if (false === this._shadowEnabled) + this._shadowEnabled = true; + + if (this._shadowBlur !== shadowBlur) { + this._shadowBlur = shadowBlur; + this._setUpdateTextureDirty(); + } + }, + + /** + * Disable shadow rendering + */ + disableShadow: function () { + if (this._shadowEnabled) { + this._shadowEnabled = false; + this._setUpdateTextureDirty(); + } + }, + + /** + * Enable label stroke with stroke parameters + * @param {cc.Color} strokeColor The color of stroke + * @param {Number} strokeSize The size of stroke + */ + enableStroke: function (strokeColor, strokeSize) { + if (this._strokeEnabled === false) + this._strokeEnabled = true; + + var locStrokeColor = this._strokeColor; + if ((locStrokeColor.r !== strokeColor.r) || (locStrokeColor.g !== strokeColor.g) || (locStrokeColor.b !== strokeColor.b)) { + locStrokeColor.r = strokeColor.r; + locStrokeColor.g = strokeColor.g; + locStrokeColor.b = strokeColor.b; + this._renderCmd._setColorsString(); + } + + if (this._strokeSize !== strokeSize) + this._strokeSize = strokeSize || 0; + this._setUpdateTextureDirty(); + }, + + _getStrokeStyle: function () { + return this._strokeColor; + }, + _setStrokeStyle: function (strokeStyle) { + if (this._strokeEnabled === false) + this._strokeEnabled = true; + + var locStrokeColor = this._strokeColor; + if ((locStrokeColor.r !== strokeStyle.r) || (locStrokeColor.g !== strokeStyle.g) || (locStrokeColor.b !== strokeStyle.b)) { + locStrokeColor.r = strokeStyle.r; + locStrokeColor.g = strokeStyle.g; + locStrokeColor.b = strokeStyle.b; + this._renderCmd._setColorsString(); + this._setUpdateTextureDirty(); + } + }, + + _getLineWidth: function () { + return this._strokeSize; + }, + _setLineWidth: function (lineWidth) { + if (this._strokeEnabled === false) + this._strokeEnabled = true; + if (this._strokeSize !== lineWidth) { + this._strokeSize = lineWidth || 0; + this._setUpdateTextureDirty(); + } + }, + + /** + * Disable label stroke + */ + disableStroke: function () { + if (this._strokeEnabled) { + this._strokeEnabled = false; + this._setUpdateTextureDirty(); + } + }, + + /** + * Sets the text fill color + * @function + * @param {cc.Color} fillColor The fill color of the label + */ + setFontFillColor: function (fillColor) { + var locTextFillColor = this._textFillColor; + if (locTextFillColor.r !== fillColor.r || locTextFillColor.g !== fillColor.g || locTextFillColor.b !== fillColor.b) { + locTextFillColor.r = fillColor.r; + locTextFillColor.g = fillColor.g; + locTextFillColor.b = fillColor.b; + this._renderCmd._setColorsString(); + this._needUpdateTexture = true; + } + }, + + _getFillStyle: function () { + return this._textFillColor; + }, + + //set the text definition for this label + _updateWithTextDefinition: function (textDefinition, mustUpdateTexture) { + if (textDefinition.fontDimensions) { + this._dimensions.width = textDefinition.boundingWidth; + this._dimensions.height = textDefinition.boundingHeight; + } else { + this._dimensions.width = 0; + this._dimensions.height = 0; + } + + this._hAlignment = textDefinition.textAlign; + this._vAlignment = textDefinition.verticalAlign; + + this._fontName = textDefinition.fontName; + this._fontSize = textDefinition.fontSize || 12; + + if (textDefinition.lineHeight) + this._lineHeight = textDefinition.lineHeight; + else + this._lineHeight = this._fontSize; + + this._renderCmd._setFontStyle(textDefinition); + + + // shadow + if (textDefinition.shadowEnabled) + this.enableShadow(textDefinition.shadowOffsetX, + textDefinition.shadowOffsetY, + textDefinition.shadowOpacity, + textDefinition.shadowBlur); + + // stroke + if (textDefinition.strokeEnabled) + this.enableStroke(textDefinition.strokeStyle, textDefinition.lineWidth); + + // fill color + this.setFontFillColor(textDefinition.fillStyle); + + if (mustUpdateTexture) + this._renderCmd._updateTexture(); + var flags = cc.Node._dirtyFlags; + this._renderCmd.setDirtyFlag(flags.colorDirty | flags.opacityDirty | flags.textDirty); + }, + + _prepareTextDefinition: function (adjustForResolution) { + var texDef = new cc.FontDefinition(); + + if (adjustForResolution) { + texDef.fontSize = this._fontSize; + texDef.boundingWidth = cc.contentScaleFactor() * this._dimensions.width; + texDef.boundingHeight = cc.contentScaleFactor() * this._dimensions.height; + } else { + texDef.fontSize = this._fontSize; + texDef.boundingWidth = this._dimensions.width; + texDef.boundingHeight = this._dimensions.height; + } + + texDef.fontName = this._fontName; + texDef.textAlign = this._hAlignment; + texDef.verticalAlign = this._vAlignment; + + // stroke + if (this._strokeEnabled) { + texDef.strokeEnabled = true; + var locStrokeColor = this._strokeColor; + texDef.strokeStyle = cc.color(locStrokeColor.r, locStrokeColor.g, locStrokeColor.b); + texDef.lineWidth = this._strokeSize; + } else + texDef.strokeEnabled = false; + + // shadow + if (this._shadowEnabled) { + texDef.shadowEnabled = true; + texDef.shadowBlur = this._shadowBlur; + texDef.shadowOpacity = this._shadowOpacity; + + texDef.shadowOffsetX = (adjustForResolution ? cc.contentScaleFactor() : 1) * this._shadowOffset.x; + texDef.shadowOffsetY = (adjustForResolution ? cc.contentScaleFactor() : 1) * this._shadowOffset.y; + } else + texDef._shadowEnabled = false; + + // text tint + var locTextFillColor = this._textFillColor; + texDef.fillStyle = cc.color(locTextFillColor.r, locTextFillColor.g, locTextFillColor.b); + return texDef; + }, + + /* + * BEGIN SCALE METHODS + * + * In order to make the value of scaleX and scaleY consistent across + * screens, we provide patched versions that return the same values as if + * the screen was not HiDPI. + */ + + /** + * Returns the scale factor of the node. + * @warning: Assertion will fail when _scaleX != _scaleY. + * @function + * @return {Number} The scale factor + */ + getScale: function () { + if (this._scaleX !== this._scaleY) + cc.log(cc._LogInfos.Node_getScale); + return this._scaleX * cc.view.getDevicePixelRatio(); + }, + + /** + * Sets the scale factor of the node. 1.0 is the default scale factor. This function can modify the X and Y scale at the same time. + * @function + * @param {Number} scale or scaleX value + * @param {Number} [scaleY=] + */ + setScale: function (scale, scaleY) { + var ratio = cc.view.getDevicePixelRatio(); + this._scaleX = scale / ratio; + this._scaleY = ((scaleY || scaleY === 0) ? scaleY : scale) / ratio; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }, + + /** + * Returns the scale factor on X axis of this node + * @function + * @return {Number} The scale factor on X axis. + */ + getScaleX: function () { + return this._scaleX * cc.view.getDevicePixelRatio(); + }, + + /** + *

+ * Changes the scale factor on X axis of this node
+ * The default value is 1.0 if you haven't changed it before + *

+ * @function + * @param {Number} newScaleX The scale factor on X axis. + */ + setScaleX: function (newScaleX) { + this._scaleX = newScaleX / cc.view.getDevicePixelRatio(); + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }, + + /** + * Returns the scale factor on Y axis of this node + * @function + * @return {Number} The scale factor on Y axis. + */ + getScaleY: function () { + return this._scaleY * cc.view.getDevicePixelRatio(); + }, + + /** + *

+ * Changes the scale factor on Y axis of this node
+ * The Default value is 1.0 if you haven't changed it before. + *

+ * @function + * @param {Number} newScaleY The scale factor on Y axis. + */ + setScaleY: function (newScaleY) { + this._scaleY = newScaleY / cc.view.getDevicePixelRatio(); + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }, + + /* + * END SCALE METHODS + */ + + /** + * Changes the text content of the label + * @warning Changing the string is as expensive as creating a new cc.LabelTTF. To obtain better performance use cc.LabelAtlas + * @param {String} text Text content for the label + */ + setString: function (text) { + text = String(text); + if (this._originalText !== text) { + this._originalText = text + ""; + + this._updateString(); + + // Force update + this._setUpdateTextureDirty(); + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + } + }, + _updateString: function () { + if ((!this._string || this._string === "") && this._string !== this._originalText) + cc.renderer.childrenOrderDirty = true; + this._string = this._originalText; + }, + + /** + * Sets Horizontal Alignment of cc.LabelTTF + * @param {cc.TEXT_ALIGNMENT_LEFT|cc.TEXT_ALIGNMENT_CENTER|cc.TEXT_ALIGNMENT_RIGHT} alignment Horizontal Alignment + */ + setHorizontalAlignment: function (alignment) { + if (alignment !== this._hAlignment) { + this._hAlignment = alignment; + // Force update + this._setUpdateTextureDirty(); + } + }, + + /** + * Sets Vertical Alignment of cc.LabelTTF + * @param {cc.VERTICAL_TEXT_ALIGNMENT_TOP|cc.VERTICAL_TEXT_ALIGNMENT_CENTER|cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM} verticalAlignment + */ + setVerticalAlignment: function (verticalAlignment) { + if (verticalAlignment !== this._vAlignment) { + this._vAlignment = verticalAlignment; + + // Force update + this._setUpdateTextureDirty(); + } + }, + + /** + * Set Dimensions of cc.LabelTTF, the dimension is the maximum size of the label, set it so that label will automatically change lines when necessary. + * @param {cc.Size|Number} dim dimensions or width of dimensions + * @param {Number} [height] height of dimensions + */ + setDimensions: function (dim, height) { + var width; + if (height === undefined) { + width = dim.width; + height = dim.height; + } else + width = dim; + + if (width !== this._dimensions.width || height !== this._dimensions.height) { + this._dimensions.width = width; + this._dimensions.height = height; + this._updateString(); + // Force update + this._setUpdateTextureDirty(); + } + }, + + _getBoundingWidth: function () { + return this._dimensions.width; + }, + _setBoundingWidth: function (width) { + if (width !== this._dimensions.width) { + this._dimensions.width = width; + this._updateString(); + // Force update + this._setUpdateTextureDirty(); + } + }, + + _getBoundingHeight: function () { + return this._dimensions.height; + }, + _setBoundingHeight: function (height) { + if (height !== this._dimensions.height) { + this._dimensions.height = height; + this._updateString(); + // Force update + this._setUpdateTextureDirty(); + } + }, + + /** + * Sets font size of cc.LabelTTF + * @param {Number} fontSize + */ + setFontSize: function (fontSize) { + if (this._fontSize !== fontSize) { + this._fontSize = fontSize; + this._renderCmd._setFontStyle(this._fontName, this._fontSize, this._fontStyle, this._fontWeight); + // Force update + this._setUpdateTextureDirty(); + } + }, + + /** + * Sets font name of cc.LabelTTF + * @param {String} fontName + */ + setFontName: function (fontName) { + if (this._fontName && this._fontName !== fontName) { + this._fontName = fontName; + this._renderCmd._setFontStyle(this._fontName, this._fontSize, this._fontStyle, this._fontWeight); + // Force update + this._setUpdateTextureDirty(); + } + }, + + _getFont: function () { + return this._renderCmd._getFontStyle(); + }, + _setFont: function (fontStyle) { + var res = cc.LabelTTF._fontStyleRE.exec(fontStyle); + if (res) { + this._fontSize = parseInt(res[1]); + this._fontName = res[2]; + this._renderCmd._setFontStyle(this._fontName, this._fontSize, this._fontStyle, this._fontWeight); + + // Force update + this._setUpdateTextureDirty(); + } + }, + + /** + * Returns the actual content size of the label, the content size is the real size that the label occupied while dimension is the outer bounding box of the label. + * @returns {cc.Size} The content size + */ + getContentSize: function () { + if (this._needUpdateTexture) + this._renderCmd._updateTTF(); + var ratio = cc.view.getDevicePixelRatio(); + return cc.size( this._contentSize.width / ratio, this._contentSize.height / ratio ); + }, + + _getWidth: function () { + if (this._needUpdateTexture) + this._renderCmd._updateTTF(); + return this._contentSize.width / cc.view.getDevicePixelRatio(); + }, + _getHeight: function () { + if (this._needUpdateTexture) + this._renderCmd._updateTTF(); + return this._contentSize.height / cc.view.getDevicePixelRatio(); + }, + + setTextureRect: function (rect, rotated, untrimmedSize) { + var _t = this; + _t._rectRotated = rotated || false; + _t.setContentSize(untrimmedSize || rect); + + var locRect = _t._rect; + locRect.x = rect.x; + locRect.y = rect.y; + locRect.width = rect.width; + locRect.height = rect.height; + _t._renderCmd._setTextureCoords(rect, false); + + var relativeOffsetX = _t._unflippedOffsetPositionFromCenter.x, relativeOffsetY = _t._unflippedOffsetPositionFromCenter.y; + if (_t._flippedX) + relativeOffsetX = -relativeOffsetX; + if (_t._flippedY) + relativeOffsetY = -relativeOffsetY; + _t._offsetPosition.x = relativeOffsetX + (rect.width - locRect.width) / 2; + _t._offsetPosition.y = relativeOffsetY + (rect.height - locRect.height) / 2; + }, + + /** + * set Target to draw on + * @param boolean onCanvas + */ + setDrawMode: function (onCacheMode) { + this._onCacheCanvasMode = onCacheMode; + }, + + _createRenderCmd: function () { + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) + return new cc.LabelTTF.WebGLRenderCmd(this); + else if (this._onCacheCanvasMode) + return new cc.LabelTTF.CacheCanvasRenderCmd(this); + else + return new cc.LabelTTF.CanvasRenderCmd(this); + }, + + //For web only + _setFontStyle: function (fontStyle) { + if (this._fontStyle !== fontStyle) { + this._fontStyle = fontStyle; + this._renderCmd._setFontStyle(this._fontName, this._fontSize, this._fontStyle, this._fontWeight); + this._setUpdateTextureDirty(); + } + }, + + _getFontStyle: function () { + return this._fontStyle; + }, + + _setFontWeight: function (fontWeight) { + if (this._fontWeight !== fontWeight) { + this._fontWeight = fontWeight; + this._renderCmd._setFontStyle(this._fontName, this._fontSize, this._fontStyle, this._fontWeight); + this._setUpdateTextureDirty(); + } + }, + + _getFontWeight: function () { + return this._fontWeight; + } +}); + +cc.assert(cc.isFunction(cc._tmp.PrototypeLabelTTF), cc._LogInfos.MissingFile, "LabelTTFPropertyDefine.js"); +cc._tmp.PrototypeLabelTTF(); +delete cc._tmp.PrototypeLabelTTF; + +// Only support style in this format: "18px Verdana" or "18px 'Helvetica Neue'" +cc.LabelTTF._fontStyleRE = /^(\d+)px\s+['"]?([\w\s\d]+)['"]?$/; + +/** + * creates a cc.LabelTTF from a font name, alignment, dimension and font size + * @deprecated since v3.0, please use the new construction instead + * @see cc.LabelTTF + * @static + * @param {String} text + * @param {String|cc.FontDefinition} [fontName="Arial"] + * @param {Number} [fontSize=16] + * @param {cc.Size} [dimensions=cc.size(0,0)] + * @param {Number} [hAlignment=cc.TEXT_ALIGNMENT_LEFT] + * @param {Number} [vAlignment=cc.VERTICAL_TEXT_ALIGNMENT_TOP] + * @return {cc.LabelTTF|Null} + */ +cc.LabelTTF.create = function (text, fontName, fontSize, dimensions, hAlignment, vAlignment) { + return new cc.LabelTTF(text, fontName, fontSize, dimensions, hAlignment, vAlignment); +}; + +/** + * @deprecated since v3.0, please use the new construction instead + * @function + * @static + */ +cc.LabelTTF.createWithFontDefinition = cc.LabelTTF.create; + +cc.LabelTTF.__labelHeightDiv = document.createElement("div"); +cc.LabelTTF.__labelHeightDiv.style.fontFamily = "Arial"; +cc.LabelTTF.__labelHeightDiv.style.position = "absolute"; +cc.LabelTTF.__labelHeightDiv.style.left = "-100px"; +cc.LabelTTF.__labelHeightDiv.style.top = "-100px"; +cc.LabelTTF.__labelHeightDiv.style.lineHeight = "normal"; + +document.body ? + document.body.appendChild(cc.LabelTTF.__labelHeightDiv) : + window.addEventListener('load', function () { + this.removeEventListener('load', arguments.callee, false); + document.body.appendChild(cc.LabelTTF.__labelHeightDiv); + }, false); + +/** + * Returns the height of text with an specified font family and font size, in + * device independent pixels. + * + * @param {string|cc.FontDefinition} fontName + * @param {number} fontSize + * @returns {number} + * @private + */ +cc.LabelTTF.__getFontHeightByDiv = function (fontName, fontSize) { + var clientHeight, labelDiv = cc.LabelTTF.__labelHeightDiv; + if(fontName instanceof cc.FontDefinition){ + /** @type cc.FontDefinition */ + var fontDef = fontName; + clientHeight = cc.LabelTTF.__fontHeightCache[fontDef._getCanvasFontStr()]; + if (clientHeight > 0) return clientHeight; + labelDiv.innerHTML = "ajghl~!"; + labelDiv.style.fontFamily = fontDef.fontName; + labelDiv.style.fontSize = fontDef.fontSize + "px"; + labelDiv.style.fontStyle = fontDef.fontStyle; + labelDiv.style.fontWeight = fontDef.fontWeight; + + clientHeight = labelDiv.clientHeight; + cc.LabelTTF.__fontHeightCache[fontDef._getCanvasFontStr()] = clientHeight; + labelDiv.innerHTML = ""; + } + else { + //Default + clientHeight = cc.LabelTTF.__fontHeightCache[fontName + "." + fontSize]; + if (clientHeight > 0) return clientHeight; + labelDiv.innerHTML = "ajghl~!"; + labelDiv.style.fontFamily = fontName; + labelDiv.style.fontSize = fontSize + "px"; + clientHeight = labelDiv.clientHeight; + cc.LabelTTF.__fontHeightCache[fontName + "." + fontSize] = clientHeight; + labelDiv.innerHTML = ""; + } + return clientHeight; + +}; + +cc.LabelTTF.__fontHeightCache = {}; diff --git a/frameworks/cocos2d-html5/cocos2d/core/labelttf/CCLabelTTFCanvasRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/core/labelttf/CCLabelTTFCanvasRenderCmd.js new file mode 100644 index 0000000..44c8f18 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/labelttf/CCLabelTTFCanvasRenderCmd.js @@ -0,0 +1,570 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + http://www.cocos2d-x.org + 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. + ****************************************************************************/ + +cc.LabelTTF._textAlign = ["left", "center", "right"]; +cc.LabelTTF._textBaseline = ["top", "middle", "bottom"]; + +//check the first character +cc.LabelTTF.wrapInspection = true; + +// These regular expressions consider a word any sequence of characters +// from these Unicode (sub)blocks: +// - Basic Latin (letters and numbers, plus the hypen-minus '-') +// - Latin-1 Supplement (accentuated letters and ¿¡ only) +// - Latin Extended-A (complete) +// - Latin Extended-B (complete) +// - IPA Extensions (complete) +// - Spacing Modifier Letters (complete) +// - Combining Diacritical Marks (Combining Grapheme Joiner excluded) +// - Greek and Coptic (complete, including reserved code points) +// - Cyrillic (complete) +// - Cyrillic Supplement (complete) +// - General Punctuation (Non-Breaking Hyphen* [U+2011] and quotation marks) +// * Note that Hyphen [U+2010] is considered a word boundary. +cc.LabelTTF._wordRex = /([a-zA-Z0-9\-¿¡«À-ÖØ-öø-ʯ\u0300-\u034e\u0350-\u036FͰ-ԯ\u2011‵-‷‹⁅]+|\S)/; +cc.LabelTTF._symbolRex = /^[!,.:;}\]%\?>、‘“》»?。,!\u2010′-‴›‼⁆⁇-⁉]/; +cc.LabelTTF._lastWordRex = /([a-zA-Z0-9\-¿¡«À-ÖØ-öø-ʯ\u0300-\u034e\u0350-\u036FͰ-ԯ\u2011‵-‷‹⁅]+|\S)$/; +cc.LabelTTF._lastEnglish = /[a-zA-Z0-9\-¿¡«À-ÖØ-öø-ʯ\u0300-\u034e\u0350-\u036FͰ-ԯ\u2011‵-‷‹⁅]+$/; +cc.LabelTTF._firsrEnglish = /^[a-zA-Z0-9\-¿¡«À-ÖØ-öø-ʯ\u0300-\u034e\u0350-\u036FͰ-ԯ\u2011‵-‷‹⁅]/; + +(function () { + cc.LabelTTF.RenderCmd = function () { + this._fontClientHeight = 18; + this._fontStyleStr = ""; + this._shadowColorStr = "rgba(128, 128, 128, 0.5)"; + this._strokeColorStr = ""; + this._fillColorStr = "rgba(255,255,255,1)"; + + this._labelCanvas = null; + this._labelContext = null; + this._lineWidths = []; + this._strings = []; + this._isMultiLine = false; + this._status = []; + this._renderingIndex = 0; + + this._canUseDirtyRegion = true; + }; + var proto = cc.LabelTTF.RenderCmd.prototype; + proto.constructor = cc.LabelTTF.RenderCmd; + proto._labelCmdCtor = cc.LabelTTF.RenderCmd; + + proto._setFontStyle = function (fontNameOrFontDef, fontSize, fontStyle, fontWeight) { + if (fontNameOrFontDef instanceof cc.FontDefinition) { + this._fontStyleStr = fontNameOrFontDef._getCanvasFontStr(); + this._fontClientHeight = cc.LabelTTF.__getFontHeightByDiv(fontNameOrFontDef); + } else { + var deviceFontSize = fontSize * cc.view.getDevicePixelRatio(); + this._fontStyleStr = fontStyle + " " + fontWeight + " " + deviceFontSize + "px '" + fontNameOrFontDef + "'"; + this._fontClientHeight = cc.LabelTTF.__getFontHeightByDiv(fontNameOrFontDef, fontSize); + } + }; + + proto._getFontStyle = function () { + return this._fontStyleStr; + }; + + proto._getFontClientHeight = function () { + return this._fontClientHeight; + }; + + proto._updateColor = function () { + this._setColorsString(); + this._updateTexture(); + }; + + proto._setColorsString = function () { + var locDisplayColor = this._displayedColor, node = this._node, + locShadowColor = node._shadowColor || this._displayedColor; + var locStrokeColor = node._strokeColor, locFontFillColor = node._textFillColor; + var dr = locDisplayColor.r / 255, dg = locDisplayColor.g / 255, db = locDisplayColor.b / 255; + + this._shadowColorStr = "rgba(" + (0 | (dr * locShadowColor.r)) + "," + (0 | ( dg * locShadowColor.g)) + "," + + (0 | (db * locShadowColor.b)) + "," + node._shadowOpacity + ")"; + this._fillColorStr = "rgba(" + (0 | (dr * locFontFillColor.r)) + "," + (0 | (dg * locFontFillColor.g)) + "," + + (0 | (db * locFontFillColor.b)) + ", 1)"; + this._strokeColorStr = "rgba(" + (0 | (dr * locStrokeColor.r)) + "," + (0 | (dg * locStrokeColor.g)) + "," + + (0 | (db * locStrokeColor.b)) + ", 1)"; + }; + + var localBB = new cc.Rect(); + proto.getLocalBB = function () { + var node = this._node; + localBB.x = localBB.y = 0; + var pixelRatio = cc.view.getDevicePixelRatio(); + localBB.width = node._getWidth() * pixelRatio; + localBB.height = node._getHeight() * pixelRatio; + return localBB; + }; + + proto._updateTTF = function () { + var node = this._node; + var pixelRatio = cc.view.getDevicePixelRatio(); + var locDimensionsWidth = node._dimensions.width * pixelRatio, i, strLength; + var locLineWidth = this._lineWidths; + locLineWidth.length = 0; + + this._isMultiLine = false; + this._measureConfig(); + var textWidthCache = {}; + if (locDimensionsWidth !== 0) { + // Content processing + this._strings = node._string.split('\n'); + + for (i = 0; i < this._strings.length; i++) { + this._checkWarp(this._strings, i, locDimensionsWidth); + } + } else { + this._strings = node._string.split('\n'); + for (i = 0, strLength = this._strings.length; i < strLength; i++) { + if(this._strings[i]) { + var measuredWidth = this._measure(this._strings[i]); + locLineWidth.push(measuredWidth); + textWidthCache[this._strings[i]] = measuredWidth; + } else { + locLineWidth.push(0); + } + } + } + + if (this._strings.length > 1) + this._isMultiLine = true; + + var locSize, locStrokeShadowOffsetX = 0, locStrokeShadowOffsetY = 0; + if (node._strokeEnabled) + locStrokeShadowOffsetX = locStrokeShadowOffsetY = node._strokeSize * 2; + if (node._shadowEnabled) { + var locOffsetSize = node._shadowOffset; + locStrokeShadowOffsetX += Math.abs(locOffsetSize.x) * 2; + locStrokeShadowOffsetY += Math.abs(locOffsetSize.y) * 2; + } + + //get offset for stroke and shadow + if (locDimensionsWidth === 0) { + if (this._isMultiLine) { + locSize = cc.size(Math.ceil(Math.max.apply(Math, locLineWidth) + locStrokeShadowOffsetX), + Math.ceil((this._fontClientHeight * pixelRatio * this._strings.length) + locStrokeShadowOffsetY)); + } + else { + var measuredWidth = textWidthCache[node._string]; + if(!measuredWidth && node._string) { + measuredWidth = this._measure(node._string); + } + locSize = cc.size(Math.ceil((measuredWidth ? measuredWidth : 0) + locStrokeShadowOffsetX), + Math.ceil(this._fontClientHeight * pixelRatio + locStrokeShadowOffsetY)); + } + } else { + if (node._dimensions.height === 0) { + if (this._isMultiLine) + locSize = cc.size( + Math.ceil(locDimensionsWidth + locStrokeShadowOffsetX), + Math.ceil((node.getLineHeight() * pixelRatio * this._strings.length) + locStrokeShadowOffsetY)); + else + locSize = cc.size( + Math.ceil(locDimensionsWidth + locStrokeShadowOffsetX), + Math.ceil(node.getLineHeight() * pixelRatio + locStrokeShadowOffsetY)); + } else { + //dimension is already set, contentSize must be same as dimension + locSize = cc.size( + Math.ceil(locDimensionsWidth + locStrokeShadowOffsetX), + Math.ceil(node._dimensions.height * pixelRatio + locStrokeShadowOffsetY)); + } + } + if (node._getFontStyle() !== "normal") { //add width for 'italic' and 'oblique' + locSize.width = Math.ceil(locSize.width + node._fontSize * 0.3); + } + node.setContentSize(locSize); + node._strokeShadowOffsetX = locStrokeShadowOffsetX; + node._strokeShadowOffsetY = locStrokeShadowOffsetY; + + // need computing _anchorPointInPoints + var locAP = node._anchorPoint; + this._anchorPointInPoints.x = (locStrokeShadowOffsetX * 0.5) + ((locSize.width - locStrokeShadowOffsetX) * locAP.x); + this._anchorPointInPoints.y = (locStrokeShadowOffsetY * 0.5) + ((locSize.height - locStrokeShadowOffsetY) * locAP.y); + }; + + proto._saveStatus = function () { + var node = this._node; + var scale = cc.view.getDevicePixelRatio(); + var locStrokeShadowOffsetX = node._strokeShadowOffsetX, locStrokeShadowOffsetY = node._strokeShadowOffsetY; + var locContentSizeHeight = node._contentSize.height - locStrokeShadowOffsetY, locVAlignment = node._vAlignment, + locHAlignment = node._hAlignment; + var dx = locStrokeShadowOffsetX * 0.5, + dy = locContentSizeHeight + locStrokeShadowOffsetY * 0.5; + var xOffset = 0, yOffset = 0, OffsetYArray = []; + var locContentWidth = node._contentSize.width - locStrokeShadowOffsetX; + + //lineHeight + var lineHeight = node.getLineHeight() * scale; + var transformTop = (lineHeight - this._fontClientHeight * scale) / 2; + + if (locHAlignment === cc.TEXT_ALIGNMENT_RIGHT) + xOffset += locContentWidth; + else if (locHAlignment === cc.TEXT_ALIGNMENT_CENTER) + xOffset += locContentWidth / 2; + else + xOffset += 0; + + if (this._isMultiLine) { + var locStrLen = this._strings.length; + if (locVAlignment === cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM) + yOffset = lineHeight - transformTop * 2 + locContentSizeHeight - lineHeight * locStrLen; + else if (locVAlignment === cc.VERTICAL_TEXT_ALIGNMENT_CENTER) + yOffset = (lineHeight - transformTop * 2) / 2 + (locContentSizeHeight - lineHeight * locStrLen) / 2; + + for (var i = 0; i < locStrLen; i++) { + var tmpOffsetY = -locContentSizeHeight + (lineHeight * i + transformTop) + yOffset; + OffsetYArray.push(tmpOffsetY); + } + } else { + if (locVAlignment === cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM) { + //do nothing + } else if (locVAlignment === cc.VERTICAL_TEXT_ALIGNMENT_TOP) { + yOffset -= locContentSizeHeight; + } else { + yOffset -= locContentSizeHeight * 0.5; + } + OffsetYArray.push(yOffset); + } + var tmpStatus = { + contextTransform: cc.p(dx, dy), + xOffset: xOffset, + OffsetYArray: OffsetYArray + }; + this._status.push(tmpStatus); + }; + + proto._drawTTFInCanvas = function (context) { + if (!context) + return; + var locStatus = this._status.pop(); + context.setTransform(1, 0, 0, 1, locStatus.contextTransform.x, locStatus.contextTransform.y); + var xOffset = locStatus.xOffset; + var yOffsetArray = locStatus.OffsetYArray; + this.drawLabels(context, xOffset, yOffsetArray); + }; + + proto._checkWarp = function (strArr, i, maxWidth) { + var text = strArr[i]; + var allWidth = this._measure(text); + if (allWidth > maxWidth && text.length > 1) { + + var fuzzyLen = text.length * ( maxWidth / allWidth ) | 0; + var tmpText = text.substr(fuzzyLen); + var width = allWidth - this._measure(tmpText); + var sLine; + var pushNum = 0; + + //Increased while cycle maximum ceiling. default 100 time + var checkWhile = 0; + + //Exceeded the size + while (width > maxWidth && checkWhile++ < 100) { + fuzzyLen *= maxWidth / width; + fuzzyLen = fuzzyLen | 0; + tmpText = text.substr(fuzzyLen); + width = allWidth - this._measure(tmpText); + } + + checkWhile = 0; + + //Find the truncation point + while (width < maxWidth && checkWhile++ < 100) { + if (tmpText) { + var exec = cc.LabelTTF._wordRex.exec(tmpText); + pushNum = exec ? exec[0].length : 1; + sLine = tmpText; + } + + fuzzyLen = fuzzyLen + pushNum; + tmpText = text.substr(fuzzyLen); + width = allWidth - this._measure(tmpText); + } + + fuzzyLen -= pushNum; + if (fuzzyLen === 0) { + fuzzyLen = 1; + sLine = sLine.substr(1); + } + + var sText = text.substr(0, fuzzyLen), result; + + //symbol in the first + if (cc.LabelTTF.wrapInspection) { + if (cc.LabelTTF._symbolRex.test(sLine || tmpText)) { + result = cc.LabelTTF._lastWordRex.exec(sText); + fuzzyLen -= result ? result[0].length : 0; + if (fuzzyLen === 0) fuzzyLen = 1; + + sLine = text.substr(fuzzyLen); + sText = text.substr(0, fuzzyLen); + } + } + + //To judge whether a English words are truncated + if (cc.LabelTTF._firsrEnglish.test(sLine)) { + result = cc.LabelTTF._lastEnglish.exec(sText); + if (result && sText !== result[0]) { + fuzzyLen -= result[0].length; + sLine = text.substr(fuzzyLen); + sText = text.substr(0, fuzzyLen); + } + } + + strArr[i] = sLine || tmpText; + strArr.splice(i, 0, sText); + } + }; + + proto.updateStatus = function () { + var flags = cc.Node._dirtyFlags, locFlag = this._dirtyFlag; + + if (locFlag & flags.textDirty) + this._updateTexture(); + + this.originUpdateStatus(); + }; + + proto._syncStatus = function (parentCmd) { + var flags = cc.Node._dirtyFlags, locFlag = this._dirtyFlag; + + if (locFlag & flags.textDirty) + this._updateTexture(); + + this._originSyncStatus(parentCmd); + + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL || locFlag & flags.transformDirty) + this.transform(parentCmd); + }; + + proto.drawLabels = function (context, xOffset, yOffsetArray) { + var node = this._node; + //shadow style setup + if (node._shadowEnabled) { + var locShadowOffset = node._shadowOffset; + context.shadowColor = this._shadowColorStr; + context.shadowOffsetX = locShadowOffset.x; + context.shadowOffsetY = -locShadowOffset.y; + context.shadowBlur = node._shadowBlur; + } + + var locHAlignment = node._hAlignment, + locVAlignment = node._vAlignment, + locStrokeSize = node._strokeSize; + + //this is fillText for canvas + if (context.font !== this._fontStyleStr) + context.font = this._fontStyleStr; + context.fillStyle = this._fillColorStr; + + //stroke style setup + var locStrokeEnabled = node._strokeEnabled; + if (locStrokeEnabled) { + context.lineWidth = locStrokeSize * 2; + context.strokeStyle = this._strokeColorStr; + } + + context.textBaseline = cc.LabelTTF._textBaseline[locVAlignment]; + context.textAlign = cc.LabelTTF._textAlign[locHAlignment]; + + var locStrLen = this._strings.length; + for (var i = 0; i < locStrLen; i++) { + var line = this._strings[i]; + if (locStrokeEnabled) { + context.lineJoin = 'round'; + context.strokeText(line, xOffset, yOffsetArray[i]); + } + context.fillText(line, xOffset, yOffsetArray[i]); + } + cc.g_NumberOfDraws++; + }; +})(); + +(function () { + cc.LabelTTF.CacheRenderCmd = function () { + this._labelCmdCtor(); + var locCanvas = this._labelCanvas = document.createElement("canvas"); + locCanvas.width = 1; + locCanvas.height = 1; + this._labelContext = locCanvas.getContext("2d"); + }; + + cc.LabelTTF.CacheRenderCmd.prototype = Object.create(cc.LabelTTF.RenderCmd.prototype); + cc.inject(cc.LabelTTF.RenderCmd.prototype, cc.LabelTTF.CacheRenderCmd.prototype); + + var proto = cc.LabelTTF.CacheRenderCmd.prototype; + proto.constructor = cc.LabelTTF.CacheRenderCmd; + proto._cacheCmdCtor = cc.LabelTTF.CacheRenderCmd; + + proto._updateTexture = function () { + this._dirtyFlag = this._dirtyFlag & cc.Node._dirtyFlags.textDirty ^ this._dirtyFlag; + var node = this._node; + node._needUpdateTexture = false; + var locContentSize = node._contentSize; + this._updateTTF(); + var width = locContentSize.width, height = locContentSize.height; + + var locContext = this._labelContext, locLabelCanvas = this._labelCanvas; + + if (!node._texture) { + var labelTexture = new cc.Texture2D(); + labelTexture.initWithElement(this._labelCanvas); + node.setTexture(labelTexture); + } + + if (node._string.length === 0) { + locLabelCanvas.width = 1; + locLabelCanvas.height = locContentSize.height || 1; + if (node._texture) { + node._texture._htmlElementObj = this._labelCanvas; + node._texture.handleLoadedTexture(); + } + node.setTextureRect(cc.rect(0, 0, 1, locContentSize.height)); + return true; + } + + //set size for labelCanvas + locContext.font = this._fontStyleStr; + + var flag = locLabelCanvas.width === width && locLabelCanvas.height === height; + locLabelCanvas.width = width; + locLabelCanvas.height = height; + if (flag) locContext.clearRect(0, 0, width, height); + this._saveStatus(); + this._drawTTFInCanvas(locContext); + if (node._texture) { + node._texture._htmlElementObj = this._labelCanvas; + node._texture.handleLoadedTexture(); + } + node.setTextureRect(cc.rect(0, 0, width, height)); + return true; + }; + + proto._measureConfig = function () { + this._labelContext.font = this._fontStyleStr; + }; + + proto._measure = function (text) { + if (text) { + return this._labelContext.measureText(text).width; + } else { + return 0; + } + }; + +})(); + +(function () { + cc.LabelTTF.CacheCanvasRenderCmd = function (renderable) { + this._spriteCmdCtor(renderable); + this._cacheCmdCtor(); + }; + + var proto = cc.LabelTTF.CacheCanvasRenderCmd.prototype = Object.create(cc.Sprite.CanvasRenderCmd.prototype); + cc.inject(cc.LabelTTF.CacheRenderCmd.prototype, proto); + proto.constructor = cc.LabelTTF.CacheCanvasRenderCmd; +})(); + +(function () { + cc.LabelTTF.CanvasRenderCmd = function (renderable) { + this._spriteCmdCtor(renderable); + this._labelCmdCtor(); + }; + + cc.LabelTTF.CanvasRenderCmd.prototype = Object.create(cc.Sprite.CanvasRenderCmd.prototype); + cc.inject(cc.LabelTTF.RenderCmd.prototype, cc.LabelTTF.CanvasRenderCmd.prototype); + + var proto = cc.LabelTTF.CanvasRenderCmd.prototype; + proto.constructor = cc.LabelTTF.CanvasRenderCmd; + + proto._measureConfig = function () { + }; + + proto._measure = function (text) { + if(text) { + var context = cc._renderContext.getContext(); + context.font = this._fontStyleStr; + return context.measureText(text).width; + } else { + return 0; + } + }; + + proto._updateTexture = function () { + this._dirtyFlag = this._dirtyFlag & cc.Node._dirtyFlags.textDirty ^ this._dirtyFlag; + var node = this._node; + var locContentSize = node._contentSize; + this._updateTTF(); + var width = locContentSize.width, height = locContentSize.height; + if (node._string.length === 0) { + node.setTextureRect(cc.rect(0, 0, 1, locContentSize.height)); + return true; + } + this._saveStatus(); + node.setTextureRect(cc.rect(0, 0, width, height)); + return true; + }; + + proto.rendering = function (ctx) { + var scaleX = cc.view.getScaleX(), + scaleY = cc.view.getScaleY(); + var wrapper = ctx || cc._renderContext, context = wrapper.getContext(); + if (!context) + return; + var node = this._node; + wrapper.computeRealOffsetY(); + if (this._status.length <= 0) + return; + var locIndex = (this._renderingIndex >= this._status.length) ? this._renderingIndex - this._status.length : this._renderingIndex; + var status = this._status[locIndex]; + this._renderingIndex = locIndex + 1; + + var locHeight = node._rect.height, + locX = node._offsetPosition.x, + locY = -node._offsetPosition.y - locHeight; + + var alpha = (this._displayedOpacity / 255); + + wrapper.setTransform(this._worldTransform, scaleX, scaleY); + wrapper.setCompositeOperation(this._blendFuncStr); + wrapper.setGlobalAlpha(alpha); + + wrapper.save(); + + if (node._flippedX) { + locX = -locX - node._rect.width; + context.scale(-1, 1); + } + if (node._flippedY) { + locY = node._offsetPosition.y; + context.scale(1, -1); + } + + var xOffset = status.xOffset + status.contextTransform.x + locX * scaleX; + var yOffsetArray = []; + + var locStrLen = this._strings.length; + for (var i = 0; i < locStrLen; i++) + yOffsetArray.push(status.OffsetYArray[i] + status.contextTransform.y + locY * scaleY); + + this.drawLabels(context, xOffset, yOffsetArray); + wrapper.restore(); + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/core/labelttf/CCLabelTTFWebGLRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/core/labelttf/CCLabelTTFWebGLRenderCmd.js new file mode 100644 index 0000000..e1e8411 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/labelttf/CCLabelTTFWebGLRenderCmd.js @@ -0,0 +1,37 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +// ----------------------------------- LabelTTF WebGL render cmd ---------------------------- +(function () { + cc.LabelTTF.WebGLRenderCmd = function (renderable) { + this._spriteCmdCtor(renderable); + this._cacheCmdCtor(); + }; + var proto = cc.LabelTTF.WebGLRenderCmd.prototype = Object.create(cc.Sprite.WebGLRenderCmd.prototype); + + cc.inject(cc.LabelTTF.CacheRenderCmd.prototype, proto); + proto.constructor = cc.LabelTTF.WebGLRenderCmd; + proto._updateColor = function () { + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/core/labelttf/LabelTTFPropertyDefine.js b/frameworks/cocos2d-html5/cocos2d/core/labelttf/LabelTTFPropertyDefine.js new file mode 100644 index 0000000..71ff3a9 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/labelttf/LabelTTFPropertyDefine.js @@ -0,0 +1,88 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + + +cc._tmp.PrototypeLabelTTF = function () { + var _p = cc.LabelTTF.prototype; + + // Override properties + cc.defineGetterSetter(_p, "color", _p.getColor, _p.setColor); + cc.defineGetterSetter(_p, "opacity", _p.getOpacity, _p.setOpacity); + + // Extended properties + /** @expose */ + _p.string; + cc.defineGetterSetter(_p, "string", _p.getString, _p.setString); + /** @expose */ + _p.textAlign; + cc.defineGetterSetter(_p, "textAlign", _p.getHorizontalAlignment, _p.setHorizontalAlignment); + /** @expose */ + _p.verticalAlign; + cc.defineGetterSetter(_p, "verticalAlign", _p.getVerticalAlignment, _p.setVerticalAlignment); + /** @expose */ + _p.fontSize; + cc.defineGetterSetter(_p, "fontSize", _p.getFontSize, _p.setFontSize); + /** @expose */ + _p.fontName; + cc.defineGetterSetter(_p, "fontName", _p.getFontName, _p.setFontName); + /** @expose */ + _p.font; + cc.defineGetterSetter(_p, "font", _p._getFont, _p._setFont); + /** @expose */ + _p.boundingSize; + //cc.defineGetterSetter(_p, "boundingSize", _p.getDimensions, _p.setDimensions); + /** @expose */ + _p.boundingWidth; + cc.defineGetterSetter(_p, "boundingWidth", _p._getBoundingWidth, _p._setBoundingWidth); + /** @expose */ + _p.boundingHeight; + cc.defineGetterSetter(_p, "boundingHeight", _p._getBoundingHeight, _p._setBoundingHeight); + /** @expose */ + _p.fillStyle; + cc.defineGetterSetter(_p, "fillStyle", _p._getFillStyle, _p.setFontFillColor); + /** @expose */ + _p.strokeStyle; + cc.defineGetterSetter(_p, "strokeStyle", _p._getStrokeStyle, _p._setStrokeStyle); + /** @expose */ + _p.lineWidth; + cc.defineGetterSetter(_p, "lineWidth", _p._getLineWidth, _p._setLineWidth); + /** @expose */ + _p.shadowOffset; + //cc.defineGetterSetter(_p, "shadowOffset", _p._getShadowOffset, _p._setShadowOffset); + /** @expose */ + _p.shadowOffsetX; + cc.defineGetterSetter(_p, "shadowOffsetX", _p._getShadowOffsetX, _p._setShadowOffsetX); + /** @expose */ + _p.shadowOffsetY; + cc.defineGetterSetter(_p, "shadowOffsetY", _p._getShadowOffsetY, _p._setShadowOffsetY); + /** @expose */ + _p.shadowOpacity; + cc.defineGetterSetter(_p, "shadowOpacity", _p._getShadowOpacity, _p._setShadowOpacity); + /** @expose */ + _p.shadowBlur; + cc.defineGetterSetter(_p, "shadowBlur", _p._getShadowBlur, _p._setShadowBlur); + +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/cocos2d/core/layers/CCLayer.js b/frameworks/cocos2d-html5/cocos2d/core/layers/CCLayer.js new file mode 100644 index 0000000..c752ad8 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/layers/CCLayer.js @@ -0,0 +1,782 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** cc.Layer is a subclass of cc.Node that implements the TouchEventsDelegate protocol.
+ * All features from cc.Node are valid, plus the bake feature: Baked layer can cache a static layer to improve performance + * @class + * @extends cc.Node + */ +cc.Layer = cc.Node.extend(/** @lends cc.Layer# */{ + _className: "Layer", + + /** + *

Constructor of cc.Layer, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.

+ */ + ctor: function () { + cc.Node.prototype.ctor.call(this); + this._ignoreAnchorPointForPosition = true; + this.setAnchorPoint(0.5, 0.5); + this.setContentSize(cc.winSize); + this._cascadeColorEnabled = false; + this._cascadeOpacityEnabled = false; + }, + + /** + * Sets the layer to cache all of children to a bake sprite, and draw itself by bake sprite. recommend using it in UI.
+ * This is useful only in html5 engine + * @function + * @see cc.Layer#unbake + */ + bake: function () { + this._renderCmd.bake(); + }, + + /** + * Cancel the layer to cache all of children to a bake sprite.
+ * This is useful only in html5 engine + * @function + * @see cc.Layer#bake + */ + unbake: function () { + this._renderCmd.unbake(); + }, + + /** + * Determines if the layer is baked. + * @function + * @returns {boolean} + * @see cc.Layer#bake and cc.Layer#unbake + */ + isBaked: function () { + return this._renderCmd._isBaked; + }, + + visit: function (parent) { + var cmd = this._renderCmd, parentCmd = parent ? parent._renderCmd : null; + + // quick return if not visible + if (!this._visible) { + cmd._propagateFlagsDown(parentCmd); + return; + } + + var renderer = cc.renderer; + cmd.visit(parentCmd); + + if (cmd._isBaked) { + renderer.pushRenderCommand(cmd); + cmd._bakeSprite.visit(this); + } + else { + var i, children = this._children, len = children.length, child; + if (len > 0) { + if (this._reorderChildDirty) { + this.sortAllChildren(); + } + // draw children zOrder < 0 + for (i = 0; i < len; i++) { + child = children[i]; + if (child._localZOrder < 0) { + child.visit(this); + } + else { + break; + } + } + + renderer.pushRenderCommand(cmd); + for (; i < len; i++) { + children[i].visit(this); + } + } else { + renderer.pushRenderCommand(cmd); + } + } + cmd._dirtyFlag = 0; + }, + + addChild: function (child, localZOrder, tag) { + cc.Node.prototype.addChild.call(this, child, localZOrder, tag); + this._renderCmd._bakeForAddChild(child); + }, + + _createRenderCmd: function () { + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return new cc.Layer.CanvasRenderCmd(this); + else + return new cc.Layer.WebGLRenderCmd(this); + } +}); + +/** + * Creates a layer + * @deprecated since v3.0, please use the new construction instead + * @see cc.Layer + * @return {cc.Layer|Null} + */ +cc.Layer.create = function () { + return new cc.Layer(); +}; + +/** + *

+ * CCLayerColor is a subclass of CCLayer that implements the CCRGBAProtocol protocol.
+ * All features from CCLayer are valid, plus the following new features:
+ * - opacity
+ * - RGB colors

+ * @class + * @extends cc.Layer + * + * @param {cc.Color} [color=] The color of the layer + * @param {Number} [width=] The width of the layer + * @param {Number} [height=] The height of the layer + * + * @example + * // Example + * //Create a yellow color layer as background + * var yellowBackground = new cc.LayerColor(cc.color(255,255,0,255)); + * //If you didn't pass in width and height, it defaults to the same size as the canvas + * + * //create a yellow box, 200 by 200 in size + * var yellowBox = new cc.LayerColor(cc.color(255,255,0,255), 200, 200); + */ +cc.LayerColor = cc.Layer.extend(/** @lends cc.LayerColor# */{ + _blendFunc: null, + _className: "LayerColor", + + /** + * Returns the blend function + * @return {cc.BlendFunc} + */ + getBlendFunc: function () { + return this._blendFunc; + }, + + /** + * Changes width and height + * @deprecated since v3.0 please use setContentSize instead + * @see cc.Node#setContentSize + * @param {Number} w width + * @param {Number} h height + */ + changeWidthAndHeight: function (w, h) { + this.width = w; + this.height = h; + }, + + /** + * Changes width in Points + * @deprecated since v3.0 please use setContentSize instead + * @see cc.Node#setContentSize + * @param {Number} w width + */ + changeWidth: function (w) { + this.width = w; + }, + + /** + * change height in Points + * @deprecated since v3.0 please use setContentSize instead + * @see cc.Node#setContentSize + * @param {Number} h height + */ + changeHeight: function (h) { + this.height = h; + }, + + setOpacityModifyRGB: function (value) { + }, + + isOpacityModifyRGB: function () { + return false; + }, + + /** + * Constructor of cc.LayerColor + * @function + * @param {cc.Color} [color=] + * @param {Number} [width=] + * @param {Number} [height=] + */ + ctor: function (color, width, height) { + cc.Layer.prototype.ctor.call(this); + this._blendFunc = cc.BlendFunc._alphaNonPremultiplied(); + cc.LayerColor.prototype.init.call(this, color, width, height); + }, + + /** + * Initialization of the layer, please do not call this function by yourself, you should pass the parameters to constructor to initialize a layer + * @param {cc.Color} [color=] + * @param {Number} [width=] + * @param {Number} [height=] + * @return {Boolean} + */ + init: function (color, width, height) { + var winSize = cc.director.getWinSize(); + color = color || cc.color(0, 0, 0, 255); + width = width === undefined ? winSize.width : width; + height = height === undefined ? winSize.height : height; + + var locRealColor = this._realColor; + locRealColor.r = color.r; + locRealColor.g = color.g; + locRealColor.b = color.b; + this._realOpacity = color.a; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.colorDirty | cc.Node._dirtyFlags.opacityDirty); + + cc.LayerColor.prototype.setContentSize.call(this, width, height); + return true; + }, + + visit: function (parent) { + var cmd = this._renderCmd, parentCmd = parent ? parent._renderCmd : null; + + // quick return if not visible + if (!this._visible) { + cmd._propagateFlagsDown(parentCmd); + return; + } + + var renderer = cc.renderer; + cmd.visit(parentCmd); + + if (cmd._isBaked) { + renderer.pushRenderCommand(cmd._bakeRenderCmd); + //the bakeSprite is drawing + cmd._bakeSprite._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + cmd._bakeSprite.visit(this); + } + else { + var i, children = this._children, len = children.length; + if (len > 0) { + if (this._reorderChildDirty) { + this.sortAllChildren(); + } + // draw children zOrder < 0 + for (i = 0; i < len; i++) { + child = children[i]; + if (child._localZOrder < 0) { + child.visit(this); + } + else { + break; + } + } + + renderer.pushRenderCommand(cmd); + for (; i < len; i++) { + children[i].visit(this); + } + } else { + renderer.pushRenderCommand(cmd); + } + } + + cmd._dirtyFlag = 0; + }, + + /** + * Sets the blend func, you can pass either a cc.BlendFunc object or source and destination value separately + * @param {Number|cc.BlendFunc} src + * @param {Number} [dst] + */ + setBlendFunc: function (src, dst) { + var locBlendFunc = this._blendFunc; + if (dst === undefined) { + locBlendFunc.src = src.src; + locBlendFunc.dst = src.dst; + } else { + locBlendFunc.src = src; + locBlendFunc.dst = dst; + } + this._renderCmd.updateBlendFunc(locBlendFunc); + }, + + _createRenderCmd: function () { + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return new cc.LayerColor.CanvasRenderCmd(this); + else + return new cc.LayerColor.WebGLRenderCmd(this); + } +}); + +/** + * Creates a cc.Layer with color, width and height in Points + * @deprecated since v3.0 please use the new construction instead + * @see cc.LayerColor + * @param {cc.Color} color + * @param {Number|Null} [width=] + * @param {Number|Null} [height=] + * @return {cc.LayerColor} + */ +cc.LayerColor.create = function (color, width, height) { + return new cc.LayerColor(color, width, height); +}; + +//LayerColor - Getter Setter +(function () { + var proto = cc.LayerColor.prototype; + cc.defineGetterSetter(proto, "width", proto._getWidth, proto._setWidth); + cc.defineGetterSetter(proto, "height", proto._getHeight, proto._setHeight); +})(); + +/** + *

+ * CCLayerGradient is a subclass of cc.LayerColor that draws gradients across the background.
+ *
+ * All features from cc.LayerColor are valid, plus the following new features:
+ *

  • direction
  • + *
  • final color
  • + *
  • interpolation mode
+ *
+ * Color is interpolated between the startColor and endColor along the given
+ * vector (starting at the origin, ending at the terminus). If no vector is
+ * supplied, it defaults to (0, -1) -- a fade from top to bottom.
+ *
+ * If 'compressedInterpolation' is disabled, you will not see either the start or end color for
+ * non-cardinal vectors; a smooth gradient implying both end points will be still
+ * be drawn, however.
+ *
+ * If 'compressedInterpolation' is enabled (default mode) you will see both the start and end colors of the gradient. + *

+ * @class + * @extends cc.LayerColor + * + * @param {cc.Color} start Starting color + * @param {cc.Color} end Ending color + * @param {cc.Point} [v=cc.p(0, -1)] A vector defines the gradient direction, default direction is from top to bottom + * + * @property {cc.Color} startColor - Start color of the color gradient + * @property {cc.Color} endColor - End color of the color gradient + * @property {Number} startOpacity - Start opacity of the color gradient + * @property {Number} endOpacity - End opacity of the color gradient + * @property {Number} vector - Direction vector of the color gradient + * @property {Number} compressedInterpolation - Indicate whether or not the interpolation will be compressed + */ +cc.LayerGradient = cc.LayerColor.extend(/** @lends cc.LayerGradient# */{ + _endColor: null, + _startOpacity: 255, + _endOpacity: 255, + _alongVector: null, + _compressedInterpolation: false, + _className: "LayerGradient", + _colorStops: [], + + /** + * Constructor of cc.LayerGradient + * @param {cc.Color} start + * @param {cc.Color} end + * @param {cc.Point} [v=cc.p(0, -1)] + * @param {Array|Null} stops + * + * @example Using ColorStops argument: + * //startColor & endColor are for default and backward compatibility + * var layerGradient = new cc.LayerGradient(cc.color.RED, new cc.Color(255,0,0,0), cc.p(0, -1), + * [{p:0, color: cc.color.RED}, + * {p:.5, color: new cc.Color(0,0,0,0)}, + * {p:1, color: cc.color.RED}]); + * //where p = A value between 0.0 and 1.0 that represents the position between start and end in a gradient + * + */ + ctor: function (start, end, v, stops) { + cc.LayerColor.prototype.ctor.call(this); + this._endColor = cc.color(0, 0, 0, 255); + this._alongVector = cc.p(0, -1); + this._startOpacity = 255; + this._endOpacity = 255; + + if (stops && stops instanceof Array) { + this._colorStops = stops; + stops.splice(0, 0, {p: 0, color: start || cc.color.BLACK}); + stops.push({p: 1, color: end || cc.color.BLACK}); + } else + this._colorStops = [{p: 0, color: start || cc.color.BLACK}, {p: 1, color: end || cc.color.BLACK}]; + + cc.LayerGradient.prototype.init.call(this, start, end, v, stops); + }, + + /** + * Initialization of the layer, please do not call this function by yourself, you should pass the parameters to constructor to initialize a layer + * @param {cc.Color} start starting color + * @param {cc.Color} end + * @param {cc.Point|Null} v + * @param {Array|Null} stops + * @return {Boolean} + */ + init: function (start, end, v, stops) { + start = start || cc.color(0, 0, 0, 255); + end = end || cc.color(0, 0, 0, 255); + v = v || cc.p(0, -1); + var _t = this; + + // Initializes the CCLayer with a gradient between start and end in the direction of v. + var locEndColor = _t._endColor; + _t._startOpacity = start.a; + + locEndColor.r = end.r; + locEndColor.g = end.g; + locEndColor.b = end.b; + _t._endOpacity = end.a; + + _t._alongVector = v; + _t._compressedInterpolation = true; + + cc.LayerColor.prototype.init.call(_t, cc.color(start.r, start.g, start.b, 255)); + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.colorDirty | cc.Node._dirtyFlags.opacityDirty | cc.Node._dirtyFlags.gradientDirty); + return true; + }, + + /** + * Sets the untransformed size of the LayerGradient. + * @param {cc.Size|Number} size The untransformed size of the LayerGradient or The untransformed size's width of the LayerGradient. + * @param {Number} [height] The untransformed size's height of the LayerGradient. + */ + setContentSize: function (size, height) { + cc.LayerColor.prototype.setContentSize.call(this, size, height); + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.gradientDirty); + }, + + _setWidth: function (width) { + cc.LayerColor.prototype._setWidth.call(this, width); + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.gradientDirty); + }, + _setHeight: function (height) { + cc.LayerColor.prototype._setHeight.call(this, height); + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.gradientDirty); + }, + + /** + * Returns the starting color + * @return {cc.Color} + */ + getStartColor: function () { + return cc.color(this._realColor); + }, + + /** + * Sets the starting color + * @param {cc.Color} color + * @example + * // Example + * myGradientLayer.setStartColor(cc.color(255,0,0)); + * //set the starting gradient to red + */ + setStartColor: function (color) { + this.color = color; + //update the color stops + var stops = this._colorStops; + if (stops && stops.length > 0) { + var selColor = stops[0].color; + selColor.r = color.r; + selColor.g = color.g; + selColor.b = color.b; + } + }, + + /** + * Sets the end gradient color + * @param {cc.Color} color + * @example + * // Example + * myGradientLayer.setEndColor(cc.color(255,0,0)); + * //set the ending gradient to red + */ + setEndColor: function (color) { + var locColor = this._endColor; + locColor.r = color.r; + locColor.g = color.g; + locColor.b = color.b; + //update the color stops + var stops = this._colorStops; + if (stops && stops.length > 0) { + var selColor = stops[stops.length - 1].color; + selColor.r = color.r; + selColor.g = color.g; + selColor.b = color.b; + } + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.colorDirty); + }, + + /** + * Returns the end color + * @return {cc.Color} + */ + getEndColor: function () { + return cc.color(this._endColor); + }, + + /** + * Sets starting gradient opacity + * @param {Number} o from 0 to 255, 0 is transparent + */ + setStartOpacity: function (o) { + this._startOpacity = o; + //update the color stops + var stops = this._colorStops; + if (stops && stops.length > 0) + stops[0].color.a = o; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.opacityDirty); + }, + + /** + * Returns the starting gradient opacity + * @return {Number} + */ + getStartOpacity: function () { + return this._startOpacity; + }, + + /** + * Sets the end gradient opacity + * @param {Number} o + */ + setEndOpacity: function (o) { + this._endOpacity = o; + var stops = this._colorStops; + if (stops && stops.length > 0) + stops[stops.length - 1].color.a = o; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.opacityDirty); + }, + + /** + * Returns the end gradient opacity + * @return {Number} + */ + getEndOpacity: function () { + return this._endOpacity; + }, + + /** + * Sets the direction vector of the gradient + * @param {cc.Point} Var + */ + setVector: function (Var) { + this._alongVector.x = Var.x; + this._alongVector.y = Var.y; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.gradientDirty); + }, + + /** + * Returns the direction vector of the gradient + * @return {cc.Point} + */ + getVector: function () { + return cc.p(this._alongVector.x, this._alongVector.y); + }, + + /** + * Returns whether compressed interpolation is enabled + * @return {Boolean} + */ + isCompressedInterpolation: function () { + return this._compressedInterpolation; + }, + + /** + * Sets whether compressed interpolation is enabled + * @param {Boolean} compress + */ + setCompressedInterpolation: function (compress) { + this._compressedInterpolation = compress; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.gradientDirty); + }, + + /** + * Return an array of Object representing a colorStop for the gradient, if no stops was specified + * start & endColor will be provided as default values + * @example + * [{p: 0, color: cc.color.RED},{p: 1, color: cc.color.RED},...] + * @returns {Array} + */ + getColorStops: function () { + return this._colorStops; + }, + /** + * Set the colorStops to create the gradient using multiple point & color + * + * @param colorStops + * + * @example + * //startColor & endColor are for default and backward compatibility + * var layerGradient = new cc.LayerGradient(cc.color.RED, new cc.Color(255,0,0,0), cc.p(0, -1)); + * layerGradient.setColorStops([{p:0, color: cc.color.RED}, + * {p:.5, color: new cc.Color(0,0,0,0)}, + * {p:1, color: cc.color.RED}]); + * //where p = A value between 0.0 and 1.0 that represents the position between start and end in a gradient + * + */ + setColorStops: function (colorStops) { + this._colorStops = colorStops; + //todo need update the start color and end color + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.colorDirty | cc.Node._dirtyFlags.opacityDirty | cc.Node._dirtyFlags.gradientDirty); + }, + + _createRenderCmd: function () { + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return new cc.LayerGradient.CanvasRenderCmd(this); + else + return new cc.LayerGradient.WebGLRenderCmd(this); + } +}); + +/** + * Creates a gradient layer + * @deprecated since v3.0, please use the new construction instead + * @see cc.layerGradient + * @param {cc.Color} start starting color + * @param {cc.Color} end ending color + * @param {cc.Point|Null} v + * @param {Array|NULL} stops + * @return {cc.LayerGradient} + */ +cc.LayerGradient.create = function (start, end, v, stops) { + return new cc.LayerGradient(start, end, v, stops); +}; +//LayerGradient - Getter Setter +(function () { + var proto = cc.LayerGradient.prototype; + // Extended properties + /** @expose */ + proto.startColor; + cc.defineGetterSetter(proto, "startColor", proto.getStartColor, proto.setStartColor); + /** @expose */ + proto.endColor; + cc.defineGetterSetter(proto, "endColor", proto.getEndColor, proto.setEndColor); + /** @expose */ + proto.startOpacity; + cc.defineGetterSetter(proto, "startOpacity", proto.getStartOpacity, proto.setStartOpacity); + /** @expose */ + proto.endOpacity; + cc.defineGetterSetter(proto, "endOpacity", proto.getEndOpacity, proto.setEndOpacity); + /** @expose */ + proto.vector; + cc.defineGetterSetter(proto, "vector", proto.getVector, proto.setVector); + /** @expose */ + proto.colorStops; + cc.defineGetterSetter(proto, "colorStops", proto.getColorStops, proto.setColorStops); +})(); + +/** + * CCMultipleLayer is a CCLayer with the ability to multiplex it's children.
+ * Features:
+ *
  • - It supports one or more children
  • + *
  • - Only one children will be active a time
+ * @class + * @extends cc.Layer + * @param {Array} layers an array of cc.Layer + * @example + * // Example + * var multiLayer = new cc.LayerMultiple(layer1, layer2, layer3);//any number of layers + */ +cc.LayerMultiplex = cc.Layer.extend(/** @lends cc.LayerMultiplex# */{ + _enabledLayer: 0, + _layers: null, + _className: "LayerMultiplex", + + /** + * Constructor of cc.LayerMultiplex + * @param {Array} layers an array of cc.Layer + */ + ctor: function (layers) { + cc.Layer.prototype.ctor.call(this); + if (layers instanceof Array) + cc.LayerMultiplex.prototype.initWithLayers.call(this, layers); + else + cc.LayerMultiplex.prototype.initWithLayers.call(this, Array.prototype.slice.call(arguments)); + }, + + /** + * Initialization of the layer multiplex, please do not call this function by yourself, you should pass the parameters to constructor to initialize a layer multiplex + * @param {Array} layers an array of cc.Layer + * @return {Boolean} + */ + initWithLayers: function (layers) { + if ((layers.length > 0) && (layers[layers.length - 1] == null)) + cc.log(cc._LogInfos.LayerMultiplex_initWithLayers); + + this._layers = layers; + this._enabledLayer = 0; + this.addChild(this._layers[this._enabledLayer]); + return true; + }, + + /** + * Switches to a certain layer indexed by n.
+ * The current (old) layer will be removed from it's parent with 'cleanup:YES'. + * @param {Number} n the layer index to switch to + */ + switchTo: function (n) { + if (n >= this._layers.length) { + cc.log(cc._LogInfos.LayerMultiplex_switchTo); + return; + } + + this.removeChild(this._layers[this._enabledLayer], true); + this._enabledLayer = n; + this.addChild(this._layers[n]); + }, + + /** + * Release the current layer and switches to another layer indexed by n.
+ * The current (old) layer will be removed from it's parent with 'cleanup:YES'. + * @param {Number} n the layer index to switch to + */ + switchToAndReleaseMe: function (n) { + if (n >= this._layers.length) { + cc.log(cc._LogInfos.LayerMultiplex_switchToAndReleaseMe); + return; + } + + this.removeChild(this._layers[this._enabledLayer], true); + + //[layers replaceObjectAtIndex:_enabledLayer withObject:[NSNull null]]; + this._layers[this._enabledLayer] = null; + this._enabledLayer = n; + this.addChild(this._layers[n]); + }, + + /** + * Add a layer to the multiplex layers list + * @param {cc.Layer} layer + */ + addLayer: function (layer) { + if (!layer) { + cc.log(cc._LogInfos.LayerMultiplex_addLayer); + return; + } + this._layers.push(layer); + } +}); + +/** + * Creates a cc.LayerMultiplex with one or more layers using a variable argument list. + * @deprecated since v3.0, please use new construction instead + * @see cc.LayerMultiplex + * @return {cc.LayerMultiplex|Null} + */ +cc.LayerMultiplex.create = function (/*Multiple Arguments*/) { + return new cc.LayerMultiplex(Array.prototype.slice.call(arguments)); +}; diff --git a/frameworks/cocos2d-html5/cocos2d/core/layers/CCLayerCanvasRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/core/layers/CCLayerCanvasRenderCmd.js new file mode 100644 index 0000000..3149895 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/layers/CCLayerCanvasRenderCmd.js @@ -0,0 +1,419 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +//-----------------------// +// 1. cc.Layer // +// 2. cc.LayerColor // +// 3. cc.LayerGradient // +//-----------------------// + +/** + * cc.Layer's rendering objects of Canvas + */ +(function () { + //Layer's canvas render command + cc.Layer.CanvasRenderCmd = function (renderable) { + this._rootCtor(renderable); + this._isBaked = false; + this._bakeSprite = null; + this._canUseDirtyRegion = true; + this._updateCache = 2; // 2: Updated child visit 1: Rendering 0: Nothing to do + }; + + var proto = cc.Layer.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + proto.constructor = cc.Layer.CanvasRenderCmd; + proto._layerCmdCtor = cc.Layer.CanvasRenderCmd; + + proto._setCacheDirty = function (child) { + if (child && this._updateCache === 0) + this._updateCache = 2; + if (this._cacheDirty === false) { + this._cacheDirty = true; + var cachedP = this._cachedParent; + cachedP && cachedP !== this && cachedP._setNodeDirtyForCache && cachedP._setNodeDirtyForCache(); + } + }; + + proto.updateStatus = function () { + var flags = cc.Node._dirtyFlags, locFlag = this._dirtyFlag; + if (locFlag & flags.orderDirty) { + this._cacheDirty = true; + if (this._updateCache === 0) + this._updateCache = 2; + this._dirtyFlag &= ~flags.orderDirty; + } + + this.originUpdateStatus(); + }; + + proto._syncStatus = function (parentCmd) { + var flags = cc.Node._dirtyFlags, locFlag = this._dirtyFlag; + // if (locFlag & flags.orderDirty) { + if (this._isBaked || locFlag & flags.orderDirty) { + this._cacheDirty = true; + if (this._updateCache === 0) + this._updateCache = 2; + this._dirtyFlag &= ~flags.orderDirty; + } + this._originSyncStatus(parentCmd); + }; + + proto.transform = function (parentCmd, recursive) { + if (!this._worldTransform) { + this._worldTransform = {a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0}; + } + var wt = this._worldTransform; + var a = wt.a, b = wt.b, c = wt.c, d = wt.d, tx = wt.tx, ty = wt.ty; + this.originTransform(parentCmd, recursive); + if (( wt.a !== a || wt.b !== b || wt.c !== c || wt.d !== d ) && this._updateCache === 0) + this._updateCache = 2; + }; + + proto.bake = function () { + if (!this._isBaked) { + this._needDraw = true; + cc.renderer.childrenOrderDirty = true; + //limit: 1. its children's blendfunc are invalid. + this._isBaked = this._cacheDirty = true; + if (this._updateCache === 0) + this._updateCache = 2; + + var children = this._node._children; + for (var i = 0, len = children.length; i < len; i++) + children[i]._renderCmd._setCachedParent(this); + + if (!this._bakeSprite) { + this._bakeSprite = new cc.BakeSprite(); + this._bakeSprite.setAnchorPoint(0, 0); + } + } + }; + + proto.unbake = function () { + if (this._isBaked) { + cc.renderer.childrenOrderDirty = true; + this._needDraw = false; + this._isBaked = false; + this._cacheDirty = true; + if (this._updateCache === 0) + this._updateCache = 2; + + var children = this._node._children; + for (var i = 0, len = children.length; i < len; i++) + children[i]._renderCmd._setCachedParent(null); + } + }; + + proto.isBaked = function () { + return this._isBaked; + }; + + proto.rendering = function () { + if (this._cacheDirty) { + var node = this._node; + var children = node._children, locBakeSprite = this._bakeSprite; + + //compute the bounding box of the bake layer. + this.transform(this.getParentRenderCmd(), true); + + var boundingBox = this._getBoundingBoxForBake(); + boundingBox.width = 0 | (boundingBox.width + 0.5); + boundingBox.height = 0 | (boundingBox.height + 0.5); + + var bakeContext = locBakeSprite.getCacheContext(); + var ctx = bakeContext.getContext(); + + locBakeSprite.setPosition(boundingBox.x, boundingBox.y); + + if (this._updateCache > 0) { + locBakeSprite.resetCanvasSize(boundingBox.width, boundingBox.height); + bakeContext.setOffset(0 - boundingBox.x, ctx.canvas.height - boundingBox.height + boundingBox.y); + //visit for canvas + node.sortAllChildren(); + cc.renderer._turnToCacheMode(this.__instanceId); + for (var i = 0, len = children.length; i < len; i++) { + children[i].visit(this); + } + cc.renderer._renderingToCacheCanvas(bakeContext, this.__instanceId); + locBakeSprite.transform(); //because bake sprite's position was changed at rendering. + this._updateCache--; + } + + this._cacheDirty = false; + } + }; + + proto._bakeForAddChild = function (child) { + if (child._parent === this._node && this._isBaked) + child._renderCmd._setCachedParent(this); + }; + + proto._getBoundingBoxForBake = function () { + var rect = null, node = this._node; + + //query child's BoundingBox + if (!node._children || node._children.length === 0) + return cc.rect(0, 0, 10, 10); + var trans = node.getNodeToWorldTransform(); + + var locChildren = node._children; + for (var i = 0, len = locChildren.length; i < len; i++) { + var child = locChildren[i]; + if (child && child._visible) { + if (rect) { + var childRect = child._getBoundingBoxToCurrentNode(trans); + if (childRect) + rect = cc.rectUnion(rect, childRect); + } else { + rect = child._getBoundingBoxToCurrentNode(trans); + } + } + } + return rect; + }; +})(); + +/** + * cc.LayerColor's rendering objects of Canvas + */ +(function () { + //LayerColor's canvas render command + cc.LayerColor.CanvasRenderCmd = function (renderable) { + this._layerCmdCtor(renderable); + this._needDraw = true; + this._blendFuncStr = "source-over"; + this._bakeRenderCmd = new cc.CustomRenderCmd(this, this._bakeRendering); + }; + var proto = cc.LayerColor.CanvasRenderCmd.prototype = Object.create(cc.Layer.CanvasRenderCmd.prototype); + proto.constructor = cc.LayerColor.CanvasRenderCmd; + + proto.unbake = function () { + cc.Layer.CanvasRenderCmd.prototype.unbake.call(this); + this._needDraw = true; + }; + + proto.rendering = function (ctx, scaleX, scaleY) { + var wrapper = ctx || cc._renderContext, context = wrapper.getContext(), + node = this._node, + curColor = this._displayedColor, + opacity = this._displayedOpacity / 255, + locWidth = node._contentSize.width, + locHeight = node._contentSize.height; + + if (opacity === 0) + return; + + wrapper.setCompositeOperation(this._blendFuncStr); + wrapper.setGlobalAlpha(opacity); + wrapper.setFillStyle("rgba(" + (0 | curColor.r) + "," + (0 | curColor.g) + "," + + (0 | curColor.b) + ", 1)"); //TODO: need cache the color string + + wrapper.setTransform(this._worldTransform, scaleX, scaleY); + context.fillRect(0, 0, locWidth, -locHeight); + + cc.g_NumberOfDraws++; + }; + + proto.updateBlendFunc = function (blendFunc) { + this._blendFuncStr = cc.Node.CanvasRenderCmd._getCompositeOperationByBlendFunc(blendFunc); + }; + + proto._updateSquareVertices = + proto._updateSquareVerticesWidth = + proto._updateSquareVerticesHeight = function () {}; + + proto._bakeRendering = function () { + if (this._cacheDirty) { + var node = this._node; + var locBakeSprite = this._bakeSprite, children = node._children; + var i, len = children.length; + + //compute the bounding box of the bake layer. + this.transform(this.getParentRenderCmd(), true); + //compute the bounding box of the bake layer. + var boundingBox = this._getBoundingBoxForBake(); + boundingBox.width = 0 | (boundingBox.width + 0.5); + boundingBox.height = 0 | (boundingBox.height + 0.5); + + var bakeContext = locBakeSprite.getCacheContext(); + var ctx = bakeContext.getContext(); + + locBakeSprite.setPosition(boundingBox.x, boundingBox.y); + + if (this._updateCache > 0) { + ctx.fillStyle = bakeContext._currentFillStyle; + locBakeSprite.resetCanvasSize(boundingBox.width, boundingBox.height); + bakeContext.setOffset(0 - boundingBox.x, ctx.canvas.height - boundingBox.height + boundingBox.y); + + var child; + cc.renderer._turnToCacheMode(this.__instanceId); + //visit for canvas + if (len > 0) { + node.sortAllChildren(); + // draw children zOrder < 0 + for (i = 0; i < len; i++) { + child = children[i]; + if (child._localZOrder < 0) + child.visit(node); + else + break; + } + cc.renderer.pushRenderCommand(this); + for (; i < len; i++) { + children[i].visit(node); + } + } else + cc.renderer.pushRenderCommand(this); + cc.renderer._renderingToCacheCanvas(bakeContext, this.__instanceId); + locBakeSprite.transform(); + this._updateCache--; + } + this._cacheDirty = false; + } + }; + + proto._getBoundingBoxForBake = function () { + var node = this._node; + //default size + var rect = cc.rect(0, 0, node._contentSize.width, node._contentSize.height); + var trans = node.getNodeToWorldTransform(); + rect = cc.rectApplyAffineTransform(rect, node.getNodeToWorldTransform()); + + //query child's BoundingBox + if (!node._children || node._children.length === 0) + return rect; + + var locChildren = node._children; + for (var i = 0; i < locChildren.length; i++) { + var child = locChildren[i]; + if (child && child._visible) { + var childRect = child._getBoundingBoxToCurrentNode(trans); + rect = cc.rectUnion(rect, childRect); + } + } + return rect; + }; +})(); + +/** + * cc.LayerGradient's rendering objects of Canvas + */ +(function () { + cc.LayerGradient.CanvasRenderCmd = function (renderable) { + cc.LayerColor.CanvasRenderCmd.call(this, renderable); + this._needDraw = true; + this._startPoint = cc.p(0, 0); + this._endPoint = cc.p(0, 0); + this._startStopStr = null; + this._endStopStr = null; + }; + var proto = cc.LayerGradient.CanvasRenderCmd.prototype = Object.create(cc.LayerColor.CanvasRenderCmd.prototype); + proto.constructor = cc.LayerGradient.CanvasRenderCmd; + + proto.rendering = function (ctx, scaleX, scaleY) { + var wrapper = ctx || cc._renderContext, context = wrapper.getContext(), + node = this._node, + opacity = this._displayedOpacity / 255; + + if (opacity === 0) + return; + + var locWidth = node._contentSize.width, locHeight = node._contentSize.height; + wrapper.setCompositeOperation(this._blendFuncStr); + wrapper.setGlobalAlpha(opacity); + var gradient = context.createLinearGradient(this._startPoint.x, this._startPoint.y, this._endPoint.x, this._endPoint.y); + + if (node._colorStops) { //Should always fall here now + for (var i = 0; i < node._colorStops.length; i++) { + var stop = node._colorStops[i]; + gradient.addColorStop(stop.p, this._colorStopsStr[i]); + } + } else { + gradient.addColorStop(0, this._startStopStr); + gradient.addColorStop(1, this._endStopStr); + } + + wrapper.setFillStyle(gradient); + + wrapper.setTransform(this._worldTransform, scaleX, scaleY); + context.fillRect(0, 0, locWidth, -locHeight); + cc.g_NumberOfDraws++; + }; + + proto.updateStatus = function () { + var flags = cc.Node._dirtyFlags, locFlag = this._dirtyFlag; + if (locFlag & flags.gradientDirty) { + this._dirtyFlag |= flags.colorDirty; + this._dirtyFlag &= ~flags.gradientDirty; + } + + this.originUpdateStatus(); + }; + + proto._syncStatus = function (parentCmd) { + var flags = cc.Node._dirtyFlags, locFlag = this._dirtyFlag; + if (locFlag & flags.gradientDirty) { + this._dirtyFlag |= flags.colorDirty; + this._dirtyFlag &= ~flags.gradientDirty; + } + + this._originSyncStatus(parentCmd); + }; + + proto._updateColor = function () { + var node = this._node; + var contentSize = node._contentSize; + var tWidth = contentSize.width * 0.5, tHeight = contentSize.height * 0.5; + + //fix the bug of gradient layer + var angle = cc.pAngleSigned(cc.p(0, -1), node._alongVector); + var p1 = cc.pRotateByAngle(cc.p(0, -1), cc.p(0, 0), angle); + var factor = Math.min(Math.abs(1 / p1.x), Math.abs(1 / p1.y)); + + this._startPoint.x = tWidth * (-p1.x * factor) + tWidth; + this._startPoint.y = tHeight * (p1.y * factor) - tHeight; + this._endPoint.x = tWidth * (p1.x * factor) + tWidth; + this._endPoint.y = tHeight * (-p1.y * factor) - tHeight; + + var locStartColor = this._displayedColor, locEndColor = node._endColor; + var startOpacity = node._startOpacity / 255, endOpacity = node._endOpacity / 255; + this._startStopStr = "rgba(" + Math.round(locStartColor.r) + "," + Math.round(locStartColor.g) + "," + + Math.round(locStartColor.b) + "," + startOpacity.toFixed(4) + ")"; + this._endStopStr = "rgba(" + Math.round(locEndColor.r) + "," + Math.round(locEndColor.g) + "," + + Math.round(locEndColor.b) + "," + endOpacity.toFixed(4) + ")"; + + if (node._colorStops) { + this._startOpacity = 0; + this._endOpacity = 0; + + this._colorStopsStr = []; + for (var i = 0; i < node._colorStops.length; i++) { + var stopColor = node._colorStops[i].color; + var stopOpacity = stopColor.a == null ? 1 : stopColor.a / 255; + this._colorStopsStr.push("rgba(" + Math.round(stopColor.r) + "," + Math.round(stopColor.g) + "," + + Math.round(stopColor.b) + "," + stopOpacity.toFixed(4) + ")"); + } + } + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/core/layers/CCLayerWebGLRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/core/layers/CCLayerWebGLRenderCmd.js new file mode 100644 index 0000000..4acf7f2 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/layers/CCLayerWebGLRenderCmd.js @@ -0,0 +1,328 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +//-----------------------// +// 1. cc.Layer // +// 2. cc.LayerColor // +// 3. cc.LayerGradient // +//-----------------------// + +/** + * cc.Layer's rendering objects of WebGL + */ +(function () { + cc.Layer.WebGLRenderCmd = function (renderable) { + this._rootCtor(renderable); + this._isBaked = false; + }; + + var proto = cc.Layer.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + proto.constructor = cc.Layer.WebGLRenderCmd; + proto._layerCmdCtor = cc.Layer.WebGLRenderCmd; + + proto.bake = function () { + }; + + proto.unbake = function () { + }; + + proto._bakeForAddChild = function () { + }; +})(); + +/** + * cc.LayerColor's rendering objects of WebGL + */ +(function () { + var FLOAT_PER_VERTEX = 4; + + cc.LayerColor.WebGLRenderCmd = function (renderable) { + this._layerCmdCtor(renderable); + this._needDraw = true; + + this._matrix = null; + + this.initData(4); + this._color = new Uint32Array(1); + this._vertexBuffer = null; + + this._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_COLOR); + }; + var proto = cc.LayerColor.WebGLRenderCmd.prototype = Object.create(cc.Layer.WebGLRenderCmd.prototype); + proto.constructor = cc.LayerColor.WebGLRenderCmd; + + proto.initData = function (vertexCount) { + this._data = new ArrayBuffer(16 * vertexCount); + this._positionView = new Float32Array(this._data); + this._colorView = new Uint32Array(this._data); + this._dataDirty = true; + }; + + proto.transform = function (parentCmd, recursive) { + this.originTransform(parentCmd, recursive); + + var node = this._node, + width = node._contentSize.width, + height = node._contentSize.height; + + var pos = this._positionView; + pos[FLOAT_PER_VERTEX] = width; // br.x + pos[FLOAT_PER_VERTEX * 2 + 1] = height; // tl.y + pos[FLOAT_PER_VERTEX * 3] = width; // tr.x + pos[FLOAT_PER_VERTEX * 3 + 1] = height; // tr.y + pos[2].z = + pos[FLOAT_PER_VERTEX + 2] = + pos[FLOAT_PER_VERTEX * 2 + 2] = + pos[FLOAT_PER_VERTEX * 3 + 2] = node._vertexZ; + + this._dataDirty = true; + }; + + proto._updateColor = function () { + var color = this._displayedColor; + this._color[0] = ((this._displayedOpacity << 24) | (color.b << 16) | (color.g << 8) | color.r); + + var colors = this._colorView; + for (var i = 0; i < 4; i++) { + colors[i * FLOAT_PER_VERTEX + 3] = this._color[0]; + } + this._dataDirty = true; + }; + + proto.rendering = function (ctx) { + var gl = ctx || cc._renderContext; + var node = this._node; + + if (!this._matrix) { + this._matrix = new cc.math.Matrix4(); + this._matrix.identity(); + } + + var wt = this._worldTransform; + this._matrix.mat[0] = wt.a; + this._matrix.mat[4] = wt.c; + this._matrix.mat[12] = wt.tx; + this._matrix.mat[1] = wt.b; + this._matrix.mat[5] = wt.d; + this._matrix.mat[13] = wt.ty; + + if (this._dataDirty) { + if (!this._vertexBuffer) { + this._vertexBuffer = gl.createBuffer(); + } + gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this._data, gl.DYNAMIC_DRAW); + this._dataDirty = false; + } + + this._glProgramState.apply(this._matrix); + cc.glBlendFunc(node._blendFunc.src, node._blendFunc.dst); + + gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer); + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_POSITION); + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_COLOR); + + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 3, gl.FLOAT, false, 16, 0); + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, gl.UNSIGNED_BYTE, true, 16, 12); + + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + }; + + proto.updateBlendFunc = function (blendFunc) { + }; +})(); + +/** + * cc.LayerGradient's rendering objects of WebGL + */ +(function () { + var FLOAT_PER_VERTEX = 4; + + cc.LayerGradient.WebGLRenderCmd = function (renderable) { + cc.LayerColor.WebGLRenderCmd.call(this, renderable); + this._needDraw = true; + this._clipRect = new cc.Rect(); + this._clippingRectDirty = false; + }; + var proto = cc.LayerGradient.WebGLRenderCmd.prototype = Object.create(cc.LayerColor.WebGLRenderCmd.prototype); + proto.constructor = cc.LayerGradient.WebGLRenderCmd; + + proto.updateStatus = function () { + var flags = cc.Node._dirtyFlags, locFlag = this._dirtyFlag; + if (locFlag & flags.gradientDirty) { + this._dirtyFlag |= flags.colorDirty; + this._updateVertex(); + this._dirtyFlag &= ~flags.gradientDirty; + } + + this.originUpdateStatus(); + }; + + proto._syncStatus = function (parentCmd) { + var flags = cc.Node._dirtyFlags, locFlag = this._dirtyFlag; + if (locFlag & flags.gradientDirty) { + this._dirtyFlag |= flags.colorDirty; + this._updateVertex(); + this._dirtyFlag &= ~flags.gradientDirty; + } + + this._originSyncStatus(parentCmd); + }; + + proto.transform = function (parentCmd, recursive) { + this.originTransform(parentCmd, recursive); + this._updateVertex(); + }; + + proto._updateVertex = function () { + var node = this._node, stops = node._colorStops; + if (!stops || stops.length < 2) + return; + + this._clippingRectDirty = true; + var i, stopsLen = stops.length, verticesLen = stopsLen * 2, contentSize = node._contentSize; + if (this._positionView.length / FLOAT_PER_VERTEX < verticesLen) { + this.initData(verticesLen); + } + + //init vertex + var angle = Math.PI + cc.pAngleSigned(cc.p(0, -1), node._alongVector), locAnchor = cc.p(contentSize.width / 2, contentSize.height / 2); + var degrees = Math.round(cc.radiansToDegrees(angle)); + var transMat = cc.affineTransformMake(1, 0, 0, 1, locAnchor.x, locAnchor.y); + transMat = cc.affineTransformRotate(transMat, angle); + var a, b; + if (degrees < 90) { + a = cc.p(-locAnchor.x, locAnchor.y); + b = cc.p(locAnchor.x, locAnchor.y); + } else if (degrees < 180) { + a = cc.p(locAnchor.x, locAnchor.y); + b = cc.p(locAnchor.x, -locAnchor.y); + } else if (degrees < 270) { + a = cc.p(locAnchor.x, -locAnchor.y); + b = cc.p(-locAnchor.x, -locAnchor.y); + } else { + a = cc.p(-locAnchor.x, -locAnchor.y); + b = cc.p(-locAnchor.x, locAnchor.y); + } + + var sin = Math.sin(angle), cos = Math.cos(angle); + var tx = Math.abs((a.x * cos - a.y * sin) / locAnchor.x), ty = Math.abs((b.x * sin + b.y * cos) / locAnchor.y); + transMat = cc.affineTransformScale(transMat, tx, ty); + var pos = this._positionView; + for (i = 0; i < stopsLen; i++) { + var stop = stops[i], y = stop.p * contentSize.height; + var p0 = cc.pointApplyAffineTransform(-locAnchor.x, y - locAnchor.y, transMat); + var offset = i * 2 * FLOAT_PER_VERTEX; + pos[offset] = p0.x; + pos[offset + 1] = p0.y; + pos[offset + 2] = node._vertexZ; + var p1 = cc.pointApplyAffineTransform(contentSize.width - locAnchor.x, y - locAnchor.y, transMat); + offset += FLOAT_PER_VERTEX; + pos[offset] = p1.x; + pos[offset + 1] = p1.y; + pos[offset + 2] = node._vertexZ; + } + + this._dataDirty = true; + }; + + proto._updateColor = function () { + var node = this._node, stops = node._colorStops; + if (!stops || stops.length < 2) + return; + + var stopsLen = stops.length, + stopColor, + offset, + colors = this._colorView, + opacityf = this._displayedOpacity / 255; + for (i = 0; i < stopsLen; i++) { + stopColor = stops[i].color; + this._color[0] = ((stopColor.a*opacityf) << 24) | (stopColor.b << 16) | (stopColor.g << 8) | stopColor.r; + + offset = i * 2 * FLOAT_PER_VERTEX; + colors[offset + 3] = this._color[0]; + offset += FLOAT_PER_VERTEX; + colors[offset + 3] = this._color[0]; + } + this._dataDirty = true; + }; + + proto.rendering = function (ctx) { + var context = ctx || cc._renderContext, node = this._node; + + if (!this._matrix) { + this._matrix = new cc.math.Matrix4(); + this._matrix.identity(); + } + + //it is too expensive to use stencil to clip, so it use Scissor, + //but it has a bug when layer rotated and layer's content size less than canvas's size. + var clippingRect = this._getClippingRect(); + context.enable(context.SCISSOR_TEST); + cc.view.setScissorInPoints(clippingRect.x, clippingRect.y, clippingRect.width, clippingRect.height); + + var wt = this._worldTransform; + this._matrix.mat[0] = wt.a; + this._matrix.mat[4] = wt.c; + this._matrix.mat[12] = wt.tx; + this._matrix.mat[1] = wt.b; + this._matrix.mat[5] = wt.d; + this._matrix.mat[13] = wt.ty; + + if (this._dataDirty) { + if (!this._vertexBuffer) { + this._vertexBuffer = gl.createBuffer(); + } + gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this._data, gl.DYNAMIC_DRAW); + this._dataDirty = false; + } + + //draw gradient layer + this._glProgramState.apply(this._matrix); + cc.glBlendFunc(node._blendFunc.src, node._blendFunc.dst); + + gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer); + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_POSITION); + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_COLOR); + + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 3, gl.FLOAT, false, 16, 0); + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, gl.UNSIGNED_BYTE, true, 16, 12); + + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + + context.disable(context.SCISSOR_TEST); + }; + + proto._getClippingRect = function () { + if (this._clippingRectDirty) { + var node = this._node; + var rect = cc.rect(0, 0, node._contentSize.width, node._contentSize.height); + var trans = node.getNodeToWorldTransform(); + this._clipRect = cc._rectApplyAffineTransformIn(rect, trans); + } + return this._clipRect; + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/core/platform/CCClass.js b/frameworks/cocos2d-html5/cocos2d/core/platform/CCClass.js new file mode 100644 index 0000000..be34ad9 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/platform/CCClass.js @@ -0,0 +1,311 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +var cc = cc || {}; + +/** + * Common getter setter configuration function + * @function + * @param {Object} proto A class prototype or an object to config
+ * @param {String} prop Property name + * @param {function} getter Getter function for the property + * @param {function} setter Setter function for the property + * @param {String} getterName Name of getter function for the property + * @param {String} setterName Name of setter function for the property + */ +cc.defineGetterSetter = function (proto, prop, getter, setter, getterName, setterName) { + if (proto.__defineGetter__) { + getter && proto.__defineGetter__(prop, getter); + setter && proto.__defineSetter__(prop, setter); + } else if (Object.defineProperty) { + var desc = {enumerable: false, configurable: true}; + getter && (desc.get = getter); + setter && (desc.set = setter); + Object.defineProperty(proto, prop, desc); + } else { + throw new Error("browser does not support getters"); + } + + if (!getterName && !setterName) { + // Lookup getter/setter function + var hasGetter = (getter != null), hasSetter = (setter != undefined), props = Object.getOwnPropertyNames(proto); + for (var i = 0; i < props.length; i++) { + var name = props[i]; + + if ((proto.__lookupGetter__ ? proto.__lookupGetter__(name) + : Object.getOwnPropertyDescriptor(proto, name)) + || typeof proto[name] !== "function") + continue; + + var func = proto[name]; + if (hasGetter && func === getter) { + getterName = name; + if (!hasSetter || setterName) break; + } + if (hasSetter && func === setter) { + setterName = name; + if (!hasGetter || getterName) break; + } + } + } + + // Found getter/setter + var ctor = proto.constructor; + if (getterName) { + if (!ctor.__getters__) { + ctor.__getters__ = {}; + } + ctor.__getters__[getterName] = prop; + } + if (setterName) { + if (!ctor.__setters__) { + ctor.__setters__ = {}; + } + ctor.__setters__[setterName] = prop; + } +}; + +/** + * Create a new object and copy all properties in an exist object to the new object + * @function + * @param {object|Array} obj The source object + * @return {Array|object} The created object + */ +cc.clone = function (obj) { + // Cloning is better if the new object is having the same prototype chain + // as the copied obj (or otherwise, the cloned object is certainly going to + // have a different hidden class). Play with C1/C2 of the + // PerformanceVirtualMachineTests suite to see how this makes an impact + // under extreme conditions. + // + // Object.create(Object.getPrototypeOf(obj)) doesn't work well because the + // prototype lacks a link to the constructor (Carakan, V8) so the new + // object wouldn't have the hidden class that's associated with the + // constructor (also, for whatever reasons, utilizing + // Object.create(Object.getPrototypeOf(obj)) + Object.defineProperty is even + // slower than the original in V8). Therefore, we call the constructor, but + // there is a big caveat - it is possible that the this.init() in the + // constructor would throw with no argument. It is also possible that a + // derived class forgets to set "constructor" on the prototype. We ignore + // these possibities for and the ultimate solution is a standardized + // Object.clone(). + var newObj = (obj.constructor) ? new obj.constructor : {}; + + // Assuming that the constuctor above initialized all properies on obj, the + // following keyed assignments won't turn newObj into dictionary mode + // because they're not *appending new properties* but *assigning existing + // ones* (note that appending indexed properties is another story). See + // CCClass.js for a link to the devils when the assumption fails. + for (var key in obj) { + var copy = obj[key]; + // Beware that typeof null == "object" ! + if (((typeof copy) === "object") && copy && !(copy instanceof cc.Node) && !(copy instanceof HTMLElement)) { + newObj[key] = cc.clone(copy); + } else { + newObj[key] = copy; + } + } + return newObj; +}; + +cc.inject = function (srcPrototype, destPrototype) { + for (var key in srcPrototype) + destPrototype[key] = srcPrototype[key]; +}; + +/** + * @namespace + * @name ClassManager + */ +var ClassManager = function () { + var id = (0|(Math.random()*998)); + var instanceId = (0|(Math.random()*998)); + + this.getNewID = function () { + return id++; + }; + + this.getNewInstanceId = function () { + return instanceId++; + }; +}; +var classManager = new ClassManager(); + +/* Managed JavaScript Inheritance + * Based on John Resig's Simple JavaScript Inheritance http://ejohn.org/blog/simple-javascript-inheritance/ + * MIT Licensed. + */ +(function () { + var fnTest = /\b_super\b/; + + /** + * The base Class implementation (does nothing) + * @class + */ + cc.Class = function () { + }; + + /** + * Create a new Class that inherits from this Class + * @static + * @param {object} props + * @return {function} + */ + cc.Class.extend = function (props) { + var _super = this.prototype; + + // Instantiate a base Class (but only create the instance, + // don't run the init constructor) + var prototype = Object.create(_super); + + // Copy the properties over onto the new prototype. We make function + // properties non-eumerable as this makes typeof === 'function' check + // unnecessary in the for...in loop used 1) for generating Class() + // 2) for cc.clone and perhaps more. It is also required to make + // these function properties cacheable in Carakan. + var desc = {writable: true, enumerable: false, configurable: true}; + + // The dummy Class constructor + var Class; + if (cc.game.config && cc.game.config[cc.game.CONFIG_KEY.exposeClassName]) { + var constructor = "(function " + (props._className || "Class") + " (arg0, arg1, arg2, arg3, arg4, arg5) {\n"; + constructor += " this.__instanceId = classManager.getNewInstanceId();\n"; + constructor += " if (this.ctor) {\n"; + constructor += " switch (arguments.length) {\n"; + constructor += " case 0: this.ctor(); break;\n"; + constructor += " case 1: this.ctor(arg0); break;\n"; + constructor += " case 3: this.ctor(arg0, arg1, arg2); break;\n"; + constructor += " case 4: this.ctor(arg0, arg1, arg2, arg3); break;\n"; + constructor += " case 5: this.ctor(arg0, arg1, arg2, arg3, arg4); break;\n"; + constructor += " default: this.ctor.apply(this, arguments);\n"; + constructor += " }\n"; + constructor += " }\n"; + constructor += "})"; + Class = eval(constructor); + } + else { + Class = function (arg0, arg1, arg2, arg3, arg4) { + this.__instanceId = classManager.getNewInstanceId(); + if (this.ctor) { + switch (arguments.length) { + case 0: this.ctor(); break; + case 1: this.ctor(arg0); break; + case 2: this.ctor(arg0, arg1); break; + case 3: this.ctor(arg0, arg1, arg2); break; + case 4: this.ctor(arg0, arg1, arg2, arg3); break; + case 5: this.ctor(arg0, arg1, arg2, arg3, arg4); break; + default: this.ctor.apply(this, arguments); + } + } + }; + } + + desc.value = classManager.getNewID(); + Object.defineProperty(prototype, '__pid', desc); + + // Populate our constructed prototype object + Class.prototype = prototype; + + // Enforce the constructor to be what we expect + desc.value = Class; + Object.defineProperty(prototype, 'constructor', desc); + + // Copy getter/setter + this.__getters__ && (Class.__getters__ = cc.clone(this.__getters__)); + this.__setters__ && (Class.__setters__ = cc.clone(this.__setters__)); + + for (var idx = 0, li = arguments.length; idx < li; ++idx) { + var prop = arguments[idx]; + for (var name in prop) { + var isFunc = (typeof prop[name] === "function"); + var override = (typeof _super[name] === "function"); + var hasSuperCall = fnTest.test(prop[name]); + + if (isFunc && override && hasSuperCall) { + desc.value = (function (name, fn) { + return function () { + var tmp = this._super; + + // Add a new ._super() method that is the same method + // but on the super-Class + this._super = _super[name]; + + // The method only need to be bound temporarily, so we + // remove it when we're done executing + var ret = fn.apply(this, arguments); + this._super = tmp; + + return ret; + }; + })(name, prop[name]); + Object.defineProperty(prototype, name, desc); + } else if (isFunc) { + desc.value = prop[name]; + Object.defineProperty(prototype, name, desc); + } else { + prototype[name] = prop[name]; + } + + if (isFunc) { + // Override registered getter/setter + var getter, setter, propertyName; + if (this.__getters__ && this.__getters__[name]) { + propertyName = this.__getters__[name]; + for (var i in this.__setters__) { + if (this.__setters__[i] === propertyName) { + setter = i; + break; + } + } + cc.defineGetterSetter(prototype, propertyName, prop[name], prop[setter] ? prop[setter] : prototype[setter], name, setter); + } + if (this.__setters__ && this.__setters__[name]) { + propertyName = this.__setters__[name]; + for (var i in this.__getters__) { + if (this.__getters__[i] === propertyName) { + getter = i; + break; + } + } + cc.defineGetterSetter(prototype, propertyName, prop[getter] ? prop[getter] : prototype[getter], prop[name], getter, name); + } + } + } + } + + // And make this Class extendable + Class.extend = cc.Class.extend; + + //add implementation method + Class.implement = function (prop) { + for (var name in prop) { + prototype[name] = prop[name]; + } + }; + return Class; + }; +})(); + diff --git a/frameworks/cocos2d-html5/cocos2d/core/platform/CCCommon.js b/frameworks/cocos2d-html5/cocos2d/core/platform/CCCommon.js new file mode 100644 index 0000000..b99ee95 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/platform/CCCommon.js @@ -0,0 +1,248 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +var cc = cc || {}; +cc._tmp = cc._tmp || {}; + +/** + * Function added for JS bindings compatibility. Not needed in cocos2d-html5. + * @function + * @param {object} jsObj subclass + * @param {object} superclass + */ +cc.associateWithNative = function (jsObj, superclass) { +}; + +/** + * Key map for keyboard event + * + * @constant + * @type {Object} + * @example + cc.eventManager.addListener({ + event: cc.EventListener.KEYBOARD, + onKeyPressed: function(keyCode, event){ + if (cc.KEY["a"] == keyCode) { + cc.log("A is pressed"); + } + } + }, this); + */ +cc.KEY = { + none: 0, + + // android + back: 6, + menu: 18, + + backspace: 8, + tab: 9, + + enter: 13, + + shift: 16, //should use shiftkey instead + ctrl: 17, //should use ctrlkey + alt: 18, //should use altkey + pause: 19, + capslock: 20, + + escape: 27, + space: 32, + pageup: 33, + pagedown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40, + select: 41, + + insert: 45, + Delete: 46, + 0: 48, + 1: 49, + 2: 50, + 3: 51, + 4: 52, + 5: 53, + 6: 54, + 7: 55, + 8: 56, + 9: 57, + a: 65, + b: 66, + c: 67, + d: 68, + e: 69, + f: 70, + g: 71, + h: 72, + i: 73, + j: 74, + k: 75, + l: 76, + m: 77, + n: 78, + o: 79, + p: 80, + q: 81, + r: 82, + s: 83, + t: 84, + u: 85, + v: 86, + w: 87, + x: 88, + y: 89, + z: 90, + + num0: 96, + num1: 97, + num2: 98, + num3: 99, + num4: 100, + num5: 101, + num6: 102, + num7: 103, + num8: 104, + num9: 105, + '*': 106, + '+': 107, + '-': 109, + 'numdel': 110, + '/': 111, + f1: 112, //f1-f12 don't work on ie + f2: 113, + f3: 114, + f4: 115, + f5: 116, + f6: 117, + f7: 118, + f8: 119, + f9: 120, + f10: 121, + f11: 122, + f12: 123, + + numlock: 144, + scrolllock: 145, + + ';': 186, + semicolon: 186, + equal: 187, + '=': 187, + ',': 188, + comma: 188, + dash: 189, + '.': 190, + period: 190, + forwardslash: 191, + grave: 192, + '[': 219, + openbracket: 219, + backslash: 220, + ']': 221, + closebracket: 221, + quote: 222, + + // gamepad control + dpadLeft: 1000, + dpadRight: 1001, + dpadUp: 1003, + dpadDown: 1004, + dpadCenter: 1005 +}; + +/** + * Image Format:JPG + * @constant + * @type {Number} + */ +cc.FMT_JPG = 0; + +/** + * Image Format:PNG + * @constant + * @type {Number} + */ +cc.FMT_PNG = 1; + +/** + * Image Format:TIFF + * @constant + * @type {Number} + */ +cc.FMT_TIFF = 2; + +/** + * Image Format:RAWDATA + * @constant + * @type {Number} + */ +cc.FMT_RAWDATA = 3; + +/** + * Image Format:WEBP + * @constant + * @type {Number} + */ +cc.FMT_WEBP = 4; + +/** + * Image Format:UNKNOWN + * @constant + * @type {Number} + */ +cc.FMT_UNKNOWN = 5; + +/** + * get image format by image data + * @function + * @param {Array} imgData + * @returns {Number} + */ +cc.getImageFormatByData = function (imgData) { + // if it is a png file buffer. + if (imgData.length > 8 && imgData[0] === 0x89 + && imgData[1] === 0x50 + && imgData[2] === 0x4E + && imgData[3] === 0x47 + && imgData[4] === 0x0D + && imgData[5] === 0x0A + && imgData[6] === 0x1A + && imgData[7] === 0x0A) { + return cc.FMT_PNG; + } + + // if it is a tiff file buffer. + if (imgData.length > 2 && ((imgData[0] === 0x49 && imgData[1] === 0x49) + || (imgData[0] === 0x4d && imgData[1] === 0x4d) + || (imgData[0] === 0xff && imgData[1] === 0xd8))) { + return cc.FMT_TIFF; + } + return cc.FMT_UNKNOWN; +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/cocos2d/core/platform/CCConfig.js b/frameworks/cocos2d-html5/cocos2d/core/platform/CCConfig.js new file mode 100644 index 0000000..b479515 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/platform/CCConfig.js @@ -0,0 +1,281 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The current version of Cocos2d-JS being used.
+ * Please DO NOT remove this String, it is an important flag for bug tracking.
+ * If you post a bug to forum, please attach this flag. + * @type {String} + * @name cc.ENGINE_VERSION + */ +window["CocosEngine"] = cc.ENGINE_VERSION = "Cocos2d-JS v3.17"; + +/** + *

+ * If enabled, the texture coordinates will be calculated by using this formula:
+ * - texCoord.left = (rect.x*2+1) / (texture.wide*2);
+ * - texCoord.right = texCoord.left + (rect.width*2-2)/(texture.wide*2);
+ *
+ * The same for bottom and top.
+ *
+ * This formula prevents artifacts by using 99% of the texture.
+ * The "correct" way to prevent artifacts is by using the spritesheet-artifact-fixer.py or a similar tool.
+ *
+ * Affected nodes:
+ * - cc.Sprite / cc.SpriteBatchNode and subclasses: cc.LabelBMFont, cc.TMXTiledMap
+ * - cc.LabelAtlas
+ * - cc.QuadParticleSystem
+ * - cc.TileMap
+ *
+ * To enabled set it to 1. Disabled by default.
+ * To modify it, in Web engine please refer to CCConfig.js, in JSB please refer to CCConfig.h + *

+ * @constant + * @type {Number} + */ +cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL = 0; + +/** + * Position of the FPS (Default: 0,0 (bottom-left corner))
+ * To modify it, in Web engine please refer to CCConfig.js, in JSB please refer to CCConfig.h + * @constant + * @type {cc.Point} + */ +cc.DIRECTOR_STATS_POSITION = cc.p(0, 0); + +/** + *

+ * Seconds between FPS updates.
+ * 0.5 seconds, means that the FPS number will be updated every 0.5 seconds.
+ * Having a bigger number means a more reliable FPS
+ *
+ * Default value: 0.1f
+ * To modify it, in Web engine please refer to CCConfig.js, in JSB please refer to CCConfig.h + *

+ * @constant + * @type {Number} + */ +cc.DIRECTOR_FPS_INTERVAL = 0.5; + +/** + *

+ * If enabled, the cc.Node objects (cc.Sprite, cc.Label,etc) will be able to render in subpixels.
+ * If disabled, integer pixels will be used.
+ *
+ * To enable set it to 1. Enabled by default.
+ * To modify it, in Web engine please refer to CCConfig.js, in JSB please refer to CCConfig.h + *

+ * @constant + * @type {Number} + */ +cc.COCOSNODE_RENDER_SUBPIXEL = 1; + +/** + *

+ * If enabled, the cc.Sprite objects rendered with cc.SpriteBatchNode will be able to render in subpixels.
+ * If disabled, integer pixels will be used.
+ *
+ * To enable set it to 1. Enabled by default.
+ * To modify it, in Web engine please refer to CCConfig.js, in JSB please refer to CCConfig.h + *

+ * @constant + * @type {Number} + */ +cc.SPRITEBATCHNODE_RENDER_SUBPIXEL = 1; + +/** + *

+ * If most of your images have pre-multiplied alpha, set it to 1 (if you are going to use .PNG/.JPG file images).
+ * Only set to 0 if ALL your images by-pass Apple UIImage loading system (eg: if you use libpng or PVR images)
+ *
+ * To enable set it to a value different than 0. Enabled by default.
+ * To modify it, in Web engine please refer to CCConfig.js, in JSB please refer to CCConfig.h + *

+ * @constant + * @type {Number} + */ +cc.OPTIMIZE_BLEND_FUNC_FOR_PREMULTIPLIED_ALPHA = 1; + +/** + *

+ * Use GL_TRIANGLE_STRIP instead of GL_TRIANGLES when rendering the texture atlas.
+ * It seems it is the recommend way, but it is much slower, so, enable it at your own risk
+ *
+ * To enable set it to a value different than 0. Disabled by default.
+ * To modify it, in Web engine please refer to CCConfig.js, in JSB please refer to CCConfig.h + *

+ * @constant + * @type {Number} + */ +cc.TEXTURE_ATLAS_USE_TRIANGLE_STRIP = 0; + +/** + *

+ * By default, cc.TextureAtlas (used by many cocos2d classes) will use VAO (Vertex Array Objects).
+ * Apple recommends its usage but they might consume a lot of memory, specially if you use many of them.
+ * So for certain cases, where you might need hundreds of VAO objects, it might be a good idea to disable it.
+ *
+ * To disable it set it to 0. disable by default.(Not Supported on WebGL)
+ * To modify it, in Web engine please refer to CCConfig.js, in JSB please refer to CCConfig.h + *

+ * @constant + * @type {Number} + */ +cc.TEXTURE_ATLAS_USE_VAO = 0; + +/** + *

+ * If enabled, NPOT textures will be used where available. Only 3rd gen (and newer) devices support NPOT textures.
+ * NPOT textures have the following limitations:
+ * - They can't have mipmaps
+ * - They only accept GL_CLAMP_TO_EDGE in GL_TEXTURE_WRAP_{S,T}
+ *
+ * To enable set it to a value different than 0. Disabled by default.
+ *
+ * This value governs only the PNG, GIF, BMP, images.
+ * This value DOES NOT govern the PVR (PVR.GZ, PVR.CCZ) files. If NPOT PVR is loaded, then it will create an NPOT texture ignoring this value.
+ * To modify it, in Web engine please refer to CCConfig.js, in JSB please refer to CCConfig.h + *

+ * @constant + * @type {Number} + * @deprecated This value will be removed in 1.1 and NPOT textures will be loaded by default if the device supports it. + */ +cc.TEXTURE_NPOT_SUPPORT = 0; + +/** + *

+ * It's the suffix that will be appended to the files in order to load "retina display" images.
+ *
+ * On an iPhone4 with Retina Display support enabled, the file @"sprite-hd.png" will be loaded instead of @"sprite.png".
+ * If the file doesn't exist it will use the non-retina display image.
+ *
+ * Platforms: Only used on Retina Display devices like iPhone 4. + *

+ * @constant + * @type {String} + */ +cc.RETINA_DISPLAY_FILENAME_SUFFIX = "-hd"; + +/** + *

+ * If enabled, it will use LA88 (Luminance Alpha 16-bit textures) for CCLabelTTF objects.
+ * If it is disabled, it will use A8 (Alpha 8-bit textures).
+ * LA88 textures are 6% faster than A8 textures, but they will consume 2x memory.
+ *
+ * This feature is enabled by default. + *

+ * @constant + * @type {Number} + */ +cc.USE_LA88_LABELS = 1; + +/** + *

+ * If enabled, all subclasses of cc.Sprite will draw a bounding box
+ * Useful for debugging purposes only. It is recommend to leave it disabled.
+ *
+ * To enable set it to a value different than 0. Disabled by default:
+ * 0 -- disabled
+ * 1 -- draw bounding box
+ * 2 -- draw texture box + *

+ * @constant + * @type {Number} + */ +cc.SPRITE_DEBUG_DRAW = 0; + +/** + *

+ * If enabled, all subclasses of cc.Sprite that are rendered using an cc.SpriteBatchNode draw a bounding box.
+ * Useful for debugging purposes only. It is recommend to leave it disabled.
+ *
+ * To enable set it to a value different than 0. Disabled by default. + *

+ * @constant + * @type {Number} + */ +cc.SPRITEBATCHNODE_DEBUG_DRAW = 0; + +/** + *

+ * If enabled, all subclasses of cc.LabelBMFont will draw a bounding box
+ * Useful for debugging purposes only. It is recommend to leave it disabled.
+ *
+ * To enable set it to a value different than 0. Disabled by default.
+ *

+ * @constant + * @type {Number} + */ +cc.LABELBMFONT_DEBUG_DRAW = 0; + +/** + *

+ * If enabled, all subclasses of cc.LabelAtlas will draw a bounding box
+ * Useful for debugging purposes only. It is recommend to leave it disabled.
+ *
+ * To enable set it to a value different than 0. Disabled by default. + *

+ * @constant + * @type {Number} + */ +cc.LABELATLAS_DEBUG_DRAW = 0; + +cc.DRAWNODE_TOTAL_VERTICES = 20000; + +/** + * Default engine + * @constant + * @type {String} + */ +cc.DEFAULT_ENGINE = cc.ENGINE_VERSION + "-canvas"; + +/** + *

+ * If enabled, actions that alter the position property (eg: CCMoveBy, CCJumpBy, CCBezierBy, etc..) will be stacked.
+ * If you run 2 or more 'position' actions at the same time on a node, then end position will be the sum of all the positions.
+ * If disabled, only the last run action will take effect. + *

+ * @constant + * @type {number} + */ +cc.ENABLE_STACKABLE_ACTIONS = 1; + +/** + *

+ * If enabled, cocos2d will maintain an OpenGL state cache internally to avoid unnecessary switches.
+ * In order to use them, you have to use the following functions, instead of the the GL ones:
+ * - ccGLUseProgram() instead of glUseProgram()
+ * - ccGLDeleteProgram() instead of glDeleteProgram()
+ * - ccGLBlendFunc() instead of glBlendFunc()
+ *
+ * If this functionality is disabled, then ccGLUseProgram(), ccGLDeleteProgram(), ccGLBlendFunc() will call the GL ones, without using the cache.
+ * It is recommend to enable whenever possible to improve speed.
+ * If you are migrating your code from GL ES 1.1, then keep it disabled. Once all your code works as expected, turn it on. + *

+ * @constant + * @type {Number} + */ +cc.ENABLE_GL_STATE_CACHE = 1; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/cocos2d/core/platform/CCEGLView.js b/frameworks/cocos2d-html5/cocos2d/core/platform/CCEGLView.js new file mode 100755 index 0000000..f91e54b --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/platform/CCEGLView.js @@ -0,0 +1,1411 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * @ignore + */ +cc.Touches = []; +cc.TouchesIntergerDict = {}; + +cc.DENSITYDPI_DEVICE = "device-dpi"; +cc.DENSITYDPI_HIGH = "high-dpi"; +cc.DENSITYDPI_MEDIUM = "medium-dpi"; +cc.DENSITYDPI_LOW = "low-dpi"; + +var __BrowserGetter = { + init: function () { + this.html = document.documentElement; + }, + availWidth: function (frame) { + if (!frame || frame === this.html) + return window.innerWidth; + else + return frame.clientWidth; + }, + availHeight: function (frame) { + if (!frame || frame === this.html) + return window.innerHeight; + else + return frame.clientHeight; + }, + meta: { + "width": "device-width" + }, + adaptationType: cc.sys.browserType +}; + +if (window.navigator.userAgent.indexOf("OS 8_1_") > -1) //this mistake like MIUI, so use of MIUI treatment method + __BrowserGetter.adaptationType = cc.sys.BROWSER_TYPE_MIUI; + +if (cc.sys.os === cc.sys.OS_IOS) // All browsers are WebView + __BrowserGetter.adaptationType = cc.sys.BROWSER_TYPE_SAFARI; + +switch (__BrowserGetter.adaptationType) { + case cc.sys.BROWSER_TYPE_SAFARI: + __BrowserGetter.meta["minimal-ui"] = "true"; + break; + case cc.sys.BROWSER_TYPE_CHROME: + __BrowserGetter.__defineGetter__("target-densitydpi", function () { + return cc.view._targetDensityDPI; + }); + break; + case cc.sys.BROWSER_TYPE_MIUI: + __BrowserGetter.init = function (view) { + if (view.__resizeWithBrowserSize) return; + var resize = function () { + view.setDesignResolutionSize( + view._designResolutionSize.width, + view._designResolutionSize.height, + view._resolutionPolicy + ); + window.removeEventListener("resize", resize, false); + }; + window.addEventListener("resize", resize, false); + }; + break; +} + +var _scissorRect = null; + +/** + * cc.view is the singleton object which represents the game window.
+ * It's main task include:
+ * - Apply the design resolution policy
+ * - Provide interaction with the window, like resize event on web, retina display support, etc...
+ * - Manage the game view port which can be different with the window
+ * - Manage the content scale and translation
+ *
+ * Since the cc.view is a singleton, you don't need to call any constructor or create functions,
+ * the standard way to use it is by calling:
+ * - cc.view.methodName();
+ * @class + * @name cc.view + * @extend cc.Class + */ +cc.EGLView = cc.Class.extend(/** @lends cc.view# */{ + _delegate: null, + // Size of parent node that contains cc.container and cc._canvas + _frameSize: null, + // resolution size, it is the size appropriate for the app resources. + _designResolutionSize: null, + _originalDesignResolutionSize: null, + // Viewport is the container's rect related to content's coordinates in pixel + _viewPortRect: null, + // The visible rect in content's coordinate in point + _visibleRect: null, + _retinaEnabled: false, + _autoFullScreen: false, + // The device's pixel ratio (for retina displays) + _devicePixelRatio: 1, + // the view name + _viewName: "", + // Custom callback for resize event + _resizeCallback: null, + + _orientationChanging: true, + _resizing: false, + + _scaleX: 1, + _originalScaleX: 1, + _scaleY: 1, + _originalScaleY: 1, + + _isRotated: false, + _orientation: 3, + + _resolutionPolicy: null, + _rpExactFit: null, + _rpShowAll: null, + _rpNoBorder: null, + _rpFixedHeight: null, + _rpFixedWidth: null, + _initialized: false, + + _contentTranslateLeftTop: null, + + // Parent node that contains cc.container and cc._canvas + _frame: null, + _frameZoomFactor: 1.0, + __resizeWithBrowserSize: false, + _isAdjustViewPort: true, + _targetDensityDPI: null, + + /** + * Constructor of cc.EGLView + */ + ctor: function () { + var _t = this, d = document, _strategyer = cc.ContainerStrategy, _strategy = cc.ContentStrategy; + + __BrowserGetter.init(this); + + _t._frame = (cc.container.parentNode === d.body) ? d.documentElement : cc.container.parentNode; + _t._frameSize = cc.size(0, 0); + _t._initFrameSize(); + + var w = cc._canvas.width, h = cc._canvas.height; + _t._designResolutionSize = cc.size(w, h); + _t._originalDesignResolutionSize = cc.size(w, h); + _t._viewPortRect = cc.rect(0, 0, w, h); + _t._visibleRect = cc.rect(0, 0, w, h); + _t._contentTranslateLeftTop = {left: 0, top: 0}; + _t._viewName = "Cocos2dHTML5"; + + var sys = cc.sys; + cc.visibleRect && cc.visibleRect.init(_t._visibleRect); + + // Setup system default resolution policies + _t._rpExactFit = new cc.ResolutionPolicy(_strategyer.EQUAL_TO_FRAME, _strategy.EXACT_FIT); + _t._rpShowAll = new cc.ResolutionPolicy(_strategyer.PROPORTION_TO_FRAME, _strategy.SHOW_ALL); + _t._rpNoBorder = new cc.ResolutionPolicy(_strategyer.EQUAL_TO_FRAME, _strategy.NO_BORDER); + _t._rpFixedHeight = new cc.ResolutionPolicy(_strategyer.EQUAL_TO_FRAME, _strategy.FIXED_HEIGHT); + _t._rpFixedWidth = new cc.ResolutionPolicy(_strategyer.EQUAL_TO_FRAME, _strategy.FIXED_WIDTH); + + _t._targetDensityDPI = cc.DENSITYDPI_HIGH; + + if (sys.isMobile) { + window.addEventListener('orientationchange', this._orientationChange); + } else { + this._orientationChanging = false; + } + }, + + // Resize helper functions + _resizeEvent: function () { + var view; + if (this.setDesignResolutionSize) { + view = this; + } else { + view = cc.view; + } + if (view._orientationChanging) { + return; + } + + // Check frame size changed or not + var prevFrameW = view._frameSize.width, prevFrameH = view._frameSize.height, prevRotated = view._isRotated; + if (cc.sys.isMobile) { + var containerStyle = cc.game.container.style, + margin = containerStyle.margin; + containerStyle.margin = '0'; + containerStyle.display = 'none'; + view._initFrameSize(); + containerStyle.margin = margin; + containerStyle.display = 'block'; + } + else { + view._initFrameSize(); + } + if (view._isRotated === prevRotated && view._frameSize.width === prevFrameW && view._frameSize.height === prevFrameH) + return; + + // Frame size changed, do resize works + var width = view._originalDesignResolutionSize.width; + var height = view._originalDesignResolutionSize.height; + view._resizing = true; + if (width > 0) { + view.setDesignResolutionSize(width, height, view._resolutionPolicy); + } + view._resizing = false; + + cc.eventManager.dispatchCustomEvent('canvas-resize'); + if (view._resizeCallback) { + view._resizeCallback.call(); + } + }, + + _orientationChange: function () { + cc.view._orientationChanging = true; + if (cc.sys.isMobile) { + cc.game.container.style.display = "none"; + } + setTimeout(function () { + cc.view._orientationChanging = false; + cc.view._resizeEvent(); + }, 300); + }, + + /** + *

+ * Sets view's target-densitydpi for android mobile browser. it can be set to:
+ * 1. cc.DENSITYDPI_DEVICE, value is "device-dpi"
+ * 2. cc.DENSITYDPI_HIGH, value is "high-dpi" (default value)
+ * 3. cc.DENSITYDPI_MEDIUM, value is "medium-dpi" (browser's default value)
+ * 4. cc.DENSITYDPI_LOW, value is "low-dpi"
+ * 5. Custom value, e.g: "480"
+ *

+ * @param {String} densityDPI + */ + setTargetDensityDPI: function (densityDPI) { + this._targetDensityDPI = densityDPI; + this._adjustViewportMeta(); + }, + + /** + * Returns the current target-densitydpi value of cc.view. + * @returns {String} + */ + getTargetDensityDPI: function () { + return this._targetDensityDPI; + }, + + /** + * Sets whether resize canvas automatically when browser's size changed.
+ * Useful only on web. + * @param {Boolean} enabled Whether enable automatic resize with browser's resize event + */ + resizeWithBrowserSize: function (enabled) { + if (enabled) { + //enable + if (!this.__resizeWithBrowserSize) { + this.__resizeWithBrowserSize = true; + window.addEventListener('resize', this._resizeEvent); + } + } else { + //disable + if (this.__resizeWithBrowserSize) { + this.__resizeWithBrowserSize = false; + window.removeEventListener('resize', this._resizeEvent); + } + } + }, + + /** + * Sets the callback function for cc.view's resize action,
+ * this callback will be invoked before applying resolution policy,
+ * so you can do any additional modifications within the callback.
+ * Useful only on web. + * @param {Function|null} callback The callback function + */ + setResizeCallback: function (callback) { + if (typeof callback === 'function' || callback == null) { + this._resizeCallback = callback; + } + }, + + /** + * Sets the orientation of the game, it can be landscape, portrait or auto. + * When set it to landscape or portrait, and screen w/h ratio doesn't fit, + * cc.view will automatically rotate the game canvas using CSS. + * Note that this function doesn't have any effect in native, + * in native, you need to set the application orientation in native project settings + * @param {Number} orientation - Possible values: cc.ORIENTATION_LANDSCAPE | cc.ORIENTATION_PORTRAIT | cc.ORIENTATION_AUTO + */ + setOrientation: function (orientation) { + orientation = orientation & cc.ORIENTATION_AUTO; + if (orientation && this._orientation !== orientation) { + this._orientation = orientation; + if (this._resolutionPolicy) { + var designWidth = this._originalDesignResolutionSize.width; + var designHeight = this._originalDesignResolutionSize.height; + this.setDesignResolutionSize(designWidth, designHeight, this._resolutionPolicy); + } + } + }, + + setDocumentPixelWidth: function (width) { + // Set viewport's width + this._setViewportMeta({"width": width}, true); + + // Set body width to the exact pixel resolution + document.documentElement.style.width = width + 'px'; + document.body.style.width = "100%"; + + // Reset the resolution size and policy + this.setDesignResolutionSize(this._designResolutionSize.width, this._designResolutionSize.height, this._resolutionPolicy); + }, + + _initFrameSize: function () { + var locFrameSize = this._frameSize; + var w = __BrowserGetter.availWidth(this._frame); + var h = __BrowserGetter.availHeight(this._frame); + var isLandscape = w >= h; + + if (!cc.sys.isMobile || + (isLandscape && this._orientation & cc.ORIENTATION_LANDSCAPE) || + (!isLandscape && this._orientation & cc.ORIENTATION_PORTRAIT)) { + locFrameSize.width = w; + locFrameSize.height = h; + cc.container.style['-webkit-transform'] = 'rotate(0deg)'; + cc.container.style.transform = 'rotate(0deg)'; + this._isRotated = false; + } + else { + locFrameSize.width = h; + locFrameSize.height = w; + cc.container.style['-webkit-transform'] = 'rotate(90deg)'; + cc.container.style.transform = 'rotate(90deg)'; + cc.container.style['-webkit-transform-origin'] = '0px 0px 0px'; + cc.container.style.transformOrigin = '0px 0px 0px'; + this._isRotated = true; + } + }, + + // hack + _adjustSizeKeepCanvasSize: function () { + var designWidth = this._originalDesignResolutionSize.width; + var designHeight = this._originalDesignResolutionSize.height; + if (designWidth > 0) + this.setDesignResolutionSize(designWidth, designHeight, this._resolutionPolicy); + }, + + _setViewportMeta: function (metas, overwrite) { + var vp = document.getElementById("cocosMetaElement"); + if (vp && overwrite) { + document.head.removeChild(vp); + } + + var elems = document.getElementsByName("viewport"), + currentVP = elems ? elems[0] : null, + content, key, pattern; + + content = currentVP ? currentVP.content : ""; + vp = vp || document.createElement("meta"); + vp.id = "cocosMetaElement"; + vp.name = "viewport"; + vp.content = ""; + + for (key in metas) { + if (content.indexOf(key) == -1) { + content += "," + key + "=" + metas[key]; + } + else if (overwrite) { + pattern = new RegExp(key + "\s*=\s*[^,]+"); + content.replace(pattern, key + "=" + metas[key]); + } + } + if (/^,/.test(content)) + content = content.substr(1); + + vp.content = content; + // For adopting certain android devices which don't support second viewport + if (currentVP) + currentVP.content = content; + + document.head.appendChild(vp); + }, + + _adjustViewportMeta: function () { + if (this._isAdjustViewPort) { + this._setViewportMeta(__BrowserGetter.meta, false); + // Only adjust viewport once + this._isAdjustViewPort = false; + } + }, + + // RenderTexture hacker + _setScaleXYForRenderTexture: function () { + //hack for RenderTexture on canvas mode when adapting multiple resolution resources + var scaleFactor = cc.contentScaleFactor(); + this._scaleX = scaleFactor; + this._scaleY = scaleFactor; + }, + + // Other helper functions + _resetScale: function () { + this._scaleX = this._originalScaleX; + this._scaleY = this._originalScaleY; + }, + + // Useless, just make sure the compatibility temporarily, should be removed + _adjustSizeToBrowser: function () { + }, + + initialize: function () { + this._initialized = true; + }, + + /** + * Sets whether the engine modify the "viewport" meta in your web page.
+ * It's enabled by default, we strongly suggest you not to disable it.
+ * And even when it's enabled, you can still set your own "viewport" meta, it won't be overridden
+ * Only useful on web + * @param {Boolean} enabled Enable automatic modification to "viewport" meta + */ + adjustViewPort: function (enabled) { + this._isAdjustViewPort = enabled; + }, + + /** + * Retina support is enabled by default for Apple device but disabled for other devices,
+ * it takes effect only when you called setDesignResolutionPolicy
+ * Only useful on web + * @param {Boolean} enabled Enable or disable retina display + */ + enableRetina: function (enabled) { + this._retinaEnabled = !!enabled; + }, + + /** + * Check whether retina display is enabled.
+ * Only useful on web + * @return {Boolean} + */ + isRetinaEnabled: function () { + return this._retinaEnabled; + }, + + /** + * If enabled, the application will try automatically to enter full screen mode on mobile devices
+ * You can pass true as parameter to enable it and disable it by passing false.
+ * Only useful on web + * @param {Boolean} enabled Enable or disable auto full screen on mobile devices + */ + enableAutoFullScreen: function (enabled) { + if (enabled && enabled !== this._autoFullScreen && cc.sys.isMobile && this._frame === document.documentElement) { + // Automatically full screen when user touches on mobile version + this._autoFullScreen = true; + cc.screen.autoFullScreen(this._frame); + } + else { + this._autoFullScreen = false; + } + }, + + /** + * Check whether auto full screen is enabled.
+ * Only useful on web + * @return {Boolean} Auto full screen enabled or not + */ + isAutoFullScreenEnabled: function () { + return this._autoFullScreen; + }, + + /** + * Get whether render system is ready(no matter opengl or canvas),
+ * this name is for the compatibility with cocos2d-x, subclass must implement this method. + * @return {Boolean} + */ + isOpenGLReady: function () { + return (cc.game.canvas && cc._renderContext); + }, + + /* + * Set zoom factor for frame. This method is for debugging big resolution (e.g.new ipad) app on desktop. + * @param {Number} zoomFactor + */ + setFrameZoomFactor: function (zoomFactor) { + this._frameZoomFactor = zoomFactor; + this.centerWindow(); + cc.director.setProjection(cc.director.getProjection()); + }, + + /** + * Exchanges the front and back buffers, subclass must implement this method. + */ + swapBuffers: function () { + }, + + /** + * Open or close IME keyboard , subclass must implement this method. + * @param {Boolean} isOpen + */ + setIMEKeyboardState: function (isOpen) { + }, + + /** + * Sets the resolution translate on EGLView + * @param {Number} offsetLeft + * @param {Number} offsetTop + */ + setContentTranslateLeftTop: function (offsetLeft, offsetTop) { + this._contentTranslateLeftTop = {left: offsetLeft, top: offsetTop}; + }, + + /** + * Returns the resolution translate on EGLView + * @return {cc.Size|Object} + */ + getContentTranslateLeftTop: function () { + return this._contentTranslateLeftTop; + }, + + /** + * Returns the canvas size of the view.
+ * On native platforms, it returns the screen size since the view is a fullscreen view.
+ * On web, it returns the size of the canvas element. + * @return {cc.Size} + */ + getCanvasSize: function () { + return cc.size(cc._canvas.width, cc._canvas.height); + }, + + /** + * Returns the frame size of the view.
+ * On native platforms, it returns the screen size since the view is a fullscreen view.
+ * On web, it returns the size of the canvas's outer DOM element. + * @return {cc.Size} + */ + getFrameSize: function () { + return cc.size(this._frameSize.width, this._frameSize.height); + }, + + /** + * On native, it sets the frame size of view.
+ * On web, it sets the size of the canvas's outer DOM element. + * @param {Number} width + * @param {Number} height + */ + setFrameSize: function (width, height) { + this._frameSize.width = width; + this._frameSize.height = height; + this._frame.style.width = width + "px"; + this._frame.style.height = height + "px"; + this._resizeEvent(); + cc.director.setProjection(cc.director.getProjection()); + }, + + /** + * Returns the visible area size of the view port. + * @return {cc.Size} + */ + getVisibleSize: function () { + return cc.size(this._visibleRect.width, this._visibleRect.height); + }, + + /** + * Returns the visible area size of the view port. + * @return {cc.Size} + */ + getVisibleSizeInPixel: function () { + return cc.size( this._visibleRect.width * this._scaleX, + this._visibleRect.height * this._scaleY ); + }, + + /** + * Returns the visible origin of the view port. + * @return {cc.Point} + */ + getVisibleOrigin: function () { + return cc.p(this._visibleRect.x, this._visibleRect.y); + }, + + /** + * Returns the visible origin of the view port. + * @return {cc.Point} + */ + getVisibleOriginInPixel: function () { + return cc.p(this._visibleRect.x * this._scaleX, + this._visibleRect.y * this._scaleY); + }, + + /** + * Returns whether developer can set content's scale factor. + * @return {Boolean} + */ + canSetContentScaleFactor: function () { + return true; + }, + + /** + * Returns the current resolution policy + * @see cc.ResolutionPolicy + * @return {cc.ResolutionPolicy} + */ + getResolutionPolicy: function () { + return this._resolutionPolicy; + }, + + /** + * Sets the current resolution policy + * @see cc.ResolutionPolicy + * @param {cc.ResolutionPolicy|Number} resolutionPolicy + */ + setResolutionPolicy: function (resolutionPolicy) { + var _t = this; + if (resolutionPolicy instanceof cc.ResolutionPolicy) { + _t._resolutionPolicy = resolutionPolicy; + } + // Ensure compatibility with JSB + else { + var _locPolicy = cc.ResolutionPolicy; + if (resolutionPolicy === _locPolicy.EXACT_FIT) + _t._resolutionPolicy = _t._rpExactFit; + if (resolutionPolicy === _locPolicy.SHOW_ALL) + _t._resolutionPolicy = _t._rpShowAll; + if (resolutionPolicy === _locPolicy.NO_BORDER) + _t._resolutionPolicy = _t._rpNoBorder; + if (resolutionPolicy === _locPolicy.FIXED_HEIGHT) + _t._resolutionPolicy = _t._rpFixedHeight; + if (resolutionPolicy === _locPolicy.FIXED_WIDTH) + _t._resolutionPolicy = _t._rpFixedWidth; + } + }, + + /** + * Sets the resolution policy with designed view size in points.
+ * The resolution policy include:
+ * [1] ResolutionExactFit Fill screen by stretch-to-fit: if the design resolution ratio of width to height is different from the screen resolution ratio, your game view will be stretched.
+ * [2] ResolutionNoBorder Full screen without black border: if the design resolution ratio of width to height is different from the screen resolution ratio, two areas of your game view will be cut.
+ * [3] ResolutionShowAll Full screen with black border: if the design resolution ratio of width to height is different from the screen resolution ratio, two black borders will be shown.
+ * [4] ResolutionFixedHeight Scale the content's height to screen's height and proportionally scale its width
+ * [5] ResolutionFixedWidth Scale the content's width to screen's width and proportionally scale its height
+ * [cc.ResolutionPolicy] [Web only feature] Custom resolution policy, constructed by cc.ResolutionPolicy
+ * @param {Number} width Design resolution width. + * @param {Number} height Design resolution height. + * @param {cc.ResolutionPolicy|Number} resolutionPolicy The resolution policy desired + */ + setDesignResolutionSize: function (width, height, resolutionPolicy) { + // Defensive code + if (!(width > 0 || height > 0)) { + cc.log(cc._LogInfos.EGLView_setDesignResolutionSize); + return; + } + + this.setResolutionPolicy(resolutionPolicy); + var policy = this._resolutionPolicy; + if (policy) { + policy.preApply(this); + } + + // Reinit frame size + if (cc.sys.isMobile) + this._adjustViewportMeta(); + + // If resizing, then frame size is already initialized, this logic should be improved + if (!this._resizing) + this._initFrameSize(); + + if (!policy) { + cc.log(cc._LogInfos.EGLView_setDesignResolutionSize_2); + return; + } + + this._originalDesignResolutionSize.width = this._designResolutionSize.width = width; + this._originalDesignResolutionSize.height = this._designResolutionSize.height = height; + + var result = policy.apply(this, this._designResolutionSize); + + if (result.scale && result.scale.length === 2) { + this._scaleX = result.scale[0]; + this._scaleY = result.scale[1]; + } + + if (result.viewport) { + var vp = this._viewPortRect, + vb = this._visibleRect, + rv = result.viewport; + + vp.x = rv.x; + vp.y = rv.y; + vp.width = rv.width; + vp.height = rv.height; + + vb.x = -vp.x / this._scaleX; + vb.y = -vp.y / this._scaleY; + vb.width = cc._canvas.width / this._scaleX; + vb.height = cc._canvas.height / this._scaleY; + cc._renderContext.setOffset && cc._renderContext.setOffset(vp.x, -vp.y); + } + + // reset director's member variables to fit visible rect + var director = cc.director; + director._winSizeInPoints.width = this._designResolutionSize.width; + director._winSizeInPoints.height = this._designResolutionSize.height; + policy.postApply(this); + cc.winSize.width = director._winSizeInPoints.width; + cc.winSize.height = director._winSizeInPoints.height; + + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + // reset director's member variables to fit visible rect + director.setGLDefaultValues(); + } + else if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) { + cc.renderer._allNeedDraw = true; + } + + this._originalScaleX = this._scaleX; + this._originalScaleY = this._scaleY; + cc.visibleRect && cc.visibleRect.init(this._visibleRect); + }, + + /** + * Returns the designed size for the view. + * Default resolution size is the same as 'getFrameSize'. + * @return {cc.Size} + */ + getDesignResolutionSize: function () { + return cc.size(this._designResolutionSize.width, this._designResolutionSize.height); + }, + + /** + * Sets the document body to desired pixel resolution and fit the game content to it. + * This function is very useful for adaptation in mobile browsers. + * In some HD android devices, the resolution is very high, but its browser performance may not be very good. + * In this case, enabling retina display is very costy and not suggested, and if retina is disabled, the image may be blurry. + * But this API can be helpful to set a desired pixel resolution which is in between. + * This API will do the following: + * 1. Set viewport's width to the desired width in pixel + * 2. Set body width to the exact pixel resolution + * 3. The resolution policy will be reset with designed view size in points. + * @param {Number} width Design resolution width. + * @param {Number} height Design resolution height. + * @param {cc.ResolutionPolicy|Number} resolutionPolicy The resolution policy desired + */ + setRealPixelResolution: function (width, height, resolutionPolicy) { + // Set viewport's width + this._setViewportMeta({"width": width}, true); + + // Set body width to the exact pixel resolution + document.documentElement.style.width = width + "px"; + document.body.style.width = width + "px"; + document.body.style.left = "0px"; + document.body.style.top = "0px"; + + // Reset the resolution size and policy + this.setDesignResolutionSize(width, height, resolutionPolicy); + }, + + /** + * Sets view port rectangle with points. + * @param {Number} x + * @param {Number} y + * @param {Number} w width + * @param {Number} h height + */ + setViewPortInPoints: function (x, y, w, h) { + var locFrameZoomFactor = this._frameZoomFactor, locScaleX = this._scaleX, locScaleY = this._scaleY; + cc._renderContext.viewport((x * locScaleX * locFrameZoomFactor + this._viewPortRect.x * locFrameZoomFactor), + (y * locScaleY * locFrameZoomFactor + this._viewPortRect.y * locFrameZoomFactor), + (w * locScaleX * locFrameZoomFactor), + (h * locScaleY * locFrameZoomFactor)); + }, + + /** + * Sets Scissor rectangle with points. + * @param {Number} x + * @param {Number} y + * @param {Number} w + * @param {Number} h + */ + setScissorInPoints: function (x, y, w, h) { + var locFrameZoomFactor = this._frameZoomFactor, locScaleX = this._scaleX, locScaleY = this._scaleY; + var sx = Math.ceil(x * locScaleX * locFrameZoomFactor + this._viewPortRect.x * locFrameZoomFactor); + var sy = Math.ceil(y * locScaleY * locFrameZoomFactor + this._viewPortRect.y * locFrameZoomFactor); + var sw = Math.ceil(w * locScaleX * locFrameZoomFactor); + var sh = Math.ceil(h * locScaleY * locFrameZoomFactor); + + if (!_scissorRect) { + var boxArr = gl.getParameter(gl.SCISSOR_BOX); + _scissorRect = cc.rect(boxArr[0], boxArr[1], boxArr[2], boxArr[3]); + } + + if (_scissorRect.x != sx || _scissorRect.y != sy || _scissorRect.width != sw || _scissorRect.height != sh) { + _scissorRect.x = sx; + _scissorRect.y = sy; + _scissorRect.width = sw; + _scissorRect.height = sh; + cc._renderContext.scissor(sx, sy, sw, sh); + } + }, + + /** + * Returns whether GL_SCISSOR_TEST is enable + * @return {Boolean} + */ + isScissorEnabled: function () { + return cc._renderContext.isEnabled(gl.SCISSOR_TEST); + }, + + /** + * Returns the current scissor rectangle + * @return {cc.Rect} + */ + getScissorRect: function () { + if (!_scissorRect) { + var boxArr = gl.getParameter(gl.SCISSOR_BOX); + _scissorRect = cc.rect(boxArr[0], boxArr[1], boxArr[2], boxArr[3]); + } + var scaleXFactor = 1 / this._scaleX; + var scaleYFactor = 1 / this._scaleY; + return cc.rect( + (_scissorRect.x - this._viewPortRect.x) * scaleXFactor, + (_scissorRect.y - this._viewPortRect.y) * scaleYFactor, + _scissorRect.width * scaleXFactor, + _scissorRect.height * scaleYFactor + ); + }, + + /** + * Sets the name of the view + * @param {String} viewName + */ + setViewName: function (viewName) { + if (viewName != null && viewName.length > 0) { + this._viewName = viewName; + } + }, + + /** + * Returns the name of the view + * @return {String} + */ + getViewName: function () { + return this._viewName; + }, + + /** + * Returns the view port rectangle. + * @return {cc.Rect} + */ + getViewPortRect: function () { + return this._viewPortRect; + }, + + /** + * Returns scale factor of the horizontal direction (X axis). + * @return {Number} + */ + getScaleX: function () { + return this._scaleX; + }, + + /** + * Returns scale factor of the vertical direction (Y axis). + * @return {Number} + */ + getScaleY: function () { + return this._scaleY; + }, + + /** + * Returns device pixel ratio for retina display. + * @return {Number} + */ + getDevicePixelRatio: function () { + return this._devicePixelRatio; + }, + + /** + * Returns the real location in view for a translation based on a related position + * @param {Number} tx The X axis translation + * @param {Number} ty The Y axis translation + * @param {Object} relatedPos The related position object including "left", "top", "width", "height" informations + * @return {cc.Point} + */ + convertToLocationInView: function (tx, ty, relatedPos) { + var x = this._devicePixelRatio * (tx - relatedPos.left); + var y = this._devicePixelRatio * (relatedPos.top + relatedPos.height - ty); + return this._isRotated ? {x: this._viewPortRect.width - y, y: x} : {x: x, y: y}; + }, + + _convertMouseToLocationInView: function (point, relatedPos) { + var viewport = this._viewPortRect, _t = this; + point.x = ((_t._devicePixelRatio * (point.x - relatedPos.left)) - viewport.x) / _t._scaleX; + point.y = (_t._devicePixelRatio * (relatedPos.top + relatedPos.height - point.y) - viewport.y) / _t._scaleY; + }, + + _convertPointWithScale: function (point) { + var viewport = this._viewPortRect; + point.x = (point.x - viewport.x) / this._scaleX; + point.y = (point.y - viewport.y) / this._scaleY; + }, + + _convertTouchesWithScale: function (touches) { + var viewport = this._viewPortRect, scaleX = this._scaleX, scaleY = this._scaleY, + selTouch, selPoint, selPrePoint; + for (var i = 0; i < touches.length; i++) { + selTouch = touches[i]; + selPoint = selTouch._point; + selPrePoint = selTouch._prevPoint; + + selPoint.x = (selPoint.x - viewport.x) / scaleX; + selPoint.y = (selPoint.y - viewport.y) / scaleY; + selPrePoint.x = (selPrePoint.x - viewport.x) / scaleX; + selPrePoint.y = (selPrePoint.y - viewport.y) / scaleY; + } + } +}); + +/** + * @function + * @return {cc.EGLView} + * @private + */ +cc.EGLView._getInstance = function () { + if (!this._instance) { + this._instance = this._instance || new cc.EGLView(); + this._instance.initialize(); + } + return this._instance; +}; + +/** + *

cc.ContainerStrategy class is the root strategy class of container's scale strategy, + * it controls the behavior of how to scale the cc.container and cc._canvas object

+ * + * @class + * @extends cc.Class + */ +cc.ContainerStrategy = cc.Class.extend(/** @lends cc.ContainerStrategy# */{ + /** + * Manipulation before appling the strategy + * @param {cc.view} The target view + */ + preApply: function (view) { + }, + + /** + * Function to apply this strategy + * @param {cc.view} view + * @param {cc.Size} designedResolution + */ + apply: function (view, designedResolution) { + }, + + /** + * Manipulation after applying the strategy + * @param {cc.view} view The target view + */ + postApply: function (view) { + + }, + + _setupContainer: function (view, w, h) { + var locCanvas = cc.game.canvas, locContainer = cc.game.container; + if (cc.sys.os === cc.sys.OS_ANDROID) { + document.body.style.width = (view._isRotated ? h : w) + 'px'; + document.body.style.height = (view._isRotated ? w : h) + 'px'; + } + + // Setup style + locContainer.style.width = locCanvas.style.width = w + 'px'; + locContainer.style.height = locCanvas.style.height = h + 'px'; + // Setup pixel ratio for retina display + var devicePixelRatio = view._devicePixelRatio = 1; + if (view.isRetinaEnabled()) + devicePixelRatio = view._devicePixelRatio = Math.min(2, window.devicePixelRatio || 1); + // Setup canvas + locCanvas.width = w * devicePixelRatio; + locCanvas.height = h * devicePixelRatio; + cc._renderContext.resetCache && cc._renderContext.resetCache(); + }, + + _fixContainer: function () { + // Add container to document body + document.body.insertBefore(cc.container, document.body.firstChild); + // Set body's width height to window's size, and forbid overflow, so that game will be centered + var bs = document.body.style; + bs.width = window.innerWidth + "px"; + bs.height = window.innerHeight + "px"; + bs.overflow = "hidden"; + // Body size solution doesn't work on all mobile browser so this is the aleternative: fixed container + var contStyle = cc.container.style; + contStyle.position = "fixed"; + contStyle.left = contStyle.top = "0px"; + // Reposition body + document.body.scrollTop = 0; + } +}); + +/** + *

cc.ContentStrategy class is the root strategy class of content's scale strategy, + * it controls the behavior of how to scale the scene and setup the viewport for the game

+ * + * @class + * @extends cc.Class + */ +cc.ContentStrategy = cc.Class.extend(/** @lends cc.ContentStrategy# */{ + + _result: { + scale: [1, 1], + viewport: null + }, + + _buildResult: function (containerW, containerH, contentW, contentH, scaleX, scaleY) { + // Makes content fit better the canvas + Math.abs(containerW - contentW) < 2 && (contentW = containerW); + Math.abs(containerH - contentH) < 2 && (contentH = containerH); + + var viewport = cc.rect(Math.round((containerW - contentW) / 2), + Math.round((containerH - contentH) / 2), + contentW, contentH); + + // Translate the content + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) { + //TODO: modify something for setTransform + //cc._renderContext.translate(viewport.x, viewport.y + contentH); + } + + this._result.scale = [scaleX, scaleY]; + this._result.viewport = viewport; + return this._result; + }, + + /** + * Manipulation before applying the strategy + * @param {cc.view} view The target view + */ + preApply: function (view) { + }, + + /** + * Function to apply this strategy + * The return value is {scale: [scaleX, scaleY], viewport: {cc.Rect}}, + * The target view can then apply these value to itself, it's preferred not to modify directly its private variables + * @param {cc.view} view + * @param {cc.Size} designedResolution + * @return {object} scaleAndViewportRect + */ + apply: function (view, designedResolution) { + return {"scale": [1, 1]}; + }, + + /** + * Manipulation after applying the strategy + * @param {cc.view} view The target view + */ + postApply: function (view) { + } +}); + +(function () { + +// Container scale strategys + /** + * @class + * @extends cc.ContainerStrategy + */ + var EqualToFrame = cc.ContainerStrategy.extend({ + apply: function (view) { + var frameH = view._frameSize.height, containerStyle = cc.container.style; + this._setupContainer(view, view._frameSize.width, view._frameSize.height); + // Setup container's margin and padding + if (view._isRotated) { + containerStyle.margin = '0 0 0 ' + frameH + 'px'; + } + else { + containerStyle.margin = '0px'; + } + } + }); + + /** + * @class + * @extends cc.ContainerStrategy + */ + var ProportionalToFrame = cc.ContainerStrategy.extend({ + apply: function (view, designedResolution) { + var frameW = view._frameSize.width, frameH = view._frameSize.height, containerStyle = cc.container.style, + designW = designedResolution.width, designH = designedResolution.height, + scaleX = frameW / designW, scaleY = frameH / designH, + containerW, containerH; + + scaleX < scaleY ? (containerW = frameW, containerH = designH * scaleX) : (containerW = designW * scaleY, containerH = frameH); + + // Adjust container size with integer value + var offx = Math.round((frameW - containerW) / 2); + var offy = Math.round((frameH - containerH) / 2); + containerW = frameW - 2 * offx; + containerH = frameH - 2 * offy; + + this._setupContainer(view, containerW, containerH); + // Setup container's margin and padding + if (view._isRotated) { + containerStyle.margin = '0 0 0 ' + frameH + 'px'; + } + else { + containerStyle.margin = '0px'; + } + containerStyle.paddingLeft = offx + "px"; + containerStyle.paddingRight = offx + "px"; + containerStyle.paddingTop = offy + "px"; + containerStyle.paddingBottom = offy + "px"; + } + }); + + /** + * @class + * @extends EqualToFrame + */ + var EqualToWindow = EqualToFrame.extend({ + preApply: function (view) { + this._super(view); + view._frame = document.documentElement; + }, + + apply: function (view) { + this._super(view); + this._fixContainer(); + } + }); + + /** + * @class + * @extends ProportionalToFrame + */ + var ProportionalToWindow = ProportionalToFrame.extend({ + preApply: function (view) { + this._super(view); + view._frame = document.documentElement; + }, + + apply: function (view, designedResolution) { + this._super(view, designedResolution); + this._fixContainer(); + } + }); + + /** + * @class + * @extends cc.ContainerStrategy + */ + var OriginalContainer = cc.ContainerStrategy.extend({ + apply: function (view) { + this._setupContainer(view, cc._canvas.width, cc._canvas.height); + } + }); + +// #NOT STABLE on Android# Alias: Strategy that makes the container's size equals to the window's size +// cc.ContainerStrategy.EQUAL_TO_WINDOW = new EqualToWindow(); +// #NOT STABLE on Android# Alias: Strategy that scale proportionally the container's size to window's size +// cc.ContainerStrategy.PROPORTION_TO_WINDOW = new ProportionalToWindow(); +// Alias: Strategy that makes the container's size equals to the frame's size + cc.ContainerStrategy.EQUAL_TO_FRAME = new EqualToFrame(); +// Alias: Strategy that scale proportionally the container's size to frame's size + cc.ContainerStrategy.PROPORTION_TO_FRAME = new ProportionalToFrame(); +// Alias: Strategy that keeps the original container's size + cc.ContainerStrategy.ORIGINAL_CONTAINER = new OriginalContainer(); + +// Content scale strategys + var ExactFit = cc.ContentStrategy.extend({ + apply: function (view, designedResolution) { + var containerW = cc._canvas.width, containerH = cc._canvas.height, + scaleX = containerW / designedResolution.width, scaleY = containerH / designedResolution.height; + + return this._buildResult(containerW, containerH, containerW, containerH, scaleX, scaleY); + } + }); + + var ShowAll = cc.ContentStrategy.extend({ + apply: function (view, designedResolution) { + var containerW = cc._canvas.width, containerH = cc._canvas.height, + designW = designedResolution.width, designH = designedResolution.height, + scaleX = containerW / designW, scaleY = containerH / designH, scale = 0, + contentW, contentH; + + scaleX < scaleY ? (scale = scaleX, contentW = containerW, contentH = designH * scale) + : (scale = scaleY, contentW = designW * scale, contentH = containerH); + + return this._buildResult(containerW, containerH, contentW, contentH, scale, scale); + } + }); + + var NoBorder = cc.ContentStrategy.extend({ + apply: function (view, designedResolution) { + var containerW = cc._canvas.width, containerH = cc._canvas.height, + designW = designedResolution.width, designH = designedResolution.height, + scaleX = containerW / designW, scaleY = containerH / designH, scale, + contentW, contentH; + + scaleX < scaleY ? (scale = scaleY, contentW = designW * scale, contentH = containerH) + : (scale = scaleX, contentW = containerW, contentH = designH * scale); + + return this._buildResult(containerW, containerH, contentW, contentH, scale, scale); + } + }); + + var FixedHeight = cc.ContentStrategy.extend({ + apply: function (view, designedResolution) { + var containerW = cc._canvas.width, containerH = cc._canvas.height, + designH = designedResolution.height, scale = containerH / designH, + contentW = containerW, contentH = containerH; + + return this._buildResult(containerW, containerH, contentW, contentH, scale, scale); + }, + + postApply: function (view) { + cc.director._winSizeInPoints = view.getVisibleSize(); + } + }); + + var FixedWidth = cc.ContentStrategy.extend({ + apply: function (view, designedResolution) { + var containerW = cc._canvas.width, containerH = cc._canvas.height, + designW = designedResolution.width, scale = containerW / designW, + contentW = containerW, contentH = containerH; + + return this._buildResult(containerW, containerH, contentW, contentH, scale, scale); + }, + + postApply: function (view) { + cc.director._winSizeInPoints = view.getVisibleSize(); + } + }); + +// Alias: Strategy to scale the content's size to container's size, non proportional + cc.ContentStrategy.EXACT_FIT = new ExactFit(); +// Alias: Strategy to scale the content's size proportionally to maximum size and keeps the whole content area to be visible + cc.ContentStrategy.SHOW_ALL = new ShowAll(); +// Alias: Strategy to scale the content's size proportionally to fill the whole container area + cc.ContentStrategy.NO_BORDER = new NoBorder(); +// Alias: Strategy to scale the content's height to container's height and proportionally scale its width + cc.ContentStrategy.FIXED_HEIGHT = new FixedHeight(); +// Alias: Strategy to scale the content's width to container's width and proportionally scale its height + cc.ContentStrategy.FIXED_WIDTH = new FixedWidth(); + +})(); + +/** + *

cc.ResolutionPolicy class is the root strategy class of scale strategy, + * its main task is to maintain the compatibility with Cocos2d-x

+ * + * @class + * @extends cc.Class + * @param {cc.ContainerStrategy} containerStg The container strategy + * @param {cc.ContentStrategy} contentStg The content strategy + */ +cc.ResolutionPolicy = cc.Class.extend(/** @lends cc.ResolutionPolicy# */{ + _containerStrategy: null, + _contentStrategy: null, + + /** + * Constructor of cc.ResolutionPolicy + * @param {cc.ContainerStrategy} containerStg + * @param {cc.ContentStrategy} contentStg + */ + ctor: function (containerStg, contentStg) { + this.setContainerStrategy(containerStg); + this.setContentStrategy(contentStg); + }, + + /** + * Manipulation before applying the resolution policy + * @param {cc.view} view The target view + */ + preApply: function (view) { + this._containerStrategy.preApply(view); + this._contentStrategy.preApply(view); + }, + + /** + * Function to apply this resolution policy + * The return value is {scale: [scaleX, scaleY], viewport: {cc.Rect}}, + * The target view can then apply these value to itself, it's preferred not to modify directly its private variables + * @param {cc.view} view The target view + * @param {cc.Size} designedResolution The user defined design resolution + * @return {object} An object contains the scale X/Y values and the viewport rect + */ + apply: function (view, designedResolution) { + this._containerStrategy.apply(view, designedResolution); + return this._contentStrategy.apply(view, designedResolution); + }, + + /** + * Manipulation after appyling the strategy + * @param {cc.view} view The target view + */ + postApply: function (view) { + this._containerStrategy.postApply(view); + this._contentStrategy.postApply(view); + }, + + /** + * Setup the container's scale strategy + * @param {cc.ContainerStrategy} containerStg + */ + setContainerStrategy: function (containerStg) { + if (containerStg instanceof cc.ContainerStrategy) + this._containerStrategy = containerStg; + }, + + /** + * Setup the content's scale strategy + * @param {cc.ContentStrategy} contentStg + */ + setContentStrategy: function (contentStg) { + if (contentStg instanceof cc.ContentStrategy) + this._contentStrategy = contentStg; + } +}); + +/** + * @memberOf cc.ResolutionPolicy# + * @name EXACT_FIT + * @constant + * @type Number + * @static + * The entire application is visible in the specified area without trying to preserve the original aspect ratio.
+ * Distortion can occur, and the application may appear stretched or compressed. + */ +cc.ResolutionPolicy.EXACT_FIT = 0; + +/** + * @memberOf cc.ResolutionPolicy# + * @name NO_BORDER + * @constant + * @type Number + * @static + * The entire application fills the specified area, without distortion but possibly with some cropping,
+ * while maintaining the original aspect ratio of the application. + */ +cc.ResolutionPolicy.NO_BORDER = 1; + +/** + * @memberOf cc.ResolutionPolicy# + * @name SHOW_ALL + * @constant + * @type Number + * @static + * The entire application is visible in the specified area without distortion while maintaining the original
+ * aspect ratio of the application. Borders can appear on two sides of the application. + */ +cc.ResolutionPolicy.SHOW_ALL = 2; + +/** + * @memberOf cc.ResolutionPolicy# + * @name FIXED_HEIGHT + * @constant + * @type Number + * @static + * The application takes the height of the design resolution size and modifies the width of the internal
+ * canvas so that it fits the aspect ratio of the device
+ * no distortion will occur however you must make sure your application works on different
+ * aspect ratios + */ +cc.ResolutionPolicy.FIXED_HEIGHT = 3; + +/** + * @memberOf cc.ResolutionPolicy# + * @name FIXED_WIDTH + * @constant + * @type Number + * @static + * The application takes the width of the design resolution size and modifies the height of the internal
+ * canvas so that it fits the aspect ratio of the device
+ * no distortion will occur however you must make sure your application works on different
+ * aspect ratios + */ +cc.ResolutionPolicy.FIXED_WIDTH = 4; + +/** + * @memberOf cc.ResolutionPolicy# + * @name UNKNOWN + * @constant + * @type Number + * @static + * Unknow policy + */ +cc.ResolutionPolicy.UNKNOWN = 5; diff --git a/frameworks/cocos2d-html5/cocos2d/core/platform/CCInputExtension.js b/frameworks/cocos2d-html5/cocos2d/core/platform/CCInputExtension.js new file mode 100644 index 0000000..e61f584 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/platform/CCInputExtension.js @@ -0,0 +1,139 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +var _p = cc.inputManager; + +/** + * whether enable accelerometer event + * @function + * @param {Boolean} isEnable + */ +_p.setAccelerometerEnabled = function(isEnable){ + var _t = this; + if(_t._accelEnabled === isEnable) + return; + + _t._accelEnabled = isEnable; + var scheduler = cc.director.getScheduler(); + if(_t._accelEnabled){ + _t._accelCurTime = 0; + _t._registerAccelerometerEvent(); + scheduler.scheduleUpdate(_t); + } else { + _t._accelCurTime = 0; + _t._unregisterAccelerometerEvent(); + scheduler.unscheduleUpdate(_t); + } +}; + +/** + * set accelerometer interval value + * @function + * @param {Number} interval + */ +_p.setAccelerometerInterval = function(interval){ + if (this._accelInterval !== interval) { + this._accelInterval = interval; + } +}; + +_p._registerKeyboardEvent = function(){ + cc._canvas.addEventListener("keydown", function (e) { + cc.eventManager.dispatchEvent(new cc.EventKeyboard(e.keyCode, true)); + e.stopPropagation(); + e.preventDefault(); + }, false); + cc._canvas.addEventListener("keyup", function (e) { + cc.eventManager.dispatchEvent(new cc.EventKeyboard(e.keyCode, false)); + e.stopPropagation(); + e.preventDefault(); + }, false); +}; + +_p._registerAccelerometerEvent = function(){ + var w = window, _t = this; + _t._acceleration = new cc.Acceleration(); + _t._accelDeviceEvent = w.DeviceMotionEvent || w.DeviceOrientationEvent; + + //TODO fix DeviceMotionEvent bug on QQ Browser version 4.1 and below. + if (cc.sys.browserType === cc.sys.BROWSER_TYPE_MOBILE_QQ) + _t._accelDeviceEvent = window.DeviceOrientationEvent; + + var _deviceEventType = (_t._accelDeviceEvent === w.DeviceMotionEvent) ? "devicemotion" : "deviceorientation"; + var ua = navigator.userAgent; + if (/Android/.test(ua) || (/Adr/.test(ua) && cc.sys.browserType === cc.BROWSER_TYPE_UC)) { + _t._minus = -1; + } + + _t.didAccelerateCallback = _t.didAccelerate.bind(_t); + w.addEventListener(_deviceEventType, _t.didAccelerateCallback, false); +}; + +_p._unregisterAccelerometerEvent = function () { + this._acceleration = null; + var _deviceEventType = (this._accelDeviceEvent === window.DeviceMotionEvent) ? "devicemotion" : "deviceorientation"; + window.removeEventListener(_deviceEventType, this.didAccelerateCallback, false); +}; + +_p.didAccelerate = function (eventData) { + var _t = this, w = window; + if (!_t._accelEnabled) + return; + + var mAcceleration = _t._acceleration; + + var x, y, z; + + if (_t._accelDeviceEvent === window.DeviceMotionEvent) { + var eventAcceleration = eventData["accelerationIncludingGravity"]; + x = _t._accelMinus * eventAcceleration.x * 0.1; + y = _t._accelMinus * eventAcceleration.y * 0.1; + z = eventAcceleration.z * 0.1; + } else { + x = (eventData["gamma"] / 90) * 0.981; + y = -(eventData["beta"] / 90) * 0.981; + z = (eventData["alpha"] / 90) * 0.981; + } + + mAcceleration.x = x; + mAcceleration.y = y; + mAcceleration.z = z; + + mAcceleration.timestamp = eventData.timeStamp || Date.now(); + + var tmpX = mAcceleration.x; + if(w.orientation === cc.UIInterfaceOrientationLandscapeRight){ + mAcceleration.x = -mAcceleration.y; + mAcceleration.y = tmpX; + }else if(w.orientation === cc.UIInterfaceOrientationLandscapeLeft){ + mAcceleration.x = mAcceleration.y; + mAcceleration.y = -tmpX; + }else if(w.orientation === cc.UIInterfaceOrientationPortraitUpsideDown){ + mAcceleration.x = -mAcceleration.x; + mAcceleration.y = -mAcceleration.y; + } +}; + +delete _p; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/cocos2d/core/platform/CCInputManager.js b/frameworks/cocos2d-html5/cocos2d/core/platform/CCInputManager.js new file mode 100644 index 0000000..270c535 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/platform/CCInputManager.js @@ -0,0 +1,630 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * ignore + */ + +/** + * @constant + * @type {number} + */ +cc.UIInterfaceOrientationLandscapeLeft = -90; +/** + * @constant + * @type {number} + */ +cc.UIInterfaceOrientationLandscapeRight = 90; +/** + * @constant + * @type {number} + */ +cc.UIInterfaceOrientationPortraitUpsideDown = 180; +/** + * @constant + * @type {number} + */ +cc.UIInterfaceOrientationPortrait = 0; + +/** + *

+ * This class manages all events of input. include: touch, mouse, accelerometer, keyboard
+ *

+ * @class + * @name cc.inputManager + */ +cc.inputManager = /** @lends cc.inputManager# */{ + TOUCH_TIMEOUT: 5000, + + _mousePressed: false, + + _isRegisterEvent: false, + + _preTouchPoint: cc.p(0, 0), + _prevMousePoint: cc.p(0, 0), + + _preTouchPool: [], + _preTouchPoolPointer: 0, + + _touches: [], + _touchesIntegerDict: {}, + + _indexBitsUsed: 0, + _maxTouches: 5, + + _accelEnabled: false, + _accelInterval: 1 / 30, + _accelMinus: 1, + _accelCurTime: 0, + _acceleration: null, + _accelDeviceEvent: null, + + _getUnUsedIndex: function () { + var temp = this._indexBitsUsed; + var now = cc.sys.now(); + + for (var i = 0; i < this._maxTouches; i++) { + if (!(temp & 0x00000001)) { + this._indexBitsUsed |= (1 << i); + return i; + } + else { + var touch = this._touches[i]; + if (now - touch._lastModified > this.TOUCH_TIMEOUT) { + this._removeUsedIndexBit(i); + delete this._touchesIntegerDict[touch.getID()]; + return i; + } + } + temp >>= 1; + } + + // all bits are used + return -1; + }, + + _removeUsedIndexBit: function (index) { + if (index < 0 || index >= this._maxTouches) + return; + + var temp = 1 << index; + temp = ~temp; + this._indexBitsUsed &= temp; + }, + + _glView: null, + + /** + * @function + * @param {Array} touches + */ + handleTouchesBegin: function (touches) { + var selTouch, index, curTouch, touchID, + handleTouches = [], locTouchIntDict = this._touchesIntegerDict, + now = cc.sys.now(); + for (var i = 0, len = touches.length; i < len; i++) { + selTouch = touches[i]; + touchID = selTouch.getID(); + index = locTouchIntDict[touchID]; + + if (index == null) { + var unusedIndex = this._getUnUsedIndex(); + if (unusedIndex === -1) { + cc.log(cc._LogInfos.inputManager_handleTouchesBegin, unusedIndex); + continue; + } + //curTouch = this._touches[unusedIndex] = selTouch; + curTouch = this._touches[unusedIndex] = new cc.Touch(selTouch._point.x, selTouch._point.y, selTouch.getID()); + curTouch._lastModified = now; + curTouch._setPrevPoint(selTouch._prevPoint); + locTouchIntDict[touchID] = unusedIndex; + handleTouches.push(curTouch); + } + } + if (handleTouches.length > 0) { + this._glView._convertTouchesWithScale(handleTouches); + var touchEvent = new cc.EventTouch(handleTouches); + touchEvent._eventCode = cc.EventTouch.EventCode.BEGAN; + cc.eventManager.dispatchEvent(touchEvent); + } + }, + + /** + * @function + * @param {Array} touches + */ + handleTouchesMove: function (touches) { + var selTouch, index, touchID, + handleTouches = [], locTouches = this._touches, + now = cc.sys.now(); + for (var i = 0, len = touches.length; i < len; i++) { + selTouch = touches[i]; + touchID = selTouch.getID(); + index = this._touchesIntegerDict[touchID]; + + if (index == null) { + //cc.log("if the index doesn't exist, it is an error"); + continue; + } + if (locTouches[index]) { + locTouches[index]._setPoint(selTouch._point); + locTouches[index]._setPrevPoint(selTouch._prevPoint); + locTouches[index]._lastModified = now; + handleTouches.push(locTouches[index]); + } + } + if (handleTouches.length > 0) { + this._glView._convertTouchesWithScale(handleTouches); + var touchEvent = new cc.EventTouch(handleTouches); + touchEvent._eventCode = cc.EventTouch.EventCode.MOVED; + cc.eventManager.dispatchEvent(touchEvent); + } + }, + + /** + * @function + * @param {Array} touches + */ + handleTouchesEnd: function (touches) { + var handleTouches = this.getSetOfTouchesEndOrCancel(touches); + if (handleTouches.length > 0) { + this._glView._convertTouchesWithScale(handleTouches); + var touchEvent = new cc.EventTouch(handleTouches); + touchEvent._eventCode = cc.EventTouch.EventCode.ENDED; + cc.eventManager.dispatchEvent(touchEvent); + } + }, + + /** + * @function + * @param {Array} touches + */ + handleTouchesCancel: function (touches) { + var handleTouches = this.getSetOfTouchesEndOrCancel(touches); + if (handleTouches.length > 0) { + this._glView._convertTouchesWithScale(handleTouches); + var touchEvent = new cc.EventTouch(handleTouches); + touchEvent._eventCode = cc.EventTouch.EventCode.CANCELLED; + cc.eventManager.dispatchEvent(touchEvent); + } + }, + + /** + * @function + * @param {Array} touches + * @returns {Array} + */ + getSetOfTouchesEndOrCancel: function (touches) { + var selTouch, index, touchID, handleTouches = [], locTouches = this._touches, locTouchesIntDict = this._touchesIntegerDict; + for (var i = 0, len = touches.length; i < len; i++) { + selTouch = touches[i]; + touchID = selTouch.getID(); + index = locTouchesIntDict[touchID]; + + if (index == null) { + continue; //cc.log("if the index doesn't exist, it is an error"); + } + if (locTouches[index]) { + locTouches[index]._setPoint(selTouch._point); + locTouches[index]._setPrevPoint(selTouch._prevPoint); + handleTouches.push(locTouches[index]); + this._removeUsedIndexBit(index); + delete locTouchesIntDict[touchID]; + } + } + return handleTouches; + }, + + /** + * @function + * @param {HTMLElement} element + * @return {Object} + */ + getHTMLElementPosition: function (element) { + var docElem = document.documentElement; + var win = window; + var box = null; + if (cc.isFunction(element.getBoundingClientRect)) { + box = element.getBoundingClientRect(); + } else { + box = { + left: 0, + top: 0, + width: parseInt(element.style.width), + height: parseInt(element.style.height) + }; + } + return { + left: box.left + win.pageXOffset - docElem.clientLeft, + top: box.top + win.pageYOffset - docElem.clientTop, + width: box.width, + height: box.height + }; + }, + + /** + * @function + * @param {cc.Touch} touch + * @return {cc.Touch} + */ + getPreTouch: function (touch) { + var preTouch = null; + var locPreTouchPool = this._preTouchPool; + var id = touch.getID(); + for (var i = locPreTouchPool.length - 1; i >= 0; i--) { + if (locPreTouchPool[i].getID() === id) { + preTouch = locPreTouchPool[i]; + break; + } + } + if (!preTouch) + preTouch = touch; + return preTouch; + }, + + /** + * @function + * @param {cc.Touch} touch + */ + setPreTouch: function (touch) { + var find = false; + var locPreTouchPool = this._preTouchPool; + var id = touch.getID(); + for (var i = locPreTouchPool.length - 1; i >= 0; i--) { + if (locPreTouchPool[i].getID() === id) { + locPreTouchPool[i] = touch; + find = true; + break; + } + } + if (!find) { + if (locPreTouchPool.length <= 50) { + locPreTouchPool.push(touch); + } else { + locPreTouchPool[this._preTouchPoolPointer] = touch; + this._preTouchPoolPointer = (this._preTouchPoolPointer + 1) % 50; + } + } + }, + + /** + * @function + * @param {Number} tx + * @param {Number} ty + * @param {cc.Point} pos + * @return {cc.Touch} + */ + getTouchByXY: function (tx, ty, pos) { + var locPreTouch = this._preTouchPoint; + var location = this._glView.convertToLocationInView(tx, ty, pos); + var touch = new cc.Touch(location.x, location.y); + touch._setPrevPoint(locPreTouch.x, locPreTouch.y); + locPreTouch.x = location.x; + locPreTouch.y = location.y; + return touch; + }, + + /** + * @function + * @param {cc.Point} location + * @param {cc.Point} pos + * @param {Number} eventType + * @returns {cc.EventMouse} + */ + getMouseEvent: function (location, pos, eventType) { + var locPreMouse = this._prevMousePoint; + this._glView._convertMouseToLocationInView(location, pos); + var mouseEvent = new cc.EventMouse(eventType); + mouseEvent.setLocation(location.x, location.y); + mouseEvent._setPrevCursor(locPreMouse.x, locPreMouse.y); + locPreMouse.x = location.x; + locPreMouse.y = location.y; + return mouseEvent; + }, + + /** + * @function + * @param {Touch} event + * @param {cc.Point} pos + * @return {cc.Point} + */ + getPointByEvent: function (event, pos) { + if (event.pageX != null) //not available in <= IE8 + return {x: event.pageX, y: event.pageY}; + + pos.left -= document.body.scrollLeft; + pos.top -= document.body.scrollTop; + return {x: event.clientX, y: event.clientY}; + }, + + /** + * @function + * @param {Touch} event + * @param {cc.Point} pos + * @returns {Array} + */ + getTouchesByEvent: function (event, pos) { + var touchArr = [], locView = this._glView; + var touch_event, touch, preLocation; + var locPreTouch = this._preTouchPoint; + + var length = event.changedTouches.length; + for (var i = 0; i < length; i++) { + touch_event = event.changedTouches[i]; + if (touch_event) { + var location; + if (cc.sys.BROWSER_TYPE_FIREFOX === cc.sys.browserType) + location = locView.convertToLocationInView(touch_event.pageX, touch_event.pageY, pos); + else + location = locView.convertToLocationInView(touch_event.clientX, touch_event.clientY, pos); + if (touch_event.identifier != null) { + touch = new cc.Touch(location.x, location.y, touch_event.identifier); + //use Touch Pool + preLocation = this.getPreTouch(touch).getLocation(); + touch._setPrevPoint(preLocation.x, preLocation.y); + this.setPreTouch(touch); + } else { + touch = new cc.Touch(location.x, location.y); + touch._setPrevPoint(locPreTouch.x, locPreTouch.y); + } + locPreTouch.x = location.x; + locPreTouch.y = location.y; + touchArr.push(touch); + } + } + return touchArr; + }, + + /** + * @function + * @param {HTMLElement} element + */ + registerSystemEvent: function (element) { + if (this._isRegisterEvent) return; + + var locView = this._glView = cc.view; + var selfPointer = this; + var supportMouse = ('mouse' in cc.sys.capabilities), supportTouches = ('touches' in cc.sys.capabilities); + + //HACK + // - At the same time to trigger the ontouch event and onmouse event + // - The function will execute 2 times + //The known browser: + // liebiao + // miui + // WECHAT + var prohibition = false; + if (cc.sys.isMobile) + prohibition = true; + + //register touch event + if (supportMouse) { + window.addEventListener('mousedown', function () { + selfPointer._mousePressed = true; + }, false); + + window.addEventListener('mouseup', function (event) { + if (prohibition) return; + var savePressed = selfPointer._mousePressed; + selfPointer._mousePressed = false; + + if (!savePressed) + return; + + var pos = selfPointer.getHTMLElementPosition(element); + var location = selfPointer.getPointByEvent(event, pos); + if (!cc.rectContainsPoint(new cc.Rect(pos.left, pos.top, pos.width, pos.height), location)) { + selfPointer.handleTouchesEnd([selfPointer.getTouchByXY(location.x, location.y, pos)]); + + var mouseEvent = selfPointer.getMouseEvent(location, pos, cc.EventMouse.UP); + mouseEvent.setButton(event.button); + cc.eventManager.dispatchEvent(mouseEvent); + } + }, false); + + //register canvas mouse event + element.addEventListener("mousedown", function (event) { + if (prohibition) return; + selfPointer._mousePressed = true; + + var pos = selfPointer.getHTMLElementPosition(element); + var location = selfPointer.getPointByEvent(event, pos); + + selfPointer.handleTouchesBegin([selfPointer.getTouchByXY(location.x, location.y, pos)]); + + var mouseEvent = selfPointer.getMouseEvent(location, pos, cc.EventMouse.DOWN); + mouseEvent.setButton(event.button); + cc.eventManager.dispatchEvent(mouseEvent); + + event.stopPropagation(); + event.preventDefault(); + element.focus(); + }, false); + + element.addEventListener("mouseup", function (event) { + if (prohibition) return; + selfPointer._mousePressed = false; + + var pos = selfPointer.getHTMLElementPosition(element); + var location = selfPointer.getPointByEvent(event, pos); + + selfPointer.handleTouchesEnd([selfPointer.getTouchByXY(location.x, location.y, pos)]); + + var mouseEvent = selfPointer.getMouseEvent(location, pos, cc.EventMouse.UP); + mouseEvent.setButton(event.button); + cc.eventManager.dispatchEvent(mouseEvent); + + event.stopPropagation(); + event.preventDefault(); + }, false); + + element.addEventListener("mousemove", function (event) { + if (prohibition) return; + + var pos = selfPointer.getHTMLElementPosition(element); + var location = selfPointer.getPointByEvent(event, pos); + + selfPointer.handleTouchesMove([selfPointer.getTouchByXY(location.x, location.y, pos)]); + + var mouseEvent = selfPointer.getMouseEvent(location, pos, cc.EventMouse.MOVE); + if (selfPointer._mousePressed) + mouseEvent.setButton(event.button); + else + mouseEvent.setButton(null); + cc.eventManager.dispatchEvent(mouseEvent); + + event.stopPropagation(); + event.preventDefault(); + }, false); + + element.addEventListener("mousewheel", function (event) { + var pos = selfPointer.getHTMLElementPosition(element); + var location = selfPointer.getPointByEvent(event, pos); + + var mouseEvent = selfPointer.getMouseEvent(location, pos, cc.EventMouse.SCROLL); + mouseEvent.setButton(event.button); + mouseEvent.setScrollData(0, event.wheelDelta); + cc.eventManager.dispatchEvent(mouseEvent); + + event.stopPropagation(); + event.preventDefault(); + }, false); + + /* firefox fix */ + element.addEventListener("DOMMouseScroll", function (event) { + var pos = selfPointer.getHTMLElementPosition(element); + var location = selfPointer.getPointByEvent(event, pos); + + var mouseEvent = selfPointer.getMouseEvent(location, pos, cc.EventMouse.SCROLL); + mouseEvent.setButton(event.button); + mouseEvent.setScrollData(0, event.detail * -120); + cc.eventManager.dispatchEvent(mouseEvent); + + event.stopPropagation(); + event.preventDefault(); + }, false); + } + + if (window.navigator.msPointerEnabled) { + var _pointerEventsMap = { + "MSPointerDown": selfPointer.handleTouchesBegin, + "MSPointerMove": selfPointer.handleTouchesMove, + "MSPointerUp": selfPointer.handleTouchesEnd, + "MSPointerCancel": selfPointer.handleTouchesCancel + }; + + for (var eventName in _pointerEventsMap) { + (function (_pointerEvent, _touchEvent) { + element.addEventListener(_pointerEvent, function (event) { + var pos = selfPointer.getHTMLElementPosition(element); + pos.left -= document.documentElement.scrollLeft; + pos.top -= document.documentElement.scrollTop; + + _touchEvent.call(selfPointer, [selfPointer.getTouchByXY(event.clientX, event.clientY, pos)]); + event.stopPropagation(); + }, false); + })(eventName, _pointerEventsMap[eventName]); + } + } + + if (supportTouches) { + //register canvas touch event + element.addEventListener("touchstart", function (event) { + if (!event.changedTouches) return; + + var pos = selfPointer.getHTMLElementPosition(element); + pos.left -= document.body.scrollLeft; + pos.top -= document.body.scrollTop; + selfPointer.handleTouchesBegin(selfPointer.getTouchesByEvent(event, pos)); + event.stopPropagation(); + event.preventDefault(); + element.focus(); + }, false); + + element.addEventListener("touchmove", function (event) { + if (!event.changedTouches) return; + + var pos = selfPointer.getHTMLElementPosition(element); + pos.left -= document.body.scrollLeft; + pos.top -= document.body.scrollTop; + selfPointer.handleTouchesMove(selfPointer.getTouchesByEvent(event, pos)); + event.stopPropagation(); + event.preventDefault(); + }, false); + + element.addEventListener("touchend", function (event) { + if (!event.changedTouches) return; + + var pos = selfPointer.getHTMLElementPosition(element); + pos.left -= document.body.scrollLeft; + pos.top -= document.body.scrollTop; + selfPointer.handleTouchesEnd(selfPointer.getTouchesByEvent(event, pos)); + event.stopPropagation(); + event.preventDefault(); + }, false); + + element.addEventListener("touchcancel", function (event) { + if (!event.changedTouches) return; + + var pos = selfPointer.getHTMLElementPosition(element); + pos.left -= document.body.scrollLeft; + pos.top -= document.body.scrollTop; + selfPointer.handleTouchesCancel(selfPointer.getTouchesByEvent(event, pos)); + event.stopPropagation(); + event.preventDefault(); + }, false); + } + + //register keyboard event + this._registerKeyboardEvent(); + + //register Accelerometer event + // this._registerAccelerometerEvent(); + + this._isRegisterEvent = true; + }, + + _registerKeyboardEvent: function () { + }, + + /** + * Register Accelerometer event + * @function + */ + _registerAccelerometerEvent: function () { + }, + + /** + * @function + * @param {Number} dt + */ + update: function (dt) { + if (this._accelCurTime > this._accelInterval) { + this._accelCurTime -= this._accelInterval; + cc.eventManager.dispatchEvent(new cc.EventAcceleration(this._acceleration)); + } + this._accelCurTime += dt; + } +}; diff --git a/frameworks/cocos2d-html5/cocos2d/core/platform/CCLoaders.js b/frameworks/cocos2d-html5/cocos2d/core/platform/CCLoaders.js new file mode 100644 index 0000000..16749a0 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/platform/CCLoaders.js @@ -0,0 +1,163 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +cc._txtLoader = { + load: function (realUrl, url, res, cb) { + cc.loader.loadTxt(realUrl, cb); + } +}; +cc.loader.register(["txt", "xml", "vsh", "fsh", "atlas"], cc._txtLoader); + +cc._jsonLoader = { + load: function (realUrl, url, res, cb) { + cc.loader.loadJson(realUrl, cb); + } +}; +cc.loader.register(["json", "ExportJson"], cc._jsonLoader); + +cc._jsLoader = { + load: function (realUrl, url, res, cb) { + cc.loader.loadJs(realUrl, cb); + } +}; +cc.loader.register(["js"], cc._jsLoader); + +cc._imgLoader = { + load: function (realUrl, url, res, cb) { + var callback; + if (cc.loader.isLoading(realUrl)) { + callback = function (err, img) { + if (err) + return cb(err); + var tex = cc.textureCache.getTextureForKey(url) || cc.textureCache.handleLoadedTexture(url, img); + cb(null, tex); + }; + } + else { + callback = function (err, img) { + if (err) + return cb(err); + var tex = cc.textureCache.handleLoadedTexture(url, img); + cb(null, tex); + }; + } + cc.loader.loadImg(realUrl, callback); + } +}; +cc.loader.register(["png", "jpg", "bmp", "jpeg", "gif", "ico", "tiff", "webp"], cc._imgLoader); +cc._serverImgLoader = { + load: function (realUrl, url, res, cb) { + cc._imgLoader.load(res.src, url, res, cb); + } +}; +cc.loader.register(["serverImg"], cc._serverImgLoader); + +cc._plistLoader = { + load: function (realUrl, url, res, cb) { + cc.loader.loadTxt(realUrl, function (err, txt) { + if (err) + return cb(err); + cb(null, cc.plistParser.parse(txt)); + }); + } +}; +cc.loader.register(["plist"], cc._plistLoader); + +cc._fontLoader = { + TYPE: { + ".eot": "embedded-opentype", + ".ttf": "truetype", + ".ttc": "truetype", + ".woff": "woff", + ".svg": "svg" + }, + _loadFont: function (name, srcs, type) { + var doc = document, path = cc.path, TYPE = this.TYPE, fontStyle = document.createElement("style"); + fontStyle.type = "text/css"; + doc.body.appendChild(fontStyle); + + var fontStr = ""; + if (isNaN(name - 0)) + fontStr += "@font-face { font-family:" + name + "; src:"; + else + fontStr += "@font-face { font-family:'" + name + "'; src:"; + if (srcs instanceof Array) { + for (var i = 0, li = srcs.length; i < li; i++) { + var src = srcs[i]; + type = path.extname(src).toLowerCase(); + fontStr += "url('" + srcs[i] + "') format('" + TYPE[type] + "')"; + fontStr += (i === li - 1) ? ";" : ","; + } + } else { + type = type.toLowerCase(); + fontStr += "url('" + srcs + "') format('" + TYPE[type] + "');"; + } + fontStyle.textContent += fontStr + "}"; + + //
.
+ var preloadDiv = document.createElement("div"); + var _divStyle = preloadDiv.style; + _divStyle.fontFamily = name; + preloadDiv.innerHTML = "."; + _divStyle.position = "absolute"; + _divStyle.left = "-100px"; + _divStyle.top = "-100px"; + doc.body.appendChild(preloadDiv); + }, + load: function (realUrl, url, res, cb) { + var self = this; + var type = res.type, name = res.name, srcs = res.srcs; + if (cc.isString(res)) { + type = cc.path.extname(res); + name = cc.path.basename(res, type); + self._loadFont(name, res, type); + } else { + self._loadFont(name, srcs); + } + if (document.fonts) { + document.fonts.load("1em " + name).then(function () { + cb(null, true); + }, function (err) { + cb(err); + }); + } else { + cb(null, true); + } + } +}; +cc.loader.register(["font", "eot", "ttf", "woff", "svg", "ttc"], cc._fontLoader); + +cc._binaryLoader = { + load: function (realUrl, url, res, cb) { + cc.loader.loadBinary(realUrl, cb); + } +}; + +cc._csbLoader = { + load: function(realUrl, url, res, cb){ + cc.loader.loadCsb(realUrl, cb); + } +}; +cc.loader.register(["csb"], cc._csbLoader); diff --git a/frameworks/cocos2d-html5/cocos2d/core/platform/CCMacro.js b/frameworks/cocos2d-html5/cocos2d/core/platform/CCMacro.js new file mode 100644 index 0000000..a52958c --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/platform/CCMacro.js @@ -0,0 +1,857 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * @constant + * @type Number + */ +cc.INVALID_INDEX = -1; + +/** + * PI is the ratio of a circle's circumference to its diameter. + * @constant + * @type Number + */ +cc.PI = Math.PI; + +/** + * @constant + * @type Number + */ +cc.FLT_MAX = parseFloat('3.402823466e+38F'); + +/** + * @constant + * @type Number + */ +cc.FLT_MIN = parseFloat("1.175494351e-38F"); + +/** + * @constant + * @type Number + */ +cc.RAD = cc.PI / 180; + +/** + * @constant + * @type Number + */ +cc.DEG = 180 / cc.PI; + +/** + * maximum unsigned int value + * @constant + * @type Number + */ +cc.UINT_MAX = 0xffffffff; + +/** + *

+ * simple macro that swaps 2 variables
+ * modified from c++ macro, you need to pass in the x and y variables names in string,
+ * and then a reference to the whole object as third variable + *

+ * @param {String} x + * @param {String} y + * @param {Object} ref + * @function + * @deprecated since v3.0 + */ +cc.swap = function (x, y, ref) { + if (cc.isObject(ref) && !cc.isUndefined(ref.x) && !cc.isUndefined(ref.y)) { + var tmp = ref[x]; + ref[x] = ref[y]; + ref[y] = tmp; + } else + cc.log(cc._LogInfos.swap); +}; + +/** + *

+ * Linear interpolation between 2 numbers, the ratio sets how much it is biased to each end + *

+ * @param {Number} a number A + * @param {Number} b number B + * @param {Number} r ratio between 0 and 1 + * @function + * @example + * cc.lerp(2,10,0.5)//returns 6
+ * cc.lerp(2,10,0.2)//returns 3.6 + */ +cc.lerp = function (a, b, r) { + return a + (b - a) * r; +}; + +/** + * get a random number from 0 to 0xffffff + * @function + * @returns {number} + */ +cc.rand = function () { + return Math.random() * 0xffffff; +}; + +/** + * returns a random float between -1 and 1 + * @return {Number} + * @function + */ +cc.randomMinus1To1 = function () { + return (Math.random() - 0.5) * 2; +}; + +/** + * returns a random float between 0 and 1 + * @return {Number} + * @function + */ +cc.random0To1 = Math.random; + +/** + * converts degrees to radians + * @param {Number} angle + * @return {Number} + * @function + */ +cc.degreesToRadians = function (angle) { + return angle * cc.RAD; +}; + +/** + * converts radians to degrees + * @param {Number} angle + * @return {Number} + * @function + */ +cc.radiansToDegrees = function (angle) { + return angle * cc.DEG; +}; +/** + * converts radians to degrees + * @param {Number} angle + * @return {Number} + * @function + */ +cc.radiansToDegress = function (angle) { + cc.log(cc._LogInfos.radiansToDegress); + return angle * cc.DEG; +}; + +/** + * @constant + * @type Number + */ +cc.REPEAT_FOREVER = Number.MAX_VALUE - 1; + +/** + * Helpful macro that setups the GL server state, the correct GL program and sets the Model View Projection matrix + * @param {cc.Node} node setup node + * @function + */ +cc.nodeDrawSetup = function (node) { + //cc.glEnable(node._glServerState); + if (node._shaderProgram) { + //cc._renderContext.useProgram(node._shaderProgram._programObj); + node._glProgramState.apply(); + node._shaderProgram.setUniformForModelViewAndProjectionMatrixWithMat4(); + } +}; + +/** + *

+ * GL states that are enabled:
+ * - GL_TEXTURE_2D
+ * - GL_VERTEX_ARRAY
+ * - GL_TEXTURE_COORD_ARRAY
+ * - GL_COLOR_ARRAY
+ *

+ * @function + */ +cc.enableDefaultGLStates = function () { + //TODO OPENGL STUFF + /* + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnable(GL_TEXTURE_2D);*/ +}; + +/** + *

+ * Disable default GL states:
+ * - GL_TEXTURE_2D
+ * - GL_TEXTURE_COORD_ARRAY
+ * - GL_COLOR_ARRAY
+ *

+ * @function + */ +cc.disableDefaultGLStates = function () { + //TODO OPENGL + /* + glDisable(GL_TEXTURE_2D); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + */ +}; + +/** + *

+ * Increments the GL Draws counts by one.
+ * The number of calls per frame are displayed on the screen when the CCDirector's stats are enabled.
+ *

+ * @param {Number} addNumber + * @function + */ +cc.incrementGLDraws = function (addNumber) { + cc.g_NumberOfDraws += addNumber; +}; + +/** + * @constant + * @type Number + */ +cc.FLT_EPSILON = 0.0000001192092896; + +/** + *

+ * On Mac it returns 1;
+ * On iPhone it returns 2 if RetinaDisplay is On. Otherwise it returns 1 + *

+ * @return {Number} + * @function + */ +cc.contentScaleFactor = function () { + return cc.director._contentScaleFactor; +}; + +/** + * Converts a Point in points to pixels + * @param {cc.Point} points + * @return {cc.Point} + * @function + */ +cc.pointPointsToPixels = function (points) { + var scale = cc.contentScaleFactor(); + return cc.p(points.x * scale, points.y * scale); +}; + +/** + * Converts a Point in pixels to points + * @param {cc.Rect} pixels + * @return {cc.Point} + * @function + */ +cc.pointPixelsToPoints = function (pixels) { + var scale = cc.contentScaleFactor(); + return cc.p(pixels.x / scale, pixels.y / scale); +}; + +cc._pointPixelsToPointsOut = function(pixels, outPoint){ + var scale = cc.contentScaleFactor(); + outPoint.x = pixels.x / scale; + outPoint.y = pixels.y / scale; +}; + +/** + * Converts a Size in points to pixels + * @param {cc.Size} sizeInPoints + * @return {cc.Size} + * @function + */ +cc.sizePointsToPixels = function (sizeInPoints) { + var scale = cc.contentScaleFactor(); + return cc.size(sizeInPoints.width * scale, sizeInPoints.height * scale); +}; + +/** + * Converts a size in pixels to points + * @param {cc.Size} sizeInPixels + * @return {cc.Size} + * @function + */ +cc.sizePixelsToPoints = function (sizeInPixels) { + var scale = cc.contentScaleFactor(); + return cc.size(sizeInPixels.width / scale, sizeInPixels.height / scale); +}; + +cc._sizePixelsToPointsOut = function (sizeInPixels, outSize) { + var scale = cc.contentScaleFactor(); + outSize.width = sizeInPixels.width / scale; + outSize.height = sizeInPixels.height / scale; +}; + +/** + * Converts a rect in pixels to points + * @param {cc.Rect} pixel + * @return {cc.Rect} + * @function + */ +cc.rectPixelsToPoints = function (pixel) { + var scale = cc.contentScaleFactor(); + return cc.rect(pixel.x / scale, pixel.y / scale, + pixel.width / scale, pixel.height / scale); +}; + +/** + * Converts a rect in points to pixels + * @param {cc.Rect} point + * @return {cc.Rect} + * @function + */ +cc.rectPointsToPixels = function (point) { + var scale = cc.contentScaleFactor(); + return cc.rect(point.x * scale, point.y * scale, + point.width * scale, point.height * scale); +}; + +//some gl constant variable +/** + * @constant + * @type Number + */ +cc.ONE = 1; + +/** + * @constant + * @type Number + */ +cc.ZERO = 0; + +/** + * @constant + * @type Number + */ +cc.SRC_ALPHA = 0x0302; + +/** + * @constant + * @type Number + */ +cc.SRC_ALPHA_SATURATE = 0x308; + +/** + * @constant + * @type Number + */ +cc.SRC_COLOR = 0x300; + +/** + * @constant + * @type Number + */ +cc.DST_ALPHA = 0x304; + +/** + * @constant + * @type Number + */ +cc.DST_COLOR = 0x306; + +/** + * @constant + * @type Number + */ +cc.ONE_MINUS_SRC_ALPHA = 0x0303; + +/** + * @constant + * @type Number + */ +cc.ONE_MINUS_SRC_COLOR = 0x301; + +/** + * @constant + * @type Number + */ +cc.ONE_MINUS_DST_ALPHA = 0x305; + +/** + * @constant + * @type Number + */ +cc.ONE_MINUS_DST_COLOR = 0x0307; + +/** + * @constant + * @type Number + */ +cc.ONE_MINUS_CONSTANT_ALPHA = 0x8004; + +/** + * @constant + * @type Number + */ +cc.ONE_MINUS_CONSTANT_COLOR = 0x8002; + +/** + * the constant variable equals gl.LINEAR for texture + * @constant + * @type Number + */ +cc.LINEAR = 0x2601; + +/** + * the constant variable equals gl.REPEAT for texture + * @constant + * @type Number + */ +cc.REPEAT = 0x2901; + +/** + * the constant variable equals gl.CLAMP_TO_EDGE for texture + * @constant + * @type Number + */ +cc.CLAMP_TO_EDGE = 0x812f; + +/** + * the constant variable equals gl.MIRRORED_REPEAT for texture + * @constant + * @type Number + */ +cc.MIRRORED_REPEAT = 0x8370; + +/** + * default gl blend src function. Compatible with premultiplied alpha images. + * @constant + * @name cc.BLEND_SRC + * @type Number + */ +cc.BLEND_SRC = cc.SRC_ALPHA; +cc.game.addEventListener(cc.game.EVENT_RENDERER_INITED, function () { + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL + && cc.OPTIMIZE_BLEND_FUNC_FOR_PREMULTIPLIED_ALPHA) { + cc.BLEND_SRC = cc.ONE; + } +}); + +/** + * default gl blend dst function. Compatible with premultiplied alpha images. + * @constant + * @type Number + */ +cc.BLEND_DST = cc.ONE_MINUS_SRC_ALPHA; + +/** + * Check webgl error.Error will be shown in console if exists. + * @function + */ +cc.checkGLErrorDebug = function () { + if (cc.renderMode === cc.game.RENDER_TYPE_WEBGL) { + var _error = cc._renderContext.getError(); + if (_error) { + cc.log(cc._LogInfos.checkGLErrorDebug, _error); + } + } +}; + +//Possible device orientations +/** + * Device oriented vertically, home button on the bottom (UIDeviceOrientationPortrait) + * @constant + * @type Number + */ +cc.ORIENTATION_PORTRAIT = 1; + +/** + * Device oriented horizontally, home button on the right (UIDeviceOrientationLandscapeLeft) + * @constant + * @type Number + */ +cc.ORIENTATION_LANDSCAPE = 2; + +/** + * Device oriented vertically, home button on the top (UIDeviceOrientationPortraitUpsideDown) + * @constant + * @type Number + */ +cc.ORIENTATION_AUTO = 3; + +/** + * The limit count for concurrency http request, useful in some mobile browsers + * Adjust its value with the test results based on your game, the preset value is just a placeholder + * @constant + * @type Number + */ +cc.CONCURRENCY_HTTP_REQUEST_COUNT = cc.sys.isMobile ? 20 : 0; + +/** + * The maximum vertex count for a single batched draw call. + * @constant + * @type Number + */ +cc.BATCH_VERTEX_COUNT = 2000; + + +// ------------------- vertex attrib flags ----------------------------- +/** + * @constant + * @type {Number} + */ +cc.VERTEX_ATTRIB_FLAG_NONE = 0; +/** + * @constant + * @type {Number} + */ +cc.VERTEX_ATTRIB_FLAG_POSITION = 1 << 0; +/** + * @constant + * @type {Number} + */ +cc.VERTEX_ATTRIB_FLAG_COLOR = 1 << 1; +/** + * @constant + * @type {Number} + */ +cc.VERTEX_ATTRIB_FLAG_TEX_COORDS = 1 << 2; +/** + * @constant + * @type {Number} + */ +cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX = ( cc.VERTEX_ATTRIB_FLAG_POSITION | cc.VERTEX_ATTRIB_FLAG_COLOR | cc.VERTEX_ATTRIB_FLAG_TEX_COORDS ); + +/** + * GL server side states + * @constant + * @type {Number} + */ +cc.GL_ALL = 0; + +//-------------Vertex Attributes----------- +/** + * @constant + * @type {Number} + */ +cc.VERTEX_ATTRIB_POSITION = 0; +/** + * @constant + * @type {Number} + */ +cc.VERTEX_ATTRIB_COLOR = 1; +/** + * @constant + * @type {Number} + */ +cc.VERTEX_ATTRIB_TEX_COORDS = 2; +/** + * @constant + * @type {Number} + */ +cc.VERTEX_ATTRIB_MAX = 7; + +//------------Uniforms------------------ +/** + * @constant + * @type {Number} + */ +cc.UNIFORM_PMATRIX = 0; +/** + * @constant + * @type {Number} + */ +cc.UNIFORM_MVMATRIX = 1; +/** + * @constant + * @type {Number} + */ +cc.UNIFORM_MVPMATRIX = 2; +/** + * @constant + * @type {Number} + */ +cc.UNIFORM_TIME = 3; +/** + * @constant + * @type {Number} + */ +cc.UNIFORM_SINTIME = 4; +/** + * @constant + * @type {Number} + */ +cc.UNIFORM_COSTIME = 5; +/** + * @constant + * @type {Number} + */ +cc.UNIFORM_RANDOM01 = 6; +/** + * @constant + * @type {Number} + */ +cc.UNIFORM_SAMPLER = 7; +/** + * @constant + * @type {Number} + */ +cc.UNIFORM_MAX = 8; + +//------------Shader Name--------------- +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_TEXTURECOLOR = "ShaderPositionTextureColor"; +/** + * @constant + * @type {String} + */ +cc.SHADER_SPRITE_POSITION_TEXTURECOLOR = "ShaderSpritePositionTextureColor"; +/** + * @constant + * @type {String} + */ +cc.SHADER_SPRITE_POSITION_TEXTURECOLOR_GRAY = "ShaderSpritePositionTextureColorGray"; +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_TEXTURECOLORALPHATEST = "ShaderPositionTextureColorAlphaTest"; +/** + * @constant + * @type {String} + */ +cc.SHADER_SPRITE_POSITION_TEXTURECOLORALPHATEST = "ShaderSpritePositionTextureColorAlphaTest"; +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_COLOR = "ShaderPositionColor"; +/** + * @constant + * @type {String} + */ +cc.SHADER_SPRITE_POSITION_COLOR = "ShaderSpritePositionColor"; +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_TEXTURE = "ShaderPositionTexture"; +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_TEXTURE_UCOLOR = "ShaderPositionTextureUColor"; +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_TEXTUREA8COLOR = "ShaderPositionTextureA8Color"; +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_UCOLOR = "ShaderPositionUColor"; +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_LENGTHTEXTURECOLOR = "ShaderPositionLengthTextureColor"; + +//------------uniform names---------------- +/** + * @constant + * @type {String} + */ +cc.UNIFORM_PMATRIX_S = "CC_PMatrix"; +/** + * @constant + * @type {String} + */ +cc.UNIFORM_MVMATRIX_S = "CC_MVMatrix"; +/** + * @constant + * @type {String} + */ +cc.UNIFORM_MVPMATRIX_S = "CC_MVPMatrix"; +/** + * @constant + * @type {String} + */ +cc.UNIFORM_TIME_S = "CC_Time"; +/** + * @constant + * @type {String} + */ +cc.UNIFORM_SINTIME_S = "CC_SinTime"; +/** + * @constant + * @type {String} + */ +cc.UNIFORM_COSTIME_S = "CC_CosTime"; +/** + * @constant + * @type {String} + */ +cc.UNIFORM_RANDOM01_S = "CC_Random01"; +/** + * @constant + * @type {String} + */ +cc.UNIFORM_SAMPLER_S = "CC_Texture0"; +/** + * @constant + * @type {String} + */ +cc.UNIFORM_ALPHA_TEST_VALUE_S = "CC_alpha_value"; + +//------------Attribute names-------------- +/** + * @constant + * @type {String} + */ +cc.ATTRIBUTE_NAME_COLOR = "a_color"; +/** + * @constant + * @type {String} + */ +cc.ATTRIBUTE_NAME_POSITION = "a_position"; +/** + * @constant + * @type {String} + */ +cc.ATTRIBUTE_NAME_TEX_COORD = "a_texCoord"; +/** + * @constant + * @type {String} + */ +cc.ATTRIBUTE_NAME_MVMAT = "a_mvMatrix"; + + +/** + * default size for font size + * @constant + * @type Number + */ +cc.ITEM_SIZE = 32; + +/** + * default tag for current item + * @constant + * @type Number + */ +cc.CURRENT_ITEM = 0xc0c05001; +/** + * default tag for zoom action tag + * @constant + * @type Number + */ +cc.ZOOM_ACTION_TAG = 0xc0c05002; +/** + * default tag for normal + * @constant + * @type Number + */ +cc.NORMAL_TAG = 8801; + +/** + * default selected tag + * @constant + * @type Number + */ +cc.SELECTED_TAG = 8802; + +/** + * default disabled tag + * @constant + * @type Number + */ +cc.DISABLE_TAG = 8803; + + +// Array utils + +/** + * Verify Array's Type + * @param {Array} arr + * @param {function} type + * @return {Boolean} + * @function + */ +cc.arrayVerifyType = function (arr, type) { + if (arr && arr.length > 0) { + for (var i = 0; i < arr.length; i++) { + if (!(arr[i] instanceof type)) { + cc.log("element type is wrong!"); + return false; + } + } + } + return true; +}; + +/** + * Searches for the first occurrence of object and removes it. If object is not found the function has no effect. + * @function + * @param {Array} arr Source Array + * @param {*} delObj remove object + */ +cc.arrayRemoveObject = function (arr, delObj) { + for (var i = 0, l = arr.length; i < l; i++) { + if (arr[i] === delObj) { + arr.splice(i, 1); + break; + } + } +}; + +/** + * Removes from arr all values in minusArr. For each Value in minusArr, the first matching instance in arr will be removed. + * @function + * @param {Array} arr Source Array + * @param {Array} minusArr minus Array + */ +cc.arrayRemoveArray = function (arr, minusArr) { + for (var i = 0, l = minusArr.length; i < l; i++) { + cc.arrayRemoveObject(arr, minusArr[i]); + } +}; + +/** + * Inserts some objects at index + * @function + * @param {Array} arr + * @param {Array} addObjs + * @param {Number} index + * @return {Array} + */ +cc.arrayAppendObjectsToIndex = function(arr, addObjs,index){ + arr.splice.apply(arr, [index, 0].concat(addObjs)); + return arr; +}; + +/** + * Copy an array's item to a new array (its performance is better than Array.slice) + * @param {Array} arr + * @return {Array} + */ +cc.copyArray = function(arr){ + var i, len = arr.length, arr_clone = new Array(len); + for (i = 0; i < len; i += 1) + arr_clone[i] = arr[i]; + return arr_clone; +}; diff --git a/frameworks/cocos2d-html5/cocos2d/core/platform/CCSAXParser.js b/frameworks/cocos2d-html5/cocos2d/core/platform/CCSAXParser.js new file mode 100644 index 0000000..f504962 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/platform/CCSAXParser.js @@ -0,0 +1,170 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * A SAX Parser + * @class + * @name cc.saxParser + * @extends cc.Class + */ +cc.SAXParser = cc.Class.extend(/** @lends cc.saxParser# */{ + _parser: null, + _isSupportDOMParser: null, + + /** + * Constructor of cc.SAXParser + */ + ctor: function () { + if (window.DOMParser) { + this._isSupportDOMParser = true; + this._parser = new DOMParser(); + } else { + this._isSupportDOMParser = false; + } + }, + + /** + * @function + * @param {String} xmlTxt + * @return {Document} + */ + parse : function(xmlTxt){ + return this._parseXML(xmlTxt); + }, + + _parseXML: function (textxml) { + // get a reference to the requested corresponding xml file + var xmlDoc; + if (this._isSupportDOMParser) { + xmlDoc = this._parser.parseFromString(textxml, "text/xml"); + } else { + // Internet Explorer (untested!) + xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); + xmlDoc.async = "false"; + xmlDoc.loadXML(textxml); + } + return xmlDoc; + } + +}); + +/** + * + * cc.plistParser is a singleton object for parsing plist files + * @class + * @name cc.plistParser + * @extends cc.SAXParser + */ +cc.PlistParser = cc.SAXParser.extend(/** @lends cc.plistParser# */{ + + /** + * parse a xml string as plist object. + * @param {String} xmlTxt plist xml contents + * @return {*} plist object + */ + parse : function (xmlTxt) { + var xmlDoc = this._parseXML(xmlTxt); + var plist = xmlDoc.documentElement; + if (plist.tagName !== 'plist') { + cc.warn("Not a plist file!"); + return {}; + } + + // Get first real node + var node = null; + for (var i = 0, len = plist.childNodes.length; i < len; i++) { + node = plist.childNodes[i]; + if (node.nodeType === 1) + break; + } + xmlDoc = null; + return this._parseNode(node); + }, + + _parseNode: function (node) { + var data = null, tagName = node.tagName; + if(tagName === "dict"){ + data = this._parseDict(node); + }else if(tagName === "array"){ + data = this._parseArray(node); + }else if(tagName === "string"){ + if (node.childNodes.length === 1) + data = node.firstChild.nodeValue; + else { + //handle Firefox's 4KB nodeValue limit + data = ""; + for (var i = 0; i < node.childNodes.length; i++) + data += node.childNodes[i].nodeValue; + } + }else if(tagName === "false"){ + data = false; + }else if(tagName === "true"){ + data = true; + }else if(tagName === "real"){ + data = parseFloat(node.firstChild.nodeValue); + }else if(tagName === "integer"){ + data = parseInt(node.firstChild.nodeValue, 10); + } + return data; + }, + + _parseArray: function (node) { + var data = []; + for (var i = 0, len = node.childNodes.length; i < len; i++) { + var child = node.childNodes[i]; + if (child.nodeType !== 1) + continue; + data.push(this._parseNode(child)); + } + return data; + }, + + _parseDict: function (node) { + var data = {}; + var key = null; + for (var i = 0, len = node.childNodes.length; i < len; i++) { + var child = node.childNodes[i]; + if (child.nodeType !== 1) + continue; + + // Grab the key, next noe should be the value + if (child.tagName === 'key') + key = child.firstChild.nodeValue; + else + data[key] = this._parseNode(child); // Parse the value node + } + return data; + } +}); + +cc.saxParser = new cc.SAXParser(); +/** + * A Plist Parser + * @type {cc.PlistParser} + * @name plistParser + * @memberof cc + */ +cc.plistParser = new cc.PlistParser(); diff --git a/frameworks/cocos2d-html5/cocos2d/core/platform/CCScreen.js b/frameworks/cocos2d-html5/cocos2d/core/platform/CCScreen.js new file mode 100644 index 0000000..8bc1e4d --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/platform/CCScreen.js @@ -0,0 +1,161 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The fullscreen API provides an easy way for web content to be presented using the user's entire screen. + * It's invalid on safari, QQbrowser and android browser + * @class + * @name cc.screen + */ +cc.screen = /** @lends cc.screen# */{ + _supportsFullScreen: false, + // the pre fullscreenchange function + _preOnFullScreenChange: null, + _touchEvent: "", + _fn: null, + // Function mapping for cross browser support + _fnMap: [ + [ + 'requestFullscreen', + 'exitFullscreen', + 'fullscreenchange', + 'fullscreenEnabled', + 'fullscreenElement' + ], + [ + 'requestFullScreen', + 'exitFullScreen', + 'fullScreenchange', + 'fullScreenEnabled', + 'fullScreenElement' + ], + [ + 'webkitRequestFullScreen', + 'webkitCancelFullScreen', + 'webkitfullscreenchange', + 'webkitIsFullScreen', + 'webkitCurrentFullScreenElement' + ], + [ + 'mozRequestFullScreen', + 'mozCancelFullScreen', + 'mozfullscreenchange', + 'mozFullScreen', + 'mozFullScreenElement' + ], + [ + 'msRequestFullscreen', + 'msExitFullscreen', + 'MSFullscreenChange', + 'msFullscreenEnabled', + 'msFullscreenElement' + ] + ], + + /** + * initialize + * @function + */ + init: function () { + this._fn = {}; + var i, val, map = this._fnMap, valL; + for (i = 0, l = map.length; i < l; i++) { + val = map[i]; + if (val && val[1] in document) { + for (i = 0, valL = val.length; i < valL; i++) { + this._fn[map[0][i]] = val[i]; + } + break; + } + } + + this._supportsFullScreen = (typeof this._fn.requestFullscreen !== 'undefined'); + this._touchEvent = ('ontouchstart' in window) ? 'touchstart' : 'mousedown'; + }, + + /** + * return true if it's full now. + * @returns {Boolean} + */ + fullScreen: function () { + if(!this._supportsFullScreen) return false; + else if( document[this._fn.fullscreenElement] === undefined || document[this._fn.fullscreenElement] === null ) + return false; + else + return true; + }, + + /** + * change the screen to full mode. + * @param {Element} element + * @param {Function} onFullScreenChange + */ + requestFullScreen: function (element, onFullScreenChange) { + if (!this._supportsFullScreen) { + return; + } + + element = element || document.documentElement; + + if (onFullScreenChange) { + var eventName = this._fn.fullscreenchange; + if (this._preOnFullScreenChange) { + document.removeEventListener(eventName, this._preOnFullScreenChange); + } + this._preOnFullScreenChange = onFullScreenChange; + document.addEventListener(eventName, onFullScreenChange, false); + } + + return element[this._fn.requestFullscreen](); + }, + + /** + * exit the full mode. + * @return {Boolean} + */ + exitFullScreen: function () { + return this._supportsFullScreen ? document[this._fn.exitFullscreen]() : true; + }, + + /** + * Automatically request full screen with a touch/click event + * @param {Element} element + * @param {Function} onFullScreenChange + */ + autoFullScreen: function (element, onFullScreenChange) { + element = element || document.body; + var touchTarget = cc.game.canvas || element; + var theScreen = this; + // Function bind will be too complicated here because we need the callback function's reference to remove the listener + function callback() { + touchTarget.removeEventListener(theScreen._touchEvent, callback); + theScreen.requestFullScreen(element, onFullScreenChange); + } + this.requestFullScreen(element, onFullScreenChange); + touchTarget.addEventListener(this._touchEvent, callback); + } +}; +cc.screen.init(); diff --git a/frameworks/cocos2d-html5/cocos2d/core/platform/CCTypes.js b/frameworks/cocos2d-html5/cocos2d/core/platform/CCTypes.js new file mode 100644 index 0000000..e076fe2 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/platform/CCTypes.js @@ -0,0 +1,1178 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * Color class, please use cc.color() to construct a color + * @class cc.Color + * @param {Number} r + * @param {Number} g + * @param {Number} b + * @param {Number} a + * @see cc.color + */ +cc.Color = function (r, g, b, a) { + r = r || 0; + g = g || 0; + b = b || 0; + a = typeof a === 'number' ? a : 255; + this._val = ((r << 24) >>> 0) + (g << 16) + (b << 8) + a; +}; + +var _p = cc.Color.prototype; +_p._getR = function () { + return (this._val & 0xff000000) >>> 24; +}; +_p._setR = function (value) { + this._val = (this._val & 0x00ffffff) | ((value << 24) >>> 0); +}; +_p._getG = function () { + return (this._val & 0x00ff0000) >> 16; +}; +_p._setG = function (value) { + this._val = (this._val & 0xff00ffff) | (value << 16); +}; +_p._getB = function () { + return (this._val & 0x0000ff00) >> 8; +}; +_p._setB = function (value) { + this._val = (this._val & 0xffff00ff) | (value << 8); +}; +_p._getA = function () { + return this._val & 0x000000ff; +}; +_p._setA = function (value) { + this._val = (this._val & 0xffffff00) | value; +}; + + +/** @expose */ +_p.r; +cc.defineGetterSetter(_p, "r", _p._getR, _p._setR); +/** @expose */ +_p.g; +cc.defineGetterSetter(_p, "g", _p._getG, _p._setG); +/** @expose */ +_p.b; +cc.defineGetterSetter(_p, "b", _p._getB, _p._setB); +/** @expose */ +_p.a; +cc.defineGetterSetter(_p, "a", _p._getA, _p._setA); + +/** + * Generate a color object based on multiple forms of parameters + * @example + * + * // 1. All channels separately as parameters + * var color1 = cc.color(255, 255, 255, 255); + * + * // 2. Convert a hex string to a color + * var color2 = cc.color("#000000"); + * + * // 3. An color object as parameter + * var color3 = cc.color({r: 255, g: 255, b: 255, a: 255}); + * + * Alpha channel is optional. Default value is 255 + * + * @param {Number|String|cc.Color} r + * @param {Number} [g] + * @param {Number} [b] + * @param {Number} [a=255] + * @return {cc.Color} + */ +cc.color = function (r, g, b, a) { + if (r === undefined) + return new cc.Color(0, 0, 0, 255); + if (typeof r === 'object') + return new cc.Color(r.r, r.g, r.b, (r.a == null) ? 255 : r.a); + if (typeof r === 'string') + return cc.hexToColor(r); + return new cc.Color(r, g, b, (a == null ? 255 : a)); +}; + +/** + * returns true if both ccColor3B are equal. Otherwise it returns false. + * @function + * @param {cc.Color} color1 + * @param {cc.Color} color2 + * @return {Boolean} true if both ccColor3B are equal. Otherwise it returns false. + */ +cc.colorEqual = function (color1, color2) { + return color1.r === color2.r && color1.g === color2.g && color1.b === color2.b; +}; + +/** + * the device accelerometer reports values for each axis in units of g-force + * @class cc.Acceleration + * @constructor + * @param {Number} x + * @param {Number} y + * @param {Number} z + * @param {Number} timestamp + */ +cc.Acceleration = function (x, y, z, timestamp) { + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + this.timestamp = timestamp || 0; +}; + +/** + * @class cc.Vertex2F + * @param {Number} x + * @param {Number}y + * @param {Array} arrayBuffer + * @param {Number}offset + * @constructor + */ +cc.Vertex2F = function (x, y, arrayBuffer, offset) { + this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.Vertex2F.BYTES_PER_ELEMENT); + this._offset = offset || 0; + + this._view = new Float32Array(this._arrayBuffer, this._offset, 2); + this._view[0] = x || 0; + this._view[1] = y || 0; +}; +/** + * @constant + * @type {number} + */ +cc.Vertex2F.BYTES_PER_ELEMENT = 8; + +_p = cc.Vertex2F.prototype; +_p._getX = function () { + return this._view[0]; +}; +_p._setX = function (xValue) { + this._view[0] = xValue; +}; +_p._getY = function () { + return this._view[1]; +}; +_p._setY = function (yValue) { + this._view[1] = yValue; +}; +/** @expose */ +_p.x; +cc.defineGetterSetter(_p, "x", _p._getX, _p._setX); +/** @expose */ +_p.y; +cc.defineGetterSetter(_p, "y", _p._getY, _p._setY); + +/** + * @class cc.Vertex3F + * @param {Number} x + * @param {Number} y + * @param {Number}z + * @param {Array} arrayBuffer + * @param {Number} offset + * @constructor + */ +cc.Vertex3F = function (x, y, z, arrayBuffer, offset) { + this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.Vertex3F.BYTES_PER_ELEMENT); + this._offset = offset || 0; + + var locArrayBuffer = this._arrayBuffer, locOffset = this._offset; + this._view = new Float32Array(locArrayBuffer, locOffset, 3); + this._view[0] = x || 0; + this._view[1] = y || 0; + this._view[2] = z || 0; +}; +/** + * @constant + * @type {number} + */ +cc.Vertex3F.BYTES_PER_ELEMENT = 12; + +_p = cc.Vertex3F.prototype; +_p._getX = function () { + return this._view[0]; +}; +_p._setX = function (xValue) { + this._view[0] = xValue; +}; +_p._getY = function () { + return this._view[1]; +}; +_p._setY = function (yValue) { + this._view[1] = yValue; +}; +_p._getZ = function () { + return this._view[2]; +}; +_p._setZ = function (zValue) { + this._view[2] = zValue; +}; +/** @expose */ +_p.x; +cc.defineGetterSetter(_p, "x", _p._getX, _p._setX); +/** @expose */ +_p.y; +cc.defineGetterSetter(_p, "y", _p._getY, _p._setY); +/** @expose */ +_p.z; +cc.defineGetterSetter(_p, "z", _p._getZ, _p._setZ); + +/** + * @class cc.Tex2F + * @param {Number} u + * @param {Number} v + * @param {Array} arrayBuffer + * @param {Number} offset + * @constructor + */ +cc.Tex2F = function (u, v, arrayBuffer, offset) { + this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.Tex2F.BYTES_PER_ELEMENT); + this._offset = offset || 0; + + this._view = new Float32Array(this._arrayBuffer, this._offset, 2); + this._view[0] = u || 0; + this._view[1] = v || 0; +}; +/** + * @constants + * @type {number} + */ +cc.Tex2F.BYTES_PER_ELEMENT = 8; + +_p = cc.Tex2F.prototype; +_p._getU = function () { + return this._view[0]; +}; +_p._setU = function (xValue) { + this._view[0] = xValue; +}; +_p._getV = function () { + return this._view[1]; +}; +_p._setV = function (yValue) { + this._view[1] = yValue; +}; +/** @expose */ +_p.u; +cc.defineGetterSetter(_p, "u", _p._getU, _p._setU); +/** @expose */ +_p.v; +cc.defineGetterSetter(_p, "v", _p._getV, _p._setV); + +/** + * @class cc.Quad2 + * @param {cc.Vertex2F} tl + * @param {cc.Vertex2F} tr + * @param {cc.Vertex2F} bl + * @param {cc.Vertex2F} br + * @param {Array} arrayBuffer + * @param {Number} offset + * @constructor + */ +cc.Quad2 = function (tl, tr, bl, br, arrayBuffer, offset) { + this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.Quad2.BYTES_PER_ELEMENT); + this._offset = offset || 0; + + var locArrayBuffer = this._arrayBuffer, locOffset = this._offset, locElementLen = cc.Vertex2F.BYTES_PER_ELEMENT; + this._tl = tl ? new cc.Vertex2F(tl.x, tl.y, locArrayBuffer, locOffset) : new cc.Vertex2F(0, 0, locArrayBuffer, locOffset); + locOffset += locElementLen; + this._tr = tr ? new cc.Vertex2F(tr.x, tr.y, locArrayBuffer, locOffset) : new cc.Vertex2F(0, 0, locArrayBuffer, locOffset); + locOffset += locElementLen; + this._bl = bl ? new cc.Vertex2F(bl.x, bl.y, locArrayBuffer, locOffset) : new cc.Vertex2F(0, 0, locArrayBuffer, locOffset); + locOffset += locElementLen; + this._br = br ? new cc.Vertex2F(br.x, br.y, locArrayBuffer, locOffset) : new cc.Vertex2F(0, 0, locArrayBuffer, locOffset); +}; +/** + * @constant + * @type {number} + */ +cc.Quad2.BYTES_PER_ELEMENT = 32; + +_p = cc.Quad2.prototype; +_p._getTL = function () { + return this._tl; +}; +_p._setTL = function (tlValue) { + this._tl._view[0] = tlValue.x; + this._tl._view[1] = tlValue.y; +}; +_p._getTR = function () { + return this._tr; +}; +_p._setTR = function (trValue) { + this._tr._view[0] = trValue.x; + this._tr._view[1] = trValue.y; +}; +_p._getBL = function () { + return this._bl; +}; +_p._setBL = function (blValue) { + this._bl._view[0] = blValue.x; + this._bl._view[1] = blValue.y; +}; +_p._getBR = function () { + return this._br; +}; +_p._setBR = function (brValue) { + this._br._view[0] = brValue.x; + this._br._view[1] = brValue.y; +}; + +/** @expose */ +_p.tl; +cc.defineGetterSetter(_p, "tl", _p._getTL, _p._setTL); +/** @expose */ +_p.tr; +cc.defineGetterSetter(_p, "tr", _p._getTR, _p._setTR); +/** @expose */ +_p.bl; +cc.defineGetterSetter(_p, "bl", _p._getBL, _p._setBL); +/** @expose */ +_p.br; +cc.defineGetterSetter(_p, "br", _p._getBR, _p._setBR); + +/** + * A 3D Quad. 4 * 3 floats + * @Class cc.Quad3 + * @Construct + * @param {cc.Vertex3F} bl + * @param {cc.Vertex3F} br + * @param {cc.Vertex3F} tl + * @param {cc.Vertex3F} tr + */ +cc.Quad3 = function (bl, br, tl, tr, arrayBuffer, offset) { + this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.Quad3.BYTES_PER_ELEMENT); + this._offset = offset || 0; + + var locArrayBuffer = this._arrayBuffer, locOffset = this._offset, locElementLen = cc.Vertex3F.BYTES_PER_ELEMENT; + this.bl = bl ? new cc.Vertex3F(bl.x, bl.y, bl.z, locArrayBuffer, locOffset) : new cc.Vertex3F(0, 0, 0, locArrayBuffer, locOffset); + locOffset += locElementLen; + this.br = br ? new cc.Vertex3F(br.x, br.y, br.z, locArrayBuffer, locOffset) : new cc.Vertex3F(0, 0, 0, locArrayBuffer, locOffset); + locOffset += locElementLen; + this.tl = tl ? new cc.Vertex3F(tl.x, tl.y, tl.z, locArrayBuffer, locOffset) : new cc.Vertex3F(0, 0, 0, locArrayBuffer, locOffset); + locOffset += locElementLen; + this.tr = tr ? new cc.Vertex3F(tr.x, tr.y, tr.z, locArrayBuffer, locOffset) : new cc.Vertex3F(0, 0, 0, locArrayBuffer, locOffset); +}; +/** + * @constant + * @type {number} + */ +cc.Quad3.BYTES_PER_ELEMENT = 48; + +/** + * @class cc.V3F_C4B_T2F + * @param {cc.Vertex3F} vertices + * @param {cc.Color} colors + * @param {cc.Tex2F} texCoords + * @param {Array} arrayBuffer + * @param {Number} offset + * @constructor + */ +cc.V3F_C4B_T2F = function (vertices, colors, texCoords, arrayBuffer, offset) { + this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.V3F_C4B_T2F.BYTES_PER_ELEMENT); + this._offset = offset || 0; + + var locArrayBuffer = this._arrayBuffer, locOffset = this._offset; + this._vertices = vertices ? new cc.Vertex3F(vertices.x, vertices.y, vertices.z, locArrayBuffer, locOffset) : + new cc.Vertex3F(0, 0, 0, locArrayBuffer, locOffset); + + locOffset += cc.Vertex3F.BYTES_PER_ELEMENT; + this._colors = colors ? new cc._WebGLColor(colors.r, colors.g, colors.b, colors.a, locArrayBuffer, locOffset) : + new cc._WebGLColor(0, 0, 0, 0, locArrayBuffer, locOffset); + + locOffset += cc._WebGLColor.BYTES_PER_ELEMENT; + this._texCoords = texCoords ? new cc.Tex2F(texCoords.u, texCoords.v, locArrayBuffer, locOffset) : + new cc.Tex2F(0, 0, locArrayBuffer, locOffset); +}; +/** + * @constant + * @type {number} + */ +cc.V3F_C4B_T2F.BYTES_PER_ELEMENT = 24; + +_p = cc.V3F_C4B_T2F.prototype; +_p._getVertices = function () { + return this._vertices; +}; +_p._setVertices = function (verticesValue) { + var locVertices = this._vertices; + locVertices._view[0] = verticesValue.x; + locVertices._view[1] = verticesValue.y; + locVertices._view[2] = verticesValue.z; +}; +_p._getColor = function () { + return this._colors; +}; +_p._setColor = function (colorValue) { + var locColors = this._colors; + locColors._view[0] = colorValue.r; + locColors._view[1] = colorValue.g; + locColors._view[2] = colorValue.b; + locColors._view[3] = colorValue.a; +}; +_p._getTexCoords = function () { + return this._texCoords; +}; +_p._setTexCoords = function (texValue) { + this._texCoords._view[0] = texValue.u; + this._texCoords._view[1] = texValue.v; +}; +/** @expose */ +_p.vertices; +cc.defineGetterSetter(_p, "vertices", _p._getVertices, _p._setVertices); +/** @expose */ +_p.colors; +cc.defineGetterSetter(_p, "colors", _p._getColor, _p._setColor); +/** @expose */ +_p.texCoords; +cc.defineGetterSetter(_p, "texCoords", _p._getTexCoords, _p._setTexCoords); + +/** + * @cc.class cc.V3F_C4B_T2F_Quad + * @param {cc.V3F_C4B_T2F} tl + * @param {cc.V3F_C4B_T2F} bl + * @param {cc.V3F_C4B_T2F} tr + * @param {cc.V3F_C4B_T2F} br + * @param {Array} arrayBuffer + * @param {Number} offset + * @constructor + */ +cc.V3F_C4B_T2F_Quad = function (tl, bl, tr, br, arrayBuffer, offset) { + this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT); + this._offset = offset || 0; + + var locArrayBuffer = this._arrayBuffer, locOffset = this._offset, locElementLen = cc.V3F_C4B_T2F.BYTES_PER_ELEMENT; + this._tl = tl ? new cc.V3F_C4B_T2F(tl.vertices, tl.colors, tl.texCoords, locArrayBuffer, locOffset) : + new cc.V3F_C4B_T2F(null, null, null, locArrayBuffer, locOffset); + locOffset += locElementLen; + this._bl = bl ? new cc.V3F_C4B_T2F(bl.vertices, bl.colors, bl.texCoords, locArrayBuffer, locOffset) : + new cc.V3F_C4B_T2F(null, null, null, locArrayBuffer, locOffset); + locOffset += locElementLen; + this._tr = tr ? new cc.V3F_C4B_T2F(tr.vertices, tr.colors, tr.texCoords, locArrayBuffer, locOffset) : + new cc.V3F_C4B_T2F(null, null, null, locArrayBuffer, locOffset); + locOffset += locElementLen; + this._br = br ? new cc.V3F_C4B_T2F(br.vertices, br.colors, br.texCoords, locArrayBuffer, locOffset) : + new cc.V3F_C4B_T2F(null, null, null, locArrayBuffer, locOffset); +}; +/** + * @constant + * @type {number} + */ +cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT = 96; +_p = cc.V3F_C4B_T2F_Quad.prototype; +_p._getTL = function () { + return this._tl; +}; +_p._setTL = function (tlValue) { + var locTl = this._tl; + locTl.vertices = tlValue.vertices; + locTl.colors = tlValue.colors; + locTl.texCoords = tlValue.texCoords; +}; +_p._getBL = function () { + return this._bl; +}; +_p._setBL = function (blValue) { + var locBl = this._bl; + locBl.vertices = blValue.vertices; + locBl.colors = blValue.colors; + locBl.texCoords = blValue.texCoords; +}; +_p._getTR = function () { + return this._tr; +}; +_p._setTR = function (trValue) { + var locTr = this._tr; + locTr.vertices = trValue.vertices; + locTr.colors = trValue.colors; + locTr.texCoords = trValue.texCoords; +}; +_p._getBR = function () { + return this._br; +}; +_p._setBR = function (brValue) { + var locBr = this._br; + locBr.vertices = brValue.vertices; + locBr.colors = brValue.colors; + locBr.texCoords = brValue.texCoords; +}; +_p._getArrayBuffer = function () { + return this._arrayBuffer; +}; + +/** @expose */ +_p.tl; +cc.defineGetterSetter(_p, "tl", _p._getTL, _p._setTL); +/** @expose */ +_p.tr; +cc.defineGetterSetter(_p, "tr", _p._getTR, _p._setTR); +/** @expose */ +_p.bl; +cc.defineGetterSetter(_p, "bl", _p._getBL, _p._setBL); +/** @expose */ +_p.br; +cc.defineGetterSetter(_p, "br", _p._getBR, _p._setBR); +/** @expose */ +_p.arrayBuffer; +cc.defineGetterSetter(_p, "arrayBuffer", _p._getArrayBuffer, null); + +/** + * @function + * @returns {cc.V3F_C4B_T2F_Quad} + */ +cc.V3F_C4B_T2F_QuadZero = function () { + return new cc.V3F_C4B_T2F_Quad(); +}; + +/** + * @function + * @param {cc.V3F_C4B_T2F_Quad} sourceQuad + * @return {cc.V3F_C4B_T2F_Quad} + */ +cc.V3F_C4B_T2F_QuadCopy = function (sourceQuad) { + if (!sourceQuad) + return cc.V3F_C4B_T2F_QuadZero(); + + //return new cc.V3F_C4B_T2F_Quad(sourceQuad,tl,sourceQuad,bl,sourceQuad.tr,sourceQuad.br,null,0); + var srcTL = sourceQuad.tl, srcBL = sourceQuad.bl, srcTR = sourceQuad.tr, srcBR = sourceQuad.br; + return { + tl: { + vertices: {x: srcTL.vertices.x, y: srcTL.vertices.y, z: srcTL.vertices.z}, + colors: {r: srcTL.colors.r, g: srcTL.colors.g, b: srcTL.colors.b, a: srcTL.colors.a}, + texCoords: {u: srcTL.texCoords.u, v: srcTL.texCoords.v} + }, + bl: { + vertices: {x: srcBL.vertices.x, y: srcBL.vertices.y, z: srcBL.vertices.z}, + colors: {r: srcBL.colors.r, g: srcBL.colors.g, b: srcBL.colors.b, a: srcBL.colors.a}, + texCoords: {u: srcBL.texCoords.u, v: srcBL.texCoords.v} + }, + tr: { + vertices: {x: srcTR.vertices.x, y: srcTR.vertices.y, z: srcTR.vertices.z}, + colors: {r: srcTR.colors.r, g: srcTR.colors.g, b: srcTR.colors.b, a: srcTR.colors.a}, + texCoords: {u: srcTR.texCoords.u, v: srcTR.texCoords.v} + }, + br: { + vertices: {x: srcBR.vertices.x, y: srcBR.vertices.y, z: srcBR.vertices.z}, + colors: {r: srcBR.colors.r, g: srcBR.colors.g, b: srcBR.colors.b, a: srcBR.colors.a}, + texCoords: {u: srcBR.texCoords.u, v: srcBR.texCoords.v} + } + }; +}; + +/** + * @function + * @param {Array} sourceQuads + * @returns {Array} + */ +cc.V3F_C4B_T2F_QuadsCopy = function (sourceQuads) { + if (!sourceQuads) + return []; + + var retArr = []; + for (var i = 0; i < sourceQuads.length; i++) { + retArr.push(cc.V3F_C4B_T2F_QuadCopy(sourceQuads[i])); + } + return retArr; +}; + +//redefine cc.V2F_C4B_T2F +/** + * @class cc.V2F_C4B_T2F + * @param {cc.Vertex2F} vertices + * @param {cc.Color} colors + * @param {cc.Tex2F} texCoords + * @param {Array} arrayBuffer + * @param {Number} offset + * @constructor + */ +cc.V2F_C4B_T2F = function (vertices, colors, texCoords, arrayBuffer, offset) { + this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.V2F_C4B_T2F.BYTES_PER_ELEMENT); + this._offset = offset || 0; + + var locArrayBuffer = this._arrayBuffer, locOffset = this._offset; + this._vertices = vertices ? new cc.Vertex2F(vertices.x, vertices.y, locArrayBuffer, locOffset) : + new cc.Vertex2F(0, 0, locArrayBuffer, locOffset); + locOffset += cc.Vertex2F.BYTES_PER_ELEMENT; + this._colors = colors ? new cc._WebGLColor(colors.r, colors.g, colors.b, colors.a, locArrayBuffer, locOffset) : + new cc._WebGLColor(0, 0, 0, 0, locArrayBuffer, locOffset); + locOffset += cc._WebGLColor.BYTES_PER_ELEMENT; + this._texCoords = texCoords ? new cc.Tex2F(texCoords.u, texCoords.v, locArrayBuffer, locOffset) : + new cc.Tex2F(0, 0, locArrayBuffer, locOffset); +}; + +/** + * @constant + * @type {number} + */ +cc.V2F_C4B_T2F.BYTES_PER_ELEMENT = 20; +_p = cc.V2F_C4B_T2F.prototype; +_p._getVertices = function () { + return this._vertices; +}; +_p._setVertices = function (verticesValue) { + this._vertices._view[0] = verticesValue.x; + this._vertices._view[1] = verticesValue.y; +}; +_p._getColor = function () { + return this._colors; +}; +_p._setColor = function (colorValue) { + var locColors = this._colors; + locColors._view[0] = colorValue.r; + locColors._view[1] = colorValue.g; + locColors._view[2] = colorValue.b; + locColors._view[3] = colorValue.a; +}; +_p._getTexCoords = function () { + return this._texCoords; +}; +_p._setTexCoords = function (texValue) { + this._texCoords._view[0] = texValue.u; + this._texCoords._view[1] = texValue.v; +}; + +/** @expose */ +_p.vertices; +cc.defineGetterSetter(_p, "vertices", _p._getVertices, _p._setVertices); +/** @expose */ +_p.colors; +cc.defineGetterSetter(_p, "colors", _p._getColor, _p._setColor); +/** @expose */ +_p.texCoords; +cc.defineGetterSetter(_p, "texCoords", _p._getTexCoords, _p._setTexCoords); + +//redefine cc.V2F_C4B_T2F_Triangle +/** + * @class cc.V2F_C4B_T2F_Triangle + * @param {cc.V2F_C4B_T2F} a + * @param {cc.V2F_C4B_T2F} b + * @param {cc.V2F_C4B_T2F} c + * @param {Array} arrayBuffer + * @param {Number} offset + * @constructor + */ +cc.V2F_C4B_T2F_Triangle = function (a, b, c, arrayBuffer, offset) { + this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT); + this._offset = offset || 0; + + var locArrayBuffer = this._arrayBuffer, locOffset = this._offset, locElementLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT; + this._a = a ? new cc.V2F_C4B_T2F(a.vertices, a.colors, a.texCoords, locArrayBuffer, locOffset) : + new cc.V2F_C4B_T2F(null, null, null, locArrayBuffer, locOffset); + locOffset += locElementLen; + this._b = b ? new cc.V2F_C4B_T2F(b.vertices, b.colors, b.texCoords, locArrayBuffer, locOffset) : + new cc.V2F_C4B_T2F(null, null, null, locArrayBuffer, locOffset); + locOffset += locElementLen; + this._c = c ? new cc.V2F_C4B_T2F(c.vertices, c.colors, c.texCoords, locArrayBuffer, locOffset) : + new cc.V2F_C4B_T2F(null, null, null, locArrayBuffer, locOffset); +}; +/** + * @constant + * @type {number} + */ +cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT = 60; +_p = cc.V2F_C4B_T2F_Triangle.prototype; +_p._getA = function () { + return this._a; +}; +_p._setA = function (aValue) { + var locA = this._a; + locA.vertices = aValue.vertices; + locA.colors = aValue.colors; + locA.texCoords = aValue.texCoords; +}; +_p._getB = function () { + return this._b; +}; +_p._setB = function (bValue) { + var locB = this._b; + locB.vertices = bValue.vertices; + locB.colors = bValue.colors; + locB.texCoords = bValue.texCoords; +}; +_p._getC = function () { + return this._c; +}; +_p._setC = function (cValue) { + var locC = this._c; + locC.vertices = cValue.vertices; + locC.colors = cValue.colors; + locC.texCoords = cValue.texCoords; +}; + +/** @expose */ +_p.a; +cc.defineGetterSetter(_p, "a", _p._getA, _p._setA); +/** @expose */ +_p.b; +cc.defineGetterSetter(_p, "b", _p._getB, _p._setB); +/** @expose */ +_p.c; +cc.defineGetterSetter(_p, "c", _p._getC, _p._setC); + +/** + * Helper macro that creates an Vertex2F type composed of 2 floats: x, y + * @function + * @param {Number} x + * @param {Number} y + * @return {cc.Vertex2F} + */ +cc.vertex2 = function (x, y) { + return new cc.Vertex2F(x, y); +}; + +/** + * Helper macro that creates an Vertex3F type composed of 3 floats: x, y, z + * @function + * @param {Number} x + * @param {Number} y + * @param {Number} z + * @return {cc.Vertex3F} + */ +cc.vertex3 = function (x, y, z) { + return new cc.Vertex3F(x, y, z); +}; + +/** + * Helper macro that creates an Tex2F type: A texcoord composed of 2 floats: u, y + * @function + * @param {Number} u + * @param {Number} v + * @return {cc.Tex2F} + */ +cc.tex2 = function (u, v) { + return new cc.Tex2F(u, v); +}; + +/** + * Blend Function used for textures + * @Class cc.BlendFunc + * @Constructor + * @param {Number} src1 source blend function + * @param {Number} dst1 destination blend function + */ +cc.BlendFunc = function (src1, dst1) { + this.src = src1; + this.dst = dst1; +}; + +/** + * @function + * @returns {cc.BlendFunc} + */ +cc.blendFuncDisable = function () { + return new cc.BlendFunc(cc.ONE, cc.ZERO); +}; + +/** + * convert a string of color for style to Color. + * e.g. "#ff06ff" to : cc.color(255,6,255) + * @function + * @param {String} hex + * @return {cc.Color} + */ +cc.hexToColor = function (hex) { + hex = hex.replace(/^#?/, "0x"); + var c = parseInt(hex); + var r = c >> 16; + var g = (c >> 8) % 256; + var b = c % 256; + return new cc.Color(r, g, b); +}; + +/** + * convert Color to a string of color for style. + * e.g. cc.color(255,6,255) to : "#ff06ff" + * @function + * @param {cc.Color} color + * @return {String} + */ +cc.colorToHex = function (color) { + var hR = color.r.toString(16), hG = color.g.toString(16), hB = color.b.toString(16); + return "#" + (color.r < 16 ? ("0" + hR) : hR) + (color.g < 16 ? ("0" + hG) : hG) + (color.b < 16 ? ("0" + hB) : hB); +}; + +/** + * text alignment : left + * @constant + * @type Number + */ +cc.TEXT_ALIGNMENT_LEFT = 0; + +/** + * text alignment : center + * @constant + * @type Number + */ +cc.TEXT_ALIGNMENT_CENTER = 1; + +/** + * text alignment : right + * @constant + * @type Number + */ +cc.TEXT_ALIGNMENT_RIGHT = 2; + +/** + * text alignment : top + * @constant + * @type Number + */ +cc.VERTICAL_TEXT_ALIGNMENT_TOP = 0; + +/** + * text alignment : center + * @constant + * @type Number + */ +cc.VERTICAL_TEXT_ALIGNMENT_CENTER = 1; + +/** + * text alignment : bottom + * @constant + * @type Number + */ +cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM = 2; + +cc._Dictionary = cc.Class.extend({ + _keyMapTb: null, + _valueMapTb: null, + __currId: 0, + + ctor: function () { + this._keyMapTb = {}; + this._valueMapTb = {}; + this.__currId = 2 << (0 | (Math.random() * 10)); + }, + + __getKey: function () { + this.__currId++; + return "key_" + this.__currId; + }, + + setObject: function (value, key) { + if (key == null) + return; + + var keyId = this.__getKey(); + this._keyMapTb[keyId] = key; + this._valueMapTb[keyId] = value; + }, + + objectForKey: function (key) { + if (key == null) + return null; + + var locKeyMapTb = this._keyMapTb; + for (var keyId in locKeyMapTb) { + if (locKeyMapTb[keyId] === key) + return this._valueMapTb[keyId]; + } + return null; + }, + + valueForKey: function (key) { + return this.objectForKey(key); + }, + + removeObjectForKey: function (key) { + if (key == null) + return; + + var locKeyMapTb = this._keyMapTb; + for (var keyId in locKeyMapTb) { + if (locKeyMapTb[keyId] === key) { + delete this._valueMapTb[keyId]; + delete locKeyMapTb[keyId]; + return; + } + } + }, + + removeObjectsForKeys: function (keys) { + if (keys == null) + return; + + for (var i = 0; i < keys.length; i++) + this.removeObjectForKey(keys[i]); + }, + + allKeys: function () { + var keyArr = [], locKeyMapTb = this._keyMapTb; + for (var key in locKeyMapTb) + keyArr.push(locKeyMapTb[key]); + return keyArr; + }, + + removeAllObjects: function () { + this._keyMapTb = {}; + this._valueMapTb = {}; + }, + + count: function () { + return this.allKeys().length; + } +}); + +/** + * Common usage: + * + * var fontDef = new cc.FontDefinition(); + * fontDef.fontName = "Arial"; + * fontDef.fontSize = 12; + * ... + * + * OR using inline definition useful for constructor injection + * + * var fontDef = new cc.FontDefinition({ + * fontName: "Arial", + * fontSize: 12 + * }); + * + * + * + * @class cc.FontDefinition + * @param {Object} properties - (OPTIONAL) Allow inline FontDefinition + * @constructor + */ +cc.FontDefinition = function (properties) { + var _t = this; + _t.fontName = "Arial"; + _t.fontSize = 12; + _t.textAlign = cc.TEXT_ALIGNMENT_CENTER; + _t.verticalAlign = cc.VERTICAL_TEXT_ALIGNMENT_TOP; + _t.fillStyle = cc.color(255, 255, 255, 255); + _t.boundingWidth = 0; + _t.boundingHeight = 0; + + _t.strokeEnabled = false; + _t.strokeStyle = cc.color(255, 255, 255, 255); + _t.lineWidth = 1; + _t.lineHeight = "normal"; + _t.fontStyle = "normal"; + _t.fontWeight = "normal"; + + _t.shadowEnabled = false; + _t.shadowOffsetX = 0; + _t.shadowOffsetY = 0; + _t.shadowBlur = 0; + _t.shadowOpacity = 1.0; + + //properties mapping: + if (properties && properties instanceof Object) { + for (var key in properties) { + _t[key] = properties[key]; + } + } +}; +/** + * Web ONLY + * */ +cc.FontDefinition.prototype._getCanvasFontStr = function () { + var lineHeight = !this.lineHeight.charAt ? this.lineHeight + "px" : this.lineHeight; + return this.fontStyle + " " + this.fontWeight + " " + this.fontSize + "px/" + lineHeight + " '" + this.fontName + "'"; +}; + +cc.game.addEventListener(cc.game.EVENT_RENDERER_INITED, function () { + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + //redefine Color + cc._WebGLColor = function (r, g, b, a, arrayBuffer, offset) { + this._arrayBuffer = arrayBuffer || new ArrayBuffer(cc._WebGLColor.BYTES_PER_ELEMENT); + this._offset = offset || 0; + + var locArrayBuffer = this._arrayBuffer, locOffset = this._offset; + this._view = new Uint8Array(locArrayBuffer, locOffset, 4); + + this._view[0] = r || 0; + this._view[1] = g || 0; + this._view[2] = b || 0; + this._view[3] = (a == null) ? 255 : a; + + if (a === undefined) + this.a_undefined = true; + }; + cc._WebGLColor.BYTES_PER_ELEMENT = 4; + _p = cc._WebGLColor.prototype; + _p._getR = function () { + return this._view[0]; + }; + _p._setR = function (value) { + this._view[0] = value < 0 ? 0 : value; + }; + _p._getG = function () { + return this._view[1]; + }; + _p._setG = function (value) { + this._view[1] = value < 0 ? 0 : value; + }; + _p._getB = function () { + return this._view[2]; + }; + _p._setB = function (value) { + this._view[2] = value < 0 ? 0 : value; + }; + _p._getA = function () { + return this._view[3]; + }; + _p._setA = function (value) { + this._view[3] = value < 0 ? 0 : value; + }; + cc.defineGetterSetter(_p, "r", _p._getR, _p._setR); + cc.defineGetterSetter(_p, "g", _p._getG, _p._setG); + cc.defineGetterSetter(_p, "b", _p._getB, _p._setB); + cc.defineGetterSetter(_p, "a", _p._getA, _p._setA); + } +}); + +_p = cc.color; +/** + * White color (255, 255, 255, 255) + * @returns {cc.Color} + * @private + */ +_p._getWhite = function () { + return cc.color(255, 255, 255); +}; + +/** + * Yellow color (255, 255, 0, 255) + * @returns {cc.Color} + * @private + */ +_p._getYellow = function () { + return cc.color(255, 255, 0); +}; + +/** + * Blue color (0, 0, 255, 255) + * @type {cc.Color} + * @private + */ +_p._getBlue = function () { + return cc.color(0, 0, 255); +}; + +/** + * Green Color (0, 255, 0, 255) + * @type {cc.Color} + * @private + */ +_p._getGreen = function () { + return cc.color(0, 255, 0); +}; + +/** + * Red Color (255, 0, 0, 255) + * @type {cc.Color} + * @private + */ +_p._getRed = function () { + return cc.color(255, 0, 0); +}; + +/** + * Magenta Color (255, 0, 255, 255) + * @type {cc.Color} + * @private + */ +_p._getMagenta = function () { + return cc.color(255, 0, 255); +}; + +/** + * Black Color (0, 0, 0, 255) + * @type {cc.Color} + * @private + */ +_p._getBlack = function () { + return cc.color(0, 0, 0); +}; + +/** + * Orange Color (255, 127, 0, 255) + * @type {_p} + * @private + */ +_p._getOrange = function () { + return cc.color(255, 127, 0); +}; + +/** + * Gray Color (166, 166, 166, 255) + * @type {_p} + * @private + */ +_p._getGray = function () { + return cc.color(166, 166, 166); +}; + +/** @expose */ +_p.WHITE; +cc.defineGetterSetter(_p, "WHITE", _p._getWhite); +/** @expose */ +_p.YELLOW; +cc.defineGetterSetter(_p, "YELLOW", _p._getYellow); +/** @expose */ +_p.BLUE; +cc.defineGetterSetter(_p, "BLUE", _p._getBlue); +/** @expose */ +_p.GREEN; +cc.defineGetterSetter(_p, "GREEN", _p._getGreen); +/** @expose */ +_p.RED; +cc.defineGetterSetter(_p, "RED", _p._getRed); +/** @expose */ +_p.MAGENTA; +cc.defineGetterSetter(_p, "MAGENTA", _p._getMagenta); +/** @expose */ +_p.BLACK; +cc.defineGetterSetter(_p, "BLACK", _p._getBlack); +/** @expose */ +_p.ORANGE; +cc.defineGetterSetter(_p, "ORANGE", _p._getOrange); +/** @expose */ +_p.GRAY; +cc.defineGetterSetter(_p, "GRAY", _p._getGray); + +cc.BlendFunc._disable = function(){ + return new cc.BlendFunc(cc.ONE, cc.ZERO); +}; +cc.BlendFunc._alphaPremultiplied = function(){ + return new cc.BlendFunc(cc.ONE, cc.ONE_MINUS_SRC_ALPHA); +}; +cc.BlendFunc._alphaNonPremultiplied = function(){ + return new cc.BlendFunc(cc.SRC_ALPHA, cc.ONE_MINUS_SRC_ALPHA); +}; +cc.BlendFunc._additive = function(){ + return new cc.BlendFunc(cc.SRC_ALPHA, cc.ONE); +}; + +/** @expose */ +cc.BlendFunc.DISABLE; +cc.defineGetterSetter(cc.BlendFunc, "DISABLE", cc.BlendFunc._disable); +/** @expose */ +cc.BlendFunc.ALPHA_PREMULTIPLIED; +cc.defineGetterSetter(cc.BlendFunc, "ALPHA_PREMULTIPLIED", cc.BlendFunc._alphaPremultiplied); +/** @expose */ +cc.BlendFunc.ALPHA_NON_PREMULTIPLIED; +cc.defineGetterSetter(cc.BlendFunc, "ALPHA_NON_PREMULTIPLIED", cc.BlendFunc._alphaNonPremultiplied); +/** @expose */ +cc.BlendFunc.ADDITIVE; +cc.defineGetterSetter(cc.BlendFunc, "ADDITIVE", cc.BlendFunc._additive); diff --git a/frameworks/cocos2d-html5/cocos2d/core/platform/CCVisibleRect.js b/frameworks/cocos2d-html5/cocos2d/core/platform/CCVisibleRect.js new file mode 100644 index 0000000..c6545e9 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/platform/CCVisibleRect.js @@ -0,0 +1,100 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + + 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. + ****************************************************************************/ + +/** + * cc.visibleRect is a singleton object which defines the actual visible rect of the current view, + * it should represent the same rect as cc.view.getViewportRect() + * + * @property {cc.Point} topLeft - Top left coordinate of the screen related to the game scene + * @property {cc.Point} topRight - Top right coordinate of the screen related to the game scene + * @property {cc.Point} top - Top center coordinate of the screen related to the game scene + * @property {cc.Point} bottomLeft - Bottom left coordinate of the screen related to the game scene + * @property {cc.Point} bottomRight - Bottom right coordinate of the screen related to the game scene + * @property {cc.Point} bottom - Bottom center coordinate of the screen related to the game scene + * @property {cc.Point} center - Center coordinate of the screen related to the game scene + * @property {cc.Point} left - Left center coordinate of the screen related to the game scene + * @property {cc.Point} right - Right center coordinate of the screen related to the game scene + * @property {Number} width - Width of the screen + * @property {Number} height - Height of the screen + * + * @class + * @name cc.visibleRect + */ +cc.visibleRect = { + topLeft:cc.p(0,0), + topRight:cc.p(0,0), + top:cc.p(0,0), + bottomLeft:cc.p(0,0), + bottomRight:cc.p(0,0), + bottom:cc.p(0,0), + center:cc.p(0,0), + left:cc.p(0,0), + right:cc.p(0,0), + width:0, + height:0, + + /** + * initialize + * @param {cc.Rect} visibleRect + */ + init:function(visibleRect){ + + var w = this.width = visibleRect.width; + var h = this.height = visibleRect.height; + var l = visibleRect.x, + b = visibleRect.y, + t = b + h, + r = l + w; + + //top + this.topLeft.x = l; + this.topLeft.y = t; + this.topRight.x = r; + this.topRight.y = t; + this.top.x = l + w/2; + this.top.y = t; + + //bottom + this.bottomLeft.x = l; + this.bottomLeft.y = b; + this.bottomRight.x = r; + this.bottomRight.y = b; + this.bottom.x = l + w/2; + this.bottom.y = b; + + //center + this.center.x = l + w/2; + this.center.y = b + h/2; + + //left + this.left.x = l; + this.left.y = b + h/2; + + //right + this.right.x = r; + this.right.y = b + h/2; + } +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/cocos2d/core/platform/miniFramework.js b/frameworks/cocos2d-html5/cocos2d/core/platform/miniFramework.js new file mode 100644 index 0000000..048a2db --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/platform/miniFramework.js @@ -0,0 +1,264 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * the dollar sign, classic like jquery, this selector add extra methods to HTMLElement without touching its prototype
+ * it is also chainable like jquery + * @param {HTMLElement|String} x pass in a css selector in string or the whole HTMLElement + * @function + * @return {cc.$} + */ +cc.$ = function (x) { + /** @lends cc.$# */ + var parent = (this === cc) ? document : this; + + var el = (x instanceof HTMLElement) ? x : parent.querySelector(x); + + if (el) { + /** + * find and return the child wth css selector (same as jquery.find) + * @lends cc.$# + * @function + * @param {HTMLElement|String} x pass in a css selector in string or the whole HTMLElement + * @return {cc.$} + */ + el.find = el.find || cc.$; + /** + * check if a DOMNode has a specific class + * @lends cc.$# + * @function + * @param {String} cls + * @return {Boolean} + */ + el.hasClass = el.hasClass || function (cls) { + return this.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')); + }; + /** + * add a class to a DOMNode, returns self to allow chaining + * @lends cc.$# + * @function + * @param {String} cls + * @return {cc.$} + */ + el.addClass = el.addClass || function (cls) { + if (!this.hasClass(cls)) { + if (this.className) { + this.className += " "; + } + this.className += cls; + } + return this; + }; + /** + * remove a specific class from a DOMNode, returns self to allow chaining + * @lends cc.$# + * @function + * @param {String} cls + * @return {cc.$} + */ + el.removeClass = el.removeClass || function (cls) { + if (this.hasClass(cls)) { + this.className = this.className.replace(cls, ''); + } + return this; + }; + /** + * detach it self from parent + * @lends cc.$# + * @function + */ + el.remove = el.remove || function () { + if (this.parentNode) + this.parentNode.removeChild(this); + return this; + }; + + /** + * add to another element as a child + * @lends cc.$# + * @function + * @param {HTMLElement|cc.$} x + * @return {cc.$} + */ + el.appendTo = el.appendTo || function (x) { + x.appendChild(this); + return this; + }; + + /** + * add to another element as a child and place on the top of the children list + * @lends cc.$# + * @function + * @param {HTMLElement|cc.$} x + * @return {cc.$} + */ + el.prependTo = el.prependTo || function (x) { + ( x.childNodes[0]) ? x.insertBefore(this, x.childNodes[0]) : x.appendChild(this); + return this; + }; + + /** + * helper function for updating the css transform + * @lends cc.$# + * @function + * @return {cc.$} + */ + el.transforms = el.transforms || function () { + this.style[cc.$.trans] = cc.$.translate(this.position) + cc.$.rotate(this.rotation) + cc.$.scale(this.scale) + cc.$.skew(this.skew); + return this; + }; + + el.position = el.position || {x: 0, y: 0}; + el.rotation = el.rotation || 0; + el.scale = el.scale || {x: 1, y: 1}; + el.skew = el.skew || {x: 0, y: 0}; + + /** + * move the element + * @memberOf cc.$# + * @name translates + * @function + * @param {Number} x in pixel + * @param {Number} y in pixel + * @return {cc.$} + */ + el.translates = function (x, y) { + this.position.x = x; + this.position.y = y; + this.transforms(); + return this + }; + + /** + * rotate the element + * @memberOf cc.$# + * @name rotate + * @function + * @param {Number} x in degrees + * @return {cc.$} + */ + el.rotate = function (x) { + this.rotation = x; + this.transforms(); + return this + }; + + /** + * resize the element + * @memberOf cc.$# + * @name resize + * @function + * @param {Number} x + * @param {Number} y + * @return {cc.$} + */ + el.resize = function (x, y) { + this.scale.x = x; + this.scale.y = y; + this.transforms(); + return this + }; + + /** + * skews the element + * @memberOf cc.$# + * @name setSkew + * @function + * @param {Number} x in degrees + * @param {Number} y + * @return {cc.$} + */ + el.setSkew = function (x, y) { + this.skew.x = x; + this.skew.y = y; + this.transforms(); + return this + }; + } + return el; +}; +//getting the prefix and css3 3d support +switch (cc.sys.browserType) { + case cc.sys.BROWSER_TYPE_FIREFOX: + cc.$.pfx = "Moz"; + cc.$.hd = true; + break; + case cc.sys.BROWSER_TYPE_CHROME: + case cc.sys.BROWSER_TYPE_SAFARI: + cc.$.pfx = "webkit"; + cc.$.hd = true; + break; + case cc.sys.BROWSER_TYPE_OPERA: + cc.$.pfx = "O"; + cc.$.hd = false; + break; + case cc.sys.BROWSER_TYPE_IE: + cc.$.pfx = "ms"; + cc.$.hd = false; + break; + default: + cc.$.pfx = "webkit"; + cc.$.hd = true; +} +//cache for prefixed transform +cc.$.trans = cc.$.pfx + "Transform"; +//helper function for constructing transform strings +cc.$.translate = (cc.$.hd) ? function (a) { + return "translate3d(" + a.x + "px, " + a.y + "px, 0) " +} : function (a) { + return "translate(" + a.x + "px, " + a.y + "px) " +}; +cc.$.rotate = (cc.$.hd) ? function (a) { + return "rotateZ(" + a + "deg) "; +} : function (a) { + return "rotate(" + a + "deg) "; +}; +cc.$.scale = function (a) { + return "scale(" + a.x + ", " + a.y + ") " +}; +cc.$.skew = function (a) { + return "skewX(" + -a.x + "deg) skewY(" + a.y + "deg)"; +}; + + +/** + * Creates a new element, and adds cc.$ methods + * @param {String} x name of the element tag to create + * @return {cc.$} + */ +cc.$new = function (x) { + return cc.$(document.createElement(x)) +}; +cc.$.findpos = function (obj) { + var curleft = 0; + var curtop = 0; + do { + curleft += obj.offsetLeft; + curtop += obj.offsetTop; + } while (obj = obj.offsetParent); + return {x: curleft, y: curtop}; +}; + diff --git a/frameworks/cocos2d-html5/cocos2d/core/renderer/DirtyRegion.js b/frameworks/cocos2d-html5/cocos2d/core/renderer/DirtyRegion.js new file mode 100644 index 0000000..161d1d1 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/renderer/DirtyRegion.js @@ -0,0 +1,337 @@ +//Region is used to label a rect which world axis aligned. +var Region = function () { + this._minX = 0; + this._minY = 0; + this._maxX = 0; + this._maxY = 0; + + this._width = 0; + this._height = 0; + this._area = 0; + //this.moved = false; +}; + +var regionProto = Region.prototype; + +var regionPool = []; + +function regionCreate() { + var region = regionPool.pop(); + if (!region) { + region = new Region(); + } + return region; +} + +function regionRelease(region) { + regionPool.push(region); +} + +regionProto.setTo = function (minX, minY, maxX, maxY) { + this._minX = minX; + this._minY = minY; + this._maxX = maxX; + this._maxY = maxY; + this.updateArea(); + return this; +}; + +//convert region to int values which is fast for clipping +regionProto.intValues = function () { + this._minX = Math.floor(this._minX); + this._minY = Math.floor(this._minY); + this._maxX = Math.ceil(this._maxX); + this._maxY = Math.ceil(this._maxY); + this.updateArea(); +}; + +//update the area of region +regionProto.updateArea = function () { + this._width = this._maxX - this._minX; + this._height = this._maxY - this._minY; + this._area = this._width * this._height; +}; + +//merge two region into one +regionProto.union = function (target) { + if(this._width <= 0 || this._height <= 0) { + this.setTo(target._minX, target._minY, target._maxX, target._maxY); + return; + } + if (this._minX > target._minX) { + this._minX = target._minX; + } + if (this._minY > target._minY) { + this._minY = target._minY; + } + if (this._maxX < target._maxX) { + this._maxX = target._maxX; + } + if (this._maxY < target._maxY) { + this._maxY = target._maxY; + } + this.updateArea(); +}; + +//regionProto.intersect = function (target) { +// if (this._minX < target._minX) { +// this._minX = target._minX; +// } +// if (this._maxX > target._maxX) { +// this._maxX = target._maxX; +// } +// if (this._minX >= this._maxX) { +// this.setEmpty(); +// return; +// } +// if (this._minY < target._minY) { +// this._minY = target._minY; +// } +// +// if (this._maxY > target._maxY) { +// this._maxY = target._maxY; +// } +// if (this._minY >= this._maxY) { +// this.setEmpty(); +// return; +// } +// this.updateArea(); +//}; + +//set region to empty +regionProto.setEmpty = function () { + this._minX = 0; + this._minY = 0; + this._maxX = 0; + this._maxY = 0; + this._width = 0; + this._height = 0; + this._area = 0; +}; + +regionProto.isEmpty = function () { + return this._width <= 0 || this._height <= 0; +}; + +//check whether two region is intersects or not +regionProto.intersects = function (target) { + if (this._width <= 0 || this._height <= 0 || target._width <= 0 || target._height <= 0) { + return false; + } + var max = this._minX > target._minX ? this._minX : target._minX; + var min = this._maxX < target._maxX ? this._maxX : target._maxX; + if (max > min) { + return false; + } + + max = this._minY > target._minY ? this._minY : target._minY; + min = this._maxY < target._maxY ? this._maxY : target._maxY; + return max <= min; +}; + +//update region by a rotated bounds +regionProto.updateRegion = function (bounds, matrix) { + if (bounds.width == 0 || bounds.height == 0) { + this.setEmpty(); + return; + } + var m = matrix; + var a = m.a; + var b = m.b; + var c = m.c; + var d = m.d; + var tx = m.tx; + var ty = m.ty; + var x = bounds.x; + var y = bounds.y; + var xMax = x + bounds.width; + var yMax = y + bounds.height; + var minX, minY, maxX, maxY; + if (a == 1.0 && b == 0.0 && c == 0.0 && d == 1.0) { + minX = x + tx - 1; + minY = y + ty - 1; + maxX = xMax + tx + 1; + maxY = yMax + ty + 1; + } + else { + var x0 = a * x + c * y + tx; + var y0 = b * x + d * y + ty; + var x1 = a * xMax + c * y + tx; + var y1 = b * xMax + d * y + ty; + var x2 = a * xMax + c * yMax + tx; + var y2 = b * xMax + d * yMax + ty; + var x3 = a * x + c * yMax + tx; + var y3 = b * x + d * yMax + ty; + + var tmp = 0; + + if (x0 > x1) { + tmp = x0; + x0 = x1; + x1 = tmp; + } + if (x2 > x3) { + tmp = x2; + x2 = x3; + x3 = tmp; + } + + minX = (x0 < x2 ? x0 : x2) - 1; + maxX = (x1 > x3 ? x1 : x3) + 1; + + if (y0 > y1) { + tmp = y0; + y0 = y1; + y1 = tmp; + } + if (y2 > y3) { + tmp = y2; + y2 = y3; + y3 = tmp; + } + + minY = (y0 < y2 ? y0 : y2) - 1; + maxY = (y1 > y3 ? y1 : y3) + 1; + } + this._minX = minX; + this._minY = minY; + this._maxX = maxX; + this._maxY = maxY; + this._width = maxX - minX; + this._height = maxY - minY; + this._area = this._width * this._height; +}; + +//get the area of the unioned region of r1 and r2 +function unionArea(r1, r2) { + var minX = r1._minX < r2._minX ? r1._minX : r2._minX; + var minY = r1._minY < r2._minY ? r1._minY : r2._minY; + var maxX = r1._maxX > r2._maxX ? r1._maxX : r2._maxX; + var maxY = r1._maxY > r2._maxY ? r1._maxY : r2._maxY; + return (maxX - minX) * (maxY - minY); +} + +//DirtyRegion is used to collect the dirty area which need to be rerendered in canvas +//there may be many small regions which is dirty, the dirty region will merge it into several big one to optimise performance +var DirtyRegion = function() { + this.dirtyList = []; + this.hasClipRect = false; + this.clipWidth = 0; + this.clipHeight = 0; + this.clipArea = 0; + this.clipRectChanged = false; +}; +var dirtyRegionProto = DirtyRegion.prototype; + +//clip rect, regions will not be considered if it is outside +dirtyRegionProto.setClipRect = function(width, height) { + this.hasClipRect = true; + this.clipRectChanged = true; + this.clipWidth = Math.ceil(width); + this.clipHeight = Math.ceil(height); + this.clipArea = this.clipWidth * this.clipHeight; +}; + +//add a new region which is dirty (need to be rendered) +dirtyRegionProto.addRegion = function(target) { + var minX = target._minX, minY = target._minY, maxX = target._maxX, maxY = target._maxY; + + if (this.hasClipRect) { + if (minX < 0) { + minX = 0; + } + if (minY < 0) { + minY = 0; + } + if (maxX > this.clipWidth) { + maxX = this.clipWidth; + } + if (maxY > this.clipHeight) { + maxY = this.clipHeight; + } + } + if (minX >= maxX || minY >= maxY) { + return false; + } + if (this.clipRectChanged) { + return true; + } + var dirtyList = this.dirtyList; + var region = regionCreate(); + dirtyList.push(region.setTo(minX, minY, maxX, maxY)); + this.mergeDirtyList(dirtyList); + return true; +}; + +//clear all the dirty regions +dirtyRegionProto.clear = function() { + var dirtyList = this.dirtyList; + var length = dirtyList.length; + for (var i = 0; i < length; i++) { + regionRelease(dirtyList[i]); + } + dirtyList.length = 0; +}; + +//get the merged dirty regions +dirtyRegionProto.getDirtyRegions = function() { + var dirtyList = this.dirtyList; + if (this.clipRectChanged) { + this.clipRectChanged = false; + this.clear(); + var region = regionCreate(); + dirtyList.push(region.setTo(0, 0, this.clipWidth, this.clipHeight)); + } + else { + while (this.mergeDirtyList(dirtyList)) { + } + } + var numDirty = this.dirtyList.length; + if (numDirty > 0) { + for (var i = 0; i < numDirty; i++) { + this.dirtyList[i].intValues(); + } + } + return this.dirtyList; +}; + +//merge the small dirty regions into bigger region, to improve the performance of dirty regions +dirtyRegionProto.mergeDirtyList = function(dirtyList) { + var length = dirtyList.length; + if (length < 2) { + return false; + } + var hasClipRect = this.hasClipRect; + var bestDelta = length > 3 ? Number.POSITIVE_INFINITY : 0; + var mergeA = 0; + var mergeB = 0; + var totalArea = 0; + for (var i = 0; i < length - 1; i++) { + var regionA = dirtyList[i]; + hasClipRect && (totalArea += regionA.area); + for (var j = i + 1; j < length; j++) { + var regionB = dirtyList[j]; + var delta = unionArea(regionA, regionB) - regionA.area - regionB.area; + if (bestDelta > delta) { + mergeA = i; + mergeB = j; + bestDelta = delta; + } + } + } + //if the area of dirty region exceed 95% of the screen, skip the following dirty regions merge + if (hasClipRect && (totalArea / this.clipArea) > 0.95) { + this.clipRectChanged = true; + } + if (mergeA != mergeB) { + var region = dirtyList[mergeB]; + dirtyList[mergeA].union(region); + regionRelease(region); + dirtyList.splice(mergeB, 1); + return true; + } + return false; +}; + +cc.Region = Region; +cc.DirtyRegion = DirtyRegion; diff --git a/frameworks/cocos2d-html5/cocos2d/core/renderer/GlobalVertexBuffer.js b/frameworks/cocos2d-html5/cocos2d/core/renderer/GlobalVertexBuffer.js new file mode 100644 index 0000000..e81c95a --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/renderer/GlobalVertexBuffer.js @@ -0,0 +1,139 @@ +/**************************************************************************** + Copyright (c) 2016 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +var GlobalVertexBuffer = (function () { + +var VERTICES_SIZE = 888; + +var GlobalVertexBuffer = function (gl, byteLength) { + // WebGL buffer + this.gl = gl; + this.vertexBuffer = gl.createBuffer(); + + this.size = VERTICES_SIZE; + this.byteLength = byteLength || VERTICES_SIZE * 4 * cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT; + + // buffer data and views + this.data = new ArrayBuffer(this.byteLength); + this.dataArray = new Float32Array(this.data); + + // Init buffer data + gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this.dataArray, gl.DYNAMIC_DRAW); + + this._dirty = false; + this._spaces = { + 0: this.byteLength + }; +}; +GlobalVertexBuffer.prototype = { + constructor: GlobalVertexBuffer, + + allocBuffer: function (offset, size) { + var space = this._spaces[offset]; + if (space && space >= size) { + // Remove the space + delete this._spaces[offset]; + if (space > size) { + var newOffset = offset + size; + this._spaces[newOffset] = space - size; + } + return true; + } + else { + return false; + } + }, + + requestBuffer: function (size) { + var key, offset, available; + for (key in this._spaces) { + offset = parseInt(key); + available = this._spaces[key]; + if (available >= size && this.allocBuffer(offset, size)) { + return offset; + } + } + return -1; + }, + + freeBuffer: function (offset, size) { + var spaces = this._spaces; + var i, key, end; + // Merge with previous space + for (key in spaces) { + i = parseInt(key); + if (i > offset) { + break; + } + if (i + spaces[key] >= offset) { + size = size + offset - i; + offset = i; + break; + } + } + + end = offset + size; + // Merge with next space + if (this._spaces[end]) { + size += this._spaces[end]; + delete this._spaces[end]; + } + + this._spaces[offset] = size; + }, + + setDirty: function () { + this._dirty = true; + }, + + update: function () { + if (this._dirty) { + this.gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + // Note: Can memorize different dirty zones and update them separately, maybe faster + this.gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.dataArray); + this._dirty = false; + } + }, + + updateSubData: function (offset, dataArray) { + this.gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); + this.gl.bufferSubData(gl.ARRAY_BUFFER, offset, dataArray); + }, + + destroy: function () { + this.gl.deleteBuffer(this.vertexBuffer); + + this.data = null; + this.positions = null; + this.colors = null; + this.texCoords = null; + + this.vertexBuffer = null; + } +}; + +return GlobalVertexBuffer; + +})(); \ No newline at end of file diff --git a/frameworks/cocos2d-html5/cocos2d/core/renderer/RendererCanvas.js b/frameworks/cocos2d-html5/cocos2d/core/renderer/RendererCanvas.js new file mode 100644 index 0000000..57e569c --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/renderer/RendererCanvas.js @@ -0,0 +1,438 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +cc.rendererCanvas = { + childrenOrderDirty: true, + assignedZ: 0, + assignedZStep: 1 / 10000, + + _transformNodePool: [], //save nodes transform dirty + _renderCmds: [], //save renderer commands + + _isCacheToCanvasOn: false, //a switch that whether cache the rendererCmd to cacheToCanvasCmds + _cacheToCanvasCmds: {}, // an array saves the renderer commands need for cache to other canvas + _cacheInstanceIds: [], + _currentID: 0, + _clearColor: cc.color(), //background color,default BLACK + _clearFillStyle: "rgb(0, 0, 0)", + + _dirtyRegion: null, + _allNeedDraw: true, + _enableDirtyRegion: false, + _debugDirtyRegion: false, + _canUseDirtyRegion: false, + //max dirty Region count, default is 10 + _dirtyRegionCountThreshold: 10, + + getRenderCmd: function (renderableObject) { + //TODO Add renderCmd pool here + return renderableObject._createRenderCmd(); + }, + + enableDirtyRegion: function (enabled) { + this._enableDirtyRegion = enabled; + }, + + isDirtyRegionEnabled: function () { + return this._enableDirtyRegion; + }, + + setDirtyRegionCountThreshold: function(threshold) { + this._dirtyRegionCountThreshold = threshold; + }, + + _collectDirtyRegion: function () { + //collect dirtyList + var locCmds = this._renderCmds, i, len; + var dirtyRegion = this._dirtyRegion; + var dirtryRegionCount = 0; + var result = true; + var localStatus = cc.Node.CanvasRenderCmd.RegionStatus; + for (i = 0, len = locCmds.length; i < len; i++) { + var cmd = locCmds[i]; + var regionFlag = cmd._regionFlag; + var oldRegion = cmd._oldRegion; + var currentRegion = cmd._currentRegion; + if (regionFlag > localStatus.NotDirty) { + ++dirtryRegionCount; + if(dirtryRegionCount > this._dirtyRegionCountThreshold) + result = false; + //add + if(result) { + (!currentRegion.isEmpty()) && dirtyRegion.addRegion(currentRegion); + if (cmd._regionFlag > localStatus.Dirty) { + (!oldRegion.isEmpty()) && dirtyRegion.addRegion(oldRegion); + } + } + cmd._regionFlag = localStatus.NotDirty; + } + + } + + return result; + }, + + _beginDrawDirtyRegion: function (ctxWrapper) { + var ctx = ctxWrapper.getContext(); + var dirtyList = this._dirtyRegion.getDirtyRegions(); + ctx.save(); + //add clip + var scaleX = ctxWrapper._scaleX; + var scaleY = ctxWrapper._scaleY; + ctxWrapper.setTransform({a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0}, scaleX, scaleY); + ctx.beginPath(); + for (var index = 0, count = dirtyList.length; index < count; ++index) { + var region = dirtyList[index]; + ctx.rect(region._minX , -region._maxY , region._width , region._height ); + } + ctx.clip(); + //end add clip + }, + + _endDrawDirtyRegion: function (ctx) { + ctx.restore(); + }, + + _debugDrawDirtyRegion: function (ctxWrapper) { + if (!this._debugDirtyRegion) return; + var ctx = ctxWrapper.getContext(); + var dirtyList = this._dirtyRegion.getDirtyRegions(); + //add clip + var scaleX = ctxWrapper._scaleX; + var scaleY = ctxWrapper._scaleY; + ctxWrapper.setTransform({a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0}, scaleX, scaleY); + ctx.beginPath(); + for (var index = 0, count = dirtyList.length; index < count; ++index) { + var region = dirtyList[index]; + ctx.rect(region._minX, -region._maxY , region._width , region._height ); + } + var oldstyle = ctx.fillStyle; + ctx.fillStyle = 'green'; + ctx.fill(); + ctx.fillStyle = oldstyle; + //end add clip + }, + /** + * drawing all renderer command to context (default is cc._renderContext) + * @param {cc.CanvasContextWrapper} [ctx=cc._renderContext] + */ + rendering: function (ctxWrapper) { + var dirtyRegion = this._dirtyRegion = this._dirtyRegion || new cc.DirtyRegion(); + var viewport = cc._canvas; + var wrapper = ctxWrapper || cc._renderContext; + var ctx = wrapper.getContext(); + + var scaleX = cc.view.getScaleX(), + scaleY = cc.view.getScaleY(); + wrapper.setViewScale(scaleX, scaleY); + wrapper.computeRealOffsetY(); + var dirtyList = this._dirtyRegion.getDirtyRegions(); + var locCmds = this._renderCmds, i, len; + var allNeedDraw = this._allNeedDraw || !this._enableDirtyRegion || !this._canUseDirtyRegion; + var collectResult = true; + if (!allNeedDraw) { + collectResult = this._collectDirtyRegion(); + } + + allNeedDraw = allNeedDraw || (!collectResult); + + if(!allNeedDraw) { + this._beginDrawDirtyRegion(wrapper); + } + + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.clearRect(0, 0, viewport.width, viewport.height); + if (this._clearColor.r !== 255 || + this._clearColor.g !== 255 || + this._clearColor.b !== 255) { + wrapper.setFillStyle(this._clearFillStyle); + wrapper.setGlobalAlpha(this._clearColor.a); + ctx.fillRect(0, 0, viewport.width, viewport.height); + } + + for (i = 0, len = locCmds.length; i < len; i++) { + var cmd = locCmds[i]; + var needRendering = false; + var cmdRegion = cmd._currentRegion; + if (!cmdRegion || allNeedDraw) { + needRendering = true; + } else { + for (var index = 0, count = dirtyList.length; index < count; ++index) { + if (dirtyList[index].intersects(cmdRegion)) { + needRendering = true; + break; + } + } + } + if (needRendering) { + cmd.rendering(wrapper, scaleX, scaleY); + } + } + + if (!allNeedDraw) { + //draw debug info for dirty region if it is needed + this._debugDrawDirtyRegion(wrapper); + this._endDrawDirtyRegion(ctx); + } + + dirtyRegion.clear(); + this._allNeedDraw = false; + }, + + /** + * drawing all renderer command to cache canvas' context + * @param {cc.CanvasContextWrapper} ctx + * @param {Number} [instanceID] + * @param {Number} [scaleX] + * @param {Number} [scaleY] + */ + _renderingToCacheCanvas: function (ctx, instanceID, scaleX, scaleY) { + if (!ctx) + cc.log("The context of RenderTexture is invalid."); + scaleX = cc.isUndefined(scaleX) ? 1 : scaleX; + scaleY = cc.isUndefined(scaleY) ? 1 : scaleY; + instanceID = instanceID || this._currentID; + var i, locCmds = this._cacheToCanvasCmds[instanceID], len; + ctx.computeRealOffsetY(); + for (i = 0, len = locCmds.length; i < len; i++) { + locCmds[i].rendering(ctx, scaleX, scaleY); + } + this._removeCache(instanceID); + + var locIDs = this._cacheInstanceIds; + if (locIDs.length === 0) + this._isCacheToCanvasOn = false; + else + this._currentID = locIDs[locIDs.length - 1]; + }, + + _turnToCacheMode: function (renderTextureID) { + this._isCacheToCanvasOn = true; + renderTextureID = renderTextureID || 0; + this._cacheToCanvasCmds[renderTextureID] = []; + if (this._cacheInstanceIds.indexOf(renderTextureID) === -1) + this._cacheInstanceIds.push(renderTextureID); + this._currentID = renderTextureID; + }, + + _turnToNormalMode: function () { + this._isCacheToCanvasOn = false; + }, + + _removeCache: function (instanceID) { + instanceID = instanceID || this._currentID; + var cmds = this._cacheToCanvasCmds[instanceID]; + if (cmds) { + cmds.length = 0; + delete this._cacheToCanvasCmds[instanceID]; + } + + var locIDs = this._cacheInstanceIds; + cc.arrayRemoveObject(locIDs, instanceID); + }, + + resetFlag: function () { + this.childrenOrderDirty = false; + this._transformNodePool.length = 0; + }, + + transform: function () { + var locPool = this._transformNodePool; + //sort the pool + locPool.sort(this._sortNodeByLevelAsc); + + //transform node + for (var i = 0, len = locPool.length; i < len; i++) { + if (locPool[i]._dirtyFlag !== 0) + locPool[i].updateStatus(); + } + locPool.length = 0; + }, + + transformDirty: function () { + return this._transformNodePool.length > 0; + }, + + _sortNodeByLevelAsc: function (n1, n2) { + return n1._curLevel - n2._curLevel; + }, + + pushDirtyNode: function (node) { + this._transformNodePool.push(node); + }, + + clear: function () { + }, + + clearRenderCommands: function () { + this._renderCmds.length = 0; + this._cacheInstanceIds.length = 0; + this._isCacheToCanvasOn = false; + this._allNeedDraw = true; + this._canUseDirtyRegion = true; + }, + + pushRenderCommand: function (cmd) { + if (!cmd.needDraw()) + return; + if (!cmd._canUseDirtyRegion) { + this._canUseDirtyRegion = false; + } + if (this._isCacheToCanvasOn) { + var currentId = this._currentID, locCmdBuffer = this._cacheToCanvasCmds; + var cmdList = locCmdBuffer[currentId]; + if (cmdList.indexOf(cmd) === -1) + cmdList.push(cmd); + } else { + if (this._renderCmds.indexOf(cmd) === -1) + this._renderCmds.push(cmd); + } + } +}; + +(function () { + cc.CanvasContextWrapper = function (context) { + this._context = context; + + this._saveCount = 0; + this._currentAlpha = context.globalAlpha; + this._currentCompositeOperation = context.globalCompositeOperation; + this._currentFillStyle = context.fillStyle; + this._currentStrokeStyle = context.strokeStyle; + + this._offsetX = 0; + this._offsetY = 0; + this._realOffsetY = this.height; + this._armatureMode = 0; + }; + + var proto = cc.CanvasContextWrapper.prototype; + + proto.resetCache = function () { + var context = this._context; + //call it after resize cc._canvas, because context will reset. + this._currentAlpha = context.globalAlpha; + this._currentCompositeOperation = context.globalCompositeOperation; + this._currentFillStyle = context.fillStyle; + this._currentStrokeStyle = context.strokeStyle; + this._realOffsetY = this._context.canvas.height + this._offsetY; + }; + + proto.setOffset = function (x, y) { + this._offsetX = x; + this._offsetY = y; + this._realOffsetY = this._context.canvas.height + this._offsetY; + }; + + proto.computeRealOffsetY = function () { + this._realOffsetY = this._context.canvas.height + this._offsetY; + }; + + proto.setViewScale = function (scaleX, scaleY) { + //call it at cc.renderCanvas.rendering + this._scaleX = scaleX; + this._scaleY = scaleY; + }; + + proto.getContext = function () { + return this._context; + }; + + proto.save = function () { + this._context.save(); + this._saveCount++; + }; + + proto.restore = function () { + this._context.restore(); + this._saveCount--; + }; + + proto.setGlobalAlpha = function (alpha) { + if (this._saveCount > 0) { + this._context.globalAlpha = alpha; + } else { + if (this._currentAlpha !== alpha) { + this._currentAlpha = alpha; + this._context.globalAlpha = alpha; + } + } + }; + + proto.setCompositeOperation = function (compositionOperation) { + if (this._saveCount > 0) { + this._context.globalCompositeOperation = compositionOperation; + } else { + if (this._currentCompositeOperation !== compositionOperation) { + this._currentCompositeOperation = compositionOperation; + this._context.globalCompositeOperation = compositionOperation; + } + } + }; + + proto.setFillStyle = function (fillStyle) { + if (this._saveCount > 0) { + this._context.fillStyle = fillStyle; + } else { + if (this._currentFillStyle !== fillStyle) { + this._currentFillStyle = fillStyle; + this._context.fillStyle = fillStyle; + } + } + }; + + proto.setStrokeStyle = function (strokeStyle) { + if (this._saveCount > 0) { + this._context.strokeStyle = strokeStyle; + } else { + if (this._currentStrokeStyle !== strokeStyle) { + this._currentStrokeStyle = strokeStyle; + this._context.strokeStyle = strokeStyle; + } + } + }; + + proto.setTransform = function (t, scaleX, scaleY) { + if (this._armatureMode > 0) { + //ugly for armature + this.restore(); + this.save(); + this._context.transform(t.a * scaleX, -t.b * scaleY, -t.c * scaleX, t.d * scaleY, t.tx * scaleX, -(t.ty * scaleY)); + } else { + this._context.setTransform(t.a * scaleX, -t.b * scaleY, -t.c * scaleX, t.d * scaleY, this._offsetX + t.tx * scaleX, this._realOffsetY - (t.ty * scaleY)); + } + }; + + proto._switchToArmatureMode = function (enable, t, scaleX, scaleY) { + if (enable) { + this._armatureMode++; + this._context.setTransform(t.a, t.c, t.b, t.d, this._offsetX + t.tx * scaleX, this._realOffsetY - (t.ty * scaleY)); + this.save(); + } else { + this._armatureMode--; + this.restore(); + } + }; +})(); + diff --git a/frameworks/cocos2d-html5/cocos2d/core/renderer/RendererWebGL.js b/frameworks/cocos2d-html5/cocos2d/core/renderer/RendererWebGL.js new file mode 100644 index 0000000..d0655f6 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/renderer/RendererWebGL.js @@ -0,0 +1,462 @@ +/**************************************************************************** + Copyright (c) 2013-2016 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +cc.rendererWebGL = (function () { + +// Internal variables + // Batching general informations +var _batchedInfo = { + // The batched texture, all batching element should have the same texture + texture: null, + // The batched blend source, all batching element should have the same blend source + blendSrc: null, + // The batched blend destination, all batching element should have the same blend destination + blendDst: null, + // The batched gl program state, all batching element should have the same state + glProgramState: null + }, + + _batchBroken = false, + _indexBuffer = null, + _vertexBuffer = null, + // Total vertex size + _maxVertexSize = 0, + // Current batching vertex size + _batchingSize = 0, + // Current batching index size + _indexSize = 0, + // Float size per vertex + _sizePerVertex = 6, + // buffer data and views + _vertexData = null, + _vertexDataSize = 0, + _vertexDataF32 = null, + _vertexDataUI32 = null, + _indexData = null, + _prevIndexSize = 0, + _pureQuad = true, + _IS_IOS = false; + + +// Inspired from @Heishe's gotta-batch-them-all branch +// https://github.com/Talisca/cocos2d-html5/commit/de731f16414eb9bcaa20480006897ca6576d362c +function updateBuffer (numVertex) { + var gl = cc._renderContext; + // Update index buffer size + if (_indexBuffer) { + var indexCount = Math.ceil(numVertex / 4) * 6; + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, _indexBuffer); + _indexData = new Uint16Array(indexCount); + var currentQuad = 0; + for (var i = 0, len = indexCount; i < len; i += 6) { + _indexData[i] = currentQuad + 0; + _indexData[i + 1] = currentQuad + 1; + _indexData[i + 2] = currentQuad + 2; + _indexData[i + 3] = currentQuad + 1; + _indexData[i + 4] = currentQuad + 2; + _indexData[i + 5] = currentQuad + 3; + currentQuad += 4; + } + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, _indexData, gl.DYNAMIC_DRAW); + } + // Update vertex buffer size + if (_vertexBuffer) { + _vertexDataSize = numVertex * _sizePerVertex; + var byteLength = _vertexDataSize * 4; + _vertexData = new ArrayBuffer(byteLength); + _vertexDataF32 = new Float32Array(_vertexData); + _vertexDataUI32 = new Uint32Array(_vertexData); + // Init buffer data + gl.bindBuffer(gl.ARRAY_BUFFER, _vertexBuffer); + gl.bufferData(gl.ARRAY_BUFFER, _vertexDataF32, gl.DYNAMIC_DRAW); + } + // Downsize by 200 to avoid vertex data overflow + _maxVertexSize = numVertex - 200; +} + +// Inspired from @Heishe's gotta-batch-them-all branch +// https://github.com/Talisca/cocos2d-html5/commit/de731f16414eb9bcaa20480006897ca6576d362c +function initQuadBuffer (numVertex) { + var gl = cc._renderContext; + if (_indexBuffer === null) { + // TODO do user need to release the memory ? + _vertexBuffer = gl.createBuffer(); + _indexBuffer = gl.createBuffer(); + } + updateBuffer(numVertex); +} + +var VertexType = { + QUAD : 0, + TRIANGLE : 1, + CUSTOM: 2 +}; + +return { + mat4Identity: null, + + childrenOrderDirty: true, + assignedZ: 0, + assignedZStep: 1 / 100, + + VertexType: VertexType, + + _transformNodePool: [], //save nodes transform dirty + _renderCmds: [], //save renderer commands + + _isCacheToBufferOn: false, //a switch that whether cache the rendererCmd to cacheToCanvasCmds + _cacheToBufferCmds: {}, // an array saves the renderer commands need for cache to other canvas + _cacheInstanceIds: [], + _currentID: 0, + _clearColor: cc.color(0, 0, 0, 255), //background color,default BLACK + + init: function () { + var gl = cc._renderContext; + gl.disable(gl.CULL_FACE); + gl.disable(gl.DEPTH_TEST); + + this.mat4Identity = new cc.math.Matrix4(); + this.mat4Identity.identity(); + initQuadBuffer(cc.BATCH_VERTEX_COUNT); + if (cc.sys.os === cc.sys.OS_IOS) { + _IS_IOS = true; + } + }, + + getVertexSize: function () { + return _maxVertexSize; + }, + + getRenderCmd: function (renderableObject) { + //TODO Add renderCmd pool here + return renderableObject._createRenderCmd(); + }, + + _turnToCacheMode: function (renderTextureID) { + this._isCacheToBufferOn = true; + renderTextureID = renderTextureID || 0; + if (!this._cacheToBufferCmds[renderTextureID]) { + this._cacheToBufferCmds[renderTextureID] = []; + } + else { + this._cacheToBufferCmds[renderTextureID].length = 0; + } + if (this._cacheInstanceIds.indexOf(renderTextureID) === -1) { + this._cacheInstanceIds.push(renderTextureID); + } + this._currentID = renderTextureID; + }, + + _turnToNormalMode: function () { + this._isCacheToBufferOn = false; + }, + + _removeCache: function (instanceID) { + instanceID = instanceID || this._currentID; + var cmds = this._cacheToBufferCmds[instanceID]; + if (cmds) { + cmds.length = 0; + delete this._cacheToBufferCmds[instanceID]; + } + + var locIDs = this._cacheInstanceIds; + cc.arrayRemoveObject(locIDs, instanceID); + }, + + /** + * drawing all renderer command to cache canvas' context + * @param {Number} [renderTextureId] + */ + _renderingToBuffer: function (renderTextureId) { + renderTextureId = renderTextureId || this._currentID; + var locCmds = this._cacheToBufferCmds[renderTextureId]; + var ctx = cc._renderContext; + this.rendering(ctx, locCmds); + this._removeCache(renderTextureId); + + var locIDs = this._cacheInstanceIds; + if (locIDs.length === 0) + this._isCacheToBufferOn = false; + else + this._currentID = locIDs[locIDs.length - 1]; + }, + + //reset renderer's flag + resetFlag: function () { + if (this.childrenOrderDirty) { + this.childrenOrderDirty = false; + } + this._transformNodePool.length = 0; + }, + + //update the transform data + transform: function () { + var locPool = this._transformNodePool; + //sort the pool + locPool.sort(this._sortNodeByLevelAsc); + //transform node + var i, len, cmd; + for (i = 0, len = locPool.length; i < len; i++) { + cmd = locPool[i]; + cmd.updateStatus(); + } + locPool.length = 0; + }, + + transformDirty: function () { + return this._transformNodePool.length > 0; + }, + + _sortNodeByLevelAsc: function (n1, n2) { + return n1._curLevel - n2._curLevel; + }, + + pushDirtyNode: function (node) { + //if (this._transformNodePool.indexOf(node) === -1) + this._transformNodePool.push(node); + }, + + clearRenderCommands: function () { + // Copy previous command list for late check in rendering + this._renderCmds.length = 0; + }, + + clear: function () { + var gl = cc._renderContext; + gl.clearColor(this._clearColor.r / 255, this._clearColor.g / 255, this._clearColor.b / 255, this._clearColor.a / 255); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + }, + + setDepthTest: function (enable) { + var gl = cc._renderContext; + if (enable) { + gl.clearDepth(1.0); + gl.enable(gl.DEPTH_TEST); + gl.depthFunc(gl.LEQUAL); + } + else { + gl.disable(gl.DEPTH_TEST); + } + }, + + pushRenderCommand: function (cmd) { + if (!cmd.rendering && !cmd.uploadData) + return; + if (this._isCacheToBufferOn) { + var currentId = this._currentID, locCmdBuffer = this._cacheToBufferCmds; + var cmdList = locCmdBuffer[currentId]; + if (cmdList.indexOf(cmd) === -1) + cmdList.push(cmd); + } else { + if (this._renderCmds.indexOf(cmd) === -1) { + this._renderCmds.push(cmd); + } + } + }, + + _increaseBatchingSize: function (increment, vertexType, indices) { + vertexType = vertexType || VertexType.QUAD; + var i, curr; + switch (vertexType) { + case VertexType.QUAD: + for (i = 0; i < increment; i += 4) { + curr = _batchingSize + i; + _indexData[_indexSize++] = curr + 0; + _indexData[_indexSize++] = curr + 1; + _indexData[_indexSize++] = curr + 2; + _indexData[_indexSize++] = curr + 1; + _indexData[_indexSize++] = curr + 2; + _indexData[_indexSize++] = curr + 3; + } + break; + case VertexType.TRIANGLE: + _pureQuad = false; + for (i = 0; i < increment; i += 3) { + curr = _batchingSize + i; + _indexData[_indexSize++] = curr + 0; + _indexData[_indexSize++] = curr + 1; + _indexData[_indexSize++] = curr + 2; + } + break; + case VertexType.CUSTOM: + // CUSTOM type increase the indices data + _pureQuad = false; + var len = indices.length; + for (i = 0; i < len; i++) { + _indexData[_indexSize++] = _batchingSize + indices[i]; + } + break; + default: + return; + } + _batchingSize += increment; + }, + + _updateBatchedInfo: function (texture, blendFunc, glProgramState) { + if (texture !== _batchedInfo.texture || + blendFunc.src !== _batchedInfo.blendSrc || + blendFunc.dst !== _batchedInfo.blendDst || + glProgramState !== _batchedInfo.glProgramState) { + // Draw batched elements + this._batchRendering(); + // Update _batchedInfo + _batchedInfo.texture = texture; + _batchedInfo.blendSrc = blendFunc.src; + _batchedInfo.blendDst = blendFunc.dst; + _batchedInfo.glProgramState = glProgramState; + + return true; + } + else { + return false; + } + }, + + _breakBatch: function () { + _batchBroken = true; + }, + + _uploadBufferData: function (cmd) { + if (_batchingSize >= _maxVertexSize) { + this._batchRendering(); + } + + // Check batching + var node = cmd._node; + var texture = node._texture || (node._spriteFrame && node._spriteFrame._texture); + var blendSrc = node._blendFunc.src; + var blendDst = node._blendFunc.dst; + var glProgramState = cmd._glProgramState; + if (_batchBroken || + _batchedInfo.texture !== texture || + _batchedInfo.blendSrc !== blendSrc || + _batchedInfo.blendDst !== blendDst || + _batchedInfo.glProgramState !== glProgramState) { + // Draw batched elements + this._batchRendering(); + // Update _batchedInfo + _batchedInfo.texture = texture; + _batchedInfo.blendSrc = blendSrc; + _batchedInfo.blendDst = blendDst; + _batchedInfo.glProgramState = glProgramState; + _batchBroken = false; + } + + // Upload vertex data + var len = cmd.uploadData(_vertexDataF32, _vertexDataUI32, _batchingSize * _sizePerVertex); + if (len > 0) { + this._increaseBatchingSize(len, cmd.vertexType); + } + }, + + _batchRendering: function () { + if (_batchingSize === 0 || !_batchedInfo.texture) { + return; + } + + var gl = cc._renderContext; + var texture = _batchedInfo.texture; + var glProgramState = _batchedInfo.glProgramState; + var uploadAll = _batchingSize > _maxVertexSize * 0.5; + + if (glProgramState) { + glProgramState.apply(); + glProgramState.getGLProgram()._updateProjectionUniform(); + } + + cc.glBlendFunc(_batchedInfo.blendSrc, _batchedInfo.blendDst); + cc.glBindTexture2DN(0, texture); // = cc.glBindTexture2D(texture); + + gl.bindBuffer(gl.ARRAY_BUFFER, _vertexBuffer); + // upload the vertex data to the gl buffer + if (uploadAll) { + gl.bufferData(gl.ARRAY_BUFFER, _vertexDataF32, gl.DYNAMIC_DRAW); + } + else { + var view = _vertexDataF32.subarray(0, _batchingSize * _sizePerVertex); + gl.bufferData(gl.ARRAY_BUFFER, view, gl.DYNAMIC_DRAW); + } + + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_POSITION); + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_COLOR); + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_TEX_COORDS); + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 3, gl.FLOAT, false, 24, 0); + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, gl.UNSIGNED_BYTE, true, 24, 12); + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, gl.FLOAT, false, 24, 16); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, _indexBuffer); + if (!_prevIndexSize || !_pureQuad || _indexSize > _prevIndexSize) { + if (uploadAll) { + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, _indexData, gl.DYNAMIC_DRAW); + } + else { + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, _indexData.subarray(0, _indexSize), gl.DYNAMIC_DRAW); + } + } + gl.drawElements(gl.TRIANGLES, _indexSize, gl.UNSIGNED_SHORT, 0); + + cc.g_NumberOfDraws++; + + if (_pureQuad) { + _prevIndexSize = _indexSize; + } + else { + _prevIndexSize = 0; + _pureQuad = true; + } + _batchingSize = 0; + _indexSize = 0; + }, + + /** + * drawing all renderer command to context (default is cc._renderContext) + * @param {WebGLRenderingContext} [ctx=cc._renderContext] + */ + rendering: function (ctx, cmds) { + var locCmds = cmds || this._renderCmds, + i, len, cmd, + context = ctx || cc._renderContext; + + // Reset buffer for rendering + context.bindBuffer(gl.ARRAY_BUFFER, null); + + for (i = 0, len = locCmds.length; i < len; ++i) { + cmd = locCmds[i]; + if (!cmd.needDraw()) continue; + + if (cmd.uploadData) { + this._uploadBufferData(cmd); + } + else { + if (_batchingSize > 0) { + this._batchRendering(); + } + cmd.rendering(context); + } + } + this._batchRendering(); + _batchedInfo.texture = null; + } +}; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/core/scenes/CCLoaderScene.js b/frameworks/cocos2d-html5/cocos2d/core/scenes/CCLoaderScene.js new file mode 100644 index 0000000..2f05181 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/scenes/CCLoaderScene.js @@ -0,0 +1,165 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ +/** + *

cc.LoaderScene is a scene that you can load it when you loading files

+ *

cc.LoaderScene can present thedownload progress

+ * @class + * @extends cc.Scene + * @example + * var lc = new cc.LoaderScene(); + */ +cc.LoaderScene = cc.Scene.extend({ + _interval : null, + _label : null, + _logo : null, + _className:"LoaderScene", + cb: null, + target: null, + /** + * Contructor of cc.LoaderScene + * @returns {boolean} + */ + init : function(){ + var self = this; + + //logo + var logoWidth = 160; + var logoHeight = 200; + + // bg + var bgLayer = self._bgLayer = new cc.LayerColor(cc.color(32, 32, 32, 255)); + self.addChild(bgLayer, 0); + + //image move to CCSceneFile.js + var fontSize = 24, lblHeight = -logoHeight / 2 + 100; + if(cc._loaderImage){ + //loading logo + cc.loader.loadImg(cc._loaderImage, {isCrossOrigin : false }, function(err, img){ + logoWidth = img.width; + logoHeight = img.height; + self._initStage(img, cc.visibleRect.center); + }); + fontSize = 14; + lblHeight = -logoHeight / 2 - 10; + } + //loading percent + var label = self._label = new cc.LabelTTF("Loading... 0%", "Arial", fontSize); + label.setPosition(cc.pAdd(cc.visibleRect.center, cc.p(0, lblHeight))); + label.setColor(cc.color(180, 180, 180)); + bgLayer.addChild(this._label, 10); + return true; + }, + + _initStage: function (img, centerPos) { + var self = this; + var texture2d = self._texture2d = new cc.Texture2D(); + texture2d.initWithElement(img); + texture2d.handleLoadedTexture(); + var logo = self._logo = new cc.Sprite(texture2d); + logo.setScale(cc.contentScaleFactor()); + logo.x = centerPos.x; + logo.y = centerPos.y; + self._bgLayer.addChild(logo, 10); + }, + /** + * custom onEnter + */ + onEnter: function () { + var self = this; + cc.Node.prototype.onEnter.call(self); + self.schedule(self._startLoading, 0.3); + }, + /** + * custom onExit + */ + onExit: function () { + cc.Node.prototype.onExit.call(this); + var tmpStr = "Loading... 0%"; + this._label.setString(tmpStr); + }, + + /** + * init with resources + * @param {Array} resources + * @param {Function|String} cb + * @param {Object} target + */ + initWithResources: function (resources, cb, target) { + if(cc.isString(resources)) + resources = [resources]; + this.resources = resources || []; + this.cb = cb; + this.target = target; + }, + + _startLoading: function () { + var self = this; + self.unschedule(self._startLoading); + var res = self.resources; + cc.loader.load(res, + function (result, count, loadedCount) { + var percent = (loadedCount / count * 100) | 0; + percent = Math.min(percent, 100); + self._label.setString("Loading... " + percent + "%"); + }, function () { + if (self.cb) + self.cb.call(self.target); + }); + }, + + _updateTransform: function(){ + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + this._bgLayer._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + this._label._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + this._logo && this._logo._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + } +}); +/** + *

cc.LoaderScene.preload can present a loaderScene with download progress.

+ *

when all the resource are downloaded it will invoke call function

+ * @param resources + * @param cb + * @param target + * @returns {cc.LoaderScene|*} + * @example + * //Example + * cc.LoaderScene.preload(g_resources, function () { + cc.director.runScene(new HelloWorldScene()); + }, this); + */ +cc.LoaderScene.preload = function(resources, cb, target){ + var _cc = cc; + if(!_cc.loaderScene) { + _cc.loaderScene = new cc.LoaderScene(); + _cc.loaderScene.init(); + cc.eventManager.addCustomListener(cc.Director.EVENT_PROJECTION_CHANGED, function(){ + _cc.loaderScene._updateTransform(); + }); + } + _cc.loaderScene.initWithResources(resources, cb, target); + + cc.director.runScene(_cc.loaderScene); + return _cc.loaderScene; +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/cocos2d/core/scenes/CCScene.js b/frameworks/cocos2d-html5/cocos2d/core/scenes/CCScene.js new file mode 100644 index 0000000..a54a179 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/scenes/CCScene.js @@ -0,0 +1,62 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + + +/** + *

cc.Scene is a subclass of cc.Node that is used only as an abstract concept.

+ *

cc.Scene an cc.Node are almost identical with the difference that cc.Scene has it's + * anchor point (by default) at the center of the screen.

+ * + *

For the moment cc.Scene has no other logic than that, but in future releases it might have + * additional logic.

+ * + *

It is a good practice to use and cc.Scene as the parent of all your nodes.

+ * @class + * @extends cc.Node + * @example + * var scene = new cc.Scene(); + */ +cc.Scene = cc.Node.extend(/** @lends cc.Scene# */{ + /** + * Constructor of cc.Scene + */ + _className:"Scene", + ctor:function () { + cc.Node.prototype.ctor.call(this); + this._ignoreAnchorPointForPosition = true; + this.setAnchorPoint(0.5, 0.5); + this.setContentSize(cc.director.getWinSize()); + } +}); + +/** + * creates a scene + * @deprecated since v3.0,please use new cc.Scene() instead. + * @return {cc.Scene} + */ +cc.Scene.create = function () { + return new cc.Scene(); +}; diff --git a/frameworks/cocos2d-html5/cocos2d/core/sprites/CCAnimation.js b/frameworks/cocos2d-html5/cocos2d/core/sprites/CCAnimation.js new file mode 100644 index 0000000..1c653a0 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/sprites/CCAnimation.js @@ -0,0 +1,477 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + *

+ * cc.AnimationFrame + * A frame of the animation. It contains information like: + * - sprite frame name + * - # of delay units. + * - offset + *

+ * @class + * @extends cc.Class + * @param spriteFrame + * @param delayUnits + * @param userInfo + * @returns {AnimationFrame} + */ +cc.AnimationFrame = cc.Class.extend(/** @lends cc.AnimationFrame# */{ + _spriteFrame:null, + _delayPerUnit:0, + _userInfo:null, + + ctor:function (spriteFrame, delayUnits, userInfo) { + this._spriteFrame = spriteFrame || null; + this._delayPerUnit = delayUnits || 0; + this._userInfo = userInfo || null; + }, + + /** + * Create a new animation frame and copy all contents into it + * @returns {AnimationFrame} + */ + clone: function(){ + var frame = new cc.AnimationFrame(); + frame.initWithSpriteFrame(this._spriteFrame.clone(), this._delayPerUnit, this._userInfo); + return frame; + }, + + /** + * Create a new animation frame and copy all contents into it + * @returns {AnimationFrame} + */ + copyWithZone:function (pZone) { + return cc.clone(this); + }, + + /** + * Create a new animation frame and copy all contents into it + * @returns {AnimationFrame} + */ + copy:function (pZone) { + var newFrame = new cc.AnimationFrame(); + newFrame.initWithSpriteFrame(this._spriteFrame.clone(), this._delayPerUnit, this._userInfo); + return newFrame; + }, + + /** + * initializes the animation frame with a spriteframe, number of delay units and a notification user info + * @param {cc.SpriteFrame} spriteFrame + * @param {Number} delayUnits + * @param {object} userInfo + */ + initWithSpriteFrame:function (spriteFrame, delayUnits, userInfo) { + this._spriteFrame = spriteFrame; + this._delayPerUnit = delayUnits; + this._userInfo = userInfo; + + return true; + }, + + /** + * Returns sprite frame to be used + * @return {cc.SpriteFrame} + */ + getSpriteFrame:function () { + return this._spriteFrame; + }, + + /** + * Sets sprite frame to be used + * @param {cc.SpriteFrame} spriteFrame + */ + setSpriteFrame:function (spriteFrame) { + this._spriteFrame = spriteFrame; + }, + + /** + * Returns how many units of time the frame takes getter + * @return {Number} + */ + getDelayUnits:function () { + return this._delayPerUnit; + }, + + /** + * Sets how many units of time the frame takes setter + * @param delayUnits + */ + setDelayUnits:function (delayUnits) { + this._delayPerUnit = delayUnits; + }, + + /** + * Returns the user custom information + * @return {object} + */ + getUserInfo:function () { + return this._userInfo; + }, + + /** + * Sets the user custom information + * @param {object} userInfo + */ + setUserInfo:function (userInfo) { + this._userInfo = userInfo; + } +}); + +/** + * Creates an animation frame. + * @deprecated since v3.0, please use the new construction instead + * @param {cc.SpriteFrame} spriteFrame + * @param {Number} delayUnits + * @param {object} userInfo + * @see cc.AnimationFrame + */ +cc.AnimationFrame.create = function(spriteFrame,delayUnits,userInfo){ + return new cc.AnimationFrame(spriteFrame,delayUnits,userInfo); +}; + +/** + *

+ * A cc.Animation object is used to perform animations on the cc.Sprite objects.
+ *
+ * The cc.Animation object contains cc.SpriteFrame objects, and a possible delay between the frames.
+ * You can animate a cc.Animation object by using the cc.Animate action. + *

+ * @class + * @extends cc.Class + * @param {Array} frames + * @param {Number} delay + * @param {Number} [loops=1] + * + * @example + * // 1. Creates an empty animation + * var animation1 = new cc.Animation(); + * + * // 2. Create an animation with sprite frames, delay and loops. + * var spriteFrames = []; + * var frame = cc.spriteFrameCache.getSpriteFrame("grossini_dance_01.png"); + * spriteFrames.push(frame); + * var animation1 = new cc.Animation(spriteFrames); + * var animation2 = new cc.Animation(spriteFrames, 0.2); + * var animation2 = new cc.Animation(spriteFrames, 0.2, 2); + * + * // 3. Create an animation with animation frames, delay and loops. + * var animationFrames = []; + * var frame = new cc.AnimationFrame(); + * animationFrames.push(frame); + * var animation1 = new cc.Animation(animationFrames); + * var animation2 = new cc.Animation(animationFrames, 0.2); + * var animation3 = new cc.Animation(animationFrames, 0.2, 2); + * + * //create an animate with this animation + * var action = cc.animate(animation1); + * + * //run animate + * sprite.runAction(action); + */ +cc.Animation = cc.Class.extend(/** @lends cc.Animation# */{ + _frames:null, + _loops:0, + _restoreOriginalFrame:false, + _duration:0, + _delayPerUnit:0, + _totalDelayUnits:0, + + ctor:function (frames, delay, loops) { + this._frames = []; + + if (frames === undefined) { + this.initWithSpriteFrames(null, 0); + } else { + var frame0 = frames[0]; + if(frame0){ + if (frame0 instanceof cc.SpriteFrame) { + //init with sprite frames , delay and loops. + this.initWithSpriteFrames(frames, delay, loops); + }else if(frame0 instanceof cc.AnimationFrame) { + //init with sprite frames , delay and loops. + this.initWithAnimationFrames(frames, delay, loops); + } + } + } + }, + + // attributes + + /** + * Returns the array of animation frames + * @return {Array} + */ + getFrames:function () { + return this._frames; + }, + + /** + * Sets array of animation frames + * @param {Array} frames + */ + setFrames:function (frames) { + this._frames = frames; + }, + + /** + * Adds a frame to a cc.Animation, the frame will be added with one "delay unit". + * @param {cc.SpriteFrame} frame + */ + addSpriteFrame:function (frame) { + var animFrame = new cc.AnimationFrame(); + + animFrame.initWithSpriteFrame(frame, 1, null); + this._frames.push(animFrame); + // update duration + this._totalDelayUnits++; + }, + + /** + * Adds a frame with an image filename. Internally it will create a cc.SpriteFrame and it will add it. The frame will be added with one "delay unit". + * @param {String} fileName + */ + addSpriteFrameWithFile:function (fileName) { + var texture = cc.textureCache.addImage(fileName); + var rect = cc.rect(0, 0, 0, 0); + rect.width = texture.width; + rect.height = texture.height; + var frame = new cc.SpriteFrame(texture, rect); + this.addSpriteFrame(frame); + }, + + /** + * Adds a frame with a texture and a rect. Internally it will create a cc.SpriteFrame and it will add it. The frame will be added with one "delay unit". + * @param {cc.Texture2D} texture + * @param {cc.Rect} rect + */ + addSpriteFrameWithTexture:function (texture, rect) { + var pFrame = new cc.SpriteFrame(texture, rect); + this.addSpriteFrame(pFrame); + }, + + /** + * Initializes a cc.Animation with cc.AnimationFrame, do not call this method yourself, please pass parameters to constructor to initialize. + * @param {Array} arrayOfAnimationFrames + * @param {Number} delayPerUnit + * @param {Number} [loops=1] + */ + initWithAnimationFrames:function (arrayOfAnimationFrames, delayPerUnit, loops) { + cc.arrayVerifyType(arrayOfAnimationFrames, cc.AnimationFrame); + + this._delayPerUnit = delayPerUnit; + this._loops = loops === undefined ? 1 : loops; + this._totalDelayUnits = 0; + + var locFrames = this._frames; + locFrames.length = 0; + for (var i = 0; i < arrayOfAnimationFrames.length; i++) { + var animFrame = arrayOfAnimationFrames[i]; + locFrames.push(animFrame); + this._totalDelayUnits += animFrame.getDelayUnits(); + } + + return true; + }, + + /** + * Clone the current animation + * @return {cc.Animation} + */ + clone: function(){ + var animation = new cc.Animation(); + animation.initWithAnimationFrames(this._copyFrames(), this._delayPerUnit, this._loops); + animation.setRestoreOriginalFrame(this._restoreOriginalFrame); + return animation; + }, + + /** + * Clone the current animation + * @return {cc.Animation} + */ + copyWithZone:function (pZone) { + var pCopy = new cc.Animation(); + pCopy.initWithAnimationFrames(this._copyFrames(), this._delayPerUnit, this._loops); + pCopy.setRestoreOriginalFrame(this._restoreOriginalFrame); + return pCopy; + }, + + _copyFrames:function(){ + var copyFrames = []; + for(var i = 0; i< this._frames.length;i++) + copyFrames.push(this._frames[i].clone()); + return copyFrames; + }, + + /** + * Clone the current animation + * @param pZone + * @returns {cc.Animation} + */ + copy:function (pZone) { + return this.copyWithZone(null); + }, + + /** + * Returns how many times the animation is going to loop. 0 means animation is not animated. 1, animation is executed one time, ... + * @return {Number} + */ + getLoops:function () { + return this._loops; + }, + + /** + * Sets how many times the animation is going to loop. 0 means animation is not animated. 1, animation is executed one time, ... + * @param {Number} value + */ + setLoops:function (value) { + this._loops = value; + }, + + /** + * Sets whether or not it shall restore the original frame when the animation finishes + * @param {Boolean} restOrigFrame + */ + setRestoreOriginalFrame:function (restOrigFrame) { + this._restoreOriginalFrame = restOrigFrame; + }, + + /** + * Returns whether or not it shall restore the original frame when the animation finishes + * @return {Boolean} + */ + getRestoreOriginalFrame:function () { + return this._restoreOriginalFrame; + }, + + /** + * Returns duration in seconds of the whole animation. It is the result of totalDelayUnits * delayPerUnit + * @return {Number} + */ + getDuration:function () { + return this._totalDelayUnits * this._delayPerUnit; + }, + + /** + * Returns delay in seconds of the "delay unit" + * @return {Number} + */ + getDelayPerUnit:function () { + return this._delayPerUnit; + }, + + /** + * Sets delay in seconds of the "delay unit" + * @param {Number} delayPerUnit + */ + setDelayPerUnit:function (delayPerUnit) { + this._delayPerUnit = delayPerUnit; + }, + + /** + * Returns total delay units of the cc.Animation. + * @return {Number} + */ + getTotalDelayUnits:function () { + return this._totalDelayUnits; + }, + + /** + * Initializes a cc.Animation with frames and a delay between frames, do not call this method yourself, please pass parameters to constructor to initialize. + * @param {Array} frames + * @param {Number} delay + * @param {Number} [loops=1] + */ + initWithSpriteFrames:function (frames, delay, loops) { + cc.arrayVerifyType(frames, cc.SpriteFrame); + this._loops = loops === undefined ? 1 : loops; + this._delayPerUnit = delay || 0; + this._totalDelayUnits = 0; + + var locFrames = this._frames; + locFrames.length = 0; + if (frames) { + for (var i = 0; i < frames.length; i++) { + var frame = frames[i]; + var animFrame = new cc.AnimationFrame(); + animFrame.initWithSpriteFrame(frame, 1, null); + locFrames.push(animFrame); + } + this._totalDelayUnits += frames.length; + } + return true; + }, + /** + *

Currently JavaScript Bindings (JSB), in some cases, needs to use retain and release. This is a bug in JSB, + * and the ugly workaround is to use retain/release. So, these 2 methods were added to be compatible with JSB. + * This is a hack, and should be removed once JSB fixes the retain/release bug
+ * You will need to retain an object if you created an engine object and haven't added it into the scene graph during the same frame.
+ * Otherwise, JSB's native autorelease pool will consider this object a useless one and release it directly,
+ * when you want to use it later, a "Invalid Native Object" error will be raised.
+ * The retain function can increase a reference count for the native object to avoid it being released,
+ * you need to manually invoke release function when you think this object is no longer needed, otherwise, there will be memory learks.
+ * retain and release function call should be paired in developer's game code.

+ * @function + * @see cc.Animation#release + */ + retain:function () { + }, + /** + *

Currently JavaScript Bindings (JSB), in some cases, needs to use retain and release. This is a bug in JSB, + * and the ugly workaround is to use retain/release. So, these 2 methods were added to be compatible with JSB. + * This is a hack, and should be removed once JSB fixes the retain/release bug
+ * You will need to retain an object if you created an engine object and haven't added it into the scene graph during the same frame.
+ * Otherwise, JSB's native autorelease pool will consider this object a useless one and release it directly,
+ * when you want to use it later, a "Invalid Native Object" error will be raised.
+ * The retain function can increase a reference count for the native object to avoid it being released,
+ * you need to manually invoke release function when you think this object is no longer needed, otherwise, there will be memory learks.
+ * retain and release function call should be paired in developer's game code.

+ * @function + * @see cc.Animation#retain + */ + release:function () { + } +}); + +/** + * Creates an animation. + * @deprecated since v3.0, please use new construction instead + * @see cc.Animation + * @param {Array} frames + * @param {Number} delay + * @param {Number} [loops=1] + * @return {cc.Animation} + */ +cc.Animation.create = function (frames, delay, loops) { + return new cc.Animation(frames, delay, loops); +}; + +/** + * @deprecated since v3.0, please use new construction instead + * @see cc.Animation + * @type {Function} + */ +cc.Animation.createWithAnimationFrames = cc.Animation.create; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/cocos2d/core/sprites/CCAnimationCache.js b/frameworks/cocos2d-html5/cocos2d/core/sprites/CCAnimationCache.js new file mode 100644 index 0000000..d156393 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/sprites/CCAnimationCache.js @@ -0,0 +1,213 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + *

+ * cc.animationCache is a singleton object that manages the Animations.
+ * It saves in a cache the animations. You should use this class if you want to save your animations in a cache.
+ *
+ * example
+ * cc.animationCache.addAnimation(animation,"animation1");
+ *

+ * @class + * @name cc.animationCache + */ +cc.animationCache = /** @lends cc.animationCache# */{ + _animations: {}, + + /** + * Adds a cc.Animation with a name. + * @param {cc.Animation} animation + * @param {String} name + */ + addAnimation:function (animation, name) { + this._animations[name] = animation; + }, + + /** + * Deletes a cc.Animation from the cache. + * @param {String} name + */ + removeAnimation:function (name) { + if (!name) { + return; + } + if (this._animations[name]) { + delete this._animations[name]; + } + }, + + /** + *

+ * Returns a cc.Animation that was previously added.
+ * If the name is not found it will return nil.
+ * You should retain the returned copy if you are going to use it.
+ *

+ * @param {String} name + * @return {cc.Animation} + */ + getAnimation:function (name) { + if (this._animations[name]) + return this._animations[name]; + return null; + }, + + _addAnimationsWithDictionary:function (dictionary,plist) { + var animations = dictionary["animations"]; + if (!animations) { + cc.log(cc._LogInfos.animationCache__addAnimationsWithDictionary); + return; + } + + var version = 1; + var properties = dictionary["properties"]; + if (properties) { + version = (properties["format"] != null) ? parseInt(properties["format"]) : version; + var spritesheets = properties["spritesheets"]; + var spriteFrameCache = cc.spriteFrameCache; + var path = cc.path; + for (var i = 0; i < spritesheets.length; i++) { + spriteFrameCache.addSpriteFrames(path.changeBasename(plist, spritesheets[i])); + } + } + + switch (version) { + case 1: + this._parseVersion1(animations); + break; + case 2: + this._parseVersion2(animations); + break; + default : + cc.log(cc._LogInfos.animationCache__addAnimationsWithDictionary_2); + break; + } + }, + + /** + *

+ * Adds an animations from a plist file.
+ * Make sure that the frames were previously loaded in the cc.SpriteFrameCache. + *

+ * @param {String} plist + */ + addAnimations:function (plist) { + + cc.assert(plist, cc._LogInfos.animationCache_addAnimations_2); + + var dict = cc.loader.getRes(plist); + + if(!dict){ + cc.log(cc._LogInfos.animationCache_addAnimations); + return; + } + + this._addAnimationsWithDictionary(dict,plist); + }, + + _parseVersion1:function (animations) { + var frameCache = cc.spriteFrameCache; + + for (var key in animations) { + var animationDict = animations[key]; + var frameNames = animationDict["frames"]; + var delay = parseFloat(animationDict["delay"]) || 0; + var animation = null; + if (!frameNames) { + cc.log(cc._LogInfos.animationCache__parseVersion1, key); + continue; + } + + var frames = []; + for (var i = 0; i < frameNames.length; i++) { + var spriteFrame = frameCache.getSpriteFrame(frameNames[i]); + if (!spriteFrame) { + cc.log(cc._LogInfos.animationCache__parseVersion1_2, key, frameNames[i]); + continue; + } + var animFrame = new cc.AnimationFrame(); + animFrame.initWithSpriteFrame(spriteFrame, 1, null); + frames.push(animFrame); + } + + if (frames.length === 0) { + cc.log(cc._LogInfos.animationCache__parseVersion1_3, key); + continue; + } else if (frames.length !== frameNames.length) { + cc.log(cc._LogInfos.animationCache__parseVersion1_4, key); + } + animation = new cc.Animation(frames, delay, 1); + cc.animationCache.addAnimation(animation, key); + } + }, + + _parseVersion2:function (animations) { + var frameCache = cc.spriteFrameCache; + + for (var key in animations) { + var animationDict = animations[key]; + + var isLoop = animationDict["loop"]; + var loopsTemp = parseInt(animationDict["loops"]); + var loops = isLoop ? cc.REPEAT_FOREVER : ((isNaN(loopsTemp)) ? 1 : loopsTemp); + var restoreOriginalFrame = (animationDict["restoreOriginalFrame"] && animationDict["restoreOriginalFrame"] == true) ? true : false; + var frameArray = animationDict["frames"]; + + if (!frameArray) { + cc.log(cc._LogInfos.animationCache__parseVersion2, key); + continue; + } + + //Array of AnimationFrames + var arr = []; + for (var i = 0; i < frameArray.length; i++) { + var entry = frameArray[i]; + var spriteFrameName = entry["spriteframe"]; + var spriteFrame = frameCache.getSpriteFrame(spriteFrameName); + if (!spriteFrame) { + cc.log(cc._LogInfos.animationCache__parseVersion2_2, key, spriteFrameName); + continue; + } + + var delayUnits = parseFloat(entry["delayUnits"]) || 0; + var userInfo = entry["notification"]; + var animFrame = new cc.AnimationFrame(); + animFrame.initWithSpriteFrame(spriteFrame, delayUnits, userInfo); + arr.push(animFrame); + } + + var delayPerUnit = parseFloat(animationDict["delayPerUnit"]) || 0; + var animation = new cc.Animation(); + animation.initWithAnimationFrames(arr, delayPerUnit, loops); + animation.setRestoreOriginalFrame(restoreOriginalFrame); + cc.animationCache.addAnimation(animation, key); + } + }, + + _clear: function () { + this._animations = {}; + } +}; diff --git a/frameworks/cocos2d-html5/cocos2d/core/sprites/CCBakeSprite.js b/frameworks/cocos2d-html5/cocos2d/core/sprites/CCBakeSprite.js new file mode 100644 index 0000000..33e4ce2 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/sprites/CCBakeSprite.js @@ -0,0 +1,78 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + Permission is hereby granted, free of charge, to any person obtaining a copy + of _t 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. + ****************************************************************************/ + +/** + * cc.BakeSprite is a type of sprite that will be cached. + * @class + * @extend cc.Sprite + */ +cc.BakeSprite = cc.Sprite.extend(/** @lends cc.BakeSprite# */{ + _cacheCanvas: null, + _cacheContext: null, + + ctor: function(){ + cc.Sprite.prototype.ctor.call(this); + var canvasElement = document.createElement("canvas"); + canvasElement.width = canvasElement.height = 10; + this._cacheCanvas = canvasElement; + this._cacheContext = new cc.CanvasContextWrapper(canvasElement.getContext("2d")); + + var texture = new cc.Texture2D(); + texture.initWithElement(canvasElement); + texture.handleLoadedTexture(); + this.setTexture(texture); + }, + + getCacheContext: function(){ + return this._cacheContext; + }, + + getCacheCanvas: function(){ + return this._cacheCanvas; + }, + + /** + * reset the cache canvas size + * @param {cc.Size|Number} sizeOrWidth size or width + * @param {Number} [height] + */ + resetCanvasSize: function(sizeOrWidth, height){ + var locCanvas = this._cacheCanvas, + locContext = this._cacheContext, + strokeStyle = locContext._context.strokeStyle, + fillStyle = locContext._context.fillStyle; + if(height === undefined){ + height = sizeOrWidth.height; + sizeOrWidth = sizeOrWidth.width; + } + locCanvas.width = sizeOrWidth; + locCanvas.height = height; //TODO note baidu browser reset the context after set width or height + if(strokeStyle !== locContext._context.strokeStyle) + locContext._context.strokeStyle = strokeStyle; + if(fillStyle !== locContext._context.fillStyle) + locContext._context.fillStyle = fillStyle; + this.getTexture().handleLoadedTexture(); + this.setTextureRect(cc.rect(0,0, sizeOrWidth, height), false, null, false); + } +}); diff --git a/frameworks/cocos2d-html5/cocos2d/core/sprites/CCSprite.js b/frameworks/cocos2d-html5/cocos2d/core/sprites/CCSprite.js new file mode 100644 index 0000000..d450424 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/sprites/CCSprite.js @@ -0,0 +1,892 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + *

cc.Sprite is a 2d image ( http://en.wikipedia.org/wiki/Sprite_(computer_graphics) )
+ * + * cc.Sprite can be created with an image, or with a sub-rectangle of an image.
+ * + * If the parent or any of its ancestors is a cc.SpriteBatchNode then the following features/limitations are valid
+ * - Features when the parent is a cc.BatchNode:
+ * - MUCH faster rendering, specially if the cc.SpriteBatchNode has many children. All the children will be drawn in a single batch.
+ * + * - Limitations
+ * - Camera is not supported yet (eg: CCOrbitCamera action doesn't work)
+ * - GridBase actions are not supported (eg: CCLens, CCRipple, CCTwirl)
+ * - The Alias/Antialias property belongs to CCSpriteBatchNode, so you can't individually set the aliased property.
+ * - The Blending function property belongs to CCSpriteBatchNode, so you can't individually set the blending function property.
+ * - Parallax scroller is not supported, but can be simulated with a "proxy" sprite.
+ * + * If the parent is an standard cc.Node, then cc.Sprite behaves like any other cc.Node:
+ * - It supports blending functions
+ * - It supports aliasing / antialiasing
+ * - But the rendering will be slower: 1 draw per children.
+ * + * The default anchorPoint in cc.Sprite is (0.5, 0.5).

+ * @class + * @extends cc.Node + * + * @param {String|cc.SpriteFrame|HTMLImageElement|cc.Texture2D} fileName The string which indicates a path to image file, e.g., "scene1/monster.png". + * @param {cc.Rect} [rect] Only the contents inside rect of pszFileName's texture will be applied for this sprite. + * @param {Boolean} [rotated] Whether or not the texture rectangle is rotated. + * @example + * + * 1.Create a sprite with image path and rect + * var sprite1 = new cc.Sprite("res/HelloHTML5World.png"); + * var sprite2 = new cc.Sprite("res/HelloHTML5World.png",cc.rect(0,0,480,320)); + * + * 2.Create a sprite with a sprite frame name. Must add "#" before frame name. + * var sprite = new cc.Sprite('#grossini_dance_01.png'); + * + * 3.Create a sprite with a sprite frame + * var spriteFrame = cc.spriteFrameCache.getSpriteFrame("grossini_dance_01.png"); + * var sprite = new cc.Sprite(spriteFrame); + * + * 4.Create a sprite with an existing texture contained in a CCTexture2D object + * After creation, the rect will be the size of the texture, and the offset will be (0,0). + * var texture = cc.textureCache.addImage("HelloHTML5World.png"); + * var sprite1 = new cc.Sprite(texture); + * var sprite2 = new cc.Sprite(texture, cc.rect(0,0,480,320)); + * + * @property {Boolean} dirty - Indicates whether the sprite needs to be updated. + * @property {Boolean} flippedX - Indicates whether or not the sprite is flipped on x axis. + * @property {Boolean} flippedY - Indicates whether or not the sprite is flipped on y axis. + * @property {Number} offsetX - <@readonly> The offset position on x axis of the sprite in texture. Calculated automatically by editors like Zwoptex. + * @property {Number} offsetY - <@readonly> The offset position on x axis of the sprite in texture. Calculated automatically by editors like Zwoptex. + * @property {Number} atlasIndex - The index used on the TextureAtlas. + * @property {cc.Texture2D} texture - Texture used to render the sprite. + * @property {Boolean} textureRectRotated - <@readonly> Indicate whether the texture rectangle is rotated. + * @property {cc.TextureAtlas} textureAtlas - The weak reference of the cc.TextureAtlas when the sprite is rendered using via cc.SpriteBatchNode. + * @property {cc.SpriteBatchNode} batchNode - The batch node object if this sprite is rendered by cc.SpriteBatchNode. + * @property {cc.V3F_C4B_T2F_Quad} quad - <@readonly> The quad (tex coords, vertex coords and color) information. + */ +cc.Sprite = cc.Node.extend(/** @lends cc.Sprite# */{ + dirty: false, + atlasIndex: 0, + textureAtlas: null, + + _batchNode: null, + _recursiveDirty: null, //Whether all of the sprite's children needs to be updated + _hasChildren: null, //Whether the sprite contains children + _shouldBeHidden: false, //should not be drawn because one of the ancestors is not visible + _transformToBatch: null, + + // + // Data used when the sprite is self-rendered + // + _blendFunc: null, //It's required for CCTextureProtocol inheritance + _texture: null, //cc.Texture2D object that is used to render the sprite + + // + // Shared data + // + // texture + _rect: null, //Rectangle of cc.Texture2D + _rectRotated: false, //Whether the texture is rotated + + // Offset Position (used by Zwoptex) + _offsetPosition: null, // absolute + _unflippedOffsetPositionFromCenter: null, + + _opacityModifyRGB: false, + + // image is flipped + _flippedX: false, //Whether the sprite is flipped horizontally or not. + _flippedY: false, //Whether the sprite is flipped vertically or not. + + _textureLoaded: false, + _className: "Sprite", + + ctor: function (fileName, rect, rotated) { + var self = this; + cc.Node.prototype.ctor.call(self); + // default transform anchor: center + this.setAnchorPoint(0.5, 0.5); + + self._loader = new cc.Sprite.LoadManager(); + self._shouldBeHidden = false; + self._offsetPosition = cc.p(0, 0); + self._unflippedOffsetPositionFromCenter = cc.p(0, 0); + self._blendFunc = {src: cc.BLEND_SRC, dst: cc.BLEND_DST}; + self._rect = cc.rect(0, 0, 0, 0); + + self._softInit(fileName, rect, rotated); + }, + + /** + * Returns whether the texture have been loaded + * @returns {boolean} + */ + textureLoaded: function () { + return this._textureLoaded; + }, + + /** + * Add a event listener for texture loaded event. + * @param {Function} callback + * @param {Object} target + * @deprecated since 3.1, please use addEventListener instead + */ + addLoadedEventListener: function (callback, target) { + this.addEventListener("load", callback, target); + }, + + /** + * Returns whether or not the Sprite needs to be updated in the Atlas + * @return {Boolean} True if the sprite needs to be updated in the Atlas, false otherwise. + */ + isDirty: function () { + return this.dirty; + }, + + /** + * Makes the sprite to be updated in the Atlas. + * @param {Boolean} bDirty + */ + setDirty: function (bDirty) { + this.dirty = bDirty; + }, + + /** + * Returns whether or not the texture rectangle is rotated. + * @return {Boolean} + */ + isTextureRectRotated: function () { + return this._rectRotated; + }, + + /** + * Returns the index used on the TextureAtlas. + * @return {Number} + */ + getAtlasIndex: function () { + return this.atlasIndex; + }, + + /** + * Sets the index used on the TextureAtlas. + * @warning Don't modify this value unless you know what you are doing + * @param {Number} atlasIndex + */ + setAtlasIndex: function (atlasIndex) { + this.atlasIndex = atlasIndex; + }, + + /** + * Returns the rect of the cc.Sprite in points + * @return {cc.Rect} + */ + getTextureRect: function () { + return cc.rect(this._rect); + }, + + /** + * Returns the weak reference of the cc.TextureAtlas when the sprite is rendered using via cc.SpriteBatchNode + * @return {cc.TextureAtlas} + */ + getTextureAtlas: function () { + return this.textureAtlas; + }, + + /** + * Sets the weak reference of the cc.TextureAtlas when the sprite is rendered using via cc.SpriteBatchNode + * @param {cc.TextureAtlas} textureAtlas + */ + setTextureAtlas: function (textureAtlas) { + this.textureAtlas = textureAtlas; + }, + + /** + * Returns the offset position of the sprite. Calculated automatically by editors like Zwoptex. + * @return {cc.Point} + */ + getOffsetPosition: function () { + return cc.p(this._offsetPosition); + }, + + _getOffsetX: function () { + return this._offsetPosition.x; + }, + _getOffsetY: function () { + return this._offsetPosition.y; + }, + + /** + * Returns the blend function + * @return {cc.BlendFunc} + */ + getBlendFunc: function () { + return this._blendFunc; + }, + + /** + * Initializes a sprite with a SpriteFrame. The texture and rect in SpriteFrame will be applied on this sprite.
+ * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself, + * @param {cc.SpriteFrame} spriteFrame A CCSpriteFrame object. It should includes a valid texture and a rect + * @return {Boolean} true if the sprite is initialized properly, false otherwise. + */ + initWithSpriteFrame: function (spriteFrame) { + cc.assert(spriteFrame, cc._LogInfos.Sprite_initWithSpriteFrame); + return this.setSpriteFrame(spriteFrame); + }, + + /** + * Initializes a sprite with a sprite frame name.
+ * A cc.SpriteFrame will be fetched from the cc.SpriteFrameCache by name.
+ * If the cc.SpriteFrame doesn't exist it will raise an exception.
+ * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself. + * @param {String} spriteFrameName A key string that can fected a valid cc.SpriteFrame from cc.SpriteFrameCache + * @return {Boolean} true if the sprite is initialized properly, false otherwise. + * @example + * var sprite = new cc.Sprite(); + * sprite.initWithSpriteFrameName("grossini_dance_01.png"); + */ + initWithSpriteFrameName: function (spriteFrameName) { + cc.assert(spriteFrameName, cc._LogInfos.Sprite_initWithSpriteFrameName); + var frame = cc.spriteFrameCache.getSpriteFrame(spriteFrameName); + cc.assert(frame, spriteFrameName + cc._LogInfos.Sprite_initWithSpriteFrameName1); + return this.initWithSpriteFrame(frame); + }, + + /** + * Tell the sprite to use batch node render. + * @param {cc.SpriteBatchNode} batchNode + */ + useBatchNode: function (batchNode) { + }, + + /** + *

+ * set the vertex rect.
+ * It will be called internally by setTextureRect.
+ * Useful if you want to create 2x images from SD images in Retina Display.
+ * Do not call it manually. Use setTextureRect instead.
+ * (override this method to generate "double scale" sprites) + *

+ * @param {cc.Rect} rect + */ + setVertexRect: function (rect) { + var locRect = this._rect; + locRect.x = rect.x; + locRect.y = rect.y; + locRect.width = rect.width; + locRect.height = rect.height; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }, + + // + // cc.Node property overloads + // + + /** + * Sets whether the sprite should be flipped horizontally or not. + * @param {Boolean} flippedX true if the sprite should be flipped horizontally, false otherwise. + */ + setFlippedX: function (flippedX) { + if (this._flippedX !== flippedX) { + this._flippedX = flippedX; + this.setTextureRect(this._rect, this._rectRotated, this._contentSize); + this.setNodeDirty(true); + } + }, + + /** + * Sets whether the sprite should be flipped vertically or not. + * @param {Boolean} flippedY true if the sprite should be flipped vertically, false otherwise. + */ + setFlippedY: function (flippedY) { + if (this._flippedY !== flippedY) { + this._flippedY = flippedY; + this.setTextureRect(this._rect, this._rectRotated, this._contentSize); + this.setNodeDirty(true); + } + }, + + /** + *

+ * Returns the flag which indicates whether the sprite is flipped horizontally or not.
+ *
+ * It only flips the texture of the sprite, and not the texture of the sprite's children.
+ * Also, flipping the texture doesn't alter the anchorPoint.
+ * If you want to flip the anchorPoint too, and/or to flip the children too use:
+ * sprite.setScaleX(sprite.getScaleX() * -1);

+ * @return {Boolean} true if the sprite is flipped horizontally, false otherwise. + */ + isFlippedX: function () { + return this._flippedX; + }, + + /** + *

+ * Return the flag which indicates whether the sprite is flipped vertically or not.
+ *
+ * It only flips the texture of the sprite, and not the texture of the sprite's children.
+ * Also, flipping the texture doesn't alter the anchorPoint.
+ * If you want to flip the anchorPoint too, and/or to flip the children too use:
+ * sprite.setScaleY(sprite.getScaleY() * -1);

+ * @return {Boolean} true if the sprite is flipped vertically, false otherwise. + */ + isFlippedY: function () { + return this._flippedY; + }, + + // + // RGBA protocol + // + /** + * Sets whether opacity modify color or not. + * @function + * @param {Boolean} modify + */ + setOpacityModifyRGB: function (modify) { + if (this._opacityModifyRGB !== modify) { + this._opacityModifyRGB = modify; + this._renderCmd._setColorDirty(); + } + }, + + /** + * Returns whether opacity modify color or not. + * @return {Boolean} + */ + isOpacityModifyRGB: function () { + return this._opacityModifyRGB; + }, + + // Animation + + /** + * Changes the display frame with animation name and index.
+ * The animation name will be get from the CCAnimationCache + * @param {String} animationName + * @param {Number} frameIndex + */ + setDisplayFrameWithAnimationName: function (animationName, frameIndex) { + cc.assert(animationName, cc._LogInfos.Sprite_setDisplayFrameWithAnimationName_3); + + var cache = cc.animationCache.getAnimation(animationName); + if (!cache) { + cc.log(cc._LogInfos.Sprite_setDisplayFrameWithAnimationName); + return; + } + var animFrame = cache.getFrames()[frameIndex]; + if (!animFrame) { + cc.log(cc._LogInfos.Sprite_setDisplayFrameWithAnimationName_2); + return; + } + this.setSpriteFrame(animFrame.getSpriteFrame()); + }, + + /** + * Returns the batch node object if this sprite is rendered by cc.SpriteBatchNode + * @returns {cc.SpriteBatchNode|null} The cc.SpriteBatchNode object if this sprite is rendered by cc.SpriteBatchNode, null if the sprite isn't used batch node. + */ + getBatchNode: function () { + return this._batchNode; + }, + + // CCTextureProtocol + /** + * Returns the texture of the sprite node + * @returns {cc.Texture2D} + */ + getTexture: function () { + return this._texture; + }, + + _softInit: function (fileName, rect, rotated) { + if (fileName === undefined) + cc.Sprite.prototype.init.call(this); + else if (typeof fileName === 'string') { + if (fileName[0] === "#") { + // Init with a sprite frame name + var frameName = fileName.substr(1, fileName.length - 1); + var spriteFrame = cc.spriteFrameCache.getSpriteFrame(frameName); + if (spriteFrame) + this.initWithSpriteFrame(spriteFrame); + else + cc.log("%s does not exist", fileName); + } else { + // Init with filename and rect + cc.Sprite.prototype.init.call(this, fileName, rect); + } + } else if (typeof fileName === "object") { + if (fileName instanceof cc.Texture2D) { + // Init with texture and rect + this.initWithTexture(fileName, rect, rotated); + } else if (fileName instanceof cc.SpriteFrame) { + // Init with a sprite frame + this.initWithSpriteFrame(fileName); + } else if ((fileName instanceof HTMLImageElement) || (fileName instanceof HTMLCanvasElement)) { + // Init with a canvas or image element + var texture2d = new cc.Texture2D(); + texture2d.initWithElement(fileName); + texture2d.handleLoadedTexture(); + this.initWithTexture(texture2d); + } + } + }, + + /** + * Returns the quad (tex coords, vertex coords and color) information. + * @return {cc.V3F_C4B_T2F_Quad|null} Returns a cc.V3F_C4B_T2F_Quad object when render mode is WebGL, returns null when render mode is Canvas. + */ + getQuad: function () { + return null; + }, + + /** + * conforms to cc.TextureProtocol protocol + * @function + * @param {Number|cc.BlendFunc} src + * @param {Number} dst + */ + setBlendFunc: function (src, dst) { + var locBlendFunc = this._blendFunc; + if (dst === undefined) { + locBlendFunc.src = src.src; + locBlendFunc.dst = src.dst; + } else { + locBlendFunc.src = src; + locBlendFunc.dst = dst; + } + this._renderCmd.updateBlendFunc(locBlendFunc); + }, + + /** + * Initializes an empty sprite with nothing init.
+ * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself. + * @function + * @return {Boolean} + */ + init: function () { + var _t = this; + if (arguments.length > 0) + return _t.initWithFile(arguments[0], arguments[1]); + + cc.Node.prototype.init.call(_t); + _t.dirty = _t._recursiveDirty = false; + + _t._blendFunc.src = cc.BLEND_SRC; + _t._blendFunc.dst = cc.BLEND_DST; + + _t.texture = null; + _t._flippedX = _t._flippedY = false; + + // default transform anchor: center + _t.anchorX = 0.5; + _t.anchorY = 0.5; + + // zwoptex default values + _t._offsetPosition.x = 0; + _t._offsetPosition.y = 0; + _t._hasChildren = false; + + // updated in "useSelfRender" + // Atlas: TexCoords + _t.setTextureRect(cc.rect(0, 0, 0, 0), false, cc.size(0, 0)); + return true; + }, + + /** + *

+ * Initializes a sprite with an image filename.
+ * + * This method will find pszFilename from local file system, load its content to CCTexture2D,
+ * then use CCTexture2D to create a sprite.
+ * After initialization, the rect used will be the size of the image. The offset will be (0,0).
+ * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself. + *

+ * @param {String} filename The path to an image file in local file system + * @param {cc.Rect} rect The rectangle assigned the content area from texture. + * @return {Boolean} true if the sprite is initialized properly, false otherwise. + */ + initWithFile: function (filename, rect) { + cc.assert(filename, cc._LogInfos.Sprite_initWithFile); + + var tex = cc.textureCache.getTextureForKey(filename); + if (!tex) { + tex = cc.textureCache.addImage(filename); + } + + if (!tex.isLoaded()) { + this._loader.clear(); + this._loader.once(tex, function () { + this.initWithFile(filename, rect); + this.dispatchEvent("load"); + }, this); + return false; + } + + if (!rect) { + var size = tex.getContentSize(); + rect = cc.rect(0, 0, size.width, size.height); + } + return this.initWithTexture(tex, rect); + }, + + /** + * Initializes a sprite with a texture and a rect in points, optionally rotated.
+ * After initialization, the rect used will be the size of the texture, and the offset will be (0,0).
+ * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself. + * @function + * @param {cc.Texture2D|HTMLImageElement|HTMLCanvasElement} texture A pointer to an existing CCTexture2D object. You can use a CCTexture2D object for many sprites. + * @param {cc.Rect} [rect] Only the contents inside rect of this texture will be applied for this sprite. + * @param {Boolean} [rotated] Whether or not the texture rectangle is rotated. + * @param {Boolean} [counterclockwise=true] Whether or not the texture rectangle rotation is counterclockwise (texture package is counterclockwise, spine is clockwise). + * @return {Boolean} true if the sprite is initialized properly, false otherwise. + */ + initWithTexture: function (texture, rect, rotated, counterclockwise) { + var _t = this; + cc.assert(arguments.length !== 0, cc._LogInfos.CCSpriteBatchNode_initWithTexture); + this._loader.clear(); + + _t._textureLoaded = texture.isLoaded(); + if (!_t._textureLoaded) { + this._loader.once(texture, function () { + this.initWithTexture(texture, rect, rotated, counterclockwise); + this.dispatchEvent("load"); + }, this); + return false; + } + + rotated = rotated || false; + texture = this._renderCmd._handleTextureForRotatedTexture(texture, rect, rotated, counterclockwise); + + if (!cc.Node.prototype.init.call(_t)) + return false; + + _t._batchNode = null; + _t._recursiveDirty = false; + _t.dirty = false; + _t._opacityModifyRGB = true; + + _t._blendFunc.src = cc.BLEND_SRC; + _t._blendFunc.dst = cc.BLEND_DST; + + _t._flippedX = _t._flippedY = false; + + // zwoptex default values + _t._offsetPosition.x = 0; + _t._offsetPosition.y = 0; + _t._hasChildren = false; + + _t._rectRotated = rotated; + if (rect) { + _t._rect.x = rect.x; + _t._rect.y = rect.y; + _t._rect.width = rect.width; + _t._rect.height = rect.height; + } + + if (!rect) + rect = cc.rect(0, 0, texture.width, texture.height); + + this._renderCmd._checkTextureBoundary(texture, rect, rotated); + + _t.setTexture(texture); + _t.setTextureRect(rect, rotated); + + // by default use "Self Render". + // if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render" + _t.setBatchNode(null); + return true; + }, + + /** + * Updates the texture rect of the CCSprite in points. + * @function + * @param {cc.Rect} rect a rect of texture + * @param {Boolean} [rotated] Whether or not the texture is rotated + * @param {cc.Size} [untrimmedSize] The original pixels size of the texture + * @param {Boolean} [needConvert] contentScaleFactor switch + */ + setTextureRect: function (rect, rotated, untrimmedSize, needConvert) { + var _t = this; + _t._rectRotated = rotated || false; + _t.setContentSize(untrimmedSize || rect); + + _t.setVertexRect(rect); + _t._renderCmd._setTextureCoords(rect, needConvert); + + var relativeOffsetX = _t._unflippedOffsetPositionFromCenter.x, relativeOffsetY = _t._unflippedOffsetPositionFromCenter.y; + if (_t._flippedX) + relativeOffsetX = -relativeOffsetX; + if (_t._flippedY) + relativeOffsetY = -relativeOffsetY; + var locRect = _t._rect; + _t._offsetPosition.x = relativeOffsetX + (_t._contentSize.width - locRect.width) / 2; + _t._offsetPosition.y = relativeOffsetY + (_t._contentSize.height - locRect.height) / 2; + }, + + // BatchNode methods + + /** + * Add child to sprite (override cc.Node) + * @function + * @param {cc.Sprite} child + * @param {Number} localZOrder child's zOrder + * @param {number|String} [tag] child's tag + * @override + */ + addChild: function (child, localZOrder, tag) { + cc.assert(child, cc._LogInfos.CCSpriteBatchNode_addChild_2); + + if (localZOrder == null) + localZOrder = child._localZOrder; + if (tag == null) + tag = child.tag; + + if (this._renderCmd._setBatchNodeForAddChild(child)) { + //cc.Node already sets isReorderChildDirty_ so this needs to be after batchNode check + cc.Node.prototype.addChild.call(this, child, localZOrder, tag); + this._hasChildren = true; + } + }, + + // Frames + /** + * Sets a new sprite frame to the sprite. + * @function + * @param {cc.SpriteFrame|String} newFrame + */ + setSpriteFrame: function (newFrame) { + var _t = this; + if (typeof newFrame === 'string') { + newFrame = cc.spriteFrameCache.getSpriteFrame(newFrame); + cc.assert(newFrame, cc._LogInfos.Sprite_setSpriteFrame); + } + this._loader.clear(); + + this.setNodeDirty(true); + + // update rect + var pNewTexture = newFrame.getTexture(); + _t._textureLoaded = newFrame.textureLoaded(); + this._loader.clear(); + if (!_t._textureLoaded) { + this._loader.once(pNewTexture, function () { + this.setSpriteFrame(newFrame); + this.dispatchEvent("load"); + }, this); + return false; + } + + var frameOffset = newFrame.getOffset(); + _t._unflippedOffsetPositionFromCenter.x = frameOffset.x; + _t._unflippedOffsetPositionFromCenter.y = frameOffset.y; + + if (pNewTexture !== _t._texture) { + this._renderCmd._setTexture(pNewTexture); + _t.setColor(_t._realColor); + } + _t.setTextureRect(newFrame.getRect(), newFrame.isRotated(), newFrame.getOriginalSize()); + }, + + /** + * Sets a new display frame to the sprite. + * @param {cc.SpriteFrame|String} newFrame + * @deprecated + */ + setDisplayFrame: function (newFrame) { + cc.log(cc._LogInfos.Sprite_setDisplayFrame); + this.setSpriteFrame(newFrame); + }, + + /** + * Returns whether or not a cc.SpriteFrame is being displayed + * @function + * @param {cc.SpriteFrame} frame + * @return {Boolean} + */ + isFrameDisplayed: function (frame) { + return this._renderCmd.isFrameDisplayed(frame); + }, + + /** + * Returns the current displayed frame. + * @deprecated since 3.4, please use getSpriteFrame instead + * @return {cc.SpriteFrame} + */ + displayFrame: function () { + return this.getSpriteFrame(); + }, + + /** + * Returns the current displayed frame. + * @return {cc.SpriteFrame} + */ + getSpriteFrame: function () { + return new cc.SpriteFrame(this._texture, + cc.rectPointsToPixels(this._rect), + this._rectRotated, + cc.pointPointsToPixels(this._unflippedOffsetPositionFromCenter), + cc.sizePointsToPixels(this._contentSize)); + }, + + /** + * Sets the batch node to sprite + * @function + * @param {cc.SpriteBatchNode|null} spriteBatchNode + * @example + * var batch = new cc.SpriteBatchNode("Images/grossini_dance_atlas.png", 15); + * var sprite = new cc.Sprite(batch.texture, cc.rect(0, 0, 57, 57)); + * batch.addChild(sprite); + * layer.addChild(batch); + */ + setBatchNode: function (spriteBatchNode) { + }, + + // CCTextureProtocol + /** + * Sets the texture of sprite + * @function + * @param {cc.Texture2D|String} texture + */ + setTexture: function (texture) { + if (!texture) + return this._renderCmd._setTexture(null); + + //CCSprite.cpp 327 and 338 + var isFileName = (typeof texture === 'string'); + + if (isFileName) + texture = cc.textureCache.addImage(texture); + + this._loader.clear(); + if (!texture._textureLoaded) { + // wait for the load to be set again + this._loader.once(texture, function () { + this.setTexture(texture); + this.dispatchEvent("load"); + }, this); + return false; + } + + this._renderCmd._setTexture(texture); + if (isFileName) + this._changeRectWithTexture(texture); + this.setColor(this._realColor); + this._textureLoaded = true; + }, + + _changeRectWithTexture: function (texture) { + var contentSize = texture._contentSize; + var rect = cc.rect( + 0, 0, + contentSize.width, contentSize.height + ); + this.setTextureRect(rect); + }, + + _createRenderCmd: function () { + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return new cc.Sprite.CanvasRenderCmd(this); + else + return new cc.Sprite.WebGLRenderCmd(this); + } +}); + +/** + * Create a sprite with image path or frame name or texture or spriteFrame. + * @deprecated since v3.0, please use new construction instead + * @see cc.Sprite + * @param {String|cc.SpriteFrame|HTMLImageElement|cc.Texture2D} fileName The string which indicates a path to image file, e.g., "scene1/monster.png". + * @param {cc.Rect} rect Only the contents inside rect of pszFileName's texture will be applied for this sprite. + * @param {Boolean} [rotated] Whether or not the texture rectangle is rotated. + * @return {cc.Sprite} A valid sprite object + */ +cc.Sprite.create = function (fileName, rect, rotated) { + return new cc.Sprite(fileName, rect, rotated); +}; + +/** + * @deprecated since v3.0, please use new construction instead + * @see cc.Sprite + * @function + */ +cc.Sprite.createWithTexture = cc.Sprite.create; + +/** + * @deprecated since v3.0, please use new construction instead + * @see cc.Sprite + * @function + */ +cc.Sprite.createWithSpriteFrameName = cc.Sprite.create; + +/** + * @deprecated since v3.0, please use new construction instead + * @see cc.Sprite + * @function + */ +cc.Sprite.createWithSpriteFrame = cc.Sprite.create; +/** + * cc.Sprite invalid index on the cc.SpriteBatchNode + * @constant + * @type {Number} + */ +cc.Sprite.INDEX_NOT_INITIALIZED = -1; + +cc.EventHelper.prototype.apply(cc.Sprite.prototype); + +cc.assert(cc.isFunction(cc._tmp.PrototypeSprite), cc._LogInfos.MissingFile, "SpritesPropertyDefine.js"); +cc._tmp.PrototypeSprite(); +delete cc._tmp.PrototypeSprite; + +(function () { + var manager = cc.Sprite.LoadManager = function () { + this.list = []; + }; + + manager.prototype.add = function (source, callback, target) { + if (!source || !source.addEventListener) return; + source.addEventListener('load', callback, target); + this.list.push({ + source: source, + listener: callback, + target: target + }); + }; + manager.prototype.once = function (source, callback, target) { + if (!source || !source.addEventListener) return; + var tmpCallback = function (event) { + source.removeEventListener('load', tmpCallback, target); + callback.call(target, event); + }; + source.addEventListener('load', tmpCallback, target); + this.list.push({ + source: source, + listener: tmpCallback, + target: target + }); + }; + manager.prototype.clear = function () { + while (this.list.length > 0) { + var item = this.list.pop(); + item.source.removeEventListener('load', item.listener, item.target); + } + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/core/sprites/CCSpriteBatchNode.js b/frameworks/cocos2d-html5/cocos2d/core/sprites/CCSpriteBatchNode.js new file mode 100644 index 0000000..3abd61f --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/sprites/CCSpriteBatchNode.js @@ -0,0 +1,438 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + + +/** + *

+ * A cc.SpriteBatchNode can reference one and only one texture (one image file, one texture atlas).
+ * Only the cc.Sprites that are contained in that texture can be added to the cc.SpriteBatchNode.
+ * All cc.Sprites added to a cc.SpriteBatchNode are drawn in one WebGL draw call.
+ * If the cc.Sprites are not added to a cc.SpriteBatchNode then an WebGL draw call will be needed for each one, which is less efficient.
+ *
+ * Limitations:
+ * - The only object that is accepted as child (or grandchild, grand-grandchild, etc...) is cc.Sprite or any subclass of cc.Sprite.
+ * eg: particles, labels and layer can't be added to a cc.SpriteBatchNode.
+ * - Either all its children are Aliased or Antialiased. It can't be a mix.
+ * This is because "alias" is a property of the texture, and all the sprites share the same texture.
+ *

+ * @class + * @extends cc.Node + * + * @param {String|cc.Texture2D} fileImage + * @example + * + * // 1. create a SpriteBatchNode with image path + * var spriteBatchNode = new cc.SpriteBatchNode("res/animations/grossini.png"); + * + * // 2. create a SpriteBatchNode with texture + * var texture = cc.textureCache.addImage("res/animations/grossini.png"); + * var spriteBatchNode = new cc.SpriteBatchNode(texture); + * + * @property {cc.TextureAtlas} textureAtlas - The texture atlas + * @property {Array} descendants - <@readonly> Descendants of sprite batch node + */ +cc.SpriteBatchNode = cc.Node.extend(/** @lends cc.SpriteBatchNode# */{ + _blendFunc: null, + // all descendants: chlidren, gran children, etc... + _texture: null, + _className: "SpriteBatchNode", + + ctor: function (fileImage) { + cc.Node.prototype.ctor.call(this); + this._blendFunc = new cc.BlendFunc(cc.BLEND_SRC, cc.BLEND_DST); + + var texture2D; + if (cc.isString(fileImage)) { + texture2D = cc.textureCache.getTextureForKey(fileImage); + if (!texture2D) + texture2D = cc.textureCache.addImage(fileImage); + } else if (fileImage instanceof cc.Texture2D) + texture2D = fileImage; + + texture2D && this.initWithTexture(texture2D); + }, + + /** + *

+ * Same as addChild + *

+ * @param {cc.Sprite} child + * @param {Number} z zOrder + * @param {Number} aTag + * @return {cc.SpriteBatchNode} + * @deprecated since v3.12 + */ + addSpriteWithoutQuad: function (child, z, aTag) { + this.addChild(child, z, aTag); + return this; + }, + + // property + /** + * Return null, no texture atlas is used any more + * @return {cc.TextureAtlas} + * @deprecated since v3.12 + */ + getTextureAtlas: function () { + return null; + }, + + /** + * TextureAtlas of cc.SpriteBatchNode setter + * @param {cc.TextureAtlas} textureAtlas + * @deprecated since v3.12 + */ + setTextureAtlas: function (textureAtlas) { + }, + + /** + * Return Descendants of cc.SpriteBatchNode + * @return {Array} + * @deprecated since v3.12 + */ + getDescendants: function () { + return this._children; + }, + + /** + *

+ * Initializes a cc.SpriteBatchNode with a file image (.png, .jpeg, .pvr, etc) and a capacity of children.
+ * The capacity will be increased in 33% in runtime if it run out of space.
+ * The file will be loaded using the TextureMgr.
+ * Please pass parameters to constructor to initialize the sprite batch node, do not call this function yourself. + *

+ * @param {String} fileImage + * @param {Number} capacity + * @return {Boolean} + */ + initWithFile: function (fileImage, capacity) { + var texture2D = cc.textureCache.getTextureForKey(fileImage); + if (!texture2D) + texture2D = cc.textureCache.addImage(fileImage); + return this.initWithTexture(texture2D, capacity); + }, + + /** + *

+ * initializes a cc.SpriteBatchNode with a file image (.png, .jpeg, .pvr, etc) and a capacity of children.
+ * The capacity will be increased in 33% in runtime if it run out of space.
+ * The file will be loaded using the TextureMgr.
+ * Please pass parameters to constructor to initialize the sprite batch node, do not call this function yourself. + *

+ * @param {String} fileImage + * @param {Number} capacity + * @return {Boolean} + */ + init: function (fileImage, capacity) { + var texture2D = cc.textureCache.getTextureForKey(fileImage); + if (!texture2D) + texture2D = cc.textureCache.addImage(fileImage); + return this.initWithTexture(texture2D, capacity); + }, + + /** + * Do nothing + * @deprecated since v3.12 + */ + increaseAtlasCapacity: function () { + }, + + /** + * Removes a child given a certain index. It will also cleanup the running actions depending on the cleanup parameter. + * @warning Removing a child from a cc.SpriteBatchNode is very slow + * @param {Number} index + * @param {Boolean} doCleanup + */ + removeChildAtIndex: function (index, doCleanup) { + this.removeChild(this._children[index], doCleanup); + }, + + /** + * Do nothing + * @param {cc.Sprite} pobParent + * @param {Number} index + * @return {Number} + * @deprecated since v3.12 + */ + rebuildIndexInOrder: function (pobParent, index) { + return index; + }, + + /** + * Returns highest atlas index in child + * @param {cc.Sprite} sprite + * @return {Number} + * @deprecated since v3.12 + */ + highestAtlasIndexInChild: function (sprite) { + var children = sprite.children; + if (!children || children.length === 0) + return sprite.zIndex; + else + return this.highestAtlasIndexInChild(children[children.length - 1]); + }, + + /** + * Returns lowest atlas index in child + * @param {cc.Sprite} sprite + * @return {Number} + * @deprecated since v3.12 + */ + lowestAtlasIndexInChild: function (sprite) { + var children = sprite.children; + if (!children || children.length === 0) + return sprite.zIndex; + else + return this.lowestAtlasIndexInChild(children[children.length - 1]); + }, + + /** + * Returns index for child + * @param {cc.Sprite} sprite + * @return {Number} + * @deprecated since v3.12 + */ + atlasIndexForChild: function (sprite) { + return sprite.zIndex; + }, + + /** + * Sprites use this to start sortChildren, don't call this manually + * @param {Boolean} reorder + * @deprecated since v3.12 + */ + reorderBatch: function (reorder) { + this._reorderChildDirty = reorder; + }, + + /** + * Sets the source and destination blending function for the texture + * @param {Number | cc.BlendFunc} src + * @param {Number} dst + */ + setBlendFunc: function (src, dst) { + if (dst === undefined) + this._blendFunc = src; + else + this._blendFunc = {src: src, dst: dst}; + }, + + /** + * Returns the blending function used for the texture + * @return {cc.BlendFunc} + */ + getBlendFunc: function () { + return new cc.BlendFunc(this._blendFunc.src, this._blendFunc.dst); + }, + + /** + *

+ * Updates a quad at a certain index into the texture atlas. The CCSprite won't be added into the children array.
+ * This method should be called only when you are dealing with very big AtlasSrite and when most of the cc.Sprite won't be updated.
+ * For example: a tile map (cc.TMXMap) or a label with lots of characters (BitmapFontAtlas)
+ *

+ * @function + * @param {cc.Sprite} sprite + * @param {Number} index + */ + updateQuadFromSprite: function (sprite, index) { + cc.assert(sprite, cc._LogInfos.CCSpriteBatchNode_updateQuadFromSprite_2); + if (!(sprite instanceof cc.Sprite)) { + cc.log(cc._LogInfos.CCSpriteBatchNode_updateQuadFromSprite); + return; + } + + // + // update the quad directly. Don't add the sprite to the scene graph + // + sprite.dirty = true; + // UpdateTransform updates the textureAtlas quad + sprite._renderCmd.transform(this._renderCmd, true); + }, + + /** + *

+ * Same as addChild(sprite, index) + *

+ * @function + * @param {cc.Sprite} sprite + * @param {Number} index + * @deprecated since v3.12 + */ + insertQuadFromSprite: function (sprite, index) { + this.addChild(sprite, index); + }, + + /** + * Same as addChild(sprite, index) + * @param {cc.Sprite} sprite The child sprite + * @param {Number} index The insert index + * @deprecated since v3.12 + */ + insertChild: function (sprite, index) { + this.addChild(sprite, index); + }, + + /** + * Add child at the end + * @function + * @param {cc.Sprite} sprite + */ + appendChild: function (sprite) { + this.sortAllChildren(); + var lastLocalZOrder = this._children[this._children.length - 1]._localZOrder; + this.addChild(sprite.lastLocalZOrder + 1); + }, + + /** + * Same as removeChild + * @function + * @param {cc.Sprite} sprite + * @param {Boolean} [cleanup=true] true if all running actions and callbacks on the child node will be cleanup, false otherwise. + * @deprecated since v3.12 + */ + removeSpriteFromAtlas: function (sprite, cleanup) { + this.removeChild(sprite, cleanup); + }, + + /** + * Set the texture property + * @function + * @param {cc.Texture2D} tex + * @return {Boolean} + */ + initWithTexture: function (tex) { + this.setTexture(tex); + return true; + }, + + // CCTextureProtocol + /** + * Returns texture of the sprite batch node + * @function + * @return {cc.Texture2D} + */ + getTexture: function () { + return this._texture; + }, + + /** + * Sets the texture of the sprite batch node. + * @function + * @param {cc.Texture2D} texture + */ + setTexture: function (texture) { + this._texture = texture; + + if (texture._textureLoaded) { + var i, children = this._children, len = children.length; + for (i = 0; i < len; ++i) { + children[i].setTexture(texture); + } + } + else { + texture.addEventListener("load", function () { + var i, children = this._children, len = children.length; + for (i = 0; i < len; ++i) { + children[i].setTexture(texture); + } + }, this); + } + }, + + setShaderProgram: function (newShaderProgram) { + this._renderCmd.setShaderProgram(newShaderProgram); + var i, children = this._children, len = children.length; + for (i = 0; i < len; ++i) { + children[i].setShaderProgram(newShaderProgram); + } + }, + + /** + * Add child to the sprite batch node (override addChild of cc.Node) + * @function + * @override + * @param {cc.Sprite} child + * @param {Number} [zOrder] + * @param {Number} [tag] + */ + addChild: function (child, zOrder, tag) { + cc.assert(child !== undefined, cc._LogInfos.CCSpriteBatchNode_addChild_3); + + if (!this._isValidChild(child)) + return; + + zOrder = (zOrder === undefined) ? child.zIndex : zOrder; + tag = (tag === undefined) ? child.tag : tag; + cc.Node.prototype.addChild.call(this, child, zOrder, tag); + + // Apply shader + if (this._renderCmd._shaderProgram) { + child.shaderProgram = this._renderCmd._shaderProgram; + } + }, + + _isValidChild: function (child) { + if (!(child instanceof cc.Sprite)) { + cc.log(cc._LogInfos.Sprite_addChild_4); + return false; + } + if (child.texture !== this._texture) { + cc.log(cc._LogInfos.Sprite_addChild_5); + return false; + } + return true; + } +}); + +var _p = cc.SpriteBatchNode.prototype; + +// Override properties +cc.defineGetterSetter(_p, "texture", _p.getTexture, _p.setTexture); +cc.defineGetterSetter(_p, "shaderProgram", _p.getShaderProgram, _p.setShaderProgram); + + +/** + *

+ * creates a cc.SpriteBatchNodeCanvas with a file image (.png, .jpg etc) with a default capacity of 29 children.
+ * The capacity will be increased in 33% in runtime if it run out of space.
+ * The file will be loaded using the TextureMgr.
+ *

+ * @deprecated since v3.0, please use new construction instead + * @see cc.SpriteBatchNode + * @param {String|cc.Texture2D} fileImage + * @return {cc.SpriteBatchNode} + */ +cc.SpriteBatchNode.create = function (fileImage) { + return new cc.SpriteBatchNode(fileImage); +}; + +/** + * @deprecated since v3.0, please use new construction instead + * @see cc.SpriteBatchNode + * @function + */ +cc.SpriteBatchNode.createWithTexture = cc.SpriteBatchNode.create; diff --git a/frameworks/cocos2d-html5/cocos2d/core/sprites/CCSpriteCanvasRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/core/sprites/CCSpriteCanvasRenderCmd.js new file mode 100644 index 0000000..87da260 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/sprites/CCSpriteCanvasRenderCmd.js @@ -0,0 +1,251 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + cc.Sprite.CanvasRenderCmd = function (renderable) { + this._rootCtor(renderable); + this._needDraw = true; + this._textureCoord = { + renderX: 0, //the x of texture coordinate for render, when texture tinted, its value doesn't equal x. + renderY: 0, //the y of texture coordinate for render, when texture tinted, its value doesn't equal y. + x: 0, //the x of texture coordinate for node. + y: 0, //the y of texture coordinate for node. + width: 0, + height: 0, + validRect: false + }; + this._blendFuncStr = "source-over"; + this._colorized = false; + this._canUseDirtyRegion = true; + this._textureToRender = null; + }; + + var proto = cc.Sprite.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + proto.constructor = cc.Sprite.CanvasRenderCmd; + proto._spriteCmdCtor = cc.Sprite.CanvasRenderCmd; + + proto.setDirtyRecursively = function (value) { + }; + + proto._setTexture = function (texture) { + var node = this._node; + if (node._texture !== texture) { + node._textureLoaded = texture ? texture._textureLoaded : false; + node._texture = texture; + + var texSize = texture._contentSize; + var rect = cc.rect(0, 0, texSize.width, texSize.height); + node.setTextureRect(rect); + this._updateColor(); + } + }; + + proto._setColorDirty = function () { + this.setDirtyFlag(cc.Node._dirtyFlags.colorDirty | cc.Node._dirtyFlags.opacityDirty); + }; + + proto.isFrameDisplayed = function (frame) { //TODO there maybe has a bug + var node = this._node; + if (frame.getTexture() !== node._texture) + return false; + return cc.rectEqualToRect(frame.getRect(), node._rect); + }; + + proto.updateBlendFunc = function (blendFunc) { + this._blendFuncStr = cc.Node.CanvasRenderCmd._getCompositeOperationByBlendFunc(blendFunc); + }; + + proto._setBatchNodeForAddChild = function (child) { + return true; + }; + + proto._handleTextureForRotatedTexture = function (texture, rect, rotated, counterclockwise) { + if (rotated && texture.isLoaded()) { + var tempElement = texture.getHtmlElementObj(); + tempElement = cc.Sprite.CanvasRenderCmd._cutRotateImageToCanvas(tempElement, rect, counterclockwise); + var tempTexture = new cc.Texture2D(); + tempTexture.initWithElement(tempElement); + tempTexture.handleLoadedTexture(); + texture = tempTexture; + rect.x = rect.y = 0; + this._node._rect = cc.rect(0, 0, rect.width, rect.height); + } + return texture; + }; + + proto._checkTextureBoundary = function (texture, rect, rotated) { + if (texture && texture.url) { + var _x = rect.x + rect.width, _y = rect.y + rect.height; + if (_x > texture.width) + cc.error(cc._LogInfos.RectWidth, texture.url); + if (_y > texture.height) + cc.error(cc._LogInfos.RectHeight, texture.url); + } + }; + + proto.rendering = function (ctx, scaleX, scaleY) { + var node = this._node; + var locTextureCoord = this._textureCoord, alpha = (this._displayedOpacity / 255); + var texture = this._textureToRender || node._texture; + + if ((texture && (locTextureCoord.width === 0 || locTextureCoord.height === 0 || !texture._textureLoaded)) || alpha === 0) + return; + + var wrapper = ctx || cc._renderContext, context = wrapper.getContext(); + var locX = node._offsetPosition.x, locHeight = node._rect.height, locWidth = node._rect.width, + locY = -node._offsetPosition.y - locHeight, image; + + wrapper.setTransform(this._worldTransform, scaleX, scaleY); + wrapper.setCompositeOperation(this._blendFuncStr); + wrapper.setGlobalAlpha(alpha); + + if (node._flippedX || node._flippedY) + wrapper.save(); + if (node._flippedX) { + locX = -locX - locWidth; + context.scale(-1, 1); + } + if (node._flippedY) { + locY = node._offsetPosition.y; + context.scale(1, -1); + } + + var sx, sy, sw, sh, x, y, w, h; + if (this._colorized) { + sx = 0; + sy = 0; + } else { + sx = locTextureCoord.renderX; + sy = locTextureCoord.renderY; + } + sw = locTextureCoord.width; + sh = locTextureCoord.height; + + x = locX; + y = locY; + w = locWidth; + h = locHeight; + + if (texture && texture._htmlElementObj) { + image = texture._htmlElementObj; + if (texture._pattern !== "") { + wrapper.setFillStyle(context.createPattern(image, texture._pattern)); + context.fillRect(x, y, w, h); + } else { + context.drawImage(image, + sx, sy, sw, sh, + x, y, w, h); + } + } else { + var contentSize = node._contentSize; + if (locTextureCoord.validRect) { + var curColor = this._displayedColor; + wrapper.setFillStyle("rgba(" + curColor.r + "," + curColor.g + "," + curColor.b + ",1)"); + context.fillRect(x, y, contentSize.width * scaleX, contentSize.height * scaleY); + } + } + if (node._flippedX || node._flippedY) + wrapper.restore(); + cc.g_NumberOfDraws++; + }; + + proto._updateColor = function () { + var node = this._node; + + var texture = node._texture, rect = this._textureCoord; + var dColor = this._displayedColor; + + if (texture) { + if (dColor.r !== 255 || dColor.g !== 255 || dColor.b !== 255) { + this._textureToRender = texture._generateColorTexture(dColor.r, dColor.g, dColor.b, rect); + this._colorized = true; + } else if (texture) { + this._textureToRender = texture; + this._colorized = false; + } + } + }; + + proto._textureLoadedCallback = function (sender) { + var node = this; + if (node._textureLoaded) + return; + + node._textureLoaded = true; + var locRect = node._rect, locRenderCmd = this._renderCmd; + if (!locRect) { + locRect = cc.rect(0, 0, sender.width, sender.height); + } else if (cc._rectEqualToZero(locRect)) { + locRect.width = sender.width; + locRect.height = sender.height; + } + + node.texture = sender; + node.setTextureRect(locRect, node._rectRotated); + + //set the texture's color after the it loaded + var locColor = locRenderCmd._displayedColor; + if (locColor.r !== 255 || locColor.g !== 255 || locColor.b !== 255) + locRenderCmd._updateColor(); + + // by default use "Self Render". + // if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render" + node.setBatchNode(node._batchNode); + node.dispatchEvent("load"); + }; + + proto._setTextureCoords = function (rect, needConvert) { + if (needConvert === undefined) + needConvert = true; + var locTextureRect = this._textureCoord, + scaleFactor = needConvert ? cc.contentScaleFactor() : 1; + locTextureRect.renderX = locTextureRect.x = 0 | (rect.x * scaleFactor); + locTextureRect.renderY = locTextureRect.y = 0 | (rect.y * scaleFactor); + locTextureRect.width = 0 | (rect.width * scaleFactor); + locTextureRect.height = 0 | (rect.height * scaleFactor); + locTextureRect.validRect = !(locTextureRect.width === 0 || locTextureRect.height === 0 || locTextureRect.x < 0 || locTextureRect.y < 0); + }; + + cc.Sprite.CanvasRenderCmd._cutRotateImageToCanvas = function (texture, rect, counterclockwise) { + if (!texture) + return null; + + if (!rect) + return texture; + + counterclockwise = counterclockwise == null ? true : counterclockwise; // texture package is counterclockwise, spine is clockwise + + var nCanvas = document.createElement("canvas"); + nCanvas.width = rect.width; + nCanvas.height = rect.height; + var ctx = nCanvas.getContext("2d"); + ctx.translate(nCanvas.width / 2, nCanvas.height / 2); + if (counterclockwise) + ctx.rotate(-1.5707963267948966); + else + ctx.rotate(1.5707963267948966); + ctx.drawImage(texture, rect.x, rect.y, rect.height, rect.width, -rect.height / 2, -rect.width / 2, rect.height, rect.width); + return nCanvas; + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/core/sprites/CCSpriteFrame.js b/frameworks/cocos2d-html5/cocos2d/core/sprites/CCSpriteFrame.js new file mode 100644 index 0000000..4879225 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/sprites/CCSpriteFrame.js @@ -0,0 +1,420 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + *

+ * A cc.SpriteFrame has:
+ * - texture: A cc.Texture2D that will be used by the cc.Sprite
+ * - rectangle: A rectangle of the texture
+ *
+ * You can modify the frame of a cc.Sprite by doing:
+ *

+ * @class + * @extends cc.Class + * + * @param {String|cc.Texture2D} filename + * @param {cc.Rect} rect If parameters' length equal 2, rect in points, else rect in pixels + * @param {Boolean} [rotated] Whether the frame is rotated in the texture + * @param {cc.Point} [offset] The offset of the frame in the texture + * @param {cc.Size} [originalSize] The size of the frame in the texture + * + * @example + * // 1. Create a cc.SpriteFrame with image path + * var frame1 = new cc.SpriteFrame("res/grossini_dance.png",cc.rect(0,0,90,128)); + * var frame2 = new cc.SpriteFrame("res/grossini_dance.png",cc.rect(0,0,90,128),false,0,cc.size(90,128)); + * + * // 2. Create a cc.SpriteFrame with a texture, rect, rotated, offset and originalSize in pixels. + * var texture = cc.textureCache.addImage("res/grossini_dance.png"); + * var frame1 = new cc.SpriteFrame(texture, cc.rect(0,0,90,128)); + * var frame2 = new cc.SpriteFrame(texture, cc.rect(0,0,90,128),false,0,cc.size(90,128)); + */ +cc.SpriteFrame = cc.Class.extend(/** @lends cc.SpriteFrame# */{ + _offset: null, + _originalSize: null, + _rectInPixels: null, + _rotated: false, + _rect: null, + _offsetInPixels: null, + _originalSizeInPixels: null, + _texture: null, + _textureFilename: "", + _textureLoaded: false, + + ctor: function (filename, rect, rotated, offset, originalSize) { + this._offset = cc.p(0, 0); + this._offsetInPixels = cc.p(0, 0); + this._originalSize = cc.size(0, 0); + this._rotated = false; + this._originalSizeInPixels = cc.size(0, 0); + this._textureFilename = ""; + this._texture = null; + this._textureLoaded = false; + + if (filename !== undefined && rect !== undefined) { + if (rotated === undefined || offset === undefined || originalSize === undefined) + this.initWithTexture(filename, rect); + else + this.initWithTexture(filename, rect, rotated, offset, originalSize); + } + }, + + /** + * Returns whether the texture have been loaded + * @returns {boolean} + */ + textureLoaded: function () { + return this._textureLoaded; + }, + + /** + * Add a event listener for texture loaded event. + * @param {Function} callback + * @param {Object} target + * @deprecated since 3.1, please use addEventListener instead + */ + addLoadedEventListener: function (callback, target) { + this.addEventListener("load", callback, target); + }, + + /** + * Gets the rect of the frame in the texture + * @return {cc.Rect} + */ + getRectInPixels: function () { + var locRectInPixels = this._rectInPixels; + return cc.rect(locRectInPixels.x, locRectInPixels.y, locRectInPixels.width, locRectInPixels.height); + }, + + /** + * Sets the rect of the frame in the texture + * @param {cc.Rect} rectInPixels + */ + setRectInPixels: function (rectInPixels) { + if (!this._rectInPixels) { + this._rectInPixels = cc.rect(0, 0, 0, 0); + } + this._rectInPixels.x = rectInPixels.x; + this._rectInPixels.y = rectInPixels.y; + this._rectInPixels.width = rectInPixels.width; + this._rectInPixels.height = rectInPixels.height; + this._rect = cc.rectPixelsToPoints(rectInPixels); + }, + + /** + * Returns whether the sprite frame is rotated in the texture. + * @return {Boolean} + */ + isRotated: function () { + return this._rotated; + }, + + /** + * Set whether the sprite frame is rotated in the texture. + * @param {Boolean} bRotated + */ + setRotated: function (bRotated) { + this._rotated = bRotated; + }, + + /** + * Returns the rect of the sprite frame in the texture + * @return {cc.Rect} + */ + getRect: function () { + var locRect = this._rect; + return cc.rect(locRect.x, locRect.y, locRect.width, locRect.height); + }, + + /** + * Sets the rect of the sprite frame in the texture + * @param {cc.Rect} rect + */ + setRect: function (rect) { + if (!this._rect) { + this._rect = cc.rect(0, 0, 0, 0); + } + this._rect.x = rect.x; + this._rect.y = rect.y; + this._rect.width = rect.width; + this._rect.height = rect.height; + this._rectInPixels = cc.rectPointsToPixels(this._rect); + }, + + /** + * Returns the offset of the sprite frame in the texture in pixel + * @return {cc.Point} + */ + getOffsetInPixels: function () { + return cc.p(this._offsetInPixels); + }, + + /** + * Sets the offset of the sprite frame in the texture in pixel + * @param {cc.Point} offsetInPixels + */ + setOffsetInPixels: function (offsetInPixels) { + this._offsetInPixels.x = offsetInPixels.x; + this._offsetInPixels.y = offsetInPixels.y; + cc._pointPixelsToPointsOut(this._offsetInPixels, this._offset); + }, + + /** + * Returns the original size of the trimmed image + * @return {cc.Size} + */ + getOriginalSizeInPixels: function () { + return cc.size(this._originalSizeInPixels); + }, + + /** + * Sets the original size of the trimmed image + * @param {cc.Size} sizeInPixels + */ + setOriginalSizeInPixels: function (sizeInPixels) { + this._originalSizeInPixels.width = sizeInPixels.width; + this._originalSizeInPixels.height = sizeInPixels.height; + }, + + /** + * Returns the original size of the trimmed image + * @return {cc.Size} + */ + getOriginalSize: function () { + return cc.size(this._originalSize); + }, + + /** + * Sets the original size of the trimmed image + * @param {cc.Size} sizeInPixels + */ + setOriginalSize: function (sizeInPixels) { + this._originalSize.width = sizeInPixels.width; + this._originalSize.height = sizeInPixels.height; + }, + + /** + * Returns the texture of the frame + * @return {cc.Texture2D} + */ + getTexture: function () { + if (this._texture) + return this._texture; + if (this._textureFilename !== "") { + var locTexture = cc.textureCache.addImage(this._textureFilename); + if (locTexture) + this._textureLoaded = locTexture.isLoaded(); + return locTexture; + } + return null; + }, + + /** + * Sets the texture of the frame, the texture is retained automatically + * @param {cc.Texture2D} texture + */ + setTexture: function (texture) { + if (this._texture !== texture) { + var locLoaded = texture.isLoaded(); + this._textureLoaded = locLoaded; + this._texture = texture; + if (!locLoaded) { + texture.addEventListener("load", function (sender) { + this._textureLoaded = true; + if (this._rotated && cc._renderType === cc.game.RENDER_TYPE_CANVAS) { + var tempElement = sender.getHtmlElementObj(); + tempElement = cc.Sprite.CanvasRenderCmd._cutRotateImageToCanvas(tempElement, this.getRect()); + var tempTexture = new cc.Texture2D(); + tempTexture.initWithElement(tempElement); + tempTexture.handleLoadedTexture(); + this.setTexture(tempTexture); + + var rect = this.getRect(); + this.setRect(cc.rect(0, 0, rect.width, rect.height)); + } + var locRect = this._rect; + if (locRect.width === 0 && locRect.height === 0) { + var w = sender.width, h = sender.height; + this._rect.width = w; + this._rect.height = h; + this._rectInPixels = cc.rectPointsToPixels(this._rect); + this._originalSizeInPixels.width = this._rectInPixels.width; + this._originalSizeInPixels.height = this._rectInPixels.height; + this._originalSize.width = w; + this._originalSize.height = h; + } + //dispatch 'load' event of cc.SpriteFrame + this.dispatchEvent("load"); + }, this); + } + } + }, + + /** + * Returns the offset of the frame in the texture + * @return {cc.Point} + */ + getOffset: function () { + return cc.p(this._offset); + }, + + /** + * Sets the offset of the frame in the texture + * @param {cc.Point} offsets + */ + setOffset: function (offsets) { + this._offset.x = offsets.x; + this._offset.y = offsets.y; + }, + + /** + * Clone the sprite frame + * @returns {SpriteFrame} + */ + clone: function () { + var frame = new cc.SpriteFrame(); + frame.initWithTexture(this._textureFilename, this._rectInPixels, this._rotated, this._offsetInPixels, this._originalSizeInPixels); + frame.setTexture(this._texture); + return frame; + }, + + /** + * Copy the sprite frame + * @return {cc.SpriteFrame} + */ + copyWithZone: function () { + var copy = new cc.SpriteFrame(); + copy.initWithTexture(this._textureFilename, this._rectInPixels, this._rotated, this._offsetInPixels, this._originalSizeInPixels); + copy.setTexture(this._texture); + return copy; + }, + + /** + * Copy the sprite frame + * @returns {cc.SpriteFrame} + */ + copy: function () { + return this.copyWithZone(); + }, + + /** + * Initializes SpriteFrame with Texture, rect, rotated, offset and originalSize in pixels.
+ * Please pass parameters to the constructor to initialize the sprite, do not call this function yourself. + * @param {String|cc.Texture2D} texture + * @param {cc.Rect} rect if parameters' length equal 2, rect in points, else rect in pixels + * @param {Boolean} [rotated=false] + * @param {cc.Point} [offset=cc.p(0,0)] + * @param {cc.Size} [originalSize=rect.size] + * @return {Boolean} + */ + initWithTexture: function (texture, rect, rotated, offset, originalSize) { + if (arguments.length === 2) + rect = cc.rectPointsToPixels(rect); + + offset = offset || cc.p(0, 0); + originalSize = originalSize || rect; + rotated = rotated || false; + + if (typeof texture === 'string') { + this._texture = null; + this._textureFilename = texture; + } else if (texture instanceof cc.Texture2D) { + this.setTexture(texture); + } + + texture = this.getTexture(); + + this._rectInPixels = rect; + this._rect = cc.rectPixelsToPoints(rect); + + if (texture && texture.url && texture.isLoaded()) { + var _x, _y; + if (rotated) { + _x = rect.x + rect.height; + _y = rect.y + rect.width; + } else { + _x = rect.x + rect.width; + _y = rect.y + rect.height; + } + if (_x > texture.getPixelsWide()) { + cc.error(cc._LogInfos.RectWidth, texture.url); + } + if (_y > texture.getPixelsHigh()) { + cc.error(cc._LogInfos.RectHeight, texture.url); + } + } + + this._offsetInPixels.x = offset.x; + this._offsetInPixels.y = offset.y; + cc._pointPixelsToPointsOut(offset, this._offset); + this._originalSizeInPixels.width = originalSize.width; + this._originalSizeInPixels.height = originalSize.height; + cc._sizePixelsToPointsOut(originalSize, this._originalSize); + this._rotated = rotated; + return true; + } +}); + +cc.EventHelper.prototype.apply(cc.SpriteFrame.prototype); + +/** + *

+ * Create a cc.SpriteFrame with a texture filename, rect, rotated, offset and originalSize in pixels.
+ * The originalSize is the size in pixels of the frame before being trimmed. + *

+ * @deprecated since v3.0, please use new construction instead + * @see cc.SpriteFrame + * @param {String|cc.Texture2D} filename + * @param {cc.Rect} rect if parameters' length equal 2, rect in points, else rect in pixels + * @param {Boolean} rotated + * @param {cc.Point} offset + * @param {cc.Size} originalSize + * @return {cc.SpriteFrame} + */ +cc.SpriteFrame.create = function (filename, rect, rotated, offset, originalSize) { + return new cc.SpriteFrame(filename, rect, rotated, offset, originalSize); +}; + +/** + * @deprecated since v3.0, please use new construction instead + * @see cc.SpriteFrame + * @function + */ +cc.SpriteFrame.createWithTexture = cc.SpriteFrame.create; + +cc.SpriteFrame._frameWithTextureForCanvas = function (texture, rect, rotated, offset, originalSize) { + var spriteFrame = new cc.SpriteFrame(); + spriteFrame._texture = texture; + spriteFrame._rectInPixels = rect; + spriteFrame._rect = cc.rectPixelsToPoints(rect); + spriteFrame._offsetInPixels.x = offset.x; + spriteFrame._offsetInPixels.y = offset.y; + cc._pointPixelsToPointsOut(spriteFrame._offsetInPixels, spriteFrame._offset); + spriteFrame._originalSizeInPixels.width = originalSize.width; + spriteFrame._originalSizeInPixels.height = originalSize.height; + cc._sizePixelsToPointsOut(spriteFrame._originalSizeInPixels, spriteFrame._originalSize); + spriteFrame._rotated = rotated; + return spriteFrame; +}; diff --git a/frameworks/cocos2d-html5/cocos2d/core/sprites/CCSpriteFrameCache.js b/frameworks/cocos2d-html5/cocos2d/core/sprites/CCSpriteFrameCache.js new file mode 100644 index 0000000..7d38e89 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/sprites/CCSpriteFrameCache.js @@ -0,0 +1,360 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + *

+ * cc.spriteFrameCache is a singleton that handles the loading of the sprite frames. It saves in a cache the sprite frames.
+ *
+ * example
+ * // add SpriteFrames to spriteFrameCache With File
+ * cc.spriteFrameCache.addSpriteFrames(s_grossiniPlist);
+ *

+ * @class + * @name cc.spriteFrameCache + */ +cc.spriteFrameCache = /** @lends cc.spriteFrameCache# */{ + _CCNS_REG1: /^\s*\{\s*([\-]?\d+[.]?\d*)\s*,\s*([\-]?\d+[.]?\d*)\s*\}\s*$/, + _CCNS_REG2: /^\s*\{\s*\{\s*([\-]?\d+[.]?\d*)\s*,\s*([\-]?\d+[.]?\d*)\s*\}\s*,\s*\{\s*([\-]?\d+[.]?\d*)\s*,\s*([\-]?\d+[.]?\d*)\s*\}\s*\}\s*$/, + + _spriteFrames: {}, + _spriteFramesAliases: {}, + _frameConfigCache: {}, + + _rectFromString: function (content) { + var result = this._CCNS_REG2.exec(content); + if (!result) return cc.rect(0, 0, 0, 0); + return cc.rect(parseFloat(result[1]), parseFloat(result[2]), parseFloat(result[3]), parseFloat(result[4])); + }, + + _pointFromString: function (content) { + var result = this._CCNS_REG1.exec(content); + if (!result) return cc.p(0, 0); + return cc.p(parseFloat(result[1]), parseFloat(result[2])); + }, + + _sizeFromString: function (content) { + var result = this._CCNS_REG1.exec(content); + if (!result) return cc.size(0, 0); + return cc.size(parseFloat(result[1]), parseFloat(result[2])); + }, + + _getFrameConfig: function (url) { + var dict = cc.loader.getRes(url); + + cc.assert(dict, cc._LogInfos.spriteFrameCache__getFrameConfig_2, url); + + cc.loader.release(url);//release it in loader + if (dict._inited) { + this._frameConfigCache[url] = dict; + return dict; + } + this._frameConfigCache[url] = this._parseFrameConfig(dict); + return this._frameConfigCache[url]; + }, + + _getFrameConfigByJsonObject: function (url, jsonObject) { + cc.assert(jsonObject, cc._LogInfos.spriteFrameCache__getFrameConfig_2, url); + this._frameConfigCache[url] = this._parseFrameConfig(jsonObject); + return this._frameConfigCache[url]; + }, + + _parseFrameConfig: function (dict) { + var tempFrames = dict["frames"], tempMeta = dict["metadata"] || dict["meta"]; + var frames = {}, meta = {}; + var format = 0; + if (tempMeta) {//init meta + var tmpFormat = tempMeta["format"]; + format = (tmpFormat.length <= 1) ? parseInt(tmpFormat) : tmpFormat; + meta.image = tempMeta["textureFileName"] || tempMeta["textureFileName"] || tempMeta["image"]; + } + for (var key in tempFrames) { + var frameDict = tempFrames[key]; + if (!frameDict) continue; + var tempFrame = {}; + + if (format == 0) { + tempFrame.rect = cc.rect(frameDict["x"], frameDict["y"], frameDict["width"], frameDict["height"]); + tempFrame.rotated = false; + tempFrame.offset = cc.p(frameDict["offsetX"], frameDict["offsetY"]); + var ow = frameDict["originalWidth"]; + var oh = frameDict["originalHeight"]; + // check ow/oh + if (!ow || !oh) { + cc.log(cc._LogInfos.spriteFrameCache__getFrameConfig); + } + // Math.abs ow/oh + ow = Math.abs(ow); + oh = Math.abs(oh); + tempFrame.size = cc.size(ow, oh); + } else if (format == 1 || format == 2) { + tempFrame.rect = this._rectFromString(frameDict["frame"]); + tempFrame.rotated = frameDict["rotated"] || false; + tempFrame.offset = this._pointFromString(frameDict["offset"]); + tempFrame.size = this._sizeFromString(frameDict["sourceSize"]); + } else if (format == 3) { + // get values + var spriteSize = this._sizeFromString(frameDict["spriteSize"]); + var textureRect = this._rectFromString(frameDict["textureRect"]); + if (spriteSize) { + textureRect = cc.rect(textureRect.x, textureRect.y, spriteSize.width, spriteSize.height); + } + tempFrame.rect = textureRect; + tempFrame.rotated = frameDict["textureRotated"] || false; // == "true"; + tempFrame.offset = this._pointFromString(frameDict["spriteOffset"]); + tempFrame.size = this._sizeFromString(frameDict["spriteSourceSize"]); + tempFrame.aliases = frameDict["aliases"]; + } else { + var tmpFrame = frameDict["frame"], tmpSourceSize = frameDict["sourceSize"]; + key = frameDict["filename"] || key; + tempFrame.rect = cc.rect(tmpFrame["x"], tmpFrame["y"], tmpFrame["w"], tmpFrame["h"]); + tempFrame.rotated = frameDict["rotated"] || false; + tempFrame.offset = cc.p(0, 0); + tempFrame.size = cc.size(tmpSourceSize["w"], tmpSourceSize["h"]); + } + frames[key] = tempFrame; + } + return {_inited: true, frames: frames, meta: meta}; + }, + + // Adds multiple Sprite Frames from a json object. it uses for local web view app. + _addSpriteFramesByObject: function (url, jsonObject, texture) { + cc.assert(url, cc._LogInfos.spriteFrameCache_addSpriteFrames_2); + if (!jsonObject || !jsonObject["frames"]) + return; + + var frameConfig = this._frameConfigCache[url] || this._getFrameConfigByJsonObject(url, jsonObject); + //this._checkConflict(frameConfig); //TODO + this._createSpriteFrames(url, frameConfig, texture); + }, + + _createSpriteFrames: function (url, frameConfig, texture) { + var frames = frameConfig.frames, meta = frameConfig.meta; + if (!texture) { + var texturePath = cc.path.changeBasename(url, meta.image || ".png"); + texture = cc.textureCache.addImage(texturePath); + } else if (texture instanceof cc.Texture2D) { + //do nothing + } else if (cc.isString(texture)) {//string + texture = cc.textureCache.addImage(texture); + } else { + cc.assert(0, cc._LogInfos.spriteFrameCache_addSpriteFrames_3); + } + + //create sprite frames + var spAliases = this._spriteFramesAliases, spriteFrames = this._spriteFrames; + for (var key in frames) { + var frame = frames[key]; + var spriteFrame = spriteFrames[key]; + if (!spriteFrame) { + spriteFrame = new cc.SpriteFrame(texture, cc.rect(frame.rect), frame.rotated, frame.offset, frame.size); + var aliases = frame.aliases; + if (aliases) {//set aliases + for (var i = 0, li = aliases.length; i < li; i++) { + var alias = aliases[i]; + if (spAliases[alias]) + cc.log(cc._LogInfos.spriteFrameCache_addSpriteFrames, alias); + spAliases[alias] = key; + } + } + + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS && spriteFrame.isRotated()) { + //clip to canvas + var locTexture = spriteFrame.getTexture(); + if (locTexture.isLoaded()) { + var tempElement = spriteFrame.getTexture().getHtmlElementObj(); + tempElement = cc.Sprite.CanvasRenderCmd._cutRotateImageToCanvas(tempElement, spriteFrame.getRectInPixels()); + var tempTexture = new cc.Texture2D(); + tempTexture.initWithElement(tempElement); + tempTexture.handleLoadedTexture(); + spriteFrame.setTexture(tempTexture); + spriteFrame.setRotated(false); + + var rect = spriteFrame._rect; + spriteFrame.setRect(cc.rect(0, 0, rect.width, rect.height)); + } + } + spriteFrames[key] = spriteFrame; + } + } + }, + + /** + *

+ * Adds multiple Sprite Frames from a plist or json file.
+ * A texture will be loaded automatically. The texture name will composed by replacing the .plist or .json suffix with .png
+ * If you want to use another texture, you should use the addSpriteFrames:texture parameter.
+ *

+ * @param {String} url file path + * @param {HTMLImageElement|cc.Texture2D|string} [texture] + * @example + * // add SpriteFrames to SpriteFrameCache With File + * cc.spriteFrameCache.addSpriteFrames(s_grossiniPlist); + * cc.spriteFrameCache.addSpriteFrames(s_grossiniJson); + */ + addSpriteFrames: function (url, texture) { + cc.assert(url, cc._LogInfos.spriteFrameCache_addSpriteFrames_2); + + //Is it a SpriteFrame plist? + var dict = this._frameConfigCache[url] || cc.loader.getRes(url); + if (!dict || !dict["frames"]) + return; + + var frameConfig = this._frameConfigCache[url] || this._getFrameConfig(url); + //this._checkConflict(frameConfig); //TODO + this._createSpriteFrames(url, frameConfig, texture); + }, + + // Function to check if frames to add exists already, if so there may be name conflit that must be solved + _checkConflict: function (dictionary) { + var framesDict = dictionary["frames"]; + + for (var key in framesDict) { + if (this._spriteFrames[key]) { + cc.log(cc._LogInfos.spriteFrameCache__checkConflict, key); + } + } + }, + + /** + *

+ * Adds an sprite frame with a given name.
+ * If the name already exists, then the contents of the old name will be replaced with the new one. + *

+ * @param {cc.SpriteFrame} frame + * @param {String} frameName + */ + addSpriteFrame: function (frame, frameName) { + this._spriteFrames[frameName] = frame; + }, + + /** + *

+ * Purges the dictionary of loaded sprite frames.
+ * Call this method if you receive the "Memory Warning".
+ * In the short term: it will free some resources preventing your app from being killed.
+ * In the medium term: it will allocate more resources.
+ * In the long term: it will be the same.
+ *

+ */ + removeSpriteFrames: function () { + this._spriteFrames = {}; + this._spriteFramesAliases = {}; + }, + + /** + * Deletes an sprite frame from the sprite frame cache. + * @param {String} name + */ + removeSpriteFrameByName: function (name) { + // explicit nil handling + if (!name) { + return; + } + + // Is this an alias ? + if (this._spriteFramesAliases[name]) { + delete(this._spriteFramesAliases[name]); + } + if (this._spriteFrames[name]) { + delete(this._spriteFrames[name]); + } + // XXX. Since we don't know the .plist file that originated the frame, we must remove all .plist from the cache + }, + + /** + *

+ * Removes multiple Sprite Frames from a plist file.
+ * Sprite Frames stored in this file will be removed.
+ * It is convinient to call this method when a specific texture needs to be removed.
+ *

+ * @param {String} url Plist filename + */ + removeSpriteFramesFromFile: function (url) { + var self = this, spriteFrames = self._spriteFrames, + aliases = self._spriteFramesAliases, cfg = self._frameConfigCache[url]; + if (!cfg) return; + var frames = cfg.frames; + for (var key in frames) { + if (spriteFrames[key]) { + delete(spriteFrames[key]); + for (var alias in aliases) {//remove alias + if (aliases[alias] === key) delete aliases[alias]; + } + } + } + }, + + /** + *

+ * Removes all Sprite Frames associated with the specified textures.
+ * It is convenient to call this method when a specific texture needs to be removed. + *

+ * @param {HTMLImageElement|HTMLCanvasElement|cc.Texture2D} texture + */ + removeSpriteFramesFromTexture: function (texture) { + var self = this, spriteFrames = self._spriteFrames, aliases = self._spriteFramesAliases; + for (var key in spriteFrames) { + var frame = spriteFrames[key]; + if (frame && (frame.getTexture() === texture)) { + delete(spriteFrames[key]); + for (var alias in aliases) {//remove alias + if (aliases[alias] === key) delete aliases[alias]; + } + } + } + }, + + /** + *

+ * Returns an Sprite Frame that was previously added.
+ * If the name is not found it will return nil.
+ * You should retain the returned copy if you are going to use it.
+ *

+ * @param {String} name name of SpriteFrame + * @return {cc.SpriteFrame} + * @example + * //get a SpriteFrame by name + * var frame = cc.spriteFrameCache.getSpriteFrame("grossini_dance_01.png"); + */ + getSpriteFrame: function (name) { + var self = this, frame = self._spriteFrames[name]; + if (!frame) { + // try alias dictionary + var key = self._spriteFramesAliases[name]; + if (key) { + frame = self._spriteFrames[key.toString()]; + if (!frame) delete self._spriteFramesAliases[name]; + } + } + return frame; + }, + + _clear: function () { + this._spriteFrames = {}; + this._spriteFramesAliases = {}; + this._frameConfigCache = {}; + } +}; diff --git a/frameworks/cocos2d-html5/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js new file mode 100644 index 0000000..51ea502 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js @@ -0,0 +1,332 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +//Sprite's WebGL render command +(function () { + + cc.Sprite.WebGLRenderCmd = function (renderable) { + this._rootCtor(renderable); + this._needDraw = true; + + this._vertices = [ + {x: 0, y: 0, u: 0, v: 0}, // tl + {x: 0, y: 0, u: 0, v: 0}, // bl + {x: 0, y: 0, u: 0, v: 0}, // tr + {x: 0, y: 0, u: 0, v: 0} // br + ]; + this._color = new Uint32Array(1); + this._dirty = false; + this._recursiveDirty = false; + + this._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_SPRITE_POSITION_TEXTURECOLOR); + }; + + var proto = cc.Sprite.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + proto.constructor = cc.Sprite.WebGLRenderCmd; + proto._spriteCmdCtor = cc.Sprite.WebGLRenderCmd; + + proto.updateBlendFunc = function (blendFunc) { + }; + + proto.setDirtyFlag = function (dirtyFlag) { + cc.Node.WebGLRenderCmd.prototype.setDirtyFlag.call(this, dirtyFlag); + this._dirty = true; + }; + + proto.setDirtyRecursively = function (value) { + this._recursiveDirty = value; + this._dirty = value; + // recursively set dirty + var locChildren = this._node._children, child, l = locChildren ? locChildren.length : 0; + for (var i = 0; i < l; i++) { + child = locChildren[i]; + (child instanceof cc.Sprite) && child._renderCmd.setDirtyRecursively(value); + } + }; + + proto._setBatchNodeForAddChild = function (child) { + var node = this._node; + if (node._batchNode) { + if (!(child instanceof cc.Sprite)) { + cc.log(cc._LogInfos.Sprite_addChild); + return false; + } + if (child.texture._webTextureObj !== node.textureAtlas.texture._webTextureObj) + cc.log(cc._LogInfos.Sprite_addChild_2); + + //put it in descendants array of batch node + node._batchNode.appendChild(child); + if (!node._reorderChildDirty) + node._setReorderChildDirtyRecursively(); + } + return true; + }; + + proto._handleTextureForRotatedTexture = function (texture) { + return texture; + }; + + proto.isFrameDisplayed = function (frame) { + var node = this._node; + return (cc.rectEqualToRect(frame.getRect(), node._rect) && frame.getTexture().getName() === node._texture.getName() + && cc.pointEqualToPoint(frame.getOffset(), node._unflippedOffsetPositionFromCenter)); + }; + + proto._textureLoadedCallback = function (sender) { + if (this._textureLoaded) + return; + + this._textureLoaded = true; + var locRect = this._rect; + if (!locRect) { + locRect = cc.rect(0, 0, sender.width, sender.height); + } else if (cc._rectEqualToZero(locRect)) { + locRect.width = sender.width; + locRect.height = sender.height; + } + + this.texture = sender; + this.setTextureRect(locRect, this._rectRotated); + + // by default use "Self Render". + // if the sprite is added to a batchnode, then it will automatically switch to "batchnode Render" + this.setBatchNode(this._batchNode); + this.dispatchEvent("load"); + + // Force refresh the render command list + cc.renderer.childrenOrderDirty = true; + }; + + proto._setTextureCoords = function (rect, needConvert) { + if (needConvert === undefined) + needConvert = true; + if (needConvert) + rect = cc.rectPointsToPixels(rect); + var node = this._node; + + var tex = node._batchNode ? node.textureAtlas.texture : node._texture; + var uvs = this._vertices; + if (!tex) + return; + + var atlasWidth = tex.pixelsWidth; + var atlasHeight = tex.pixelsHeight; + + var left, right, top, bottom, tempSwap; + if (node._rectRotated) { + if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) { + left = (2 * rect.x + 1) / (2 * atlasWidth); + right = left + (rect.height * 2 - 2) / (2 * atlasWidth); + top = (2 * rect.y + 1) / (2 * atlasHeight); + bottom = top + (rect.width * 2 - 2) / (2 * atlasHeight); + } else { + left = rect.x / atlasWidth; + right = (rect.x + rect.height) / atlasWidth; + top = rect.y / atlasHeight; + bottom = (rect.y + rect.width) / atlasHeight; + } + + if (node._flippedX) { + tempSwap = top; + top = bottom; + bottom = tempSwap; + } + + if (node._flippedY) { + tempSwap = left; + left = right; + right = tempSwap; + } + + uvs[0].u = right; // tl + uvs[0].v = top; // tl + uvs[1].u = left; // bl + uvs[1].v = top; // bl + uvs[2].u = right; // tr + uvs[2].v = bottom; // tr + uvs[3].u = left; // br + uvs[3].v = bottom; // br + } else { + if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) { + left = (2 * rect.x + 1) / (2 * atlasWidth); + right = left + (rect.width * 2 - 2) / (2 * atlasWidth); + top = (2 * rect.y + 1) / (2 * atlasHeight); + bottom = top + (rect.height * 2 - 2) / (2 * atlasHeight); + } else { + left = rect.x / atlasWidth; + right = (rect.x + rect.width) / atlasWidth; + top = rect.y / atlasHeight; + bottom = (rect.y + rect.height) / atlasHeight; + } + + if (node._flippedX) { + tempSwap = left; + left = right; + right = tempSwap; + } + + if (node._flippedY) { + tempSwap = top; + top = bottom; + bottom = tempSwap; + } + + uvs[0].u = left; // tl + uvs[0].v = top; // tl + uvs[1].u = left; // bl + uvs[1].v = bottom; // bl + uvs[2].u = right; // tr + uvs[2].v = top; // tr + uvs[3].u = right; // br + uvs[3].v = bottom; // br + } + }; + + proto._setColorDirty = function () { + }; + + proto._updateBlendFunc = function () { + if (this._batchNode) { + cc.log(cc._LogInfos.Sprite__updateBlendFunc); + return; + } + + // it's possible to have an untextured sprite + var node = this._node, + blendFunc = node._blendFunc; + if (!node._texture || !node._texture.hasPremultipliedAlpha()) { + if (blendFunc.src === cc.ONE && blendFunc.dst === cc.BLEND_DST) { + blendFunc.src = cc.SRC_ALPHA; + } + node.opacityModifyRGB = false; + } else { + if (blendFunc.src === cc.SRC_ALPHA && blendFunc.dst === cc.BLEND_DST) { + blendFunc.src = cc.ONE; + } + node.opacityModifyRGB = true; + } + }; + + proto._setTexture = function (texture) { + var node = this._node; + if (node._texture !== texture) { + node._textureLoaded = texture ? texture._textureLoaded : false; + node._texture = texture; + + // Update texture rect and blend func + if (texture) { + var texSize = texture._contentSize; + var rect = cc.rect(0, 0, texSize.width, texSize.height); + node.setTextureRect(rect); + this._updateBlendFunc(); + } + + if (node._textureLoaded) { + // Force refresh the render command list + cc.renderer.childrenOrderDirty = true; + } + } + }; + + proto._checkTextureBoundary = function (texture, rect, rotated) { + if (texture && texture.url) { + var _x, _y; + if (rotated) { + _x = rect.x + rect.height; + _y = rect.y + rect.width; + } else { + _x = rect.x + rect.width; + _y = rect.y + rect.height; + } + if (_x > texture.width) { + cc.error(cc._LogInfos.RectWidth, texture.url); + } + if (_y > texture.height) { + cc.error(cc._LogInfos.RectHeight, texture.url); + } + } + }; + + proto.transform = function (parentCmd, recursive) { + this.originTransform(parentCmd, recursive); + + var node = this._node, + lx = node._offsetPosition.x, rx = lx + node._rect.width, + by = node._offsetPosition.y, ty = by + node._rect.height, + wt = this._worldTransform, + wtx = wt.tx, wty = wt.ty, + lxa = lx * wt.a, lxb = lx * wt.b, rxa = rx * wt.a, rxb = rx * wt.b, + tyc = ty * wt.c, tyd = ty * wt.d, byc = by * wt.c, byd = by * wt.d; + + var vertices = this._vertices; + vertices[0].x = lxa + tyc + wtx; // tl + vertices[0].y = lxb + tyd + wty; + vertices[1].x = lxa + byc + wtx; // bl + vertices[1].y = lxb + byd + wty; + vertices[2].x = rxa + tyc + wtx; // tr + vertices[2].y = rxb + tyd + wty; + vertices[3].x = rxa + byc + wtx; // br + vertices[3].y = rxb + byd + wty; + }; + + proto.needDraw = function () { + var node = this._node, locTexture = node._texture; + return (this._needDraw && locTexture); + }; + + proto.uploadData = function (f32buffer, ui32buffer, vertexDataOffset) { + var node = this._node, locTexture = node._texture; + if (!(locTexture && locTexture._textureLoaded && node._rect.width && node._rect.height) || !this._displayedOpacity) + return 0; + + // Fill in vertex data with quad information (4 vertices for sprite) + var opacity = this._displayedOpacity; + var r = this._displayedColor.r, + g = this._displayedColor.g, + b = this._displayedColor.b; + if (node._opacityModifyRGB) { + var a = opacity / 255; + r *= a; + g *= a; + b *= a; + } + this._color[0] = ((opacity << 24) | (b << 16) | (g << 8) | r); + var z = node._vertexZ; + + var vertices = this._vertices; + var i, len = vertices.length, vertex, offset = vertexDataOffset; + for (i = 0; i < len; ++i) { + vertex = vertices[i]; + f32buffer[offset] = vertex.x; + f32buffer[offset + 1] = vertex.y; + f32buffer[offset + 2] = z; + ui32buffer[offset + 3] = this._color[0]; + f32buffer[offset + 4] = vertex.u; + f32buffer[offset + 5] = vertex.v; + offset += 6; + } + + return len; + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/core/sprites/SpritesPropertyDefine.js b/frameworks/cocos2d-html5/cocos2d/core/sprites/SpritesPropertyDefine.js new file mode 100644 index 0000000..20e8cc9 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/sprites/SpritesPropertyDefine.js @@ -0,0 +1,67 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +cc._tmp.PrototypeSprite = function () { + var _p = cc.Sprite.prototype; + + // Override properties + cc.defineGetterSetter(_p, "opacityModifyRGB", _p.isOpacityModifyRGB, _p.setOpacityModifyRGB); + cc.defineGetterSetter(_p, "opacity", _p.getOpacity, _p.setOpacity); + cc.defineGetterSetter(_p, "color", _p.getColor, _p.setColor); + + // Extended properties + /** @expose */ + _p.dirty; + /** @expose */ + _p.flippedX; + cc.defineGetterSetter(_p, "flippedX", _p.isFlippedX, _p.setFlippedX); + /** @expose */ + _p.flippedY; + cc.defineGetterSetter(_p, "flippedY", _p.isFlippedY, _p.setFlippedY); + /** @expose */ + _p.offsetX; + cc.defineGetterSetter(_p, "offsetX", _p._getOffsetX); + /** @expose */ + _p.offsetY; + cc.defineGetterSetter(_p, "offsetY", _p._getOffsetY); + /** @expose */ + _p.atlasIndex; + /** @expose */ + _p.texture; + cc.defineGetterSetter(_p, "texture", _p.getTexture, _p.setTexture); + /** @expose */ + _p.textureRectRotated; + cc.defineGetterSetter(_p, "textureRectRotated", _p.isTextureRectRotated); + /** @expose */ + _p.textureAtlas; + /** @expose */ + _p.batchNode; + cc.defineGetterSetter(_p, "batchNode", _p.getBatchNode, _p.setBatchNode); + /** @expose */ + _p.quad; + cc.defineGetterSetter(_p, "quad", _p.getQuad); + +}; diff --git a/frameworks/cocos2d-html5/cocos2d/core/support/CCPointExtension.js b/frameworks/cocos2d-html5/cocos2d/core/support/CCPointExtension.js new file mode 100644 index 0000000..3171a5f --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/support/CCPointExtension.js @@ -0,0 +1,516 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + *

cc.Point extensions based on Chipmunk's cpVect file.
+ * These extensions work both with cc.Point

+ * + *

The "ccp" prefix means: "CoCos2d Point"

+ * + *

//Examples:
+ * - cc.pAdd( cc.p(1,1), cc.p(2,2) ); // preferred cocos2d way
+ * - cc.pAdd( cc.p(1,1), cc.p(2,2) ); // also ok but more verbose
+ * - cc.pAdd( cc.cpv(1,1), cc.cpv(2,2) ); // mixing chipmunk and cocos2d (avoid)

+ */ + +/** + * smallest such that 1.0+FLT_EPSILON != 1.0 + * @constant + * @type Number + */ +cc.POINT_EPSILON = parseFloat('1.192092896e-07F'); + +/** + * Returns opposite of point. + * @param {cc.Point} point + * @return {cc.Point} + */ +cc.pNeg = function (point) { + return cc.p(-point.x, -point.y); +}; + +/** + * Calculates sum of two points. + * @param {cc.Point} v1 + * @param {cc.Point} v2 + * @return {cc.Point} + */ +cc.pAdd = function (v1, v2) { + return cc.p(v1.x + v2.x, v1.y + v2.y); +}; + +/** + * Calculates difference of two points. + * @param {cc.Point} v1 + * @param {cc.Point} v2 + * @return {cc.Point} + */ +cc.pSub = function (v1, v2) { + return cc.p(v1.x - v2.x, v1.y - v2.y); +}; + +/** + * Returns point multiplied by given factor. + * @param {cc.Point} point + * @param {Number} floatVar + * @return {cc.Point} + */ +cc.pMult = function (point, floatVar) { + return cc.p(point.x * floatVar, point.y * floatVar); +}; + +/** + * Calculates midpoint between two points. + * @param {cc.Point} v1 + * @param {cc.Point} v2 + * @return {cc.Point} + */ +cc.pMidpoint = function (v1, v2) { + return cc.pMult(cc.pAdd(v1, v2), 0.5); +}; + +/** + * Calculates dot product of two points. + * @param {cc.Point} v1 + * @param {cc.Point} v2 + * @return {Number} + */ +cc.pDot = function (v1, v2) { + return v1.x * v2.x + v1.y * v2.y; +}; + +/** + * Calculates cross product of two points. + * @param {cc.Point} v1 + * @param {cc.Point} v2 + * @return {Number} + */ +cc.pCross = function (v1, v2) { + return v1.x * v2.y - v1.y * v2.x; +}; + +/** + * Calculates perpendicular of v, rotated 90 degrees counter-clockwise -- cross(v, perp(v)) >= 0 + * @param {cc.Point} point + * @return {cc.Point} + */ +cc.pPerp = function (point) { + return cc.p(-point.y, point.x); +}; + +/** + * Calculates perpendicular of v, rotated 90 degrees clockwise -- cross(v, rperp(v)) <= 0 + * @param {cc.Point} point + * @return {cc.Point} + */ +cc.pRPerp = function (point) { + return cc.p(point.y, -point.x); +}; + +/** + * Calculates the projection of v1 over v2. + * @param {cc.Point} v1 + * @param {cc.Point} v2 + * @return {cc.Point} + */ +cc.pProject = function (v1, v2) { + return cc.pMult(v2, cc.pDot(v1, v2) / cc.pDot(v2, v2)); +}; + +/** + * Rotates two points. + * @param {cc.Point} v1 + * @param {cc.Point} v2 + * @return {cc.Point} + */ +cc.pRotate = function (v1, v2) { + return cc.p(v1.x * v2.x - v1.y * v2.y, v1.x * v2.y + v1.y * v2.x); +}; + +/** + * Unrotates two points. + * @param {cc.Point} v1 + * @param {cc.Point} v2 + * @return {cc.Point} + */ +cc.pUnrotate = function (v1, v2) { + return cc.p(v1.x * v2.x + v1.y * v2.y, v1.y * v2.x - v1.x * v2.y); +}; + +/** + * Calculates the square length of a cc.Point (not calling sqrt() ) + * @param {cc.Point} v + *@return {Number} + */ +cc.pLengthSQ = function (v) { + return cc.pDot(v, v); +}; + +/** + * Calculates the square distance between two points (not calling sqrt() ) + * @param {cc.Point} point1 + * @param {cc.Point} point2 + * @return {Number} + */ +cc.pDistanceSQ = function(point1, point2){ + return cc.pLengthSQ(cc.pSub(point1,point2)); +}; + +/** + * Calculates distance between point an origin + * @param {cc.Point} v + * @return {Number} + */ +cc.pLength = function (v) { + return Math.sqrt(cc.pLengthSQ(v)); +}; + +/** + * Calculates the distance between two points + * @param {cc.Point} v1 + * @param {cc.Point} v2 + * @return {Number} + */ +cc.pDistance = function (v1, v2) { + return cc.pLength(cc.pSub(v1, v2)); +}; + +/** + * Returns point multiplied to a length of 1. + * @param {cc.Point} v + * @return {cc.Point} + */ +cc.pNormalize = function (v) { + var n = cc.pLength(v); + return n === 0 ? cc.p(v) : cc.pMult(v, 1.0 / n); +}; + +/** + * Converts radians to a normalized vector. + * @param {Number} a + * @return {cc.Point} + */ +cc.pForAngle = function (a) { + return cc.p(Math.cos(a), Math.sin(a)); +}; + +/** + * Converts a vector to radians. + * @param {cc.Point} v + * @return {Number} + */ +cc.pToAngle = function (v) { + return Math.atan2(v.y, v.x); +}; + +/** + * Clamp a value between from and to. + * @param {Number} value + * @param {Number} min_inclusive + * @param {Number} max_inclusive + * @return {Number} + */ +cc.clampf = function (value, min_inclusive, max_inclusive) { + if (min_inclusive > max_inclusive) { + var temp = min_inclusive; + min_inclusive = max_inclusive; + max_inclusive = temp; + } + return value < min_inclusive ? min_inclusive : value < max_inclusive ? value : max_inclusive; +}; + +/** + * Clamp a point between from and to. + * @param {Point} p + * @param {Number} min_inclusive + * @param {Number} max_inclusive + * @return {cc.Point} + */ +cc.pClamp = function (p, min_inclusive, max_inclusive) { + return cc.p(cc.clampf(p.x, min_inclusive.x, max_inclusive.x), cc.clampf(p.y, min_inclusive.y, max_inclusive.y)); +}; + +/** + * Quickly convert cc.Size to a cc.Point + * @param {cc.Size} s + * @return {cc.Point} + */ +cc.pFromSize = function (s) { + return cc.p(s.width, s.height); +}; + +/** + * Run a math operation function on each point component
+ * Math.abs, Math.fllor, Math.ceil, Math.round. + * @param {cc.Point} p + * @param {Function} opFunc + * @return {cc.Point} + * @example + * //For example: let's try to take the floor of x,y + * var p = cc.pCompOp(cc.p(10,10),Math.abs); + */ +cc.pCompOp = function (p, opFunc) { + return cc.p(opFunc(p.x), opFunc(p.y)); +}; + +/** + * Linear Interpolation between two points a and b + * alpha == 0 ? a + * alpha == 1 ? b + * otherwise a value between a..b + * @param {cc.Point} a + * @param {cc.Point} b + * @param {Number} alpha + * @return {cc.Point} + */ +cc.pLerp = function (a, b, alpha) { + return cc.pAdd(cc.pMult(a, 1 - alpha), cc.pMult(b, alpha)); +}; + +/** + * @param {cc.Point} a + * @param {cc.Point} b + * @param {Number} variance + * @return {Boolean} if points have fuzzy equality which means equal with some degree of variance. + */ +cc.pFuzzyEqual = function (a, b, variance) { + if (a.x - variance <= b.x && b.x <= a.x + variance) { + if (a.y - variance <= b.y && b.y <= a.y + variance) + return true; + } + return false; +}; + +/** + * Multiplies a nd b components, a.x*b.x, a.y*b.y + * @param {cc.Point} a + * @param {cc.Point} b + * @return {cc.Point} + */ +cc.pCompMult = function (a, b) { + return cc.p(a.x * b.x, a.y * b.y); +}; + +/** + * @param {cc.Point} a + * @param {cc.Point} b + * @return {Number} the signed angle in radians between two vector directions + */ +cc.pAngleSigned = function (a, b) { + var a2 = cc.pNormalize(a); + var b2 = cc.pNormalize(b); + var angle = Math.atan2(a2.x * b2.y - a2.y * b2.x, cc.pDot(a2, b2)); + if (Math.abs(angle) < cc.POINT_EPSILON) + return 0.0; + return angle; +}; + +/** + * @param {cc.Point} a + * @param {cc.Point} b + * @return {Number} the angle in radians between two vector directions + */ +cc.pAngle = function (a, b) { + var angle = Math.acos(cc.pDot(cc.pNormalize(a), cc.pNormalize(b))); + if (Math.abs(angle) < cc.POINT_EPSILON) return 0.0; + return angle; +}; + +/** + * Rotates a point counter clockwise by the angle around a pivot + * @param {cc.Point} v v is the point to rotate + * @param {cc.Point} pivot pivot is the pivot, naturally + * @param {Number} angle angle is the angle of rotation cw in radians + * @return {cc.Point} the rotated point + */ +cc.pRotateByAngle = function (v, pivot, angle) { + var r = cc.pSub(v, pivot); + var cosa = Math.cos(angle), sina = Math.sin(angle); + var t = r.x; + r.x = t * cosa - r.y * sina + pivot.x; + r.y = t * sina + r.y * cosa + pivot.y; + return r; +}; + +/** + * A general line-line intersection test + * indicating successful intersection of a line
+ * note that to truly test intersection for segments we have to make
+ * sure that s & t lie within [0..1] and for rays, make sure s & t > 0
+ * the hit point is p3 + t * (p4 - p3);
+ * the hit point also is p1 + s * (p2 - p1); + * @param {cc.Point} A A is the startpoint for the first line P1 = (p1 - p2). + * @param {cc.Point} B B is the endpoint for the first line P1 = (p1 - p2). + * @param {cc.Point} C C is the startpoint for the second line P2 = (p3 - p4). + * @param {cc.Point} D D is the endpoint for the second line P2 = (p3 - p4). + * @param {cc.Point} retP retP.x is the range for a hitpoint in P1 (pa = p1 + s*(p2 - p1)),
+ * retP.y is the range for a hitpoint in P3 (pa = p2 + t*(p4 - p3)). + * @return {Boolean} + */ +cc.pLineIntersect = function (A, B, C, D, retP) { + if ((A.x === B.x && A.y === B.y) || (C.x === D.x && C.y === D.y)) { + return false; + } + var BAx = B.x - A.x; + var BAy = B.y - A.y; + var DCx = D.x - C.x; + var DCy = D.y - C.y; + var ACx = A.x - C.x; + var ACy = A.y - C.y; + + var denom = DCy * BAx - DCx * BAy; + + retP.x = DCx * ACy - DCy * ACx; + retP.y = BAx * ACy - BAy * ACx; + + if (denom === 0) { + if (retP.x === 0 || retP.y === 0) { + // Lines incident + return true; + } + // Lines parallel and not incident + return false; + } + + retP.x = retP.x / denom; + retP.y = retP.y / denom; + + return true; +}; + +/** + * ccpSegmentIntersect return YES if Segment A-B intersects with segment C-D. + * @param {cc.Point} A + * @param {cc.Point} B + * @param {cc.Point} C + * @param {cc.Point} D + * @return {Boolean} + */ +cc.pSegmentIntersect = function (A, B, C, D) { + var retP = cc.p(0, 0); + if (cc.pLineIntersect(A, B, C, D, retP)) + if (retP.x >= 0.0 && retP.x <= 1.0 && retP.y >= 0.0 && retP.y <= 1.0) + return true; + return false; +}; + +/** + * ccpIntersectPoint return the intersection point of line A-B, C-D + * @param {cc.Point} A + * @param {cc.Point} B + * @param {cc.Point} C + * @param {cc.Point} D + * @return {cc.Point} + */ +cc.pIntersectPoint = function (A, B, C, D) { + var retP = cc.p(0, 0); + + if (cc.pLineIntersect(A, B, C, D, retP)) { + // Point of intersection + var P = cc.p(0, 0); + P.x = A.x + retP.x * (B.x - A.x); + P.y = A.y + retP.x * (B.y - A.y); + return P; + } + + return cc.p(0,0); +}; + +/** + * check to see if both points are equal + * @param {cc.Point} A A ccp a + * @param {cc.Point} B B ccp b to be compared + * @return {Boolean} the true if both ccp are same + */ +cc.pSameAs = function (A, B) { + if ((A != null) && (B != null)) { + return (A.x === B.x && A.y === B.y); + } + return false; +}; + + + +// High Performance In Place Operationrs --------------------------------------- + +/** + * sets the position of the point to 0 + * @param {cc.Point} v + */ +cc.pZeroIn = function(v) { + v.x = 0; + v.y = 0; +}; + +/** + * copies the position of one point to another + * @param {cc.Point} v1 + * @param {cc.Point} v2 + */ +cc.pIn = function(v1, v2) { + v1.x = v2.x; + v1.y = v2.y; +}; + +/** + * multiplies the point with the given factor (inplace) + * @param {cc.Point} point + * @param {Number} floatVar + */ +cc.pMultIn = function(point, floatVar) { + point.x *= floatVar; + point.y *= floatVar; +}; + +/** + * subtracts one point from another (inplace) + * @param {cc.Point} v1 + * @param {cc.Point} v2 + */ +cc.pSubIn = function(v1, v2) { + v1.x -= v2.x; + v1.y -= v2.y; +}; + +/** + * adds one point to another (inplace) + * @param {cc.Point} v1 + * @param {cc.Point} v2 + */ +cc.pAddIn = function(v1, v2) { + v1.x += v2.x; + v1.y += v2.y; +}; + +/** + * normalizes the point (inplace) + * @param {cc.Point} v + */ +cc.pNormalizeIn = function(v) { + var n = Math.sqrt(v.x * v.x + v.y * v.y); + if (n !== 0) + cc.pMultIn(v, 1.0 / n); +}; diff --git a/frameworks/cocos2d-html5/cocos2d/core/support/CCVertex.js b/frameworks/cocos2d-html5/cocos2d/core/support/CCVertex.js new file mode 100644 index 0000000..44b92f0 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/support/CCVertex.js @@ -0,0 +1,170 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2009 Valentin Milea + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * converts a line to a polygon + * @param {Float32Array} points + * @param {Number} stroke + * @param {Float32Array} vertices + * @param {Number} offset + * @param {Number} nuPoints + */ +cc.vertexLineToPolygon = function (points, stroke, vertices, offset, nuPoints) { + nuPoints += offset; + if (nuPoints <= 1) + return; + + stroke *= 0.5; + var idx; + var nuPointsMinus = nuPoints - 1; + for (var i = offset; i < nuPoints; i++) { + idx = i * 2; + var p1 = cc.p(points[i * 2], points[i * 2 + 1]); + var perpVector; + + if (i === 0) + perpVector = cc.pPerp(cc.pNormalize(cc.pSub(p1, cc.p(points[(i + 1) * 2], points[(i + 1) * 2 + 1])))); + else if (i === nuPointsMinus) + perpVector = cc.pPerp(cc.pNormalize(cc.pSub(cc.p(points[(i - 1) * 2], points[(i - 1) * 2 + 1]), p1))); + else { + var p0 = cc.p(points[(i - 1) * 2], points[(i - 1) * 2 + 1]); + var p2 = cc.p(points[(i + 1) * 2], points[(i + 1) * 2 + 1]); + + var p2p1 = cc.pNormalize(cc.pSub(p2, p1)); + var p0p1 = cc.pNormalize(cc.pSub(p0, p1)); + + // Calculate angle between vectors + var angle = Math.acos(cc.pDot(p2p1, p0p1)); + + if (angle < cc.degreesToRadians(70)) + perpVector = cc.pPerp(cc.pNormalize(cc.pMidpoint(p2p1, p0p1))); + else if (angle < cc.degreesToRadians(170)) + perpVector = cc.pNormalize(cc.pMidpoint(p2p1, p0p1)); + else + perpVector = cc.pPerp(cc.pNormalize(cc.pSub(p2, p0))); + } + perpVector = cc.pMult(perpVector, stroke); + + vertices[idx * 2] = p1.x + perpVector.x; + vertices[idx * 2 + 1] = p1.y + perpVector.y; + vertices[(idx + 1) * 2] = p1.x - perpVector.x; + vertices[(idx + 1) * 2 + 1] = p1.y - perpVector.y; + } + + // Validate vertexes + offset = (offset === 0) ? 0 : offset - 1; + for (i = offset; i < nuPointsMinus; i++) { + idx = i * 2; + var idx1 = idx + 2; + + var v1 = cc.vertex2(vertices[idx * 2], vertices[idx * 2 + 1]); + var v2 = cc.vertex2(vertices[(idx + 1) * 2], vertices[(idx + 1) * 2 + 1]); + var v3 = cc.vertex2(vertices[idx1 * 2], vertices[idx1 * 2]); + var v4 = cc.vertex2(vertices[(idx1 + 1) * 2], vertices[(idx1 + 1) * 2 + 1]); + + //BOOL fixVertex = !ccpLineIntersect(ccp(p1.x, p1.y), ccp(p4.x, p4.y), ccp(p2.x, p2.y), ccp(p3.x, p3.y), &s, &t); + var fixVertexResult = !cc.vertexLineIntersect(v1.x, v1.y, v4.x, v4.y, v2.x, v2.y, v3.x, v3.y); + if (!fixVertexResult.isSuccess) + if (fixVertexResult.value < 0.0 || fixVertexResult.value > 1.0) + fixVertexResult.isSuccess = true; + + if (fixVertexResult.isSuccess) { + vertices[idx1 * 2] = v4.x; + vertices[idx1 * 2 + 1] = v4.y; + vertices[(idx1 + 1) * 2] = v3.x; + vertices[(idx1 + 1) * 2 + 1] = v3.y; + } + } +}; + +/** + * returns whether or not the line intersects + * @param {Number} Ax + * @param {Number} Ay + * @param {Number} Bx + * @param {Number} By + * @param {Number} Cx + * @param {Number} Cy + * @param {Number} Dx + * @param {Number} Dy + * @return {Object} + */ +cc.vertexLineIntersect = function (Ax, Ay, Bx, By, Cx, Cy, Dx, Dy) { + var distAB, theCos, theSin, newX; + + // FAIL: Line undefined + if ((Ax === Bx && Ay === By) || (Cx === Dx && Cy === Dy)) + return {isSuccess:false, value:0}; + + // Translate system to make A the origin + Bx -= Ax; + By -= Ay; + Cx -= Ax; + Cy -= Ay; + Dx -= Ax; + Dy -= Ay; + + // Length of segment AB + distAB = Math.sqrt(Bx * Bx + By * By); + + // Rotate the system so that point B is on the positive X axis. + theCos = Bx / distAB; + theSin = By / distAB; + newX = Cx * theCos + Cy * theSin; + Cy = Cy * theCos - Cx * theSin; + Cx = newX; + newX = Dx * theCos + Dy * theSin; + Dy = Dy * theCos - Dx * theSin; + Dx = newX; + + // FAIL: Lines are parallel. + if (Cy === Dy) return {isSuccess:false, value:0}; + + // Discover the relative position of the intersection in the line AB + var t = (Dx + (Cx - Dx) * Dy / (Dy - Cy)) / distAB; + + // Success. + return {isSuccess:true, value:t}; +}; + +/** + * returns wheter or not polygon defined by vertex list is clockwise + * @param {Array} verts + * @return {Boolean} + */ +cc.vertexListIsClockwise = function(verts) { + for (var i = 0, len = verts.length; i < len; i++) { + var a = verts[i]; + var b = verts[(i + 1) % len]; + var c = verts[(i + 2) % len]; + + if (cc.pCross(cc.pSub(b, a), cc.pSub(c, b)) > 0) + return false; + } + + return true; +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/cocos2d/core/support/TransformUtils.js b/frameworks/cocos2d-html5/cocos2d/core/support/TransformUtils.js new file mode 100644 index 0000000..bb57c6f --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/support/TransformUtils.js @@ -0,0 +1,62 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2009 Valentin Milea + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * convert an affine transform object to a kmMat4 object + * @param {cc.AffineTransform} trans + * @param {cc.kmMat4} mat + * @function + */ +cc.CGAffineToGL = function (trans, mat) { + // | m[0] m[4] m[8] m[12] | | m11 m21 m31 m41 | | a c 0 tx | + // | m[1] m[5] m[9] m[13] | | m12 m22 m32 m42 | | b d 0 ty | + // | m[2] m[6] m[10] m[14] | <=> | m13 m23 m33 m43 | <=> | 0 0 1 0 | + // | m[3] m[7] m[11] m[15] | | m14 m24 m34 m44 | | 0 0 0 1 | + mat[2] = mat[3] = mat[6] = mat[7] = mat[8] = mat[9] = mat[11] = mat[14] = 0.0; + mat[10] = mat[15] = 1.0; + mat[0] = trans.a; + mat[4] = trans.c; + mat[12] = trans.tx; + mat[1] = trans.b; + mat[5] = trans.d; + mat[13] = trans.ty; +}; + +/** + * Convert a kmMat4 object to an affine transform object + * @param {cc.kmMat4} mat + * @param {cc.AffineTransform} trans + * @function + */ +cc.GLToCGAffine = function (mat, trans) { + trans.a = mat[0]; + trans.c = mat[4]; + trans.tx = mat[12]; + trans.b = mat[1]; + trans.d = mat[5]; + trans.ty = mat[13]; +}; diff --git a/frameworks/cocos2d-html5/cocos2d/core/textures/CCTexture2D.js b/frameworks/cocos2d-html5/cocos2d/core/textures/CCTexture2D.js new file mode 100644 index 0000000..b69d83c --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/textures/CCTexture2D.js @@ -0,0 +1,622 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +//CONSTANTS: + +/** + * Horizontal center and vertical center. + * @constant + * @type Number + */ +cc.ALIGN_CENTER = 0x33; + +/** + * Horizontal center and vertical top. + * @constant + * @type Number + */ +cc.ALIGN_TOP = 0x13; + +/** + * Horizontal right and vertical top. + * @constant + * @type Number + */ +cc.ALIGN_TOP_RIGHT = 0x12; + +/** + * Horizontal right and vertical center. + * @constant + * @type Number + */ +cc.ALIGN_RIGHT = 0x32; + +/** + * Horizontal right and vertical bottom. + * @constant + * @type Number + */ +cc.ALIGN_BOTTOM_RIGHT = 0x22; + +/** + * Horizontal center and vertical bottom. + * @constant + * @type Number + */ +cc.ALIGN_BOTTOM = 0x23; + +/** + * Horizontal left and vertical bottom. + * @constant + * @type Number + */ +cc.ALIGN_BOTTOM_LEFT = 0x21; + +/** + * Horizontal left and vertical center. + * @constant + * @type Number + */ +cc.ALIGN_LEFT = 0x31; + +/** + * Horizontal left and vertical top. + * @constant + * @type Number + */ +cc.ALIGN_TOP_LEFT = 0x11; +//----------------------Possible texture pixel formats---------------------------- + + +// By default PVR images are treated as if they don't have the alpha channel premultiplied +cc.PVRHaveAlphaPremultiplied_ = false; + +//cc.Texture2DWebGL move to TextureWebGL.js + +cc.game.addEventListener(cc.game.EVENT_RENDERER_INITED, function () { + + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) { + + var proto = { + _contentSize: null, + _textureLoaded: false, + _htmlElementObj: null, + url: null, + _pattern: null, + + ctor: function () { + this._contentSize = cc.size(0, 0); + this._textureLoaded = false; + this._htmlElementObj = null; + this._pattern = ""; + this._pixelsWide = 0; + this._pixelsHigh = 0; + }, + + /** + * get width in pixels + * @return {Number} + */ + getPixelsWide: function () { + return this._pixelsWide; + }, + + /** + * get height of in pixels + * @return {Number} + */ + getPixelsHigh: function () { + return this._pixelsHigh; + }, + + /** + * get content size + * @returns {cc.Size} + */ + getContentSize: function () { + var locScaleFactor = cc.contentScaleFactor(); + return cc.size(this._contentSize.width / locScaleFactor, this._contentSize.height / locScaleFactor); + }, + + _getWidth: function () { + return this._contentSize.width / cc.contentScaleFactor(); + }, + _getHeight: function () { + return this._contentSize.height / cc.contentScaleFactor(); + }, + + /** + * get content size in pixels + * @returns {cc.Size} + */ + getContentSizeInPixels: function () { + return this._contentSize; + }, + + /** + * init with HTML element + * @param {HTMLImageElement|HTMLCanvasElement} element + */ + initWithElement: function (element) { + if (!element) + return; + this._htmlElementObj = element; + this._pixelsWide = this._contentSize.width = element.width; + this._pixelsHigh = this._contentSize.height = element.height; + this._textureLoaded = true; + }, + + /** + * HTMLElement Object getter + * @return {HTMLImageElement|HTMLCanvasElement} + */ + getHtmlElementObj: function () { + return this._htmlElementObj; + }, + + /** + * check whether texture is loaded + * @returns {boolean} + */ + isLoaded: function () { + return this._textureLoaded; + }, + + /** + * handle loaded texture + */ + handleLoadedTexture: function () { + var self = this; + if (!self._htmlElementObj) { + return; + } + + var locElement = self._htmlElementObj; + self._pixelsWide = self._contentSize.width = locElement.width; + self._pixelsHigh = self._contentSize.height = locElement.height; + + //dispatch load event to listener. + self.dispatchEvent("load"); + }, + + /** + * description of cc.Texture2D + * @returns {string} + */ + description: function () { + return ""; + }, + + initWithData: function (data, pixelFormat, pixelsWide, pixelsHigh, contentSize) { + //support only in WebGl rendering mode + return false; + }, + + initWithImage: function (uiImage) { + //support only in WebGl rendering mode + return false; + }, + + initWithString: function (text, fontName, fontSize, dimensions, hAlignment, vAlignment) { + //support only in WebGl rendering mode + return false; + }, + + releaseTexture: function () { + this._htmlElementObj = null; + cc.loader.release(this.url); + }, + + getName: function () { + //support only in WebGl rendering mode + return null; + }, + + getMaxS: function () { + //support only in WebGl rendering mode + return 1; + }, + + setMaxS: function (maxS) { + //support only in WebGl rendering mode + }, + + getMaxT: function () { + return 1; + }, + + setMaxT: function (maxT) { + //support only in WebGl rendering mode + }, + + getPixelFormat: function () { + //support only in WebGl rendering mode + return null; + }, + + getShaderProgram: function () { + //support only in WebGl rendering mode + return null; + }, + + setShaderProgram: function (shaderProgram) { + //support only in WebGl rendering mode + }, + + hasPremultipliedAlpha: function () { + //support only in WebGl rendering mode + return false; + }, + + hasMipmaps: function () { + //support only in WebGl rendering mode + return false; + }, + + releaseData: function (data) { + //support only in WebGl rendering mode + data = null; + }, + + keepData: function (data, length) { + //support only in WebGl rendering mode + return data; + }, + + drawAtPoint: function (point) { + //support only in WebGl rendering mode + }, + + drawInRect: function (rect) { + //support only in WebGl rendering mode + }, + + /** + * init with ETC file + * @warning does not support on HTML5 + */ + initWithETCFile: function (file) { + cc.log(cc._LogInfos.Texture2D_initWithETCFile); + return false; + }, + + /** + * init with PVR file + * @warning does not support on HTML5 + */ + initWithPVRFile: function (file) { + cc.log(cc._LogInfos.Texture2D_initWithPVRFile); + return false; + }, + + /** + * init with PVRTC data + * @warning does not support on HTML5 + */ + initWithPVRTCData: function (data, level, bpp, hasAlpha, length, pixelFormat) { + cc.log(cc._LogInfos.Texture2D_initWithPVRTCData); + return false; + }, + + setTexParameters: function (texParams, magFilter, wrapS, wrapT) { + if (magFilter !== undefined) + texParams = {minFilter: texParams, magFilter: magFilter, wrapS: wrapS, wrapT: wrapT}; + + if (texParams.wrapS === cc.REPEAT && texParams.wrapT === cc.REPEAT) { + this._pattern = "repeat"; + return; + } + + if (texParams.wrapS === cc.REPEAT) { + this._pattern = "repeat-x"; + return; + } + + if (texParams.wrapT === cc.REPEAT) { + this._pattern = "repeat-y"; + return; + } + + this._pattern = ""; + }, + + setAntiAliasTexParameters: function () { + //support only in WebGl rendering mode + }, + + setAliasTexParameters: function () { + //support only in WebGl rendering mode + }, + + generateMipmap: function () { + //support only in WebGl rendering mode + }, + + stringForFormat: function () { + //support only in WebGl rendering mode + return ""; + }, + + bitsPerPixelForFormat: function (format) { + //support only in WebGl rendering mode + return -1; + }, + + /** + * add listener for loaded event + * @param {Function} callback + * @param {cc.Node} target + * @deprecated since 3.1, please use addEventListener instead + */ + addLoadedEventListener: function (callback, target) { + this.addEventListener("load", callback, target); + }, + + /** + * remove listener from listeners by target + * @param {cc.Node} target + */ + removeLoadedEventListener: function (target) { + this.removeEventTarget("load", target); + }, + + _generateColorTexture: function () {/*overide*/ + }, + _generateTextureCacheForColor: function () { + if (this.channelCache) + return this.channelCache; + + var textureCache = [ + document.createElement("canvas"), + document.createElement("canvas"), + document.createElement("canvas"), + document.createElement("canvas") + ]; + //todo texture onload + renderToCache(this._htmlElementObj, textureCache); + return this.channelCache = textureCache; + }, + + //hack for gray effect + _grayElementObj: null, + _backupElement: null, + _isGray: false, + _switchToGray: function (toGray) { + if (!this._textureLoaded || this._isGray === toGray) + return; + this._isGray = toGray; + if (this._isGray) { + this._backupElement = this._htmlElementObj; + if (!this._grayElementObj) + this._grayElementObj = cc.Texture2D._generateGrayTexture(this._htmlElementObj); + this._htmlElementObj = this._grayElementObj; + } else { + if (this._backupElement !== null) + this._htmlElementObj = this._backupElement; + } + }, + + _generateGrayTexture: function() { + if(!this._textureLoaded) + return null; + var grayElement = cc.Texture2D._generateGrayTexture(this._htmlElementObj); + var newTexture = new cc.Texture2D(); + newTexture.initWithElement(grayElement); + newTexture.handleLoadedTexture(); + return newTexture; + }, + }; + + var renderToCache = function (image, cache) { + var w = image.width; + var h = image.height; + + cache[0].width = w; + cache[0].height = h; + cache[1].width = w; + cache[1].height = h; + cache[2].width = w; + cache[2].height = h; + cache[3].width = w; + cache[3].height = h; + + var cacheCtx = cache[3].getContext("2d"); + cacheCtx.drawImage(image, 0, 0); + var pixels = cacheCtx.getImageData(0, 0, w, h).data; + + var ctx; + for (var rgbI = 0; rgbI < 4; rgbI++) { + ctx = cache[rgbI].getContext("2d"); + + var to = ctx.getImageData(0, 0, w, h); + var data = to.data; + for (var i = 0; i < pixels.length; i += 4) { + data[i] = (rgbI === 0) ? pixels[i] : 0; + data[i + 1] = (rgbI === 1) ? pixels[i + 1] : 0; + data[i + 2] = (rgbI === 2) ? pixels[i + 2] : 0; + data[i + 3] = pixels[i + 3]; + } + ctx.putImageData(to, 0, 0); + } + image.onload = null; + }; + + //change color function + if (cc.sys._supportCanvasNewBlendModes) { + //multiply mode + //Primary afferent, Draw a new texture based on rect + proto._generateColorTexture = function (r, g, b, rect, canvas) { + var onlyCanvas = false; + if (canvas) + onlyCanvas = true; + else + canvas = document.createElement("canvas"); + var textureImage = this._htmlElementObj; + if (!rect) + rect = cc.rect(0, 0, textureImage.width, textureImage.height); + + canvas.width = rect.width; + canvas.height = rect.height; + + var context = canvas.getContext("2d"); + context.globalCompositeOperation = "source-over"; + context.fillStyle = "rgb(" + (r | 0) + "," + (g | 0) + "," + (b | 0) + ")"; + context.fillRect(0, 0, rect.width, rect.height); + context.globalCompositeOperation = "multiply"; + context.drawImage( + textureImage, + rect.x, rect.y, rect.width, rect.height, + 0, 0, rect.width, rect.height + ); + context.globalCompositeOperation = "destination-atop"; + context.drawImage( + textureImage, + rect.x, rect.y, rect.width, rect.height, + 0, 0, rect.width, rect.height + ); + if (onlyCanvas) + return canvas; + var newTexture = new cc.Texture2D(); + newTexture.initWithElement(canvas); + newTexture.handleLoadedTexture(); + return newTexture; + }; + } else { + //Four color map overlay + proto._generateColorTexture = function (r, g, b, rect, canvas) { + var onlyCanvas = false; + if (canvas) + onlyCanvas = true; + else + canvas = document.createElement("canvas"); + + var textureImage = this._htmlElementObj; + if (!rect) + rect = cc.rect(0, 0, textureImage.width, textureImage.height); + var x, y, w, h; + x = rect.x; y = rect.y; w = rect.width; h = rect.height; + if (!w || !h) + return; + + canvas.width = w; + canvas.height = h; + + var context = canvas.getContext("2d"); + var tintedImgCache = cc.textureCache.getTextureColors(this); + context.globalCompositeOperation = 'lighter'; + context.drawImage( + tintedImgCache[3], + x, y, w, h, + 0, 0, w, h + ); + if (r > 0) { + context.globalAlpha = r / 255; + context.drawImage( + tintedImgCache[0], + x, y, w, h, + 0, 0, w, h + ); + } + if (g > 0) { + context.globalAlpha = g / 255; + context.drawImage( + tintedImgCache[1], + x, y, w, h, + 0, 0, w, h + ); + } + if (b > 0) { + context.globalAlpha = b / 255; + context.drawImage( + tintedImgCache[2], + x, y, w, h, + 0, 0, w, h + ); + } + if (onlyCanvas) + return canvas; + + var newTexture = new cc.Texture2D(); + newTexture.initWithElement(canvas); + newTexture.handleLoadedTexture(); + return newTexture; + }; + } + + /** + *

+ * This class allows to easily create OpenGL or Canvas 2D textures from images, text or raw data.
+ * The created cc.Texture2D object will always have power-of-two dimensions.
+ * Depending on how you create the cc.Texture2D object, the actual image area of the texture might be smaller than the texture dimensions
+ * i.e. "contentSize" != (pixelsWide, pixelsHigh) and (maxS, maxT) != (1.0, 1.0).
+ * Be aware that the content of the generated textures will be upside-down!

+ * @name cc.Texture2D + * @class + * @extends cc.Class + * + * @property {WebGLTexture} name - <@readonly> WebGLTexture Object + * @property {Number} pixelFormat - <@readonly> Pixel format of the texture + * @property {Number} pixelsWidth - <@readonly> Width in pixels + * @property {Number} pixelsHeight - <@readonly> Height in pixels + * @property {Number} width - Content width in points + * @property {Number} height - Content height in points + * @property {cc.GLProgram} shaderProgram - The shader program used by drawAtPoint and drawInRect + * @property {Number} maxS - Texture max S + * @property {Number} maxT - Texture max T + */ + cc.Texture2D = cc.Class.extend(/** @lends cc.Texture2D# */proto); + + cc.Texture2D._generateGrayTexture = function (texture, rect, renderCanvas) { + if (texture === null) + return null; + renderCanvas = renderCanvas || document.createElement("canvas"); + rect = rect || cc.rect(0, 0, texture.width, texture.height); + renderCanvas.width = rect.width; + renderCanvas.height = rect.height; + + var context = renderCanvas.getContext("2d"); + context.drawImage(texture, rect.x, rect.y, rect.width, rect.height, 0, 0, rect.width, rect.height); + var imgData = context.getImageData(0, 0, rect.width, rect.height); + var data = imgData.data; + for (var i = 0, len = data.length; i < len; i += 4) { + data[i] = data[i + 1] = data[i + 2] = 0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2]; + } + context.putImageData(imgData, 0, 0); + return renderCanvas; + }; + + } else if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + cc.assert(cc.isFunction(cc._tmp.WebGLTexture2D), cc._LogInfos.MissingFile, "TexturesWebGL.js"); + cc._tmp.WebGLTexture2D(); + delete cc._tmp.WebGLTexture2D; + } + + cc.EventHelper.prototype.apply(cc.Texture2D.prototype); + + cc.assert(cc.isFunction(cc._tmp.PrototypeTexture2D), cc._LogInfos.MissingFile, "TexturesPropertyDefine.js"); + cc._tmp.PrototypeTexture2D(); + delete cc._tmp.PrototypeTexture2D; +}); diff --git a/frameworks/cocos2d-html5/cocos2d/core/textures/CCTextureAtlas.js b/frameworks/cocos2d-html5/cocos2d/core/textures/CCTextureAtlas.js new file mode 100644 index 0000000..b996dc7 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/textures/CCTextureAtlas.js @@ -0,0 +1,655 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + *

A class that implements a Texture Atlas.
+ * Supported features:
+ * The atlas file can be a PNG, JPG.
+ * Quads can be updated in runtime
+ * Quads can be added in runtime
+ * Quads can be removed in runtime
+ * Quads can be re-ordered in runtime
+ * The TextureAtlas capacity can be increased or decreased in runtime.

+ * @class + * @extends cc.Class + * + * @property {Boolean} dirty - Indicates whether or not the array buffer of the VBO needs to be updated. + * @property {Image} texture - Image texture for cc.TextureAtlas. + * @property {Number} capacity - <@readonly> Quantity of quads that can be stored with the current texture atlas size. + * @property {Number} totalQuads - <@readonly> Quantity of quads that are going to be drawn. + * @property {Array} quads - <@readonly> Quads that are going to be rendered + */ +cc.TextureAtlas = cc.Class.extend(/** @lends cc.TextureAtlas# */{ //WebGL only + dirty: false, + texture: null, + + _indices: null, + //0: vertex 1: indices + _buffersVBO: null, + _capacity: 0, + + _quads: null, + _quadsArrayBuffer: null, + _quadsWebBuffer: null, + _quadsReader: null, + + /** + *

Creates a TextureAtlas with an filename and with an initial capacity for Quads.
+ * The TextureAtlas capacity can be increased in runtime.

+ * Constructor of cc.TextureAtlas + * @param {String|cc.Texture2D} fileName + * @param {Number} capacity + * @example + * 1. + * //creates a TextureAtlas with filename + * var textureAtlas = new cc.TextureAtlas("res/hello.png", 3); + * 2. + * //creates a TextureAtlas with texture + * var texture = cc.textureCache.addImage("hello.png"); + * var textureAtlas = new cc.TextureAtlas(texture, 3); + */ + ctor: function (fileName, capacity) { + this._buffersVBO = []; + + if (cc.isString(fileName)) { + this.initWithFile(fileName, capacity); + } else if (fileName instanceof cc.Texture2D) { + this.initWithTexture(fileName, capacity); + } + }, + + /** + * Quantity of quads that are going to be drawn. + * @return {Number} + */ + getTotalQuads: function () { + //return this._quads.length; + return this._totalQuads; + }, + + /** + * Quantity of quads that can be stored with the current texture atlas size + * @return {Number} + */ + getCapacity: function () { + return this._capacity; + }, + + /** + * Texture of the texture atlas + * @return {Image} + */ + getTexture: function () { + return this.texture; + }, + + /** + * @param {Image} texture + */ + setTexture: function (texture) { + this.texture = texture; + }, + + /** + * specify if the array buffer of the VBO needs to be updated + * @param {Boolean} dirty + */ + setDirty: function (dirty) { + this.dirty = dirty; + }, + + /** + * whether or not the array buffer of the VBO needs to be updated + * @returns {boolean} + */ + isDirty: function () { + return this.dirty; + }, + + /** + * Quads that are going to be rendered + * @return {Array} + */ + getQuads: function () { + return this._quads; + }, + + /** + * @param {Array} quads + */ + setQuads: function (quads) { + //TODO need re-binding + this._quads = quads; + }, + + _copyQuadsToTextureAtlas: function (quads, index) { + if (!quads) + return; + + for (var i = 0; i < quads.length; i++) + this._setQuadToArray(quads[i], index + i); + }, + + _setQuadToArray: function (quad, index) { + var locQuads = this._quads; + if (!locQuads[index]) { + locQuads[index] = new cc.V3F_C4B_T2F_Quad(quad.tl, quad.bl, quad.tr, quad.br, this._quadsArrayBuffer, index * cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT); + return; + } + locQuads[index].bl = quad.bl; + locQuads[index].br = quad.br; + locQuads[index].tl = quad.tl; + locQuads[index].tr = quad.tr; + }, + + /** + * Description + * @return {String} + */ + description: function () { + return ''; + }, + + _setupIndices: function () { + if (this._capacity === 0) + return; + var locIndices = this._indices, locCapacity = this._capacity; + for (var i = 0; i < locCapacity; i++) { + if (cc.TEXTURE_ATLAS_USE_TRIANGLE_STRIP) { + locIndices[i * 6 + 0] = i * 4 + 0; + locIndices[i * 6 + 1] = i * 4 + 0; + locIndices[i * 6 + 2] = i * 4 + 2; + locIndices[i * 6 + 3] = i * 4 + 1; + locIndices[i * 6 + 4] = i * 4 + 3; + locIndices[i * 6 + 5] = i * 4 + 3; + } else { + locIndices[i * 6 + 0] = i * 4 + 0; + locIndices[i * 6 + 1] = i * 4 + 1; + locIndices[i * 6 + 2] = i * 4 + 2; + + // inverted index. issue #179 + locIndices[i * 6 + 3] = i * 4 + 3; + locIndices[i * 6 + 4] = i * 4 + 2; + locIndices[i * 6 + 5] = i * 4 + 1; + } + } + }, + + _setupVBO: function () { + var gl = cc._renderContext; + //create WebGLBuffer + this._buffersVBO[0] = gl.createBuffer(); + this._buffersVBO[1] = gl.createBuffer(); + + this._quadsWebBuffer = gl.createBuffer(); + this._mapBuffers(); + }, + + _mapBuffers: function () { + var gl = cc._renderContext; + + gl.bindBuffer(gl.ARRAY_BUFFER, this._quadsWebBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this._quadsArrayBuffer, gl.DYNAMIC_DRAW); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._buffersVBO[1]); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this._indices, gl.STATIC_DRAW); + }, + + /** + *

Initializes a TextureAtlas with a filename and with a certain capacity for Quads.
+ * The TextureAtlas capacity can be increased in runtime.
+ * WARNING: Do not reinitialize the TextureAtlas because it will leak memory.

+ * @param {String} file + * @param {Number} capacity + * @return {Boolean} + * @example + * //example + * var textureAtlas = new cc.TextureAtlas(); + * textureAtlas.initWithTexture("hello.png", 3); + */ + initWithFile: function (file, capacity) { + // retained in property + var texture = cc.textureCache.addImage(file); + if (texture) + return this.initWithTexture(texture, capacity); + else { + cc.log(cc._LogInfos.TextureAtlas_initWithFile, file); + return false; + } + }, + + /** + *

Initializes a TextureAtlas with a previously initialized Texture2D object, and
+ * with an initial capacity for Quads.
+ * The TextureAtlas capacity can be increased in runtime.
+ * WARNING: Do not reinitialize the TextureAtlas because it will leak memory

+ * @param {Image} texture + * @param {Number} capacity + * @return {Boolean} + * @example + * //example + * var texture = cc.textureCache.addImage("hello.png"); + * var textureAtlas = new cc.TextureAtlas(); + * textureAtlas.initWithTexture(texture, 3); + */ + initWithTexture: function (texture, capacity) { + cc.assert(texture, cc._LogInfos.TextureAtlas_initWithTexture); + + capacity = 0 | (capacity); + this._capacity = capacity; + this._totalQuads = 0; + + // retained in property + this.texture = texture; + + // Re-initialization is not allowed + this._quads = []; + this._indices = new Uint16Array(capacity * 6); + var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT; + this._quadsArrayBuffer = new ArrayBuffer(quadSize * capacity); + this._quadsReader = new Uint8Array(this._quadsArrayBuffer); + + if (!( this._quads && this._indices) && capacity > 0) + return false; + + var locQuads = this._quads; + for (var i = 0; i < capacity; i++) + locQuads[i] = new cc.V3F_C4B_T2F_Quad(null, null, null, null, this._quadsArrayBuffer, i * quadSize); + + this._setupIndices(); + this._setupVBO(); + this.dirty = true; + return true; + }, + + /** + *

Updates a Quad (texture, vertex and color) at a certain index
+ * index must be between 0 and the atlas capacity - 1

+ * @param {cc.V3F_C4B_T2F_Quad} quad + * @param {Number} index + */ + updateQuad: function (quad, index) { + cc.assert(quad, cc._LogInfos.TextureAtlas_updateQuad); + cc.assert(index >= 0 && index < this._capacity, cc._LogInfos.TextureAtlas_updateQuad_2); + + this._totalQuads = Math.max(index + 1, this._totalQuads); + this._setQuadToArray(quad, index); + this.dirty = true; + }, + + /** + *

Inserts a Quad (texture, vertex and color) at a certain index
+ * index must be between 0 and the atlas capacity - 1

+ * @param {cc.V3F_C4B_T2F_Quad} quad + * @param {Number} index + */ + insertQuad: function (quad, index) { + cc.assert(index < this._capacity, cc._LogInfos.TextureAtlas_insertQuad_2); + + this._totalQuads++; + if (this._totalQuads > this._capacity) { + cc.log(cc._LogInfos.TextureAtlas_insertQuad); + return; + } + var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT; + // issue #575. index can be > totalQuads + var remaining = (this._totalQuads - 1) - index; + var startOffset = index * quadSize; + var moveLength = remaining * quadSize; + this._quads[this._totalQuads - 1] = new cc.V3F_C4B_T2F_Quad(null, null, null, null, this._quadsArrayBuffer, (this._totalQuads - 1) * quadSize); + this._quadsReader.set(this._quadsReader.subarray(startOffset, startOffset + moveLength), startOffset + quadSize); + + this._setQuadToArray(quad, index); + this.dirty = true; + }, + + /** + *

+ * Inserts a c array of quads at a given index
+ * index must be between 0 and the atlas capacity - 1
+ * this method doesn't enlarge the array when amount + index > totalQuads
+ *

+ * @param {Array} quads + * @param {Number} index + * @param {Number} amount + */ + insertQuads: function (quads, index, amount) { + amount = amount || quads.length; + + cc.assert((index + amount) <= this._capacity, cc._LogInfos.TextureAtlas_insertQuads); + + var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT; + this._totalQuads += amount; + if (this._totalQuads > this._capacity) { + cc.log(cc._LogInfos.TextureAtlas_insertQuad); + return; + } + + // issue #575. index can be > totalQuads + var remaining = (this._totalQuads - 1) - index - amount; + var startOffset = index * quadSize; + var moveLength = remaining * quadSize; + var lastIndex = (this._totalQuads - 1) - amount; + + var i; + for (i = 0; i < amount; i++) + this._quads[lastIndex + i] = new cc.V3F_C4B_T2F_Quad(null, null, null, null, this._quadsArrayBuffer, (this._totalQuads - 1) * quadSize); + this._quadsReader.set(this._quadsReader.subarray(startOffset, startOffset + moveLength), startOffset + quadSize * amount); + for (i = 0; i < amount; i++) + this._setQuadToArray(quads[i], index + i); + + this.dirty = true; + }, + + /** + *

Removes the quad that is located at a certain index and inserts it at a new index
+ * This operation is faster than removing and inserting in a quad in 2 different steps

+ * @param {Number} fromIndex + * @param {Number} newIndex + */ + insertQuadFromIndex: function (fromIndex, newIndex) { + if (fromIndex === newIndex) + return; + + cc.assert(newIndex >= 0 || newIndex < this._totalQuads, cc._LogInfos.TextureAtlas_insertQuadFromIndex); + + cc.assert(fromIndex >= 0 || fromIndex < this._totalQuads, cc._LogInfos.TextureAtlas_insertQuadFromIndex_2); + + var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT; + var locQuadsReader = this._quadsReader; + var sourceArr = locQuadsReader.subarray(fromIndex * quadSize, quadSize); + var startOffset, moveLength; + if (fromIndex > newIndex) { + startOffset = newIndex * quadSize; + moveLength = (fromIndex - newIndex) * quadSize; + locQuadsReader.set(locQuadsReader.subarray(startOffset, startOffset + moveLength), startOffset + quadSize); + locQuadsReader.set(sourceArr, startOffset); + } else { + startOffset = (fromIndex + 1) * quadSize; + moveLength = (newIndex - fromIndex) * quadSize; + locQuadsReader.set(locQuadsReader.subarray(startOffset, startOffset + moveLength), startOffset - quadSize); + locQuadsReader.set(sourceArr, newIndex * quadSize); + } + this.dirty = true; + }, + + /** + *

Removes a quad at a given index number.
+ * The capacity remains the same, but the total number of quads to be drawn is reduced in 1

+ * @param {Number} index + */ + removeQuadAtIndex: function (index) { + cc.assert(index < this._totalQuads, cc._LogInfos.TextureAtlas_removeQuadAtIndex); + + var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT; + this._totalQuads--; + this._quads.length = this._totalQuads; + if (index !== this._totalQuads) { + //move data + var startOffset = (index + 1) * quadSize; + var moveLength = (this._totalQuads - index) * quadSize; + this._quadsReader.set(this._quadsReader.subarray(startOffset, startOffset + moveLength), startOffset - quadSize); + } + this.dirty = true; + }, + + /** + * Removes a given number of quads at a given index + * @param {Number} index + * @param {Number} amount + */ + removeQuadsAtIndex: function (index, amount) { + cc.assert(index + amount <= this._totalQuads, cc._LogInfos.TextureAtlas_removeQuadsAtIndex); + + this._totalQuads -= amount; + + if (index !== this._totalQuads) { + //move data + var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT; + var srcOffset = (index + amount) * quadSize; + var moveLength = (this._totalQuads - index) * quadSize; + var dstOffset = index * quadSize; + this._quadsReader.set(this._quadsReader.subarray(srcOffset, srcOffset + moveLength), dstOffset); + } + this.dirty = true; + }, + + /** + *

Removes all Quads.
+ * The TextureAtlas capacity remains untouched. No memory is freed.
+ * The total number of quads to be drawn will be 0

+ */ + removeAllQuads: function () { + this._quads.length = 0; + this._totalQuads = 0; + }, + + _setDirty: function (dirty) { + this.dirty = dirty; + }, + + /** + *

Resize the capacity of the CCTextureAtlas.
+ * The new capacity can be lower or higher than the current one
+ * It returns YES if the resize was successful.
+ * If it fails to resize the capacity it will return NO with a new capacity of 0.
+ * no used for js

+ * @param {Number} newCapacity + * @return {Boolean} + */ + resizeCapacity: function (newCapacity) { + if (newCapacity === this._capacity) + return true; + + var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT; + var oldCapacity = this._capacity; + // update capacity and totolQuads + this._totalQuads = Math.min(this._totalQuads, newCapacity); + this._capacity = 0 | newCapacity; + var i, capacity = this._capacity, locTotalQuads = this._totalQuads; + + if (this._quads === null) { + this._quads = []; + this._quadsArrayBuffer = new ArrayBuffer(quadSize * capacity); + this._quadsReader = new Uint8Array(this._quadsArrayBuffer); + for (i = 0; i < capacity; i++) + this._quads = new cc.V3F_C4B_T2F_Quad(null, null, null, null, this._quadsArrayBuffer, i * quadSize); + } else { + var newQuads, newArrayBuffer, quads = this._quads; + if (capacity > oldCapacity) { + newQuads = []; + newArrayBuffer = new ArrayBuffer(quadSize * capacity); + for (i = 0; i < locTotalQuads; i++) { + newQuads[i] = new cc.V3F_C4B_T2F_Quad(quads[i].tl, quads[i].bl, quads[i].tr, quads[i].br, + newArrayBuffer, i * quadSize); + } + for (; i < capacity; i++) + newQuads[i] = new cc.V3F_C4B_T2F_Quad(null, null, null, null, newArrayBuffer, i * quadSize); + + this._quadsReader = new Uint8Array(newArrayBuffer); + this._quads = newQuads; + this._quadsArrayBuffer = newArrayBuffer; + } else { + var count = Math.max(locTotalQuads, capacity); + newQuads = []; + newArrayBuffer = new ArrayBuffer(quadSize * capacity); + for (i = 0; i < count; i++) { + newQuads[i] = new cc.V3F_C4B_T2F_Quad(quads[i].tl, quads[i].bl, quads[i].tr, quads[i].br, + newArrayBuffer, i * quadSize); + } + this._quadsReader = new Uint8Array(newArrayBuffer); + this._quads = newQuads; + this._quadsArrayBuffer = newArrayBuffer; + } + } + + if (this._indices === null) { + this._indices = new Uint16Array(capacity * 6); + } else { + if (capacity > oldCapacity) { + var tempIndices = new Uint16Array(capacity * 6); + tempIndices.set(this._indices, 0); + this._indices = tempIndices; + } else { + this._indices = this._indices.subarray(0, capacity * 6); + } + } + + this._setupIndices(); + this._mapBuffers(); + this.dirty = true; + return true; + }, + + /** + * Used internally by CCParticleBatchNode
+ * don't use this unless you know what you're doing + * @param {Number} amount + */ + increaseTotalQuadsWith: function (amount) { + this._totalQuads += amount; + }, + + /** + * Moves an amount of quads from oldIndex at newIndex + * @param {Number} oldIndex + * @param {Number} amount + * @param {Number} newIndex + */ + moveQuadsFromIndex: function (oldIndex, amount, newIndex) { + if (newIndex === undefined) { + newIndex = amount; + amount = this._totalQuads - oldIndex; + + cc.assert((newIndex + (this._totalQuads - oldIndex)) <= this._capacity, cc._LogInfos.TextureAtlas_moveQuadsFromIndex); + + if (amount === 0) + return; + } else { + cc.assert((newIndex + amount) <= this._totalQuads, cc._LogInfos.TextureAtlas_moveQuadsFromIndex_2); + cc.assert(oldIndex < this._totalQuads, cc._LogInfos.TextureAtlas_moveQuadsFromIndex_3); + + if (oldIndex === newIndex) + return; + } + + var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT; + var srcOffset = oldIndex * quadSize; + var srcLength = amount * quadSize; + var locQuadsReader = this._quadsReader; + var sourceArr = locQuadsReader.subarray(srcOffset, srcOffset + srcLength); + var dstOffset = newIndex * quadSize; + var moveLength, moveStart; + if (newIndex < oldIndex) { + moveLength = (oldIndex - newIndex) * quadSize; + moveStart = newIndex * quadSize; + locQuadsReader.set(locQuadsReader.subarray(moveStart, moveStart + moveLength), moveStart + srcLength) + } else { + moveLength = (newIndex - oldIndex) * quadSize; + moveStart = (oldIndex + amount) * quadSize; + locQuadsReader.set(locQuadsReader.subarray(moveStart, moveStart + moveLength), srcOffset); + } + locQuadsReader.set(sourceArr, dstOffset); + this.dirty = true; + }, + + /** + * Ensures that after a realloc quads are still empty
+ * Used internally by CCParticleBatchNode + * @param {Number} index + * @param {Number} amount + */ + fillWithEmptyQuadsFromIndex: function (index, amount) { + var count = amount * cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT; + var clearReader = new Uint8Array(this._quadsArrayBuffer, index * cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT, count); + for (var i = 0; i < count; i++) + clearReader[i] = 0; + }, + + // TextureAtlas - Drawing + + /** + * Draws all the Atlas's Quads + */ + drawQuads: function () { + this.drawNumberOfQuads(this._totalQuads, 0); + }, + + _releaseBuffer: function () { + var gl = cc._renderContext; + if (this._buffersVBO) { + if (this._buffersVBO[0]) + gl.deleteBuffer(this._buffersVBO[0]); + if (this._buffersVBO[1]) + gl.deleteBuffer(this._buffersVBO[1]) + } + if (this._quadsWebBuffer) + gl.deleteBuffer(this._quadsWebBuffer); + } +}); + +var _p = cc.TextureAtlas.prototype; + +// Extended properties +/** @expose */ +_p.totalQuads; +cc.defineGetterSetter(_p, "totalQuads", _p.getTotalQuads); +/** @expose */ +_p.capacity; +cc.defineGetterSetter(_p, "capacity", _p.getCapacity); +/** @expose */ +_p.quads; +cc.defineGetterSetter(_p, "quads", _p.getQuads, _p.setQuads); + +/** + *

Creates a TextureAtlas with an filename and with an initial capacity for Quads.
+ * The TextureAtlas capacity can be increased in runtime.

+ * @deprecated since v3.0, please use new cc.TextureAtlas(fileName, capacity) instead + * @param {String|cc.Texture2D} fileName + * @param {Number} capacity + * @return {cc.TextureAtlas|Null} + */ +cc.TextureAtlas.create = function (fileName, capacity) { + return new cc.TextureAtlas(fileName, capacity); +}; + +/** + * @deprecated since v3.0, please use new cc.TextureAtlas(texture) instead + * @function + */ +cc.TextureAtlas.createWithTexture = cc.TextureAtlas.create; + +cc.game.addEventListener(cc.game.EVENT_RENDERER_INITED, function () { + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + cc.assert(cc.isFunction(cc._tmp.WebGLTextureAtlas), cc._LogInfos.MissingFile, "TexturesWebGL.js"); + cc._tmp.WebGLTextureAtlas(); + delete cc._tmp.WebGLTextureAtlas; + } +}); + +cc.assert(cc.isFunction(cc._tmp.PrototypeTextureAtlas), cc._LogInfos.MissingFile, "TexturesPropertyDefine.js"); +cc._tmp.PrototypeTextureAtlas(); +delete cc._tmp.PrototypeTextureAtlas; diff --git a/frameworks/cocos2d-html5/cocos2d/core/textures/CCTextureCache.js b/frameworks/cocos2d-html5/cocos2d/core/textures/CCTextureCache.js new file mode 100644 index 0000000..5e59c2f --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/textures/CCTextureCache.js @@ -0,0 +1,385 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * cc.textureCache is a singleton object, it's the global cache for cc.Texture2D + * @class + * @name cc.textureCache + */ +cc.textureCache = /** @lends cc.textureCache# */{ + _textures: {}, + _textureColorsCache: {}, + _textureKeySeq: (0 | Math.random() * 1000), + + _loadedTexturesBefore: {}, + + //handleLoadedTexture move to Canvas/WebGL + + _initializingRenderer: function () { + var selPath; + //init texture from _loadedTexturesBefore + var locLoadedTexturesBefore = this._loadedTexturesBefore, locTextures = this._textures; + for (selPath in locLoadedTexturesBefore) { + var tex2d = locLoadedTexturesBefore[selPath]; + tex2d.handleLoadedTexture(); + locTextures[selPath] = tex2d; + } + this._loadedTexturesBefore = {}; + }, + + /** + *

+ * Returns a Texture2D object given an PVR filename
+ * If the file image was not previously loaded, it will create a new CCTexture2D
+ * object and it will return it. Otherwise it will return a reference of a previously loaded image
+ * note: AddPVRTCImage does not support on HTML5 + *

+ * @param {String} filename + * @return {cc.Texture2D} + */ + addPVRTCImage: function (filename) { + cc.log(cc._LogInfos.textureCache_addPVRTCImage); + }, + + /** + *

+ * Returns a Texture2D object given an ETC filename
+ * If the file image was not previously loaded, it will create a new CCTexture2D
+ * object and it will return it. Otherwise it will return a reference of a previously loaded image
+ * note:addETCImage does not support on HTML5 + *

+ * @param {String} filename + * @return {cc.Texture2D} + */ + addETCImage: function (filename) { + cc.log(cc._LogInfos.textureCache_addETCImage); + }, + + /** + * Description + * @return {String} + */ + description: function () { + return ""; + }, + + /** + * Returns an already created texture. Returns null if the texture doesn't exist. + * @param {String} textureKeyName + * @return {cc.Texture2D|Null} + * @deprecated + * @example + * //example + * var key = cc.textureCache.textureForKey("hello.png"); + */ + textureForKey: function (textureKeyName) { + cc.log(cc._LogInfos.textureCache_textureForKey); + return this.getTextureForKey(textureKeyName); + }, + + /** + * Returns an already created texture. Returns null if the texture doesn't exist. + * @param {String} textureKeyName + * @return {cc.Texture2D|Null} + * @example + * //example + * var key = cc.textureCache.getTextureForKey("hello.png"); + */ + getTextureForKey: function (textureKeyName) { + return this._textures[textureKeyName] || this._textures[cc.loader._getAliase(textureKeyName)]; + }, + + /** + * @param {Image} texture + * @return {String|Null} + * @example + * //example + * var key = cc.textureCache.getKeyByTexture(texture); + */ + getKeyByTexture: function (texture) { + for (var key in this._textures) { + if (this._textures[key] === texture) { + return key; + } + } + return null; + }, + + _generalTextureKey: function (id) { + return "_textureKey_" + id; + }, + + /** + * @param {Image} texture + * @return {Array} + * @example + * //example + * var cacheTextureForColor = cc.textureCache.getTextureColors(texture); + */ + getTextureColors: function (texture) { + var image = texture._htmlElementObj; + var key = this.getKeyByTexture(image); + if (!key) { + if (image instanceof HTMLImageElement) + key = image.src; + else + key = this._generalTextureKey(texture.__instanceId); + } + + if (!this._textureColorsCache[key]) + this._textureColorsCache[key] = texture._generateTextureCacheForColor(); + return this._textureColorsCache[key]; + }, + + /** + *

Returns a Texture2D object given an PVR filename
+ * If the file image was not previously loaded, it will create a new Texture2D
+ * object and it will return it. Otherwise it will return a reference of a previously loaded image

+ * @param {String} path + * @return {cc.Texture2D} + */ + addPVRImage: function (path) { + cc.log(cc._LogInfos.textureCache_addPVRImage); + }, + + /** + *

Purges the dictionary of loaded textures.
+ * Call this method if you receive the "Memory Warning"
+ * In the short term: it will free some resources preventing your app from being killed
+ * In the medium term: it will allocate more resources
+ * In the long term: it will be the same

+ * @example + * //example + * cc.textureCache.removeAllTextures(); + */ + removeAllTextures: function () { + var locTextures = this._textures; + for (var selKey in locTextures) { + if (locTextures[selKey]) + locTextures[selKey].releaseTexture(); + } + this._textures = {}; + }, + + /** + * Deletes a texture from the cache given a texture + * @param {Image} texture + * @example + * //example + * cc.textureCache.removeTexture(texture); + */ + removeTexture: function (texture) { + if (!texture) + return; + + var locTextures = this._textures; + for (var selKey in locTextures) { + if (locTextures[selKey] === texture) { + locTextures[selKey].releaseTexture(); + delete(locTextures[selKey]); + } + } + }, + + /** + * Deletes a texture from the cache given a its key name + * @param {String} textureKeyName + * @example + * //example + * cc.textureCache.removeTexture("hello.png"); + */ + removeTextureForKey: function (textureKeyName) { + if (textureKeyName == null) + return; + var tex = this._textures[textureKeyName]; + if (tex) { + tex.releaseTexture(); + delete(this._textures[textureKeyName]); + } + }, + + //addImage move to Canvas/WebGL + + /** + * Cache the image data + * @param {String} path + * @param {Image|HTMLImageElement|HTMLCanvasElement} texture + */ + cacheImage: function (path, texture) { + if (texture instanceof cc.Texture2D) { + this._textures[path] = texture; + return; + } + var texture2d = new cc.Texture2D(); + texture2d.initWithElement(texture); + texture2d.handleLoadedTexture(); + this._textures[path] = texture2d; + }, + + /** + *

Returns a Texture2D object given an UIImage image
+ * If the image was not previously loaded, it will create a new Texture2D object and it will return it.
+ * Otherwise it will return a reference of a previously loaded image
+ * The "key" parameter will be used as the "key" for the cache.
+ * If "key" is null, then a new texture will be created each time.

+ * @param {HTMLImageElement|HTMLCanvasElement} image + * @param {String} key + * @return {cc.Texture2D} + */ + addUIImage: function (image, key) { + cc.assert(image, cc._LogInfos.textureCache_addUIImage_2); + + if (key) { + if (this._textures[key]) + return this._textures[key]; + } + + // prevents overloading the autorelease pool + var texture = new cc.Texture2D(); + texture.initWithImage(image); + if (key != null) + this._textures[key] = texture; + else + cc.log(cc._LogInfos.textureCache_addUIImage); + return texture; + }, + + /** + *

Output to cc.log the current contents of this TextureCache
+ * This will attempt to calculate the size of each texture, and the total texture memory in use.

+ */ + dumpCachedTextureInfo: function () { + var count = 0; + var totalBytes = 0, locTextures = this._textures; + + for (var key in locTextures) { + var selTexture = locTextures[key]; + count++; + if (selTexture.getHtmlElementObj() instanceof HTMLImageElement) + cc.log(cc._LogInfos.textureCache_dumpCachedTextureInfo, key, selTexture.getHtmlElementObj().src, selTexture.getPixelsWide(), selTexture.getPixelsHigh()); + else { + cc.log(cc._LogInfos.textureCache_dumpCachedTextureInfo_2, key, selTexture.getPixelsWide(), selTexture.getPixelsHigh()); + } + totalBytes += selTexture.getPixelsWide() * selTexture.getPixelsHigh() * 4; + } + + var locTextureColorsCache = this._textureColorsCache; + for (key in locTextureColorsCache) { + var selCanvasColorsArr = locTextureColorsCache[key]; + for (var selCanvasKey in selCanvasColorsArr) { + var selCanvas = selCanvasColorsArr[selCanvasKey]; + count++; + cc.log(cc._LogInfos.textureCache_dumpCachedTextureInfo_2, key, selCanvas.width, selCanvas.height); + totalBytes += selCanvas.width * selCanvas.height * 4; + } + + } + cc.log(cc._LogInfos.textureCache_dumpCachedTextureInfo_3, count, totalBytes / 1024, (totalBytes / (1024.0 * 1024.0)).toFixed(2)); + }, + + _clear: function () { + this._textures = {}; + this._textureColorsCache = {}; + this._textureKeySeq = (0 | Math.random() * 1000); + this._loadedTexturesBefore = {}; + } +}; + +cc.game.addEventListener(cc.game.EVENT_RENDERER_INITED, function () { + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) { + + var _p = cc.textureCache; + + _p.handleLoadedTexture = function (url, img) { + var locTexs = this._textures; + //remove judge + var tex = locTexs[url]; + if (!tex) { + tex = locTexs[url] = new cc.Texture2D(); + tex.url = url; + } + tex.initWithElement(img); + tex.handleLoadedTexture(); + return tex; + }; + + /** + *

Returns a Texture2D object given an file image
+ * If the file image was not previously loaded, it will create a new Texture2D
+ * object and it will return it. It will use the filename as a key.
+ * Otherwise it will return a reference of a previously loaded image.
+ * Supported image extensions: .png, .jpg, .gif

+ * @param {String} url + * @param {Function} cb + * @param {Object} target + * @return {cc.Texture2D} + * @example + * //example + * cc.textureCache.addImage("hello.png"); + */ + _p.addImage = function (url, cb, target) { + + cc.assert(url, cc._LogInfos.Texture2D_addImage); + + var locTexs = this._textures; + //remove judge + var tex = locTexs[url] || locTexs[cc.loader._getAliase(url)]; + if (tex) { + if (tex.isLoaded()) { + cb && cb.call(target, tex); + return tex; + } + else { + tex.addEventListener("load", function () { + cb && cb.call(target, tex); + }, target); + return tex; + } + } + + tex = locTexs[url] = new cc.Texture2D(); + tex.url = url; + var basePath = cc.loader.getBasePath ? cc.loader.getBasePath() : cc.loader.resPath; + cc.loader.loadImg(cc.path.join(basePath || "", url), function (err, img) { + if (err) + return cb && cb.call(target, err); + + var texResult = cc.textureCache.handleLoadedTexture(url, img); + cb && cb.call(target, texResult); + }); + + return tex; + }; + + _p.addImageAsync = _p.addImage; + _p = null; + + } else if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + cc.assert(cc.isFunction(cc._tmp.WebGLTextureCache), cc._LogInfos.MissingFile, "TexturesWebGL.js"); + cc._tmp.WebGLTextureCache(); + delete cc._tmp.WebGLTextureCache; + } +}); diff --git a/frameworks/cocos2d-html5/cocos2d/core/textures/TexturesPropertyDefine.js b/frameworks/cocos2d-html5/cocos2d/core/textures/TexturesPropertyDefine.js new file mode 100644 index 0000000..518a5f5 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/textures/TexturesPropertyDefine.js @@ -0,0 +1,228 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +cc._tmp.PrototypeTexture2D = function () { + + var _c = cc.Texture2D; + + /** + *

+ * treats (or not) PVR files as if they have alpha premultiplied.
+ * Since it is impossible to know at runtime if the PVR images have the alpha channel premultiplied, it is
+ * possible load them as if they have (or not) the alpha channel premultiplied.
+ *
+ * By default it is disabled.
+ *

+ * @param haveAlphaPremultiplied + */ + _c.PVRImagesHavePremultipliedAlpha = function (haveAlphaPremultiplied) { + cc.PVRHaveAlphaPremultiplied_ = haveAlphaPremultiplied; + }; + + /** + * 32-bit texture: RGBA8888 + * @memberOf cc.Texture2D + * @name PIXEL_FORMAT_RGBA8888 + * @static + * @constant + * @type {Number} + */ + _c.PIXEL_FORMAT_RGBA8888 = 2; + + /** + * 24-bit texture: RGBA888 + * @memberOf cc.Texture2D + * @name PIXEL_FORMAT_RGB888 + * @static + * @constant + * @type {Number} + */ + _c.PIXEL_FORMAT_RGB888 = 3; + + /** + * 16-bit texture without Alpha channel + * @memberOf cc.Texture2D + * @name PIXEL_FORMAT_RGB565 + * @static + * @constant + * @type {Number} + */ + _c.PIXEL_FORMAT_RGB565 = 4; + + /** + * 8-bit textures used as masks + * @memberOf cc.Texture2D + * @name PIXEL_FORMAT_A8 + * @static + * @constant + * @type {Number} + */ + _c.PIXEL_FORMAT_A8 = 5; + + /** + * 8-bit intensity texture + * @memberOf cc.Texture2D + * @name PIXEL_FORMAT_I8 + * @static + * @constant + * @type {Number} + */ + _c.PIXEL_FORMAT_I8 = 6; + + /** + * 16-bit textures used as masks + * @memberOf cc.Texture2D + * @name PIXEL_FORMAT_AI88 + * @static + * @constant + * @type {Number} + */ + _c.PIXEL_FORMAT_AI88 = 7; + + /** + * 16-bit textures: RGBA4444 + * @memberOf cc.Texture2D + * @name PIXEL_FORMAT_RGBA4444 + * @static + * @constant + * @type {Number} + */ + _c.PIXEL_FORMAT_RGBA4444 = 8; + + /** + * 16-bit textures: RGB5A1 + * @memberOf cc.Texture2D + * @name PIXEL_FORMAT_RGB5A1 + * @static + * @constant + * @type {Number} + */ + _c.PIXEL_FORMAT_RGB5A1 = 7; + + /** + * 4-bit PVRTC-compressed texture: PVRTC4 + * @memberOf cc.Texture2D + * @name PIXEL_FORMAT_PVRTC4 + * @static + * @constant + * @type {Number} + */ + _c.PIXEL_FORMAT_PVRTC4 = 9; + + /** + * 2-bit PVRTC-compressed texture: PVRTC2 + * @memberOf cc.Texture2D + * @name PIXEL_FORMAT_PVRTC2 + * @static + * @constant + * @type {Number} + */ + _c.PIXEL_FORMAT_PVRTC2 = 10; + + /** + * Default texture format: RGBA8888 + * @memberOf cc.Texture2D + * @name PIXEL_FORMAT_DEFAULT + * @static + * @constant + * @type {Number} + */ + _c.PIXEL_FORMAT_DEFAULT = _c.PIXEL_FORMAT_RGBA8888; + + /** + * The default pixel format + * @memberOf cc.Texture2D + * @name PIXEL_FORMAT_PVRTC2 + * @static + * @type {Number} + */ + _c.defaultPixelFormat = _c.PIXEL_FORMAT_DEFAULT; + + var _M = cc.Texture2D._M = {}; + _M[_c.PIXEL_FORMAT_RGBA8888] = "RGBA8888"; + _M[_c.PIXEL_FORMAT_RGB888] = "RGB888"; + _M[_c.PIXEL_FORMAT_RGB565] = "RGB565"; + _M[_c.PIXEL_FORMAT_A8] = "A8"; + _M[_c.PIXEL_FORMAT_I8] = "I8"; + _M[_c.PIXEL_FORMAT_AI88] = "AI88"; + _M[_c.PIXEL_FORMAT_RGBA4444] = "RGBA4444"; + _M[_c.PIXEL_FORMAT_RGB5A1] = "RGB5A1"; + _M[_c.PIXEL_FORMAT_PVRTC4] = "PVRTC4"; + _M[_c.PIXEL_FORMAT_PVRTC2] = "PVRTC2"; + + var _B = cc.Texture2D._B = {}; + _B[_c.PIXEL_FORMAT_RGBA8888] = 32; + _B[_c.PIXEL_FORMAT_RGB888] = 24; + _B[_c.PIXEL_FORMAT_RGB565] = 16; + _B[_c.PIXEL_FORMAT_A8] = 8; + _B[_c.PIXEL_FORMAT_I8] = 8; + _B[_c.PIXEL_FORMAT_AI88] = 16; + _B[_c.PIXEL_FORMAT_RGBA4444] = 16; + _B[_c.PIXEL_FORMAT_RGB5A1] = 16; + _B[_c.PIXEL_FORMAT_PVRTC4] = 4; + _B[_c.PIXEL_FORMAT_PVRTC2] = 3; + + var _p = cc.Texture2D.prototype; + + // Extended properties + /** @expose */ + _p.name; + cc.defineGetterSetter(_p, "name", _p.getName); + /** @expose */ + _p.pixelFormat; + cc.defineGetterSetter(_p, "pixelFormat", _p.getPixelFormat); + /** @expose */ + _p.pixelsWidth; + cc.defineGetterSetter(_p, "pixelsWidth", _p.getPixelsWide); + /** @expose */ + _p.pixelsHeight; + cc.defineGetterSetter(_p, "pixelsHeight", _p.getPixelsHigh); + //cc.defineGetterSetter(_p, "size", _p.getContentSize, _p.setContentSize); + /** @expose */ + _p.width; + cc.defineGetterSetter(_p, "width", _p._getWidth); + /** @expose */ + _p.height; + cc.defineGetterSetter(_p, "height", _p._getHeight); +}; + +cc._tmp.PrototypeTextureAtlas = function () { + + var _p = cc.TextureAtlas.prototype; + + // Extended properties + /** @expose */ + _p.totalQuads; + cc.defineGetterSetter(_p, "totalQuads", _p.getTotalQuads); + /** @expose */ + _p.capacity; + cc.defineGetterSetter(_p, "capacity", _p.getCapacity); + /** @expose */ + _p.quads; + cc.defineGetterSetter(_p, "quads", _p.getQuads, _p.setQuads); + +}; + diff --git a/frameworks/cocos2d-html5/cocos2d/core/textures/TexturesWebGL.js b/frameworks/cocos2d-html5/cocos2d/core/textures/TexturesWebGL.js new file mode 100644 index 0000000..2dfe145 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/textures/TexturesWebGL.js @@ -0,0 +1,932 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +cc._tmp.WebGLTexture2D = function () { + + /** + *

+ * This class allows to easily create OpenGL or Canvas 2D textures from images, text or raw data.
+ * The created cc.Texture2D object will always have power-of-two dimensions.
+ * Depending on how you create the cc.Texture2D object, the actual image area of the texture might be smaller than the texture dimensions
+ * i.e. "contentSize" != (pixelsWide, pixelsHigh) and (maxS, maxT) != (1.0, 1.0).
+ * Be aware that the content of the generated textures will be upside-down!

+ * @name cc.Texture2D + * @class + * @extends cc.Class + * + * @property {WebGLTexture} name - <@readonly> WebGLTexture Object + * @property {Number} pixelFormat - <@readonly> Pixel format of the texture + * @property {Number} pixelsWidth - <@readonly> Width in pixels + * @property {Number} pixelsHeight - <@readonly> Height in pixels + * @property {Number} width - Content width in points + * @property {Number} height - Content height in points + * @property {cc.GLProgram} shaderProgram - The shader program used by drawAtPoint and drawInRect + * @property {Number} maxS - Texture max S + * @property {Number} maxT - Texture max T + */ + //Original : Texture2DWebGL + cc.Texture2D = cc.Class.extend(/** @lends cc.Texture2D# */{ + // By default PVR images are treated as if they don't have the alpha channel premultiplied + _pVRHaveAlphaPremultiplied: true, + _pixelFormat: null, + _pixelsWide: 0, + _pixelsHigh: 0, + _name: "", + _contentSize: null, + maxS: 0, + maxT: 0, + _hasPremultipliedAlpha: false, + _hasMipmaps: false, + + shaderProgram: null, + + _textureLoaded: false, + _htmlElementObj: null, + _webTextureObj: null, + + url: null, + + /** + * constructor of cc.Texture2D + */ + ctor: function () { + this._contentSize = cc.size(0, 0); + this._pixelFormat = cc.Texture2D.defaultPixelFormat; + }, + + /** + * release texture + */ + releaseTexture: function () { + if (this._webTextureObj) + cc._renderContext.deleteTexture(this._webTextureObj); + this._htmlElementObj = null; + cc.loader.release(this.url); + }, + + /** + * pixel format of the texture + * @return {Number} + */ + getPixelFormat: function () { + return this._pixelFormat; + }, + + /** + * width in pixels + * @return {Number} + */ + getPixelsWide: function () { + return this._pixelsWide; + }, + + /** + * height in pixels + * @return {Number} + */ + getPixelsHigh: function () { + return this._pixelsHigh; + }, + + /** + * get WebGLTexture Object + * @return {WebGLTexture} + */ + getName: function () { + return this._webTextureObj; + }, + + /** + * content size + * @return {cc.Size} + */ + getContentSize: function () { + return cc.size(this._contentSize.width / cc.contentScaleFactor(), this._contentSize.height / cc.contentScaleFactor()); + }, + + _getWidth: function () { + return this._contentSize.width / cc.contentScaleFactor(); + }, + _getHeight: function () { + return this._contentSize.height / cc.contentScaleFactor(); + }, + + /** + * get content size in pixels + * @return {cc.Size} + */ + getContentSizeInPixels: function () { + return this._contentSize; + }, + + /** + * texture max S + * @return {Number} + */ + getMaxS: function () { + return this.maxS; + }, + + /** + * set texture max S + * @param {Number} maxS + */ + setMaxS: function (maxS) { + this.maxS = maxS; + }, + + /** + * get texture max T + * @return {Number} + */ + getMaxT: function () { + return this.maxT; + }, + + /** + * set texture max T + * @param {Number} maxT + */ + setMaxT: function (maxT) { + this.maxT = maxT; + }, + + /** + * return shader program used by drawAtPoint and drawInRect + * @return {cc.GLProgram} + */ + getShaderProgram: function () { + return this.shaderProgram; + }, + + /** + * set shader program used by drawAtPoint and drawInRect + * @param {cc.GLProgram} shaderProgram + */ + setShaderProgram: function (shaderProgram) { + this.shaderProgram = shaderProgram; + }, + + /** + * whether or not the texture has their Alpha premultiplied + * @return {Boolean} + */ + hasPremultipliedAlpha: function () { + return this._hasPremultipliedAlpha; + }, + + /** + * whether or not use mipmap + * @return {Boolean} + */ + hasMipmaps: function () { + return this._hasMipmaps; + }, + + /** + * description + * @return {string} + */ + description: function () { + var _t = this; + return ""; + }, + + /** + * These functions are needed to create mutable textures + * @param {Array} data + */ + releaseData: function (data) { + data = null; + }, + + keepData: function (data, length) { + //The texture data mustn't be saved because it isn't a mutable texture. + return data; + }, + + /** + * Intializes with a texture2d with data + * @param {Array} data + * @param {Number} pixelFormat + * @param {Number} pixelsWide + * @param {Number} pixelsHigh + * @param {cc.Size} contentSize + * @return {Boolean} + */ + initWithData: function (data, pixelFormat, pixelsWide, pixelsHigh, contentSize) { + var self = this, tex2d = cc.Texture2D; + var gl = cc._renderContext; + var format = gl.RGBA, type = gl.UNSIGNED_BYTE; + + var bitsPerPixel = cc.Texture2D._B[pixelFormat]; + + var bytesPerRow = pixelsWide * bitsPerPixel / 8; + if (bytesPerRow % 8 === 0) { + gl.pixelStorei(gl.UNPACK_ALIGNMENT, 8); + } else if (bytesPerRow % 4 === 0) { + gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4); + } else if (bytesPerRow % 2 === 0) { + gl.pixelStorei(gl.UNPACK_ALIGNMENT, 2); + } else { + gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); + } + + self._webTextureObj = gl.createTexture(); + cc.glBindTexture2D(self); + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + + // Specify OpenGL texture image + switch (pixelFormat) { + case tex2d.PIXEL_FORMAT_RGBA8888: + format = gl.RGBA; + break; + case tex2d.PIXEL_FORMAT_RGB888: + format = gl.RGB; + break; + case tex2d.PIXEL_FORMAT_RGBA4444: + type = gl.UNSIGNED_SHORT_4_4_4_4; + break; + case tex2d.PIXEL_FORMAT_RGB5A1: + type = gl.UNSIGNED_SHORT_5_5_5_1; + break; + case tex2d.PIXEL_FORMAT_RGB565: + type = gl.UNSIGNED_SHORT_5_6_5; + break; + case tex2d.PIXEL_FORMAT_AI88: + format = gl.LUMINANCE_ALPHA; + break; + case tex2d.PIXEL_FORMAT_A8: + format = gl.ALPHA; + break; + case tex2d.PIXEL_FORMAT_I8: + format = gl.LUMINANCE; + break; + default: + cc.assert(0, cc._LogInfos.Texture2D_initWithData); + } + gl.texImage2D(gl.TEXTURE_2D, 0, format, pixelsWide, pixelsHigh, 0, format, type, data); + + + self._contentSize.width = contentSize.width; + self._contentSize.height = contentSize.height; + self._pixelsWide = pixelsWide; + self._pixelsHigh = pixelsHigh; + self._pixelFormat = pixelFormat; + self.maxS = contentSize.width / pixelsWide; + self.maxT = contentSize.height / pixelsHigh; + + self._hasPremultipliedAlpha = false; + self._hasMipmaps = false; + self.shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURE); + + self._textureLoaded = true; + + return true; + }, + + /** + Drawing extensions to make it easy to draw basic quads using a CCTexture2D object. + These functions require gl.TEXTURE_2D and both gl.VERTEX_ARRAY and gl.TEXTURE_COORD_ARRAY client states to be enabled. + */ + + /** + * draws a texture at a given point + * @param {cc.Point} point + */ + drawAtPoint: function (point) { + var self = this; + var coordinates = [ + 0.0, self.maxT, + self.maxS, self.maxT, + 0.0, 0.0, + self.maxS, 0.0], + gl = cc._renderContext; + + var width = self._pixelsWide * self.maxS, + height = self._pixelsHigh * self.maxT; + + var vertices = [ + point.x, point.y, 0.0, + width + point.x, point.y, 0.0, + point.x, height + point.y, 0.0, + width + point.x, height + point.y, 0.0]; + + self._glProgramState.apply(); + self._glProgramState._glprogram.setUniformsForBuiltins(); + + cc.glBindTexture2D(self); + + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_POSITION); + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_TEX_COORDS); + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, gl.FLOAT, false, 0, vertices); + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, gl.FLOAT, false, 0, coordinates); + + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + }, + + /** + * draws a texture inside a rect + * @param {cc.Rect} rect + */ + drawInRect: function (rect) { + var self = this; + var coordinates = [ + 0.0, self.maxT, + self.maxS, self.maxT, + 0.0, 0.0, + self.maxS, 0.0]; + + var vertices = [rect.x, rect.y, /*0.0,*/ + rect.x + rect.width, rect.y, /*0.0,*/ + rect.x, rect.y + rect.height, /*0.0,*/ + rect.x + rect.width, rect.y + rect.height /*0.0*/]; + + self._glProgramState.apply(); + self._glProgramState._glprogram.setUniformsForBuiltins(); + + cc.glBindTexture2D(self); + + var gl = cc._renderContext; + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_POSITION); + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_TEX_COORDS); + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, gl.FLOAT, false, 0, vertices); + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, gl.FLOAT, false, 0, coordinates); + + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + }, + + /** + Extensions to make it easy to create a CCTexture2D object from an image file. + Note that RGBA type textures will have their alpha premultiplied - use the blending mode (gl.ONE, gl.ONE_MINUS_SRC_ALPHA). + */ + + /** + * Initializes a texture from a UIImage object + * @param uiImage + * @return {Boolean} + */ + initWithImage: function (uiImage) { + if (uiImage == null) { + cc.log(cc._LogInfos.Texture2D_initWithImage); + return false; + } + + var imageWidth = uiImage.getWidth(); + var imageHeight = uiImage.getHeight(); + + var maxTextureSize = cc.configuration.getMaxTextureSize(); + if (imageWidth > maxTextureSize || imageHeight > maxTextureSize) { + cc.log(cc._LogInfos.Texture2D_initWithImage_2, imageWidth, imageHeight, maxTextureSize, maxTextureSize); + return false; + } + this._textureLoaded = true; + + // always load premultiplied images + return this._initPremultipliedATextureWithImage(uiImage, imageWidth, imageHeight); + }, + + /** + * init with HTML element + * @param {HTMLImageElement|HTMLCanvasElement} element + */ + initWithElement: function (element) { + if (!element) + return; + this._webTextureObj = cc._renderContext.createTexture(); + this._htmlElementObj = element; + this._textureLoaded = true; + // Textures should be loaded with premultiplied alpha in order to avoid gray bleeding + // when semitransparent textures are interpolated (e.g. when scaled). + this._hasPremultipliedAlpha = true; + }, + + /** + * HTMLElement Object getter + * @return {HTMLElement} + */ + getHtmlElementObj: function () { + return this._htmlElementObj; + }, + + /** + * whether texture is loaded + * @return {Boolean} + */ + isLoaded: function () { + return this._textureLoaded; + }, + + /** + * handler of texture loaded event + * @param {Boolean} [premultiplied=false] + */ + handleLoadedTexture: function (premultiplied) { + var self = this; + premultiplied = + (premultiplied !== undefined) + ? premultiplied + : self._hasPremultipliedAlpha; + // Not sure about this ! Some texture need to be updated even after loaded + if (!cc.game._rendererInitialized) + return; + if (!self._htmlElementObj) + return; + if (!self._htmlElementObj.width || !self._htmlElementObj.height) + return; + + //upload image to buffer + var gl = cc._renderContext; + + cc.glBindTexture2D(self); + + gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4); + if (premultiplied) + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1); + + // Specify OpenGL texture image + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, self._htmlElementObj); + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + + self.shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURE); + cc.glBindTexture2D(null); + if (premultiplied) + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 0); + + var pixelsWide = self._htmlElementObj.width; + var pixelsHigh = self._htmlElementObj.height; + + self._pixelsWide = self._contentSize.width = pixelsWide; + self._pixelsHigh = self._contentSize.height = pixelsHigh; + self._pixelFormat = cc.Texture2D.PIXEL_FORMAT_RGBA8888; + self.maxS = 1; + self.maxT = 1; + + self._hasPremultipliedAlpha = premultiplied; + self._hasMipmaps = false; + if (window.ENABLE_IMAEG_POOL) { + self._htmlElementObj = null; + } + + //dispatch load event to listener. + self.dispatchEvent("load"); + }, + + /** + Extensions to make it easy to create a cc.Texture2D object from a string of text. + Note that the generated textures are of type A8 - use the blending mode (gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA). + */ + /** + * Initializes a texture from a string with dimensions, alignment, font name and font size (note: initWithString does not support on HTML5) + * @param {String} text + * @param {String | cc.FontDefinition} fontName or fontDefinition + * @param {Number} fontSize + * @param {cc.Size} dimensions + * @param {Number} hAlignment + * @param {Number} vAlignment + * @return {Boolean} + */ + initWithString: function (text, fontName, fontSize, dimensions, hAlignment, vAlignment) { + cc.log(cc._LogInfos.Texture2D_initWithString); + return null; + }, + + /** + * Initializes a texture from a ETC file (note: initWithETCFile does not support on HTML5) + * @note Compatible to Cocos2d-x + * @param {String} file + * @return {Boolean} + */ + initWithETCFile: function (file) { + cc.log(cc._LogInfos.Texture2D_initWithETCFile_2); + return false; + }, + + /** + * Initializes a texture from a PVR file + * @param {String} file + * @return {Boolean} + */ + initWithPVRFile: function (file) { + cc.log(cc._LogInfos.Texture2D_initWithPVRFile_2); + return false; + }, + + /** + Extensions to make it easy to create a cc.Texture2D object from a PVRTC file + Note that the generated textures don't have their alpha premultiplied - use the blending mode (gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA). + */ + /** + * Initializes a texture from a PVRTC buffer + * @note compatible to cocos2d-iphone interface. + * @param {Array} data + * @param {Number} level + * @param {Number} bpp + * @param {Boolean} hasAlpha + * @param {Number} length + * @param {Number} pixelFormat + * @return {Boolean} + */ + initWithPVRTCData: function (data, level, bpp, hasAlpha, length, pixelFormat) { + cc.log(cc._LogInfos.Texture2D_initWithPVRTCData_2); + return false; + }, + + /** + * sets the min filter, mag filter, wrap s and wrap t texture parameters.
+ * If the texture size is NPOT (non power of 2), then in can only use gl.CLAMP_TO_EDGE in gl.TEXTURE_WRAP_{S,T}. + * @param {Object|Number} texParams texParams object or minFilter + * @param {Number} [magFilter] + * @param {Number} [wrapS] + * @param {Number} [wrapT] + */ + setTexParameters: function (texParams, magFilter, wrapS, wrapT) { + var _t = this; + var gl = cc._renderContext; + + if (magFilter !== undefined) + texParams = {minFilter: texParams, magFilter: magFilter, wrapS: wrapS, wrapT: wrapT}; + + cc.assert((_t._pixelsWide === cc.NextPOT(_t._pixelsWide) && _t._pixelsHigh === cc.NextPOT(_t._pixelsHigh)) || + (texParams.wrapS === gl.CLAMP_TO_EDGE && texParams.wrapT === gl.CLAMP_TO_EDGE), + "WebGLRenderingContext.CLAMP_TO_EDGE should be used in NPOT textures"); + + cc.glBindTexture2D(_t); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texParams.minFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texParams.magFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, texParams.wrapS); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, texParams.wrapT); + }, + + /** + * sets antialias texture parameters:
+ * - GL_TEXTURE_MIN_FILTER = GL_NEAREST
+ * - GL_TEXTURE_MAG_FILTER = GL_NEAREST + */ + setAntiAliasTexParameters: function () { + var gl = cc._renderContext; + + cc.glBindTexture2D(this); + if (!this._hasMipmaps) + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + else + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + }, + + /** + * sets alias texture parameters: + * GL_TEXTURE_MIN_FILTER = GL_NEAREST + * GL_TEXTURE_MAG_FILTER = GL_NEAREST + */ + setAliasTexParameters: function () { + var gl = cc._renderContext; + + cc.glBindTexture2D(this); + if (!this._hasMipmaps) + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + else + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + }, + + /** + * Generates mipmap images for the texture.
+ * It only works if the texture size is POT (power of 2). + */ + generateMipmap: function () { + var _t = this; + cc.assert(_t._pixelsWide === cc.NextPOT(_t._pixelsWide) && _t._pixelsHigh === cc.NextPOT(_t._pixelsHigh), "Mimpap texture only works in POT textures"); + + cc.glBindTexture2D(_t); + cc._renderContext.generateMipmap(cc._renderContext.TEXTURE_2D); + _t._hasMipmaps = true; + }, + + /** + * returns the pixel format. + * @return {String} + */ + stringForFormat: function () { + return cc.Texture2D._M[this._pixelFormat]; + }, + + /** + * returns the bits-per-pixel of the in-memory OpenGL texture + * @return {Number} + */ + bitsPerPixelForFormat: function (format) {//TODO I want to delete the format argument, use this._pixelFormat + format = format || this._pixelFormat; + var value = cc.Texture2D._B[format]; + if (value != null) return value; + cc.log(cc._LogInfos.Texture2D_bitsPerPixelForFormat, format); + return -1; + }, + + _initPremultipliedATextureWithImage: function (uiImage, width, height) { + var tex2d = cc.Texture2D; + var tempData = uiImage.getData(); + var inPixel32 = null; + var inPixel8 = null; + var outPixel16 = null; + var hasAlpha = uiImage.hasAlpha(); + var imageSize = cc.size(uiImage.getWidth(), uiImage.getHeight()); + var pixelFormat = tex2d.defaultPixelFormat; + var bpp = uiImage.getBitsPerComponent(); + + // compute pixel format + if (!hasAlpha) { + if (bpp >= 8) { + pixelFormat = tex2d.PIXEL_FORMAT_RGB888; + } else { + cc.log(cc._LogInfos.Texture2D__initPremultipliedATextureWithImage); + pixelFormat = tex2d.PIXEL_FORMAT_RGB565; + } + } + + // Repack the pixel data into the right format + var i, length = width * height; + + if (pixelFormat === tex2d.PIXEL_FORMAT_RGB565) { + if (hasAlpha) { + // Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGGBBBBB" + tempData = new Uint16Array(width * height); + inPixel32 = uiImage.getData(); + + for (i = 0; i < length; ++i) { + tempData[i] = + ((((inPixel32[i] >> 0) & 0xFF) >> 3) << 11) | // R + ((((inPixel32[i] >> 8) & 0xFF) >> 2) << 5) | // G + ((((inPixel32[i] >> 16) & 0xFF) >> 3) << 0); // B + } + } else { + // Convert "RRRRRRRRRGGGGGGGGBBBBBBBB" to "RRRRRGGGGGGBBBBB" + tempData = new Uint16Array(width * height); + inPixel8 = uiImage.getData(); + + for (i = 0; i < length; ++i) { + tempData[i] = + (((inPixel8[i] & 0xFF) >> 3) << 11) | // R + (((inPixel8[i] & 0xFF) >> 2) << 5) | // G + (((inPixel8[i] & 0xFF) >> 3) << 0); // B + } + } + } else if (pixelFormat === tex2d.PIXEL_FORMAT_RGBA4444) { + // Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRGGGGBBBBAAAA" + tempData = new Uint16Array(width * height); + inPixel32 = uiImage.getData(); + + for (i = 0; i < length; ++i) { + tempData[i] = + ((((inPixel32[i] >> 0) & 0xFF) >> 4) << 12) | // R + ((((inPixel32[i] >> 8) & 0xFF) >> 4) << 8) | // G + ((((inPixel32[i] >> 16) & 0xFF) >> 4) << 4) | // B + ((((inPixel32[i] >> 24) & 0xFF) >> 4) << 0); // A + } + } else if (pixelFormat === tex2d.PIXEL_FORMAT_RGB5A1) { + // Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGBBBBBA" + tempData = new Uint16Array(width * height); + inPixel32 = uiImage.getData(); + + for (i = 0; i < length; ++i) { + tempData[i] = + ((((inPixel32[i] >> 0) & 0xFF) >> 3) << 11) | // R + ((((inPixel32[i] >> 8) & 0xFF) >> 3) << 6) | // G + ((((inPixel32[i] >> 16) & 0xFF) >> 3) << 1) | // B + ((((inPixel32[i] >> 24) & 0xFF) >> 7) << 0); // A + } + } else if (pixelFormat === tex2d.PIXEL_FORMAT_A8) { + // Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "AAAAAAAA" + tempData = new Uint8Array(width * height); + inPixel32 = uiImage.getData(); + + for (i = 0; i < length; ++i) { + tempData[i] = (inPixel32 >> 24) & 0xFF; // A + } + } + + if (hasAlpha && pixelFormat === tex2d.PIXEL_FORMAT_RGB888) { + // Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRRRRGGGGGGGGBBBBBBBB" + inPixel32 = uiImage.getData(); + tempData = new Uint8Array(width * height * 3); + + for (i = 0; i < length; ++i) { + tempData[i * 3] = (inPixel32 >> 0) & 0xFF; // R + tempData[i * 3 + 1] = (inPixel32 >> 8) & 0xFF; // G + tempData[i * 3 + 2] = (inPixel32 >> 16) & 0xFF; // B + } + } + + this.initWithData(tempData, pixelFormat, width, height, imageSize); + + if (tempData != uiImage.getData()) + tempData = null; + + this._hasPremultipliedAlpha = uiImage.isPremultipliedAlpha(); + return true; + }, + + /** + * add listener for loaded event + * @param {Function} callback + * @param {cc.Node} target + * @deprecated since 3.1, please use addEventListener instead + */ + addLoadedEventListener: function (callback, target) { + this.addEventListener("load", callback, target); + }, + + /** + * remove listener from listeners by target + * @param {cc.Node} target + */ + removeLoadedEventListener: function (target) { + this.removeEventTarget("load", target); + } + }); +}; + +cc._tmp.WebGLTextureAtlas = function () { + var _p = cc.TextureAtlas.prototype; + _p._setupVBO = function () { + var _t = this; + var gl = cc._renderContext; + //create WebGLBuffer + _t._buffersVBO[0] = gl.createBuffer(); + _t._buffersVBO[1] = gl.createBuffer(); + + _t._quadsWebBuffer = gl.createBuffer(); + _t._mapBuffers(); + }; + + _p._mapBuffers = function () { + var _t = this; + var gl = cc._renderContext; + + gl.bindBuffer(gl.ARRAY_BUFFER, _t._quadsWebBuffer); + gl.bufferData(gl.ARRAY_BUFFER, _t._quadsArrayBuffer, gl.DYNAMIC_DRAW); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, _t._buffersVBO[1]); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, _t._indices, gl.STATIC_DRAW); + + //cc.checkGLErrorDebug(); + }; + + /** + *

Draws n quads from an index (offset).
+ * n + start can't be greater than the capacity of the atlas

+ * @param {Number} n + * @param {Number} start + */ + _p.drawNumberOfQuads = function (n, start) { + var _t = this; + start = start || 0; + if (0 === n || !_t.texture || !_t.texture.isLoaded()) + return; + + var gl = cc._renderContext; + cc.glBindTexture2D(_t.texture); + + // + // Using VBO without VAO + // + //vertices + //gl.bindBuffer(gl.ARRAY_BUFFER, _t._buffersVBO[0]); + // XXX: update is done in draw... perhaps it should be done in a timer + + gl.bindBuffer(gl.ARRAY_BUFFER, _t._quadsWebBuffer); + if (_t.dirty) { + gl.bufferData(gl.ARRAY_BUFFER, _t._quadsArrayBuffer, gl.DYNAMIC_DRAW); + _t.dirty = false; + } + + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_POSITION); + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_COLOR); + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_TEX_COORDS); + + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 3, gl.FLOAT, false, 24, 0); // vertices + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, gl.UNSIGNED_BYTE, true, 24, 12); // colors + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, gl.FLOAT, false, 24, 16); // tex coords + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, _t._buffersVBO[1]); + + if (cc.TEXTURE_ATLAS_USE_TRIANGLE_STRIP) + gl.drawElements(gl.TRIANGLE_STRIP, n * 6, gl.UNSIGNED_SHORT, start * 6 * _t._indices.BYTES_PER_ELEMENT); + else + gl.drawElements(gl.TRIANGLES, n * 6, gl.UNSIGNED_SHORT, start * 6 * _t._indices.BYTES_PER_ELEMENT); + + cc.g_NumberOfDraws++; + //cc.checkGLErrorDebug(); + }; +}; + +cc._tmp.WebGLTextureCache = function () { + var _p = cc.textureCache; + + _p.handleLoadedTexture = function (url, img) { + var locTexs = this._textures, tex, ext; + //remove judge(webgl) + if (!cc.game._rendererInitialized) { + locTexs = this._loadedTexturesBefore; + } + tex = locTexs[url]; + if (!tex) { + tex = locTexs[url] = new cc.Texture2D(); + tex.url = url; + } + tex.initWithElement(img); + ext = cc.path.extname(url); + if (ext === ".png") { + tex.handleLoadedTexture(true); + } + else { + tex.handleLoadedTexture(); + } + return tex; + }; + + /** + *

Returns a Texture2D object given an file image
+ * If the file image was not previously loaded, it will create a new Texture2D
+ * object and it will return it. It will use the filename as a key.
+ * Otherwise it will return a reference of a previously loaded image.
+ * Supported image extensions: .png, .jpg, .gif

+ * @param {String} url + * @param {Function} cb + * @param {Object} target + * @return {cc.Texture2D} + * @example + * //example + * cc.textureCache.addImage("hello.png"); + */ + _p.addImage = function (url, cb, target) { + cc.assert(url, cc._LogInfos.Texture2D_addImage_2); + + var locTexs = this._textures; + //remove judge(webgl) + if (!cc.game._rendererInitialized) { + locTexs = this._loadedTexturesBefore; + } + var tex = locTexs[url] || locTexs[cc.loader._getAliase(url)]; + if (tex) { + if (tex.isLoaded()) { + cb && cb.call(target, tex); + return tex; + } + else { + tex.addEventListener("load", function () { + cb && cb.call(target, tex); + }, target); + return tex; + } + } + + tex = locTexs[url] = new cc.Texture2D(); + tex.url = url; + var basePath = cc.loader.getBasePath ? cc.loader.getBasePath() : cc.loader.resPath; + cc.loader.loadImg(cc.path.join(basePath || "", url), function (err, img) { + if (err) + return cb && cb.call(target, err); + + var texResult = cc.textureCache.handleLoadedTexture(url, img); + cb && cb.call(target, texResult); + }); + + return tex; + }; + + _p.addImageAsync = _p.addImage; + _p = null; +}; diff --git a/frameworks/cocos2d-html5/cocos2d/core/utils/BinaryLoader.js b/frameworks/cocos2d-html5/cocos2d/core/utils/BinaryLoader.js new file mode 100644 index 0000000..46b82b6 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/utils/BinaryLoader.js @@ -0,0 +1,155 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + + +/** + * Load binary data by url. + * @function + * @param {String} url + * @param {Function} [cb] + */ + +cc.loader.loadBinary = function (url, cb) { + var self = this; + var xhr = this.getXMLHttpRequest(), + errInfo = "load " + url + " failed!"; + xhr.open("GET", url, true); + xhr.responseType = 'arraybuffer'; + if (cc.loader.loadBinary._IEFilter) { + // IE-specific logic here + xhr.setRequestHeader("Accept-Charset", "x-user-defined"); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4 && xhr.status === 200) { + var fileContents = cc._convertResponseBodyToText(xhr["responseBody"]); + cb(null, self._str2Uint8Array(fileContents)); + } else cb(errInfo); + }; + } else { + if (xhr.overrideMimeType) xhr.overrideMimeType("text\/plain; charset=x-user-defined"); + xhr.onload = function () { + xhr.readyState === 4 && xhr.status === 200 ? cb(null, new Uint8Array(xhr.response)) : cb(errInfo); + }; + } + xhr.send(null); +}; + +cc.loader.loadBinary._IEFilter = (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent) && window.IEBinaryToArray_ByteStr && window.IEBinaryToArray_ByteStr_Last); + +cc.loader._str2Uint8Array = function (strData) { + if (!strData) + return null; + + var arrData = new Uint8Array(strData.length); + for (var i = 0; i < strData.length; i++) { + arrData[i] = strData.charCodeAt(i) & 0xff; + } + return arrData; +}; + +/** + * Load binary data by url synchronously + * @function + * @param {String} url + * @return {Uint8Array} + */ +cc.loader.loadBinarySync = function (url) { + var self = this; + var req = this.getXMLHttpRequest(); + req.timeout = 0; + var errInfo = "load " + url + " failed!"; + req.open('GET', url, false); + var arrayInfo = null; + if (cc.loader.loadBinary._IEFilter) { + req.setRequestHeader("Accept-Charset", "x-user-defined"); + req.send(null); + if (req.status !== 200) { + cc.log(errInfo); + return null; + } + + var fileContents = cc._convertResponseBodyToText(req["responseBody"]); + if (fileContents) { + arrayInfo = self._str2Uint8Array(fileContents); + } + } else { + if (req.overrideMimeType) + req.overrideMimeType('text\/plain; charset=x-user-defined'); + req.send(null); + if (req.status !== 200) { + cc.log(errInfo); + return null; + } + + arrayInfo = self._str2Uint8Array(req.responseText); + } + return arrayInfo; +}; + +//Compatibility with IE9 +window.Uint8Array = window.Uint8Array || window.Array; + +if (cc.loader.loadBinary._IEFilter) { + var IEBinaryToArray_ByteStr_Script = + "\r\n" + + //"\r\n"; + + // inject VBScript + //document.write(IEBinaryToArray_ByteStr_Script); + var myVBScript = document.createElement('script'); + myVBScript.type = "text/vbscript"; + myVBScript.textContent = IEBinaryToArray_ByteStr_Script; + document.body.appendChild(myVBScript); + + // helper to convert from responseBody to a "responseText" like thing + cc._convertResponseBodyToText = function (binary) { + var byteMapping = {}; + for (var i = 0; i < 256; i++) { + for (var j = 0; j < 256; j++) { + byteMapping[ String.fromCharCode(i + j * 256) ] = + String.fromCharCode(i) + String.fromCharCode(j); + } + } + var rawBytes = IEBinaryToArray_ByteStr(binary); + var lastChr = IEBinaryToArray_ByteStr_Last(binary); + return rawBytes.replace(/[\s\S]/g, + function (match) { + return byteMapping[match]; + }) + lastChr; + }; +} diff --git a/frameworks/cocos2d-html5/cocos2d/core/utils/CCProfiler.js b/frameworks/cocos2d-html5/cocos2d/core/utils/CCProfiler.js new file mode 100644 index 0000000..94d0c41 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/utils/CCProfiler.js @@ -0,0 +1,151 @@ +cc.profiler = (function () { + var _showFPS = false; + var _inited = false; + var _frames = 0, _frameRate = 0, _lastSPF = 0, _accumDt = 0; + var _afterVisitListener = null, + _FPSLabel = document.createElement('div'), + _SPFLabel = document.createElement('div'), + _drawsLabel = document.createElement('div'), + _fps = document.createElement('div'); + + var LEVEL_DET_FACTOR = 0.6, _levelDetCycle = 10; + var LEVELS = [0, 10, 20, 30]; + var _fpsCount = [0, 0, 0, 0]; + var _currLevel = 3, _analyseCount = 0, _totalFPS = 0; + + _fps.id = 'fps'; + _fps.style.position = 'absolute'; + _fps.style.padding = '3px'; + _fps.style.textAlign = 'left'; + _fps.style.backgroundColor = 'rgb(0, 0, 34)'; + _fps.style.bottom = cc.DIRECTOR_STATS_POSITION.y + '0px'; + _fps.style.left = cc.DIRECTOR_STATS_POSITION.x + 'px'; + _fps.style.width = '45px'; + _fps.style.height = '80px'; + + var labels = [_drawsLabel, _SPFLabel, _FPSLabel]; + for (var i = 0; i < 3; ++i) { + var style = labels[i].style; + style.color = 'rgb(0, 255, 255)'; + style.font = 'bold 12px Helvetica, Arial'; + style.lineHeight = '20px'; + style.width = '100%'; + _fps.appendChild(labels[i]); + } + + var analyseFPS = function (fps) { + var lastId = LEVELS.length - 1, i = lastId, ratio, average = 0; + _analyseCount++; + _totalFPS += fps; + + for (; i >= 0; i--) { + if (fps >= LEVELS[i]) { + _fpsCount[i]++; + break; + } + } + + if (_analyseCount >= _levelDetCycle) { + average = _totalFPS / _levelDetCycle; + for (i = lastId; i > 0; i--) { + ratio = _fpsCount[i] / _levelDetCycle; + // Determined level + if (ratio >= LEVEL_DET_FACTOR && average >= LEVELS[i]) { + // Level changed + if (i != _currLevel) { + _currLevel = i; + profiler.onFrameRateChange && profiler.onFrameRateChange(average.toFixed(2)); + } + break; + } + // If no level determined, that means the framerate is not stable + } + + _changeCount = 0; + _analyseCount = 0; + _totalFPS = 0; + for (i = lastId; i > 0; i--) { + _fpsCount[i] = 0; + } + } + }; + + var afterVisit = function () { + _lastSPF = cc.director.getSecondsPerFrame(); + _frames++; + _accumDt += cc.director.getDeltaTime(); + + if (_accumDt > cc.DIRECTOR_FPS_INTERVAL) { + _frameRate = _frames / _accumDt; + _frames = 0; + _accumDt = 0; + + if (profiler.onFrameRateChange) { + analyseFPS(_frameRate); + } + + if (_showFPS) { + var mode = cc._renderType === cc.game.RENDER_TYPE_CANVAS ? "\n canvas" : "\n webgl"; + _SPFLabel.innerHTML = _lastSPF.toFixed(3); + _FPSLabel.innerHTML = _frameRate.toFixed(1).toString() + mode; + _drawsLabel.innerHTML = (0 | cc.g_NumberOfDraws).toString(); + } + } + }; + + var profiler = { + onFrameRateChange: null, + + getSecondsPerFrame: function () { + return _lastSPF; + }, + getFrameRate: function () { + return _frameRate; + }, + + setProfileDuration: function (duration) { + if (!isNaN(duration) && duration > 0) { + _levelDetCycle = duration / cc.DIRECTOR_FPS_INTERVAL; + } + }, + + resumeProfiling: function () { + cc.eventManager.addListener(_afterVisitListener, 1); + }, + + stopProfiling: function () { + cc.eventManager.removeListener(_afterVisitListener); + }, + + isShowingStats: function () { + return _showFPS; + }, + + showStats: function () { + if (!_inited) { + this.init(); + } + + if (_fps.parentElement === null) { + cc.container.appendChild(_fps); + } + _showFPS = true; + }, + + hideStats: function () { + _showFPS = false; + if (_fps.parentElement === cc.container) { + cc.container.removeChild(_fps); + } + }, + + init: function () { + if (!_inited) { + _afterVisitListener = cc.eventManager.addCustomListener(cc.Director.EVENT_AFTER_VISIT, afterVisit); + _inited = true; + } + } + }; + + return profiler; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/core/utils/CCSimplePool.js b/frameworks/cocos2d-html5/cocos2d/core/utils/CCSimplePool.js new file mode 100644 index 0000000..30fa22c --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/core/utils/CCSimplePool.js @@ -0,0 +1,74 @@ +/**************************************************************************** + Copyright (c) 2016 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +cc.SimplePool = function () { + this._pool = []; +}; +cc.SimplePool.prototype = { + constructor: cc.SimplePool, + + size: function () { + return this._pool.length; + }, + + put: function (obj) { + if (obj && this._pool.indexOf(obj) === -1) { + this._pool.unshift(obj); + } + }, + + get: function () { + var last = this._pool.length-1; + if (last < 0) { + return null; + } + else { + var obj = this._pool[last]; + this._pool.length = last; + return obj; + } + }, + + find: function (finder, end) { + var found, i, obj, pool = this._pool, last = pool.length-1; + for (i = pool.length; i >= 0; --i) { + obj = pool[i]; + found = finder(i, obj); + if (found) { + pool[i] = pool[last]; + pool.length = last; + return obj; + } + } + if (end) { + var index = end(); + if (index >= 0) { + pool[index] = pool[last]; + pool.length = last; + return obj; + } + } + return null; + } +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/cocos2d/effects/CCGrabber.js b/frameworks/cocos2d-html5/cocos2d/effects/CCGrabber.js new file mode 100644 index 0000000..2138025 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/effects/CCGrabber.js @@ -0,0 +1,110 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * FBO class that grabs the the contents of the screen + * @class + * @extends cc.Class + */ +cc.Grabber = cc.Class.extend({ + _FBO:null, + _oldFBO:null, + _oldClearColor:null, + + _gl:null, + + /** + * constructor of cc.Grabber + */ + ctor:function () { + cc.sys._checkWebGLRenderMode(); + this._gl = cc._renderContext; + this._oldClearColor = [0, 0, 0, 0]; + this._oldFBO = null; + // generate FBO + this._FBO = this._gl.createFramebuffer(); + }, + + /** + * grab + * @param {cc.Texture2D} texture + */ + grab:function (texture) { + var locGL = this._gl; + this._oldFBO = locGL.getParameter(locGL.FRAMEBUFFER_BINDING); + // bind + locGL.bindFramebuffer(locGL.FRAMEBUFFER, this._FBO); + // associate texture with FBO + locGL.framebufferTexture2D(locGL.FRAMEBUFFER, locGL.COLOR_ATTACHMENT0, locGL.TEXTURE_2D, texture._webTextureObj, 0); + + // check if it worked (probably worth doing :) ) + var status = locGL.checkFramebufferStatus(locGL.FRAMEBUFFER); + if (status !== locGL.FRAMEBUFFER_COMPLETE) + cc.log("Frame Grabber: could not attach texture to frmaebuffer"); + locGL.bindFramebuffer(locGL.FRAMEBUFFER, this._oldFBO); + }, + + /** + * should be invoked before drawing + * @param {cc.Texture2D} texture + */ + beforeRender:function (texture) { + var locGL = this._gl; + this._oldFBO = locGL.getParameter(locGL.FRAMEBUFFER_BINDING); + locGL.bindFramebuffer(locGL.FRAMEBUFFER, this._FBO); + + // save clear color + this._oldClearColor = locGL.getParameter(locGL.COLOR_CLEAR_VALUE); + + // BUG XXX: doesn't work with RGB565. + locGL.clearColor(0, 0, 0, 0); + + // BUG #631: To fix #631, uncomment the lines with #631 + // Warning: But it CCGrabber won't work with 2 effects at the same time + // glClearColor(0.0f,0.0f,0.0f,1.0f); // #631 + + locGL.clear(locGL.COLOR_BUFFER_BIT | locGL.DEPTH_BUFFER_BIT); + + // glColorMask(true, true, true, false); // #631 + }, + + /** + * should be invoked after drawing + * @param {cc.Texture2D} texture + */ + afterRender:function (texture) { + var locGL = this._gl; + locGL.bindFramebuffer(locGL.FRAMEBUFFER, this._oldFBO); + locGL.colorMask(true, true, true, true); // #631 + }, + + /** + * delete FBO + */ + destroy:function(){ + this._gl.deleteFramebuffer(this._FBO); + } +}); diff --git a/frameworks/cocos2d-html5/cocos2d/effects/CCGrid.js b/frameworks/cocos2d-html5/cocos2d/effects/CCGrid.js new file mode 100644 index 0000000..361cba8 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/effects/CCGrid.js @@ -0,0 +1,850 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2009 On-Core + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * Base class for cc.Grid + * @class + * @extends cc.Class + */ +cc.GridBase = cc.Class.extend(/** @lends cc.GridBase# */{ + _active: false, + _reuseGrid: 0, + _gridSize: null, + _gridRect: null, + _texture: null, + _step: null, + _grabber: null, + _isTextureFlipped: false, + _glProgramState: null, + _directorProjection: 0, + + _dirty: false, + + /** + * create one cc.GridBase Object + * Constructor of cc.GridBase + * @param {cc.Size} gridSize + * @param {cc.Texture2D} [texture=] + * @param {Boolean} [flipped=] + * @param {cc.Rect} rect + */ + ctor: function (gridSize, texture, flipped, rect) { + cc.sys._checkWebGLRenderMode(); + this._active = false; + this._reuseGrid = 0; + this._gridSize = null; + this._gridRect = new cc.rect(); + this._texture = null; + this._step = cc.p(0, 0); + this._grabber = null; + this._isTextureFlipped = false; + this._glProgramState = null; + this._directorProjection = 0; + this._dirty = false; + + if (gridSize !== undefined) + this.initWithSize(gridSize, texture, flipped, rect); + }, + + /** + * whether or not the grid is active + * @return {Boolean} + */ + isActive: function () { + return this._active; + }, + + /** + * whether or not the grid is active + * @param {Number} active + */ + setActive: function (active) { + this._active = active; + if (!active) { + var director = cc.director; + var proj = director.getProjection(); + director.setProjection(proj); + } + }, + + /** + * get number of times that the grid will be reused + * @return {Number} + */ + getReuseGrid: function () { + return this._reuseGrid; + }, + /** + * set number of times that the grid will be reused + * @param reuseGrid + */ + setReuseGrid: function (reuseGrid) { + this._reuseGrid = reuseGrid; + }, + + /** + * get size of the grid + * @return {cc.Size} + */ + getGridSize: function () { + return cc.size(this._gridSize.width, this._gridSize.height); + }, + + /** + * set size of the grid + * @param {cc.Size} gridSize + */ + setGridSize: function (gridSize) { + this._gridSize.width = parseInt(gridSize.width); + this._gridSize.height = parseInt(gridSize.height); + }, + + /** + * set rect of the grid + * @param {cc.Rect} rect + */ + setGridRect: function (rect) { + this._gridRect = rect; + }, + + /** + * get rect of the grid + * @return {cc.Rect} rect + */ + getGridRect: function () { + return this._gridRect; + }, + + /** + * get pixels between the grids + * @return {cc.Point} + */ + getStep: function () { + return cc.p(this._step.x, this._step.y); + }, + + /** + * set pixels between the grids + * @param {cc.Point} step + */ + setStep: function (step) { + this._step.x = step.x; + this._step.y = step.y; + }, + + /** + * get whether or not the texture is flipped + * @return {Boolean} + */ + isTextureFlipped: function () { + return this._isTextureFlipped; + }, + + /** + * set whether or not the texture is flipped + * @param {Boolean} flipped + */ + setTextureFlipped: function (flipped) { + if (this._isTextureFlipped !== flipped) { + this._isTextureFlipped = flipped; + this.calculateVertexPoints(); + } + }, + + /** + * + * @param {cc.Size} gridSize + * @param {cc.Texture2D} [texture=] + * @param {Boolean} [flipped=false] + * @param {cc.Rect} [rect=] + * @returns {boolean} + */ + initWithSize: function (gridSize, texture, flipped, rect) { + if (!texture) { + var director = cc.director; + var winSize = director.getWinSizeInPixels(); + + var POTWide = cc.NextPOT(winSize.width); + var POTHigh = cc.NextPOT(winSize.height); + + var data = new Uint8Array(POTWide * POTHigh * 4); + if (!data) { + cc.log("cocos2d: CCGrid: not enough memory."); + return false; + } + + texture = new cc.Texture2D(); + // we only use rgba8888 + texture.initWithData(data, cc.Texture2D.PIXEL_FORMAT_RGBA8888, POTWide, POTHigh, winSize); + if (!texture) { + cc.log("cocos2d: CCGrid: error creating texture"); + return false; + } + } + + flipped = flipped || false; + + this._active = false; + this._reuseGrid = 0; + this._gridSize = gridSize; + this._texture = texture; + this._isTextureFlipped = flipped; + if (rect === undefined || cc._rectEqualToZero(rect)) { + var size = this._texture.getContentSize(); + rect = new cc.rect(0, 0, size.width, size.height); + } + + this._gridRect = rect; + + this._step.x = this._gridRect.width / gridSize.width; + this._step.y = this._gridRect.height / gridSize.height; + + this._grabber = new cc.Grabber(); + if (!this._grabber) + return false; + this._grabber.grab(this._texture); + this._glProgramState = cc.GLProgramState.getOrCreateWithGLProgram(cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURE)); + this.calculateVertexPoints(); + return true; + }, + + beforeDraw: function () { + // save projection + this._directorProjection = cc.director.getProjection(); + + //this.set2DProjection(); //TODO why? + var size = cc.director.getWinSizeInPixels(); + gl.viewport(0, 0, size.width, size.height); + this._grabber.beforeRender(this._texture); + }, + + afterDraw: function (target) { + this._grabber.afterRender(this._texture); + + // restore projection + //cc.director.setProjection(this._directorProjection); + cc.director.setViewport(); + + cc.glBindTexture2D(this._texture); + this.beforeBlit(); + this.blit(target); + this.afterBlit(); + }, + + beforeBlit: function () { + }, + + afterBlit: function () { + }, + + blit: function () { + cc.log("cc.GridBase.blit(): Shall be overridden in subclass."); + }, + + reuse: function () { + cc.log("cc.GridBase.reuse(): Shall be overridden in subclass."); + }, + + calculateVertexPoints: function () { + cc.log("cc.GridBase.calculateVertexPoints(): Shall be overridden in subclass."); + }, + + set2DProjection: function () { + var winSize = cc.director.getWinSizeInPixels(); + + var gl = cc._renderContext; + gl.viewport(0, 0, winSize.width, winSize.height); + cc.kmGLMatrixMode(cc.KM_GL_PROJECTION); + cc.kmGLLoadIdentity(); + + var orthoMatrix = cc.math.Matrix4.createOrthographicProjection(0, winSize.width, 0, winSize.height, -1, 1); + cc.kmGLMultMatrix(orthoMatrix); + + cc.kmGLMatrixMode(cc.KM_GL_MODELVIEW); + cc.kmGLLoadIdentity(); + cc.setProjectionMatrixDirty() + } +}); + +/** + * create one cc.GridBase Object + * @deprecated + * @param {cc.Size} gridSize + * @param {cc.Texture2D} [texture=] + * @param {Boolean} [flipped=] + * @param {cc.Rect} [rect=] + * @return {cc.GridBase} + */ +cc.GridBase.create = function (gridSize, texture, flipped, rect) { + return new cc.GridBase(gridSize, texture, flipped, rect); +}; + +/** + * cc.Grid3D is a 3D grid implementation. Each vertex has 3 dimensions: x,y,z + * @class + * @extends cc.GridBase + */ +cc.Grid3D = cc.GridBase.extend(/** @lends cc.Grid3D# */{ + _texCoordinates: null, + _vertices: null, + _originalVertices: null, + _indices: null, + + _texCoordinateBuffer: null, + _verticesBuffer: null, + _indicesBuffer: null, + + _needDepthTestForBlit: false, + _oldDepthTestValue: false, + _oldDepthWriteValue: false, + + /** + * create one Grid3D object + * Constructor of cc.Grid3D + * @param {cc.Size} gridSize + * @param {cc.Texture2D} [texture=] + * @param {Boolean} [flipped=] + * @param {cc.Rect} [rect=] + */ + ctor: function (gridSize, texture, flipped, rect) { + cc.GridBase.prototype.ctor.call(this); + this._texCoordinates = null; + this._vertices = null; + this._originalVertices = null; + this._indices = null; + + this._texCoordinateBuffer = null; + this._verticesBuffer = null; + this._indicesBuffer = null; + + this._matrix = new cc.math.Matrix4(); + this._matrix.identity(); + + if (gridSize !== undefined) + this.initWithSize(gridSize, texture, flipped, rect); + }, + + /** + * returns the vertex at a given position
+ * It will be deprecated in future, please use getVertex instead. + * @param {cc.Point} pos + * @return {cc.Vertex3F} + */ + vertex: function (pos) { + return this.getVertex(pos); + }, + + /** + * returns the vertex at a given position + * @param {cc.Point} pos + * @return {cc.Vertex3F} + */ + getVertex: function (pos) { + if (pos.x !== (0 | pos.x) || pos.y !== (0 | pos.y)) + cc.log("cc.Grid3D.vertex() : Numbers must be integers"); + var index = 0 | ((pos.x * (this._gridSize.height + 1) + pos.y) * 3); + var locVertices = this._vertices; + return new cc.Vertex3F(locVertices[index], locVertices[index + 1], locVertices[index + 2]); + }, + + /** + * returns the original (non-transformed) vertex at a given position
+ * It will be deprecated in future, please use getOriginalVertex instead. + * @param {cc.Point} pos + * @return {cc.Vertex3F} + */ + originalVertex: function (pos) { + return this.getOriginalVertex(pos); + }, + + /** + * returns the original (non-transformed) vertex at a given position + * @param {cc.Point} pos + * @return {cc.Vertex3F} + */ + getOriginalVertex: function (pos) { + if (pos.x !== (0 | pos.x) || pos.y !== (0 | pos.y)) + cc.log("cc.Grid3D.originalVertex() : Numbers must be integers"); + var index = 0 | ((pos.x * (this._gridSize.height + 1) + pos.y) * 3); + var locOriginalVertices = this._originalVertices; + return new cc.Vertex3F(locOriginalVertices[index], locOriginalVertices[index + 1], locOriginalVertices[index + 2]); + }, + + /** + * sets a new vertex at a given position + * @param {cc.Point} pos + * @param {cc.Vertex3F} vertex + */ + setVertex: function (pos, vertex) { + if (pos.x !== (0 | pos.x) || pos.y !== (0 | pos.y)) + cc.log("cc.Grid3D.setVertex() : Numbers must be integers"); + var index = 0 | ((pos.x * (this._gridSize.height + 1) + pos.y) * 3); + var vertArray = this._vertices; + vertArray[index] = vertex.x; + vertArray[index + 1] = vertex.y; + vertArray[index + 2] = vertex.z; + this._dirty = true; + }, + + beforeBlit: function () { + if (this._needDepthTestForBlit) { + var gl = cc._renderContext; + this._oldDepthTestValue = gl.isEnabled(gl.DEPTH_TEST); + this._oldDepthWriteValue = gl.getParameter(gl.DEPTH_WRITEMASK); + //CHECK_GL_ERROR_DEBUG(); + gl.enable(gl.DEPTH_TEST); + gl.depthMask(true); + } + }, + + afterBlit: function () { + if (this._needDepthTestForBlit) { + var gl = cc._renderContext; + if (this._oldDepthTestValue) + gl.enable(gl.DEPTH_TEST); + else + gl.disable(gl.DEPTH_TEST); + gl.depthMask(this._oldDepthWriteValue); + } + }, + + blit: function (target) { + var n = this._gridSize.width * this._gridSize.height; + + var wt = target._renderCmd._worldTransform; + this._matrix.mat[0] = wt.a; + this._matrix.mat[4] = wt.c; + this._matrix.mat[12] = wt.tx; + this._matrix.mat[1] = wt.b; + this._matrix.mat[5] = wt.d; + this._matrix.mat[13] = wt.ty; + + this._glProgramState.apply(this._matrix); + + var gl = cc._renderContext, locDirty = this._dirty; + + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_POSITION); + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_TEX_COORDS); + // + // Attributes + // + // position + gl.bindBuffer(gl.ARRAY_BUFFER, this._verticesBuffer); + if (locDirty) + gl.bufferData(gl.ARRAY_BUFFER, this._vertices, gl.DYNAMIC_DRAW); + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 3, gl.FLOAT, false, 0, 0); + + // texCoords + gl.bindBuffer(gl.ARRAY_BUFFER, this._texCoordinateBuffer); + if (locDirty) + gl.bufferData(gl.ARRAY_BUFFER, this._texCoordinates, gl.DYNAMIC_DRAW); + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indicesBuffer); + if (locDirty) + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this._indices, gl.STATIC_DRAW); + gl.drawElements(gl.TRIANGLES, n * 6, gl.UNSIGNED_SHORT, 0); + if (locDirty) + this._dirty = false; + cc.incrementGLDraws(1); + }, + + reuse: function () { + if (this._reuseGrid > 0) { + var locOriginalVertices = this._originalVertices, locVertices = this._vertices; + for (var i = 0, len = this._vertices.length; i < len; i++) + locOriginalVertices[i] = locVertices[i]; + --this._reuseGrid; + } + }, + + calculateVertexPoints: function () { + var gl = cc._renderContext; + + var width = this._texture.pixelsWidth; + var height = this._texture.pixelsHeight; + var imageH = this._texture.getContentSizeInPixels().height; + var locGridSize = this._gridSize; + + var numOfPoints = (locGridSize.width + 1) * (locGridSize.height + 1); + this._vertices = new Float32Array(numOfPoints * 3); + this._texCoordinates = new Float32Array(numOfPoints * 2); + this._indices = new Uint16Array(locGridSize.width * locGridSize.height * 6); + + if (this._verticesBuffer) + gl.deleteBuffer(this._verticesBuffer); + this._verticesBuffer = gl.createBuffer(); + if (this._texCoordinateBuffer) + gl.deleteBuffer(this._texCoordinateBuffer); + this._texCoordinateBuffer = gl.createBuffer(); + if (this._indicesBuffer) + gl.deleteBuffer(this._indicesBuffer); + this._indicesBuffer = gl.createBuffer(); + + var x, y, i, locIndices = this._indices, locTexCoordinates = this._texCoordinates; + var locIsTextureFlipped = this._isTextureFlipped, locVertices = this._vertices; + for (x = 0; x < locGridSize.width; ++x) { + for (y = 0; y < locGridSize.height; ++y) { + var idx = (y * locGridSize.width) + x; + var x1 = x * this._step.x + this._gridRect.x; + var x2 = x1 + this._step.x; + var y1 = y * this._step.y + this._gridRect.y; + var y2 = y1 + this._step.y; + + var a = (x * (locGridSize.height + 1) + y); + var b = ((x + 1) * (locGridSize.height + 1) + y); + var c = ((x + 1) * (locGridSize.height + 1) + (y + 1)); + var d = (x * (locGridSize.height + 1) + (y + 1)); + + locIndices[idx * 6] = a; + locIndices[idx * 6 + 1] = b; + locIndices[idx * 6 + 2] = d; + locIndices[idx * 6 + 3] = b; + locIndices[idx * 6 + 4] = c; + locIndices[idx * 6 + 5] = d; + + var l1 = [a * 3, b * 3, c * 3, d * 3]; + var e = {x: x1, y: y1, z: 0}; //new cc.Vertex3F(x1, y1, 0); + var f = {x: x2, y: y1, z: 0}; //new cc.Vertex3F(x2, y1, 0); + var g = {x: x2, y: y2, z: 0}; // new cc.Vertex3F(x2, y2, 0); + var h = {x: x1, y: y2, z: 0}; //new cc.Vertex3F(x1, y2, 0); + + var l2 = [e, f, g, h]; + var tex1 = [a * 2, b * 2, c * 2, d * 2]; + var tex2 = [cc.p(x1, y1), cc.p(x2, y1), cc.p(x2, y2), cc.p(x1, y2)]; + for (i = 0; i < 4; ++i) { + locVertices[l1[i]] = l2[i].x; + locVertices[l1[i] + 1] = l2[i].y; + locVertices[l1[i] + 2] = l2[i].z; + locTexCoordinates[tex1[i]] = tex2[i].x / width; + if (locIsTextureFlipped) + locTexCoordinates[tex1[i] + 1] = (imageH - tex2[i].y) / height; + else + locTexCoordinates[tex1[i] + 1] = tex2[i].y / height; + } + } + } + this._originalVertices = new Float32Array(this._vertices); + + gl.bindBuffer(gl.ARRAY_BUFFER, this._verticesBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this._vertices, gl.DYNAMIC_DRAW); + gl.bindBuffer(gl.ARRAY_BUFFER, this._texCoordinateBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this._texCoordinates, gl.DYNAMIC_DRAW); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indicesBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this._indices, gl.STATIC_DRAW); + this._dirty = true; + }, + + setNeedDepthTestForBlit: function (needDepthTest) { + this._needDepthTestForBlit = needDepthTest; + }, + + getNeedDepthTestForBlit: function () { + return this._needDepthTestForBlit; + } +}); + +/** + * create one Grid3D object + * @deprecated + * @param {cc.Size} gridSize + * @param {cc.Texture2D} [texture=] + * @param {Boolean} [flipped=] + * @return {cc.Grid3D} + */ +cc.Grid3D.create = function (gridSize, texture, flipped) { + return new cc.Grid3D(gridSize, texture, flipped); +}; + +/** + * cc.TiledGrid3D is a 3D grid implementation. It differs from Grid3D in that
+ * the tiles can be separated from the grid. + * @class + * @extends cc.GridBase + */ +cc.TiledGrid3D = cc.GridBase.extend(/** @lends cc.TiledGrid3D# */{ + _texCoordinates: null, + _vertices: null, + _originalVertices: null, + _indices: null, + + _texCoordinateBuffer: null, + _verticesBuffer: null, + _indicesBuffer: null, + + /** + * create one TiledGrid3D object + * Constructor of cc.TiledGrid3D + * @param {cc.Size} gridSize + * @param {cc.Texture2D} [texture=] + * @param {Boolean} [flipped=] + */ + ctor: function (gridSize, texture, flipped, rect) { + cc.GridBase.prototype.ctor.call(this); + this._texCoordinates = null; + this._vertices = null; + this._originalVertices = null; + this._indices = null; + + this._texCoordinateBuffer = null; + this._verticesBuffer = null; + this._indicesBuffer = null; + + this._matrix = new cc.math.Matrix4(); + this._matrix.identity(); + + if (gridSize !== undefined) + this.initWithSize(gridSize, texture, flipped, rect); + }, + + /** + * returns the tile at the given position
+ * It will be deprecated in future, please use getTile instead. + * @param {cc.Point} pos + * @return {cc.Quad3} + */ + tile: function (pos) { + return this.getTile(pos); + }, + + /** + * returns the tile at the given position + * @param {cc.Point} pos + * @return {cc.Quad3} + */ + getTile: function (pos) { + if (pos.x !== (0 | pos.x) || pos.y !== (0 | pos.y)) + cc.log("cc.TiledGrid3D.tile() : Numbers must be integers"); + + var idx = (this._gridSize.height * pos.x + pos.y) * 4 * 3; + var locVertices = this._vertices; + return new cc.Quad3(new cc.Vertex3F(locVertices[idx], locVertices[idx + 1], locVertices[idx + 2]), + new cc.Vertex3F(locVertices[idx + 3], locVertices[idx + 4], locVertices[idx + 5]), + new cc.Vertex3F(locVertices[idx + 6], locVertices[idx + 7], locVertices[idx + 8]), + new cc.Vertex3F(locVertices[idx + 9], locVertices[idx + 10], locVertices[idx + 11])); + }, + + /** + * returns the original tile (untransformed) at the given position + * @param {cc.Point} pos + * @return {cc.Quad3} + */ + getOriginalTile: function (pos) { + if (pos.x !== (0 | pos.x) || pos.y !== (0 | pos.y)) + cc.log("cc.TiledGrid3D.originalTile() : Numbers must be integers"); + + var idx = (this._gridSize.height * pos.x + pos.y) * 4 * 3; + var locOriginalVertices = this._originalVertices; + return new cc.Quad3(new cc.Vertex3F(locOriginalVertices[idx], locOriginalVertices[idx + 1], locOriginalVertices[idx + 2]), + new cc.Vertex3F(locOriginalVertices[idx + 3], locOriginalVertices[idx + 4], locOriginalVertices[idx + 5]), + new cc.Vertex3F(locOriginalVertices[idx + 6], locOriginalVertices[idx + 7], locOriginalVertices[idx + 8]), + new cc.Vertex3F(locOriginalVertices[idx + 9], locOriginalVertices[idx + 10], locOriginalVertices[idx + 11])); + }, + + /** + * returns the original tile (untransformed) at the given position.
+ * It will be deprecated in future, please use getOriginalTile instead. + * @param {cc.Point} pos + * @return {cc.Quad3} + */ + originalTile: function (pos) { + return this.getOriginalTile(pos); + }, + + /** + * sets a new tile + * @param {cc.Point} pos + * @param {cc.Quad3} coords + */ + setTile: function (pos, coords) { + if (pos.x !== (0 | pos.x) || pos.y !== (0 | pos.y)) + cc.log("cc.TiledGrid3D.setTile() : Numbers must be integers"); + + var idx = (this._gridSize.height * pos.x + pos.y) * 12; + var locVertices = this._vertices; + locVertices[idx] = coords.bl.x; + locVertices[idx + 1] = coords.bl.y; + locVertices[idx + 2] = coords.bl.z; + locVertices[idx + 3] = coords.br.x; + locVertices[idx + 4] = coords.br.y; + locVertices[idx + 5] = coords.br.z; + locVertices[idx + 6] = coords.tl.x; + locVertices[idx + 7] = coords.tl.y; + locVertices[idx + 8] = coords.tl.z; + locVertices[idx + 9] = coords.tr.x; + locVertices[idx + 10] = coords.tr.y; + locVertices[idx + 11] = coords.tr.z; + this._dirty = true; + }, + + blit: function (target) { + var n = this._gridSize.width * this._gridSize.height; + + var wt = target._renderCmd._worldTransform; + this._matrix.mat[0] = wt.a; + this._matrix.mat[4] = wt.c; + this._matrix.mat[12] = wt.tx; + this._matrix.mat[1] = wt.b; + this._matrix.mat[5] = wt.d; + this._matrix.mat[13] = wt.ty; + + this._glProgramState.apply(this._matrix); + + // + // Attributes + // + var gl = cc._renderContext, locDirty = this._dirty; + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_POSITION); + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_TEX_COORDS); + + // position + gl.bindBuffer(gl.ARRAY_BUFFER, this._verticesBuffer); + if (locDirty) + gl.bufferData(gl.ARRAY_BUFFER, this._vertices, gl.DYNAMIC_DRAW); + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 3, gl.FLOAT, false, 0, this._vertices); + + // texCoords + gl.bindBuffer(gl.ARRAY_BUFFER, this._texCoordinateBuffer); + if (locDirty) + gl.bufferData(gl.ARRAY_BUFFER, this._texCoordinates, gl.DYNAMIC_DRAW); + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, gl.FLOAT, false, 0, this._texCoordinates); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indicesBuffer); + if (locDirty) + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this._indices, gl.STATIC_DRAW); + gl.drawElements(gl.TRIANGLES, n * 6, gl.UNSIGNED_SHORT, 0); + if (locDirty) + this._dirty = false; + cc.incrementGLDraws(1); + }, + + reuse: function () { + if (this._reuseGrid > 0) { + var locVertices = this._vertices, locOriginalVertices = this._originalVertices; + for (var i = 0; i < locVertices.length; i++) + locOriginalVertices[i] = locVertices[i]; + --this._reuseGrid; + } + }, + + calculateVertexPoints: function () { + var width = this._texture.pixelsWidth; + var height = this._texture.pixelsHeight; + var imageH = this._texture.getContentSizeInPixels().height; + var locGridSize = this._gridSize; + + var numQuads = locGridSize.width * locGridSize.height; + this._vertices = new Float32Array(numQuads * 12); + this._texCoordinates = new Float32Array(numQuads * 8); + this._indices = new Uint16Array(numQuads * 6); + + var gl = cc._renderContext; + if (this._verticesBuffer) + gl.deleteBuffer(this._verticesBuffer); + this._verticesBuffer = gl.createBuffer(); + if (this._texCoordinateBuffer) + gl.deleteBuffer(this._texCoordinateBuffer); + this._texCoordinateBuffer = gl.createBuffer(); + if (this._indicesBuffer) + gl.deleteBuffer(this._indicesBuffer); + this._indicesBuffer = gl.createBuffer(); + + var x, y, i = 0; + var locStep = this._step, locVertices = this._vertices, locTexCoords = this._texCoordinates, locIsTextureFlipped = this._isTextureFlipped; + for (x = 0; x < locGridSize.width; x++) { + for (y = 0; y < locGridSize.height; y++) { + var x1 = x * locStep.x; + var x2 = x1 + locStep.x; + var y1 = y * locStep.y; + var y2 = y1 + locStep.y; + + locVertices[i * 12] = x1; + locVertices[i * 12 + 1] = y1; + locVertices[i * 12 + 2] = 0; + locVertices[i * 12 + 3] = x2; + locVertices[i * 12 + 4] = y1; + locVertices[i * 12 + 5] = 0; + locVertices[i * 12 + 6] = x1; + locVertices[i * 12 + 7] = y2; + locVertices[i * 12 + 8] = 0; + locVertices[i * 12 + 9] = x2; + locVertices[i * 12 + 10] = y2; + locVertices[i * 12 + 11] = 0; + + var newY1 = y1; + var newY2 = y2; + + if (locIsTextureFlipped) { + newY1 = imageH - y1; + newY2 = imageH - y2; + } + + locTexCoords[i * 8] = x1 / width; + locTexCoords[i * 8 + 1] = newY1 / height; + locTexCoords[i * 8 + 2] = x2 / width; + locTexCoords[i * 8 + 3] = newY1 / height; + locTexCoords[i * 8 + 4] = x1 / width; + locTexCoords[i * 8 + 5] = newY2 / height; + locTexCoords[i * 8 + 6] = x2 / width; + locTexCoords[i * 8 + 7] = newY2 / height; + i++; + } + } + + var locIndices = this._indices; + for (x = 0; x < numQuads; x++) { + locIndices[x * 6 + 0] = (x * 4 + 0); + locIndices[x * 6 + 1] = (x * 4 + 1); + locIndices[x * 6 + 2] = (x * 4 + 2); + + locIndices[x * 6 + 3] = (x * 4 + 1); + locIndices[x * 6 + 4] = (x * 4 + 2); + locIndices[x * 6 + 5] = (x * 4 + 3); + } + this._originalVertices = new Float32Array(this._vertices); + + gl.bindBuffer(gl.ARRAY_BUFFER, this._verticesBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this._vertices, gl.DYNAMIC_DRAW); + gl.bindBuffer(gl.ARRAY_BUFFER, this._texCoordinateBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this._texCoordinates, gl.DYNAMIC_DRAW); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indicesBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this._indices, gl.DYNAMIC_DRAW); + this._dirty = true; + } +}); + +/** + * create one TiledGrid3D object + * @deprecated since v3.0, please use new cc.TiledGrid3D(gridSize, texture, flipped) instead + * @param {cc.Size} gridSize + * @param {cc.Texture2D} [texture=] + * @param {Boolean} [flipped=] + * @return {cc.TiledGrid3D} + */ +cc.TiledGrid3D.create = function (gridSize, texture, flipped) { + return new cc.TiledGrid3D(gridSize, texture, flipped); +}; diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/SIMDPolyfill.js b/frameworks/cocos2d-html5/cocos2d/kazmath/SIMDPolyfill.js new file mode 100644 index 0000000..7c65b0a --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/SIMDPolyfill.js @@ -0,0 +1,78 @@ +/** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2008, Luke Benstead. + 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. + + 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. + */ + +(function(cc) { + var _useSIMD = false; + + var mat4Proto = cc.math.Matrix4.prototype; + var mat4Inverse = mat4Proto.inverse; + var mat4IsIdentity = mat4Proto.isIdentity; + var mat4Transpose = mat4Proto.transpose; + var mat4Multiply = mat4Proto.multiply; + var mat4GetMat4MultiplyValue = mat4Proto.getMat4MultiplyValue; + var mat4AssignFrom = mat4Proto.assignFrom; + var mat4Equals = mat4Proto.equals; + var mat4LookAt = mat4Proto.lookAt; + + var vec3Proto = cc.math.Vec3.prototype; + var vec3TransformCoord = vec3Proto.transformCoord; + + function _isEnabledSIMD () { + return _useSIMD; + } + + function _enableSIMD (enable) { + if(typeof(SIMD) === 'undefined') + return; + + if (enable) { + mat4Proto.inverse = mat4Proto.inverseSIMD; + mat4Proto.isIdentity = mat4Proto.isIdentitySIMD; + mat4Proto.transpose = mat4Proto.transposeSIMD; + mat4Proto.multiply = mat4Proto.multiplySIMD; + mat4Proto.getMat4MultiplyValue = mat4Proto.getMat4MultiplyValueSIMD; + mat4Proto.assignFrom = mat4Proto.assignFromSIMD; + mat4Proto.equals = mat4Proto.equalsSIMD; + mat4Proto.lookAt = mat4Proto.lookAtSIMD; + vec3Proto.transformCoord = vec3Proto.transformCoordSIMD; + } else { + mat4Proto.inverse = mat4Inverse; + mat4Proto.isIdentity = mat4IsIdentity; + mat4Proto.transpose = mat4Transpose; + mat4Proto.multiply = mat4Multiply; + mat4Proto.getMat4MultiplyValue = mat4GetMat4MultiplyValue; + mat4Proto.assignFrom = mat4AssignFrom; + mat4Proto.equals = mat4Equals; + mat4Proto.lookAt = mat4LookAt; + vec3Proto.transformCoord = vec3TransformCoord; + } + _useSIMD = enable; + } + + cc.defineGetterSetter(cc.sys, "useSIMD", _isEnabledSIMD, _enableSIMD); +})(cc); \ No newline at end of file diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/aabb.js b/frameworks/cocos2d-html5/cocos2d/kazmath/aabb.js new file mode 100644 index 0000000..8c5dc5b --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/aabb.js @@ -0,0 +1,78 @@ +/** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2008, Luke Benstead. + 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. + + 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. + */ + +/** + * A structure that represents an axis-aligned bounding box. + * cc.kmAABB => cc.math.AABB + */ +cc.math.AABB = function (min, max) { + /** The max corner of the box */ + this.min = min || new cc.math.Vec3(); + /** The min corner of the box */ + this.max = max || new cc.math.Vec3(); +}; + +/** + * Returns true if point is in the specified AABB, returns false otherwise. + * @param {cc.math.Vec3} point + * @returns {boolean} + */ +cc.math.AABB.prototype.containsPoint = function (point) { + return (point.x >= this.min.x && point.x <= this.max.x && + point.y >= this.min.y && point.y <= this.max.y && + point.z >= this.min.z && point.z <= this.max.z); +}; + +/** + * Returns true if point is in the specified AABB, returns + * false otherwise. + */ +cc.math.AABB.containsPoint = function (pPoint, pBox) { + return (pPoint.x >= pBox.min.x && pPoint.x <= pBox.max.x && + pPoint.y >= pBox.min.y && pPoint.y <= pBox.max.y && + pPoint.z >= pBox.min.z && pPoint.z <= pBox.max.z); +}; + +/** + * Assigns aabb to current AABB object + * @param {cc.math.AABB} aabb + */ +cc.math.AABB.prototype.assignFrom = function(aabb){ + this.min.assignFrom(aabb.min); + this.max.assignFrom(aabb.max); +}; + +/** + * Assigns pIn to pOut, returns pOut. + */ +cc.math.AABB.assign = function (pOut, pIn) { //cc.kmAABBAssign + pOut.min.assignFrom(pIn.min); + pOut.max.assignFrom(pIn.max); + return pOut; +}; + diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/gl/mat4stack.js b/frameworks/cocos2d-html5/cocos2d/kazmath/gl/mat4stack.js new file mode 100644 index 0000000..cd33942 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/gl/mat4stack.js @@ -0,0 +1,99 @@ +/** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2008, Luke Benstead. + 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. + + 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. + */ + +(function (cc) { + /** + * The stack of cc.math.Matrix4 + * @param {cc.math.Matrix4} [top] + * @param {Array} [stack] + * @constructor + */ + cc.math.Matrix4Stack = function (top, stack) { + this.top = top; + this.stack = stack || []; + this.lastUpdated = 0; + //this._matrixPool = []; // use pool in next version + }; + cc.km_mat4_stack = cc.math.Matrix4Stack; + var proto = cc.math.Matrix4Stack.prototype; + + proto.initialize = function () { //cc.km_mat4_stack_initialize + this.stack.length = 0; + this.top = null; + }; + + //for compatibility + cc.km_mat4_stack_push = function (stack, item) { + stack.stack.push(stack.top); + stack.top = new cc.math.Matrix4(item); + }; + + cc.km_mat4_stack_pop = function (stack, pOut) { + stack.top = stack.stack.pop(); + }; + + cc.km_mat4_stack_release = function (stack) { + stack.stack = null; + stack.top = null; + }; + + proto.push = function (item) { + item = item || this.top; + this.stack.push(this.top); + this.top = new cc.math.Matrix4(item); + //this.top = this._getFromPool(item); + }; + + proto.pop = function () { + //this._putInPool(this.top); + this.top = this.stack.pop(); + }; + + proto.release = function () { + this.stack = null; + this.top = null; + this._matrixPool = null; + }; + + proto._getFromPool = function (item) { + var pool = this._matrixPool; + if (pool.length === 0) + return new cc.math.Matrix4(item); + var ret = pool.pop(); + ret.assignFrom(item); + return ret; + }; + + proto._putInPool = function (matrix) { + this._matrixPool.push(matrix); + }; +})(cc); + + + + diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/gl/matrix.js b/frameworks/cocos2d-html5/cocos2d/kazmath/gl/matrix.js new file mode 100644 index 0000000..13c9798 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/gl/matrix.js @@ -0,0 +1,168 @@ +/** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2008, Luke Benstead. + 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. + + 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. + */ + +(function (cc) { + cc.KM_GL_MODELVIEW = 0x1700; + cc.KM_GL_PROJECTION = 0x1701; + cc.KM_GL_TEXTURE = 0x1702; + + cc.modelview_matrix_stack = new cc.math.Matrix4Stack(); + cc.projection_matrix_stack = new cc.math.Matrix4Stack(); + cc.texture_matrix_stack = new cc.math.Matrix4Stack(); + + cc.current_stack = null; + var initialized = false; + + cc.lazyInitialize = function () { + if (!initialized) { + var identity = new cc.math.Matrix4(); //Temporary identity matrix + + //Initialize all 3 stacks + cc.modelview_matrix_stack.initialize(); + cc.projection_matrix_stack.initialize(); + cc.texture_matrix_stack.initialize(); + + cc.current_stack = cc.modelview_matrix_stack; + cc.initialized = true; + identity.identity(); + + //Make sure that each stack has the identity matrix + cc.modelview_matrix_stack.push(identity); + cc.projection_matrix_stack.push(identity); + cc.texture_matrix_stack.push(identity); + } + }; + + cc.lazyInitialize(); + + cc.kmGLFreeAll = function () { + //Clear the matrix stacks + cc.modelview_matrix_stack.release(); + cc.modelview_matrix_stack = null; + cc.projection_matrix_stack.release(); + cc.projection_matrix_stack = null; + cc.texture_matrix_stack.release(); + cc.texture_matrix_stack = null; + + //Delete the matrices + cc.initialized = false; //Set to uninitialized + cc.current_stack = null; //Set the current stack to point nowhere + }; + + cc.kmGLPushMatrix = function () { + cc.current_stack.push(cc.current_stack.top); + }; + + cc.kmGLPushMatrixWitMat4 = function (saveMat) { + cc.current_stack.stack.push(cc.current_stack.top); + saveMat.assignFrom(cc.current_stack.top); + cc.current_stack.top = saveMat; + }; + + cc.kmGLPopMatrix = function () { + //No need to lazy initialize, you shouldn't be popping first anyway! + //cc.km_mat4_stack_pop(cc.current_stack, null); + cc.current_stack.top = cc.current_stack.stack.pop(); + }; + + cc.kmGLMatrixMode = function (mode) { + //cc.lazyInitialize(); + switch (mode) { + case cc.KM_GL_MODELVIEW: + cc.current_stack = cc.modelview_matrix_stack; + break; + case cc.KM_GL_PROJECTION: + cc.current_stack = cc.projection_matrix_stack; + break; + case cc.KM_GL_TEXTURE: + cc.current_stack = cc.texture_matrix_stack; + break; + default: + throw new Error("Invalid matrix mode specified"); //TODO: Proper error handling + break; + } + cc.current_stack.lastUpdated = cc.director.getTotalFrames(); + }; + + cc.kmGLLoadIdentity = function () { + //cc.lazyInitialize(); + cc.current_stack.top.identity(); //Replace the top matrix with the identity matrix + }; + + cc.kmGLLoadMatrix = function (pIn) { + //cc.lazyInitialize(); + cc.current_stack.top.assignFrom(pIn); + }; + + cc.kmGLMultMatrix = function (pIn) { + //cc.lazyInitialize(); + cc.current_stack.top.multiply(pIn); + }; + + var tempMatrix = new cc.math.Matrix4(); //an internal matrix + cc.kmGLTranslatef = function (x, y, z) { + //Create a rotation matrix using translation + var translation = cc.math.Matrix4.createByTranslation(x, y, z, tempMatrix); + + //Multiply the rotation matrix by the current matrix + cc.current_stack.top.multiply(translation); + }; + + var tempVector3 = new cc.math.Vec3(); + cc.kmGLRotatef = function (angle, x, y, z) { + tempVector3.fill(x, y, z); + //Create a rotation matrix using the axis and the angle + var rotation = cc.math.Matrix4.createByAxisAndAngle(tempVector3, cc.degreesToRadians(angle), tempMatrix); + + //Multiply the rotation matrix by the current matrix + cc.current_stack.top.multiply(rotation); + }; + + cc.kmGLScalef = function (x, y, z) { + var scaling = cc.math.Matrix4.createByScale(x, y, z, tempMatrix); + cc.current_stack.top.multiply(scaling); + }; + + cc.kmGLGetMatrix = function (mode, pOut) { + //cc.lazyInitialize(); + switch (mode) { + case cc.KM_GL_MODELVIEW: + pOut.assignFrom(cc.modelview_matrix_stack.top); + break; + case cc.KM_GL_PROJECTION: + pOut.assignFrom(cc.projection_matrix_stack.top); + break; + case cc.KM_GL_TEXTURE: + pOut.assignFrom(cc.texture_matrix_stack.top); + break; + default: + throw new Error("Invalid matrix mode specified"); //TODO: Proper error handling + break; + } + }; +})(cc); diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/mat3.js b/frameworks/cocos2d-html5/cocos2d/kazmath/mat3.js new file mode 100644 index 0000000..d3023f0 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/mat3.js @@ -0,0 +1,357 @@ +/** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2008, Luke Benstead. + 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. + + 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. + */ + +window.Uint16Array = window.Uint16Array || window.Array; +window.Float32Array = window.Float32Array || window.Array; +(function(cc){ + /** + *

+ * A 3x3 matrix
+ *

+ * @class + * @param {cc.math.Matrix3} [mat3] + */ + cc.math.Matrix3 = function(mat3) { + if (mat3 && mat3.mat) { + this.mat = new Float32Array(mat3.mat); + } else { + this.mat = new Float32Array(9); + } + }; + cc.kmMat3 = cc.math.Matrix3; + var _p = cc.math.Matrix3.prototype; + + /** + * Copy matrix. + * @fn fill + * @memberof cc.math.Matrix3 + * @param {cc.math.Matrix3} mat3 Matrix to copy + * @return {cc.math.Matrix3} this + */ + _p.fill = function(mat3) { //cc.kmMat3Fill + var mat = this.mat, matIn = mat3.mat; + mat[0] = matIn[0]; + mat[1] = matIn[1]; + mat[2] = matIn[2]; + mat[3] = matIn[3]; + mat[4] = matIn[4]; + mat[5] = matIn[5]; + mat[6] = matIn[6]; + mat[7] = matIn[7]; + mat[8] = matIn[8]; + return this; + }; + + _p.adjugate = function(){ //= cc.kmMat3Adjugate + var mat = this.mat; + var m0 = mat[0], m1 = mat[1], m2 = mat[2], m3 = mat[3], m4 = mat[4], + m5 = mat[5], m6 = mat[6], m7 = mat[7], m8 = mat[8]; + mat[0] = m4 * m8 - m5 * m7; + mat[1] = m2 * m7 - m1 * m8; + mat[2] = m1 * m5 - m2 * m4; + mat[3] = m5 * m6 - m3 * m8; + mat[4] = m0 * m8 - m2 * m6; + mat[5] = m2 * m3 - m0 * m5; + mat[6] = m3 * m7 - m4 * m6; + + // XXX: pIn.mat[9] is invalid! + //mat[7] = m1 * m6 - pIn.mat[9] * m7; + mat[8] = m0 * m4 - m1 * m3; + return this; + }; + + /** + * Sets pOut to an identity matrix returns pOut + * @memberof cc.math.Matrix3 + * @param {cc.math.Matrix3} pOut - A pointer to the matrix to set to identity + * @return {cc.math.Matrix3} this + */ + _p.identity = function() { //cc.kmMat3Identity + var mat = this.mat; + mat[1] = mat[2] = mat[3] = + mat[5] = mat[6] = mat[7] = 0; + mat[0] = mat[4] = mat[8] = 1.0; + return this; + }; + + var tmpMatrix = new cc.math.Matrix3(); // internal matrix + + _p.inverse = function(determinate){ //cc.kmMat3Inverse + if (determinate === 0.0) + return this; + tmpMatrix.assignFrom(this); + var detInv = 1.0 / determinate; + this.adjugate(); + this.multiplyScalar(detInv); + return this; + }; + + _p.isIdentity = function(){ //= cc.kmMat3IsIdentity + var mat = this.mat; + return (mat[0] === 1 && mat[1] === 0 && mat[2] === 0 + && mat[3] === 0 && mat[4] === 1 && mat[5] === 0 + && mat[6] === 0 && mat[7] === 0 && mat[8] === 1); + }; + + _p.transpose = function(){ // cc.kmMat3Transpose + var mat = this.mat; + var m1 = mat[1], m2 = mat[2], m3 = mat[3], m5 = mat[5], + m6 = mat[6], m7 = mat[7]; + // m0 = mat[0],m8 = mat[8], m4 = mat[4]; + //mat[0] = m0; + //mat[8] = m8; + //mat[4] = m4 + mat[1] = m3; + mat[2] = m6; + mat[3] = m1; + mat[5] = m7; + mat[6] = m2; + mat[7] = m5; + return this; + }; + + _p.determinant = function(){ + var mat = this.mat; + /* + calculating the determinant following the rule of sarus, + | 0 3 6 | 0 3 | + m = | 1 4 7 | 1 4 | + | 2 5 8 | 2 5 | + now sum up the products of the diagonals going to the right (i.e. 0,4,8) + and subtract the products of the other diagonals (i.e. 2,4,6) + */ + var output = mat[0] * mat[4] * mat[8] + mat[1] * mat[5] * mat[6] + mat[2] * mat[3] * mat[7]; + output -= mat[2] * mat[4] * mat[6] + mat[0] * mat[5] * mat[7] + mat[1] * mat[3] * mat[8]; + return output; + }; + + _p.multiply = function(mat3){ + var m1 = this.mat, m2 = mat3.mat; + var a0 = m1[0], a1 = m1[1], a2 = m1[2], a3 = m1[3], a4 = m1[4], a5 = m1[5], + a6 = m1[6], a7 = m1[7], a8 = m1[8]; + var b0 = m2[0], b1 = m2[1], b2 = m2[2], b3 = m2[3], b4 = m2[4], b5 = m2[5], + b6 = m2[6], b7 = m2[7], b8 = m2[8]; + + m1[0] = a0 * b0 + a3 * b1 + a6 * b2; + m1[1] = a1 * b0 + a4 * b1 + a7 * b2; + m1[2] = a2 * b0 + a5 * b1 + a8 * b2; + + m1[3] = a2 * b0 + a5 * b1 + a8 * b2; + m1[4] = a1 * b3 + a4 * b4 + a7 * b5; + m1[5] = a2 * b3 + a5 * b4 + a8 * b5; + + m1[6] = a0 * b6 + a3 * b7 + a6 * b8; + m1[7] = a1 * b6 + a4 * b7 + a7 * b8; + m1[8] = a2 * b6 + a5 * b7 + a8 * b8; + return this; + }; + + _p.multiplyScalar = function(factor) { + var mat = this.mat; + mat[0] *= factor; + mat[1] *= factor; + mat[2] *= factor; + mat[3] *= factor; + mat[4] *= factor; + mat[5] *= factor; + mat[6] *= factor; + mat[7] *= factor; + mat[8] *= factor; + return this; + }; + + cc.math.Matrix3.rotationAxisAngle = function(axis, radians) { //cc.kmMat3RotationAxisAngle + var rcos = Math.cos(radians), rsin = Math.sin(radians); + var retMat = new cc.math.Matrix3(); + var mat = retMat.mat; + + mat[0] = rcos + axis.x * axis.x * (1 - rcos); + mat[1] = axis.z * rsin + axis.y * axis.x * (1 - rcos); + mat[2] = -axis.y * rsin + axis.z * axis.x * (1 - rcos); + + mat[3] = -axis.z * rsin + axis.x * axis.y * (1 - rcos); + mat[4] = rcos + axis.y * axis.y * (1 - rcos); + mat[5] = axis.x * rsin + axis.z * axis.y * (1 - rcos); + + mat[6] = axis.y * rsin + axis.x * axis.z * (1 - rcos); + mat[7] = -axis.x * rsin + axis.y * axis.z * (1 - rcos); + mat[8] = rcos + axis.z * axis.z * (1 - rcos); + + return retMat; + }; + + _p.assignFrom = function(matIn){ // cc.kmMat3Assign + if(this === matIn) { + cc.log("cc.math.Matrix3.assign(): current matrix equals matIn"); + return this; + } + var mat = this.mat, m2 = matIn.mat; + mat[0] = m2[0]; + mat[1] = m2[1]; + mat[2] = m2[2]; + mat[3] = m2[3]; + mat[4] = m2[4]; + mat[5] = m2[5]; + mat[6] = m2[6]; + mat[7] = m2[7]; + mat[8] = m2[8]; + return this; + }; + + _p.equals = function(mat3) { + if (this === mat3) + return true; + var EPSILON = cc.math.EPSILON,m1 = this.mat, m2 = mat3.mat; + for (var i = 0; i < 9; ++i) { + if (!(m1[i] + EPSILON > m2[i] && m1[i] - EPSILON < m2[i])) + return false; + } + return true; + }; + + cc.math.Matrix3.createByRotationX = function(radians) { + var retMat = new cc.math.Matrix3(), mat = retMat.mat; + mat[0] = 1.0; + mat[1] = 0.0; + mat[2] = 0.0; + + mat[3] = 0.0; + mat[4] = Math.cos(radians); + mat[5] = Math.sin(radians); + + mat[6] = 0.0; + mat[7] = -Math.sin(radians); + mat[8] = Math.cos(radians); + return retMat; + }; + + cc.math.Matrix3.createByRotationY = function(radians) { + /* + | cos(A) 0 sin(A) | + M = | 0 1 0 | + | -sin(A) 0 cos(A) | + */ + var retMat = new cc.math.Matrix3(), mat = retMat.mat; + mat[0] = Math.cos(radians); + mat[1] = 0.0; + mat[2] = -Math.sin(radians); + + mat[3] = 0.0; + mat[4] = 1.0; + mat[5] = 0.0; + + mat[6] = Math.sin(radians); + mat[7] = 0.0; + mat[8] = Math.cos(radians); + return retMat; + }; + + cc.math.Matrix3.createByRotationZ = function(radians) { + /* + | cos(A) -sin(A) 0 | + M = | sin(A) cos(A) 0 | + | 0 0 1 | + */ + var retMat = new cc.math.Matrix3(), mat = retMat.mat; + mat[0] = Math.cos(radians); + mat[1] = -Math.sin(radians); + mat[2] = 0.0; + + mat[3] = Math.sin(radians); + mat[4] = Math.cos(radians); + mat[5] = 0.0; + + mat[6] = 0.0; + mat[7] = 0.0; + mat[8] = 1.0; + return retMat; + }; + + cc.math.Matrix3.createByRotation = function(radians) { + /* + | cos(A) -sin(A) 0 | + M = | sin(A) cos(A) 0 | + | 0 0 1 | + */ + var retMat = new cc.math.Matrix3(), mat = retMat.mat; + mat[0] = Math.cos(radians); + mat[1] = Math.sin(radians); + mat[2] = 0.0; + + mat[3] = -Math.sin(radians); + mat[4] = Math.cos(radians); + mat[5] = 0.0; + + mat[6] = 0.0; + mat[7] = 0.0; + mat[8] = 1.0; + return retMat; + }; + + cc.math.Matrix3.createByScale = function(x, y) { + var ret = new cc.math.Matrix3(); + ret.identity(); + ret.mat[0] = x; + ret.mat[4] = y; + return ret; + }; + + cc.math.Matrix3.createByTranslation = function(x, y){ + var ret = new cc.math.Matrix3(); + ret.identity(); + ret.mat[6] = x; + ret.mat[7] = y; + return ret; + }; + + cc.math.Matrix3.createByQuaternion = function(quaternion) { //cc.kmMat3RotationQuaternion + if(!quaternion) + return null; + + var ret = new cc.math.Matrix3(), mat = ret.mat; + // First row + mat[0] = 1.0 - 2.0 * (quaternion.y * quaternion.y + quaternion.z * quaternion.z); + mat[1] = 2.0 * (quaternion.x * quaternion.y - quaternion.w * quaternion.z); + mat[2] = 2.0 * (quaternion.x * quaternion.z + quaternion.w * quaternion.y); + + // Second row + mat[3] = 2.0 * (quaternion.x * quaternion.y + quaternion.w * quaternion.z); + mat[4] = 1.0 - 2.0 * (quaternion.x * quaternion.x + quaternion.z * quaternion.z); + mat[5] = 2.0 * (quaternion.y * quaternion.z - quaternion.w * quaternion.x); + + // Third row + mat[6] = 2.0 * (quaternion.x * quaternion.z - quaternion.w * quaternion.y); + mat[7] = 2.0 * (quaternion.y * quaternion.z + quaternion.w * quaternion.x); + mat[8] = 1.0 - 2.0 * (quaternion.x * quaternion.x + quaternion.y * quaternion.y); + return ret; + }; + + _p.rotationToAxisAngle = function() { //cc.kmMat3RotationToAxisAngle + return cc.math.Quaternion.rotationMatrix(this).toAxisAndAngle(); + } +})(cc); diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/mat4.js b/frameworks/cocos2d-html5/cocos2d/kazmath/mat4.js new file mode 100644 index 0000000..35a56e1 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/mat4.js @@ -0,0 +1,1011 @@ +/** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2008, Luke Benstead. + 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. + + 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. + */ + +(function(cc) { + /** + *

+ * A 4x4 matrix
+ *
+ * mat =
+ * | 0 4 8 12 |
+ * | 1 5 9 13 |
+ * | 2 6 10 14 |
+ * | 3 7 11 15 | + *

+ * @class + * @param {cc.math.Matrix4} [mat4] + */ + cc.math.Matrix4 = function (mat4) { + if(mat4 && mat4.mat){ + this.mat = new Float32Array(mat4.mat); + } else { + this.mat = new Float32Array(16); + } + }; + cc.kmMat4 = cc.math.Matrix4; + var proto = cc.math.Matrix4.prototype; + + /** + * Fills a cc.math.Matrix4 structure with the values from a 16 element array of floats + * @param {Array} scalarArr + */ + proto.fill = function(scalarArr){ //cc.kmMat4Fill + var mat = this.mat; + for(var i = 0; i < 16; i++){ + mat[i] = scalarArr[i]; + } + return this; + }; + + /** + * Sets pOut to an identity matrix returns pOut + * @param pOut - A pointer to the matrix to set to identity + * @returns Returns pOut so that the call can be nested + */ + cc.kmMat4Identity = function (pOut) { + var mat = pOut.mat; + mat[1] = mat[2] = mat[3] = mat[4] = mat[6] = mat[7] + = mat[8] = mat[9] = mat[11] = mat[12] = mat[13] = mat[14] = 0; + mat[0] = mat[5] = mat[10] = mat[15] = 1.0; + return pOut; + }; + + /** + * Sets matrix to identity value. + * @returns {cc.math.Matrix4} + */ + proto.identity = function(){ + var mat = this.mat; + mat[1] = mat[2] = mat[3] = mat[4] = mat[6] = mat[7] + = mat[8] = mat[9] = mat[11] = mat[12] = mat[13] = mat[14] = 0; + mat[0] = mat[5] = mat[10] = mat[15] = 1.0; + return this; + }; + + proto.get = function(row, col){ + return this.mat[row + 4 * col]; + }; + + proto.set = function(row, col, value){ + this.mat[row + 4 * col] = value; + }; + + proto.swap = function(r1, c1, r2, c2) { +/* var tmp = this.get(r1, c1); + this.set(r1, c1, this.get(r2, c2)); + this.set(r2, c2, tmp);*/ + var mat = this.mat, tmp = mat[r1 + 4 * c1]; + mat[r1 + 4 * c1] = mat[r2 + 4 * c2]; + mat[r2 + 4 * c2] = tmp; + }; + + //Returns an upper and a lower triangular matrix which are L and R in the Gauss algorithm + cc.math.Matrix4._gaussj = function (a, b) { + var i, icol = 0, irow = 0, j, k, l, ll, n = 4, m = 4, selElement; + var big, dumb, pivinv; + var indxc = [0, 0, 0, 0], indxr = [0, 0, 0, 0], ipiv = [0, 0, 0, 0]; + + /* for (j = 0; j < n; j++) { + ipiv[j] = 0; + }*/ + + for (i = 0; i < n; i++) { + big = 0.0; + for (j = 0; j < n; j++) { + if (ipiv[j] !== 1) { + for (k = 0; k < n; k++) { + if (ipiv[k] === 0) { + selElement = Math.abs(a.get(j, k)); + if (selElement >= big) { + big = selElement; + irow = j; + icol = k; + } + } + } + } + } + ++(ipiv[icol]); + if (irow !== icol) { + for (l = 0; l < n; l++) + a.swap(irow, l, icol, l); + for (l = 0; l < m; l++) + b.swap(irow, l, icol, l); + } + indxr[i] = irow; + indxc[i] = icol; + if (a.get(icol, icol) === 0.0) + return false; + + pivinv = 1.0 / a.get(icol, icol); + a.set(icol, icol, 1.0); + for (l = 0; l < n; l++) + a.set(icol, l, a.get(icol, l) * pivinv); + + for (l = 0; l < m; l++) + b.set(icol, l, b.get(icol, l) * pivinv); + + for (ll = 0; ll < n; ll++) { + if (ll !== icol) { + dumb = a.get(ll, icol); + a.set(ll, icol, 0.0); + for (l = 0; l < n; l++) + a.set(ll, l, a.get(ll, l) - a.get(icol, l) * dumb); + + for (l = 0; l < m; l++) + b.set(ll, l, a.get(ll, l) - b.get(icol, l) * dumb); + } + } + } + // This is the end of the main loop over columns of the reduction. It only remains to unscram- + // ble the solution in view of the column interchanges. We do this by interchanging pairs of + // columns in the reverse order that the permutation was built up. + for (l = n - 1; l >= 0; l--) { + if (indxr[l] !== indxc[l]) { + for (k = 0; k < n; k++) + a.swap(k, indxr[l], k, indxc[l]); + } + } + return true; + }; + + var identityMatrix = new cc.math.Matrix4().identity(); + /** + * Calculates the inverse of pM and stores the result in pOut. + * Please use matrix4's inverse function instead. + * @Return Returns NULL if there is no inverse, else pOut + */ + cc.kmMat4Inverse = function (pOut, pM) { + var inv = new cc.math.Matrix4(pM); + var tmp = new cc.math.Matrix4(identityMatrix); + if (cc.math.Matrix4._gaussj(inv, tmp) === false) + return null; + pOut.assignFrom(inv); + return pOut; + }; + + /** + * Calculates the inverse of current matrix. + * @returns {cc.math.Matrix4} Returns null if there is no inverse, else returns a new inverse matrix object + */ + proto.inverse = function(){ //cc.kmMat4Inverse + var inv = new cc.math.Matrix4(this); + var tmp = new cc.math.Matrix4(identityMatrix); + if (cc.math.Matrix4._gaussj(inv, tmp) === false) + return null; + return inv; + }; + + /** + * Returns true if current matrix is an identity matrix, false otherwise + */ + proto.isIdentity = function () { // cc.kmMat4IsIdentity + var mat = this.mat; + return (mat[0] === 1 && mat[1] === 0 && mat[2] === 0 && mat[3] === 0 + && mat[4] === 0 && mat[5] === 1 && mat[6] === 0 && mat[7] === 0 + && mat[8] === 0 && mat[9] === 0 && mat[10] === 1 && mat[11] === 0 + && mat[12] === 0 && mat[13] === 0 && mat[14] === 0 && mat[15] === 1); + }; + + /** + * transpose the current matrix + */ + proto.transpose = function() { // cc.kmMat4Transpose + var mat = this.mat; + var m1 = mat[1], m2 = mat[2], m3 = mat[3], + m4 = mat[4], m6 = mat[6], m7 = mat[7], + m8 = mat[8], m9 = mat[9], m11 = mat[11], + m12 = mat[12], m13 = mat[13], m14 = mat[14]; + mat[1] = m4; + mat[2] = m8; + mat[3] = m12; + + mat[4] = m1; + mat[6] = m9; + mat[7] = m13; + + mat[8] = m2; + mat[9] = m6; + mat[11] = m14; + + mat[12] = m3; + mat[13] = m7; + mat[14] = m11; + return this; + }; + + /** + * Multiplies pM1 with pM2, stores the result in pOut, returns pOut + */ + cc.kmMat4Multiply = function (pOut, pM1, pM2) { + // Cache the matrix values (makes for huge speed increases!) + var outArray = pOut.mat, mat1 = pM1.mat, mat2 = pM2.mat; + var a00 = mat1[0], a01 = mat1[1], a02 = mat1[2], a03 = mat1[3]; + var a10 = mat1[4], a11 = mat1[5], a12 = mat1[6], a13 = mat1[7]; + var a20 = mat1[8], a21 = mat1[9], a22 = mat1[10], a23 = mat1[11]; + var a30 = mat1[12], a31 = mat1[13], a32 = mat1[14], a33 = mat1[15]; + + var b00 = mat2[0], b01 = mat2[1], b02 = mat2[2], b03 = mat2[3]; + var b10 = mat2[4], b11 = mat2[5], b12 = mat2[6], b13 = mat2[7]; + var b20 = mat2[8], b21 = mat2[9], b22 = mat2[10], b23 = mat2[11]; + var b30 = mat2[12], b31 = mat2[13], b32 = mat2[14], b33 = mat2[15]; + + outArray[0] = b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30; + outArray[1] = b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31; + outArray[2] = b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32; + outArray[3] = b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33; + outArray[4] = b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30; + outArray[5] = b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31; + outArray[6] = b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32; + outArray[7] = b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33; + outArray[8] = b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30; + outArray[9] = b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31; + outArray[10] = b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32; + outArray[11] = b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33; + outArray[12] = b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30; + outArray[13] = b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31; + outArray[14] = b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32; + outArray[15] = b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33; + return pOut; + }; + + /** + * current matrix multiplies with other matrix mat4 + * @param {cc.math.Matrix4} mat4 + * @returns {cc.math.Matrix4} + */ + proto.multiply = function(mat4){ + // Cache the matrix values (makes for huge speed increases!) + var mat = this.mat, mat2 = mat4.mat; + var a00 = mat[0], a01 = mat[1], a02 = mat[2], a03 = mat[3]; + var a10 = mat[4], a11 = mat[5], a12 = mat[6], a13 = mat[7]; + var a20 = mat[8], a21 = mat[9], a22 = mat[10], a23 = mat[11]; + var a30 = mat[12], a31 = mat[13], a32 = mat[14], a33 = mat[15]; + + var b00 = mat2[0], b01 = mat2[1], b02 = mat2[2], b03 = mat2[3]; + var b10 = mat2[4], b11 = mat2[5], b12 = mat2[6], b13 = mat2[7]; + var b20 = mat2[8], b21 = mat2[9], b22 = mat2[10], b23 = mat2[11]; + var b30 = mat2[12], b31 = mat2[13], b32 = mat2[14], b33 = mat2[15]; + + mat[0] = b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30; + mat[1] = b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31; + mat[2] = b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32; + mat[3] = b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33; + mat[4] = b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30; + mat[5] = b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31; + mat[6] = b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32; + mat[7] = b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33; + mat[8] = b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30; + mat[9] = b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31; + mat[10] = b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32; + mat[11] = b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33; + mat[12] = b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30; + mat[13] = b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31; + mat[14] = b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32; + mat[15] = b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33; + return this; + }; + + cc.getMat4MultiplyValue = function (pM1, pM2) { + var m1 = pM1.mat, m2 = pM2.mat; + var mat = new Float32Array(16); + + mat[0] = m1[0] * m2[0] + m1[4] * m2[1] + m1[8] * m2[2] + m1[12] * m2[3]; + mat[1] = m1[1] * m2[0] + m1[5] * m2[1] + m1[9] * m2[2] + m1[13] * m2[3]; + mat[2] = m1[2] * m2[0] + m1[6] * m2[1] + m1[10] * m2[2] + m1[14] * m2[3]; + mat[3] = m1[3] * m2[0] + m1[7] * m2[1] + m1[11] * m2[2] + m1[15] * m2[3]; + + mat[4] = m1[0] * m2[4] + m1[4] * m2[5] + m1[8] * m2[6] + m1[12] * m2[7]; + mat[5] = m1[1] * m2[4] + m1[5] * m2[5] + m1[9] * m2[6] + m1[13] * m2[7]; + mat[6] = m1[2] * m2[4] + m1[6] * m2[5] + m1[10] * m2[6] + m1[14] * m2[7]; + mat[7] = m1[3] * m2[4] + m1[7] * m2[5] + m1[11] * m2[6] + m1[15] * m2[7]; + + mat[8] = m1[0] * m2[8] + m1[4] * m2[9] + m1[8] * m2[10] + m1[12] * m2[11]; + mat[9] = m1[1] * m2[8] + m1[5] * m2[9] + m1[9] * m2[10] + m1[13] * m2[11]; + mat[10] = m1[2] * m2[8] + m1[6] * m2[9] + m1[10] * m2[10] + m1[14] * m2[11]; + mat[11] = m1[3] * m2[8] + m1[7] * m2[9] + m1[11] * m2[10] + m1[15] * m2[11]; + + mat[12] = m1[0] * m2[12] + m1[4] * m2[13] + m1[8] * m2[14] + m1[12] * m2[15]; + mat[13] = m1[1] * m2[12] + m1[5] * m2[13] + m1[9] * m2[14] + m1[13] * m2[15]; + mat[14] = m1[2] * m2[12] + m1[6] * m2[13] + m1[10] * m2[14] + m1[14] * m2[15]; + mat[15] = m1[3] * m2[12] + m1[7] * m2[13] + m1[11] * m2[14] + m1[15] * m2[15]; + + return mat; + }; + + /** + * Assigns the value of pIn to pOut + */ + cc.kmMat4Assign = function (pOut, pIn) { + if (pOut === pIn) { + cc.log("cc.kmMat4Assign(): pOut equals pIn"); + return pOut; + } + + var outArr = pOut.mat; + var inArr = pIn.mat; + + outArr[0] = inArr[0]; + outArr[1] = inArr[1]; + outArr[2] = inArr[2]; + outArr[3] = inArr[3]; + + outArr[4] = inArr[4]; + outArr[5] = inArr[5]; + outArr[6] = inArr[6]; + outArr[7] = inArr[7]; + + outArr[8] = inArr[8]; + outArr[9] = inArr[9]; + outArr[10] = inArr[10]; + outArr[11] = inArr[11]; + + outArr[12] = inArr[12]; + outArr[13] = inArr[13]; + outArr[14] = inArr[14]; + outArr[15] = inArr[15]; + return pOut; + }; + + /** + * Assigns the value of current matrix from mat4 + * @param {cc.math.Matrix4} mat4 + * @returns {cc.math.Matrix4} + */ + proto.assignFrom = function(mat4) { + if (this === mat4) { + cc.log("cc.mat.Matrix4.assignFrom(): mat4 equals current matrix"); + return this; + } + var outArr = this.mat, inArr = mat4.mat; + + outArr[0] = inArr[0]; + outArr[1] = inArr[1]; + outArr[2] = inArr[2]; + outArr[3] = inArr[3]; + + outArr[4] = inArr[4]; + outArr[5] = inArr[5]; + outArr[6] = inArr[6]; + outArr[7] = inArr[7]; + + outArr[8] = inArr[8]; + outArr[9] = inArr[9]; + outArr[10] = inArr[10]; + outArr[11] = inArr[11]; + + outArr[12] = inArr[12]; + outArr[13] = inArr[13]; + outArr[14] = inArr[14]; + outArr[15] = inArr[15]; + return this; + }; + + /** + * Returns true if current matrix equal mat4 (approximately) + * @param {cc.math.Matrix4} mat4 + * @returns {boolean} + */ + proto.equals = function(mat4) { + if (this === mat4) { + cc.log("cc.kmMat4AreEqual(): pMat1 and pMat2 are same object."); + return true; + } + var matA = this.mat, matB = mat4.mat, EPSILON = cc.math.EPSILON; + for (var i = 0; i < 16; i++) { + if (!(matA[i] + EPSILON > matB[i] && matA[i] - EPSILON < matB[i])) + return false; + } + return true; + }; + + /** + * Builds an X-axis rotation matrix and stores it in matrix, returns matrix, if matrix is null, create a new matrix + * @param {Number} radians + * @param {cc.math.Matrix4} [matrix] + * @returns {cc.math.Matrix4} + */ + cc.math.Matrix4.createByRotationX = function(radians, matrix) { //cc.kmMat4RotationX + /* + | 1 0 0 0 | + M = | 0 cos(A) -sin(A) 0 | + | 0 sin(A) cos(A) 0 | + | 0 0 0 1 | + */ + matrix = matrix || new cc.math.Matrix4(); + var mat = matrix.mat; + mat[0] = 1.0; + mat[3] = mat[2] = mat[1] = 0.0; + + mat[4] = 0.0; + mat[5] = Math.cos(radians); + mat[6] = Math.sin(radians); + mat[7] = 0.0; + + mat[8] = 0.0; + mat[9] = -Math.sin(radians); + mat[10] = Math.cos(radians); + mat[11] = 0.0; + + mat[14] = mat[13] = mat[12] = 0.0; + mat[15] = 1.0; + return matrix; + }; + + /** + * Builds a rotation matrix using the rotation around the Y-axis, The result is stored in matrix, matrix is returned. + * @param {Number} radians + * @param {cc.math.Matrix4} [matrix] + * @returns {*} + */ + cc.math.Matrix4.createByRotationY = function(radians, matrix) { // cc.kmMat4RotationY + /* + | cos(A) 0 sin(A) 0 | + M = | 0 1 0 0 | + | -sin(A) 0 cos(A) 0 | + | 0 0 0 1 | + */ + matrix = matrix || new cc.math.Matrix4(); + var mat = matrix.mat; + mat[0] = Math.cos(radians); + mat[1] = 0.0; + mat[2] = -Math.sin(radians); + mat[3] = 0.0; + + mat[7] = mat[6] = mat[4] = 0.0; + mat[5] = 1.0; + + mat[8] = Math.sin(radians); + mat[9] = 0.0; + mat[10] = Math.cos(radians); + mat[11] = 0.0; + + mat[14] = mat[13] = mat[12] = 0.0; + mat[15] = 1.0; + return matrix; + }; + + /** + * Builds a rotation matrix around the Z-axis. The resulting matrix is stored in matrix. matrix is returned. + * @param {Number} radians + * @param {cc.math.Matrix4} matrix + * @return {cc.math.Matrix4} + */ + cc.math.Matrix4.createByRotationZ = function(radians, matrix){ // cc.kmMat4RotationZ + /* + | cos(A) -sin(A) 0 0 | + M = | sin(A) cos(A) 0 0 | + | 0 0 1 0 | + | 0 0 0 1 | + */ + matrix = matrix || new cc.math.Matrix4(); + var mat = matrix.mat; + mat[0] = Math.cos(radians); + mat[1] = Math.sin(radians); + mat[3] = mat[2] = 0.0; + + mat[4] = -Math.sin(radians); + mat[5] = Math.cos(radians); + mat[7] = mat[6] = 0.0; + + mat[11] = mat[9] = mat[8] = 0.0; + mat[10] = 1.0; + + mat[14] = mat[13] = mat[12] = 0.0; + mat[15] = 1.0; + + return matrix; + }; + + /** + * Builds a rotation matrix from pitch, yaw and roll. The resulting matrix is stored in parameter matrix and returns. + * @param {Number} pitch + * @param {Number} yaw + * @param {Number} roll + * @param {cc.math.Matrix4} [matrix] if matrix is undefined, creates a new matrix. + * @returns {cc.math.Matrix4} + */ + cc.math.Matrix4.createByPitchYawRoll = function(pitch, yaw, roll, matrix) { + matrix = matrix || new cc.math.Matrix4(); + var cr = Math.cos(pitch), sr = Math.sin(pitch); + var cp = Math.cos(yaw), sp = Math.sin(yaw); + var cy = Math.cos(roll), sy = Math.sin(roll); + var srsp = sr * sp, crsp = cr * sp; + var mat = matrix.mat; + + mat[0] = cp * cy; + mat[4] = cp * sy; + mat[8] = -sp; + + mat[1] = srsp * cy - cr * sy; + mat[5] = srsp * sy + cr * cy; + mat[9] = sr * cp; + + mat[2] = crsp * cy + sr * sy; + mat[6] = crsp * sy - sr * cy; + mat[10] = cr * cp; + + mat[3] = mat[7] = mat[11] = 0.0; + mat[15] = 1.0; + return matrix; + }; + + /** + * Builds a matrix by a quaternion. + * @param {cc.math.Quaternion} quaternion + * @param {cc.math.Matrix4} [matrix] if matrix is undefined, creates a new matrix. + * @returns {cc.math.Matrix4} + */ + cc.math.Matrix4.createByQuaternion = function(quaternion, matrix) { + matrix = matrix || new cc.math.Matrix4(); + var mat = matrix.mat; + mat[0] = 1.0 - 2.0 * (quaternion.y * quaternion.y + quaternion.z * quaternion.z ); + mat[1] = 2.0 * (quaternion.x * quaternion.y + quaternion.z * quaternion.w); + mat[2] = 2.0 * (quaternion.x * quaternion.z - quaternion.y * quaternion.w); + mat[3] = 0.0; + + // Second row + mat[4] = 2.0 * ( quaternion.x * quaternion.y - quaternion.z * quaternion.w ); + mat[5] = 1.0 - 2.0 * ( quaternion.x * quaternion.x + quaternion.z * quaternion.z ); + mat[6] = 2.0 * (quaternion.z * quaternion.y + quaternion.x * quaternion.w ); + mat[7] = 0.0; + + // Third row + mat[8] = 2.0 * ( quaternion.x * quaternion.z + quaternion.y * quaternion.w ); + mat[9] = 2.0 * ( quaternion.y * quaternion.z - quaternion.x * quaternion.w ); + mat[10] = 1.0 - 2.0 * ( quaternion.x * quaternion.x + quaternion.y * quaternion.y ); + mat[11] = 0.0; + + // Fourth row + mat[14] = mat[13] = mat[12] = 0; + mat[15] = 1.0; + return matrix; + }; + + /** + * Build a 4x4 OpenGL transformation matrix using a 3x3 rotation matrix, and a 3d vector representing a translation. + * @param {cc.math.Matrix3} rotation + * @param {cc.math.Vec3} translation + * @param {cc.math.Matrix4} [matrix] if matrix is undefined, creates a new matrix. + * @returns {cc.math.Matrix4} + */ + cc.math.Matrix4.createByRotationTranslation = function(rotation, translation, matrix) { + matrix = matrix || new cc.math.Matrix4(); + var mat = matrix.mat, rMat = rotation.mat; + mat[0] = rMat[0]; + mat[1] = rMat[1]; + mat[2] = rMat[2]; + mat[3] = 0.0; + + mat[4] = rMat[3]; + mat[5] = rMat[4]; + mat[6] = rMat[5]; + mat[7] = 0.0; + + mat[8] = rMat[6]; + mat[9] = rMat[7]; + mat[10] = rMat[8]; + mat[11] = 0.0; + + mat[12] = translation.x; + mat[13] = translation.y; + mat[14] = translation.z; + mat[15] = 1.0; + return matrix; + }; + + /** + * Builds a scaling matrix + * @param {Number} x + * @param {Number} y + * @param {Number} z + * @param {cc.math.Matrix4} [matrix] if matrix is undefined, creates a new matrix. + * @returns {cc.math.Matrix4} + */ + cc.math.Matrix4.createByScale = function(x, y, z, matrix) { //cc.kmMat4Scaling + matrix = matrix || new cc.math.Matrix4(); + var mat = matrix.mat; + mat[0] = x; + mat[5] = y; + mat[10] = z; + mat[15] = 1.0; + mat[1] = mat[2] = mat[3] = mat[4] = mat[6] = mat[7] = + mat[8] = mat[9] = mat[11] = mat[12] = mat[13] = mat[14] = 0; + return matrix; + }; + + /** + * Builds a translation matrix. All other elements in the matrix + * will be set to zero except for the diagonal which is set to 1.0 + */ + cc.kmMat4Translation = function (pOut, x, y, z) { + //FIXME: Write a test for this + pOut.mat[0] = pOut.mat[5] = pOut.mat[10] = pOut.mat[15] = 1.0; + pOut.mat[1] = pOut.mat[2] = pOut.mat[3] = + pOut.mat[4] = pOut.mat[6] = pOut.mat[7] = + pOut.mat[8] = pOut.mat[9] = pOut.mat[11] = 0.0; + pOut.mat[12] = x; + pOut.mat[13] = y; + pOut.mat[14] = z; + return pOut; + }; + + /** + * Builds a translation matrix. + * @param {Number} x + * @param {Number} y + * @param {Number} z + * @param {cc.math.Matrix4} [matrix] if matrix is undefined, creates a new matrix. + * @returns {cc.math.Matrix4} + */ + cc.math.Matrix4.createByTranslation = function(x, y, z, matrix){ //cc.kmMat4Translation + matrix = matrix || new cc.math.Matrix4(); + matrix.identity(); + matrix.mat[12] = x; + matrix.mat[13] = y; + matrix.mat[14] = z; + return matrix; + }; + + /** + * Get the up vector from a matrix. + * @returns {cc.math.Vec3} + */ + proto.getUpVec3 = function() { + var mat = this.mat; + var ret = new cc.math.Vec3(mat[4],mat[5], mat[6]); + return ret.normalize(); + }; + + /** + * Extract the right vector from a 4x4 matrix. + * @returns {cc.math.Vec3} + */ + proto.getRightVec3 = function(){ + var mat = this.mat; + var ret = new cc.math.Vec3(mat[0],mat[1], mat[2]); + return ret.normalize(); + }; + + /** + * Extract the forward vector from a 4x4 matrix. + * @returns {cc.math.Vec3} + */ + proto.getForwardVec3 = function() { + var mat = this.mat; + var ret = new cc.math.Vec3(mat[8],mat[9], mat[10]); + return ret.normalize(); + }; + + /** + * Creates a perspective projection matrix in the + * same way as gluPerspective + */ + cc.kmMat4PerspectiveProjection = function (pOut, fovY, aspect, zNear, zFar) { + var r = cc.degreesToRadians(fovY / 2); + var deltaZ = zFar - zNear; + var s = Math.sin(r); + + if (deltaZ === 0 || s === 0 || aspect === 0) + return null; + + //cos(r) / sin(r) = cot(r) + var cotangent = Math.cos(r) / s; + pOut.identity(); + pOut.mat[0] = cotangent / aspect; + pOut.mat[5] = cotangent; + pOut.mat[10] = -(zFar + zNear) / deltaZ; + pOut.mat[11] = -1; + pOut.mat[14] = -2 * zNear * zFar / deltaZ; + pOut.mat[15] = 0; + + return pOut; + }; + + /** + * Creates a perspective projection matrix in the same way as gluPerspective + * @param {Number} fovY + * @param {Number} aspect + * @param {Number} zNear + * @param {Number} zFar + * @returns {cc.math.Matrix4|Null} + */ + cc.math.Matrix4.createPerspectiveProjection = function(fovY, aspect, zNear, zFar){ + var r = cc.degreesToRadians(fovY / 2), deltaZ = zFar - zNear; + var s = Math.sin(r); + + if (deltaZ === 0 || s === 0 || aspect === 0) + return null; + + //cos(r) / sin(r) = cot(r) + var cotangent = Math.cos(r) / s; + var matrix = new cc.math.Matrix4(), mat = matrix.mat; + matrix.identity(); + mat[0] = cotangent / aspect; + mat[5] = cotangent; + mat[10] = -(zFar + zNear) / deltaZ; + mat[11] = -1; + mat[14] = -2 * zNear * zFar / deltaZ; + mat[15] = 0; + return matrix; + }; + + /** Creates an orthographic projection matrix like glOrtho */ + cc.kmMat4OrthographicProjection = function (pOut, left, right, bottom, top, nearVal, farVal) { + pOut.identity(); + pOut.mat[0] = 2 / (right - left); + pOut.mat[5] = 2 / (top - bottom); + pOut.mat[10] = -2 / (farVal - nearVal); + pOut.mat[12] = -((right + left) / (right - left)); + pOut.mat[13] = -((top + bottom) / (top - bottom)); + pOut.mat[14] = -((farVal + nearVal) / (farVal - nearVal)); + return pOut; + }; + + /** + * Creates an orthographic projection matrix like glOrtho + * @param {Number} left + * @param {Number} right + * @param {Number} bottom + * @param {Number} top + * @param {Number} nearVal + * @param {Number} farVal + * @returns {cc.math.Matrix4} + */ + cc.math.Matrix4.createOrthographicProjection = function (left, right, bottom, top, nearVal, farVal) { + var matrix = new cc.math.Matrix4(), mat = matrix.mat; + matrix.identity(); + mat[0] = 2 / (right - left); + mat[5] = 2 / (top - bottom); + mat[10] = -2 / (farVal - nearVal); + mat[12] = -((right + left) / (right - left)); + mat[13] = -((top + bottom) / (top - bottom)); + mat[14] = -((farVal + nearVal) / (farVal - nearVal)); + return matrix; + }; + + /** + * Builds a translation matrix in the same way as gluLookAt() + * the resulting matrix is stored in pOut. pOut is returned. + */ + cc.kmMat4LookAt = function (pOut, pEye, pCenter, pUp) { + var f = new cc.math.Vec3(pCenter), up = new cc.math.Vec3(pUp); + f.subtract(pEye); + f.normalize(); + up.normalize(); + + var s = new cc.math.Vec3(f); + s.cross(up); + s.normalize(); + + var u = new cc.math.Vec3(s); + u.cross(f); + s.normalize(); + + pOut.identity(); + + pOut.mat[0] = s.x; + pOut.mat[4] = s.y; + pOut.mat[8] = s.z; + + pOut.mat[1] = u.x; + pOut.mat[5] = u.y; + pOut.mat[9] = u.z; + + pOut.mat[2] = -f.x; + pOut.mat[6] = -f.y; + pOut.mat[10] = -f.z; + + var translate = cc.math.Matrix4.createByTranslation(-pEye.x, -pEye.y, -pEye.z); + pOut.multiply(translate); + return pOut; + }; + + var tempMatrix = new cc.math.Matrix4(); // an internal matrix + proto.lookAt = function(eyeVec, centerVec, upVec) { + var f = new cc.math.Vec3(centerVec), up = new cc.math.Vec3(upVec), mat = this.mat; + f.subtract(eyeVec); + f.normalize(); + up.normalize(); + + var s = new cc.math.Vec3(f); + s.cross(up); + s.normalize(); + + var u = new cc.math.Vec3(s); + u.cross(f); + s.normalize(); + + this.identity(); + mat[0] = s.x; + mat[4] = s.y; + mat[8] = s.z; + + mat[1] = u.x; + mat[5] = u.y; + mat[9] = u.z; + + mat[2] = -f.x; + mat[6] = -f.y; + mat[10] = -f.z; + + tempMatrix = cc.math.Matrix4.createByTranslation(-eyeVec.x, -eyeVec.y, -eyeVec.z, tempMatrix); + this.multiply(tempMatrix); + return this; + }; + + /** + * Build a rotation matrix from an axis and an angle. Result is stored in pOut. + * pOut is returned. + */ + cc.kmMat4RotationAxisAngle = function (pOut, axis, radians) { + var rcos = Math.cos(radians), rsin = Math.sin(radians); + + var normalizedAxis = new cc.math.Vec3(axis); + normalizedAxis.normalize(); + + pOut.mat[0] = rcos + normalizedAxis.x * normalizedAxis.x * (1 - rcos); + pOut.mat[1] = normalizedAxis.z * rsin + normalizedAxis.y * normalizedAxis.x * (1 - rcos); + pOut.mat[2] = -normalizedAxis.y * rsin + normalizedAxis.z * normalizedAxis.x * (1 - rcos); + pOut.mat[3] = 0.0; + + pOut.mat[4] = -normalizedAxis.z * rsin + normalizedAxis.x * normalizedAxis.y * (1 - rcos); + pOut.mat[5] = rcos + normalizedAxis.y * normalizedAxis.y * (1 - rcos); + pOut.mat[6] = normalizedAxis.x * rsin + normalizedAxis.z * normalizedAxis.y * (1 - rcos); + pOut.mat[7] = 0.0; + + pOut.mat[8] = normalizedAxis.y * rsin + normalizedAxis.x * normalizedAxis.z * (1 - rcos); + pOut.mat[9] = -normalizedAxis.x * rsin + normalizedAxis.y * normalizedAxis.z * (1 - rcos); + pOut.mat[10] = rcos + normalizedAxis.z * normalizedAxis.z * (1 - rcos); + pOut.mat[11] = 0.0; + + pOut.mat[12] = 0.0; + pOut.mat[13] = 0.0; + pOut.mat[14] = 0.0; + pOut.mat[15] = 1.0; + + return pOut; + }; + + /** + * Build a rotation matrix from an axis and an angle. + * @param {cc.math.Vec3} axis + * @param {Number} radians + * @param {cc.math.Matrix4} [matrix] + * @returns {cc.math.Matrix4} + */ + cc.math.Matrix4.createByAxisAndAngle = function(axis, radians, matrix) { + matrix = matrix || new cc.math.Matrix4(); + var mat = this.mat, rcos = Math.cos(radians), rsin = Math.sin(radians) ; + + var normalizedAxis = new cc.math.Vec3(axis); + normalizedAxis.normalize(); + + mat[0] = rcos + normalizedAxis.x * normalizedAxis.x * (1 - rcos); + mat[1] = normalizedAxis.z * rsin + normalizedAxis.y * normalizedAxis.x * (1 - rcos); + mat[2] = -normalizedAxis.y * rsin + normalizedAxis.z * normalizedAxis.x * (1 - rcos); + mat[3] = 0.0; + + mat[4] = -normalizedAxis.z * rsin + normalizedAxis.x * normalizedAxis.y * (1 - rcos); + mat[5] = rcos + normalizedAxis.y * normalizedAxis.y * (1 - rcos); + mat[6] = normalizedAxis.x * rsin + normalizedAxis.z * normalizedAxis.y * (1 - rcos); + mat[7] = 0.0; + + mat[8] = normalizedAxis.y * rsin + normalizedAxis.x * normalizedAxis.z * (1 - rcos); + mat[9] = -normalizedAxis.x * rsin + normalizedAxis.y * normalizedAxis.z * (1 - rcos); + mat[10] = rcos + normalizedAxis.z * normalizedAxis.z * (1 - rcos); + mat[11] = 0.0; + + mat[12] = mat[13] = mat[14] = 0.0; + mat[15] = 1.0; + return matrix; + }; + + /** + * Extract a 3x3 rotation matrix from the input 4x4 transformation. + * @returns {cc.math.Matrix3} + */ + proto.extractRotation = function(){ + var matrix = new cc.math.Matrix3(), mat4 = this.mat, mat3 = matrix.mat; + mat3[0] = mat4[0]; + mat3[1] = mat4[1]; + mat3[2] = mat4[2]; + + mat3[3] = mat4[4]; + mat3[4] = mat4[5]; + mat3[5] = mat4[6]; + + mat3[6] = mat4[8]; + mat3[7] = mat4[9]; + mat3[8] = mat4[10]; + return matrix; + }; + + proto.extractPlane = function(planeType) { + var plane = new cc.math.Plane(), mat = this.mat; + switch (planeType) { + case cc.math.Plane.RIGHT: + plane.a = mat[3] - mat[0]; + plane.b = mat[7] - mat[4]; + plane.c = mat[11] - mat[8]; + plane.d = mat[15] - mat[12]; + break; + case cc.math.Plane.LEFT: + plane.a = mat[3] + mat[0]; + plane.b = mat[7] + mat[4]; + plane.c = mat[11] + mat[8]; + plane.d = mat[15] + mat[12]; + break; + case cc.math.Plane.BOTTOM: + plane.a = mat[3] + mat[1]; + plane.b = mat[7] + mat[5]; + plane.c = mat[11] + mat[9]; + plane.d = mat[15] + mat[13]; + break; + case cc.math.Plane.TOP: + plane.a = mat[3] - mat[1]; + plane.b = mat[7] - mat[5]; + plane.c = mat[11] - mat[9]; + plane.d = mat[15] - mat[13]; + break; + case cc.math.Plane.FAR: + plane.a = mat[3] - mat[2]; + plane.b = mat[7] - mat[6]; + plane.c = mat[11] - mat[10]; + plane.d = mat[15] - mat[14]; + break; + case cc.math.Plane.NEAR: + plane.a = mat[3] + mat[2]; + plane.b = mat[7] + mat[6]; + plane.c = mat[11] + mat[10]; + plane.d = mat[15] + mat[14]; + break; + default: + cc.log("cc.math.Matrix4.extractPlane: Invalid plane index"); + break; + } + + var t = Math.sqrt(plane.a * plane.a + plane.b * plane.b + plane.c * plane.c); + plane.a /= t; + plane.b /= t; + plane.c /= t; + plane.d /= t; + return plane; + }; + + /** + * Take the rotation from a 4x4 transformation matrix, and return it as an axis and an angle (in radians) + * @returns {*|{axis: cc.math.Vec3, angle: number}} + */ + proto.toAxisAndAngle = function() { + /*Surely not this easy?*/ + var rotation = this.extractRotation(); + var temp = cc.math.Quaternion.rotationMatrix(rotation); + return temp.toAxisAndAngle(); + }; +})(cc); diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/mat4SIMD.js b/frameworks/cocos2d-html5/cocos2d/kazmath/mat4SIMD.js new file mode 100644 index 0000000..f716de7 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/mat4SIMD.js @@ -0,0 +1,428 @@ +/** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2008, Luke Benstead. + 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. + + 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. + */ + +(function(cc) { + var proto = cc.math.Matrix4.prototype; + + cc.kmMat4InverseSIMD = function (pOut, pM) { + pOut = pM.inverseSIMD(); + return pOut; + }; + + proto.inverseSIMD = function(){ + var inv = new cc.math.Matrix4(); + var src = this.mat; + var dest = inv.mat; + var src0, src1, src2, src3; + var row0, row1, row2, row3; + var tmp1; + var minor0, minor1, minor2, minor3; + var det; + + // Load the 4 rows + var src0 = SIMD.float32x4.load(src, 0); + var src1 = SIMD.float32x4.load(src, 4); + var src2 = SIMD.float32x4.load(src, 8); + var src3 = SIMD.float32x4.load(src, 12); + + // Transpose the source matrix. Sort of. Not a true transpose operation + + tmp1 = SIMD.float32x4.shuffle(src0, src1, 0, 1, 4, 5); + row1 = SIMD.float32x4.shuffle(src2, src3, 0, 1, 4, 5); + row0 = SIMD.float32x4.shuffle(tmp1, row1, 0, 2, 4, 6); + row1 = SIMD.float32x4.shuffle(row1, tmp1, 1, 3, 5, 7); + + tmp1 = SIMD.float32x4.shuffle(src0, src1, 2, 3, 6, 7); + row3 = SIMD.float32x4.shuffle(src2, src3, 2, 3, 6, 7); + row2 = SIMD.float32x4.shuffle(tmp1, row3, 0, 2, 4, 6); + row3 = SIMD.float32x4.shuffle(row3, tmp1, 1, 3, 5, 7); + + // ---- + tmp1 = SIMD.float32x4.mul(row2, row3); + tmp1 = SIMD.float32x4.swizzle(tmp1, 1, 0, 3, 2); // 0xB1 = 10110001 + minor0 = SIMD.float32x4.mul(row1, tmp1); + minor1 = SIMD.float32x4.mul(row0, tmp1); + tmp1 = SIMD.float32x4.swizzle(tmp1, 2, 3, 0, 1); // 0x4E = 01001110 + minor0 = SIMD.float32x4.sub(SIMD.float32x4.mul(row1, tmp1), minor0); + minor1 = SIMD.float32x4.sub(SIMD.float32x4.mul(row0, tmp1), minor1); + minor1 = SIMD.float32x4.swizzle(minor1, 2, 3, 0, 1); // 0x4E = 01001110 + + // ---- + tmp1 = SIMD.float32x4.mul(row1, row2); + tmp1 = SIMD.float32x4.swizzle(tmp1, 1, 0, 3, 2); // 0xB1 = 10110001 + minor0 = SIMD.float32x4.add(SIMD.float32x4.mul(row3, tmp1), minor0); + minor3 = SIMD.float32x4.mul(row0, tmp1); + tmp1 = SIMD.float32x4.swizzle(tmp1, 2, 3, 0, 1); // 0x4E = 01001110 + minor0 = SIMD.float32x4.sub(minor0, SIMD.float32x4.mul(row3, tmp1)); + minor3 = SIMD.float32x4.sub(SIMD.float32x4.mul(row0, tmp1), minor3); + minor3 = SIMD.float32x4.swizzle(minor3, 2, 3, 0, 1); // 0x4E = 01001110 + + // ---- + tmp1 = SIMD.float32x4.mul(SIMD.float32x4.swizzle(row1, 2, 3, 0, 1), row3); // 0x4E = 01001110 + tmp1 = SIMD.float32x4.swizzle(tmp1, 1, 0, 3, 2); // 0xB1 = 10110001 + row2 = SIMD.float32x4.swizzle(row2, 2, 3, 0, 1); // 0x4E = 01001110 + minor0 = SIMD.float32x4.add(SIMD.float32x4.mul(row2, tmp1), minor0); + minor2 = SIMD.float32x4.mul(row0, tmp1); + tmp1 = SIMD.float32x4.swizzle(tmp1, 2, 3, 0, 1); // 0x4E = 01001110 + minor0 = SIMD.float32x4.sub(minor0, SIMD.float32x4.mul(row2, tmp1)); + minor2 = SIMD.float32x4.sub(SIMD.float32x4.mul(row0, tmp1), minor2); + minor2 = SIMD.float32x4.swizzle(minor2, 2, 3, 0, 1); // 0x4E = 01001110 + + // ---- + tmp1 = SIMD.float32x4.mul(row0, row1); + tmp1 = SIMD.float32x4.swizzle(tmp1, 1, 0, 3, 2); // 0xB1 = 10110001 + minor2 = SIMD.float32x4.add(SIMD.float32x4.mul(row3, tmp1), minor2); + minor3 = SIMD.float32x4.sub(SIMD.float32x4.mul(row2, tmp1), minor3); + tmp1 = SIMD.float32x4.swizzle(tmp1, 2, 3, 0, 1); // 0x4E = 01001110 + minor2 = SIMD.float32x4.sub(SIMD.float32x4.mul(row3, tmp1), minor2); + minor3 = SIMD.float32x4.sub(minor3, SIMD.float32x4.mul(row2, tmp1)); + + // ---- + tmp1 = SIMD.float32x4.mul(row0, row3); + tmp1 = SIMD.float32x4.swizzle(tmp1, 1, 0, 3, 2); // 0xB1 = 10110001 + minor1 = SIMD.float32x4.sub(minor1, SIMD.float32x4.mul(row2, tmp1)); + minor2 = SIMD.float32x4.add(SIMD.float32x4.mul(row1, tmp1), minor2); + tmp1 = SIMD.float32x4.swizzle(tmp1, 2, 3, 0, 1); // 0x4E = 01001110 + minor1 = SIMD.float32x4.add(SIMD.float32x4.mul(row2, tmp1), minor1); + minor2 = SIMD.float32x4.sub(minor2, SIMD.float32x4.mul(row1, tmp1)); + + // ---- + tmp1 = SIMD.float32x4.mul(row0, row2); + tmp1 = SIMD.float32x4.swizzle(tmp1, 1, 0, 3, 2); // 0xB1 = 10110001 + minor1 = SIMD.float32x4.add(SIMD.float32x4.mul(row3, tmp1), minor1); + minor3 = SIMD.float32x4.sub(minor3, SIMD.float32x4.mul(row1, tmp1)); + tmp1 = SIMD.float32x4.swizzle(tmp1, 2, 3, 0, 1); // 0x4E = 01001110 + minor1 = SIMD.float32x4.sub(minor1, SIMD.float32x4.mul(row3, tmp1)); + minor3 = SIMD.float32x4.add(SIMD.float32x4.mul(row1, tmp1), minor3); + + // Compute determinant + det = SIMD.float32x4.mul(row0, minor0); + det = SIMD.float32x4.add(SIMD.float32x4.swizzle(det, 2, 3, 0, 1), det); // 0x4E = 01001110 + det = SIMD.float32x4.add(SIMD.float32x4.swizzle(det, 1, 0, 3, 2), det); // 0xB1 = 10110001 + tmp1 = SIMD.float32x4.reciprocalApproximation(det); + det = SIMD.float32x4.sub(SIMD.float32x4.add(tmp1, tmp1), SIMD.float32x4.mul(det, SIMD.float32x4.mul(tmp1, tmp1))); + det = SIMD.float32x4.swizzle(det, 0, 0, 0, 0); + + // Compute final values by multiplying with 1/det + minor0 = SIMD.float32x4.mul(det, minor0); + minor1 = SIMD.float32x4.mul(det, minor1); + minor2 = SIMD.float32x4.mul(det, minor2); + minor3 = SIMD.float32x4.mul(det, minor3); + + SIMD.float32x4.store(dest, 0, minor0); + SIMD.float32x4.store(dest, 4, minor1); + SIMD.float32x4.store(dest, 8, minor2); + SIMD.float32x4.store(dest, 12, minor3); + + return inv; + }; + + var identityMatrix = new cc.math.Matrix4().identity(); + proto.isIdentitySIMD = function () { + var inx4 = SIMD.float32x4.load(this.mat, 0); + var identityx4 = SIMD.float32x4.load(identityMatrix.mat, 0); + var ret = SIMD.float32x4.equal(inx4, identityx4); + if(ret.signMask === 0x00) + return false; + + inx4 = SIMD.float32x4.load(this.mat, 4); + identityx4 = SIMD.float32x4.load(identityMatrix.mat, 4); + ret = SIMD.float32x4.equal(inx4, identityx4); + if(ret.signMask === 0x00) + return false; + + inx4 = SIMD.float32x4.load(this.mat, 8); + identityx4 = SIMD.float32x4.load(identityMatrix.mat, 8); + ret = SIMD.float32x4.equal(inx4, identityx4); + if(ret.signMask === 0x00) + return false; + + inx4 = SIMD.float32x4.load(this.mat, 12); + identityx4 = SIMD.float32x4.load(identityMatrix.mat, 12); + ret = SIMD.float32x4.equal(inx4, identityx4); + if(ret.signMask === 0x00) + return false; + return true; + }; + + proto.transposeSIMD = function () { + var outArr = this.mat, inArr = this.mat; + var src0 = SIMD.float32x4.load(inArr, 0); + var src1 = SIMD.float32x4.load(inArr, 4); + var src2 = SIMD.float32x4.load(inArr, 8); + var src3 = SIMD.float32x4.load(inArr, 12); + var dst0; + var dst1; + var dst2; + var dst3; + var tmp01; + var tmp23; + + tmp01 = SIMD.float32x4.shuffle(src0, src1, 0, 1, 4, 5); + tmp23 = SIMD.float32x4.shuffle(src2, src3, 0, 1, 4, 5); + dst0 = SIMD.float32x4.shuffle(tmp01, tmp23, 0, 2, 4, 6); + dst1 = SIMD.float32x4.shuffle(tmp01, tmp23, 1, 3, 5, 7); + + tmp01 = SIMD.float32x4.shuffle(src0, src1, 2, 3, 6, 7); + tmp23 = SIMD.float32x4.shuffle(src2, src3, 2, 3, 6, 7); + dst2 = SIMD.float32x4.shuffle(tmp01, tmp23, 0, 2, 4, 6); + dst3 = SIMD.float32x4.shuffle(tmp01, tmp23, 1, 3, 5, 7); + + SIMD.float32x4.store(outArr, 0, dst0); + SIMD.float32x4.store(outArr, 4, dst1); + SIMD.float32x4.store(outArr, 8, dst2); + SIMD.float32x4.store(outArr, 12, dst3); + return this; + }; + + cc.kmMat4MultiplySIMD = function (pOut, pM1, pM2) { + pOut = new cc.math.Matrix4(pM1); + return pOut.multiplySIMD(pM2); + }; + + proto.multiplySIMD = function(mat4) { + var a = this.mat; + var b = mat4.mat; + var out = this.mat; + + var a0 = SIMD.float32x4.load(a,0); + var a1 = SIMD.float32x4.load(a,4); + var a2 = SIMD.float32x4.load(a,8); + var a3 = SIMD.float32x4.load(a,12); + var b0 = SIMD.float32x4.load(b, 0); + SIMD.float32x4.store(out, 0, SIMD.float32x4.add( + SIMD.float32x4.mul( + SIMD.float32x4.swizzle(b0, 0, 0, 0, 0), a0), + SIMD.float32x4.add( + SIMD.float32x4.mul(SIMD.float32x4.swizzle(b0, 1, 1, 1, 1), a1), + SIMD.float32x4.add( + SIMD.float32x4.mul(SIMD.float32x4.swizzle(b0, 2, 2, 2, 2), a2), + SIMD.float32x4.mul(SIMD.float32x4.swizzle(b0, 3, 3, 3, 3), a3))))); + var b1 = SIMD.float32x4.load(b, 4); + SIMD.float32x4.store(out, 4, SIMD.float32x4.add( + SIMD.float32x4.mul( + SIMD.float32x4.swizzle(b1, 0, 0, 0, 0), a0), + SIMD.float32x4.add( + SIMD.float32x4.mul(SIMD.float32x4.swizzle(b1, 1, 1, 1, 1), a1), + SIMD.float32x4.add( + SIMD.float32x4.mul(SIMD.float32x4.swizzle(b1, 2, 2, 2, 2), a2), + SIMD.float32x4.mul(SIMD.float32x4.swizzle(b1, 3, 3, 3, 3), a3))))); + var b2 = SIMD.float32x4.load(b, 8); + SIMD.float32x4.store(out, 8, SIMD.float32x4.add( + SIMD.float32x4.mul( + SIMD.float32x4.swizzle(b2, 0, 0, 0, 0), a0), + SIMD.float32x4.add( + SIMD.float32x4.mul(SIMD.float32x4.swizzle(b2, 1, 1, 1, 1), a1), + SIMD.float32x4.add( + SIMD.float32x4.mul(SIMD.float32x4.swizzle(b2, 2, 2, 2, 2), a2), + SIMD.float32x4.mul(SIMD.float32x4.swizzle(b2, 3, 3, 3, 3), a3))))); + var b3 = SIMD.float32x4.load(b, 12); + SIMD.float32x4.store(out, 12, SIMD.float32x4.add( + SIMD.float32x4.mul( + SIMD.float32x4.swizzle(b3, 0, 0, 0, 0), a0), + SIMD.float32x4.add( + SIMD.float32x4.mul(SIMD.float32x4.swizzle(b3, 1, 1, 1, 1), a1), + SIMD.float32x4.add( + SIMD.float32x4.mul(SIMD.float32x4.swizzle(b3, 2, 2, 2, 2), a2), + SIMD.float32x4.mul(SIMD.float32x4.swizzle(b3, 3, 3, 3, 3), a3))))); + + return this; + }; + + cc.getMat4MultiplyValueSIMD = function (pM1, pM2) { + var mat = new cc.math.Matrix4(pM1); + return mat.multiplySIMD(pM2); + }; + + cc.kmMat4AssignSIMD = function (pOut, pIn) { + if(pOut == pIn) { + cc.log("cc.kmMat4Assign(): pOut equals pIn");//TODO: ADD SIMD? + return pOut; + } + + return pOut.assignFromSIMD(pIn); + }; + + proto.assignFromSIMD = function (mat4) { + if(this == mat4) { + cc.log("cc.mat.Matrix4.assignFrom(): mat4 equals current matrix");//TODO: ADD SIMD? + return this; + } + + var outArr = this.mat; + var inArr = mat4.mat; + + SIMD.float32x4.store(outArr, 0, SIMD.float32x4.load(inArr, 0)); + SIMD.float32x4.store(outArr, 4, SIMD.float32x4.load(inArr, 4)); + SIMD.float32x4.store(outArr, 8, SIMD.float32x4.load(inArr, 8)); + SIMD.float32x4.store(outArr, 12, SIMD.float32x4.load(inArr, 12)); + + return this; + }; + + proto.equalsSIMD = function (mat4) { + if(this === mat4){ + cc.log("cc.kmMat4AreEqual(): pMat1 and pMat2 are same object."); + return true; + } + var m10 = SIMD.float32x4.load(this.mat, 0); + var m20 = SIMD.float32x4.load(mat4.mat, 0); + + var epsilon = SIMD.float32x4.splat(cc.math.EPSILON); + + var ret = SIMD.float32x4.lessThanOrEqual(SIMD.float32x4.abs(SIMD.float32x4.sub(m10, m20)), epsilon); + if (ret.signMask === 0) + return false; + + var m11 = SIMD.float32x4.load(this.mat, 4); + var m21 = SIMD.float32x4.load(mat4.mat, 4); + ret = SIMD.float32x4.lessThanOrEqual(SIMD.float32x4.abs(SIMD.float32x4.sub(m11, m21)), epsilon); + if (ret.signMask === 0) + return false; + + var m12 = SIMD.float32x4.load(this.mat, 8); + var m22 = SIMD.float32x4.load(mat4.mat, 8); + ret = SIMD.float32x4.lessThanOrEqual(SIMD.float32x4.abs(SIMD.float32x4.sub(m12, m22)), epsilon); + if (ret.signMask === 0) + return false; + + var m13 = SIMD.float32x4.load(this.mat, 12); + var m23 = SIMD.float32x4.load(mat4.mat, 12); + ret = SIMD.float32x4.lessThanOrEqual(SIMD.float32x4.abs(SIMD.float32x4.sub(m13, m23)), epsilon); + if (ret.signMask === 0) + return false; + return true; + }; + + cc.kmMat4LookAtSIMD = function (pOut, pEye, pCenter, pUp) { + return pOut.lookAtSIMD(pEye, pCenter, pUp); + }; + + proto.lookAtSIMD = function(eyeVec, centerVec, upVec) { + var out = this.mat; + + var center = SIMD.float32x4(centerVec.x, centerVec.y, centerVec.z, 0.0); + var eye = SIMD.float32x4(eyeVec.x, eyeVec.y, eyeVec.z, 0.0); + var up = SIMD.float32x4(upVec.x, upVec.y, upVec.z, 0.0); + + // cc.kmVec3Subtract(f, pCenter, pEye); + var f = SIMD.float32x4.sub(center, eye); + // cc.kmVec3Normalize(f, f); + var tmp = SIMD.float32x4.mul(f, f); + tmp = SIMD.float32x4.add(tmp, SIMD.float32x4.add(SIMD.float32x4.swizzle(tmp, 1, 2, 0, 3), SIMD.float32x4.swizzle(tmp, 2, 0, 1, 3))); + f = SIMD.float32x4.mul(f, SIMD.float32x4.reciprocalSqrtApproximation(tmp)); + + // cc.kmVec3Assign(up, pUp); + // cc.kmVec3Normalize(up, up); + tmp = SIMD.float32x4.mul(up, up); + tmp = SIMD.float32x4.add(tmp, SIMD.float32x4.add(SIMD.float32x4.swizzle(tmp, 1, 2, 0, 3), SIMD.float32x4.swizzle(tmp, 2, 0, 1, 3))); + up = SIMD.float32x4.mul(up, SIMD.float32x4.reciprocalSqrtApproximation(tmp)); + + // cc.kmVec3Cross(s, f, up); + var s = SIMD.float32x4.sub(SIMD.float32x4.mul(SIMD.float32x4.swizzle(f, 1, 2, 0, 3), SIMD.float32x4.swizzle(up, 2, 0, 1, 3)), + SIMD.float32x4.mul(SIMD.float32x4.swizzle(f, 2, 0, 1, 3), SIMD.float32x4.swizzle(up, 1, 2, 0, 3))); + // cc.kmVec3Normalize(s, s); + tmp = SIMD.float32x4.mul(s, s); + tmp = SIMD.float32x4.add(tmp, SIMD.float32x4.add(SIMD.float32x4.swizzle(tmp, 1, 2, 0, 3), SIMD.float32x4.swizzle(tmp, 2, 0, 1, 3))); + s = SIMD.float32x4.mul(s, SIMD.float32x4.reciprocalSqrtApproximation(tmp)); + + // cc.kmVec3Cross(u, s, f); + var u = SIMD.float32x4.sub(SIMD.float32x4.mul(SIMD.float32x4.swizzle(s, 1, 2, 0, 3), SIMD.float32x4.swizzle(f, 2, 0, 1, 3)), + SIMD.float32x4.mul(SIMD.float32x4.swizzle(s, 2, 0, 1, 3), SIMD.float32x4.swizzle(f, 1, 2, 0, 3))); + // cc.kmVec3Normalize(s, s); + tmp = SIMD.float32x4.mul(s, s); + tmp = SIMD.float32x4.add(tmp, SIMD.float32x4.add(SIMD.float32x4.swizzle(tmp, 1, 2, 0, 3), SIMD.float32x4.swizzle(tmp, 2, 0, 1, 3))); + s = SIMD.float32x4.mul(s, SIMD.float32x4.reciprocalSqrtApproximation(tmp)); + + //cc.kmMat4Identity(pOut); + //pOut.mat[0] = s.x; + //pOut.mat[4] = s.y; + //pOut.mat[8] = s.z; + //pOut.mat[1] = u.x; + //pOut.mat[5] = u.y; + //pOut.mat[9] = u.z; + //pOut.mat[2] = -f.x; + //pOut.mat[6] = -f.y; + //pOut.mat[10] = -f.z; + var zero = SIMD.float32x4.splat(0.0); + f = SIMD.float32x4.neg(f); + var tmp01 = SIMD.float32x4.shuffle(s, u, 0, 1, 4, 5); + var tmp23 = SIMD.float32x4.shuffle(f, zero, 0, 1, 4, 5); + var a0 = SIMD.float32x4.shuffle(tmp01, tmp23, 0, 2, 4, 6); + var a1 = SIMD.float32x4.shuffle(tmp01, tmp23, 1, 3, 5, 7); + + var tmp01 = SIMD.float32x4.shuffle(s, u, 2, 3, 6, 7); + var tmp23 = SIMD.float32x4.shuffle(f, zero, 2, 3, 6, 7); + var a2 = SIMD.float32x4.shuffle(tmp01, tmp23, 0, 2, 4, 6); + var a3 = SIMD.float32x4(0.0, 0.0, 0.0, 1.0); + + // cc.kmMat4Translation(translate, -pEye.x, -pEye.y, -pEye.z); + var b0 = SIMD.float32x4(1.0, 0.0, 0.0, 0.0); + var b1 = SIMD.float32x4(0.0, 1.0, 0.0, 0.0); + var b2 = SIMD.float32x4(0.0, 0.0, 1.0, 0.0); + var b3 = SIMD.float32x4.neg(eye); + b3 = SIMD.float32x4.withW(b3, 1.0); + + // cc.kmMat4Multiply(pOut, pOut, translate); + SIMD.float32x4.store(out, 0, SIMD.float32x4.add( + SIMD.float32x4.mul( + SIMD.float32x4.swizzle(b0, 0, 0, 0, 0), a0), + SIMD.float32x4.add( + SIMD.float32x4.mul(SIMD.float32x4.swizzle(b0, 1, 1, 1, 1), a1), + SIMD.float32x4.add( + SIMD.float32x4.mul(SIMD.float32x4.swizzle(b0, 2, 2, 2, 2), a2), + SIMD.float32x4.mul(SIMD.float32x4.swizzle(b0, 3, 3, 3, 3), a3))))); + SIMD.float32x4.store(out, 4, SIMD.float32x4.add( + SIMD.float32x4.mul( + SIMD.float32x4.swizzle(b1, 0, 0, 0, 0), a0), + SIMD.float32x4.add( + SIMD.float32x4.mul(SIMD.float32x4.swizzle(b1, 1, 1, 1, 1), a1), + SIMD.float32x4.add( + SIMD.float32x4.mul(SIMD.float32x4.swizzle(b1, 2, 2, 2, 2), a2), + SIMD.float32x4.mul(SIMD.float32x4.swizzle(b1, 3, 3, 3, 3), a3))))); + SIMD.float32x4.store(out, 8, SIMD.float32x4.add( + SIMD.float32x4.mul( + SIMD.float32x4.swizzle(b2, 0, 0, 0, 0), a0), + SIMD.float32x4.add( + SIMD.float32x4.mul(SIMD.float32x4.swizzle(b2, 1, 1, 1, 1), a1), + SIMD.float32x4.add( + SIMD.float32x4.mul(SIMD.float32x4.swizzle(b2, 2, 2, 2, 2), a2), + SIMD.float32x4.mul(SIMD.float32x4.swizzle(b2, 3, 3, 3, 3), a3))))); + SIMD.float32x4.store(out, 12, SIMD.float32x4.add( + SIMD.float32x4.mul( + SIMD.float32x4.swizzle(b3, 0, 0, 0, 0), a0), + SIMD.float32x4.add( + SIMD.float32x4.mul(SIMD.float32x4.swizzle(b3, 1, 1, 1, 1), a1), + SIMD.float32x4.add( + SIMD.float32x4.mul(SIMD.float32x4.swizzle(b3, 2, 2, 2, 2), a2), + SIMD.float32x4.mul(SIMD.float32x4.swizzle(b3, 3, 3, 3, 3), a3))))); + return this; + }; + + +})(cc); diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/plane.js b/frameworks/cocos2d-html5/cocos2d/kazmath/plane.js new file mode 100644 index 0000000..db5f9dd --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/plane.js @@ -0,0 +1,137 @@ +/** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2008, Luke Benstead. + 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. + + 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. + */ + +/** + * @ignore + */ +(function(cc){ + cc.math.Plane = function (a, b, c, d) { + if (a && b === undefined) { + this.a = a.a; + this.b = a.b; + this.c = a.c; + this.d = a.d; + } else { + this.a = a || 0; + this.b = b || 0; + this.c = c || 0; + this.d = d || 0; + } + }; + cc.kmPlane = cc.math.Plane; + var proto = cc.math.Plane.prototype; + + cc.math.Plane.LEFT = 0; + + cc.math.Plane.RIGHT = 1; + + cc.math.Plane.BOTTOM = 2; + + cc.math.Plane.TOP = 3; + + cc.math.Plane.NEAR = 4; + + cc.math.Plane.FAR = 5; + + cc.math.Plane.POINT_INFRONT_OF_PLANE = 0; + + cc.math.Plane.POINT_BEHIND_PLANE = 1; + + cc.math.Plane.POINT_ON_PLANE = 2; + + proto.dot = function(vec4){ //cc.kmPlaneDot + return (this.a * vec4.x + this.b * vec4.y + this.c * vec4.z + this.d * vec4.w); + }; + + proto.dotCoord = function(vec3) { //=cc.kmPlaneDotCoord + return (this.a * vec3.x + this.b * vec3.y + this.c * vec3.z + this.d); + }; + + proto.dotNormal = function(vec3) { //=cc.kmPlaneDotNormal + return (this.a * vec3.x + this.b * vec3.y + this.c * vec3.z); + }; + + cc.math.Plane.fromPointNormal = function(vec3, normal) { //cc.kmPlaneFromPointNormal + /* + Planea = Nx + Planeb = Ny + Planec = Nz + Planned = −N⋅P + */ + return new cc.math.Plane(normal.x, normal.y, normal.z, -normal.dot(vec3)); + }; + + cc.math.Plane.fromPoints = function(vec1, vec2, vec3) { //cc.kmPlaneFromPoints + /* + v = (B − A) × (C − A) + n = 1⁄|v| v + Outa = nx + Outb = ny + Outc = nz + Outd = −n⋅A + */ + var v1 = new cc.math.Vec3(vec2), v2 = new cc.math.Vec3(vec3), plane = new cc.math.Plane(); + v1.subtract(vec1); //Create the vectors for the 2 sides of the triangle + v2.subtract(vec1); + v1.cross(v2); // Use the cross product to get the normal + v1.normalize(); //Normalize it and assign to pOut.m_N + + plane.a = v1.x; + plane.b = v1.y; + plane.c = v1.z; + plane.d = v1.scale(-1.0).dot(vec1); + return plane; + }; + + proto.normalize = function(){ //cc.kmPlaneNormalize + var n = new cc.math.Vec3(this.a, this.b, this.c), l = 1.0 / n.length(); //Get 1/length + n.normalize(); //Normalize the vector and assign to pOut + this.a = n.x; + this.b = n.y; + this.c = n.z; + this.d = this.d * l; //Scale the D value and assign to pOut + return this; + }; + + proto.classifyPoint = function(vec3) { + // This function will determine if a point is on, in front of, or behind + // the plane. First we store the dot product of the plane and the point. + var distance = this.a * vec3.x + this.b * vec3.y + this.c * vec3.z + this.d; + + // Simply put if the dot product is greater than 0 then it is infront of it. + // If it is less than 0 then it is behind it. And if it is 0 then it is on it. + if(distance > 0.001) + return cc.math.Plane.POINT_INFRONT_OF_PLANE; + if(distance < -0.001) + return cc.math.Plane.POINT_BEHIND_PLANE; + return cc.math.Plane.POINT_ON_PLANE; + }; +})(cc); + + + diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/quaternion.js b/frameworks/cocos2d-html5/cocos2d/kazmath/quaternion.js new file mode 100644 index 0000000..fcbd652 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/quaternion.js @@ -0,0 +1,459 @@ +/** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2008, Luke Benstead. + 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. + + 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. + */ + +(function(cc) { + /** + * The Quaternion class + * @param {Number|cc.math.Quaternion} [x=0] + * @param {Number} [y=0] + * @param {Number} [z=0] + * @param {Number} [w=0] + * @constructor + */ + cc.math.Quaternion = function (x, y, z, w) { + if (x && y === undefined) { + this.x = x.x; + this.y = x.y; + this.z = x.z; + this.w = x.w; + } else { + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + this.w = w || 0; + } + }; + cc.kmQuaternion = cc.math.Quaternion; + var proto = cc.math.Quaternion.prototype; + + /** + * Sets the conjugate of quaternion to self + * @param {cc.math.Quaternion} quaternion + */ + proto.conjugate = function (quaternion) { //= cc.kmQuaternionConjugate + this.x = -quaternion.x; + this.y = -quaternion.y; + this.z = -quaternion.z; + this.w = quaternion.w; + return this; + }; + + /** + * Returns the dot product of the current quaternion and parameter quaternion + * @param quaternion + * @returns {number} + */ + proto.dot = function(quaternion) { // = cc.kmQuaternionDot + // A dot B = B dot A = AtBt + AxBx + AyBy + AzBz + return (this.w * quaternion.w + this.x * quaternion.x + this.y * quaternion.y + this.z * quaternion.z); + }; + + /** + * Returns the exponential of the quaternion, this function doesn't implemented. + * @returns {cc.math.Quaternion} + */ + proto.exponential = function(){ //=cc.kmQuaternionExp + return this; + }; + + /** + * Makes the current quaternion an identity quaternion + */ + proto.identity = function(){ //=cc.kmQuaternionIdentity + this.x = 0.0; + this.y = 0.0; + this.z = 0.0; + this.w = 1.0; + return this; + }; + + /** + * Inverses the value of current Quaternion + */ + proto.inverse = function(){ //=cc.kmQuaternionInverse + var len = this.length(); + if (Math.abs(len) > cc.math.EPSILON) { + this.x = 0.0; + this.y = 0.0; + this.z = 0.0; + this.w = 0.0; + return this; + } + + ///Get the conjugute and divide by the length + this.conjugate(this).scale(1.0 / len); + return this; + }; + + /** + * Returns true if the quaternion is an identity quaternion + * @returns {boolean} + */ + proto.isIdentity = function(){ //=cc.kmQuaternionIsIdentity + return (this.x === 0.0 && this.y === 0.0 && this.z === 0.0 && this.w === 1.0); + }; + + /** + * Returns the length of the quaternion + * @returns {number} + */ + proto.length = function() { //=cc.kmQuaternionLength + return Math.sqrt(this.lengthSq()); + }; + + /** + * Returns the length of the quaternion squared (prevents a sqrt) + * @returns {number} + */ + proto.lengthSq = function() { //=cc.kmQuaternionLengthSq + return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; + }; + + /** + * Uses current quaternion multiplies other quaternion. + * @param {cc.math.Quaternion} quaternion + * @returns {cc.math.Quaternion} + */ + proto.multiply = function(quaternion) { //cc.kmQuaternionMultiply + var x = this.x, y = this.y, z = this.z, w = this.w; + this.w = w * quaternion.w - x * quaternion.x - y * quaternion.y - z * quaternion.z; + this.x = w * quaternion.x + x * quaternion.w + y * quaternion.z - z * quaternion.y; + this.y = w * quaternion.y + y * quaternion.w + z * quaternion.x - x * quaternion.z; + this.z = w * quaternion.z + z * quaternion.w + x * quaternion.y - y * quaternion.x; + return this; + }; + + /** + * Normalizes a quaternion + * @returns {cc.math.Quaternion} + */ + proto.normalize = function(){ //=cc.kmQuaternionNormalize + var length = this.length(); + if (Math.abs(length) <= cc.math.EPSILON) + throw new Error("current quaternion is an invalid value"); + this.scale(1.0 / length); + return this; + }; + + /** + * Rotates a quaternion around an axis and an angle + * @param {cc.math.Vec3} axis + * @param {Number} angle + */ + proto.rotationAxis = function(axis, angle){ //cc.kmQuaternionRotationAxis + var rad = angle * 0.5, scale = Math.sin(rad); + this.w = Math.cos(rad); + this.x = axis.x * scale; + this.y = axis.y * scale; + this.z = axis.z * scale; + return this; + }; + + /** + * Creates a quaternion from a rotation matrix + * @param mat3 + * @returns {*} + */ + cc.math.Quaternion.rotationMatrix = function (mat3) { //cc.kmQuaternionRotationMatrix + if (!mat3) + return null; + + var x, y, z, w; + var m4x4 = [], mat = mat3.mat, scale = 0.0; + + /* 0 3 6 + 1 4 7 + 2 5 8 + + 0 1 2 3 + 4 5 6 7 + 8 9 10 11 + 12 13 14 15*/ + m4x4[0] = mat[0]; + m4x4[1] = mat[3]; + m4x4[2] = mat[6]; + m4x4[4] = mat[1]; + m4x4[5] = mat[4]; + m4x4[6] = mat[7]; + m4x4[8] = mat[2]; + m4x4[9] = mat[5]; + m4x4[10] = mat[8]; + m4x4[15] = 1; + var pMatrix = m4x4[0]; + + var diagonal = pMatrix[0] + pMatrix[5] + pMatrix[10] + 1; + if (diagonal > cc.math.EPSILON) { + // Calculate the scale of the diagonal + scale = Math.sqrt(diagonal) * 2; + + // Calculate the x, y, x and w of the quaternion through the respective equation + x = ( pMatrix[9] - pMatrix[6] ) / scale; + y = ( pMatrix[2] - pMatrix[8] ) / scale; + z = ( pMatrix[4] - pMatrix[1] ) / scale; + w = 0.25 * scale; + } else { + // If the first element of the diagonal is the greatest value + if (pMatrix[0] > pMatrix[5] && pMatrix[0] > pMatrix[10]) { + // Find the scale according to the first element, and double that value + scale = Math.sqrt(1.0 + pMatrix[0] - pMatrix[5] - pMatrix[10]) * 2.0; + + // Calculate the x, y, x and w of the quaternion through the respective equation + x = 0.25 * scale; + y = (pMatrix[4] + pMatrix[1] ) / scale; + z = (pMatrix[2] + pMatrix[8] ) / scale; + w = (pMatrix[9] - pMatrix[6] ) / scale; + } + // Else if the second element of the diagonal is the greatest value + else if (pMatrix[5] > pMatrix[10]) { + // Find the scale according to the second element, and double that value + scale = Math.sqrt(1.0 + pMatrix[5] - pMatrix[0] - pMatrix[10]) * 2.0; + + // Calculate the x, y, x and w of the quaternion through the respective equation + x = (pMatrix[4] + pMatrix[1] ) / scale; + y = 0.25 * scale; + z = (pMatrix[9] + pMatrix[6] ) / scale; + w = (pMatrix[2] - pMatrix[8] ) / scale; + } else { + // Else the third element of the diagonal is the greatest value + + // Find the scale according to the third element, and double that value + scale = Math.sqrt(1.0 + pMatrix[10] - pMatrix[0] - pMatrix[5]) * 2.0; + + // Calculate the x, y, x and w of the quaternion through the respective equation + x = (pMatrix[2] + pMatrix[8] ) / scale; + y = (pMatrix[9] + pMatrix[6] ) / scale; + z = 0.25 * scale; + w = (pMatrix[4] - pMatrix[1] ) / scale; + } + } + return new cc.math.Quaternion(x, y, z, w); + }; + + /** + * Create a quaternion from yaw, pitch and roll + * @param yaw + * @param pitch + * @param roll + * @returns {cc.math.Quaternion} + */ + cc.math.Quaternion.rotationYawPitchRoll = function (yaw, pitch, roll) { //cc.kmQuaternionRotationYawPitchRoll + var ex, ey, ez; // temp half euler angles + var cr, cp, cy, sr, sp, sy, cpcy, spsy; // temp vars in roll,pitch yaw + + ex = cc.degreesToRadians(pitch) / 2.0; // convert to rads and half them + ey = cc.degreesToRadians(yaw) / 2.0; + ez = cc.degreesToRadians(roll) / 2.0; + + cr = Math.cos(ex); + cp = Math.cos(ey); + cy = Math.cos(ez); + + sr = Math.sin(ex); + sp = Math.sin(ey); + sy = Math.sin(ez); + + cpcy = cp * cy; + spsy = sp * sy; + + var ret = new cc.math.Quaternion(); + ret.w = cr * cpcy + sr * spsy; + ret.x = sr * cpcy - cr * spsy; + ret.y = cr * sp * cy + sr * cp * sy; + ret.z = cr * cp * sy - sr * sp * cy; + ret.normalize(); + return ret; + }; + + /** + * Interpolate with other quaternions + * @param {cc.math.Quaternion} quaternion + * @param {Number} t + * @returns {cc.math.Quaternion} + */ + proto.slerp = function(quaternion, t) { //=cc.kmQuaternionSlerp + if (this.x === quaternion.x && this.y === quaternion.y && this.z === quaternion.z && this.w === quaternion.w) { + return this; + } + var ct = this.dot(quaternion), theta = Math.acos(ct), st = Math.sqrt(1.0 - cc.math.square(ct)); + var stt = Math.sin(t * theta) / st, somt = Math.sin((1.0 - t) * theta) / st; + var temp2 = new cc.math.Quaternion(quaternion); + this.scale(somt); + temp2.scale(stt); + this.add(temp2); + return this; + }; + + /** + * Get the axis and angle of rotation from a quaternion + * @returns {{axis: cc.math.Vec3, angle: number}} + */ + proto.toAxisAndAngle = function(){ //=cc.kmQuaternionToAxisAngle + var tempAngle; // temp angle + var scale; // temp vars + var retAngle, retAxis = new cc.math.Vec3(); + + tempAngle = Math.acos(this.w); + scale = Math.sqrt(cc.math.square(this.x) + cc.math.square(this.y) + cc.math.square(this.z)); + + if (((scale > -cc.math.EPSILON) && scale < cc.math.EPSILON) + || (scale < 2 * Math.PI + cc.math.EPSILON && scale > 2 * Math.PI - cc.math.EPSILON)) { // angle is 0 or 360 so just simply set axis to 0,0,1 with angle 0 + retAngle = 0.0; + retAxis.x = 0.0; + retAxis.y = 0.0; + retAxis.z = 1.0; + } else { + retAngle = tempAngle * 2.0; // angle in radians + retAxis.x = this.x / scale; + retAxis.y = this.y / scale; + retAxis.z = this.z / scale; + retAxis.normalize(); + } + return {axis: retAxis, angle: retAngle}; + }; + + /** + * Scale a quaternion + * @param {Number} scale + */ + proto.scale = function(scale) { //cc.kmQuaternionScale + this.x *= scale; + this.y *= scale; + this.z *= scale; + this.w *= scale; + return this; + }; + + /** + * Assign current quaternion value from a quaternion. + * @param {cc.math.Quaternion} quaternion + * @returns {cc.math.Quaternion} current quaternion + */ + proto.assignFrom = function(quaternion){ //=cc.kmQuaternionAssign + this.x = quaternion.x; + this.y = quaternion.y; + this.z = quaternion.z; + this.w = quaternion.w; + return this; + }; + + /** + * Adds other quaternion + * @param {cc.math.Quaternion} quaternion + * @returns {cc.math.Quaternion} + */ + proto.add = function(quaternion) { //cc.kmQuaternionAdd + this.x += quaternion.x; + this.y += quaternion.y; + this.z += quaternion.z; + this.w += quaternion.w; + return this; + }; + + /** + *

+ * Adapted from the OGRE engine!
+ * Gets the shortest arc quaternion to rotate this vector to the destination vector.
+ * @remarks
+ * If you call this with a destination vector that is close to the inverse
+ * of this vector, we will rotate 180 degrees around the 'fallbackAxis'
+ * (if specified, or a generated axis if not) since in this case ANY axis of rotation is valid. + *

+ * @param {cc.math.Vec3} vec1 + * @param {cc.math.Vec3} vec2 + * @param {cc.math.Vec3} fallback + * @returns {cc.math.Quaternion} + */ + cc.math.Quaternion.rotationBetweenVec3 = function(vec1, vec2, fallback) { //cc.kmQuaternionRotationBetweenVec3 + var v1 = new cc.math.Vec3(vec1), v2 = new cc.math.Vec3(vec2); + v1.normalize(); + v2.normalize(); + var a = v1.dot(v2), quaternion = new cc.math.Quaternion(); + + if (a >= 1.0) { + quaternion.identity(); + return quaternion; + } + + if (a < (1e-6 - 1.0)) { + if (Math.abs(fallback.lengthSq()) < cc.math.EPSILON) { + quaternion.rotationAxis(fallback, Math.PI); + } else { + var axis = new cc.math.Vec3(1.0, 0.0, 0.0); + axis.cross(vec1); + + //If axis is zero + if (Math.abs(axis.lengthSq()) < cc.math.EPSILON) { + axis.fill(0.0, 1.0, 0.0); + axis.cross(vec1); + } + axis.normalize(); + quaternion.rotationAxis(axis, Math.PI); + } + } else { + var s = Math.sqrt((1 + a) * 2), invs = 1 / s; + v1.cross(v2); + quaternion.x = v1.x * invs; + quaternion.y = v1.y * invs; + quaternion.z = v1.z * invs; + quaternion.w = s * 0.5; + quaternion.normalize(); + } + return quaternion; + }; + + /** + * Current quaternion multiplies a vec3 + * @param {cc.math.Vec3} vec + * @returns {cc.math.Vec3} + */ + proto.multiplyVec3 = function(vec){ //=cc.kmQuaternionMultiplyVec3 + var x = this.x, y = this.y, z = this.z, retVec = new cc.math.Vec3(vec); + var uv = new cc.math.Vec3(x, y, z), uuv = new cc.math.Vec3(x, y, z); + uv.cross(vec); + uuv.cross(uv); + uv.scale((2.0 * q.w)); + uuv.scale(2.0); + + retVec.add(uv); + retVec.add(uuv); + return retVec; + }; +})(cc); + + + + + + + + + + + + + diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/ray2.js b/frameworks/cocos2d-html5/cocos2d/kazmath/ray2.js new file mode 100644 index 0000000..5a29794 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/ray2.js @@ -0,0 +1,140 @@ +/** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2008, Luke Benstead. + 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. + + 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. + */ + +(function(cc){ + cc.math.Ray2 = function (start, dir) { // = cc.kmRay2 + this.start = start || new cc.math.Vec2(); + this.dir = dir || new cc.math.Vec2(); + }; + + cc.math.Ray2.prototype.fill = function (px, py, vx, vy) { // = cc.kmRay2Fill + this.start.x = px; + this.start.y = py; + this.dir.x = vx; + this.dir.y = vy; + }; + + cc.math.Ray2.prototype.intersectLineSegment = function (p1, p2, intersection) { // = cc.kmRay2IntersectLineSegment + var x1 = this.start.x, y1 = this.start.y; + var x2 = this.start.x + this.dir.x, y2 = this.start.y + this.dir.y; + var x3 = p1.x, y3 = p1.y; + var x4 = p2.x, y4 = p2.y; + + var denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); + var ua, x, y; + //If denom is zero, the lines are parallel + if (denom > -cc.math.EPSILON && denom < cc.math.EPSILON) + return false; + + ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom; + //var ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denom; + + x = x1 + ua * (x2 - x1); + y = y1 + ua * (y2 - y1); + + if (x < Math.min(p1.x, p2.x) - cc.math.EPSILON || + x > Math.max(p1.x, p2.x) + cc.math.EPSILON || + y < Math.min(p1.y, p2.y) - cc.math.EPSILON || + y > Math.max(p1.y, p2.y) + cc.math.EPSILON) { + //Outside of line + //printf("Outside of line, %f %f (%f %f)(%f, %f)\n", x, y, p1.x, p1.y, p2.x, p2.y); + return false; + } + + if (x < Math.min(x1, x2) - cc.math.EPSILON || + x > Math.max(x1, x2) + cc.math.EPSILON || + y < Math.min(y1, y2) - cc.math.EPSILON || + y > Math.max(y1, y2) + cc.math.EPSILON) { + //printf("Outside of ray, %f %f (%f %f)(%f, %f)\n", x, y, x1, y1, x2, y2); + return false; + } + + intersection.x = x; + intersection.y = y; + return true; + }; + + function calculate_line_normal(p1, p2, normalOut){ + var tmp = new cc.math.Vec2(p2); + tmp.subtract(p1); + + normalOut.x = -tmp.y; + normalOut.y = tmp.x; + normalOut.normalize(); + //TODO: should check that the normal is pointing out of the triangle + } + + cc.math.Ray2.prototype.intersectTriangle = function(p1, p2, p3, intersection, normal_out){ + var intersect = new cc.math.Vec2(), final_intersect = new cc.math.Vec2(); + var normal = new cc.math.Vec2(), distance = 10000.0, intersected = false; + var this_distance; + + if(this.intersectLineSegment(p1, p2, intersect)) { + intersected = true; + this_distance = intersect.subtract(this.start).length(); + if(this_distance < distance) { + final_intersect.x = intersect.x; + final_intersect.y = intersect.y; + distance = this_distance; + calculate_line_normal(p1, p2, normal); + } + } + + if(this.intersectLineSegment(p2, p3, intersect)) { + intersected = true; + this_distance = intersect.subtract(this.start).length(); + if(this_distance < distance) { + final_intersect.x = intersect.x; + final_intersect.y = intersect.y; + distance = this_distance; + calculate_line_normal(p2, p3, normal); + } + } + + if(this.intersectLineSegment(p3, p1, intersect)) { + intersected = true; + this_distance = intersect.subtract(this.start).length(); + if(this_distance < distance) { + final_intersect.x = intersect.x; + final_intersect.y = intersect.y; + distance = this_distance; + calculate_line_normal(p3, p1, normal); + } + } + + if(intersected) { + intersection.x = final_intersect.x; + intersection.y = final_intersect.y; + if(normal_out) { + normal_out.x = normal.x; + normal_out.y = normal.y; + } + } + return intersected; + }; +})(cc); diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/base.js b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/base.js new file mode 100644 index 0000000..d01857c --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/base.js @@ -0,0 +1,139 @@ +// SIMD Kernel Benchmark Harness +// Author: Peter Jensen + +function Benchmark (config) { + this.config = config; + this.initOk = true; // Initialize all properties used on a Benchmark object + this.cleanupOk = true; + this.useAutoIterations = true; + this.autoIterations = 0; + this.actualIterations = 0; + this.simdTime = 0; + this.nonSimdTime = 0; +} + +function Benchmarks () { + this.benchmarks = []; +} + +Benchmarks.prototype.add = function (benchmark) { + this.benchmarks.push (benchmark); + return this.benchmarks.length - 1; +} + +Benchmarks.prototype.runOne = function (benchmark) { + + function timeKernel(kernel, iterations) { + var start, stop; + start = Date.now(); + kernel(iterations); + stop = Date.now(); + return stop - start; + } + + function computeIterations() { + var desiredRuntime = 1000; // milliseconds for longest running kernel + var testIterations = 10; // iterations used to determine time for desiredRuntime + + // Make the slowest kernel run for at least 500ms + var simdTime = timeKernel(benchmark.config.kernelSimd, testIterations); + var nonSimdTime = timeKernel(benchmark.config.kernelNonSimd, testIterations); + var maxTime = simdTime > nonSimdTime ? simdTime : nonSimdTime; + while (maxTime < 500) { + testIterations *= 2; + simdTime = timeKernel(benchmark.config.kernelSimd, testIterations); + nonSimdTime = timeKernel(benchmark.config.kernelNonSimd, testIterations); + maxTime = simdTime > nonSimdTime ? simdTime : nonSimdTime; + } + maxTime = simdTime > nonSimdTime ? simdTime : nonSimdTime; + + // Compute iteration count for 1 second run of slowest kernel + var iterations = Math.ceil(desiredRuntime * testIterations / maxTime); + return iterations; + } + + // Initialize the kernels and check the correctness status + if (!benchmark.config.kernelInit()) { + benchmark.initOk = false; + return false; + } + + // Determine how many iterations to use. + if (benchmark.useAutoIterations) { + benchmark.autoIterations = computeIterations(); + benchmark.actualIterations = benchmark.autoIterations; + } + else { + benchmark.actualIterations = benchmark.config.kernelIterations; + } + + // Run the SIMD kernel + benchmark.simdTime = timeKernel(benchmark.config.kernelSimd, benchmark.actualIterations); + + // Run the non-SIMD kernel + benchmark.nonSimdTime = timeKernel(benchmark.config.kernelNonSimd, benchmark.actualIterations); + + // Do the final sanity check + if (!benchmark.config.kernelCleanup()) { + benchmark.cleanupOk = false; + return false; + } + + return true; +} + +Benchmarks.prototype.report = function (benchmark, outputFunctions) { + + function fillRight(str, width) { + str += ""; // make sure it's a string + while (str.length < width) { + str += " "; + } + return str; + } + + function fillLeft(str, width) { + str += ""; // make sure it's a string + while (str.length < width) { + str = " " + str; + } + return str; + } + + if (!benchmark.initOk) { + outputFunctions.notifyError(fillRight(benchmark.config.kernelName + ": ", 23) + "FAILED INIT"); + return; + } + if (!benchmark.cleanupOk) { + outputFunctions.notifyError(fillRight(benchmark.config.kernelName + ": ", 23) + "FAILED CLEANUP"); + return; + } + + var ratio = benchmark.nonSimdTime / benchmark.simdTime; + ratio = ratio.toFixed(2); + outputFunctions.notifyResult( + fillRight(benchmark.config.kernelName + ": ", 23) + + "Iterations(" + fillLeft(benchmark.actualIterations, 10) + ")" + + ", SIMD(" + fillLeft(benchmark.simdTime + "ms)", 8) + + ", Non-SIMD(" + fillLeft(benchmark.nonSimdTime + "ms)", 8) + + ", Speedup(" + ratio + ")"); + outputFunctions.timeData.labels.push(benchmark.config.kernelName); + outputFunctions.timeData.datasets[0].data.push(benchmark.simdTime); + outputFunctions.timeData.datasets[1].data.push(benchmark.nonSimdTime); + outputFunctions.speedupData.labels.push(benchmark.config.kernelName); + outputFunctions.speedupData.datasets[0].data.push(ratio); +} + +Benchmarks.prototype.runAll = function (outputFunctions, useAutoIterations) { + if (typeof useAutoIterations === "undefined") { + useAutoIterations = false; + } + for (var i = 0, n = this.benchmarks.length; i < n; ++i) { + var benchmark = this.benchmarks[i]; + benchmark.useAutoIterations = useAutoIterations; + this.runOne(benchmark); + this.report(benchmark, outputFunctions); + } +} + +var benchmarks = new Benchmarks (); diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/index.html b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/index.html new file mode 100644 index 0000000..8c9cb4f --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/index.html @@ -0,0 +1,44 @@ + + + + + Kazmath SIMD benchmarks + + + +

Running benchmarks...

+
+
+

+ +
+
+

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kernel-template.js b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kernel-template.js new file mode 100644 index 0000000..d7c3775 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kernel-template.js @@ -0,0 +1,64 @@ +// Kernel template +// Author: Peter Jensen +(function () { + + // Kernel configuration + var kernelConfig = { + kernelName: "Test", + kernelInit: init, + kernelCleanup: cleanup, + kernelSimd: simd, + kernelNonSimd: nonSimd, + kernelIterations: 100000000 + }; + + // Hook up to the harness + benchmarks.add (new Benchmark (kernelConfig)); + + // Kernel Initializer + function init () { + // Do initial sanity check and initialize data for the kernels. + // The sanity check should verify that the simd and nonSimd results + // are the same. + // It is recommended to do minimal object creation in the kernels + // themselves. If global data needs to be initialized, here would + // be the place to do it. + // If the sanity checks fails the kernels will not be executed + // Returns: + // true: First run (unoptimized) of the kernels passed + // false: First run (unoptimized) of the kernels failed + return simd (1) === nonSimd (1); + } + + // Kernel Cleanup + function cleanup () { + // Do final sanity check and perform cleanup. + // This function is called when all the kernel iterations have been + // executed, so they should be in their final optimized version. The + // sanity check done during initialization will probably be of the + // initial unoptimized version. + // Returns: + // true: Last run (optimized) of the kernels passed + // false: last run (optimized) of the kernels failed + return simd (1) === nonSimd (1); + } + + // SIMD version of the kernel + function simd (n) { + var s = 0; + for (var i = 0; i < n; ++i) { + s += i; + } + return s; + } + + // Non SIMD version of the kernel + function nonSimd (n) { + var s = 0; + for (var i = 0; i < n; ++i) { + s += i; + } + return s; + } + +} ()); diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kmMat4AreEqual.js b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kmMat4AreEqual.js new file mode 100644 index 0000000..a3dee72 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kmMat4AreEqual.js @@ -0,0 +1,77 @@ +// kmMat4AreEqual + +(function () { + + // Kernel configuration + var kernelConfig = { + kernelName: "kmMat4AreEqual", + kernelInit: init, + kernelCleanup: cleanup, + kernelSimd: simd, + kernelNonSimd: nonSimd, + kernelIterations: 10000 + }; + + // Hook up to the harness + benchmarks.add(new Benchmark(kernelConfig)); + + // Benchmark data, initialization and kernel functions + var T1 = new cc.kmMat4(); + var T2 = new cc.kmMat4(); + var T1x4 = new cc.kmMat4(); + var T2x4 = new cc.kmMat4(); + var areEqual, areEqualSIMD; + + function equals(A, B) { + for (var i = 0; i < 16; ++i) { + if (A[i] != B[i]) + return false; + } + return true; + } + + function init() { + T1.mat[0] = 1.0; + T1.mat[5] = 1.0; + T1.mat[10] = 1.0; + T1.mat[15] = 1.0; + + T2.mat[0] = 1.0; + T2.mat[5] = 1.0; + T2.mat[10] = 1.0; + T2.mat[15] = 1.0; + + T1x4.mat[0] = 1.0; + T1x4.mat[5] = 1.0; + T1x4.mat[10] = 1.0; + T1x4.mat[15] = 1.0; + + T2x4.mat[0] = 1.0; + T2x4.mat[5] = 1.0; + T2x4.mat[10] = 1.0; + T2x4.mat[15] = 1.0; + + nonSimd(1); + simd(1); + + return equals(T1.mat, T1x4.mat) && equals(T2.mat, T2x4.mat) && (areEqual === areEqualSIMD); + + } + + function cleanup() { + return init(); // Sanity checking before and after are the same + } + + function nonSimd(n) { + for (var i = 0; i < n; i++) { + areEqual = T1.equals(T2); + } + } + + function simd(n) { + for (var i = 0; i < n; i++) { + areEqualSIMD = T1x4.equalsSIMD(T2x4); + } + } + +} ()); diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kmMat4Assign.js b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kmMat4Assign.js new file mode 100644 index 0000000..e8e386f --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kmMat4Assign.js @@ -0,0 +1,68 @@ +// kmMat4Assign + +(function () { + + // Kernel configuration + var kernelConfig = { + kernelName: "kmMat4Assign", + kernelInit: init, + kernelCleanup: cleanup, + kernelSimd: simd, + kernelNonSimd: nonSimd, + kernelIterations: 10000 + }; + + // Hook up to the harness + benchmarks.add(new Benchmark(kernelConfig)); + + // Benchmark data, initialization and kernel functions + var T1 = new cc.kmMat4(); + var T2 = new cc.kmMat4(); + var T1x4 = new cc.kmMat4(); + var T2x4 = new cc.kmMat4(); + + function equals(A, B) { + for (var i = 0; i < 16; ++i) { + if (A[i] != B[i]) + return false; + } + return true; + } + + function init() { + T1.mat[0] = 1.0; + T1.mat[5] = 1.0; + T1.mat[10] = 1.0; + T1.mat[15] = 1.0; + + T1x4.mat[0] = 1.0; + T1x4.mat[5] = 1.0; + T1x4.mat[10] = 1.0; + T1x4.mat[15] = 1.0; + + nonSimd(1); + simd(1); + + return equals(T1.mat, T1x4.mat) && equals(T2.mat, T2x4.mat); + + } + + function cleanup() { + return init(); // Sanity checking before and after are the same + } + + function nonSimd(n) { + for (var i = 0; i < n; i++) { + //cc.kmMat4Assign(T2, T1); + T2.assignFrom(T1); + } + } + + function simd(n) { + for (var i = 0; i < n; i++) { + //cc.kmMat4AssignSIMD(T2x4, T1x4); + T2x4.assignFromSIMD(T1x4); + } + } + +} ()); diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kmMat4Inverse.js b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kmMat4Inverse.js new file mode 100644 index 0000000..212dff1 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kmMat4Inverse.js @@ -0,0 +1,116 @@ +// kmMat4Inverse + +(function () { + + // Kernel configuration + var kernelConfig = { + kernelName: "kmMat4Inverse", + kernelInit: init, + kernelCleanup: cleanup, + kernelSimd: simd, + kernelNonSimd: nonSimd, + kernelIterations: 10000 + }; + + // Hook up to the harness + benchmarks.add(new Benchmark(kernelConfig)); + + // Benchmark data, initialization and kernel functions + var src = new cc.kmMat4(); + var dst = new cc.kmMat4(); + var srcx4 = new cc.kmMat4(); + var dstx4 = new cc.kmMat4(); + var ident = new Float32Array( + [1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1]); + + function equals(A, B) { + for (var i = 0; i < 16; ++i) { + if (Math.abs (A[i] - B[i]) > 5) + return false; + } + return true; + } + + function initMatrix(matrix) { + // These values were chosen somewhat randomly, but they will at least yield a solution. + matrix [0] = 0; matrix[1] = 1; matrix[2] = 2; matrix[3] = 3; + matrix [4] = -1; matrix[5] = -2; matrix[6] = -3; matrix[7] = -4; + matrix [8] = 0; matrix[9] = 0; matrix[10] = 2; matrix[11] = 3; + matrix [12] = -1; matrix[13] = -2; matrix[14] = 0; matrix[15] = -4; + } + + function mulMatrix(dst, op1, op2) { + for (var r = 0; r < 4; ++r) { + for (var c = 0; c < 4; ++c) { + var ri = 4*r; + dst[ri + c] = op1[ri]*op2[c] + op1[ri+1]*op2[c+4] + op1[ri+2]*op2[c+8] + op1[ri+3]*op2[c+12]; + } + } + } + + function printMatrix(matrix, str) { + print('--------matrix ' + str + '----------'); + for (var r = 0; r < 4; ++r) { + var str = ""; + var ri = r*4; + for (var c = 0; c < 4; ++c) { + var value = matrix[ri + c]; + str += " " + value.toFixed(2); + } + print(str); + } + } + + function checkMatrix(src, dst) { + // when multiplied with the src matrix it should yield the identity matrix + var tmp = new Float32Array(16); + mulMatrix(tmp, src, dst); + for (var i = 0; i < 16; ++i) { + if (Math.abs (tmp[i] - ident[i]) > 0.00001) { + return false; + } + } + return true; + } + + function init() { + initMatrix(src.mat); + // printMatrix(src); + nonSimd(1); + // printMatrix(dst); + if (!checkMatrix(src.mat, dst.mat)) { + return false; + } + + initMatrix(srcx4.mat); + simd(1); + // printMatrix(dst); + if (!checkMatrix(srcx4.mat, dstx4.mat)) { + return false; + } + + return true; + } + + function cleanup() { + return init(); // Sanity checking before and after are the same + } + + function nonSimd(n) { + for (var i = 0; i < n; i++) { + //cc.kmMat4Inverse(dst, src); + dst = src.inverse(); + } + } + + function simd(n) { + for (var i = 0; i < n; i++) { + //cc.kmMat4InverseSIMD(dstx4, srcx4); + dstx4 = srcx4.inverseSIMD(); + } + } + +} ()); diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kmMat4IsIdentity.js b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kmMat4IsIdentity.js new file mode 100644 index 0000000..9ab17ac --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kmMat4IsIdentity.js @@ -0,0 +1,65 @@ +// kmMat4IsIdentity + +(function () { + + // Kernel configuration + var kernelConfig = { + kernelName: "kmMat4IsIdentity", + kernelInit: init, + kernelCleanup: cleanup, + kernelSimd: simd, + kernelNonSimd: nonSimd, + kernelIterations: 10000 + }; + + // Hook up to the harness + benchmarks.add(new Benchmark(kernelConfig)); + + // Benchmark data, initialization and kernel functions + var T1 = new cc.kmMat4(); + var T1x4 = new cc.kmMat4(); + var isIdentity, isIdentitySIMD; + + function equals(A, B) { + for (var i = 0; i < 16; ++i) { + if (A[i] != B[i]) + return false; + } + return true; + } + + function init() { + T1.mat[0] = 1.0; + T1.mat[5] = 1.0; + T1.mat[10] = 1.0; + T1.mat[15] = 1.0; + + T1x4.mat[0] = 1.0; + T1x4.mat[5] = 1.0; + T1x4.mat[10] = 1.0; + T1x4.mat[15] = 1.0; + + nonSimd(1); + simd(1); + + return equals(T1.mat, T1x4.mat) && (isIdentity === isIdentitySIMD); + + } + + function cleanup() { + return init(); // Sanity checking before and after are the same + } + + function nonSimd(n) { + for (var i = 0; i < n; i++) { + isIdentity = T1.isIdentity(); + } + } + + function simd(n) { + for (var i = 0; i < n; i++) { + isIdentitySIMD = T1x4.isIdentitySIMD(); + } + } + +} ()); diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kmMat4LookAt.js b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kmMat4LookAt.js new file mode 100644 index 0000000..1015ad1 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kmMat4LookAt.js @@ -0,0 +1,94 @@ +// kmMat4LookAt + +(function () { + + // Kernel configuration + var kernelConfig = { + kernelName: "kmMat4LookAt", + kernelInit: init, + kernelCleanup: cleanup, + kernelSimd: simd, + kernelNonSimd: nonSimd, + kernelIterations: 10000 + }; + + // Hook up to the harness + benchmarks.add(new Benchmark(kernelConfig)); + + // Benchmark data, initialization and kernel functions + var eye = new cc.kmVec3(), center = new cc.kmVec3(), up = new cc.kmVec3(); + var T1 = new cc.kmMat4(); + var eye1 = new cc.kmVec3(), center1 = new cc.kmVec3(), up1 = new cc.kmVec3(); + var T1x4 = new cc.kmMat4(); + + function printMatrix(matrix) { + print('--------matrix----------'); + for (var r = 0; r < 4; ++r) { + var str = ""; + var ri = r*4; + for (var c = 0; c < 4; ++c) { + var value = matrix[ri + c]; + str += " " + value.toFixed(2); + } + print(str); + } + } + + function equals(A, B) { + for (var i = 0; i < 16; ++i) { + if (Math.abs (A[i] - B[i]) > 0.001) { + return false; + } + } + return true; + } + + function init() { + + eye.fill(0, 1, 2); + center.fill(2, 1, 0); + up.fill(1, 1, 1); + + eye1.fill(0, 1, 2); + center1.fill(2, 1, 0); + up1.fill(1, 1, 1); + /* + eye1.data[0] = 0; + eye1.data[1] = 1; + eye1.data[2] = 2; + + center1.data[0] = 2; + center1.data[1] = 1; + center1.data[2] = 0; + + up1.data[0] = 1; + up1.data[1] = 1; + up1.data[2] = 1; + */ + nonSimd(1); + //printMatrix(T1.mat); + simd(1); + //printMatrix(T1x4.mat); + + return equals(T1.mat, T1x4.mat); + } + + function cleanup() { + return init(); // Sanity checking before and after are the same + } + + function nonSimd(n) { + for (var i = 0; i < n; i++) { + //cc.kmMat4LookAt(T1, eye, center, up); + T1.lookAt(eye, center, up); + } + } + + function simd(n) { + for (var i = 0; i < n; i++) { + //cc.kmMat4LookAtSIMD(T1x4, eye1, center1, up1); + T1x4.lookAtSIMD(eye1, center1, up1); + } + } + +} ()); diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kmMat4Multiply.js b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kmMat4Multiply.js new file mode 100644 index 0000000..325b6f9 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kmMat4Multiply.js @@ -0,0 +1,74 @@ +// kmMat4Multiply + +(function () { + + // Kernel configuration + var kernelConfig = { + kernelName: "kmMat4Multiply", + kernelInit: init, + kernelCleanup: cleanup, + kernelSimd: simd, + kernelNonSimd: nonSimd, + kernelIterations: 10000 + }; + + // Hook up to the harness + benchmarks.add(new Benchmark(kernelConfig)); + + // Benchmark data, initialization and kernel functions + var T1 = new cc.kmMat4(); + var T2 = new cc.kmMat4(); + var T1x4 = new cc.kmMat4(); + var T2x4 = new cc.kmMat4(); + + function equals(A, B) { + for (var i = 0; i < 16; ++i) { + if (A[i] != B[i]) + return false; + } + return true; + } + + function init() { + T1.mat[0] = 1.0; + T1.mat[5] = 1.0; + T1.mat[10] = 1.0; + T1.mat[15] = 1.0; + + T2.mat[0] = 1.0; + T2.mat[5] = 1.0; + T2.mat[10] = 1.0; + T2.mat[15] = 1.0; + + T1x4.mat[0] = 1.0; + T1x4.mat[5] = 1.0; + T1x4.mat[10] = 1.0; + T1x4.mat[15] = 1.0; + + T2x4.mat[0] = 1.0; + T2x4.mat[5] = 1.0; + T2x4.mat[10] = 1.0; + T2x4.mat[15] = 1.0; + + nonSimd(1); + simd(1); + return equals(T1.mat, T1x4.mat) && equals(T2.mat, T2x4.mat); + } + + function cleanup() { + return init(); // Sanity checking before and after are the same + } + + function nonSimd(n) { + for (var i = 0; i < n; i++) { + T1.multiply(T2); + } + } + + function simd(n) { + for (var i = 0; i < n; i++) { + T1x4.multiplySIMD(T2x4); + } + } + +} ()); diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kmMat4Transpose.js b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kmMat4Transpose.js new file mode 100644 index 0000000..cd1b776 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kmMat4Transpose.js @@ -0,0 +1,82 @@ +// kmMat4Transpose + +(function () { + + // Kernel configuration + var kernelConfig = { + kernelName: "kmMat4Transpose", + kernelInit: init, + kernelCleanup: cleanup, + kernelSimd: simd, + kernelNonSimd: nonSimd, + kernelIterations: 10000 + }; + + // Hook up to the harness + benchmarks.add(new Benchmark(kernelConfig)); + + // Benchmark data, initialization and kernel functions + var T1 = new cc.kmMat4(); + var T2 = new cc.kmMat4(); + var T1x4 = new cc.kmMat4(); + var T2x4 = new cc.kmMat4(); + + function equals(A, B) { + for (var i = 0; i < 16; ++i) { + if (A[i] != B[i]) + return false; + } + return true; + } + + function printMatrix(matrix) { + print('--------matrix----------'); + for (var r = 0; r < 4; ++r) { + var str = ""; + var ri = r*4; + for (var c = 0; c < 4; ++c) { + var value = matrix[ri + c]; + str += " " + value.toFixed(2); + } + print(str); + } + } + + function init() { + T1.mat [0] = 0; T1.mat[1] = 1; T1.mat[2] = 2; T1.mat[3] = 3; + T1.mat [4] = -1; T1.mat[5] = -2; T1.mat[6] = -3; T1.mat[7] = -4; + T1.mat [8] = 0; T1.mat[9] = 0; T1.mat[10] = 2; T1.mat[11] = 3; + T1.mat [12] = -1; T1.mat[13] = -2; T1.mat[14] = 0; T1.mat[15] = -4; + + T1x4.mat [0] = 0; T1x4.mat[1] = 1; T1x4.mat[2] = 2; T1x4.mat[3] = 3; + T1x4.mat [4] = -1; T1x4.mat[5] = -2; T1x4.mat[6] = -3; T1x4.mat[7] = -4; + T1x4.mat [8] = 0; T1x4.mat[9] = 0; T1x4.mat[10] = 2; T1x4.mat[11] = 3; + T1x4.mat [12] = -1; T1x4.mat[13] = -2; T1x4.mat[14] = 0; T1x4.mat[15] = -4; + + nonSimd(1); + //printMatrix(T2.mat); + simd(1); + //printMatrix(T2x4.mat); + return equals(T1.mat, T1x4.mat) && equals(T2.mat, T2x4.mat); + + } + + function cleanup() { + return init(); // Sanity checking before and after are the same + } + + function nonSimd(n) { + for (var i = 0; i < n; i++) { + T2 = T1.transpose(); + //T1.transpose(); + } + } + + function simd(n) { + for (var i = 0; i < n; i++) { + T2x4 = T1x4.transposeSIMD(); + //T1x4.transposeSIMD(); + } + } + +} ()); diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kmVec3TransformCoord.js b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kmVec3TransformCoord.js new file mode 100644 index 0000000..541a926 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/kmVec3TransformCoord.js @@ -0,0 +1,65 @@ +// kmVec3TransformCoord + +(function () { + + // Kernel configuration + var kernelConfig = { + kernelName: "kmVec3TransformCoord", + kernelInit: init, + kernelCleanup: cleanup, + kernelSimd: simd, + kernelNonSimd: nonSimd, + kernelIterations: 10000 + }; + + // Hook up to the harness + benchmarks.add(new Benchmark(kernelConfig)); + + // Benchmark data, initialization and kernel functions + var V = new cc.kmVec3(); + var T = new cc.kmMat4(); + var Out = new cc.kmVec3(); + var Vx4 = new cc.kmVec3(); + var Tx4 = new cc.kmMat4(); + var Outx4 = new cc.kmVec3(); + + function init() { + T.mat[0] = 1.0; + T.mat[5] = 1.0; + T.mat[10] = 1.0; + T.mat[15] = 1.0; + + V.fill(0.0, 1.0, 0.0); + + Tx4.mat[0] = 1.0; + Tx4.mat[5] = 1.0; + Tx4.mat[10] = 1.0; + Tx4.mat[15] = 1.0; + + Vx4.fill(0.0, 1.0, 0.0); + + nonSimd(1); + simd(1); + //console.log(V); + //console.log(Vx4); + return V.equals(Vx4); + + } + + function cleanup() { + return init(); // Sanity checking before and after are the same + } + + function nonSimd(n) { + for (var i = 0; i < n; i++) { + V.transformCoord(T); + } + } + + function simd(n) { + for (var i = 0; i < n; i++) { + Vx4.transformCoordSIMD(Tx4); + } + } + +} ()); diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/run.js b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/run.js new file mode 100644 index 0000000..8441ae1 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/run.js @@ -0,0 +1,40 @@ +"use strict" + +var cc = {}; + +load ('base.js'); + +load ('../utility.js'); +load ('../vec3.js'); +load ('../vec4.js'); +load ('../mat4.js'); +load ('../vec3SIMD.js'); +load ('../mat4SIMD.js'); + +// load individual benchmarks +load ('kernel-template.js'); +load ('kmMat4Multiply.js'); +load ('kmMat4Assign.js'); +load ('kmMat4AreEqual.js'); +load ('kmMat4Inverse.js'); +load ('kmMat4IsIdentity.js'); +load ('kmMat4Transpose.js'); +load ('kmMat4LookAt.js'); +load ('kmVec3TransformCoord.js'); + +function printResult (str) { + print (str); +} + +function printError (str) { + print (str); +} + +function printScore (str) { + print (str); +} + +benchmarks.runAll ({notifyResult: printResult, + notifyError: printError, + notifyScore: printScore}, + true); diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/run_browser.js b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/run_browser.js new file mode 100644 index 0000000..5d2d0cd --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/simd_benchmark/run_browser.js @@ -0,0 +1,80 @@ +var echo = document.getElementById('echo'); + +function printResult(str) { + console.log(str); + echo.innerHTML += str + '
'; +} + +function printError(str) { + console.log(str); + echo.innerHTML += str + '
'; +} + +function printScore(str) { + console.log(str); + echo.innerHTML += str + '
'; +} + +var timeData = { + labels: [], + datasets: [ + { + labels: 'Non-SIMD', + fillColor: "rgba(220,220,220,0.5)", + strokeColor: "rgba(220,220,220,0.8)", + highlightFill: "rgba(220,220,220,0.75)", + highlightStroke: "rgba(220,220,220,1)", + data: [] + }, + { + labels: 'SIMD', + fillColor: "rgba(151,187,205,0.5)", + strokeColor: "rgba(151,187,205,0.8)", + highlightFill: "rgba(151,187,205,0.75)", + highlightStroke: "rgba(151,187,205,1)", + data: [] + } + ] +}; + +var speedupData ={ + labels: [], + datasets: [ + { + labels: 'SIMD', + fillColor: "rgba(151,187,205,0.5)", + strokeColor: "rgba(151,187,205,0.8)", + highlightFill: "rgba(151,187,205,0.75)", + highlightStroke: "rgba(151,187,205,1)", + data: [] + } + ] +}; + +window.onload = function() { + if (typeof(SIMD) === 'undefined') { + var head = document.getElementById('head'); + head.innerHTML = 'SIMD is not implemented in your browser, stops.'; + return; + } + console.log('Running benchmarks.'); + benchmarks.runAll({notifyResult: printResult, + notifyError: printError, + notifyScore: printScore, + timeData: timeData, + speedupData: speedupData}, true); + document.getElementById('head').innerHTML = 'Results'; + document.getElementById('time').innerHTML = 'Time'; + document.getElementById('speedup').innerHTML = 'Speedup'; + var ctx1 = document.getElementById("canvasTime").getContext("2d"); + window.Bar1 = new Chart(ctx1).Bar(timeData, { + scaleLabel: "<%=value%>ms", + responsive: true + }); + var ctx2 = document.getElementById("canvasSpeedup").getContext("2d"); + window.Bar2 = new Chart(ctx2).Bar(speedupData, { + scaleLabel: " <%=value%>", + responsive: true + }); + console.log('Benchmarks completed.'); +}; diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/utility.js b/frameworks/cocos2d-html5/cocos2d/kazmath/utility.js new file mode 100644 index 0000000..9cd3fb4 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/utility.js @@ -0,0 +1,54 @@ +/** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2008, Luke Benstead. + 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. + + 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. + */ + + +/** + *

The main namespace of Cocos2d-html5's math library,
+ * all math core classes, functions, properties and constants are defined in this namespace

+ * @namespace + * @name cc.math + */ +cc.math = cc.math || {}; + +//cc.kmPIOver180 = 0.017453; please use cc.RAD + +//cc.kmPIUnder180 = 57.295779; please use cc.DEG + +cc.math.EPSILON = 1.0 / 64.0; //cc.kmEpsilon + +/** + * Returns the square of s (e.g. s*s) + * @param {Number} s + */ +cc.math.square = function(s){ + return s*s; +}; + +cc.math.almostEqual = function(lhs,rhs){ + return (lhs + cc.math.EPSILON > rhs && lhs - cc.math.EPSILON < rhs); +}; diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/vec2.js b/frameworks/cocos2d-html5/cocos2d/kazmath/vec2.js new file mode 100644 index 0000000..a682bc4 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/vec2.js @@ -0,0 +1,113 @@ +/** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2008, Luke Benstead. + 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. + + 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. + */ + +(function(cc){ + cc.math.Vec2 = function (x, y) { + if(y === undefined){ + this.x = x.x; + this.y = x.y; + }else{ + this.x = x || 0; + this.y = y || 0; + } + }; + + var proto = cc.math.Vec2.prototype; + proto.fill = function(x, y){ // = cc.kmVec2Fill + this.x = x; + this.y = y; + }; + + proto.length = function(){ // = cc.kmVec2Length + return Math.sqrt(cc.math.square(this.x) + cc.math.square(this.y)); + }; + + proto.lengthSq = function(){ // = cc.kmVec2LengthSq + return cc.math.square(this.x) + cc.math.square(this.y); + }; + + proto.normalize = function(){ // = cc.kmVec2Normalize + var l = 1.0 / this.length(); + this.x *= l; + this.y *= l; + return this; + }; + + cc.math.Vec2.add = function (pOut, pV1, pV2) { // = cc.kmVec2Add + pOut.x = pV1.x + pV2.x; + pOut.y = pV1.y + pV2.y; + return pOut + }; + + proto.add = function(vec){ // = cc.kmVec2Add + this.x += vec.x; + this.y += vec.y; + return this; + }; + + proto.dot = function (vec) { //cc.kmVec2Dot + return this.x * vec.x + this.y * vec.y; + }; + + cc.math.Vec2.subtract = function (pOut, pV1, pV2) { // = cc.kmVec2Subtract + pOut.x = pV1.x - pV2.x; + pOut.y = pV1.y - pV2.y; + return pOut; + }; + + proto.subtract = function(vec){ // = cc.kmVec2Subtract + this.x -= vec.x; + this.y -= vec.y; + return this; + }; + + proto.transform = function (mat3) { // = cc.kmVec2Transform + var x = this.x, y = this.y; + this.x = x * mat3.mat[0] + y * mat3.mat[3] + mat3.mat[6]; + this.y = x * mat3.mat[1] + y * mat3.mat[4] + mat3.mat[7]; + return this; + }; + + cc.math.Vec2.scale = function (pOut, pIn, s) { // = cc.kmVec2Scale + pOut.x = pIn.x * s; + pOut.y = pIn.y * s; + return pOut; + }; + + proto.scale = function(s) { // = cc.kmVec2Scale + this.x *= s; + this.y *= s; + return this; + }; + + proto.equals = function (vec) { // = cc.kmVec2AreEqual + return (this.x < vec.x + cc.math.EPSILON && this.x > vec.x - cc.math.EPSILON) && + (this.y < vec.y + cc.math.EPSILON && this.y > vec.y - cc.math.EPSILON); + }; +})(cc); + diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/vec3.js b/frameworks/cocos2d-html5/cocos2d/kazmath/vec3.js new file mode 100644 index 0000000..d9eafbe --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/vec3.js @@ -0,0 +1,199 @@ +/** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2008, Luke Benstead. + 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. + + 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. + */ + +(function(cc) { + /** + * A 3d vector. + * @class + * @param {number} [x] + * @param {number} [y] + * @param {number} [z] + */ + + cc.math.Vec3 = cc.kmVec3 = function (x, y, z) { + if(x && y === undefined){ + this.x = x.x; + this.y = x.y; + this.z = x.z; + } else { + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + } + }; + + cc.math.vec3 = function(x, y, z){ + return new cc.math.Vec3(x, y, z); + }; + + var _p = cc.math.Vec3.prototype; + + _p.fill = function (x, y, z) { // =cc.kmVec3Fill + if (x && y === undefined) { + this.x = x.x; + this.y = x.y; + this.z = x.z; + } else { + this.x = x; + this.y = y; + this.z = z; + } + return this; + }; + + _p.length = function () { //=cc.kmVec3Length + return Math.sqrt(cc.math.square(this.x) + cc.math.square(this.y) + cc.math.square(this.z)); + }; + + _p.lengthSq = function () { //=cc.kmVec3LengthSq + return cc.math.square(this.x) + cc.math.square(this.y) + cc.math.square(this.z) + }; + + _p.normalize = function () { //= cc.kmVec3Normalize + var l = 1.0 / this.length(); + this.x *= l; + this.y *= l; + this.z *= l; + return this; + }; + + _p.cross = function (vec3) { //= cc.kmVec3Cross + var x = this.x, y = this.y, z = this.z; + this.x = (y * vec3.z) - (z * vec3.y); + this.y = (z * vec3.x) - (x * vec3.z); + this.z = (x * vec3.y) - (y * vec3.x); + return this; + }; + + _p.dot = function (vec) { //= cc.kmVec3Dot + return ( this.x * vec.x + this.y * vec.y + this.z * vec.z ); + }; + + _p.add = function(vec){ //= cc.kmVec3Add + this.x += vec.x; + this.y += vec.y; + this.z += vec.z; + return this; + }; + + _p.subtract = function (vec) { // = cc.kmVec3Subtract + this.x -= vec.x; + this.y -= vec.y; + this.z -= vec.z; + return this; + }; + + _p.transform = function (mat4) { // = cc.kmVec3Transform + var x = this.x, y = this.y, z = this.z, mat = mat4.mat; + this.x = x * mat[0] + y * mat[4] + z * mat[8] + mat[12]; + this.y = x * mat[1] + y * mat[5] + z * mat[9] + mat[13]; + this.z = x * mat[2] + y * mat[6] + z * mat[10] + mat[14]; + return this; + }; + + _p.transformNormal = function(mat4){ + /* + a = (Vx, Vy, Vz, 0) + b = (a×M)T + Out = (bx, by, bz) + */ + //Omits the translation, only scaling + rotating + var x = this.x, y = this.y, z = this.z, mat = mat4.mat; + this.x = x * mat[0] + y * mat[4] + z * mat[8]; + this.y = x * mat[1] + y * mat[5] + z * mat[9]; + this.z = x * mat[2] + y * mat[6] + z * mat[10]; + return this; + }; + + _p.transformCoord = function(mat4){ // = cc.kmVec3TransformCoord + /* + a = (Vx, Vy, Vz, 1) + b = (a×M)T + Out = 1⁄bw(bx, by, bz) + */ + var v = new cc.math.Vec4(this.x, this.y, this.z, 1.0); + v.transform(mat4); + this.x = v.x / v.w; + this.y = v.y / v.w; + this.z = v.z / v.w; + return this; + }; + + _p.scale = function(scale){ // = cc.kmVec3Scale + this.x *= scale; + this.y *= scale; + this.z *= scale; + return this; + }; + + _p.equals = function(vec){ // = cc.kmVec3AreEqual + var EPSILON = cc.math.EPSILON; + return (this.x < (vec.x + EPSILON) && this.x > (vec.x - EPSILON)) && + (this.y < (vec.y + EPSILON) && this.y > (vec.y - EPSILON)) && + (this.z < (vec.z + EPSILON) && this.z > (vec.z - EPSILON)); + }; + + _p.inverseTransform = function(mat4){ //= cc.kmVec3InverseTransform + var mat = mat4.mat; + var v1 = new cc.math.Vec3(this.x - mat[12], this.y - mat[13], this.z - mat[14]); + this.x = v1.x * mat[0] + v1.y * mat[1] + v1.z * mat[2]; + this.y = v1.x * mat[4] + v1.y * mat[5] + v1.z * mat[6]; + this.z = v1.x * mat[8] + v1.y * mat[9] + v1.z * mat[10]; + return this; + }; + + _p.inverseTransformNormal = function(mat4){ // = cc.kmVec3InverseTransformNormal + var x = this.x, y = this.y, z = this.z, mat = mat4.mat; + this.x = x * mat[0] + y * mat[1] + z * mat[2]; + this.y = x * mat[4] + y * mat[5] + z * mat[6]; + this.z = x * mat[8] + y * mat[9] + z * mat[10]; + return this; + }; + + _p.assignFrom = function(vec){ + if(!vec) + return this; + this.x = vec.x; + this.y = vec.y; + this.z = vec.z; + return this; + }; + + cc.math.Vec3.zero = function(vec){ // = cc.kmVec3Zero + vec.x = vec.y = vec.z = 0.0; + return vec; + }; + + _p.toTypeArray = function(){ //cc.kmVec3ToTypeArray + var tyArr = new Float32Array(3); + tyArr[0] = this.x; + tyArr[1] = this.y; + tyArr[2] = this.z; + return tyArr; + }; +})(cc); diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/vec3SIMD.js b/frameworks/cocos2d-html5/cocos2d/kazmath/vec3SIMD.js new file mode 100644 index 0000000..0bf4ff4 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/vec3SIMD.js @@ -0,0 +1,51 @@ +/** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2008, Luke Benstead. + 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. + + 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. + */ + + (function(cc) { + var proto = cc.math.Vec3.prototype; + + proto.transformCoordSIMD = function(mat4){ + var vec = SIMD.float32x4(this.x, this.y, this.z, 0.0); + var mat0 = SIMD.float32x4.load(mat4.mat, 0); + var mat1 = SIMD.float32x4.load(mat4.mat, 4); + var mat2 = SIMD.float32x4.load(mat4.mat, 8); + var mat3 = SIMD.float32x4.load(mat4.mat, 12); + + //cc.kmVec4Transform(v, inV,pM); + var out = SIMD.float32x4.add( + SIMD.float32x4.add(SIMD.float32x4.mul(mat0, SIMD.float32x4.swizzle(vec, 0, 0, 0, 0)), + SIMD.float32x4.mul(mat1, SIMD.float32x4.swizzle(vec, 1, 1, 1, 1))), + SIMD.float32x4.add(SIMD.float32x4.mul(mat2, SIMD.float32x4.swizzle(vec, 2, 2, 2, 2)), + mat3)); + + out = SIMD.float32x4.div(out, SIMD.float32x4.swizzle(out, 3, 3, 3, 3)); + this.fill(out); + + return this; + }; +})(cc); diff --git a/frameworks/cocos2d-html5/cocos2d/kazmath/vec4.js b/frameworks/cocos2d-html5/cocos2d/kazmath/vec4.js new file mode 100644 index 0000000..fb2e376 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/kazmath/vec4.js @@ -0,0 +1,158 @@ +/** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2008, Luke Benstead. + 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. + + 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. + */ + +(function(cc) { + cc.math.Vec4 = function (x, y, z, w) { + if (x && y === undefined) { + this.x = x.x; + this.y = x.y; + this.z = x.z; + this.w = x.w; + } else { + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + this.w = w || 0; + } + }; + cc.kmVec4 = cc.math.Vec4; + var proto = cc.math.Vec4.prototype; + + proto.fill = function (x, y, z, w) { //=cc.kmVec4Fill + if (x && y === undefined) { + this.x = x.x; + this.y = x.y; + this.z = x.z; + this.w = x.w; + } else { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + }; + + proto.add = function(vec) { //cc.kmVec4Add + if(!vec) + return this; + this.x += vec.x; + this.y += vec.y; + this.z += vec.z; + this.w += vec.w; + return this; + }; + + proto.dot = function(vec){ //cc.kmVec4Dot + return ( this.x * vec.x + this.y * vec.y + this.z * vec.z + this.w * vec.w ); + }; + + proto.length = function(){ //=cc.kmVec4Length + return Math.sqrt(cc.math.square(this.x) + cc.math.square(this.y) + cc.math.square(this.z) + cc.math.square(this.w)); + }; + + proto.lengthSq = function(){ //=cc.kmVec4LengthSq + return cc.math.square(this.x) + cc.math.square(this.y) + cc.math.square(this.z) + cc.math.square(this.w); + }; + + proto.lerp = function(vec, t){ //= cc.kmVec4Lerp + //not implemented + return this; + }; + + proto.normalize = function() { // cc.kmVec4Normalize + var l = 1.0 / this.length(); + this.x *= l; + this.y *= l; + this.z *= l; + this.w *= l; + return this; + }; + + proto.scale = function(scale){ //= cc.kmVec4Scale + /// Scales a vector to the required length. This performs a Normalize before multiplying by S. + this.normalize(); + this.x *= scale; + this.y *= scale; + this.z *= scale; + this.w *= scale; + return this; + }; + + proto.subtract = function(vec) { + this.x -= vec.x; + this.y -= vec.y; + this.z -= vec.z; + this.w -= vec.w; + }; + + proto.transform = function(mat4) { + var x = this.x, y = this.y, z = this.z, w = this.w, mat = mat4.mat; + this.x = x * mat[0] + y * mat[4] + z * mat[8] + w * mat[12]; + this.y = x * mat[1] + y * mat[5] + z * mat[9] + w * mat[13]; + this.z = x * mat[2] + y * mat[6] + z * mat[10] + w * mat[14]; + this.w = x * mat[3] + y * mat[7] + z * mat[11] + w * mat[15]; + return this; + }; + + cc.math.Vec4.transformArray = function(vecArray, mat4){ + var retArray = []; + for (var i = 0; i < vecArray.length; i++) { + var selVec = new cc.math.Vec4(vecArray[i]); + selVec.transform(mat4); + retArray.push(selVec); + } + return retArray; + }; + + proto.equals = function(vec){ //=cc.kmVec4AreEqual + var EPSILON = cc.math.EPSILON; + return (this.x < vec.x + EPSILON && this.x > vec.x - EPSILON) && + (this.y < vec.y + EPSILON && this.y > vec.y - EPSILON) && + (this.z < vec.z + EPSILON && this.z > vec.z - EPSILON) && + (this.w < vec.w + EPSILON && this.w > vec.w - EPSILON); + }; + + proto.assignFrom = function(vec) { //= cc.kmVec4Assign + this.x = vec.x; + this.y = vec.y; + this.z = vec.z; + this.w = vec.w; + return this; + }; + + proto.toTypeArray = function(){ //cc.kmVec4ToTypeArray + var tyArr = new Float32Array(4); + tyArr[0] = this.x; + tyArr[1] = this.y; + tyArr[2] = this.z; + tyArr[3] = this.w; + return tyArr; + }; +})(cc); + + diff --git a/frameworks/cocos2d-html5/cocos2d/labels/CCLabelAtlas.js b/frameworks/cocos2d-html5/cocos2d/labels/CCLabelAtlas.js new file mode 100644 index 0000000..c5cd816 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/labels/CCLabelAtlas.js @@ -0,0 +1,224 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * using image file to print text label on the screen, might be a bit slower than cc.Label, similar to cc.LabelBMFont + * @class + * @extends cc.LabelBMFont + * + * @property {String} string - Content string of label + * + * @param {String} strText + * @param {String} charMapFile charMapFile or fntFile + * @param {Number} [itemWidth=0] + * @param {Number} [itemHeight=0] + * @param {Number} [startCharMap=""] + * @example + * //creates the cc.LabelAtlas with a string, a char map file(the atlas), the width and height of each element and the starting char of the atlas + * var myLabel = new cc.LabelAtlas('Text to display', 'CharMapfile.png', 12, 20, ' ') + * + * //creates the cc.LabelAtlas with a string, a fnt file + * var myLabel = new cc.LabelAtlas('Text to display', 'CharMapFile.plist‘); + */ +cc.LabelAtlas = cc.LabelBMFont.extend(/** @lends cc.LabelBMFont# */{ + _className: "LabelAtlas", + + /** + *

+ * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * Create a label atlas.
+ * It accepts two groups of parameters:
+ * a) string, fntFile
+ * b) label, textureFilename, width, height, startChar
+ *

+ * @param {String} strText + * @param {String} charMapFile charMapFile or fntFile + * @param {Number} [itemWidth=0] + * @param {Number} [itemHeight=0] + * @param {Number} [startCharMap=""] + */ + ctor: function (strText, charMapFile, itemWidth, itemHeight, startCharMap) { + cc.SpriteBatchNode.prototype.ctor.call(this); + this._imageOffset = cc.p(0, 0); + this._cascadeColorEnabled = true; + this._cascadeOpacityEnabled = true; + + charMapFile && cc.LabelAtlas.prototype.initWithString.call(this, strText, charMapFile, itemWidth, itemHeight, startCharMap); + }, + + _createRenderCmd: function () { + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) + return new cc.LabelBMFont.WebGLRenderCmd(this); + else + return new cc.LabelBMFont.CanvasRenderCmd(this); + }, + + _createFntConfig: function (texture, itemWidth, itemHeight, startCharMap) { + var fnt = {}; + fnt.commonHeight = itemHeight; + + var fontDefDictionary = fnt.fontDefDictionary = {}; + + var textureWidth = texture.pixelsWidth; + var textureHeight = texture.pixelsHeight; + + var startCharCode = startCharMap.charCodeAt(0); + var i = 0; + for (var col = itemHeight; col <= textureHeight; col += itemHeight) { + for (var row = 0; row < textureWidth; row += itemWidth) { + fontDefDictionary[startCharCode+i] = { + rect: {x: row, y: col - itemHeight, width:itemWidth, height: itemHeight }, + xOffset: 0, + yOffset: 0, + xAdvance: itemWidth + }; + ++i; + } + } + + fnt.kerningDict = {}; + + return fnt; + }, + + /** + *

+ * initializes the cc.LabelAtlas with a string, a char map file(the atlas),
+ * the width and height of each element and the starting char of the atlas
+ * It accepts two groups of parameters:
+ * a) string, fntFile
+ * b) label, textureFilename, width, height, startChar
+ *

+ * @param {String} strText + * @param {String|cc.Texture2D} charMapFile charMapFile or fntFile or texture file + * @param {Number} [itemWidth=0] + * @param {Number} [itemHeight=0] + * @param {Number} [startCharMap=""] + * @return {Boolean} returns true on success + */ + initWithString: function (strText, charMapFile, itemWidth, itemHeight, startCharMap) { + var label = strText + "", textureFilename, width, height, startChar; + var self = this, theString = label || ""; + this._initialString = theString; + self._string = theString; + + if (itemWidth === undefined) { + var dict = cc.loader.getRes(charMapFile); + if (parseInt(dict["version"], 10) !== 1) { + cc.log("cc.LabelAtlas.initWithString(): Unsupported version. Upgrade cocos2d version"); + return false; + } + + textureFilename = cc.path.changeBasename(charMapFile, dict["textureFilename"]); + var locScaleFactor = cc.contentScaleFactor(); + width = parseInt(dict["itemWidth"], 10) / locScaleFactor; + height = parseInt(dict["itemHeight"], 10) / locScaleFactor; + startChar = String.fromCharCode(parseInt(dict["firstChar"], 10)); + } else { + textureFilename = charMapFile; + width = itemWidth || 0; + height = itemHeight || 0; + startChar = startCharMap || " "; + } + + var texture; + if (charMapFile) { + self._fntFile = "dummy_fnt_file:" + textureFilename; + var spriteFrameBaseName = textureFilename; + var spriteFrame = cc.spriteFrameCache.getSpriteFrame(spriteFrameBaseName) || cc.spriteFrameCache.getSpriteFrame(cc.path.basename(spriteFrameBaseName)); + if(spriteFrame) { + texture = spriteFrame.getTexture(); + this._spriteFrame = spriteFrame; + } else { + texture = cc.textureCache.addImage(textureFilename); + } + + var newConf = this._createFntConfig(texture, width, height, startChar); + newConf.atlasName = textureFilename; + self._config = newConf; + + var locIsLoaded = texture.isLoaded(); + self._textureLoaded = locIsLoaded; + if (!locIsLoaded) { + texture.addEventListener("load", function (sender) { + var self1 = this; + self1._textureLoaded = true; + //reset the LabelBMFont + self1.initWithTexture(sender, self1._initialString.length); + self1.setString(self1._initialString, true); + self1.dispatchEvent("load"); + }, self); + } + } else { + texture = new cc.Texture2D(); + var image = new Image(); + texture.initWithElement(image); + self._textureLoaded = false; + } + + if (self.initWithTexture(texture, theString.length)) { + self._alignment = cc.TEXT_ALIGNMENT_LEFT; + self._imageOffset = cc.p(0, 0); + self._width = -1; + + self._realOpacity = 255; + self._realColor = cc.color(255, 255, 255, 255); + + self._contentSize.width = 0; + self._contentSize.height = 0; + + self.setString(theString, true); + return true; + } + return false; + }, + + setFntFile: function () { + cc.warn("setFntFile doesn't support with LabelAtlas."); + } + +}); + +/** + *

+ * Please use new cc.LabelAtlas instead.
+ * Create a label atlas.
+ * It accepts two groups of parameters:
+ * a) string, fntFile
+ * b) label, textureFilename, width, height, startChar
+ *

+ * @deprecated since v3.0 please use new cc.LabelAtlas + * @param {String} strText + * @param {String} charMapFile charMapFile or fntFile + * @param {Number} [itemWidth=0] + * @param {Number} [itemHeight=0] + * @param {Number} [startCharMap=""] + * @return {cc.LabelAtlas} returns the LabelAtlas object on success + */ +cc.LabelAtlas.create = function (strText, charMapFile, itemWidth, itemHeight, startCharMap) { + return new cc.LabelAtlas(strText, charMapFile, itemWidth, itemHeight, startCharMap); +}; + diff --git a/frameworks/cocos2d-html5/cocos2d/labels/CCLabelAtlasCanvasRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/labels/CCLabelAtlasCanvasRenderCmd.js new file mode 100644 index 0000000..890c98b --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/labels/CCLabelAtlasCanvasRenderCmd.js @@ -0,0 +1,110 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function(){ + cc.LabelAtlas.CanvasRenderCmd = function(renderableObject){ + cc.AtlasNode.CanvasRenderCmd.call(this, renderableObject); + this._needDraw = false; + }; + + var proto = cc.LabelAtlas.CanvasRenderCmd.prototype = Object.create(cc.AtlasNode.CanvasRenderCmd.prototype); + proto.constructor = cc.LabelAtlas.CanvasRenderCmd; + + proto.setCascade = function(){ + var node = this._node; + node._cascadeOpacityEnabled = true; + node._cascadeColorEnabled = false; + }; + + proto.updateAtlasValues = function(){ + var node = this._node; + var locString = node._string || ""; + var n = locString.length; + var texture = this._textureToRender; + var locItemWidth = node._itemWidth , locItemHeight = node._itemHeight; //needn't multiply cc.contentScaleFactor(), because sprite's draw will do this + + for (var i = 0, cr = -1; i < n; i++) { + var a = locString.charCodeAt(i) - node._mapStartChar.charCodeAt(0); + var row = parseInt(a % node._itemsPerRow, 10); + var col = parseInt(a / node._itemsPerRow, 10); + if(row < 0 || col < 0) + continue; + var rect = cc.rect(row * locItemWidth, col * locItemHeight, locItemWidth, locItemHeight); + var textureContent = texture._contentSize; + if(rect.x < 0 || rect.y < 0 || rect.x + rect.width > textureContent.width || rect.y + rect.height > textureContent.height) + continue; + + cr++; + var c = locString.charCodeAt(i); + var fontChar = node.getChildByTag(i); + if (!fontChar) { + fontChar = new cc.Sprite(); + if (c === 32) { + fontChar.init(); + fontChar.setTextureRect(cc.rect(0, 0, 10, 10), false, cc.size(0, 0)); + } else + fontChar.initWithTexture(texture, rect); + + cc.Node.prototype.addChild.call(node, fontChar, 0, i); + } else { + if (c === 32) { + fontChar.init(); + fontChar.setTextureRect(cc.rect(0, 0, 10, 10), false, cc.size(0, 0)); + } else { + // reusing fonts + fontChar.initWithTexture(texture, rect); + // restore to default in case they were modified + fontChar.visible = true; + } + } + fontChar.setPosition(cr * locItemWidth + locItemWidth / 2, locItemHeight / 2); + } + this.updateContentSize(i, cr+1); + }; + + proto.updateContentSize = function(i, cr){ + var node = this._node, + contentSize = node._contentSize; + if(i !== cr && i*node._itemWidth === contentSize.width && node._itemHeight === contentSize.height){ + node.setContentSize(cr * node._itemWidth, node._itemHeight); + } + }; + + proto.setString = function(label){ + var node = this._node; + if (node._children) { + var locChildren = node._children; + var len = locChildren.length; + for (var i = 0; i < len; i++) { + var child = locChildren[i]; + if (child && !child._lateChild) + child.visible = false; + } + } + }; + + proto._addChild = function(){ + child._lateChild = true; + }; +})(); \ No newline at end of file diff --git a/frameworks/cocos2d-html5/cocos2d/labels/CCLabelAtlasWebGLRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/labels/CCLabelAtlasWebGLRenderCmd.js new file mode 100644 index 0000000..5edf6c5 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/labels/CCLabelAtlasWebGLRenderCmd.js @@ -0,0 +1,170 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + cc.LabelAtlas.WebGLRenderCmd = function (renderable) { + cc.AtlasNode.WebGLRenderCmd.call(this, renderable); + this._needDraw = true; + }; + + var proto = cc.LabelAtlas.WebGLRenderCmd.prototype = Object.create(cc.AtlasNode.WebGLRenderCmd.prototype); + proto.constructor = cc.LabelAtlas.WebGLRenderCmd; + + proto._updateColor = function () { + if (this._colorF32Array) { + var locDisplayedColor = this._displayedColor; + var a = this._displayedOpacity / 255; + if (this._node._opacityModifyRGB) { + this._colorF32Array[0] = locDisplayedColor.r * a / 255; + this._colorF32Array[1] = locDisplayedColor.g * a / 255; + this._colorF32Array[2] = locDisplayedColor.b * a / 255; + this._colorF32Array[3] = a; + } + else { + this._colorF32Array[0] = locDisplayedColor.r / 255; + this._colorF32Array[1] = locDisplayedColor.g / 255; + this._colorF32Array[2] = locDisplayedColor.b / 255; + this._colorF32Array[3] = a; + } + } + }; + + proto.setCascade = function () { + var node = this._node; + node._cascadeOpacityEnabled = true; + node._cascadeColorEnabled = true; + }; + + proto.rendering = function (ctx) { + cc.AtlasNode.WebGLRenderCmd.prototype.rendering.call(this, ctx); + if (cc.LABELATLAS_DEBUG_DRAW) { + var node = this._node; + var s = node.getContentSize(); + var locRect = node.getBoundingBoxToWorld(); + var posX = locRect.x, + posY = locRect.y; + s.width = locRect.width; + s.height = locRect.height; + var vertices = [cc.p(posX, posY), cc.p(posX + s.width, posY), + cc.p(s.width + posX, s.height + posY), cc.p(posX, posY + s.height)]; + cc._drawingUtil.drawPoly(vertices, 4, true); + } + }; + + proto.updateAtlasValues = function () { + var node = this._node; + var locString = node._string; + var n = locString.length; + var locTextureAtlas = this._textureAtlas; + + var texture = locTextureAtlas.texture; + var textureWide = texture.pixelsWidth; + var textureHigh = texture.pixelsHeight; + var itemWidthInPixels = node._itemWidth; + var itemHeightInPixels = node._itemHeight; + if (!node._ignoreContentScaleFactor) { + itemWidthInPixels = node._itemWidth * cc.contentScaleFactor(); + itemHeightInPixels = node._itemHeight * cc.contentScaleFactor(); + } + if (n > locTextureAtlas.getCapacity()) + cc.log("cc.LabelAtlas._updateAtlasValues(): Invalid String length"); + var quads = locTextureAtlas.quads; + var locItemWidth = node._itemWidth; + var locItemHeight = node._itemHeight; + for (var i = 0, cr = -1; i < n; i++) { + var a = locString.charCodeAt(i) - node._mapStartChar.charCodeAt(0); + var row = a % node._itemsPerRow; + var col = 0 | (a / node._itemsPerRow); + if (row < 0 || col < 0) + continue; + if (row * locItemWidth + locItemWidth > textureWide || col * locItemHeight + locItemHeight > textureHigh) + continue; + + cr++; + var left, right, top, bottom; + if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) { + // Issue #938. Don't use texStepX & texStepY + left = (2 * row * itemWidthInPixels + 1) / (2 * textureWide); + right = left + (itemWidthInPixels * 2 - 2) / (2 * textureWide); + top = (2 * col * itemHeightInPixels + 1) / (2 * textureHigh); + bottom = top + (itemHeightInPixels * 2 - 2) / (2 * textureHigh); + } else { + left = row * itemWidthInPixels / textureWide; + right = left + itemWidthInPixels / textureWide; + top = col * itemHeightInPixels / textureHigh; + bottom = top + itemHeightInPixels / textureHigh; + } + var quad = quads[i]; + var locQuadTL = quad.tl, locQuadTR = quad.tr, locQuadBL = quad.bl, locQuadBR = quad.br; + locQuadTL.texCoords.u = left; + locQuadTL.texCoords.v = top; + locQuadTR.texCoords.u = right; + locQuadTR.texCoords.v = top; + locQuadBL.texCoords.u = left; + locQuadBL.texCoords.v = bottom; + locQuadBR.texCoords.u = right; + locQuadBR.texCoords.v = bottom; + + locQuadBL.vertices.x = (cr * locItemWidth); + locQuadBL.vertices.y = 0; + locQuadBL.vertices.z = 0.0; + locQuadBR.vertices.x = (cr * locItemWidth + locItemWidth); + locQuadBR.vertices.y = 0; + locQuadBR.vertices.z = 0.0; + locQuadTL.vertices.x = cr * locItemWidth; + locQuadTL.vertices.y = node._itemHeight; + locQuadTL.vertices.z = 0.0; + locQuadTR.vertices.x = cr * locItemWidth + locItemWidth; + locQuadTR.vertices.y = node._itemHeight; + locQuadTR.vertices.z = 0.0; + } + + this._updateColor(); + + this.updateContentSize(i, cr + 1); + if (n > 0) { + locTextureAtlas.dirty = true; + var totalQuads = locTextureAtlas.totalQuads; + if (n > totalQuads) + locTextureAtlas.increaseTotalQuadsWith(n - totalQuads); + } + }; + + proto.updateContentSize = function (i, cr) { + var node = this._node, + contentSize = node._contentSize; + if (i !== cr && i * node._itemWidth === contentSize.width && node._itemHeight === contentSize.height) { + node.setContentSize(cr * node._itemWidth, node._itemHeight); + } + }; + + proto.setString = function (label) { + var len = label.length; + if (len > this._textureAtlas.totalQuads) + this._textureAtlas.resizeCapacity(len); + }; + + proto._addChild = function () { + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/labels/CCLabelBMFont.js b/frameworks/cocos2d-html5/cocos2d/labels/CCLabelBMFont.js new file mode 100644 index 0000000..388f0e8 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/labels/CCLabelBMFont.js @@ -0,0 +1,1022 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + + Use any of these editors to generate BMFonts: + http://glyphdesigner.71squared.com/ (Commercial, Mac OS X) + http://www.n4te.com/hiero/hiero.jnlp (Free, Java) + http://slick.cokeandcode.com/demos/hiero.jnlp (Free, Java) + http://www.angelcode.com/products/bmfont/ (Free, Windows only) + ****************************************************************************/ + +/** + *

cc.LabelBMFont is a subclass of cc.SpriteBatchNode.

+ * + *

Features:
+ *

  • - Treats each character like a cc.Sprite. This means that each individual character can be:
  • + *
  • - rotated
  • + *
  • - scaled
  • + *
  • - translated
  • + *
  • - tinted
  • + *
  • - change the opacity
  • + *
  • - It can be used as part of a menu item.
  • + *
  • - anchorPoint can be used to align the "label"
  • + *
  • - Supports AngelCode text format

+ * + *

Limitations:
+ * - All inner characters are using an anchorPoint of (0.5, 0.5) and it is not recommend to change it + * because it might affect the rendering

+ * + *

cc.LabelBMFont implements the protocol cc.LabelProtocol, like cc.Label and cc.LabelAtlas.
+ * cc.LabelBMFont has the flexibility of cc.Label, the speed of cc.LabelAtlas and all the features of cc.Sprite.
+ * If in doubt, use cc.LabelBMFont instead of cc.LabelAtlas / cc.Label.

+ * + *

Supported editors:
+ * http://glyphdesigner.71squared.com/ (Commercial, Mac OS X)
+ * http://www.n4te.com/hiero/hiero.jnlp (Free, Java)
+ * http://slick.cokeandcode.com/demos/hiero.jnlp (Free, Java)
+ * http://www.angelcode.com/products/bmfont/ (Free, Windows only)

+ * @class + * @extends cc.SpriteBatchNode + * + * @property {String} string - Content string of label + * @property {Number} textAlign - Horizontal Alignment of label, cc.TEXT_ALIGNMENT_LEFT|cc.TEXT_ALIGNMENT_CENTER|cc.TEXT_ALIGNMENT_RIGHT + * @property {Number} boundingWidth - Width of the bounding box of label, the real content width is limited by boundingWidth + * + * @param {String} str + * @param {String} fntFile + * @param {Number} [width=-1] + * @param {Number} [alignment=cc.TEXT_ALIGNMENT_LEFT] + * @param {cc.Point} [imageOffset=cc.p(0,0)] + * + * @example + * // Example 01 + * var label1 = new cc.LabelBMFont("Test case", "test.fnt"); + * + * // Example 02 + * var label2 = new cc.LabelBMFont("test case", "test.fnt", 200, cc.TEXT_ALIGNMENT_LEFT); + * + * // Example 03 + * var label3 = new cc.LabelBMFont("This is a \n test case", "test.fnt", 200, cc.TEXT_ALIGNMENT_LEFT, cc.p(0,0)); + */ +cc.LabelBMFont = cc.SpriteBatchNode.extend(/** @lends cc.LabelBMFont# */{ + //property string is Getter and Setter. + //property textAlign is Getter and Setter. + //property boundingWidth is Getter and Setter. + _opacityModifyRGB: false, + + _string: "", + _config: null, + + // name of fntFile + _fntFile: "", + + // initial string without line breaks + _initialString: "", + + // alignment of all lines + _alignment: cc.TEXT_ALIGNMENT_CENTER, + + // max width until a line break is added + _width: -1, + _lineBreakWithoutSpaces: false, + _imageOffset: null, + + _textureLoaded: false, + _className: "LabelBMFont", + + _createRenderCmd: function () { + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) + return new cc.LabelBMFont.WebGLRenderCmd(this); + else + return new cc.LabelBMFont.CanvasRenderCmd(this); + }, + + _setString: function (newString, needUpdateLabel) { + if (!needUpdateLabel) { + this._string = newString; + } else { + this._initialString = newString; + } + + var locChildren = this._children; + if (locChildren) { + for (var i = 0; i < locChildren.length; i++) { + var selNode = locChildren[i]; + if (selNode) + selNode.setVisible(false); + } + } + if (this._textureLoaded) { + if(this._string && this._string.length > 0) { + this.createFontChars(); + } + if (needUpdateLabel) + this.updateLabel(); + } + }, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * creates a bitmap font atlas with an initial string and the FNT file. + * @param {String} str + * @param {String} fntFile + * @param {Number} [width=-1] + * @param {Number} [alignment=cc.TEXT_ALIGNMENT_LEFT] + * @param {cc.Point} [imageOffset=cc.p(0,0)] + */ + ctor: function (str, fntFile, width, alignment, imageOffset) { + cc.SpriteBatchNode.prototype.ctor.call(this); + this._imageOffset = cc.p(0, 0); + this._cascadeColorEnabled = true; + this._cascadeOpacityEnabled = true; + this.initWithString(str, fntFile, width, alignment, imageOffset); + }, + + /** + * return texture is loaded + * @returns {boolean} + */ + textureLoaded: function () { + return this._textureLoaded; + }, + + /** + * add texture loaded event listener.
+ * Will execute the callback in the loaded. + * @param {Function} callback + * @param {Object} target + * @deprecated since 3.1, please use addEventListener instead + */ + addLoadedEventListener: function (callback, target) { + this.addEventListener("load", callback, target); + }, + + /** + * Conforms to cc.RGBAProtocol protocol. + * @return {Boolean} + */ + isOpacityModifyRGB: function () { + return this._opacityModifyRGB; + }, + + /** + * Set whether to support cc.RGBAProtocol protocol + * @param {Boolean} opacityModifyRGB + */ + setOpacityModifyRGB: function (opacityModifyRGB) { + this._opacityModifyRGB = opacityModifyRGB; + var locChildren = this._children; + if (locChildren) { + for (var i = 0; i < locChildren.length; i++) { + var node = locChildren[i]; + if (node) + node.opacityModifyRGB = this._opacityModifyRGB; + } + } + }, + + _changeTextureColor: function () { + this._renderCmd._changeTextureColor(); + }, + + /** + * Initialization of the node, please do not call this function by yourself, you should pass the parameters to constructor to initialize it
. + */ + init: function () { + return this.initWithString(null, null, null, null, null); + }, + + /** + * init a bitmap font atlas with an initial string and the FNT file + * @param {String} str + * @param {String} fntFile + * @param {Number} [width=-1] + * @param {Number} [alignment=cc.TEXT_ALIGNMENT_LEFT] + * @param {cc.Point} [imageOffset=cc.p(0,0)] + * @return {Boolean} + */ + initWithString: function (str, fntFile, width, alignment, imageOffset) { + var self = this, theString = str || ""; + + if (self._config) + cc.log("cc.LabelBMFont.initWithString(): re-init is no longer supported"); + + var texture; + if (fntFile) { + var newConf = cc.loader.getRes(fntFile); + if (!newConf) { + newConf = cc.FntFrameCache[fntFile] || cc.FntFrameCache[cc.path.basename(fntFile)]; + if(!newConf) { + cc.log("cc.LabelBMFont.initWithString(): Impossible to create font. Please check file"); + return false; + } + } + + self._config = newConf; + self._fntFile = fntFile; + var spriteFrameBaseName = newConf.atlasName; + var spriteFrame = cc.spriteFrameCache.getSpriteFrame(spriteFrameBaseName) || cc.spriteFrameCache.getSpriteFrame(cc.path.basename(spriteFrameBaseName)); + if(spriteFrame) { + texture = spriteFrame.getTexture(); + this._spriteFrame = spriteFrame; + } else { + texture = cc.textureCache.addImage(newConf.atlasName); + } + var locIsLoaded = texture.isLoaded(); + self._textureLoaded = locIsLoaded; + if (!locIsLoaded) { + texture.addEventListener("load", function (sender) { + var self1 = this; + self1._textureLoaded = true; + //reset the LabelBMFont + self1.initWithTexture(sender, self1._initialString.length); + self1.setString(self1._initialString, true); + self1.dispatchEvent("load"); + }, self); + } + } else { + texture = new cc.Texture2D(); + var image = new Image(); + texture.initWithElement(image); + self._textureLoaded = false; + } + + if (self.initWithTexture(texture, theString.length)) { + self._alignment = alignment || cc.TEXT_ALIGNMENT_LEFT; + self._imageOffset = imageOffset || cc.p(0, 0); + self._width = (width === undefined) ? -1 : width; + + self._realOpacity = 255; + self._realColor = cc.color(255, 255, 255, 255); + + self._contentSize.width = 0; + self._contentSize.height = 0; + + self.setAnchorPoint(0.5, 0.5); + + self.setString(theString, true); + + return true; + } + return false; + }, + + /** + * updates the font chars based on the string to render + */ + createFontChars: function () { + var locStr = this._string; + var stringLen = locStr ? locStr.length : 0; + + var self = this; + var cmd = this._renderCmd; + var locTexture = cmd._texture || this._texture; + + var nextFontPositionX = 0; + + var tmpSize = cc.size(0, 0); + + var longestLine = 0; + + var quantityOfLines = 1; + + + var i, locCfg = self._config, locKerningDict = locCfg.kerningDict, + locCommonH = locCfg.commonHeight, locFontDict = locCfg.fontDefDictionary; + for (i = 0; i < stringLen - 1; i++) { + if (locStr.charCodeAt(i) === 10) quantityOfLines++; + } + + var totalHeight = locCommonH * quantityOfLines; + var nextFontPositionY = -(locCommonH - locCommonH * quantityOfLines); + + var prev = -1; + var fontDef; + for (i = 0; i < stringLen; i++) { + var key = locStr.charCodeAt(i); + if (key === 0) continue; + + if (key === 10) { + //new line + nextFontPositionX = 0; + nextFontPositionY -= locCfg.commonHeight; + continue; + } + + var kerningAmount = locKerningDict[(prev << 16) | (key & 0xffff)] || 0; + fontDef = locFontDict[key]; + if (!fontDef) { + cc.log("cocos2d: LabelBMFont: character not found " + locStr[i]); + + fontDef = { + rect: { + x: 0, + y: 0, + width: 0, + height: 0 + }, + xOffset: 0, + yOffset: 0, + xAdvance: 0 + }; + } + + var rect = cc.rect(fontDef.rect.x, fontDef.rect.y, fontDef.rect.width, fontDef.rect.height); + rect = cc.rectPixelsToPoints(rect); + rect.x += self._imageOffset.x; + rect.y += self._imageOffset.y; + + var isRotated = false; + if(this._spriteFrame) { + var textureWidth = locTexture.width; + var spriteFrameRect = this._spriteFrame._rect; + if(!this._spriteFrame._rotated) { + rect.x = rect.x + spriteFrameRect.x; + rect.y = rect.y + spriteFrameRect.y; + } else { + isRotated = true; + var originalX = rect.x; + rect.x = rect.y + spriteFrameRect.x; + rect.y = originalX + spriteFrameRect.y; + } + } + + var fontChar = self.getChildByTag(i); + + if (!fontChar) { + fontChar = new cc.Sprite(); + fontChar.initWithTexture(locTexture, rect, isRotated); + fontChar._newTextureWhenChangeColor = true; + this.addChild(fontChar, 0, i); + } else { + cmd._updateCharTexture(fontChar, rect, key, isRotated); + } + + // Apply label properties + fontChar.opacityModifyRGB = this._opacityModifyRGB; + cmd._updateCharColorAndOpacity(fontChar); + + var yOffset = locCfg.commonHeight - fontDef.yOffset; + var fontPos = cc.p(nextFontPositionX + fontDef.xOffset + fontDef.rect.width * 0.5 + kerningAmount, + nextFontPositionY + yOffset - rect.height * 0.5 * cc.contentScaleFactor()); + fontChar.setPosition(cc.pointPixelsToPoints(fontPos)); + + // update kerning + nextFontPositionX += fontDef.xAdvance + kerningAmount; + prev = key; + + if (longestLine < nextFontPositionX) + longestLine = nextFontPositionX; + } + + //If the last character processed has an xAdvance which is less that the width of the characters image, then we need + // to adjust the width of the string to take this into account, or the character will overlap the end of the bounding box + if (fontDef && fontDef.xAdvance < fontDef.rect.width) + tmpSize.width = longestLine - fontDef.xAdvance + fontDef.rect.width; + else + tmpSize.width = longestLine; + tmpSize.height = totalHeight; + self.setContentSize(cc.sizePixelsToPoints(tmpSize)); + }, + + /** + * Update String.
+ * Only update this label display string. + * @param {Boolean} fromUpdate + */ + updateString: function (fromUpdate) { + var self = this; + var locChildren = self._children; + if (locChildren) { + var length = locChildren.length; + for (var i = 0, li = length; i < li; i++) { + var node = locChildren[i]; + if (node) node.visible = false; + } + } + if (self._config) { + if(self._string && self._string.length > 0) { + self.createFontChars(); + } + } + + if (!fromUpdate) + self.updateLabel(); + }, + + /** + * Gets the text of this label + * @return {String} + */ + getString: function () { + return this._initialString; + }, + + /** + * Set the text + * @param {String} newString + * @param {Boolean|null} needUpdateLabel + */ + setString: function (newString, needUpdateLabel) { + newString = String(newString); + if (needUpdateLabel === undefined) + needUpdateLabel = true; + if (newString === undefined || typeof newString !== 'string') + newString = newString + ""; + + this._initialString = newString; + this._setString(newString, needUpdateLabel); + }, + + _setStringForSetter: function (newString) { + this.setString(newString, false); + }, + + /** + * Set the text.
+ * Change this Label display string. + * @deprecated since v3.0 please use .setString + * @param label + */ + setCString: function (label) { + this.setString(label, true); + }, + + // calc the text all with in a line + _getCharsWidth: function (startIndex, endIndex) { + if (endIndex <= 0) { + return 0; + } + var curTextFirstSprite = this.getChildByTag(startIndex); + var curTextLastSprite = this.getChildByTag(startIndex + endIndex); + return this._getLetterPosXLeft(curTextLastSprite) - this._getLetterPosXLeft(curTextFirstSprite); + }, + + _checkWarp: function (strArr, i, maxWidth, initStringWrapNum) { + var self = this; + var text = strArr[i]; + var curLength = 0; + for (var strArrIndex = 0; strArrIndex < i; strArrIndex++) { + curLength += strArr[strArrIndex].length; + } + + curLength = curLength + i - initStringWrapNum; // add the wrap line num + + var allWidth = self._getCharsWidth(curLength, strArr[i].length - 1); + + if (allWidth > maxWidth && text.length > 1) { + var fuzzyLen = text.length * ( maxWidth / allWidth ) | 0; + var tmpText = text.substr(fuzzyLen); + var width = allWidth - this._getCharsWidth(curLength + fuzzyLen, tmpText.length - 1); + var sLine; + var pushNum = 0; + + //Increased while cycle maximum ceiling. default 100 time + var checkWhile = 0; + + //Exceeded the size + while (width > maxWidth && checkWhile++ < 100) { + fuzzyLen *= maxWidth / width; + fuzzyLen = fuzzyLen | 0; + tmpText = text.substr(fuzzyLen); + width = allWidth - this._getCharsWidth(curLength + fuzzyLen, tmpText.length - 1); + } + + checkWhile = 0; + + //Find the truncation point + while (width < maxWidth && checkWhile++ < 100) { + if (tmpText) { + var exec = cc.LabelTTF._wordRex.exec(tmpText); + pushNum = exec ? exec[0].length : 1; + sLine = tmpText; + } + if (self._lineBreakWithoutSpaces) { + pushNum = 0; + } + fuzzyLen = fuzzyLen + pushNum; + tmpText = text.substr(fuzzyLen); + width = allWidth - this._getCharsWidth(curLength + fuzzyLen, tmpText.length - 1); + } + + fuzzyLen -= pushNum; + if (fuzzyLen === 0) { + fuzzyLen = 1; + sLine = sLine.substr(1); + } + + var sText = text.substr(0, fuzzyLen), result; + + //symbol in the first + if (cc.LabelTTF.wrapInspection) { + if (cc.LabelTTF._symbolRex.test(sLine || tmpText)) { + result = cc.LabelTTF._lastWordRex.exec(sText); + pushNum = result ? result[0].length : 0; + if (self._lineBreakWithoutSpaces) { + pushNum = 0; + } + fuzzyLen -= pushNum; + + sLine = text.substr(fuzzyLen); + sText = text.substr(0, fuzzyLen); + } + } + + //To judge whether a English words are truncated + if (cc.LabelTTF._firsrEnglish.test(sLine)) { + result = cc.LabelTTF._lastEnglish.exec(sText); + if (result && sText !== result[0]) { + pushNum = result[0].length; + if (self._lineBreakWithoutSpaces) { + pushNum = 0; + } + fuzzyLen -= pushNum; + sLine = text.substr(fuzzyLen); + sText = text.substr(0, fuzzyLen); + } + } + strArr[i] = sLine || tmpText; + strArr.splice(i, 0, sText); + } + }, + + /** + * Update Label.
+ * Update this Label display string and more... + */ + updateLabel: function () { + var self = this; + self.string = self._initialString; + + var i, j, characterSprite; + // process string + // Step 1: Make multiline + if (self._width > 0) { + var stringArr = self.string.split('\n'); + var wrapString = ""; + var newWrapNum = 0; + var oldArrLength = 0; + for (i = 0; i < stringArr.length; i++) { + oldArrLength = stringArr.length; + this._checkWarp(stringArr, i, self._width * this._scaleX, newWrapNum); + if (oldArrLength < stringArr.length) { + newWrapNum++; + } + if (i > 0) { + wrapString += "\n"; + } + wrapString += stringArr[i]; + } + wrapString = wrapString + String.fromCharCode(0); + self._setString(wrapString, false); + } + // Step 2: Make alignment + if (self._alignment !== cc.TEXT_ALIGNMENT_LEFT) { + i = 0; + + var lineNumber = 0; + var strlen = self._string.length; + var last_line = []; + + for (var ctr = 0; ctr < strlen; ctr++) { + if (self._string[ctr].charCodeAt(0) === 10 || self._string[ctr].charCodeAt(0) === 0) { + var lineWidth = 0; + var line_length = last_line.length; + // if last line is empty we must just increase lineNumber and work with next line + if (line_length === 0) { + lineNumber++; + continue; + } + var index = i + line_length - 1 + lineNumber; + if (index < 0) continue; + + var lastChar = self.getChildByTag(index); + if (lastChar == null) + continue; + lineWidth = lastChar.getPositionX() + lastChar._getWidth() / 2; + + var shift = 0; + switch (self._alignment) { + case cc.TEXT_ALIGNMENT_CENTER: + shift = self.width / 2 - lineWidth / 2; + break; + case cc.TEXT_ALIGNMENT_RIGHT: + shift = self.width - lineWidth; + break; + default: + break; + } + + if (shift !== 0) { + for (j = 0; j < line_length; j++) { + index = i + j + lineNumber; + if (index < 0) continue; + characterSprite = self.getChildByTag(index); + if (characterSprite) + characterSprite.x += shift; + } + } + + i += line_length; + lineNumber++; + + last_line.length = 0; + continue; + } + last_line.push(self._string[i]); + } + } + }, + + /** + * Set text alignment. + * @param {Number} alignment + */ + setAlignment: function (alignment) { + this._alignment = alignment; + this.updateLabel(); + }, + + _getAlignment: function () { + return this._alignment; + }, + + /** + * Set the bounding width.
+ * max with display width. The exceeding string will be wrapping. + * @param {Number} width + */ + setBoundingWidth: function (width) { + this._width = width; + this.updateLabel(); + }, + + _getBoundingWidth: function () { + return this._width; + }, + + /** + * Set the param to change English word warp according to whether the space.
+ * default is false. + * @param {Boolean} breakWithoutSpace + */ + setLineBreakWithoutSpace: function (breakWithoutSpace) { + this._lineBreakWithoutSpaces = breakWithoutSpace; + this.updateLabel(); + }, + + /** + * Set scale.
+ * Input a number, will be decrease or increase the font size.
+ * @param {Number} scale + * @param {Number} [scaleY=null] default is scale + */ + setScale: function (scale, scaleY) { + cc.Node.prototype.setScale.call(this, scale, scaleY); + this.updateLabel(); + }, + + /** + * Set scale of x.
+ * Input a number, will be decrease or increase the font size.
+ * Horizontal scale. + * @param {Number} scaleX + */ + setScaleX: function (scaleX) { + cc.Node.prototype.setScaleX.call(this, scaleX); + this.updateLabel(); + }, + + /** + * Set scale of x.
+ * Input a number, will be decrease or increase the font size.
+ * Longitudinal scale. + * @param {Number} scaleY + */ + setScaleY: function (scaleY) { + cc.Node.prototype.setScaleY.call(this, scaleY); + this.updateLabel(); + }, + + /** + * set fnt file path.
+ * Change the fnt file path. + * @param {String} fntFile + */ + setFntFile: function (fntFile) { + var self = this; + if (fntFile != null && fntFile !== self._fntFile) { + var newConf = cc.loader.getRes(fntFile); + + if (!newConf) { + cc.log("cc.LabelBMFont.setFntFile() : Impossible to create font. Please check file"); + return; + } + + self._fntFile = fntFile; + self._config = newConf; + + var texture = cc.textureCache.addImage(newConf.atlasName); + var locIsLoaded = texture.isLoaded(); + self._textureLoaded = locIsLoaded; + if (!locIsLoaded) { + texture.addEventListener("load", function (sender) { + var self1 = this; + self1._textureLoaded = true; + self1.setTexture(sender); + if(self1._string && self1._string.length > 0) { + self1.createFontChars(); + } + + self1._changeTextureColor(); + self1.updateLabel(); + + self1.dispatchEvent("load"); + }, self); + } else { + self.setTexture(texture); + if(self._string && self._string.length > 0) { + self.createFontChars(); + } + } + } + }, + + /** + * Return the fnt file path. + * @return {String} + */ + getFntFile: function () { + return this._fntFile; + }, + + setTexture: function (texture) { + this._texture = texture; + this._renderCmd.setTexture(texture); + }, + + /** + * Set the AnchorPoint of the labelBMFont.
+ * In order to change the location of label. + * @override + * @param {cc.Point|Number} point The anchor point of labelBMFont or The anchor point.x of labelBMFont. + * @param {Number} [y] The anchor point.y of labelBMFont. + */ + setAnchorPoint: function (point, y) { + cc.Node.prototype.setAnchorPoint.call(this, point, y); + this.updateLabel(); + }, + + _setAnchorX: function (x) { + cc.Node.prototype._setAnchorX.call(this, x); + this.updateLabel(); + }, + + _setAnchorY: function (y) { + cc.Node.prototype._setAnchorY.call(this, y); + this.updateLabel(); + }, + + _atlasNameFromFntFile: function (fntFile) { + }, + + _kerningAmountForFirst: function (first, second) { + var ret = 0; + var key = (first << 16) | (second & 0xffff); + if (this._configuration.kerningDictionary) { + var element = this._configuration.kerningDictionary[key.toString()]; + if (element) + ret = element.amount; + } + return ret; + }, + + _getLetterPosXLeft: function (sp) { + return sp.getPositionX() * this._scaleX - (sp._getWidth() * this._scaleX * sp._getAnchorX()); + }, + + _getLetterPosXRight: function (sp) { + return sp.getPositionX() * this._scaleX + (sp._getWidth() * this._scaleX * sp._getAnchorX()); + }, + + //Checking whether the character is a whitespace + _isspace_unicode: function (ch) { + ch = ch.charCodeAt(0); + return ((ch >= 9 && ch <= 13) || ch === 32 || ch === 133 || ch === 160 || ch === 5760 + || (ch >= 8192 && ch <= 8202) || ch === 8232 || ch === 8233 || ch === 8239 + || ch === 8287 || ch === 12288); + }, + + _utf8_trim_ws: function (str) { + var len = str.length; + + if (len <= 0) + return; + + var last_index = len - 1; + + // Only start trimming if the last character is whitespace.. + if (this._isspace_unicode(str[last_index])) { + for (var i = last_index - 1; i >= 0; --i) { + if (this._isspace_unicode(str[i])) { + last_index = i; + } + else { + break; + } + } + this._utf8_trim_from(str, last_index); + } + }, + + //Trims str st str=[0, index) after the operation. + //Return value: the trimmed string. + _utf8_trim_from: function (str, index) { + var len = str.length; + if (index >= len || index < 0) + return; + str.splice(index, len); + } +}); + +(function () { + var p = cc.LabelBMFont.prototype; + cc.EventHelper.prototype.apply(p); + + /** @expose */ + p.string; + cc.defineGetterSetter(p, "string", p.getString, p._setStringForSetter); + /** @expose */ + p.boundingWidth; + cc.defineGetterSetter(p, "boundingWidth", p._getBoundingWidth, p.setBoundingWidth); + /** @expose */ + p.textAlign; + cc.defineGetterSetter(p, "textAlign", p._getAlignment, p.setAlignment); + + // Override properties + cc.defineGetterSetter(p, "texture", p.getTexture, p.setTexture); +})(); + +/** + * creates a bitmap font atlas with an initial string and the FNT file + * @deprecated since v3.0 please use new cc.LabelBMFont + * @param {String} str + * @param {String} fntFile + * @param {Number} [width=-1] + * @param {Number} [alignment=cc.TEXT_ALIGNMENT_LEFT] + * @param {cc.Point} [imageOffset=cc.p(0,0)] + * @return {cc.LabelBMFont|Null} + */ +cc.LabelBMFont.create = function (str, fntFile, width, alignment, imageOffset) { + return new cc.LabelBMFont(str, fntFile, width, alignment, imageOffset); +}; + +cc.FntFrameCache = {}; + +var _fntLoader = { + FNT_HEAD: /fntframes [^\n]*(\n|$)/gi, + FNT_FRAME_NAME: /fntframe [^\n]*(\n|$)/gi, + INFO_EXP: /info [^\n]*(\n|$)/gi, + COMMON_EXP: /common [^\n]*(\n|$)/gi, + PAGE_EXP: /page [^\n]*(\n|$)/gi, + CHAR_EXP: /char [^\n]*(\n|$)/gi, + KERNING_EXP: /kerning [^\n]*(\n|$)/gi, + ITEM_EXP: /\w+=[^ \r\n]+/gi, + INT_EXP: /^[\-]?\d+$/, + + _parseStrToObj: function (str) { + var arr = str.match(this.ITEM_EXP); + var obj = {}; + if (arr) { + for (var i = 0, li = arr.length; i < li; i++) { + var tempStr = arr[i]; + var index = tempStr.indexOf("="); + var key = tempStr.substring(0, index); + var value = tempStr.substring(index + 1); + if (value.match(this.INT_EXP)) value = parseInt(value); + else if (value[0] === '"') value = value.substring(1, value.length - 1); + obj[key] = value; + } + } + return obj; + }, + + _parseFntContent: function (fnt, fntStr, url, useAtlas) { + var self = this; + //common + var commonObj = self._parseStrToObj(fntStr.match(self.COMMON_EXP)[0]); + fnt.commonHeight = commonObj["lineHeight"]; + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + var texSize = cc.configuration.getMaxTextureSize(); + if (commonObj["scaleW"] > texSize.width || commonObj["scaleH"] > texSize.height) + cc.log("cc.LabelBMFont._parseCommonArguments(): page can't be larger than supported"); + } + if (commonObj["pages"] !== 1) cc.log("cc.LabelBMFont._parseCommonArguments(): only supports 1 page"); + + //page + var pageObj = self._parseStrToObj(fntStr.match(self.PAGE_EXP)[0]); + if (pageObj["id"] !== 0) cc.log("cc.LabelBMFont._parseImageFileName() : file could not be found"); + if(!useAtlas) { + fnt.atlasName = cc.path.changeBasename(url, pageObj["file"]); + } else { + fnt.atlasName = cc.path.join(cc.path.dirname(useAtlas.path) + pageObj["file"]); + } + + //char + var charLines = fntStr.match(self.CHAR_EXP); + var fontDefDictionary = fnt.fontDefDictionary = {}; + for (var i = 0, li = charLines.length; i < li; i++) { + var charObj = self._parseStrToObj(charLines[i]); + var charId = charObj["id"]; + fontDefDictionary[charId] = { + rect: {x: charObj["x"], y: charObj["y"], width: charObj["width"], height: charObj["height"]}, + xOffset: charObj["xoffset"], + yOffset: charObj["yoffset"], + xAdvance: charObj["xadvance"] + }; + } + + //kerning + var kerningDict = fnt.kerningDict = {}; + var kerningLines = fntStr.match(self.KERNING_EXP); + if (kerningLines) { + for (var i = 0, li = kerningLines.length; i < li; i++) { + var kerningObj = self._parseStrToObj(kerningLines[i]); + kerningDict[(kerningObj["first"] << 16) | (kerningObj["second"] & 0xffff)] = kerningObj["amount"]; + } + } + + return fnt; + }, + + /** + * Parse Fnt string. + * @param fntStr + * @param url + * @returns {{}} + */ + parseFnt: function (fntStr, url) { + var self = this, fnt = {}; + var headString = fntStr.match(self.FNT_HEAD); + if(headString) { + var headObj = self._parseStrToObj(headString[0]); + if(headObj && headObj.count) { + fntStr = fntStr.substr(headString[0].length); + var fntFrames = fntStr.split("----"); + for(var i = 0; i < headObj.count; ++i) { + var contentString = fntFrames[i]; + var frameNameStr = contentString.match(self.FNT_FRAME_NAME); + if(frameNameStr) { + var frameName = self._parseStrToObj(frameNameStr[0]); + if(frameName && frameName.name) { + fnt = {}; + var realFntPathKey = cc.path.join(cc.path.dirname(url), frameName.name); + cc.FntFrameCache[realFntPathKey] = this._parseFntContent(fnt, contentString.substr(frameNameStr[0].length), url, {path: frameName.name}); + } + } + } + } + } else { + fnt = this._parseFntContent(fnt, fntStr, url); + } + return fnt; + }, + + /** + * load the fnt + * @param realUrl + * @param url + * @param res + * @param cb + */ + load: function (realUrl, url, res, cb) { + var self = this; + cc.loader.loadTxt(realUrl, function (err, txt) { + if (err) return cb(err); + cb(null, self.parseFnt(txt, url)); + }); + } +}; +cc.loader.register(["fnt"], _fntLoader); diff --git a/frameworks/cocos2d-html5/cocos2d/labels/CCLabelBMFontCanvasRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/labels/CCLabelBMFontCanvasRenderCmd.js new file mode 100644 index 0000000..5861daa --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/labels/CCLabelBMFontCanvasRenderCmd.js @@ -0,0 +1,101 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + + Use any of these editors to generate BMFonts: + http://glyphdesigner.71squared.com/ (Commercial, Mac OS X) + http://www.n4te.com/hiero/hiero.jnlp (Free, Java) + http://slick.cokeandcode.com/demos/hiero.jnlp (Free, Java) + http://www.angelcode.com/products/bmfont/ (Free, Windows only) + ****************************************************************************/ + +(function () { + cc.LabelBMFont.CanvasRenderCmd = function (renderableObject) { + this._rootCtor(renderableObject); + }; + + var proto = cc.LabelBMFont.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + proto.constructor = cc.LabelBMFont.CanvasRenderCmd; + + proto._updateCharTexture = function (fontChar, rect, key) { + if (key === 32) { + fontChar.setTextureRect(rect, false, cc.size(0, 0)); + } else { + // updating previous sprite + fontChar.setTextureRect(rect, false); + // restore to default in case they were modified + fontChar.visible = true; + } + }; + + proto._updateCharColorAndOpacity = function (fontChar) { + // Color MUST be set before opacity, since opacity might change color if OpacityModifyRGB is on + fontChar._displayedColor = this._displayedColor; + fontChar._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.colorDirty); + fontChar._displayedOpacity = this._displayedOpacity; + fontChar._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.opacityDirty); + }; + + proto.setTexture = function (texture) { + var node = this._node; + var locChildren = node._children; + var locDisplayedColor = this._displayedColor; + for (var i = 0; i < locChildren.length; i++) { + var selChild = locChildren[i]; + var cm = selChild._renderCmd; + var childDColor = cm._displayedColor; + if (node._texture !== cm._texture && (childDColor.r !== locDisplayedColor.r || + childDColor.g !== locDisplayedColor.g || childDColor.b !== locDisplayedColor.b)) + continue; + selChild.texture = texture; + } + node._texture = texture; + }; + + proto._changeTextureColor = function () { + var node = this._node; + var texture = node._texture, + contentSize = texture.getContentSize(); + + var oTexture = node._texture, + oElement = oTexture.getHtmlElementObj(); + var disColor = this._displayedColor; + var textureRect = cc.rect(0, 0, oElement.width, oElement.height); + if (texture && contentSize.width > 0) { + if (!oElement) + return; + var textureToRender = oTexture._generateColorTexture(disColor.r, disColor.g, disColor.b, textureRect); + node.setTexture(textureToRender); + } + }; + + proto._updateChildrenDisplayedOpacity = function (locChild) { + cc.Node.prototype.updateDisplayedOpacity.call(locChild, this._displayedOpacity); + }; + + proto._updateChildrenDisplayedColor = function (locChild) { + cc.Node.prototype.updateDisplayedColor.call(locChild, this._displayedColor); + }; + +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/labels/CCLabelBMFontWebGLRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/labels/CCLabelBMFontWebGLRenderCmd.js new file mode 100644 index 0000000..09e18a5 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/labels/CCLabelBMFontWebGLRenderCmd.js @@ -0,0 +1,57 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + + Use any of these editors to generate BMFonts: + http://glyphdesigner.71squared.com/ (Commercial, Mac OS X) + http://www.n4te.com/hiero/hiero.jnlp (Free, Java) + http://slick.cokeandcode.com/demos/hiero.jnlp (Free, Java) + http://www.angelcode.com/products/bmfont/ (Free, Windows only) + ****************************************************************************/ + +(function () { + cc.LabelBMFont.WebGLRenderCmd = function (renderableObject) { + this._rootCtor(renderableObject); + }; + + var proto = cc.LabelBMFont.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + proto.constructor = cc.LabelBMFont.WebGLRenderCmd; + + proto.setTexture = function (texture) { + this._node.setOpacityModifyRGB(this._node._texture.hasPremultipliedAlpha()); + }; + + proto._updateCharTexture = function(fontChar, rect, key, isRotated){ + // updating previous sprite + fontChar.setTextureRect(rect, isRotated); + // restore to default in case they were modified + fontChar.visible = true; + }; + + proto._changeTextureColor = function () { + }; + + proto._updateCharColorAndOpacity = function () { + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/menus/CCMenu.js b/frameworks/cocos2d-html5/cocos2d/menus/CCMenu.js new file mode 100644 index 0000000..e3edd73 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/menus/CCMenu.js @@ -0,0 +1,608 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * @constant + * @type Number + */ +cc.MENU_STATE_WAITING = 0; +/** + * @constant + * @type Number + */ +cc.MENU_STATE_TRACKING_TOUCH = 1; +/** + * @constant + * @type Number + */ +cc.MENU_HANDLER_PRIORITY = -128; +/** + * @constant + * @type Number + */ +cc.DEFAULT_PADDING = 5; + +/** + *

Features and Limitation:
+ * - You can add MenuItem objects in runtime using addChild:
+ * - But the only accepted children are MenuItem objects

+ * @class + * @extends cc.Layer + * @param {...cc.MenuItem|null} menuItems + * @example + * var layer = new cc.Menu(menuitem1, menuitem2, menuitem3); + */ +cc.Menu = cc.Layer.extend(/** @lends cc.Menu# */{ + enabled: false, + + _selectedItem: null, + _state: -1, + _touchListener: null, + _className: "Menu", + + /** + * Constructor of cc.Menu override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {...cc.MenuItem|null} menuItems + */ + ctor: function (menuItems) { + cc.Layer.prototype.ctor.call(this); + this._color = cc.color.WHITE; + this.enabled = false; + this._opacity = 255; + this._selectedItem = null; + this._state = -1; + + this._touchListener = cc.EventListener.create({ + event: cc.EventListener.TOUCH_ONE_BY_ONE, + swallowTouches: true, + onTouchBegan: this._onTouchBegan, + onTouchMoved: this._onTouchMoved, + onTouchEnded: this._onTouchEnded, + onTouchCancelled: this._onTouchCancelled + }); + + var argc = arguments.length, items; + if (menuItems instanceof Array) { + items = menuItems; + } + else if (argc === 0) { + items = []; + } + else if (argc > 0) { + items = []; + for (var i = 0; i < argc; i++) { + if (arguments[i]) + items.push(arguments[i]); + } + } + this.initWithArray(items); + }, + /** + *

+ * Event callback that is invoked every time when CCMenu enters the 'stage'.
+ * If the CCMenu enters the 'stage' with a transition, this event is called when the transition starts.
+ * During onEnter you can't access a "sister/brother" node.
+ * If you override onEnter, you must call its parent's onEnter function with this._super(). + *

+ */ + onEnter: function () { + var locListener = this._touchListener; + if (!locListener._isRegistered()) + cc.eventManager.addListener(locListener, this); + cc.Node.prototype.onEnter.call(this); + }, + + /** + * return whether or not the menu will receive events + * @return {Boolean} + */ + isEnabled: function () { + return this.enabled; + }, + + /** + * set whether or not the menu will receive events + * @param {Boolean} enabled + */ + setEnabled: function (enabled) { + this.enabled = enabled; + }, + + /** + * initializes a cc.Menu with it's items + * @param {Array} args + * @return {Boolean} + */ + initWithItems: function (args) { + var pArray = []; + if (args) { + for (var i = 0; i < args.length; i++) { + if (args[i]) + pArray.push(args[i]); + } + } + + return this.initWithArray(pArray); + }, + + /** + * initializes a cc.Menu with a Array of cc.MenuItem objects + * @param {Array} arrayOfItems array Of cc.MenuItem Items + * @return {Boolean} + */ + initWithArray: function (arrayOfItems) { + if (cc.Layer.prototype.init.call(this)) { + this.enabled = true; + + // menu in the center of the screen + var winSize = cc.winSize; + this.setPosition(winSize.width / 2, winSize.height / 2); + this.setContentSize(winSize); + this.setAnchorPoint(0.5, 0.5); + this.ignoreAnchorPointForPosition(true); + + if (arrayOfItems) { + for (var i = 0; i < arrayOfItems.length; i++) + this.addChild(arrayOfItems[i], i); + } + + this._selectedItem = null; + this._state = cc.MENU_STATE_WAITING; + + // enable cascade color and opacity on menus + this.cascadeColor = true; + this.cascadeOpacity = true; + + return true; + } + return false; + }, + + /** + * add a child for cc.Menu + * @param {cc.Node} child + * @param {Number|Null} [zOrder=] zOrder for the child + * @param {Number|Null} [tag=] tag for the child + */ + addChild: function (child, zOrder, tag) { + if (!(child instanceof cc.MenuItem)) + throw new Error("cc.Menu.addChild() : Menu only supports MenuItem objects as children"); + cc.Layer.prototype.addChild.call(this, child, zOrder, tag); + }, + + updateAlign: function () { + switch (this._align) { + case 'vertically': + this.alignItemsVertically(); + break; + case 'horizontally': + this.alignItemsHorizontally(); + break; + } + }, + + /** + * align items vertically with default padding + */ + alignItemsVertically: function () { + this.alignItemsVerticallyWithPadding(cc.DEFAULT_PADDING); + }, + + /** + * align items vertically with specified padding + * @param {Number} padding + */ + alignItemsVerticallyWithPadding: function (padding) { + this._align = 'vertically'; + var height = -padding, locChildren = this._children, len, i, locScaleY, locHeight, locChild; + if (locChildren && locChildren.length > 0) { + for (i = 0, len = locChildren.length; i < len; i++) + height += locChildren[i].height * locChildren[i].scaleY + padding; + + var y = height / 2.0; + + for (i = 0, len = locChildren.length; i < len; i++) { + locChild = locChildren[i]; + locHeight = locChild.height; + locScaleY = locChild.scaleY; + locChild.setPosition(0, y - locHeight * locScaleY / 2); + y -= locHeight * locScaleY + padding; + } + } + }, + + /** + * align items horizontally with default padding + */ + alignItemsHorizontally: function () { + this.alignItemsHorizontallyWithPadding(cc.DEFAULT_PADDING); + }, + + /** + * align items horizontally with specified padding + * @param {Number} padding + */ + alignItemsHorizontallyWithPadding: function (padding) { + this._align = 'horizontally'; + var width = -padding, locChildren = this._children, i, len, locScaleX, locWidth, locChild; + if (locChildren && locChildren.length > 0) { + for (i = 0, len = locChildren.length; i < len; i++) + width += locChildren[i].width * locChildren[i].scaleX + padding; + + var x = -width / 2.0; + + for (i = 0, len = locChildren.length; i < len; i++) { + locChild = locChildren[i]; + locScaleX = locChild.scaleX; + locWidth = locChildren[i].width; + locChild.setPosition(x + locWidth * locScaleX / 2, 0); + x += locWidth * locScaleX + padding; + } + } + }, + + /** + * align items in columns + * @example + * // Example + * menu.alignItemsInColumns(3,2,3)// this will create 3 columns, with 3 items for first column, 2 items for second and 3 for third + * + * menu.alignItemsInColumns(3,3)//this creates 2 columns, each have 3 items + */ + alignItemsInColumns: function (/*Multiple Arguments*/) { + if ((arguments.length > 0) && (arguments[arguments.length - 1] == null)) + cc.log("parameters should not be ending with null in Javascript"); + + var i, rows = []; + for (i = 0; i < arguments.length; i++) { + rows.push(arguments[i]); + } + var height = -5; + var row = 0; + var rowHeight = 0; + var columnsOccupied = 0; + var rowColumns, tmp, len; + var locChildren = this._children; + if (locChildren && locChildren.length > 0) { + for (i = 0, len = locChildren.length; i < len; i++) { + if (row >= rows.length) + continue; + + rowColumns = rows[row]; + // can not have zero columns on a row + if (!rowColumns) + continue; + + tmp = locChildren[i].height; + rowHeight = ((rowHeight >= tmp || isNaN(tmp)) ? rowHeight : tmp); + + ++columnsOccupied; + if (columnsOccupied >= rowColumns) { + height += rowHeight + 5; + + columnsOccupied = 0; + rowHeight = 0; + ++row; + } + } + } + // check if too many rows/columns for available menu items + //cc.assert(!columnsOccupied, ""); //? + var winSize = cc.director.getWinSize(); + + row = 0; + rowHeight = 0; + rowColumns = 0; + var w = 0.0; + var x = 0.0; + var y = (height / 2); + + if (locChildren && locChildren.length > 0) { + for (i = 0, len = locChildren.length; i < len; i++) { + var child = locChildren[i]; + if (rowColumns === 0) { + rowColumns = rows[row]; + w = winSize.width / (1 + rowColumns); + x = w; + } + + tmp = child._getHeight(); + rowHeight = ((rowHeight >= tmp || isNaN(tmp)) ? rowHeight : tmp); + child.setPosition(x - winSize.width / 2, y - tmp / 2); + + x += w; + ++columnsOccupied; + + if (columnsOccupied >= rowColumns) { + y -= rowHeight + 5; + columnsOccupied = 0; + rowColumns = 0; + rowHeight = 0; + ++row; + } + } + } + }, + /** + * align menu items in rows + * @param {Number} + * @example + * // Example + * menu.alignItemsInRows(5,3)//this will align items to 2 rows, first row with 5 items, second row with 3 + * + * menu.alignItemsInRows(4,4,4,4)//this creates 4 rows each have 4 items + */ + alignItemsInRows: function (/*Multiple arguments*/) { + if ((arguments.length > 0) && (arguments[arguments.length - 1] == null)) + cc.log("parameters should not be ending with null in Javascript"); + var i, columns = []; + for (i = 0; i < arguments.length; i++) { + columns.push(arguments[i]); + } + var columnWidths = []; + var columnHeights = []; + + var width = -10; + var columnHeight = -5; + var column = 0; + var columnWidth = 0; + var rowsOccupied = 0; + var columnRows, child, len, tmp; + + var locChildren = this._children; + if (locChildren && locChildren.length > 0) { + for (i = 0, len = locChildren.length; i < len; i++) { + child = locChildren[i]; + // check if too many menu items for the amount of rows/columns + if (column >= columns.length) + continue; + + columnRows = columns[column]; + // can't have zero rows on a column + if (!columnRows) + continue; + + // columnWidth = fmaxf(columnWidth, [item contentSize].width); + tmp = child.width; + columnWidth = ((columnWidth >= tmp || isNaN(tmp)) ? columnWidth : tmp); + + columnHeight += (child.height + 5); + ++rowsOccupied; + + if (rowsOccupied >= columnRows) { + columnWidths.push(columnWidth); + columnHeights.push(columnHeight); + width += columnWidth + 10; + + rowsOccupied = 0; + columnWidth = 0; + columnHeight = -5; + ++column; + } + } + } + // check if too many rows/columns for available menu items. + //cc.assert(!rowsOccupied, ""); + var winSize = cc.director.getWinSize(); + + column = 0; + columnWidth = 0; + columnRows = 0; + var x = -width / 2; + var y = 0.0; + + if (locChildren && locChildren.length > 0) { + for (i = 0, len = locChildren.length; i < len; i++) { + child = locChildren[i]; + if (columnRows === 0) { + columnRows = columns[column]; + y = columnHeights[column]; + } + + // columnWidth = fmaxf(columnWidth, [item contentSize].width); + tmp = child._getWidth(); + columnWidth = ((columnWidth >= tmp || isNaN(tmp)) ? columnWidth : tmp); + + child.setPosition(x + columnWidths[column] / 2, y - winSize.height / 2); + + y -= child.height + 10; + ++rowsOccupied; + + if (rowsOccupied >= columnRows) { + x += columnWidth + 5; + rowsOccupied = 0; + columnRows = 0; + columnWidth = 0; + ++column; + } + } + } + }, + + /** + * remove a child from cc.Menu + * @param {cc.Node} child the child you want to remove + * @param {boolean} cleanup whether to cleanup + */ + removeChild: function (child, cleanup) { + if (child == null) + return; + if (!(child instanceof cc.MenuItem)) { + cc.log("cc.Menu.removeChild():Menu only supports MenuItem objects as children"); + return; + } + + if (this._selectedItem === child) + this._selectedItem = null; + cc.Node.prototype.removeChild.call(this, child, cleanup); + }, + + _onTouchBegan: function (touch, event) { + var target = event.getCurrentTarget(); + if (target._state !== cc.MENU_STATE_WAITING || !target._visible || !target.enabled) + return false; + + for (var c = target.parent; c != null; c = c.parent) { + if (!c.isVisible()) + return false; + } + + target._selectedItem = target._itemForTouch(touch); + if (target._selectedItem) { + target._state = cc.MENU_STATE_TRACKING_TOUCH; + target._selectedItem.selected(); + target._selectedItem.setNodeDirty(); + return true; + } + return false; + }, + + _onTouchEnded: function (touch, event) { + var target = event.getCurrentTarget(); + if (target._state !== cc.MENU_STATE_TRACKING_TOUCH) { + cc.log("cc.Menu.onTouchEnded(): invalid state"); + return; + } + if (target._selectedItem) { + target._selectedItem.unselected(); + target._selectedItem.setNodeDirty(); + target._selectedItem.activate(); + } + target._state = cc.MENU_STATE_WAITING; + }, + + _onTouchCancelled: function (touch, event) { + var target = event.getCurrentTarget(); + if (target._state !== cc.MENU_STATE_TRACKING_TOUCH) { + cc.log("cc.Menu.onTouchCancelled(): invalid state"); + return; + } + if (target._selectedItem) { + target._selectedItem.unselected(); + target._selectedItem.setNodeDirty(); + } + target._state = cc.MENU_STATE_WAITING; + }, + + _onTouchMoved: function (touch, event) { + var target = event.getCurrentTarget(); + if (target._state !== cc.MENU_STATE_TRACKING_TOUCH) { + cc.log("cc.Menu.onTouchMoved(): invalid state"); + return; + } + var currentItem = target._itemForTouch(touch); + if (currentItem !== target._selectedItem) { + if (target._selectedItem) { + target._selectedItem.unselected(); + target._selectedItem.setNodeDirty(); + } + target._selectedItem = currentItem; + if (target._selectedItem) { + target._selectedItem.selected(); + target._selectedItem.setNodeDirty(); + } + } + }, + + /** + *

+ * callback that is called every time the cc.Menu leaves the 'stage'.
+ * If the cc.Menu leaves the 'stage' with a transition, this callback is called when the transition finishes.
+ * During onExit you can't access a sibling node.
+ * If you override onExit, you shall call its parent's onExit with this._super(). + *

+ */ + onExit: function () { + if (this._state === cc.MENU_STATE_TRACKING_TOUCH) { + if (this._selectedItem) { + this._selectedItem.unselected(); + this._selectedItem = null; + } + this._state = cc.MENU_STATE_WAITING; + } + cc.Node.prototype.onExit.call(this); + }, + /** + * only use for jsbinding + * @param value + */ + setOpacityModifyRGB: function (value) { + }, + /** + * only use for jsbinding + * @returns {boolean} + */ + isOpacityModifyRGB: function () { + return false; + }, + + _itemForTouch: function (touch) { + var touchLocation = touch.getLocation(); + var itemChildren = this._children, locItemChild; + if (itemChildren && itemChildren.length > 0) { + for (var i = itemChildren.length - 1; i >= 0; i--) { + locItemChild = itemChildren[i]; + if (locItemChild.isVisible() && locItemChild.isEnabled()) { + var local = locItemChild.convertToNodeSpace(touchLocation); + var r = locItemChild.rect(); + r.x = 0; + r.y = 0; + if (cc.rectContainsPoint(r, local)) + return locItemChild; + } + } + } + return null; + } +}); + +var _p = cc.Menu.prototype; + +// Extended properties +/** @expose */ +_p.enabled; + +/** + * create a new menu + * @deprecated since v3.0, please use new cc.Menu(menuitem1, menuitem2, menuitem3) to create a new menu + * @param {...cc.MenuItem|null} menuItems + * todo: need to use new + * @return {cc.Menu} + */ +cc.Menu.create = function (menuItems) { + var argc = arguments.length; + if ((argc > 0) && (arguments[argc - 1] == null)) + cc.log("parameters should not be ending with null in Javascript"); + + var ret; + if (argc === 0) + ret = new cc.Menu(); + else if (argc === 1) + ret = new cc.Menu(menuItems); + else + ret = new cc.Menu(Array.prototype.slice.call(arguments, 0)); + return ret; +}; diff --git a/frameworks/cocos2d-html5/cocos2d/menus/CCMenuItem.js b/frameworks/cocos2d-html5/cocos2d/menus/CCMenuItem.js new file mode 100644 index 0000000..de25bee --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/menus/CCMenuItem.js @@ -0,0 +1,1371 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +cc._globalFontSize = cc.ITEM_SIZE; +cc._globalFontName = "Arial"; +cc._globalFontNameRelease = false; + +/** + * Subclass cc.MenuItem (or any subclass) to create your custom cc.MenuItem objects. + * @class + * @extends cc.Node + * @param {function|String} callback + * @param {cc.Node} target + */ +cc.MenuItem = cc.Node.extend(/** @lends cc.MenuItem# */{ + _enabled: false, + _target: null, + _callback: null, + _isSelected: false, + _className: "MenuItem", + + /** + * Constructor of cc.MenuItem + * @param {function|String} callback + * @param {cc.Node} target + */ + ctor: function (callback, target) { + var nodeP = cc.Node.prototype; + nodeP.ctor.call(this); + this._target = null; + this._callback = null; + this._isSelected = false; + this._enabled = false; + + nodeP.setAnchorPoint.call(this, 0.5, 0.5); + this._target = target || null; + this._callback = callback || null; + if (this._callback) { + this._enabled = true; + } + }, + + /** + * return whether MenuItem is selected + * @return {Boolean} + */ + isSelected: function () { + return this._isSelected; + }, + /** + * only use for jsbinding + * @param value + */ + setOpacityModifyRGB: function (value) { + }, + /** + * only use for jsbinding + * @returns {boolean} + */ + isOpacityModifyRGB: function () { + return false; + }, + + /** + * set the target/selector of the menu item + * @param {function|String} selector + * @param {cc.Node} rec + * @deprecated since v3.0 + */ + setTarget: function (selector, rec) { + this._target = rec; + this._callback = selector; + }, + + /** + * return whether MenuItem is Enabled + * @return {Boolean} + */ + isEnabled: function () { + return this._enabled; + }, + + /** + * set enable value of MenuItem + * @param {Boolean} enable + */ + setEnabled: function (enable) { + this._enabled = enable; + }, + + /** + * initializes a cc.MenuItem with callback + * @param {function|String} callback + * @param {cc.Node} target + * @return {Boolean} + */ + initWithCallback: function (callback, target) { + this.anchorX = 0.5; + this.anchorY = 0.5; + this._target = target; + this._callback = callback; + this._enabled = true; + this._isSelected = false; + return true; + }, + + /** + * return rect value of cc.MenuItem + * @return {cc.Rect} + */ + rect: function () { + var locPosition = this._position, locContentSize = this._contentSize, locAnchorPoint = this._anchorPoint; + return cc.rect(locPosition.x - locContentSize.width * locAnchorPoint.x, + locPosition.y - locContentSize.height * locAnchorPoint.y, + locContentSize.width, locContentSize.height); + }, + + /** + * set the cc.MenuItem selected same as setIsSelected(true) + */ + selected: function () { + this._isSelected = true; + }, + + /** + * set the cc.MenuItem unselected same as setIsSelected(false) + */ + unselected: function () { + this._isSelected = false; + }, + + /** + * set the callback to the menu item + * @param {function|String} callback + * @param {cc.Node} target + */ + setCallback: function (callback, target) { + this._target = target; + this._callback = callback; + }, + + /** + * call the selector with target + */ + activate: function () { + if (this._enabled) { + var locTarget = this._target, locCallback = this._callback; + if (!locCallback) + return; + if (locTarget && cc.isString(locCallback)) { + locTarget[locCallback](this); + } else if (locTarget && cc.isFunction(locCallback)) { + locCallback.call(locTarget, this); + } else + locCallback(this); + } + } +}); + +var _p = cc.MenuItem.prototype; + +// Extended properties +/** @expose */ +_p.enabled; +cc.defineGetterSetter(_p, "enabled", _p.isEnabled, _p.setEnabled); + +/** + * creates an empty menu item with target and callback
+ * Not recommended to use the base class, should use more defined menu item classes + * @deprecated since v3.0, please use new cc.MenuItem(callback,target) instead + * @param {function|String} callback callback + * @param {cc.Node} target + * @return {cc.MenuItem} + */ +cc.MenuItem.create = function (callback, target) { + return new cc.MenuItem(callback, target); +}; + +/** + * Any cc.Node that supports the cc.LabelProtocol protocol can be added.
+ * Supported nodes:
+ * - cc.BitmapFontAtlas
+ * - cc.LabelAtlas
+ * - cc.LabelTTF
+ * @class + * @extends cc.MenuItem + * @param {cc.Node} label + * @param {function|String} selector + * @param {cc.Node} target + * @example + * var menuitemLabel = new cc.MenuItemLabel(label,selector,target); + * + * @property {String} string - Content string of label item + * @property {cc.Node} label - Label of label item + * @property {cc.Color} disabledColor - Color of label when it's disabled + */ +cc.MenuItemLabel = cc.MenuItem.extend(/** @lends cc.MenuItemLabel# */{ + _disabledColor: null, + _label: null, + _originalScale: 0, + _colorBackup: null, + + /** + * Constructor of cc.MenuItemLabel + * @param {cc.Node} label + * @param {function|String} selector + * @param {cc.Node} target + */ + ctor: function (label, selector, target) { + cc.MenuItem.prototype.ctor.call(this, selector, target); + this._disabledColor = null; + this._label = null; + this._colorBackup = null; + + if (label) { + this._originalScale = 1.0; + this._colorBackup = cc.color.WHITE; + this._disabledColor = cc.color(126, 126, 126); + this.setLabel(label); + + if (label.textureLoaded && !label.textureLoaded()) { + label.addEventListener("load", function (sender) { + this.width = sender.width; + this.height = sender.height; + if (this.parent instanceof cc.Menu) { + this.parent.updateAlign(); + } + }, this); + } + + this.setCascadeColorEnabled(true); + this.setCascadeOpacityEnabled(true); + } + }, + + /** + * return the disable color for this cc.MenuItemLabel + * @return {cc.Color} + */ + getDisabledColor: function () { + return this._disabledColor; + }, + + /** + * set the disable color for this cc.MenuItemLabel + * @param {cc.Color} color + */ + setDisabledColor: function (color) { + this._disabledColor = color; + }, + + /** + * return label of cc.MenuItemLabel + * @return {cc.Node} + */ + getLabel: function () { + return this._label; + }, + + /** + * set a label for cc.MenuItemLabel + * @param {cc.Node} label + */ + setLabel: function (label) { + if (label) { + this.addChild(label); + label.anchorX = 0; + label.anchorY = 0; + this.width = label.width; + this.height = label.height; + label.setCascadeColorEnabled(true); + } + + if (this._label) { + this.removeChild(this._label, true); + } + + this._label = label; + }, + + /** + * set enable value to cc.MenuItemLabel + * @param {Boolean} enabled + */ + setEnabled: function (enabled) { + if (this._enabled !== enabled) { + if (!enabled) { + this._colorBackup = this.color; + this.setColor(this._disabledColor); + } else { + this.setColor(this._colorBackup); + } + } + cc.MenuItem.prototype.setEnabled.call(this, enabled); + }, + + /** + * initializes a cc.MenuItemLabel with a label + * @param {cc.Node} label + * @param {function|String} selector + * @param {cc.Node} target + * @return {Boolean} + */ + initWithLabel: function (label, selector, target) { + this.initWithCallback(selector, target); + this._originalScale = 1.0; + this._colorBackup = cc.color.WHITE; + this._disabledColor = cc.color(126, 126, 126); + this.setLabel(label); + + this.setCascadeColorEnabled(true); + this.setCascadeOpacityEnabled(true); + + return true; + }, + + /** + * set the string for cc.MenuItemLabel + * @param {String} label + */ + setString: function (label) { + this._label.string = label; + this.width = this._label.width; + this.height = this._label.height; + }, + /** + * return the string of cc.MenuItemLabel + * @returns {String} + */ + getString: function () { + return this._label.string; + }, + + /** + * activate the menu item + */ + activate: function () { + if (this._enabled) { + this.stopAllActions(); + this.scale = this._originalScale; + cc.MenuItem.prototype.activate.call(this); + } + }, + + /** + * menu item is selected (runs callback) + */ + selected: function () { + if (this._enabled) { + cc.MenuItem.prototype.selected.call(this); + + var action = this.getActionByTag(cc.ZOOM_ACTION_TAG); + if (action) + this.stopAction(action); + else + this._originalScale = this.scale; + + var zoomAction = cc.scaleTo(0.1, this._originalScale * 1.2); + zoomAction.setTag(cc.ZOOM_ACTION_TAG); + this.runAction(zoomAction); + } + }, + + /** + * menu item goes back to unselected state + */ + unselected: function () { + if (this._enabled) { + cc.MenuItem.prototype.unselected.call(this); + this.stopActionByTag(cc.ZOOM_ACTION_TAG); + var zoomAction = cc.scaleTo(0.1, this._originalScale); + zoomAction.setTag(cc.ZOOM_ACTION_TAG); + this.runAction(zoomAction); + } + } +}); + +var _p = cc.MenuItemLabel.prototype; + +// Extended properties +/** @expose */ +_p.string; +cc.defineGetterSetter(_p, "string", _p.getString, _p.setString); +/** @expose */ +_p.disabledColor; +cc.defineGetterSetter(_p, "disabledColor", _p.getDisabledColor, _p.setDisabledColor); +/** @expose */ +_p.label; +cc.defineGetterSetter(_p, "label", _p.getLabel, _p.setLabel); + + +/** + * @deprecated since v3.0 ,please use new cc.MenuItemLabel(label,selector,target) instead + * @param {cc.Node} label + * @param {function|String|Null} [selector=] + * @param {cc.Node|Null} [target=] + * @return {cc.MenuItemLabel} + */ +cc.MenuItemLabel.create = function (label, selector, target) { + return new cc.MenuItemLabel(label, selector, target); +}; + +/** + * Helper class that creates a MenuItemLabel class with a LabelAtlas + * @class + * @extends cc.MenuItemLabel + * @param {String} value + * @param {String} charMapFile + * @param {Number} itemWidth + * @param {Number} itemHeight + * @param {String} startCharMap a single character + * @param {function|String|Null} callback + * @param {cc.Node|Null} target + * @example + * var menuItem = new cc.MenuItemAtlasFont(param1,param2...); + */ +cc.MenuItemAtlasFont = cc.MenuItemLabel.extend(/** @lends cc.MenuItemAtlasFont# */{ + + /** + * the contructor of cc.MenuItemAtlasFont + * @param {String} value + * @param {String} charMapFile + * @param {Number} itemWidth + * @param {Number} itemHeight + * @param {String} startCharMap a single character + * @param {function|String|Null} callback + * @param {cc.Node|Null} target + */ + ctor: function (value, charMapFile, itemWidth, itemHeight, startCharMap, callback, target) { + var label; + if (value && value.length > 0) { + label = new cc.LabelAtlas(value, charMapFile, itemWidth, itemHeight, startCharMap); + } + + cc.MenuItemLabel.prototype.ctor.call(this, label, callback, target); + }, + + /** + * initializes a cc.MenuItemAtlasFont with string + * @param {String} value + * @param {String} charMapFile + * @param {Number} itemWidth + * @param {Number} itemHeight + * @param {String} startCharMap a single character + * @param {function|String|Null} callback + * @param {cc.Node|Null} target + * @return {Boolean} + */ + initWithString: function (value, charMapFile, itemWidth, itemHeight, startCharMap, callback, target) { + if (!value || value.length === 0) + throw new Error("cc.MenuItemAtlasFont.initWithString(): value should be non-null and its length should be greater than 0"); + + var label = new cc.LabelAtlas(); + label.initWithString(value, charMapFile, itemWidth, itemHeight, startCharMap); + if (this.initWithLabel(label, callback, target)) { + // do something ? + } + return true; + } +}); + +/** + * create menu item from string with font + * @deprecated since v3.0 ,please use new cc.MenuItemAtlasFont() instead. + * @param {String} value the text to display + * @param {String} charMapFile the character map file + * @param {Number} itemWidth + * @param {Number} itemHeight + * @param {String} startCharMap a single character + * @param {function|String|Null} [callback=null] + * @param {cc.Node|Null} [target=] + * @return {cc.MenuItemAtlasFont} + */ +cc.MenuItemAtlasFont.create = function (value, charMapFile, itemWidth, itemHeight, startCharMap, callback, target) { + return new cc.MenuItemAtlasFont(value, charMapFile, itemWidth, itemHeight, startCharMap, callback, target); +}; + +/** + * Helper class that creates a CCMenuItemLabel class with a Label + * @class + * @extends cc.MenuItemLabel + * @param {String} value text for the menu item + * @param {function|String} callback + * @param {cc.Node} target + * @example + * var menuItem = new cc.MenuItemFont(value, callback, target); + * + * @property {Number} fontSize - Font size of font item + * @property {String} fontName - Font name of font item + */ +cc.MenuItemFont = cc.MenuItemLabel.extend(/** @lends cc.MenuItemFont# */{ + _fontSize: null, + _fontName: null, + + /** + * Constructor of cc.MenuItemFont + * @param {String} value text for the menu item + * @param {function|String} callback + * @param {cc.Node} target + */ + ctor: function (value, callback, target) { + var label; + if (value && value.length > 0) { + this._fontName = cc._globalFontName; + this._fontSize = cc._globalFontSize; + label = new cc.LabelTTF(value, this._fontName, this._fontSize); + } + else { + this._fontSize = 0; + this._fontName = ""; + } + + cc.MenuItemLabel.prototype.ctor.call(this, label, callback, target); + }, + + /** + * initializes cc.MenuItemFont with string + * @param {String} value text for the menu item + * @param {function|String} callback + * @param {cc.Node} target + * @return {Boolean} + */ + initWithString: function (value, callback, target) { + if (!value || value.length === 0) + throw new Error("Value should be non-null and its length should be greater than 0"); + + this._fontName = cc._globalFontName; + this._fontSize = cc._globalFontSize; + + var label = new cc.LabelTTF(value, this._fontName, this._fontSize); + if (this.initWithLabel(label, callback, target)) { + // do something ? + } + return true; + }, + + /** + * set the font size for cc.MenuItemFont + * @param {Number} s + */ + setFontSize: function (s) { + this._fontSize = s; + this._recreateLabel(); + }, + + /** + *return the font size of cc.MenuItemFont + * @return {Number} + */ + getFontSize: function () { + return this._fontSize; + }, + + /** + * set the font name for cc.MenuItemFont + * @param {String} name + */ + setFontName: function (name) { + this._fontName = name; + this._recreateLabel(); + }, + + /** + * return the font name for cc.MenuItemFont + * @return {String} + */ + getFontName: function () { + return this._fontName; + }, + + _recreateLabel: function () { + var label = new cc.LabelTTF(this._label.string, this._fontName, this._fontSize); + this.setLabel(label); + } +}); + +/** + * a shared function to set the fontSize for menuitem font + * @param {Number} fontSize + */ +cc.MenuItemFont.setFontSize = function (fontSize) { + cc._globalFontSize = fontSize; +}; + +/** + * a shared function to get the font size for menuitem font + * @return {Number} + */ +cc.MenuItemFont.fontSize = function () { + return cc._globalFontSize; +}; + +/** + * a shared function to set the fontsize for menuitem font + * @param name + */ +cc.MenuItemFont.setFontName = function (name) { + if (cc._globalFontNameRelease) { + cc._globalFontName = ''; + } + cc._globalFontName = name; + cc._globalFontNameRelease = true; +}; + +var _p = cc.MenuItemFont.prototype; + +// Extended properties +/** @expose */ +_p.fontSize; +cc.defineGetterSetter(_p, "fontSize", _p.getFontSize, _p.setFontSize); +/** @expose */ +_p.fontName; +cc.defineGetterSetter(_p, "fontName", _p.getFontName, _p.setFontName); + + +/** + * a shared function to get the font name for menuitem font + * @return {String} + */ +cc.MenuItemFont.fontName = function () { + return cc._globalFontName; +}; + +/** + * create a menu item from string + * @deprecated since v3.0, please use new construction instead + * @param {String} value the text to display + * @param {String|function|Null} callback the callback to run, either in function name or pass in the actual function + * @param {cc.Node|Null} target the target to run callback + * @return {cc.MenuItemFont} + */ +cc.MenuItemFont.create = function (value, callback, target) { + return new cc.MenuItemFont(value, callback, target); +}; + + +/** + * CCMenuItemSprite accepts CCNode objects as items.
+ * The images has 3 different states:
+ * - unselected image
+ * - selected image
+ * - disabled image
+ * @class + * @extends cc.MenuItem + * @param {Image|Null} normalSprite normal state image + * @param {Image|Null} selectedSprite selected state image + * @param {Image|cc.Node|Null} three disabled state image OR target node + * @param {String|function|cc.Node|Null} four callback function name in string or actual function, OR target Node + * @param {String|function|Null} five callback function name in string or actual function + * + * @example + * var item = new cc.MenuItemSprite(normalImage)//create a menu item from a sprite with no functionality + * var item = new cc.MenuItemSprite(normalImage, selectedImage)//create a menu Item, nothing will happen when clicked + * var item = new cc.MenuItemSprite(normalImage, SelectedImage, disabledImage)//same above, but with disabled state image + * var item = new cc.MenuItemSprite(normalImage, SelectedImage, 'callback', targetNode)//create a menu item, when clicked runs targetNode.callback() + * var item = new cc.MenuItemSprite(normalImage, SelectedImage, disabledImage, targetNode.callback, targetNode) + * //same as above, but with disabled image, and passing in callback function + * + * @property {cc.Sprite} normalImage - Sprite in normal state + * @property {cc.Sprite} selectedImage - Sprite in selected state + * @property {cc.Sprite} disabledImage - Sprite in disabled state + */ +cc.MenuItemSprite = cc.MenuItem.extend(/** @lends cc.MenuItemSprite# */{ + _normalImage: null, + _selectedImage: null, + _disabledImage: null, + + /** + * Constructor of cc.MenuItemSprite + * @param {Image|Null} normalSprite normal state image + * @param {Image|Null} selectedSprite selected state image + * @param {Image|cc.Node|Null} three disabled state image OR target node + * @param {String|function|cc.Node|Null} four callback function name in string or actual function, OR target Node + * @param {String|function|Null} five callback function name in string or actual function + */ + ctor: function (normalSprite, selectedSprite, three, four, five) { + cc.MenuItem.prototype.ctor.call(this); + this._normalImage = null; + this._selectedImage = null; + this._disabledImage = null; + this._loader = new cc.Sprite.LoadManager(); + + if (normalSprite !== undefined) { + //normalSprite = normalSprite; + selectedSprite = selectedSprite || null; + var disabledImage, target, callback; + //when you send 4 arguments, five is undefined + if (five !== undefined) { + disabledImage = three; + callback = four; + target = five; + } else if (four !== undefined && cc.isFunction(four)) { + disabledImage = three; + callback = four; + } else if (four !== undefined && cc.isFunction(three)) { + target = four; + callback = three; + disabledImage = null; + } else if (three === undefined) { + disabledImage = null; + } + + this._loader.clear(); + if (normalSprite.textureLoaded && !normalSprite.textureLoaded()) { + this._loader.once(normalSprite, function () { + this.initWithNormalSprite(normalSprite, selectedSprite, disabledImage, callback, target); + }, this); + return false; + } + + this.initWithNormalSprite(normalSprite, selectedSprite, disabledImage, callback, target); + return true; + } + }, + + /** + * return the normal status image(cc.Sprite) + * @return {cc.Sprite} + */ + getNormalImage: function () { + return this._normalImage; + }, + + /** + * set the normal status image(cc.Sprite) + * @param {cc.Sprite} normalImage + */ + setNormalImage: function (normalImage) { + if (this._normalImage === normalImage) { + return; + } + if (normalImage) { + this.addChild(normalImage, 0, cc.NORMAL_TAG); + normalImage.anchorX = 0; + normalImage.anchorY = 0; + } + if (this._normalImage) { + this.removeChild(this._normalImage, true); + } + + this._normalImage = normalImage; + if(!this._normalImage) + return; + + this.width = this._normalImage.width; + this.height = this._normalImage.height; + this._updateImagesVisibility(); + + if (normalImage.textureLoaded && !normalImage.textureLoaded()) { + normalImage.addEventListener("load", function (sender) { + this.width = sender.width; + this.height = sender.height; + if (this.parent instanceof cc.Menu) { + this.parent.updateAlign(); + } + }, this); + } + }, + + /** + * return the selected status image(cc.Sprite) of cc.MenuItemSprite + * @return {cc.Sprite} + */ + getSelectedImage: function () { + return this._selectedImage; + }, + + /** + * set the selected status image(cc.Sprite) + * @param {cc.Sprite} selectedImage + */ + setSelectedImage: function (selectedImage) { + if (this._selectedImage === selectedImage) + return; + + if (selectedImage) { + this.addChild(selectedImage, 0, cc.SELECTED_TAG); + selectedImage.anchorX = 0; + selectedImage.anchorY = 0; + } + + if (this._selectedImage) { + this.removeChild(this._selectedImage, true); + } + + this._selectedImage = selectedImage; + this._updateImagesVisibility(); + }, + + /** + * return the disabled status of cc.MenuItemSprite + * @return {cc.Sprite} + */ + getDisabledImage: function () { + return this._disabledImage; + }, + + /** + * set the disabled status image(cc.Sprite) + * @param {cc.Sprite} disabledImage + */ + setDisabledImage: function (disabledImage) { + if (this._disabledImage === disabledImage) + return; + + if (disabledImage) { + this.addChild(disabledImage, 0, cc.DISABLE_TAG); + disabledImage.anchorX = 0; + disabledImage.anchorY = 0; + } + + if (this._disabledImage) + this.removeChild(this._disabledImage, true); + + this._disabledImage = disabledImage; + this._updateImagesVisibility(); + }, + + /** + * initializes cc.MenuItemSprite with a cc.Sprite + * @param {cc.Node} normalSprite + * @param {cc.Node} selectedSprite + * @param {cc.Node} disabledSprite + * @param {function|String} callback + * @param {cc.Node} target + * @return {Boolean} + */ + initWithNormalSprite: function (normalSprite, selectedSprite, disabledSprite, callback, target) { + this._loader.clear(); + if (normalSprite.textureLoaded && !normalSprite.textureLoaded()) { + this._loader.once(normalSprite, function () { + this.initWithNormalSprite(normalSprite, selectedSprite, disabledSprite, callback, target); + }, this); + return false; + } + this.initWithCallback(callback, target); + this.setNormalImage(normalSprite); + this.setSelectedImage(selectedSprite); + this.setDisabledImage(disabledSprite); + var locNormalImage = this._normalImage; + if (locNormalImage) { + this.width = locNormalImage.width; + this.height = locNormalImage.height; + } + this.setCascadeColorEnabled(true); + this.setCascadeOpacityEnabled(true); + return true; + }, + + /** + * menu item is selected (runs callback) + */ + selected: function () { + cc.MenuItem.prototype.selected.call(this); + if (this._normalImage) { + if (this._disabledImage) + this._disabledImage.visible = false; + + if (this._selectedImage) { + this._normalImage.visible = false; + this._selectedImage.visible = true; + } else + this._normalImage.visible = true; + } + }, + + /** + * menu item goes back to unselected state + */ + unselected: function () { + cc.MenuItem.prototype.unselected.call(this); + if (this._normalImage) { + this._normalImage.visible = true; + + if (this._selectedImage) + this._selectedImage.visible = false; + + if (this._disabledImage) + this._disabledImage.visible = false; + } + }, + + /** + * set cc.MenuItemSprite enable to receive the touch event + * @param {Boolean} bEnabled + */ + setEnabled: function (bEnabled) { + if (this._enabled !== bEnabled) { + cc.MenuItem.prototype.setEnabled.call(this, bEnabled); + this._updateImagesVisibility(); + } + }, + + _updateImagesVisibility: function () { + var locNormalImage = this._normalImage, locSelImage = this._selectedImage, locDisImage = this._disabledImage; + if (this._enabled) { + if (locNormalImage) + locNormalImage.visible = true; + if (locSelImage) + locSelImage.visible = false; + if (locDisImage) + locDisImage.visible = false; + } else { + if (locDisImage) { + if (locNormalImage) + locNormalImage.visible = false; + if (locSelImage) + locSelImage.visible = false; + if (locDisImage) + locDisImage.visible = true; + } else { + if (locNormalImage) + locNormalImage.visible = true; + if (locSelImage) + locSelImage.visible = false; + } + } + } +}); + +var _p = cc.MenuItemSprite.prototype; + +// Extended properties +/** @expose */ +_p.normalImage; +cc.defineGetterSetter(_p, "normalImage", _p.getNormalImage, _p.setNormalImage); +/** @expose */ +_p.selectedImage; +cc.defineGetterSetter(_p, "selectedImage", _p.getSelectedImage, _p.setSelectedImage); +/** @expose */ +_p.disabledImage; +cc.defineGetterSetter(_p, "disabledImage", _p.getDisabledImage, _p.setDisabledImage); + +/** + * create a menu item from sprite + * @deprecated since v3.0 please use new cc.MenuItemSprite(normalSprite, selectedSprite, three, four, five) instead + * @param {Image} normalSprite normal state image + * @param {Image|Null} selectedSprite selected state image + * @param {Image|cc.Node|Null} three disabled state image OR target node + * @param {String|function|cc.Node|Null} four callback function name in string or actual function, OR target Node + * @param {String|function|Null} five callback function name in string or actual function + * @return {cc.MenuItemSprite} + */ +cc.MenuItemSprite.create = function (normalSprite, selectedSprite, three, four, five) { + return new cc.MenuItemSprite(normalSprite, selectedSprite, three, four, five || undefined); +}; + +/** + * cc.MenuItemImage accepts images as items.
+ * The images has 3 different states:
+ * - unselected image
+ * - selected image
+ * - disabled image
+ *
+ * For best results try that all images are of the same size
+ * @class + * @extends cc.MenuItemSprite + * @param {string|null} normalImage + * @param {string|null} selectedImage + * @param {string|null} disabledImage + * @param {function|string|null} callback + * @param {cc.Node|null} target + * @example + * var menuItem = new cc.MenuItemImage(normalImage, selectedImage, three, four, five); + */ +cc.MenuItemImage = cc.MenuItemSprite.extend(/** @lends cc.MenuItemImage# */{ + + /** + * Constructor of cc.MenuItemImage + * @param {string|null} normalImage + * @param {string|null} selectedImage + * @param {string|null} disabledImage + * @param {function|string|null} callback + * @param {cc.Node|null} target + */ + ctor: function (normalImage, selectedImage, three, four, five) { + var normalSprite = null, + selectedSprite = null, + disabledSprite = null, + callback = null, + target = null; + + if (normalImage === undefined || normalImage === null) { + cc.MenuItemSprite.prototype.ctor.call(this); + } + else { + normalSprite = new cc.Sprite(normalImage); + selectedImage && + (selectedSprite = new cc.Sprite(selectedImage)); + + if (four === undefined) { + callback = three; + } + else if (five === undefined) { + callback = three; + target = four; + } + else if (five) { + disabledSprite = new cc.Sprite(three); + callback = four; + target = five; + } + cc.MenuItemSprite.prototype.ctor.call(this, normalSprite, selectedSprite, disabledSprite, callback, target); + } + }, + + /** + * sets the sprite frame for the normal image + * @param {cc.SpriteFrame} frame + */ + setNormalSpriteFrame: function (frame) { + this.setNormalImage(new cc.Sprite(frame)); + }, + + /** + * sets the sprite frame for the selected image + * @param {cc.SpriteFrame} frame + */ + setSelectedSpriteFrame: function (frame) { + this.setSelectedImage(new cc.Sprite(frame)); + }, + + /** + * sets the sprite frame for the disabled image + * @param {cc.SpriteFrame} frame + */ + setDisabledSpriteFrame: function (frame) { + this.setDisabledImage(new cc.Sprite(frame)); + }, + + /** + * initializes a cc.MenuItemImage + * @param {string|null} normalImage + * @param {string|null} selectedImage + * @param {string|null} disabledImage + * @param {function|string|null} callback + * @param {cc.Node|null} target + * @returns {boolean} + */ + initWithNormalImage: function (normalImage, selectedImage, disabledImage, callback, target) { + var normalSprite = null; + var selectedSprite = null; + var disabledSprite = null; + + if (normalImage) { + normalSprite = new cc.Sprite(normalImage); + } + if (selectedImage) { + selectedSprite = new cc.Sprite(selectedImage); + } + if (disabledImage) { + disabledSprite = new cc.Sprite(disabledImage); + } + return this.initWithNormalSprite(normalSprite, selectedSprite, disabledSprite, callback, target); + } +}); + +/** + * creates a new menu item image + * @deprecated since v3.0, please use new cc.MenuItemImage(normalImage, selectedImage, three, four, five) instead. + * @param {String} normalImage file name for normal state + * @param {String} selectedImage image for selected state + * @param {String|cc.Node} three Disabled image OR callback function + * @param {String|function|Null} [four] callback function, either name in string or pass the whole function OR the target + * @param {cc.Node|String|function|Null} [five] cc.Node target to run callback when clicked + * @return {cc.MenuItemImage} + */ +cc.MenuItemImage.create = function (normalImage, selectedImage, three, four, five) { + return new cc.MenuItemImage(normalImage, selectedImage, three, four, five); +}; + + +/** + * A simple container class that "toggles" it's inner items
+ * The inner items can be any MenuItem + * @class + * @extends cc.MenuItem + * + * @property {Array} subItems - Sub items + * @property {Number} selectedIndex - Index of selected sub item + * + *@example + * // Example + * //create a toggle item with 2 menu items (which you can then toggle between them later) + * var toggler = new cc.MenuItemToggle( new cc.MenuItemFont("On"), new cc.MenuItemFont("Off"), this.callback, this) + * //Note: the first param is the target, the second is the callback function, afterwards, you can pass in any number of menuitems + * + * //if you pass only 1 variable, then it must be a cc.MenuItem + * var notYetToggler = new cc.MenuItemToggle(cc.MenuItemFont("On"));//it is useless right now, until you add more stuff to it + * notYetToggler.addSubItem(new cc.MenuItemFont("Off")); + * //this is useful for constructing a toggler without a callback function (you wish to control the behavior from somewhere else) + */ +cc.MenuItemToggle = cc.MenuItem.extend(/** @lends cc.MenuItemToggle# */{ + subItems: null, + + _selectedIndex: 0, + _opacity: null, + _color: null, + + /** + * Constructor of cc.MenuItemToggle + */ + ctor: function (/*Multiple arguments follow*/) { + + cc.MenuItem.prototype.ctor.call(this); + this._selectedIndex = 0; + this.subItems = []; + this._opacity = 0; + this._color = cc.color.WHITE; + + if(arguments.length > 0) + this.initWithItems(Array.prototype.slice.apply(arguments)); + + }, + + /** + * return the opacity of cc.MenuItemToggle + * @return {Number} + */ + getOpacity: function () { + return this._opacity; + }, + + /** + * set the opacity for cc.MenuItemToggle + * @param {Number} opacity + */ + setOpacity: function (opacity) { + this._opacity = opacity; + if (this.subItems && this.subItems.length > 0) { + for (var it = 0; it < this.subItems.length; it++) { + this.subItems[it].opacity = opacity; + } + } + this._color.a = opacity; + }, + + /** + * return the color of cc.MenuItemToggle + * @return {cc.Color} + */ + getColor: function () { + var locColor = this._color; + return cc.color(locColor.r, locColor.g, locColor.b, locColor.a); + }, + + /** + * set the color for cc.MenuItemToggle + * @param {cc.Color} Color + */ + setColor: function (color) { + var locColor = this._color; + locColor.r = color.r; + locColor.g = color.g; + locColor.b = color.b; + + if (this.subItems && this.subItems.length > 0) { + for (var it = 0; it < this.subItems.length; it++) { + this.subItems[it].setColor(color); + } + } + + if (color.a !== undefined && !color.a_undefined) { + this.setOpacity(color.a); + } + }, + + /** + * return the index of selected + * @return {Number} + */ + getSelectedIndex: function () { + return this._selectedIndex; + }, + + /** + * set the seleceted index for cc.MenuItemToggle + * @param {Number} SelectedIndex + */ + setSelectedIndex: function (SelectedIndex) { + if (SelectedIndex !== this._selectedIndex) { + this._selectedIndex = SelectedIndex; + var currItem = this.getChildByTag(cc.CURRENT_ITEM); + if (currItem) + currItem.removeFromParent(false); + + var item = this.subItems[this._selectedIndex]; + this.addChild(item, 0, cc.CURRENT_ITEM); + var w = item.width, h = item.height; + this.width = w; + this.height = h; + item.setPosition(w / 2, h / 2); + } + }, + + /** + * similar to get children,return the sumItem array. + * @return {Array} + */ + getSubItems: function () { + return this.subItems; + }, + + /** + * set the subitem for cc.MenuItemToggle + * @param {cc.MenuItem} subItems + */ + setSubItems: function (subItems) { + this.subItems = subItems; + }, + + /** + * initializes a cc.MenuItemToggle with items + * @param {...cc.MenuItem} array the rest in the array are cc.MenuItems + * @param {function|String} secondTolast the second item in the args array is the callback + * @param {cc.Node} last the first item in the args array is a target + * @return {Boolean} + */ + initWithItems: function (args) { + var l = args.length; + // passing callback. + if (cc.isFunction(args[args.length - 2])) { + this.initWithCallback(args[args.length - 2], args[args.length - 1]); + l = l - 2; + } else if (cc.isFunction(args[args.length - 1])) { + this.initWithCallback(args[args.length - 1], null); + l = l - 1; + } else { + this.initWithCallback(null, null); + } + + var locSubItems = this.subItems; + locSubItems.length = 0; + for (var i = 0; i < l; i++) { + if (args[i]) + locSubItems.push(args[i]); + } + this._selectedIndex = cc.UINT_MAX; + this.setSelectedIndex(0); + + this.setCascadeColorEnabled(true); + this.setCascadeOpacityEnabled(true); + + return true; + }, + + /** + * add the subitem for cc.MenuItemToggle + * @param {cc.MenuItem} item + */ + addSubItem: function (item) { + this.subItems.push(item); + }, + + /** + * activate the menu item + */ + activate: function () { + // update index + if (this._enabled) { + var newIndex = (this._selectedIndex + 1) % this.subItems.length; + this.setSelectedIndex(newIndex); + } + cc.MenuItem.prototype.activate.call(this); + }, + + /** + * menu item is selected (runs callback) + */ + selected: function () { + cc.MenuItem.prototype.selected.call(this); + this.subItems[this._selectedIndex].selected(); + }, + + /** + * menu item goes back to unselected state + */ + unselected: function () { + cc.MenuItem.prototype.unselected.call(this); + this.subItems[this._selectedIndex].unselected(); + }, + + /** + * set the enable status for cc.MenuItemToggle + * @param {Boolean} enabled + */ + setEnabled: function (enabled) { + if (this._enabled !== enabled) { + cc.MenuItem.prototype.setEnabled.call(this, enabled); + var locItems = this.subItems; + if (locItems && locItems.length > 0) { + for (var it = 0; it < locItems.length; it++) + locItems[it].enabled = enabled; + } + } + }, + + /** + * returns the selected item (deprecated in -x, please use getSelectedItem instead.) + * @return {cc.MenuItem} + */ + selectedItem: function () { + return this.subItems[this._selectedIndex]; + }, + + /** + * returns the selected item. + * @return {cc.MenuItem} + */ + getSelectedItem: function() { + return this.subItems[this._selectedIndex]; + }, + + /** + * *

+ * Event callback that is invoked every time when cc.MenuItemToggle enters the 'stage'.
+ * If the cc.MenuItemToggle enters the 'stage' with a transition, this event is called when the transition starts.
+ * During onEnter you can't access a "sister/brother" node.
+ * If you override onEnter, you must call its parent's onEnter function with this._super(). + *

+ */ + onEnter: function () { + cc.Node.prototype.onEnter.call(this); + this.setSelectedIndex(this._selectedIndex); + } +}); + +var _p = cc.MenuItemToggle.prototype; + +// Extended properties +/** @expose */ +_p.selectedIndex; +cc.defineGetterSetter(_p, "selectedIndex", _p.getSelectedIndex, _p.setSelectedIndex); + + +/** + * create a simple container class that "toggles" it's inner items
+ * The inner items can be any MenuItem + * @deprecated since v3.0 please use new cc.MenuItemToggle(params) instead + * @return {cc.MenuItemToggle} + */ +cc.MenuItemToggle.create = function (/*Multiple arguments follow*/) { + if ((arguments.length > 0) && (arguments[arguments.length - 1] == null)) + cc.log("parameters should not be ending with null in Javascript"); + var ret = new cc.MenuItemToggle(); + ret.initWithItems(Array.prototype.slice.apply(arguments)); + return ret; +}; diff --git a/frameworks/cocos2d-html5/cocos2d/motion-streak/CCMotionStreak.js b/frameworks/cocos2d-html5/cocos2d/motion-streak/CCMotionStreak.js new file mode 100644 index 0000000..1583934 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/motion-streak/CCMotionStreak.js @@ -0,0 +1,533 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2008-2009 Jason Booth + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * cc.MotionStreak manages a Ribbon based on it's motion in absolute space.
+ * You construct it with a fadeTime, minimum segment size, texture path, texture
+ * length and color. The fadeTime controls how long it takes each vertex in
+ * the streak to fade out, the minimum segment size it how many pixels the
+ * streak will move before adding a new ribbon segment, and the texture
+ * length is the how many pixels the texture is stretched across. The texture
+ * is vertically aligned along the streak segment. + * @class + * @extends cc.Node + * + * @property {cc.Texture2D} texture - Texture used for the motion streak. + * @property {Boolean} fastMode - Indicate whether use fast mode. + * @property {Boolean} startingPositionInitialized - Indicate whether starting position initialized. + * @example + * //example + * new cc.MotionStreak(2, 3, 32, cc.color.GREEN, s_streak); + */ +cc.MotionStreak = cc.Node.extend(/** @lends cc.MotionStreak# */{ + texture: null, + fastMode: false, + startingPositionInitialized: false, + + _blendFunc: null, + + _stroke: 0, + _fadeDelta: 0, + _minSeg: 0, + + _maxPoints: 0, + _nuPoints: 0, + _previousNuPoints: 0, + + /* Pointers */ + _pointVertexes: null, + _pointState: null, + + // webgl + _vertices: null, + _colorPointer: null, + _texCoords: null, + + _verticesBuffer: null, + _colorPointerBuffer: null, + _texCoordsBuffer: null, + _className: "MotionStreak", + + /** + * creates and initializes a motion streak with fade in seconds, minimum segments, stroke's width, color, texture filename or texture
+ * Constructor of cc.MotionStreak + * @param {Number} fade time to fade + * @param {Number} minSeg minimum segment size + * @param {Number} stroke stroke's width + * @param {Number} color + * @param {string|cc.Texture2D} texture texture filename or texture + */ + ctor: function (fade, minSeg, stroke, color, texture) { + cc.Node.prototype.ctor.call(this); + this._positionR = cc.p(0, 0); + this._blendFunc = new cc.BlendFunc(cc.SRC_ALPHA, cc.ONE_MINUS_SRC_ALPHA); + + this.fastMode = false; + this.startingPositionInitialized = false; + + this.texture = null; + + this._stroke = 0; + this._fadeDelta = 0; + this._minSeg = 0; + + this._maxPoints = 0; + this._nuPoints = 0; + this._previousNuPoints = 0; + + /** Pointers */ + this._pointVertexes = null; + this._pointState = null; + + // webgl + this._vertices = null; + this._colorPointer = null; + this._texCoords = null; + + this._verticesBuffer = null; + this._colorPointerBuffer = null; + this._texCoordsBuffer = null; + + if (texture !== undefined) + this.initWithFade(fade, minSeg, stroke, color, texture); + }, + + /** + * Gets the texture. + * @return {cc.Texture2D} + */ + getTexture: function () { + return this.texture; + }, + + /** + * Set the texture. + * @param {cc.Texture2D} texture + */ + setTexture: function (texture) { + if (this.texture !== texture) + this.texture = texture; + }, + + /** + * Gets the blend func. + * @return {cc.BlendFunc} + */ + getBlendFunc: function () { + return this._blendFunc; + }, + + /** + * Set the blend func. + * @param {Number} src + * @param {Number} dst + */ + setBlendFunc: function (src, dst) { + if (dst === undefined) { + this._blendFunc = src; + } else { + this._blendFunc.src = src; + this._blendFunc.dst = dst; + } + }, + + /** + * Gets opacity. + * @warning cc.MotionStreak.getOpacity has not been supported. + * @returns {number} + */ + getOpacity: function () { + cc.log("cc.MotionStreak.getOpacity has not been supported."); + return 0; + }, + + /** + * Set opacity. + * @warning cc.MotionStreak.setOpacity has not been supported. + * @param opacity + */ + setOpacity: function (opacity) { + cc.log("cc.MotionStreak.setOpacity has not been supported."); + }, + + /** + * set opacity modify RGB. + * @warning cc.MotionStreak.setOpacityModifyRGB has not been supported. + * @param value + */ + setOpacityModifyRGB: function (value) { + }, + + /** + * Checking OpacityModifyRGB. + * @returns {boolean} + */ + isOpacityModifyRGB: function () { + return false; + }, + + /** + * Checking fast mode. + * @returns {boolean} + */ + isFastMode: function () { + return this.fastMode; + }, + + /** + * set fast mode + * @param {Boolean} fastMode + */ + setFastMode: function (fastMode) { + this.fastMode = fastMode; + }, + + /** + * Checking starting position initialized. + * @returns {boolean} + */ + isStartingPositionInitialized: function () { + return this.startingPositionInitialized; + }, + + /** + * Set Starting Position Initialized. + * @param {Boolean} startingPositionInitialized + */ + setStartingPositionInitialized: function (startingPositionInitialized) { + this.startingPositionInitialized = startingPositionInitialized; + }, + + /** + * Get stroke. + * @returns {Number} stroke + */ + getStroke: function () { + return this._stroke; + }, + + /** + * Set stroke. + * @param {Number} stroke + */ + setStroke: function (stroke) { + this._stroke = stroke; + }, + + /** + * initializes a motion streak with fade in seconds, minimum segments, stroke's width, color and texture filename or texture + * @param {Number} fade time to fade + * @param {Number} minSeg minimum segment size + * @param {Number} stroke stroke's width + * @param {Number} color + * @param {string|cc.Texture2D} texture texture filename or texture + * @return {Boolean} + */ + initWithFade: function (fade, minSeg, stroke, color, texture) { + if (!texture) + throw new Error("cc.MotionStreak.initWithFade(): Invalid filename or texture"); + + if (cc.isString(texture)) + texture = cc.textureCache.addImage(texture); + + cc.Node.prototype.setPosition.call(this, cc.p(0, 0)); + this.anchorX = 0; + this.anchorY = 0; + this.ignoreAnchor = true; + this.startingPositionInitialized = false; + + this.fastMode = true; + this._minSeg = (minSeg === -1.0) ? (stroke / 5.0) : minSeg; + this._minSeg *= this._minSeg; + + this._stroke = stroke; + this._fadeDelta = 1.0 / fade; + + var locMaxPoints = (0 | (fade * 60)) + 2; + this._maxPoints = locMaxPoints; + this._nuPoints = 0; + this._pointState = new Float32Array(locMaxPoints); + this._pointVertexes = new Float32Array(locMaxPoints * 2); + + this._vertices = new Float32Array(locMaxPoints * 4); + this._texCoords = new Float32Array(locMaxPoints * 4); + this._colorPointer = new Uint8Array(locMaxPoints * 8); + + this._verticesBuffer = gl.createBuffer(); + this._texCoordsBuffer = gl.createBuffer(); + this._colorPointerBuffer = gl.createBuffer(); + + // Set blend mode + this._blendFunc.src = gl.SRC_ALPHA; + this._blendFunc.dst = gl.ONE_MINUS_SRC_ALPHA; + + this.texture = texture; + this.color = color; + this.scheduleUpdate(); + + //bind buffer + gl.bindBuffer(gl.ARRAY_BUFFER, this._verticesBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this._vertices, gl.DYNAMIC_DRAW); + gl.bindBuffer(gl.ARRAY_BUFFER, this._texCoordsBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this._texCoords, gl.DYNAMIC_DRAW); + gl.bindBuffer(gl.ARRAY_BUFFER, this._colorPointerBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this._colorPointer, gl.DYNAMIC_DRAW); + + return true; + }, + + /** + * color used for the tint + * @param {cc.Color} color + */ + tintWithColor: function (color) { + this.color = color; + + // Fast assignation + var locColorPointer = this._colorPointer; + for (var i = 0, len = this._nuPoints * 2; i < len; i++) { + locColorPointer[i * 4] = color.r; + locColorPointer[i * 4 + 1] = color.g; + locColorPointer[i * 4 + 2] = color.b; + } + }, + + /** + * Remove all living segments of the ribbon + */ + reset: function () { + this._nuPoints = 0; + }, + + /** + * Set the position.
+ * + * @param {cc.Point|Number} position + * @param {Number} [yValue=undefined] If not exists, the first parameter must be cc.Point. + */ + setPosition: function (position, yValue) { + this.startingPositionInitialized = true; + if (yValue === undefined) { + this._positionR.x = position.x; + this._positionR.y = position.y; + } else { + this._positionR.x = position; + this._positionR.y = yValue; + } + }, + + /** + * Gets the position.x + * @return {Number} + */ + getPositionX: function () { + return this._positionR.x; + }, + + /** + * Set the position.x + * @param {Number} x + */ + setPositionX: function (x) { + this._positionR.x = x; + if (!this.startingPositionInitialized) + this.startingPositionInitialized = true; + }, + + /** + * Gets the position.y + * @return {Number} + */ + getPositionY: function () { + return this._positionR.y; + }, + + /** + * Set the position.y + * @param {Number} y + */ + setPositionY: function (y) { + this._positionR.y = y; + if (!this.startingPositionInitialized) + this.startingPositionInitialized = true; + }, + + /** + *

schedules the "update" method.
+ * It will use the order number 0. This method will be called every frame.
+ * Scheduled methods with a lower order value will be called before the ones that have a higher order value.
+ * Only one "update" method could be scheduled per node.

+ * @param {Number} delta + */ + update: function (delta) { + if (!this.startingPositionInitialized) + return; + + //TODO update the color (need move to render cmd) + this._renderCmd._updateDisplayColor(); + + delta *= this._fadeDelta; + + var i, newIdx, newIdx2, i2; + var mov = 0; + + // Update current points + var locNuPoints = this._nuPoints; + var locPointState = this._pointState, locPointVertexes = this._pointVertexes, locVertices = this._vertices; + var locColorPointer = this._colorPointer; + + for (i = 0; i < locNuPoints; i++) { + locPointState[i] -= delta; + + if (locPointState[i] <= 0) + mov++; + else { + newIdx = i - mov; + if (mov > 0) { + // Move data + locPointState[newIdx] = locPointState[i]; + // Move point + locPointVertexes[newIdx * 2] = locPointVertexes[i * 2]; + locPointVertexes[newIdx * 2 + 1] = locPointVertexes[i * 2 + 1]; + + // Move vertices + i2 = i * 2; + newIdx2 = newIdx * 2; + locVertices[newIdx2 * 2] = locVertices[i2 * 2]; + locVertices[newIdx2 * 2 + 1] = locVertices[i2 * 2 + 1]; + locVertices[(newIdx2 + 1) * 2] = locVertices[(i2 + 1) * 2]; + locVertices[(newIdx2 + 1) * 2 + 1] = locVertices[(i2 + 1) * 2 + 1]; + + // Move color + i2 *= 4; + newIdx2 *= 4; + locColorPointer[newIdx2 + 0] = locColorPointer[i2 + 0]; + locColorPointer[newIdx2 + 1] = locColorPointer[i2 + 1]; + locColorPointer[newIdx2 + 2] = locColorPointer[i2 + 2]; + locColorPointer[newIdx2 + 4] = locColorPointer[i2 + 4]; + locColorPointer[newIdx2 + 5] = locColorPointer[i2 + 5]; + locColorPointer[newIdx2 + 6] = locColorPointer[i2 + 6]; + } else + newIdx2 = newIdx * 8; + + var op = locPointState[newIdx] * 255.0; + locColorPointer[newIdx2 + 3] = op; + locColorPointer[newIdx2 + 7] = op; + } + } + locNuPoints -= mov; + + // Append new point + var appendNewPoint = true; + if (locNuPoints >= this._maxPoints) + appendNewPoint = false; + else if (locNuPoints > 0) { + var a1 = cc.pDistanceSQ(cc.p(locPointVertexes[(locNuPoints - 1) * 2], locPointVertexes[(locNuPoints - 1) * 2 + 1]), + this._positionR) < this._minSeg; + var a2 = (locNuPoints === 1) ? false : (cc.pDistanceSQ( + cc.p(locPointVertexes[(locNuPoints - 2) * 2], locPointVertexes[(locNuPoints - 2) * 2 + 1]), this._positionR) < (this._minSeg * 2.0)); + if (a1 || a2) + appendNewPoint = false; + } + + if (appendNewPoint) { + locPointVertexes[locNuPoints * 2] = this._positionR.x; + locPointVertexes[locNuPoints * 2 + 1] = this._positionR.y; + locPointState[locNuPoints] = 1.0; + + // Color assignment + var offset = locNuPoints * 8; + + var locDisplayedColor = this.getDisplayedColor(); + locColorPointer[offset] = locDisplayedColor.r; + locColorPointer[offset + 1] = locDisplayedColor.g; + locColorPointer[offset + 2] = locDisplayedColor.b; + //*((ccColor3B*)(m_pColorPointer + offset+4)) = this._color; + locColorPointer[offset + 4] = locDisplayedColor.r; + locColorPointer[offset + 5] = locDisplayedColor.g; + locColorPointer[offset + 6] = locDisplayedColor.b; + + // Opacity + locColorPointer[offset + 3] = 255; + locColorPointer[offset + 7] = 255; + + // Generate polygon + if (locNuPoints > 0 && this.fastMode) { + if (locNuPoints > 1) + cc.vertexLineToPolygon(locPointVertexes, this._stroke, this._vertices, locNuPoints, 1); + else + cc.vertexLineToPolygon(locPointVertexes, this._stroke, this._vertices, 0, 2); + } + locNuPoints++; + } + + if (!this.fastMode) + cc.vertexLineToPolygon(locPointVertexes, this._stroke, this._vertices, 0, locNuPoints); + + // Updated Tex Coords only if they are different than previous step + if (locNuPoints && this._previousNuPoints !== locNuPoints) { + var texDelta = 1.0 / locNuPoints; + var locTexCoords = this._texCoords; + for (i = 0; i < locNuPoints; i++) { + locTexCoords[i * 4] = 0; + locTexCoords[i * 4 + 1] = texDelta * i; + + locTexCoords[(i * 2 + 1) * 2] = 1; + locTexCoords[(i * 2 + 1) * 2 + 1] = texDelta * i; + } + + this._previousNuPoints = locNuPoints; + } + + this._nuPoints = locNuPoints; + }, + + _createRenderCmd: function () { + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) + return new cc.MotionStreak.WebGLRenderCmd(this); + else + return null; //MotionStreak doesn't support Canvas mode + } +}); + +/** + * Please use new cc.MotionStreak instead.
+ * Creates and initializes a motion streak with fade in seconds, minimum segments, stroke's width, color, texture filename or texture + * @deprecated since v3.0 please use new cc.MotionStreak instead. + * @param {Number} fade time to fade + * @param {Number} minSeg minimum segment size + * @param {Number} stroke stroke's width + * @param {Number} color + * @param {string|cc.Texture2D} texture texture filename or texture + * @return {cc.MotionStreak} + * @example + * //example + * new cc.MotionStreak(2, 3, 32, cc.color.GREEN, s_streak); + */ +cc.MotionStreak.create = function (fade, minSeg, stroke, color, texture) { + return new cc.MotionStreak(fade, minSeg, stroke, color, texture); +}; diff --git a/frameworks/cocos2d-html5/cocos2d/motion-streak/CCMotionStreakWebGLRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/motion-streak/CCMotionStreakWebGLRenderCmd.js new file mode 100644 index 0000000..a7d793a --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/motion-streak/CCMotionStreakWebGLRenderCmd.js @@ -0,0 +1,79 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +cc.MotionStreak.WebGLRenderCmd = function (renderableObject) { + this._rootCtor(renderableObject); + this._needDraw = true; + this._matrix = new cc.math.Matrix4(); + this._matrix.identity(); + this._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR); +}; + +cc.MotionStreak.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); +cc.MotionStreak.WebGLRenderCmd.prototype.constructor = cc.Sprite.WebGLRenderCmd; + +cc.MotionStreak.WebGLRenderCmd.prototype.rendering = function (ctx) { + var node = this._node; + if (node._nuPoints <= 1) + return; + + if (node.texture && node.texture.isLoaded()) { + ctx = ctx || cc._renderContext; + + var wt = this._worldTransform; + this._matrix.mat[0] = wt.a; + this._matrix.mat[4] = wt.c; + this._matrix.mat[12] = wt.tx; + this._matrix.mat[1] = wt.b; + this._matrix.mat[5] = wt.d; + this._matrix.mat[13] = wt.ty; + + this._glProgramState.apply(this._matrix); + cc.glBlendFunc(node._blendFunc.src, node._blendFunc.dst); + + cc.glBindTexture2D(node.texture); + + ctx.enableVertexAttribArray(cc.VERTEX_ATTRIB_POSITION); + ctx.enableVertexAttribArray(cc.VERTEX_ATTRIB_COLOR); + ctx.enableVertexAttribArray(cc.VERTEX_ATTRIB_TEX_COORDS); + + //position + ctx.bindBuffer(ctx.ARRAY_BUFFER, node._verticesBuffer); + ctx.bufferData(ctx.ARRAY_BUFFER, node._vertices, ctx.DYNAMIC_DRAW); + ctx.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, ctx.FLOAT, false, 0, 0); + + //texcoords + ctx.bindBuffer(ctx.ARRAY_BUFFER, node._texCoordsBuffer); + ctx.bufferData(ctx.ARRAY_BUFFER, node._texCoords, ctx.DYNAMIC_DRAW); + ctx.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, ctx.FLOAT, false, 0, 0); + + //colors + ctx.bindBuffer(ctx.ARRAY_BUFFER, node._colorPointerBuffer); + ctx.bufferData(ctx.ARRAY_BUFFER, node._colorPointer, ctx.DYNAMIC_DRAW); + ctx.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, ctx.UNSIGNED_BYTE, true, 0, 0); + + ctx.drawArrays(ctx.TRIANGLE_STRIP, 0, node._nuPoints * 2); + cc.g_NumberOfDraws++; + } +}; diff --git a/frameworks/cocos2d-html5/cocos2d/node-grid/CCNodeGrid.js b/frameworks/cocos2d-html5/cocos2d/node-grid/CCNodeGrid.js new file mode 100644 index 0000000..684b388 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/node-grid/CCNodeGrid.js @@ -0,0 +1,110 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + *

NodeGrid class is a class serves as a decorator of cc.Node,
+ * Grid node can run grid actions over all its children (WebGL only) + *

+ * @type {Class} + * + * @property {cc.GridBase} grid - Grid object that is used when applying effects + * @property {cc.Node} target - <@writeonly>Target + */ +cc.NodeGrid = cc.Node.extend(/** @lends cc.NodeGrid# */{ + grid: null, + _target: null, + _gridRect:null, + + ctor: function (rect) { + cc.Node.prototype.ctor.call(this); + if(rect === undefined) rect = cc.rect(); + this._gridRect = rect; + }, + /** + * Gets the grid object. + * @returns {cc.GridBase} + */ + getGrid: function () { + return this.grid; + }, + + /** + * Set the grid object. + * @param {cc.GridBase} grid + */ + setGrid: function (grid) { + this.grid = grid; + }, + + /** + * @brief Set the effect grid rect. + * @param {cc.Rect} rect + */ + setGridRect: function (rect) { + this._gridRect = rect; + }, + /** + * @brief Get the effect grid rect. + * @return {cc.Rect} rect. + */ + getGridRect: function () { + return this._gridRect; + }, + + /** + * Set the target + * @param {cc.Node} target + */ + setTarget: function (target) { + this._target = target; + }, + + _createRenderCmd: function(){ + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) + return new cc.NodeGrid.WebGLRenderCmd(this); + else + return new cc.Node.CanvasRenderCmd(this); // cc.NodeGrid doesn't support Canvas mode. + } +}); + +var _p = cc.NodeGrid.prototype; +// Extended property +/** @expose */ +_p.grid; +/** @expose */ +_p.target; +cc.defineGetterSetter(_p, "target", null, _p.setTarget); + + +/** + * Creates a NodeGrid.
+ * Implementation cc.NodeGrid + * @deprecated since v3.0 please new cc.NodeGrid instead. + * @return {cc.NodeGrid} + */ +cc.NodeGrid.create = function () { + return new cc.NodeGrid(); +}; diff --git a/frameworks/cocos2d-html5/cocos2d/node-grid/CCNodeGridWebGLRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/node-grid/CCNodeGridWebGLRenderCmd.js new file mode 100644 index 0000000..9288a0f --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/node-grid/CCNodeGridWebGLRenderCmd.js @@ -0,0 +1,98 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + cc.NodeGrid.WebGLRenderCmd = function (renderable) { + this._rootCtor(renderable); + this._needDraw = false; + this._gridBeginCommand = new cc.CustomRenderCmd(this, this.onGridBeginDraw); + this._gridEndCommand = new cc.CustomRenderCmd(this, this.onGridEndDraw); + }; + + var proto = cc.NodeGrid.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + proto.constructor = cc.NodeGrid.WebGLRenderCmd; + + proto.visit = function (parentCmd) { + var node = this._node; + // quick return if not visible + if (!node._visible) + return; + + parentCmd = parentCmd || this.getParentRenderCmd(); + if (parentCmd) + this._curLevel = parentCmd._curLevel + 1; + + var currentStack = cc.current_stack; + currentStack.stack.push(currentStack.top); + this._syncStatus(parentCmd); + currentStack.top = this._stackMatrix; + + /*var beforeProjectionType = cc.director.PROJECTION_DEFAULT; + if (locGrid && locGrid._active) { + //var backMatrix = new cc.kmMat4(); + //cc.kmMat4Assign(backMatrix, this._stackMatrix); + + beforeProjectionType = cc.director.getProjection(); + //locGrid.set2DProjection(); + + //reset this._stackMatrix to current_stack.top + //cc.kmMat4Assign(currentStack.top, backMatrix); + }*/ + cc.renderer.pushRenderCommand(this._gridBeginCommand); + + if (node._target) + node._target.visit(); + + var locChildren = node._children; + if (locChildren && locChildren.length > 0) { + var childLen = locChildren.length; + node.sortAllChildren(); + // draw children + for (var i = 0; i < childLen; i++) { + var child = locChildren[i]; + child && child.visit(); + } + } + + //if (locGrid && locGrid._active) { + //cc.director.setProjection(beforeProjectionType); + //} + cc.renderer.pushRenderCommand(this._gridEndCommand); + + this._dirtyFlag = 0; + currentStack.top = currentStack.stack.pop(); + }; + + proto.onGridBeginDraw = function () { + var locGrid = this._node.grid; + if (locGrid && locGrid._active) + locGrid.beforeDraw(); + }; + + proto.onGridEndDraw = function () { + var locGrid = this._node.grid; + if (locGrid && locGrid._active) + locGrid.afterDraw(this._node); + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/parallax/CCParallaxNode.js b/frameworks/cocos2d-html5/cocos2d/parallax/CCParallaxNode.js new file mode 100644 index 0000000..7efc9cf --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/parallax/CCParallaxNode.js @@ -0,0 +1,252 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * Parallax Object.
+ * Parallax required attributes are stored. + * @class + * @extends cc.Class + */ +cc.PointObject = cc.Class.extend(/** @lends cc.PointObject# */{ + _ratio:null, + _offset:null, + _child:null, + + ctor: function(ratio, offset){ + this.initWithCCPoint(ratio, offset); + }, + + /** + * Gets the ratio. + * @return {cc.Point} Not point, this is ratio. + */ + getRatio:function () { + return this._ratio; + }, + + /** + * Set the ratio. + * @param {cc.Point} value + */ + setRatio:function (value) { + this._ratio = value; + }, + + /** + * Gets the offset. + * @return {cc.Point} + */ + getOffset:function () { + return this._offset; + }, + + /** + * Set the offset. + * @param {cc.Point} value + */ + setOffset:function (value) { + this._offset = value; + }, + + /** + * Gets the child. + * @return {cc.Node} + */ + getChild:function () { + return this._child; + }, + + /** + * Set the child. + * @param {cc.Node} value + */ + setChild:function (value) { + this._child = value; + }, + + /** + * initializes cc.PointObject + * @param {cc.Point} ratio Not point, this is a ratio. + * @param {cc.Point} offset + * @return {Boolean} + */ + initWithCCPoint:function (ratio, offset) { + this._ratio = ratio; + this._offset = offset; + this._child = null; + return true; + } +}); + +/** + * Create a object to stored parallax data. + * @param {cc.Point} ratio + * @param {cc.Point} offset + * @return {cc.PointObject} + * @deprecated since v3.0 please use new cc.PointObject() instead. + */ +cc.PointObject.create = function (ratio, offset) { + return new cc.PointObject(ratio, offset); +}; + +/** + *

cc.ParallaxNode: A node that simulates a parallax scroller
+ * The children will be moved faster / slower than the parent according the the parallax ratio.

+ * @class + * @extends cc.Node + * + * @property {Array} parallaxArray - Parallax nodes array + */ +cc.ParallaxNode = cc.Node.extend(/** @lends cc.ParallaxNode# */{ + parallaxArray:null, + + _lastPosition:null, + _className:"ParallaxNode", + + /** + * Gets the parallax array. + * @return {Array} + */ + getParallaxArray:function () { + return this.parallaxArray; + }, + + /** + * Set parallax array. + * @param {Array} value + */ + setParallaxArray:function (value) { + this.parallaxArray = value; + }, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + */ + ctor:function () { + cc.Node.prototype.ctor.call(this); + this.parallaxArray = []; + this._lastPosition = cc.p(-100, -100); + }, + + /** + * Adds a child to the container with a z-order, a parallax ratio and a position offset + * It returns self, so you can chain several addChilds. + * @param {cc.Node} child + * @param {Number} z + * @param {cc.Point} ratio + * @param {cc.Point} offset + * @example + * //example + * voidNode.addChild(background, -1, cc.p(0.4, 0.5), cc.p(0,0)); + */ + addChild:function (child, z, ratio, offset) { + if (arguments.length === 3) { + cc.log("ParallaxNode: use addChild(child, z, ratio, offset) instead"); + return; + } + if(!child) + throw new Error("cc.ParallaxNode.addChild(): child should be non-null"); + var obj = new cc.PointObject(ratio, offset); + obj.setChild(child); + this.parallaxArray.push(obj); + + child.setPosition(this._position.x * ratio.x + offset.x, this._position.y * ratio.y + offset.y); + + cc.Node.prototype.addChild.call(this, child, z, child.tag); + }, + + /** + * Remove Child + * @param {cc.Node} child + * @param {Boolean} cleanup + * @example + * //example + * voidNode.removeChild(background,true); + */ + removeChild:function (child, cleanup) { + var locParallaxArray = this.parallaxArray; + for (var i = 0; i < locParallaxArray.length; i++) { + var point = locParallaxArray[i]; + if (point.getChild() === child) { + locParallaxArray.splice(i, 1); + break; + } + } + cc.Node.prototype.removeChild.call(this, child, cleanup); + }, + + /** + * Remove all children with cleanup + * @param {Boolean} [cleanup=true] + */ + removeAllChildren:function (cleanup) { + this.parallaxArray.length = 0; + cc.Node.prototype.removeAllChildren.call(this, cleanup); + }, + + _updateParallaxPosition: function(){ + var pos = this._absolutePosition(); + if (!cc.pointEqualToPoint(pos, this._lastPosition)) { + var locParallaxArray = this.parallaxArray; + for (var i = 0, len = locParallaxArray.length; i < len; i++) { + var point = locParallaxArray[i]; + var child = point.getChild(); + child.setPosition(-pos.x + pos.x * point.getRatio().x + point.getOffset().x, + -pos.y + pos.y * point.getRatio().y + point.getOffset().y); + } + this._lastPosition = pos; + } + }, + + _absolutePosition:function () { + var ret = this._position; + var cn = this; + while (cn.parent !== null) { + cn = cn.parent; + ret = cc.pAdd(ret, cn.getPosition()); + } + return ret; + }, + + _createRenderCmd: function(){ + if(cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return new cc.ParallaxNode.CanvasRenderCmd(this); + else + return new cc.ParallaxNode.WebGLRenderCmd(this); + } +}); + +/** + * Create new parallax node. + * @deprecated since v3.0 please use new cc.ParallaxNode() instead. + * @return {cc.ParallaxNode} + * @example + * //example + * var voidNode = new cc.ParallaxNode(); + */ +cc.ParallaxNode.create = function () { + return new cc.ParallaxNode(); +}; diff --git a/frameworks/cocos2d-html5/cocos2d/parallax/CCParallaxNodeRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/parallax/CCParallaxNodeRenderCmd.js new file mode 100644 index 0000000..2f5031c --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/parallax/CCParallaxNodeRenderCmd.js @@ -0,0 +1,69 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +//TODO find a way to simple these code. + +(function () { + cc.ParallaxNode.CanvasRenderCmd = function (renderable) { + this._rootCtor(renderable); + this._needDraw = false; + }; + + var proto = cc.ParallaxNode.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + proto.constructor = cc.ParallaxNode.CanvasRenderCmd; + + proto.updateStatus = function () { + this._node._updateParallaxPosition(); + this.originUpdateStatus(); + }; + + proto._syncStatus = function (parentCmd) { + this._node._updateParallaxPosition(); + this._originSyncStatus(parentCmd); + }; +})(); + +cc.game.addEventListener(cc.game.EVENT_RENDERER_INITED, function () { + if (cc._renderType !== cc.game.RENDER_TYPE_WEBGL) + return; + + cc.ParallaxNode.WebGLRenderCmd = function (renderable) { + this._rootCtor(renderable); + this._needDraw = false; + }; + + var proto = cc.ParallaxNode.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + proto.constructor = cc.ParallaxNode.WebGLRenderCmd; + + proto.updateStatus = function () { + this._node._updateParallaxPosition(); + this.originUpdateStatus(); + }; + + proto._syncStatus = function (parentCmd) { + this._node._updateParallaxPosition(); + this._originSyncStatus(parentCmd); + }; +}); + diff --git a/frameworks/cocos2d-html5/cocos2d/particle/CCPNGReader.js b/frameworks/cocos2d-html5/cocos2d/particle/CCPNGReader.js new file mode 100644 index 0000000..20667a2 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/particle/CCPNGReader.js @@ -0,0 +1,330 @@ +/**************************************************************************** + Copyright (c) 2011 Devon Govett + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + + 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. + ****************************************************************************/ + +/** + * A png file reader + * @name cc.tiffReader + */ +cc.PNGReader = cc.Class.extend({ + ctor:function(data){ + var chunkSize, colors, delayDen, delayNum, frame, i, index, key, section, ccshort, text, _i, _j, _ref; + this.data = data; + this.pos = 8; + this.palette = []; + this.imgData = []; + this.transparency = {}; + this.animation = null; + this.text = {}; + frame = null; + while (true) { + chunkSize = this.readUInt32(); + section = ((function() { + var _i, _results; + _results = []; + for (i = _i = 0; _i < 4; i = ++_i) { + _results.push(String.fromCharCode(this.data[this.pos++])); + } + return _results; + }).call(this)).join(''); + switch (section) { + case 'IHDR': + this.width = this.readUInt32(); + this.height = this.readUInt32(); + this.bits = this.data[this.pos++]; + this.colorType = this.data[this.pos++]; + this.compressionMethod = this.data[this.pos++]; + this.filterMethod = this.data[this.pos++]; + this.interlaceMethod = this.data[this.pos++]; + break; + case 'acTL': + this.animation = { + numFrames: this.readUInt32(), + numPlays: this.readUInt32() || Infinity, + frames: [] + }; + break; + case 'PLTE': + this.palette = this.read(chunkSize); + break; + case 'fcTL': + if (frame) { + this.animation.frames.push(frame); + } + this.pos += 4; + frame = { + width: this.readUInt32(), + height: this.readUInt32(), + xOffset: this.readUInt32(), + yOffset: this.readUInt32() + }; + delayNum = this.readUInt16(); + delayDen = this.readUInt16() || 100; + frame.delay = 1000 * delayNum / delayDen; + frame.disposeOp = this.data[this.pos++]; + frame.blendOp = this.data[this.pos++]; + frame.data = []; + break; + case 'IDAT': + case 'fdAT': + if (section === 'fdAT') { + this.pos += 4; + chunkSize -= 4; + } + data = (frame != null ? frame.data : void 0) || this.imgData; + for (i = _i = 0; 0 <= chunkSize ? _i < chunkSize : _i > chunkSize; i = 0 <= chunkSize ? ++_i : --_i) { + data.push(this.data[this.pos++]); + } + break; + case 'tRNS': + this.transparency = {}; + switch (this.colorType) { + case 3: + this.transparency.indexed = this.read(chunkSize); + ccshort = 255 - this.transparency.indexed.length; + if (ccshort > 0) { + for (i = _j = 0; 0 <= ccshort ? _j < ccshort : _j > ccshort; i = 0 <= ccshort ? ++_j : --_j) { + this.transparency.indexed.push(255); + } + } + break; + case 0: + this.transparency.grayscale = this.read(chunkSize)[0]; + break; + case 2: + this.transparency.rgb = this.read(chunkSize); + } + break; + case 'tEXt': + text = this.read(chunkSize); + index = text.indexOf(0); + key = String.fromCharCode.apply(String, text.slice(0, index)); + this.text[key] = String.fromCharCode.apply(String, text.slice(index + 1)); + break; + case 'IEND': + if (frame) { + this.animation.frames.push(frame); + } + this.colors = (function() { + switch (this.colorType) { + case 0: + case 3: + case 4: + return 1; + case 2: + case 6: + return 3; + } + }).call(this); + this.hasAlphaChannel = (_ref = this.colorType) === 4 || _ref === 6; + colors = this.colors + (this.hasAlphaChannel ? 1 : 0); + this.pixelBitlength = this.bits * colors; + this.colorSpace = (function() { + switch (this.colors) { + case 1: + return 'DeviceGray'; + case 3: + return 'DeviceRGB'; + } + }).call(this); + if(Uint8Array != Array) + this.imgData = new Uint8Array(this.imgData); + return; + default: + this.pos += chunkSize; + } + this.pos += 4; + if (this.pos > this.data.length) { + throw new Error("Incomplete or corrupt PNG file"); + } + } + }, + read:function(bytes){ + var i, _i, _results; + _results = []; + for (i = _i = 0; 0 <= bytes ? _i < bytes : _i > bytes; i = 0 <= bytes ? ++_i : --_i) { + _results.push(this.data[this.pos++]); + } + return _results; + }, + readUInt32:function(){ + var b1, b2, b3, b4; + b1 = this.data[this.pos++] << 24; + b2 = this.data[this.pos++] << 16; + b3 = this.data[this.pos++] << 8; + b4 = this.data[this.pos++]; + return b1 | b2 | b3 | b4; + }, + readUInt16:function(){ + var b1, b2; + b1 = this.data[this.pos++] << 8; + b2 = this.data[this.pos++]; + return b1 | b2; + }, + decodePixels:function(data){ + var ccbyte, c, col, i, left, length, p, pa, paeth, pb, pc, pixelBytes, pixels, pos, row, scanlineLength, upper, upperLeft, _i, _j, _k, _l, _m; + if (data == null) { + data = this.imgData; + } + if (data.length === 0) { + return new Uint8Array(0); + } + var inflate = new Zlib.Inflate(data,{index:0, verify:false}); + data = inflate.decompress(); + + pixelBytes = this.pixelBitlength / 8; + scanlineLength = pixelBytes * this.width; + pixels = new Uint8Array(scanlineLength * this.height); + length = data.length; + row = 0; + pos = 0; + c = 0; + while (pos < length) { + switch (data[pos++]) { + case 0: + for (i = _i = 0; _i < scanlineLength; i = _i += 1) { + pixels[c++] = data[pos++]; + } + break; + case 1: + for (i = _j = 0; _j < scanlineLength; i = _j += 1) { + ccbyte = data[pos++]; + left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; + pixels[c++] = (ccbyte + left) % 256; + } + break; + case 2: + for (i = _k = 0; _k < scanlineLength; i = _k += 1) { + ccbyte = data[pos++]; + col = (i - (i % pixelBytes)) / pixelBytes; + upper = row && pixels[(row - 1) * scanlineLength + col * pixelBytes + (i % pixelBytes)]; + pixels[c++] = (upper + ccbyte) % 256; + } + break; + case 3: + for (i = _l = 0; _l < scanlineLength; i = _l += 1) { + ccbyte = data[pos++]; + col = (i - (i % pixelBytes)) / pixelBytes; + left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; + upper = row && pixels[(row - 1) * scanlineLength + col * pixelBytes + (i % pixelBytes)]; + pixels[c++] = (ccbyte + Math.floor((left + upper) / 2)) % 256; + } + break; + case 4: + for (i = _m = 0; _m < scanlineLength; i = _m += 1) { + ccbyte = data[pos++]; + col = (i - (i % pixelBytes)) / pixelBytes; + left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; + if (row === 0) { + upper = upperLeft = 0; + } else { + upper = pixels[(row - 1) * scanlineLength + col * pixelBytes + (i % pixelBytes)]; + upperLeft = col && pixels[(row - 1) * scanlineLength + (col - 1) * pixelBytes + (i % pixelBytes)]; + } + p = left + upper - upperLeft; + pa = Math.abs(p - left); + pb = Math.abs(p - upper); + pc = Math.abs(p - upperLeft); + if (pa <= pb && pa <= pc) { + paeth = left; + } else if (pb <= pc) { + paeth = upper; + } else { + paeth = upperLeft; + } + pixels[c++] = (ccbyte + paeth) % 256; + } + break; + default: + throw new Error("Invalid filter algorithm: " + data[pos - 1]); + } + row++; + } + return pixels; + }, + copyToImageData:function(imageData,pixels){ + var alpha, colors, data, i, input, j, k, length, palette, v, _ref; + colors = this.colors; + palette = null; + alpha = this.hasAlphaChannel; + if (this.palette.length) { + palette = (_ref = this._decodedPalette) != null ? _ref : this._decodedPalette = this.decodePalette(); + colors = 4; + alpha = true; + } + data = imageData.data || imageData; + length = data.length; + input = palette || pixels; + i = j = 0; + if (colors === 1) { + while (i < length) { + k = palette ? pixels[i / 4] * 4 : j; + v = input[k++]; + data[i++] = v; + data[i++] = v; + data[i++] = v; + data[i++] = alpha ? input[k++] : 255; + j = k; + } + } else { + while (i < length) { + k = palette ? pixels[i / 4] * 4 : j; + data[i++] = input[k++]; + data[i++] = input[k++]; + data[i++] = input[k++]; + data[i++] = alpha ? input[k++] : 255; + j = k; + } + } + }, + decodePalette:function(){ + var c, i, palette, pos, ret, transparency, _i, _ref, _ref1; + palette = this.palette; + transparency = this.transparency.indexed || []; + ret = new Uint8Array((transparency.length || 0) + palette.length); + pos = 0; + c = 0; + for (i = _i = 0, _ref = palette.length; _i < _ref; i = _i += 3) { + ret[pos++] = palette[i]; + ret[pos++] = palette[i + 1]; + ret[pos++] = palette[i + 2]; + ret[pos++] = (_ref1 = transparency[c++]) != null ? _ref1 : 255; + } + return ret; + }, + render: function (canvas) { + var ctx, data; + canvas.width = this.width; + canvas.height = this.height; + ctx = canvas.getContext("2d"); + data = ctx.createImageData(this.width, this.height); + this.copyToImageData(data, this.decodePixels()); + return ctx.putImageData(data, 0, 0); + + } +}); + diff --git a/frameworks/cocos2d-html5/cocos2d/particle/CCParticleBatchNode.js b/frameworks/cocos2d-html5/cocos2d/particle/CCParticleBatchNode.js new file mode 100644 index 0000000..53c381f --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/particle/CCParticleBatchNode.js @@ -0,0 +1,538 @@ +/** + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2011-2012 cocos2d-x.org + * Copyright (c) 2013-2014 Chukong Technologies Inc. + * Copyright (C) 2009 Matt Oswald + * Copyright (c) 2011 Marco Tillemans + * + * http://www.cocos2d-x.org + * + * 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. + * + */ + +/** + * paticle default capacity + * @constant + * @type Number + */ +cc.PARTICLE_DEFAULT_CAPACITY = 500; + +/** + *

+ * cc.ParticleBatchNode is like a batch node: if it contains children, it will draw them in 1 single OpenGL call
+ * (often known as "batch draw").
+ * + * A cc.ParticleBatchNode can reference one and only one texture (one image file, one texture atlas).
+ * Only the cc.ParticleSystems that are contained in that texture can be added to the cc.SpriteBatchNode.
+ * All cc.ParticleSystems added to a cc.SpriteBatchNode are drawn in one OpenGL ES draw call.
+ * If the cc.ParticleSystems are not added to a cc.ParticleBatchNode then an OpenGL ES draw call will be needed for each one, which is less efficient.
+ * + * Limitations:
+ * - At the moment only cc.ParticleSystem is supported
+ * - All systems need to be drawn with the same parameters, blend function, aliasing, texture
+ * + * Most efficient usage
+ * - Initialize the ParticleBatchNode with the texture and enough capacity for all the particle systems
+ * - Initialize all particle systems and add them as child to the batch node
+ *

+ * @class + * @extends cc.ParticleSystem + * @param {String|cc.Texture2D} fileImage + * @param {Number} capacity + * + * @property {cc.Texture2D|HTMLImageElement|HTMLCanvasElement} texture - The used texture + * @property {cc.TextureAtlas} textureAtlas - The texture atlas used for drawing the quads + * + * @example + * 1. + * //Create a cc.ParticleBatchNode with image path and capacity + * var particleBatchNode = new cc.ParticleBatchNode("res/grossini_dance.png",30); + * + * 2. + * //Create a cc.ParticleBatchNode with a texture and capacity + * var texture = cc.TextureCache.getInstance().addImage("res/grossini_dance.png"); + * var particleBatchNode = new cc.ParticleBatchNode(texture, 30); + */ +cc.ParticleBatchNode = cc.Node.extend(/** @lends cc.ParticleBatchNode# */{ + textureAtlas: null, + //the blend function used for drawing the quads + _blendFunc: null, + _className: "ParticleBatchNode", + + /** + * initializes the particle system with the name of a file on disk (for a list of supported formats look at the cc.Texture2D class), a capacity of particles + * Constructor of cc.ParticleBatchNode + * @param {String|cc.Texture2D} fileImage + * @param {Number} capacity + * @example + * 1. + * //Create a cc.ParticleBatchNode with image path and capacity + * var particleBatchNode = new cc.ParticleBatchNode("res/grossini_dance.png",30); + * + * 2. + * //Create a cc.ParticleBatchNode with a texture and capacity + * var texture = cc.TextureCache.getInstance().addImage("res/grossini_dance.png"); + * var particleBatchNode = new cc.ParticleBatchNode(texture, 30); + */ + ctor: function (fileImage, capacity) { + cc.Node.prototype.ctor.call(this); + this._blendFunc = {src: cc.BLEND_SRC, dst: cc.BLEND_DST}; + if (cc.isString(fileImage)) { + this.init(fileImage, capacity); + } else if (fileImage instanceof cc.Texture2D) { + this.initWithTexture(fileImage, capacity); + } + }, + + _createRenderCmd: function () { + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return new cc.ParticleBatchNode.CanvasRenderCmd(this); + else + return new cc.ParticleBatchNode.WebGLRenderCmd(this); + }, + + /** + * initializes the particle system with cc.Texture2D, a capacity of particles + * @param {cc.Texture2D|HTMLImageElement|HTMLCanvasElement} texture + * @param {Number} capacity + * @return {Boolean} + */ + initWithTexture: function (texture, capacity) { + this.textureAtlas = new cc.TextureAtlas(); + this.textureAtlas.initWithTexture(texture, capacity); + + // no lazy alloc in this node + this._children.length = 0; + + this._renderCmd._initWithTexture(); + return true; + }, + + /** + * initializes the particle system with the name of a file on disk (for a list of supported formats look at the cc.Texture2D class), a capacity of particles + * @param {String} fileImage + * @param {Number} capacity + * @return {Boolean} + */ + initWithFile: function (fileImage, capacity) { + var tex = cc.textureCache.addImage(fileImage); + return this.initWithTexture(tex, capacity); + }, + + /** + * initializes the particle system with the name of a file on disk (for a list of supported formats look at the cc.Texture2D class), a capacity of particles + * @param {String} fileImage + * @param {Number} capacity + * @return {Boolean} + */ + init: function (fileImage, capacity) { + var tex = cc.textureCache.addImage(fileImage); + return this.initWithTexture(tex, capacity); + }, + + visit: function (parent) { + var cmd = this._renderCmd, parentCmd = parent ? parent._renderCmd : null; + + // quick return if not visible + if (!this._visible) { + cmd._propagateFlagsDown(parentCmd); + return; + } + + cmd.visit(parentCmd); + cc.renderer.pushRenderCommand(cmd); + cmd._dirtyFlag = 0; + }, + + /** + * Add a child into the cc.ParticleBatchNode + * @param {cc.ParticleSystem} child + * @param {Number} zOrder + * @param {Number} tag + */ + addChild: function (child, zOrder, tag) { + if (!child) + throw new Error("cc.ParticleBatchNode.addChild() : child should be non-null"); + if (!(child instanceof cc.ParticleSystem)) + throw new Error("cc.ParticleBatchNode.addChild() : only supports cc.ParticleSystem as children"); + zOrder = (zOrder == null) ? child.zIndex : zOrder; + tag = (tag == null) ? child.tag : tag; + + if (child.getTexture() !== this.textureAtlas.texture) + throw new Error("cc.ParticleSystem.addChild() : the child is not using the same texture id"); + + // If this is the 1st children, then copy blending function + var childBlendFunc = child.getBlendFunc(); + if (this._children.length === 0) + this.setBlendFunc(childBlendFunc); + else { + if ((childBlendFunc.src !== this._blendFunc.src) || (childBlendFunc.dst !== this._blendFunc.dst)) { + cc.log("cc.ParticleSystem.addChild() : Can't add a ParticleSystem that uses a different blending function"); + return; + } + } + + //no lazy sorting, so don't call super addChild, call helper instead + var pos = this._addChildHelper(child, zOrder, tag); + + //get new atlasIndex + var atlasIndex = 0; + + if (pos !== 0) { + var p = this._children[pos - 1]; + atlasIndex = p.getAtlasIndex() + p.getTotalParticles(); + } else + atlasIndex = 0; + + this.insertChild(child, atlasIndex); + + // update quad info + child.setBatchNode(this); + }, + + /** + * Inserts a child into the cc.ParticleBatchNode + * @param {cc.ParticleSystem} pSystem + * @param {Number} index + */ + insertChild: function (pSystem, index) { + var totalParticles = pSystem.getTotalParticles(); + var locTextureAtlas = this.textureAtlas; + var totalQuads = locTextureAtlas.totalQuads; + pSystem.setAtlasIndex(index); + if (totalQuads + totalParticles > locTextureAtlas.getCapacity()) { + this._increaseAtlasCapacityTo(totalQuads + totalParticles); + // after a realloc empty quads of textureAtlas can be filled with gibberish (realloc doesn't perform calloc), insert empty quads to prevent it + locTextureAtlas.fillWithEmptyQuadsFromIndex(locTextureAtlas.getCapacity() - totalParticles, totalParticles); + } + + // make room for quads, not necessary for last child + if (pSystem.getAtlasIndex() + totalParticles !== totalQuads) + locTextureAtlas.moveQuadsFromIndex(index, index + totalParticles); + + // increase totalParticles here for new particles, update method of particlesystem will fill the quads + locTextureAtlas.increaseTotalQuadsWith(totalParticles); + this._updateAllAtlasIndexes(); + }, + + /** + * @param {cc.ParticleSystem} child + * @param {Boolean} cleanup + */ + removeChild: function (child, cleanup) { + // explicit nil handling + if (child == null) + return; + + if (!(child instanceof cc.ParticleSystem)) + throw new Error("cc.ParticleBatchNode.removeChild(): only supports cc.ParticleSystem as children"); + if (this._children.indexOf(child) === -1) { + cc.log("cc.ParticleBatchNode.removeChild(): doesn't contain the sprite. Can't remove it"); + return; + } + + cc.Node.prototype.removeChild.call(this, child, cleanup); + + var locTextureAtlas = this.textureAtlas; + // remove child helper + locTextureAtlas.removeQuadsAtIndex(child.getAtlasIndex(), child.getTotalParticles()); + + // after memmove of data, empty the quads at the end of array + locTextureAtlas.fillWithEmptyQuadsFromIndex(locTextureAtlas.totalQuads, child.getTotalParticles()); + + // paticle could be reused for self rendering + child.setBatchNode(null); + + this._updateAllAtlasIndexes(); + }, + + /** + * Reorder will be done in this function, no "lazy" reorder to particles + * @param {cc.ParticleSystem} child + * @param {Number} zOrder + */ + reorderChild: function (child, zOrder) { + if (!child) + throw new Error("cc.ParticleBatchNode.reorderChild(): child should be non-null"); + if (!(child instanceof cc.ParticleSystem)) + throw new Error("cc.ParticleBatchNode.reorderChild(): only supports cc.QuadParticleSystems as children"); + if (this._children.indexOf(child) === -1) { + cc.log("cc.ParticleBatchNode.reorderChild(): Child doesn't belong to batch"); + return; + } + + // no reordering if only 1 child + if (this._children.length > 1) { + var getIndexes = this._getCurrentIndex(child, zOrder); + + if (getIndexes.oldIndex !== getIndexes.newIndex) { + // reorder m_pChildren.array + this._children.splice(getIndexes.oldIndex, 1) + this._children.splice(getIndexes.newIndex, 0, child); + + // save old altasIndex + var oldAtlasIndex = child.getAtlasIndex(); + + // update atlas index + this._updateAllAtlasIndexes(); + + // Find new AtlasIndex + var newAtlasIndex = 0; + var locChildren = this._children; + for (var i = 0; i < locChildren.length; i++) { + var pNode = locChildren[i]; + if (pNode === child) { + newAtlasIndex = child.getAtlasIndex(); + break; + } + } + + // reorder textureAtlas quads + this.textureAtlas.moveQuadsFromIndex(oldAtlasIndex, child.getTotalParticles(), newAtlasIndex); + + child.updateWithNoTime(); + } + } + child._setLocalZOrder(zOrder); + }, + + /** + * @param {Number} index + * @param {Boolean} doCleanup + */ + removeChildAtIndex: function (index, doCleanup) { + this.removeChild(this._children[i], doCleanup); + }, + + /** + * @param {Boolean} [doCleanup=true] + */ + removeAllChildren: function (doCleanup) { + var locChildren = this._children; + for (var i = 0; i < locChildren.length; i++) { + locChildren[i].setBatchNode(null); + } + cc.Node.prototype.removeAllChildren.call(this, doCleanup); + this.textureAtlas.removeAllQuads(); + }, + + /** + * disables a particle by inserting a 0'd quad into the texture atlas + * @param {Number} particleIndex + */ + disableParticle: function (particleIndex) { + var quad = this.textureAtlas.quads[particleIndex]; + quad.br.vertices.x = quad.br.vertices.y = quad.tr.vertices.x = quad.tr.vertices.y = + quad.tl.vertices.x = quad.tl.vertices.y = quad.bl.vertices.x = quad.bl.vertices.y = 0.0; + this.textureAtlas._setDirty(true); + }, + + /** + * returns the used texture + * @return {cc.Texture2D} + */ + getTexture: function () { + return this.textureAtlas.texture; + }, + + /** + * sets a new texture. it will be retained + * @param {cc.Texture2D} texture + */ + setTexture: function (texture) { + this.textureAtlas.texture = texture; + + // If the new texture has No premultiplied alpha, AND the blendFunc hasn't been changed, then update it + var locBlendFunc = this._blendFunc; + if (texture && !texture.hasPremultipliedAlpha() && ( locBlendFunc.src === cc.BLEND_SRC && locBlendFunc.dst === cc.BLEND_DST )) { + locBlendFunc.src = cc.SRC_ALPHA; + locBlendFunc.dst = cc.ONE_MINUS_SRC_ALPHA; + } + }, + + /** + * set the blending function used for the texture + * @param {Number|Object} src + * @param {Number} dst + */ + setBlendFunc: function (src, dst) { + if (dst === undefined) { + this._blendFunc.src = src.src; + this._blendFunc.dst = src.dst; + } else { + this._blendFunc.src = src; + this._blendFunc.src = dst; + } + }, + + /** + * returns the blending function used for the texture + * @return {cc.BlendFunc} + */ + getBlendFunc: function () { + return new cc.BlendFunc(this._blendFunc.src, this._blendFunc.dst); + }, + + _updateAllAtlasIndexes: function () { + var index = 0; + var locChildren = this._children; + for (var i = 0; i < locChildren.length; i++) { + var child = locChildren[i]; + child.setAtlasIndex(index); + index += child.getTotalParticles(); + } + }, + + _increaseAtlasCapacityTo: function (quantity) { + cc.log("cocos2d: cc.ParticleBatchNode: resizing TextureAtlas capacity from [" + this.textureAtlas.getCapacity() + + "] to [" + quantity + "]."); + + if (!this.textureAtlas.resizeCapacity(quantity)) { + // serious problems + cc.log("cc.ParticleBatchNode._increaseAtlasCapacityTo() : WARNING: Not enough memory to resize the atlas"); + } + }, + + _searchNewPositionInChildrenForZ: function (z) { + var locChildren = this._children; + var count = locChildren.length; + for (var i = 0; i < count; i++) { + if (locChildren[i].zIndex > z) + return i; + } + return count; + }, + + _getCurrentIndex: function (child, z) { + var foundCurrentIdx = false; + var foundNewIdx = false; + + var newIndex = 0; + var oldIndex = 0; + + var minusOne = 0, locChildren = this._children; + var count = locChildren.length; + for (var i = 0; i < count; i++) { + var pNode = locChildren[i]; + // new index + if (pNode.zIndex > z && !foundNewIdx) { + newIndex = i; + foundNewIdx = true; + + if (foundCurrentIdx && foundNewIdx) + break; + } + // current index + if (child === pNode) { + oldIndex = i; + foundCurrentIdx = true; + if (!foundNewIdx) + minusOne = -1; + if (foundCurrentIdx && foundNewIdx) + break; + } + } + if (!foundNewIdx) + newIndex = count; + newIndex += minusOne; + return {newIndex: newIndex, oldIndex: oldIndex}; + }, + + // + //

+ // don't use lazy sorting, reordering the particle systems quads afterwards would be too complex
+ // XXX research whether lazy sorting + freeing current quads and calloc a new block with size of capacity would be faster
+ // XXX or possibly using vertexZ for reordering, that would be fastest
+ // this helper is almost equivalent to CCNode's addChild, but doesn't make use of the lazy sorting
+ //

+ // @param {cc.ParticleSystem} child + // @param {Number} z + // @param {Number} aTag + // @return {Number} + // @private + // + _addChildHelper: function (child, z, aTag) { + if (!child) + throw new Error("cc.ParticleBatchNode._addChildHelper(): child should be non-null"); + if (child.parent) { + cc.log("cc.ParticleBatchNode._addChildHelper(): child already added. It can't be added again"); + return null; + } + + + if (!this._children) + this._children = []; + + //don't use a lazy insert + var pos = this._searchNewPositionInChildrenForZ(z); + + this._children.splice(pos, 0, child); + child.tag = aTag; + child._setLocalZOrder(z); + child.parent = this; + if (this._running) { + child._performRecursive(cc.Node._stateCallbackType.onEnter); + child._performRecursive(cc.Node._stateCallbackType.onEnterTransitionDidFinish); + } + return pos; + }, + + _updateBlendFunc: function () { + if (!this.textureAtlas.texture.hasPremultipliedAlpha()) { + this._blendFunc.src = cc.SRC_ALPHA; + this._blendFunc.dst = cc.ONE_MINUS_SRC_ALPHA; + } + }, + + /** + * return the texture atlas used for drawing the quads + * @return {cc.TextureAtlas} + */ + getTextureAtlas: function () { + return this.textureAtlas; + }, + + /** + * set the texture atlas used for drawing the quads + * @param {cc.TextureAtlas} textureAtlas + */ + setTextureAtlas: function (textureAtlas) { + this.textureAtlas = textureAtlas; + } +}); + +var _p = cc.ParticleBatchNode.prototype; + +// Extended properties +/** @expose */ +_p.texture; +cc.defineGetterSetter(_p, "texture", _p.getTexture, _p.setTexture); + + +/** + * initializes the particle system with the name of a file on disk (for a list of supported formats look at the cc.Texture2D class), a capacity of particles + * @deprecated since v3.0 please use new cc.ParticleBatchNode(filename, capacity) instead. + * @param {String|cc.Texture2D} fileImage + * @param {Number} capacity + * @return {cc.ParticleBatchNode} + */ +cc.ParticleBatchNode.create = function (fileImage, capacity) { + return new cc.ParticleBatchNode(fileImage, capacity); +}; diff --git a/frameworks/cocos2d-html5/cocos2d/particle/CCParticleBatchNodeCanvasRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/particle/CCParticleBatchNodeCanvasRenderCmd.js new file mode 100644 index 0000000..6e0990d --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/particle/CCParticleBatchNodeCanvasRenderCmd.js @@ -0,0 +1,39 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + /** + * cc.ParticleBatchNode's rendering objects of Canvas + */ + cc.ParticleBatchNode.CanvasRenderCmd = function (renderable) { + this._rootCtor(renderable); + this._needDraw = false; + }; + + var proto = cc.ParticleBatchNode.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + proto.constructor = cc.ParticleBatchNode.CanvasRenderCmd; + + proto._initWithTexture = function () { + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/particle/CCParticleBatchNodeWebGLRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/particle/CCParticleBatchNodeWebGLRenderCmd.js new file mode 100644 index 0000000..765e755 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/particle/CCParticleBatchNodeWebGLRenderCmd.js @@ -0,0 +1,60 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + /** + * cc.ParticleBatchNode's rendering objects of WebGL + */ + cc.ParticleBatchNode.WebGLRenderCmd = function (renderable) { + this._rootCtor(renderable); + this._needDraw = true; + this._matrix = new cc.math.Matrix4(); + this._matrix.identity(); + }; + + var proto = cc.ParticleBatchNode.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + proto.constructor = cc.ParticleBatchNode.WebGLRenderCmd; + + proto.rendering = function (ctx) { + var _t = this._node; + if (_t.textureAtlas.totalQuads === 0) + return; + + var wt = this._worldTransform; + this._matrix.mat[0] = wt.a; + this._matrix.mat[4] = wt.c; + this._matrix.mat[12] = wt.tx; + this._matrix.mat[1] = wt.b; + this._matrix.mat[5] = wt.d; + this._matrix.mat[13] = wt.ty; + + this._glProgramState.apply(this._matrix); + cc.glBlendFuncForParticle(_t._blendFunc.src, _t._blendFunc.dst); + _t.textureAtlas.drawQuads(); + }; + + proto._initWithTexture = function () { + this._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR); + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/particle/CCParticleExamples.js b/frameworks/cocos2d-html5/cocos2d/particle/CCParticleExamples.js new file mode 100644 index 0000000..7c89bb3 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/particle/CCParticleExamples.js @@ -0,0 +1,1006 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * A fire particle system + * @class + * @extends cc.ParticleSystem + * + * @example + * var emitter = new cc.ParticleFire(); + */ +cc.ParticleFire = cc.ParticleSystem.extend(/** @lends cc.ParticleFire# */{ + /** + *

The cc.ParticleFire's constructor.
+ * This function will automatically be invoked when you create a node using new construction: "var node = new cc.ParticleFire()".
+ * Override it to extend its behavior, remember to call "this._super()" in the extended "ctor" function.

+ */ + ctor:function () { + cc.ParticleSystem.prototype.ctor.call(this, (cc._renderType === cc.game.RENDER_TYPE_WEBGL) ? 300 : 150); + }, + + /** + * initialize a fire particle system with number Of Particles + * @param {Number} numberOfParticles + * @return {Boolean} + */ + initWithTotalParticles:function (numberOfParticles) { + if (cc.ParticleSystem.prototype.initWithTotalParticles.call(this, numberOfParticles)) { + // duration + this.setDuration(cc.ParticleSystem.DURATION_INFINITY); + + // Gravity Mode + this.setEmitterMode(cc.ParticleSystem.MODE_GRAVITY); + + + // Gravity Mode: gravity + this.setGravity(cc.p(0, 0)); + + // Gravity Mode: radial acceleration + this.setRadialAccel(0); + this.setRadialAccelVar(0); + + // Gravity Mode: speed of particles + this.setSpeed(60); + this.setSpeedVar(20); + + // starting angle + this.setAngle(90); + this.setAngleVar(10); + + // emitter position + var winSize = cc.director.getWinSize(); + this.setPosition(winSize.width / 2, 60); + this.setPosVar(cc.p(40, 20)); + + // life of particles + this.setLife(3); + this.setLifeVar(0.25); + + + // size, in pixels + this.setStartSize(54.0); + this.setStartSizeVar(10.0); + this.setEndSize(cc.ParticleSystem.START_SIZE_EQUAL_TO_END_SIZE); + + // emits per frame + this.setEmissionRate(this.getTotalParticles() / this.getLife()); + + // color of particles + this.setStartColor(cc.color(194,64,31,255)); + this.setStartColorVar(cc.color(0,0,0,0)); + this.setEndColor(cc.color(0,0,0,255)); + this.setEndColorVar(cc.color(0,0,0,0)); + + // additive + this.setBlendAdditive(true); + return true; + } + return false; + } +}); + +/** + * Create a fire particle system + * @deprecated since v3.0 please use new cc.ParticleFire() instead + * @return {cc.ParticleFire} + */ +cc.ParticleFire.create = function () { + return new cc.ParticleFire(); +}; + +/** + * A fireworks particle system + * @class + * @extends cc.ParticleSystem + * + * @example + * var emitter = new cc.ParticleFireworks(); + */ +cc.ParticleFireworks = cc.ParticleSystem.extend(/** @lends cc.ParticleFireworks# */{ + /** + *

The cc.ParticleFireworks's constructor.
+ * This function will automatically be invoked when you create a node using new construction: "var node = new cc.ParticleFireworks()".
+ * Override it to extend its behavior, remember to call "this._super()" in the extended "ctor" function.

+ */ + ctor:function () { + cc.ParticleSystem.prototype.ctor.call(this, (cc._renderType === cc.game.RENDER_TYPE_WEBGL) ? 1500 : 150); + }, + + /** + * initialize a fireworks particle system with number Of Particles + * @param {Number} numberOfParticles + * @return {Boolean} + */ + initWithTotalParticles:function (numberOfParticles) { + if (cc.ParticleSystem.prototype.initWithTotalParticles.call(this, numberOfParticles)) { + // duration + this.setDuration(cc.ParticleSystem.DURATION_INFINITY); + + // Gravity Mode + this.setEmitterMode(cc.ParticleSystem.MODE_GRAVITY); + + // Gravity Mode: gravity + this.setGravity(cc.p(0, -90)); + + // Gravity Mode: radial + this.setRadialAccel(0); + this.setRadialAccelVar(0); + + // Gravity Mode: speed of particles + this.setSpeed(180); + this.setSpeedVar(50); + + // emitter position + var winSize = cc.director.getWinSize(); + this.setPosition(winSize.width / 2, winSize.height / 2); + + // angle + this.setAngle(90); + this.setAngleVar(20); + + // life of particles + this.setLife(3.5); + this.setLifeVar(1); + + // emits per frame + this.setEmissionRate(this.getTotalParticles() / this.getLife()); + + // color of particles + this.setStartColor(cc.color(128,128,128,255)); + this.setStartColorVar(cc.color(128,128,128,255)); + this.setEndColor(cc.color(26,26,26,51)); + this.setEndColorVar(cc.color(26,26,26,51)); + + // size, in pixels + this.setStartSize(8.0); + this.setStartSizeVar(2.0); + this.setEndSize(cc.ParticleSystem.START_SIZE_EQUAL_TO_END_SIZE); + + // additive + this.setBlendAdditive(false); + return true; + } + return false; + } +}); + +/** + * Create a fireworks particle system + * @deprecated since v3.0 please use new cc.ParticleFireworks() instead. + * @return {cc.ParticleFireworks} + */ +cc.ParticleFireworks.create = function () { + return new cc.ParticleFireworks(); +}; + +/** + * A sun particle system + * @class + * @extends cc.ParticleSystem + * + * @example + * var emitter = new cc.ParticleSun(); + */ +cc.ParticleSun = cc.ParticleSystem.extend(/** @lends cc.ParticleSun# */{ + /** + *

The cc.ParticleSun's constructor.
+ * This function will automatically be invoked when you create a node using new construction: "var node = new cc.ParticleSun()".
+ * Override it to extend its behavior, remember to call "this._super()" in the extended "ctor" function.

+ */ + ctor:function () { + cc.ParticleSystem.prototype.ctor.call(this, (cc._renderType === cc.game.RENDER_TYPE_WEBGL) ? 350 : 150); + }, + + /** + * initialize a sun particle system with number Of Particles + * @param {Number} numberOfParticles + * @return {Boolean} + */ + initWithTotalParticles:function (numberOfParticles) { + if (cc.ParticleSystem.prototype.initWithTotalParticles.call(this, numberOfParticles)) { + // additive + this.setBlendAdditive(true); + + // duration + this.setDuration(cc.ParticleSystem.DURATION_INFINITY); + + // Gravity Mode + this.setEmitterMode(cc.ParticleSystem.MODE_GRAVITY); + + // Gravity Mode: gravity + this.setGravity(cc.p(0, 0)); + + // Gravity mode: radial acceleration + this.setRadialAccel(0); + this.setRadialAccelVar(0); + + // Gravity mode: speed of particles + this.setSpeed(20); + this.setSpeedVar(5); + + // angle + this.setAngle(90); + this.setAngleVar(360); + + // emitter position + var winSize = cc.director.getWinSize(); + this.setPosition(winSize.width / 2, winSize.height / 2); + this.setPosVar(cc.p(0,0)); + + // life of particles + this.setLife(1); + this.setLifeVar(0.5); + + // size, in pixels + this.setStartSize(30.0); + this.setStartSizeVar(10.0); + this.setEndSize(cc.ParticleSystem.START_SIZE_EQUAL_TO_END_SIZE); + + // emits per seconds + this.setEmissionRate(this.getTotalParticles() / this.getLife()); + + // color of particles + this.setStartColor(cc.color(194, 64, 31, 255)); + this.setStartColorVar(cc.color(0, 0, 0, 0)); + this.setEndColor(cc.color(0, 0, 0, 255)); + this.setEndColorVar(cc.color(0, 0, 0, 0)); + + return true; + } + return false; + } +}); + +/** + * Create a sun particle system + * @deprecated since v3.0 please use new cc.ParticleSun() instead. + * @return {cc.ParticleSun} + */ +cc.ParticleSun.create = function () { + return new cc.ParticleSun(); +}; + +//! @brief A particle system +/** + * A galaxy particle system + * @class + * @extends cc.ParticleSystem + * + * @example + * var emitter = new cc.ParticleGalaxy(); + */ +cc.ParticleGalaxy = cc.ParticleSystem.extend(/** @lends cc.ParticleGalaxy# */{ + /** + *

The cc.ParticleGalaxy's constructor.
+ * This function will automatically be invoked when you create a node using new construction: "var node = new cc.ParticleGalaxy()".
+ * Override it to extend its behavior, remember to call "this._super()" in the extended "ctor" function.

+ */ + ctor:function () { + cc.ParticleSystem.prototype.ctor.call(this, (cc._renderType === cc.game.RENDER_TYPE_WEBGL) ? 200 : 100); + }, + + /** + * initialize a galaxy particle system with number Of Particles + * @param {Number} numberOfParticles + * @return {Boolean} + */ + initWithTotalParticles:function (numberOfParticles) { + if (cc.ParticleSystem.prototype.initWithTotalParticles.call(this, numberOfParticles)) { + // duration + this.setDuration(cc.ParticleSystem.DURATION_INFINITY); + + // Gravity Mode + this.setEmitterMode(cc.ParticleSystem.MODE_GRAVITY); + + // Gravity Mode: gravity + this.setGravity(cc.p(0, 0)); + + // Gravity Mode: speed of particles + this.setSpeed(60); + this.setSpeedVar(10); + + // Gravity Mode: radial + this.setRadialAccel(-80); + this.setRadialAccelVar(0); + + // Gravity Mode: tangential + this.setTangentialAccel(80); + this.setTangentialAccelVar(0); + + // angle + this.setAngle(90); + this.setAngleVar(360); + + // emitter position + var winSize = cc.director.getWinSize(); + this.setPosition(winSize.width / 2, winSize.height / 2); + this.setPosVar(cc.p(0,0)); + + // life of particles + this.setLife(4); + this.setLifeVar(1); + + // size, in pixels + this.setStartSize(37.0); + this.setStartSizeVar(10.0); + this.setEndSize(cc.ParticleSystem.START_SIZE_EQUAL_TO_END_SIZE); + + // emits per second + this.setEmissionRate(this.getTotalParticles() / this.getLife()); + + // color of particles + this.setStartColor(cc.color(31, 64, 194, 255)); + this.setStartColorVar(cc.color(0, 0, 0, 0)); + this.setEndColor(cc.color(0, 0, 0, 255)); + this.setEndColorVar(cc.color(0, 0, 0, 0)); + + // additive + this.setBlendAdditive(true); + return true; + } + return false; + } +}); +/** + * Create a galaxy particle system + * @deprecated since v3.0 please use new cc.OarticleGalaxy() instead. + * @return {cc.ParticleGalaxy} + */ +cc.ParticleGalaxy.create = function () { + return new cc.ParticleGalaxy(); +}; + +/** + * A flower particle system + * @class + * @extends cc.ParticleSystem + * + * @example + * var emitter = new cc.ParticleFlower(); + */ +cc.ParticleFlower = cc.ParticleSystem.extend(/** @lends cc.ParticleFlower# */{ + /** + *

The cc.ParticleFlower's constructor.
+ * This function will automatically be invoked when you create a node using new construction: "var node = new cc.ParticleFlower()".
+ * Override it to extend its behavior, remember to call "this._super()" in the extended "ctor" function.

+ */ + ctor : function () { + cc.ParticleSystem.prototype.ctor.call(this, (cc._renderType === cc.game.RENDER_TYPE_WEBGL) ? 250 : 100); + }, + + /** + * initialize a flower particle system with number Of Particles + * @param {Number} numberOfParticles + * @return {Boolean} + */ + initWithTotalParticles:function (numberOfParticles) { + if (cc.ParticleSystem.prototype.initWithTotalParticles.call(this, numberOfParticles)) { + // duration + this.setDuration(cc.ParticleSystem.DURATION_INFINITY); + + // Gravity Mode + this.setEmitterMode(cc.ParticleSystem.MODE_GRAVITY); + + // Gravity Mode: gravity + this.setGravity(cc.p(0, 0)); + + // Gravity Mode: speed of particles + this.setSpeed(80); + this.setSpeedVar(10); + + // Gravity Mode: radial + this.setRadialAccel(-60); + this.setRadialAccelVar(0); + + // Gravity Mode: tangential + this.setTangentialAccel(15); + this.setTangentialAccelVar(0); + + // angle + this.setAngle(90); + this.setAngleVar(360); + + // emitter position + var winSize = cc.director.getWinSize(); + this.setPosition(winSize.width / 2, winSize.height / 2); + this.setPosVar(cc.p(0,0)); + + // life of particles + this.setLife(4); + this.setLifeVar(1); + + // size, in pixels + this.setStartSize(30.0); + this.setStartSizeVar(10.0); + this.setEndSize(cc.ParticleSystem.START_SIZE_EQUAL_TO_END_SIZE); + + // emits per second + this.setEmissionRate(this.getTotalParticles() / this.getLife()); + + // color of particles + this.setStartColor(cc.color(128, 128, 128, 255)); + this.setStartColorVar(cc.color(128, 128, 128, 128)); + this.setEndColor(cc.color(0, 0, 0, 255)); + this.setEndColorVar(cc.color(0, 0, 0, 0)); + + // additive + this.setBlendAdditive(true); + return true; + } + return false; + } +}); + +/** + * Create a flower particle system + * @deprecated since v3.0 please use new cc.ParticleFlower() instead. + * @return {cc.ParticleFlower} + */ +cc.ParticleFlower.create = function () { + return new cc.ParticleFlower(); +}; + +//! @brief A meteor particle system +/** + * A meteor particle system + * @class + * @extends cc.ParticleSystem + * + * @example + * var emitter = new cc.ParticleMeteor(); + */ +cc.ParticleMeteor = cc.ParticleSystem.extend(/** @lends cc.ParticleMeteor# */{ + /** + *

The cc.ParticleMeteor's constructor.
+ * This function will automatically be invoked when you create a node using new construction: "var node = new cc.ParticleMeteor()".
+ * Override it to extend its behavior, remember to call "this._super()" in the extended "ctor" function.

+ */ + ctor:function () { + cc.ParticleSystem.prototype.ctor.call(this, (cc._renderType === cc.game.RENDER_TYPE_WEBGL) ? 150 : 100); + }, + + /** + * initialize a meteor particle system with number Of Particles + * @param {Number} numberOfParticles + * @return {Boolean} + */ + initWithTotalParticles:function (numberOfParticles) { + if (cc.ParticleSystem.prototype.initWithTotalParticles.call(this, numberOfParticles)) { + // duration + this.setDuration(cc.ParticleSystem.DURATION_INFINITY); + + // Gravity Mode + this.setEmitterMode(cc.ParticleSystem.MODE_GRAVITY); + + // Gravity Mode: gravity + this.setGravity(cc.p(-200, 200)); + + // Gravity Mode: speed of particles + this.setSpeed(15); + this.setSpeedVar(5); + + // Gravity Mode: radial + this.setRadialAccel(0); + this.setRadialAccelVar(0); + + // Gravity Mode: tangential + this.setTangentialAccel(0); + this.setTangentialAccelVar(0); + + // angle + this.setAngle(90); + this.setAngleVar(360); + + // emitter position + var winSize = cc.director.getWinSize(); + this.setPosition(winSize.width / 2, winSize.height / 2); + this.setPosVar(cc.p(0,0)); + + // life of particles + this.setLife(2); + this.setLifeVar(1); + + // size, in pixels + this.setStartSize(60.0); + this.setStartSizeVar(10.0); + this.setEndSize(cc.ParticleSystem.START_SIZE_EQUAL_TO_END_SIZE); + + // emits per second + this.setEmissionRate(this.getTotalParticles() / this.getLife()); + + // color of particles + this.setStartColor(cc.color(51, 102, 179)); + this.setStartColorVar(cc.color(0, 0, 51, 26)); + this.setEndColor(cc.color(0, 0, 0, 255)); + this.setEndColorVar(cc.color(0, 0, 0, 0)); + + // additive + this.setBlendAdditive(true); + return true; + } + return false; + } +}); + +/** + * Create a meteor particle system + * @deprecated since v3.0 please use new cc.ParticleMeteor() instead. + * @return {cc.ParticleMeteor} + */ +cc.ParticleMeteor.create = function () { + return new cc.ParticleMeteor(); +}; + +/** + * A spiral particle system + * @class + * @extends cc.ParticleSystem + * + * @example + * var emitter = new cc.ParticleSpiral(); + */ +cc.ParticleSpiral = cc.ParticleSystem.extend(/** @lends cc.ParticleSpiral# */{ + + /** + *

The cc.ParticleSpiral's constructor.
+ * This function will automatically be invoked when you create a node using new construction: "var node = new cc.ParticleSpiral()".
+ * Override it to extend its behavior, remember to call "this._super()" in the extended "ctor" function.

+ */ + ctor:function() { + cc.ParticleSystem.prototype.ctor.call(this,(cc._renderType === cc.game.RENDER_TYPE_WEBGL) ? 500 : 100); + }, + + /** + * initialize a spiral particle system with number Of Particles + * @param {Number} numberOfParticles + * @return {Boolean} + */ + initWithTotalParticles:function (numberOfParticles) { + if (cc.ParticleSystem.prototype.initWithTotalParticles.call(this, numberOfParticles)) { + // duration + this.setDuration(cc.ParticleSystem.DURATION_INFINITY); + + // Gravity Mode + this.setEmitterMode(cc.ParticleSystem.MODE_GRAVITY); + + // Gravity Mode: gravity + this.setGravity(cc.p(0, 0)); + + // Gravity Mode: speed of particles + this.setSpeed(150); + this.setSpeedVar(0); + + // Gravity Mode: radial + this.setRadialAccel(-380); + this.setRadialAccelVar(0); + + // Gravity Mode: tangential + this.setTangentialAccel(45); + this.setTangentialAccelVar(0); + + // angle + this.setAngle(90); + this.setAngleVar(0); + + // emitter position + var winSize = cc.director.getWinSize(); + this.setPosition(winSize.width / 2, winSize.height / 2); + this.setPosVar(cc.p(0,0)); + + // life of particles + this.setLife(12); + this.setLifeVar(0); + + // size, in pixels + this.setStartSize(20.0); + this.setStartSizeVar(0.0); + this.setEndSize(cc.ParticleSystem.START_SIZE_EQUAL_TO_END_SIZE); + + // emits per second + this.setEmissionRate(this.getTotalParticles() / this.getLife()); + + // color of particles + this.setStartColor(cc.color(128,128,128,255)); + this.setStartColorVar(cc.color(128,128,128,0)); + this.setEndColor(cc.color(128,128,128,255)); + this.setEndColorVar(cc.color(128,128,128,0)); + + // additive + this.setBlendAdditive(false); + return true; + } + return false; + } +}); + +/** + * Create a spiral particle system + * @deprecated since v3.0 please use new cc.ParticleSpiral() instead. + * @return {cc.ParticleSpiral} + */ +cc.ParticleSpiral.create = function () { + return new cc.ParticleSpiral(); +}; + +/** + * An explosion particle system + * @class + * @extends cc.ParticleSystem + * + * @example + * var emitter = new cc.ParticleExplosion(); + */ +cc.ParticleExplosion = cc.ParticleSystem.extend(/** @lends cc.ParticleExplosion# */{ + /** + *

The cc.ParticleExplosion's constructor.
+ * This function will automatically be invoked when you create a node using new construction: "var node = new cc.ParticleExplosion()".
+ * Override it to extend its behavior, remember to call "this._super()" in the extended "ctor" function.

+ */ + ctor:function () { + cc.ParticleSystem.prototype.ctor.call(this, (cc._renderType === cc.game.RENDER_TYPE_WEBGL) ? 700 : 300); + }, + + /** + * initialize an explosion particle system with number Of Particles + * @param {Number} numberOfParticles + * @return {Boolean} + */ + initWithTotalParticles:function (numberOfParticles) { + if (cc.ParticleSystem.prototype.initWithTotalParticles.call(this, numberOfParticles)) { + // duration + this.setDuration(0.1); + + this.setEmitterMode(cc.ParticleSystem.MODE_GRAVITY); + + // Gravity Mode: gravity + this.setGravity(cc.p(0, 0)); + + // Gravity Mode: speed of particles + this.setSpeed(70); + this.setSpeedVar(40); + + // Gravity Mode: radial + this.setRadialAccel(0); + this.setRadialAccelVar(0); + + // Gravity Mode: tangential + this.setTangentialAccel(0); + this.setTangentialAccelVar(0); + + // angle + this.setAngle(90); + this.setAngleVar(360); + + // emitter position + var winSize = cc.director.getWinSize(); + this.setPosition(winSize.width / 2, winSize.height / 2); + this.setPosVar(cc.p(0,0)); + + // life of particles + this.setLife(5.0); + this.setLifeVar(2); + + // size, in pixels + this.setStartSize(15.0); + this.setStartSizeVar(10.0); + this.setEndSize(cc.ParticleSystem.START_SIZE_EQUAL_TO_END_SIZE); + + // emits per second + this.setEmissionRate(this.getTotalParticles() / this.getDuration()); + + // color of particles + this.setStartColor(cc.color(179, 26, 51, 255)); + this.setStartColorVar(cc.color(128, 128, 128, 0)); + this.setEndColor(cc.color(128, 128, 128, 0)); + this.setEndColorVar(cc.color(128, 128, 128, 0)); + + // additive + this.setBlendAdditive(false); + return true; + } + return false; + } +}); + +/** + * Create an explosion particle system + * @deprecated since v3.0 please use new cc.ParticleExplosion() instead. + * @return {cc.ParticleExplosion} + */ +cc.ParticleExplosion.create = function () { + return new cc.ParticleExplosion(); +}; + +/** + * A smoke particle system + * @class + * @extends cc.ParticleSystem + * + * @example + * var emitter = new cc.ParticleSmoke(); + */ +cc.ParticleSmoke = cc.ParticleSystem.extend(/** @lends cc.ParticleSmoke# */{ + + /** + *

The cc.ParticleSmoke's constructor.
+ * This function will automatically be invoked when you create a node using new construction: "var node = new cc.ParticleSmoke()".
+ * Override it to extend its behavior, remember to call "this._super()" in the extended "ctor" function.

+ */ + ctor:function () { + cc.ParticleSystem.prototype.ctor.call(this, (cc._renderType === cc.game.RENDER_TYPE_WEBGL) ? 200 : 100); + }, + + /** + * initialize a smoke particle system with number Of Particles + * @param {Number} numberOfParticles + * @return {Boolean} + */ + initWithTotalParticles:function (numberOfParticles) { + if (cc.ParticleSystem.prototype.initWithTotalParticles.call(this, numberOfParticles)) { + // duration + this.setDuration(cc.ParticleSystem.DURATION_INFINITY); + + // Emitter mode: Gravity Mode + this.setEmitterMode(cc.ParticleSystem.MODE_GRAVITY); + + // Gravity Mode: gravity + this.setGravity(cc.p(0, 0)); + + // Gravity Mode: radial acceleration + this.setRadialAccel(0); + this.setRadialAccelVar(0); + + // Gravity Mode: speed of particles + this.setSpeed(25); + this.setSpeedVar(10); + + // angle + this.setAngle(90); + this.setAngleVar(5); + + // emitter position + var winSize = cc.director.getWinSize(); + this.setPosition(winSize.width / 2, 0); + this.setPosVar(cc.p(20, 0)); + + // life of particles + this.setLife(4); + this.setLifeVar(1); + + // size, in pixels + this.setStartSize(60.0); + this.setStartSizeVar(10.0); + this.setEndSize(cc.ParticleSystem.START_SIZE_EQUAL_TO_END_SIZE); + + // emits per frame + this.setEmissionRate(this.getTotalParticles() / this.getLife()); + + // color of particles + this.setStartColor(cc.color(204, 204, 204, 255)); + this.setStartColorVar(cc.color(5, 5, 5, 0)); + this.setEndColor(cc.color(0, 0, 0, 255)); + this.setEndColorVar(cc.color(0, 0, 0, 0)); + + // additive + this.setBlendAdditive(false); + return true; + } + return false; + } +}); + +/** + * Create a smoke particle system + * @deprecated since v3.0 please use new cc.ParticleSmoke() instead. + * @return {cc.ParticleSmoke} + */ +cc.ParticleSmoke.create = function () { + return new cc.ParticleSmoke(); +}; + +/** + * A snow particle system + * @class + * @extends cc.ParticleSystem + * + * @example + * var emitter = new cc.ParticleSnow(); + */ +cc.ParticleSnow = cc.ParticleSystem.extend(/** @lends cc.ParticleSnow# */{ + + /** + *

The cc.ParticleSnow's constructor.
+ * This function will automatically be invoked when you create a node using new construction: "var node = new cc.ParticleSnow()".
+ * Override it to extend its behavior, remember to call "this._super()" in the extended "ctor" function.

+ */ + ctor:function () { + cc.ParticleSystem.prototype.ctor.call(this, (cc._renderType === cc.game.RENDER_TYPE_WEBGL) ? 700 : 250); + }, + + /** + * initialize a snow particle system with number Of Particles + * @param {Number} numberOfParticles + * @return {Boolean} + */ + initWithTotalParticles:function (numberOfParticles) { + if (cc.ParticleSystem.prototype.initWithTotalParticles.call(this, numberOfParticles)) { + // duration + this.setDuration(cc.ParticleSystem.DURATION_INFINITY); + + // set gravity mode. + this.setEmitterMode(cc.ParticleSystem.MODE_GRAVITY); + + // Gravity Mode: gravity + this.setGravity(cc.p(0, -1)); + + // Gravity Mode: speed of particles + this.setSpeed(5); + this.setSpeedVar(1); + + // Gravity Mode: radial + this.setRadialAccel(0); + this.setRadialAccelVar(1); + + // Gravity mode: tangential + this.setTangentialAccel(0); + this.setTangentialAccelVar(1); + + // emitter position + var winSize = cc.director.getWinSize(); + this.setPosition(winSize.width / 2, winSize.height + 10); + this.setPosVar(cc.p(winSize.width / 2, 0)); + + // angle + this.setAngle(-90); + this.setAngleVar(5); + + // life of particles + this.setLife(45); + this.setLifeVar(15); + + // size, in pixels + this.setStartSize(10.0); + this.setStartSizeVar(5.0); + this.setEndSize(cc.ParticleSystem.START_SIZE_EQUAL_TO_END_SIZE); + + // emits per second + this.setEmissionRate(10); + + // color of particles + this.setStartColor(cc.color(255, 255, 255, 255)); + this.setStartColorVar(cc.color(0, 0, 0, 0)); + this.setEndColor(cc.color(255, 255, 255, 0)); + this.setEndColorVar(cc.color(0, 0, 0, 0)); + + // additive + this.setBlendAdditive(false); + return true; + } + return false; + } +}); + +/** + * Create a snow particle system + * @deprecated since v3.0 please use new cc.ParticleSnow() instead. + * @return {cc.ParticleSnow} + */ +cc.ParticleSnow.create = function () { + return new cc.ParticleSnow(); +}; + +//! @brief A rain particle system +/** + * A rain particle system + * @class + * @extends cc.ParticleSystem + * + * @example + * var emitter = new cc.ParticleRain(); + */ +cc.ParticleRain = cc.ParticleSystem.extend(/** @lends cc.ParticleRain# */{ + + /** + *

The cc.ParticleRain's constructor.
+ * This function will automatically be invoked when you create a node using new construction: "var node = new cc.ParticleRain()".
+ * Override it to extend its behavior, remember to call "this._super()" in the extended "ctor" function.

+ */ + ctor:function () { + cc.ParticleSystem.prototype.ctor.call(this, (cc._renderType === cc.game.RENDER_TYPE_WEBGL) ? 1000 : 300); + }, + + /** + * initialize a rain particle system with number Of Particles + * @param {Number} numberOfParticles + * @return {Boolean} + */ + initWithTotalParticles:function (numberOfParticles) { + if (cc.ParticleSystem.prototype.initWithTotalParticles.call(this, numberOfParticles)) { + // duration + this.setDuration(cc.ParticleSystem.DURATION_INFINITY); + + this.setEmitterMode(cc.ParticleSystem.MODE_GRAVITY); + + // Gravity Mode: gravity + this.setGravity(cc.p(10, -10)); + + // Gravity Mode: radial + this.setRadialAccel(0); + this.setRadialAccelVar(1); + + // Gravity Mode: tangential + this.setTangentialAccel(0); + this.setTangentialAccelVar(1); + + // Gravity Mode: speed of particles + this.setSpeed(130); + this.setSpeedVar(30); + + // angle + this.setAngle(-90); + this.setAngleVar(5); + + + // emitter position + var winSize = cc.director.getWinSize(); + this.setPosition(winSize.width / 2, winSize.height); + this.setPosVar(cc.p(winSize.width / 2, 0)); + + // life of particles + this.setLife(4.5); + this.setLifeVar(0); + + // size, in pixels + this.setStartSize(4.0); + this.setStartSizeVar(2.0); + this.setEndSize(cc.ParticleSystem.START_SIZE_EQUAL_TO_END_SIZE); + + // emits per second + this.setEmissionRate(20); + + // color of particles + this.setStartColor(cc.color(179, 204, 255, 255)); + this.setStartColorVar(cc.color(0, 0, 0, 0)); + this.setEndColor(cc.color(179, 204, 255, 128)); + this.setEndColorVar(cc.color(0, 0, 0, 0)); + + // additive + this.setBlendAdditive(false); + return true; + } + return false; + } +}); + +/** + * Create a rain particle system + * @deprecated since v3.0 please use cc.ParticleRain() instead. + * @return {cc.ParticleRain} + */ +cc.ParticleRain.create = function () { + return new cc.ParticleRain(); +}; diff --git a/frameworks/cocos2d-html5/cocos2d/particle/CCParticleSystem.js b/frameworks/cocos2d-html5/cocos2d/particle/CCParticleSystem.js new file mode 100644 index 0000000..7a18fa4 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/particle/CCParticleSystem.js @@ -0,0 +1,2318 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +// ideas taken from: +// . The ocean spray in your face [Jeff Lander] +// http://www.double.co.nz/dust/col0798.pdf +// . Building an Advanced Particle System [John van der Burg] +// http://www.gamasutra.com/features/20000623/vanderburg_01.htm +// . LOVE game engine +// http://love2d.org/ +// +// +// Radius mode support, from 71 squared +// http://particledesigner.71squared.com/ +// +// IMPORTANT: Particle Designer is supported by cocos2d, but +// 'Radius Mode' in Particle Designer uses a fixed emit rate of 30 hz. Since that can't be guarateed in cocos2d, +// cocos2d uses a another approach, but the results are almost identical. +// + + +// tCCPositionType +// possible types of particle positions + + +/** + * Structure that contains the values of each particle + * @Class + * @Construct + * @param {cc.Point} [pos=cc.p(0,0)] Position of particle + * @param {cc.Point} [startPos=cc.p(0,0)] + * @param {cc.Color} [color= cc.color(0, 0, 0, 255)] + * @param {cc.Color} [deltaColor=cc.color(0, 0, 0, 255)] + * @param {cc.Size} [size=0] + * @param {cc.Size} [deltaSize=0] + * @param {Number} [rotation=0] + * @param {Number} [deltaRotation=0] + * @param {Number} [timeToLive=0] + * @param {Number} [atlasIndex=0] + * @param {cc.Particle.ModeA} [modeA=] + * @param {cc.Particle.ModeA} [modeB=] + */ +cc.Particle = function (pos, startPos, color, deltaColor, size, deltaSize, rotation, deltaRotation, timeToLive, atlasIndex, modeA, modeB) { + this.pos = pos ? pos : cc.p(0, 0); + this.startPos = startPos ? startPos : cc.p(0, 0); + this.color = color ? color : {r: 0, g: 0, b: 0, a: 255}; + this.deltaColor = deltaColor ? deltaColor : {r: 0, g: 0, b: 0, a: 255}; + this.size = size || 0; + this.deltaSize = deltaSize || 0; + this.rotation = rotation || 0; + this.deltaRotation = deltaRotation || 0; + this.timeToLive = timeToLive || 0; + this.atlasIndex = atlasIndex || 0; + this.modeA = modeA ? modeA : new cc.Particle.ModeA(); + this.modeB = modeB ? modeB : new cc.Particle.ModeB(); + this.isChangeColor = false; + this.drawPos = cc.p(0, 0); +}; + +/** + * Mode A: gravity, direction, radial accel, tangential accel + * @Class + * @Construct + * @param {cc.Point} dir direction of particle + * @param {Number} radialAccel + * @param {Number} tangentialAccel + */ +cc.Particle.ModeA = function (dir, radialAccel, tangentialAccel) { + this.dir = dir ? dir : cc.p(0, 0); + this.radialAccel = radialAccel || 0; + this.tangentialAccel = tangentialAccel || 0; +}; + +/** + * Mode B: radius mode + * @Class + * @Construct + * @param {Number} angle + * @param {Number} degreesPerSecond + * @param {Number} radius + * @param {Number} deltaRadius + */ +cc.Particle.ModeB = function (angle, degreesPerSecond, radius, deltaRadius) { + this.angle = angle || 0; + this.degreesPerSecond = degreesPerSecond || 0; + this.radius = radius || 0; + this.deltaRadius = deltaRadius || 0; +}; + +/** + * Array of Point instances used to optimize particle updates + */ +cc.Particle.TemporaryPoints = [ + cc.p(), + cc.p(), + cc.p(), + cc.p() +]; + +/** + *

+ * Particle System base class.
+ * Attributes of a Particle System:
+ * - emission rate of the particles
+ * - Gravity Mode (Mode A):
+ * - gravity
+ * - direction
+ * - speed +- variance
+ * - tangential acceleration +- variance
+ * - radial acceleration +- variance
+ * - Radius Mode (Mode B):
+ * - startRadius +- variance
+ * - endRadius +- variance
+ * - rotate +- variance
+ * - Properties common to all modes:
+ * - life +- life variance
+ * - start spin +- variance
+ * - end spin +- variance
+ * - start size +- variance
+ * - end size +- variance
+ * - start color +- variance
+ * - end color +- variance
+ * - life +- variance
+ * - blending function
+ * - texture
+ *
+ * cocos2d also supports particles generated by Particle Designer (http://particledesigner.71squared.com/).
+ * 'Radius Mode' in Particle Designer uses a fixed emit rate of 30 hz. Since that can't be guarateed in cocos2d,
+ * cocos2d uses a another approach, but the results are almost identical.
+ * cocos2d supports all the variables used by Particle Designer plus a bit more:
+ * - spinning particles (supported when using ParticleSystem)
+ * - tangential acceleration (Gravity mode)
+ * - radial acceleration (Gravity mode)
+ * - radius direction (Radius mode) (Particle Designer supports outwards to inwards direction only)
+ * It is possible to customize any of the above mentioned properties in runtime. Example:
+ *

+ * @class + * @extends cc.Node + * + * @property {Boolean} opacityModifyRGB - Indicate whether the alpha value modify color. + * @property {cc.SpriteBatchNode} batchNode - Weak reference to the sprite batch node. + * @property {Boolean} active - <@readonly> Indicate whether the particle system is activated. + * @property {Number} shapeType - ShapeType of ParticleSystem : cc.ParticleSystem.BALL_SHAPE | cc.ParticleSystem.STAR_SHAPE. + * @property {Number} atlasIndex - Index of system in batch node array. + * @property {Number} particleCount - Current quantity of particles that are being simulated. + * @property {Number} duration - How many seconds the emitter wil run. -1 means 'forever' + * @property {cc.Point} sourcePos - Source position of the emitter. + * @property {cc.Point} posVar - Variation of source position. + * @property {Number} life - Life of each particle setter. + * @property {Number} lifeVar - Variation of life. + * @property {Number} angle - Angle of each particle setter. + * @property {Number} angleVar - Variation of angle of each particle setter. + * @property {Number} startSize - Start size in pixels of each particle. + * @property {Number} startSizeVar - Variation of start size in pixels. + * @property {Number} endSize - End size in pixels of each particle. + * @property {Number} endSizeVar - Variation of end size in pixels. + * @property {Number} startSpin - Start angle of each particle. + * @property {Number} startSpinVar - Variation of start angle. + * @property {Number} endSpin - End angle of each particle. + * @property {Number} endSpinVar - Variation of end angle. + * @property {cc.Point} gravity - Gravity of the emitter. + * @property {cc.Point} speed - Speed of the emitter. + * @property {cc.Point} speedVar - Variation of the speed. + * @property {Number} tangentialAccel - Tangential acceleration of each particle. Only available in 'Gravity' mode. + * @property {Number} tangentialAccelVar - Variation of the tangential acceleration. + * @property {Number} tangentialAccel - Radial acceleration of each particle. Only available in 'Gravity' mode. + * @property {Number} tangentialAccelVar - Variation of the radial acceleration. + * @property {Boolean} rotationIsDir - Indicate whether the rotation of each particle equals to its direction. Only available in 'Gravity' mode. + * @property {Number} startRadius - Starting radius of the particles. Only available in 'Radius' mode. + * @property {Number} startRadiusVar - Variation of the starting radius. + * @property {Number} endRadius - Ending radius of the particles. Only available in 'Radius' mode. + * @property {Number} endRadiusVar - Variation of the ending radius. + * @property {Number} rotatePerS - Number of degress to rotate a particle around the source pos per second. Only available in 'Radius' mode. + * @property {Number} rotatePerSVar - Variation of the degress to rotate a particle around the source pos per second. + * @property {cc.Color} startColor - Start color of each particle. + * @property {cc.Color} startColorVar - Variation of the start color. + * @property {cc.Color} endColor - Ending color of each particle. + * @property {cc.Color} endColorVar - Variation of the end color. + * @property {Number} emissionRate - Emission rate of the particles. + * @property {Number} emitterMode - Emitter modes: CCParticleSystem.MODE_GRAVITY: uses gravity, speed, radial and tangential acceleration; CCParticleSystem.MODE_RADIUS: uses radius movement + rotation. + * @property {Number} positionType - Particles movement type: cc.ParticleSystem.TYPE_FREE | cc.ParticleSystem.TYPE_GROUPED. + * @property {Number} totalParticles - Maximum particles of the system. + * @property {Boolean} autoRemoveOnFinish - Indicate whether the node will be auto-removed when it has no particles left. + * @property {cc.Texture2D|HTMLImageElement|HTMLCanvasElement} texture - Texture of Particle System. + * + * @example + * emitter.radialAccel = 15; + * emitter.startSpin = 0; + */ +cc.ParticleSystem = cc.Node.extend(/** @lends cc.ParticleSystem# */{ + _className: "ParticleSystem", + //***********variables************* + _plistFile: "", + //! time elapsed since the start of the system (in seconds) + _elapsed: 0, + _dontTint: false, + + // Different modes + //! Mode A:Gravity + Tangential Accel + Radial Accel + modeA: null, + //! Mode B: circular movement (gravity, radial accel and tangential accel don't are not used in this mode) + modeB: null, + + //private POINTZERO for ParticleSystem + _pointZeroForParticle: cc.p(0, 0), + + //! Array of particles + _particles: null, + + // color modulate + // BOOL colorModulate; + + //! How many particles can be emitted per second + _emitCounter: 0, + //! particle idx + _particleIdx: 0, + + _batchNode: null, + atlasIndex: 0, + + //true if scaled or rotated + _transformSystemDirty: false, + _allocatedParticles: 0, + + _isActive: false, + particleCount: 0, + duration: 0, + _sourcePosition: null, + _posVar: null, + life: 0, + lifeVar: 0, + angle: 0, + angleVar: 0, + startSize: 0, + startSizeVar: 0, + endSize: 0, + endSizeVar: 0, + _startColor: null, + _startColorVar: null, + _endColor: null, + _endColorVar: null, + startSpin: 0, + startSpinVar: 0, + endSpin: 0, + endSpinVar: 0, + emissionRate: 0, + _totalParticles: 0, + _texture: null, + _blendFunc: null, + _opacityModifyRGB: false, + positionType: null, + autoRemoveOnFinish: false, + emitterMode: 0, + + _textureLoaded: null, + + /** + *

return the string found by key in dict.
+ * This plist files can be create manually or with Particle Designer:
+ * http://particledesigner.71squared.com/
+ *

+ * Constructor of cc.ParticleSystem + * @param {String|Number} plistFile + */ + ctor: function (plistFile) { + cc.Node.prototype.ctor.call(this); + this.emitterMode = cc.ParticleSystem.MODE_GRAVITY; + this.modeA = new cc.ParticleSystem.ModeA(); + this.modeB = new cc.ParticleSystem.ModeB(); + this._blendFunc = {src: cc.BLEND_SRC, dst: cc.BLEND_DST}; + + this._particles = []; + this._sourcePosition = cc.p(0, 0); + this._posVar = cc.p(0, 0); + + this._startColor = cc.color(255, 255, 255, 255); + this._startColorVar = cc.color(255, 255, 255, 255); + this._endColor = cc.color(255, 255, 255, 255); + this._endColorVar = cc.color(255, 255, 255, 255); + + this._plistFile = ""; + this._elapsed = 0; + this._dontTint = false; + this._pointZeroForParticle = cc.p(0, 0); + this._emitCounter = 0; + this._particleIdx = 0; + this._batchNode = null; + this.atlasIndex = 0; + + this._transformSystemDirty = false; + this._allocatedParticles = 0; + this._isActive = false; + this.particleCount = 0; + this.duration = 0; + this.life = 0; + this.lifeVar = 0; + this.angle = 0; + this.angleVar = 0; + this.startSize = 0; + this.startSizeVar = 0; + this.endSize = 0; + this.endSizeVar = 0; + + this.startSpin = 0; + this.startSpinVar = 0; + this.endSpin = 0; + this.endSpinVar = 0; + this.emissionRate = 0; + this._totalParticles = 0; + this._texture = null; + this._opacityModifyRGB = false; + this.positionType = cc.ParticleSystem.TYPE_FREE; + this.autoRemoveOnFinish = false; + + this._textureLoaded = true; + + if (!plistFile || cc.isNumber(plistFile)) { + var ton = plistFile || 100; + this.setDrawMode(cc.ParticleSystem.TEXTURE_MODE); + this.initWithTotalParticles(ton); + } else if (cc.isString(plistFile)) { + this.initWithFile(plistFile); + } else if (cc.isObject(plistFile)) { + this.initWithDictionary(plistFile, ""); + } + }, + + _createRenderCmd: function () { + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return new cc.ParticleSystem.CanvasRenderCmd(this); + else + return new cc.ParticleSystem.WebGLRenderCmd(this); + }, + + /** + * This is a hack function for performance, it's only available on Canvas mode.
+ * It's very expensive to change color on Canvas mode, so if set it to true, particle system will ignore the changing color operation. + * @param {boolean} ignore + */ + ignoreColor: function (ignore) { + this._dontTint = ignore; + }, + + /** + *

initializes the texture with a rectangle measured Points
+ * pointRect should be in Texture coordinates, not pixel coordinates + *

+ * @param {cc.Rect} pointRect + */ + initTexCoordsWithRect: function (pointRect) { + this._renderCmd.initTexCoordsWithRect(pointRect); + }, + + /** + * return weak reference to the cc.SpriteBatchNode that renders the cc.Sprite + * @return {cc.ParticleBatchNode} + */ + getBatchNode: function () { + return this._batchNode; + }, + + /** + * set weak reference to the cc.SpriteBatchNode that renders the cc.Sprite + * @param {cc.ParticleBatchNode} batchNode + */ + setBatchNode: function (batchNode) { + this._renderCmd.setBatchNode(batchNode); + }, + + /** + * return index of system in batch node array + * @return {Number} + */ + getAtlasIndex: function () { + return this.atlasIndex; + }, + + /** + * set index of system in batch node array + * @param {Number} atlasIndex + */ + setAtlasIndex: function (atlasIndex) { + this.atlasIndex = atlasIndex; + }, + + /** + * Return DrawMode of ParticleSystem (Canvas Mode only) + * @return {Number} + */ + getDrawMode: function () { + return this._renderCmd.getDrawMode(); + }, + + /** + * DrawMode of ParticleSystem setter (Canvas Mode only) + * @param {Number} drawMode + */ + setDrawMode: function (drawMode) { + this._renderCmd.setDrawMode(drawMode); + }, + + /** + * Return ShapeType of ParticleSystem (Canvas Mode only) + * @return {Number} + */ + getShapeType: function () { + return this._renderCmd.getShapeType(); + }, + + /** + * ShapeType of ParticleSystem setter (Canvas Mode only) + * @param {Number} shapeType + */ + setShapeType: function (shapeType) { + this._renderCmd.setShapeType(shapeType); + }, + + /** + * Return ParticleSystem is active + * @return {Boolean} + */ + isActive: function () { + return this._isActive; + }, + + /** + * Quantity of particles that are being simulated at the moment + * @return {Number} + */ + getParticleCount: function () { + return this.particleCount; + }, + + /** + * Quantity of particles setter + * @param {Number} particleCount + */ + setParticleCount: function (particleCount) { + this.particleCount = particleCount; + }, + + /** + * How many seconds the emitter wil run. -1 means 'forever' + * @return {Number} + */ + getDuration: function () { + return this.duration; + }, + + /** + * set run seconds of the emitter + * @param {Number} duration + */ + setDuration: function (duration) { + this.duration = duration; + }, + + /** + * Return sourcePosition of the emitter + * @return {cc.Point | Object} + */ + getSourcePosition: function () { + return {x: this._sourcePosition.x, y: this._sourcePosition.y}; + }, + + /** + * sourcePosition of the emitter setter + * @param sourcePosition + */ + setSourcePosition: function (sourcePosition) { + this._sourcePosition.x = sourcePosition.x; + this._sourcePosition.y = sourcePosition.y; + }, + + /** + * Return Position variance of the emitter + * @return {cc.Point | Object} + */ + getPosVar: function () { + return {x: this._posVar.x, y: this._posVar.y}; + }, + + /** + * Position variance of the emitter setter + * @param {cc.Point} posVar + */ + setPosVar: function (posVar) { + this._posVar.x = posVar.x; + this._posVar.y = posVar.y; + }, + + /** + * Return life of each particle + * @return {Number} + */ + getLife: function () { + return this.life; + }, + + /** + * life of each particle setter + * @param {Number} life + */ + setLife: function (life) { + this.life = life; + }, + + /** + * Return life variance of each particle + * @return {Number} + */ + getLifeVar: function () { + return this.lifeVar; + }, + + /** + * life variance of each particle setter + * @param {Number} lifeVar + */ + setLifeVar: function (lifeVar) { + this.lifeVar = lifeVar; + }, + + /** + * Return angle of each particle + * @return {Number} + */ + getAngle: function () { + return this.angle; + }, + + /** + * angle of each particle setter + * @param {Number} angle + */ + setAngle: function (angle) { + this.angle = angle; + }, + + /** + * Return angle variance of each particle + * @return {Number} + */ + getAngleVar: function () { + return this.angleVar; + }, + + /** + * angle variance of each particle setter + * @param angleVar + */ + setAngleVar: function (angleVar) { + this.angleVar = angleVar; + }, + + // mode A + /** + * Return Gravity of emitter + * @return {cc.Point} + */ + getGravity: function () { + if (this.emitterMode !== cc.ParticleSystem.MODE_GRAVITY) + cc.log("cc.ParticleBatchNode.getGravity() : Particle Mode should be Gravity"); + var locGravity = this.modeA.gravity; + return cc.p(locGravity.x, locGravity.y); + }, + + /** + * Gravity of emitter setter + * @param {cc.Point} gravity + */ + setGravity: function (gravity) { + if (this.emitterMode !== cc.ParticleSystem.MODE_GRAVITY) + cc.log("cc.ParticleBatchNode.setGravity() : Particle Mode should be Gravity"); + this.modeA.gravity = gravity; + }, + + /** + * Return Speed of each particle + * @return {Number} + */ + getSpeed: function () { + if (this.emitterMode !== cc.ParticleSystem.MODE_GRAVITY) + cc.log("cc.ParticleBatchNode.getSpeed() : Particle Mode should be Gravity"); + return this.modeA.speed; + }, + + /** + * Speed of each particle setter + * @param {Number} speed + */ + setSpeed: function (speed) { + if (this.emitterMode !== cc.ParticleSystem.MODE_GRAVITY) + cc.log("cc.ParticleBatchNode.setSpeed() : Particle Mode should be Gravity"); + this.modeA.speed = speed; + }, + + /** + * return speed variance of each particle. Only available in 'Gravity' mode. + * @return {Number} + */ + getSpeedVar: function () { + if (this.emitterMode !== cc.ParticleSystem.MODE_GRAVITY) + cc.log("cc.ParticleBatchNode.getSpeedVar() : Particle Mode should be Gravity"); + return this.modeA.speedVar; + }, + + /** + * speed variance of each particle setter. Only available in 'Gravity' mode. + * @param {Number} speedVar + */ + setSpeedVar: function (speedVar) { + if (this.emitterMode !== cc.ParticleSystem.MODE_GRAVITY) + cc.log("cc.ParticleBatchNode.setSpeedVar() : Particle Mode should be Gravity"); + this.modeA.speedVar = speedVar; + }, + + /** + * Return tangential acceleration of each particle. Only available in 'Gravity' mode. + * @return {Number} + */ + getTangentialAccel: function () { + if (this.emitterMode !== cc.ParticleSystem.MODE_GRAVITY) + cc.log("cc.ParticleBatchNode.getTangentialAccel() : Particle Mode should be Gravity"); + return this.modeA.tangentialAccel; + }, + + /** + * Tangential acceleration of each particle setter. Only available in 'Gravity' mode. + * @param {Number} tangentialAccel + */ + setTangentialAccel: function (tangentialAccel) { + if (this.emitterMode !== cc.ParticleSystem.MODE_GRAVITY) + cc.log("cc.ParticleBatchNode.setTangentialAccel() : Particle Mode should be Gravity"); + this.modeA.tangentialAccel = tangentialAccel; + }, + + /** + * Return tangential acceleration variance of each particle. Only available in 'Gravity' mode. + * @return {Number} + */ + getTangentialAccelVar: function () { + if (this.emitterMode !== cc.ParticleSystem.MODE_GRAVITY) + cc.log("cc.ParticleBatchNode.getTangentialAccelVar() : Particle Mode should be Gravity"); + return this.modeA.tangentialAccelVar; + }, + + /** + * tangential acceleration variance of each particle setter. Only available in 'Gravity' mode. + * @param {Number} tangentialAccelVar + */ + setTangentialAccelVar: function (tangentialAccelVar) { + if (this.emitterMode !== cc.ParticleSystem.MODE_GRAVITY) + cc.log("cc.ParticleBatchNode.setTangentialAccelVar() : Particle Mode should be Gravity"); + this.modeA.tangentialAccelVar = tangentialAccelVar; + }, + + /** + * Return radial acceleration of each particle. Only available in 'Gravity' mode. + * @return {Number} + */ + getRadialAccel: function () { + if (this.emitterMode !== cc.ParticleSystem.MODE_GRAVITY) + cc.log("cc.ParticleBatchNode.getRadialAccel() : Particle Mode should be Gravity"); + return this.modeA.radialAccel; + }, + + /** + * radial acceleration of each particle setter. Only available in 'Gravity' mode. + * @param {Number} radialAccel + */ + setRadialAccel: function (radialAccel) { + if (this.emitterMode !== cc.ParticleSystem.MODE_GRAVITY) + cc.log("cc.ParticleBatchNode.setRadialAccel() : Particle Mode should be Gravity"); + this.modeA.radialAccel = radialAccel; + }, + + /** + * Return radial acceleration variance of each particle. Only available in 'Gravity' mode. + * @return {Number} + */ + getRadialAccelVar: function () { + if (this.emitterMode !== cc.ParticleSystem.MODE_GRAVITY) + cc.log("cc.ParticleBatchNode.getRadialAccelVar() : Particle Mode should be Gravity"); + return this.modeA.radialAccelVar; + }, + + /** + * radial acceleration variance of each particle setter. Only available in 'Gravity' mode. + * @param {Number} radialAccelVar + */ + setRadialAccelVar: function (radialAccelVar) { + if (this.emitterMode !== cc.ParticleSystem.MODE_GRAVITY) + cc.log("cc.ParticleBatchNode.setRadialAccelVar() : Particle Mode should be Gravity"); + this.modeA.radialAccelVar = radialAccelVar; + }, + + /** + * get the rotation of each particle to its direction Only available in 'Gravity' mode. + * @returns {boolean} + */ + getRotationIsDir: function () { + if (this.emitterMode !== cc.ParticleSystem.MODE_GRAVITY) + cc.log("cc.ParticleBatchNode.getRotationIsDir() : Particle Mode should be Gravity"); + return this.modeA.rotationIsDir; + }, + + /** + * set the rotation of each particle to its direction Only available in 'Gravity' mode. + * @param {boolean} t + */ + setRotationIsDir: function (t) { + if (this.emitterMode !== cc.ParticleSystem.MODE_GRAVITY) + cc.log("cc.ParticleBatchNode.setRotationIsDir() : Particle Mode should be Gravity"); + this.modeA.rotationIsDir = t; + }, + + // mode B + /** + * Return starting radius of the particles. Only available in 'Radius' mode. + * @return {Number} + */ + getStartRadius: function () { + if (this.emitterMode !== cc.ParticleSystem.MODE_RADIUS) + cc.log("cc.ParticleBatchNode.getStartRadius() : Particle Mode should be Radius"); + return this.modeB.startRadius; + }, + + /** + * starting radius of the particles setter. Only available in 'Radius' mode. + * @param {Number} startRadius + */ + setStartRadius: function (startRadius) { + if (this.emitterMode !== cc.ParticleSystem.MODE_RADIUS) + cc.log("cc.ParticleBatchNode.setStartRadius() : Particle Mode should be Radius"); + this.modeB.startRadius = startRadius; + }, + + /** + * Return starting radius variance of the particles. Only available in 'Radius' mode. + * @return {Number} + */ + getStartRadiusVar: function () { + if (this.emitterMode !== cc.ParticleSystem.MODE_RADIUS) + cc.log("cc.ParticleBatchNode.getStartRadiusVar() : Particle Mode should be Radius"); + return this.modeB.startRadiusVar; + }, + + /** + * starting radius variance of the particles setter. Only available in 'Radius' mode. + * @param {Number} startRadiusVar + */ + setStartRadiusVar: function (startRadiusVar) { + if (this.emitterMode !== cc.ParticleSystem.MODE_RADIUS) + cc.log("cc.ParticleBatchNode.setStartRadiusVar() : Particle Mode should be Radius"); + this.modeB.startRadiusVar = startRadiusVar; + }, + + /** + * Return ending radius of the particles. Only available in 'Radius' mode. + * @return {Number} + */ + getEndRadius: function () { + if (this.emitterMode !== cc.ParticleSystem.MODE_RADIUS) + cc.log("cc.ParticleBatchNode.getEndRadius() : Particle Mode should be Radius"); + return this.modeB.endRadius; + }, + + /** + * ending radius of the particles setter. Only available in 'Radius' mode. + * @param {Number} endRadius + */ + setEndRadius: function (endRadius) { + if (this.emitterMode !== cc.ParticleSystem.MODE_RADIUS) + cc.log("cc.ParticleBatchNode.setEndRadius() : Particle Mode should be Radius"); + this.modeB.endRadius = endRadius; + }, + + /** + * Return ending radius variance of the particles. Only available in 'Radius' mode. + * @return {Number} + */ + getEndRadiusVar: function () { + if (this.emitterMode !== cc.ParticleSystem.MODE_RADIUS) + cc.log("cc.ParticleBatchNode.getEndRadiusVar() : Particle Mode should be Radius"); + return this.modeB.endRadiusVar; + }, + + /** + * ending radius variance of the particles setter. Only available in 'Radius' mode. + * @param endRadiusVar + */ + setEndRadiusVar: function (endRadiusVar) { + if (this.emitterMode !== cc.ParticleSystem.MODE_RADIUS) + cc.log("cc.ParticleBatchNode.setEndRadiusVar() : Particle Mode should be Radius"); + this.modeB.endRadiusVar = endRadiusVar; + }, + + /** + * get Number of degress to rotate a particle around the source pos per second. Only available in 'Radius' mode. + * @return {Number} + */ + getRotatePerSecond: function () { + if (this.emitterMode !== cc.ParticleSystem.MODE_RADIUS) + cc.log("cc.ParticleBatchNode.getRotatePerSecond() : Particle Mode should be Radius"); + return this.modeB.rotatePerSecond; + }, + + /** + * set Number of degress to rotate a particle around the source pos per second. Only available in 'Radius' mode. + * @param {Number} degrees + */ + setRotatePerSecond: function (degrees) { + if (this.emitterMode !== cc.ParticleSystem.MODE_RADIUS) + cc.log("cc.ParticleBatchNode.setRotatePerSecond() : Particle Mode should be Radius"); + this.modeB.rotatePerSecond = degrees; + }, + + /** + * Return Variance in degrees for rotatePerSecond. Only available in 'Radius' mode. + * @return {Number} + */ + getRotatePerSecondVar: function () { + if (this.emitterMode !== cc.ParticleSystem.MODE_RADIUS) + cc.log("cc.ParticleBatchNode.getRotatePerSecondVar() : Particle Mode should be Radius"); + return this.modeB.rotatePerSecondVar; + }, + + /** + * Variance in degrees for rotatePerSecond setter. Only available in 'Radius' mode. + * @param degrees + */ + setRotatePerSecondVar: function (degrees) { + if (this.emitterMode !== cc.ParticleSystem.MODE_RADIUS) + cc.log("cc.ParticleBatchNode.setRotatePerSecondVar() : Particle Mode should be Radius"); + this.modeB.rotatePerSecondVar = degrees; + }, + ////////////////////////////////////////////////////////////////////////// + + //don't use a transform matrix, this is faster + setScale: function (scale, scaleY) { + this._transformSystemDirty = true; + cc.Node.prototype.setScale.call(this, scale, scaleY); + }, + + setRotation: function (newRotation) { + this._transformSystemDirty = true; + cc.Node.prototype.setRotation.call(this, newRotation); + }, + + setScaleX: function (newScaleX) { + this._transformSystemDirty = true; + cc.Node.prototype.setScaleX.call(this, newScaleX); + }, + + setScaleY: function (newScaleY) { + this._transformSystemDirty = true; + cc.Node.prototype.setScaleY.call(this, newScaleY); + }, + + /** + * get start size in pixels of each particle + * @return {Number} + */ + getStartSize: function () { + return this.startSize; + }, + + /** + * set start size in pixels of each particle + * @param {Number} startSize + */ + setStartSize: function (startSize) { + this.startSize = startSize; + }, + + /** + * get size variance in pixels of each particle + * @return {Number} + */ + getStartSizeVar: function () { + return this.startSizeVar; + }, + + /** + * set size variance in pixels of each particle + * @param {Number} startSizeVar + */ + setStartSizeVar: function (startSizeVar) { + this.startSizeVar = startSizeVar; + }, + + /** + * get end size in pixels of each particle + * @return {Number} + */ + getEndSize: function () { + return this.endSize; + }, + + /** + * set end size in pixels of each particle + * @param endSize + */ + setEndSize: function (endSize) { + this.endSize = endSize; + }, + + /** + * get end size variance in pixels of each particle + * @return {Number} + */ + getEndSizeVar: function () { + return this.endSizeVar; + }, + + /** + * set end size variance in pixels of each particle + * @param {Number} endSizeVar + */ + setEndSizeVar: function (endSizeVar) { + this.endSizeVar = endSizeVar; + }, + + /** + * set start color of each particle + * @return {cc.Color} + */ + getStartColor: function () { + return cc.color(this._startColor.r, this._startColor.g, this._startColor.b, this._startColor.a); + }, + + /** + * get start color of each particle + * @param {cc.Color} startColor + */ + setStartColor: function (startColor) { + this._startColor.r = startColor.r; + this._startColor.g = startColor.g; + this._startColor.b = startColor.b; + this._startColor.a = startColor.a; + }, + + /** + * get start color variance of each particle + * @return {cc.Color} + */ + getStartColorVar: function () { + return cc.color(this._startColorVar.r, this._startColorVar.g, this._startColorVar.b, this._startColorVar.a); + }, + + /** + * set start color variance of each particle + * @param {cc.Color} startColorVar + */ + setStartColorVar: function (startColorVar) { + this._startColorVar.r = startColorVar.r; + this._startColorVar.g = startColorVar.g; + this._startColorVar.b = startColorVar.b; + this._startColorVar.a = startColorVar.a; + }, + + /** + * get end color and end color variation of each particle + * @return {cc.Color} + */ + getEndColor: function () { + return cc.color(this._endColor.r, this._endColor.g, this._endColor.b, this._endColor.a); + }, + + /** + * set end color and end color variation of each particle + * @param {cc.Color} endColor + */ + setEndColor: function (endColor) { + this._endColor.r = endColor.r; + this._endColor.g = endColor.g; + this._endColor.b = endColor.b; + this._endColor.a = endColor.a; + }, + + /** + * get end color variance of each particle + * @return {cc.Color} + */ + getEndColorVar: function () { + return cc.color(this._endColorVar.r, this._endColorVar.g, this._endColorVar.b, this._endColorVar.a); + }, + + /** + * set end color variance of each particle + * @param {cc.Color} endColorVar + */ + setEndColorVar: function (endColorVar) { + this._endColorVar.r = endColorVar.r; + this._endColorVar.g = endColorVar.g; + this._endColorVar.b = endColorVar.b; + this._endColorVar.a = endColorVar.a; + }, + + /** + * get initial angle of each particle + * @return {Number} + */ + getStartSpin: function () { + return this.startSpin; + }, + + /** + * set initial angle of each particle + * @param {Number} startSpin + */ + setStartSpin: function (startSpin) { + this.startSpin = startSpin; + }, + + /** + * get initial angle variance of each particle + * @return {Number} + */ + getStartSpinVar: function () { + return this.startSpinVar; + }, + + /** + * set initial angle variance of each particle + * @param {Number} startSpinVar + */ + setStartSpinVar: function (startSpinVar) { + this.startSpinVar = startSpinVar; + }, + + /** + * get end angle of each particle + * @return {Number} + */ + getEndSpin: function () { + return this.endSpin; + }, + + /** + * set end angle of each particle + * @param {Number} endSpin + */ + setEndSpin: function (endSpin) { + this.endSpin = endSpin; + }, + + /** + * get end angle variance of each particle + * @return {Number} + */ + getEndSpinVar: function () { + return this.endSpinVar; + }, + + /** + * set end angle variance of each particle + * @param {Number} endSpinVar + */ + setEndSpinVar: function (endSpinVar) { + this.endSpinVar = endSpinVar; + }, + + /** + * get emission rate of the particles + * @return {Number} + */ + getEmissionRate: function () { + return this.emissionRate; + }, + + /** + * set emission rate of the particles + * @param {Number} emissionRate + */ + setEmissionRate: function (emissionRate) { + this.emissionRate = emissionRate; + }, + + /** + * get maximum particles of the system + * @return {Number} + */ + getTotalParticles: function () { + return this._totalParticles; + }, + + /** + * set maximum particles of the system + * @param {Number} tp totalParticles + */ + setTotalParticles: function (tp) { + this._renderCmd.setTotalParticles(tp); + }, + + /** + * get Texture of Particle System + * @return {cc.Texture2D} + */ + getTexture: function () { + return this._texture; + }, + + /** + * set Texture of Particle System + * @param {cc.Texture2D } texture + */ + setTexture: function (texture) { + if (!texture) + return; + + if (texture.isLoaded()) { + this.setTextureWithRect(texture, cc.rect(0, 0, texture.width, texture.height)); + } else { + this._textureLoaded = false; + texture.addEventListener("load", function (sender) { + this._textureLoaded = true; + this.setTextureWithRect(sender, cc.rect(0, 0, sender.width, sender.height)); + }, this); + } + }, + + /** conforms to CocosNodeTexture protocol */ + /** + * get BlendFunc of Particle System + * @return {cc.BlendFunc} + */ + getBlendFunc: function () { + return this._blendFunc; + }, + + /** + * set BlendFunc of Particle System + * @param {Number} src + * @param {Number} dst + */ + setBlendFunc: function (src, dst) { + if (dst === undefined) { + if (this._blendFunc !== src) { + this._blendFunc = src; + this._updateBlendFunc(); + } + } else { + if (this._blendFunc.src !== src || this._blendFunc.dst !== dst) { + this._blendFunc = {src: src, dst: dst}; + this._updateBlendFunc(); + } + } + }, + + /** + * does the alpha value modify color getter + * @return {Boolean} + */ + isOpacityModifyRGB: function () { + return this._opacityModifyRGB; + }, + + /** + * does the alpha value modify color setter + * @param newValue + */ + setOpacityModifyRGB: function (newValue) { + this._opacityModifyRGB = newValue; + }, + + /** + *

whether or not the particles are using blend additive.
+ * If enabled, the following blending function will be used.
+ *

+ * @return {Boolean} + * @example + * source blend function = GL_SRC_ALPHA; + * dest blend function = GL_ONE; + */ + isBlendAdditive: function () { + return (( this._blendFunc.src === cc.SRC_ALPHA && this._blendFunc.dst === cc.ONE) || (this._blendFunc.src === cc.ONE && this._blendFunc.dst === cc.ONE)); + }, + + /** + *

whether or not the particles are using blend additive.
+ * If enabled, the following blending function will be used.
+ *

+ * @param {Boolean} isBlendAdditive + */ + setBlendAdditive: function (isBlendAdditive) { + var locBlendFunc = this._blendFunc; + if (isBlendAdditive) { + locBlendFunc.src = cc.SRC_ALPHA; + locBlendFunc.dst = cc.ONE; + } else { + this._renderCmd._setBlendAdditive(); + } + }, + + /** + * get particles movement type: Free or Grouped + * @return {Number} + */ + getPositionType: function () { + return this.positionType; + }, + + /** + * set particles movement type: Free or Grouped + * @param {Number} positionType + */ + setPositionType: function (positionType) { + this.positionType = positionType; + }, + + /** + *

return whether or not the node will be auto-removed when it has no particles left.
+ * By default it is false.
+ *

+ * @return {Boolean} + */ + isAutoRemoveOnFinish: function () { + return this.autoRemoveOnFinish; + }, + + /** + *

set whether or not the node will be auto-removed when it has no particles left.
+ * By default it is false.
+ *

+ * @param {Boolean} isAutoRemoveOnFinish + */ + setAutoRemoveOnFinish: function (isAutoRemoveOnFinish) { + this.autoRemoveOnFinish = isAutoRemoveOnFinish; + }, + + /** + * return kind of emitter modes + * @return {Number} + */ + getEmitterMode: function () { + return this.emitterMode; + }, + + /** + *

Switch between different kind of emitter modes:
+ * - CCParticleSystem.MODE_GRAVITY: uses gravity, speed, radial and tangential acceleration
+ * - CCParticleSystem.MODE_RADIUS: uses radius movement + rotation
+ *

+ * @param {Number} emitterMode + */ + setEmitterMode: function (emitterMode) { + this.emitterMode = emitterMode; + }, + + /** + * initializes a cc.ParticleSystem + */ + init: function () { + return this.initWithTotalParticles(150); + }, + + /** + *

+ * initializes a CCParticleSystem from a plist file.
+ * This plist files can be creted manually or with Particle Designer:
+ * http://particledesigner.71squared.com/ + *

+ * @param {String} plistFile + * @return {boolean} + */ + initWithFile: function (plistFile) { + this._plistFile = plistFile; + var dict = cc.loader.getRes(plistFile); + if (!dict) { + cc.log("cc.ParticleSystem.initWithFile(): Particles: file not found"); + return false; + } + + // XXX compute path from a path, should define a function somewhere to do it + return this.initWithDictionary(dict, ""); + }, + + /** + * return bounding box of particle system in world space + * @return {cc.Rect} + */ + getBoundingBoxToWorld: function () { + return cc.rect(0, 0, cc._canvas.width, cc._canvas.height); + }, + + /** + * initializes a particle system from a NSDictionary and the path from where to load the png + * @param {object} dictionary + * @param {String} dirname + * @return {Boolean} + */ + initWithDictionary: function (dictionary, dirname) { + var ret = false; + var buffer = null; + var image = null; + var locValueForKey = this._valueForKey; + + var maxParticles = parseInt(locValueForKey("maxParticles", dictionary)); + // self, not super + if (this.initWithTotalParticles(maxParticles)) { + // angle + this.angle = parseFloat(locValueForKey("angle", dictionary)); + this.angleVar = parseFloat(locValueForKey("angleVariance", dictionary)); + + // duration + this.duration = parseFloat(locValueForKey("duration", dictionary)); + + // blend function + this._blendFunc.src = parseInt(locValueForKey("blendFuncSource", dictionary)); + this._blendFunc.dst = parseInt(locValueForKey("blendFuncDestination", dictionary)); + + // color + var locStartColor = this._startColor; + locStartColor.r = parseFloat(locValueForKey("startColorRed", dictionary)) * 255; + locStartColor.g = parseFloat(locValueForKey("startColorGreen", dictionary)) * 255; + locStartColor.b = parseFloat(locValueForKey("startColorBlue", dictionary)) * 255; + locStartColor.a = parseFloat(locValueForKey("startColorAlpha", dictionary)) * 255; + + var locStartColorVar = this._startColorVar; + locStartColorVar.r = parseFloat(locValueForKey("startColorVarianceRed", dictionary)) * 255; + locStartColorVar.g = parseFloat(locValueForKey("startColorVarianceGreen", dictionary)) * 255; + locStartColorVar.b = parseFloat(locValueForKey("startColorVarianceBlue", dictionary)) * 255; + locStartColorVar.a = parseFloat(locValueForKey("startColorVarianceAlpha", dictionary)) * 255; + + var locEndColor = this._endColor; + locEndColor.r = parseFloat(locValueForKey("finishColorRed", dictionary)) * 255; + locEndColor.g = parseFloat(locValueForKey("finishColorGreen", dictionary)) * 255; + locEndColor.b = parseFloat(locValueForKey("finishColorBlue", dictionary)) * 255; + locEndColor.a = parseFloat(locValueForKey("finishColorAlpha", dictionary)) * 255; + + var locEndColorVar = this._endColorVar; + locEndColorVar.r = parseFloat(locValueForKey("finishColorVarianceRed", dictionary)) * 255; + locEndColorVar.g = parseFloat(locValueForKey("finishColorVarianceGreen", dictionary)) * 255; + locEndColorVar.b = parseFloat(locValueForKey("finishColorVarianceBlue", dictionary)) * 255; + locEndColorVar.a = parseFloat(locValueForKey("finishColorVarianceAlpha", dictionary)) * 255; + + // particle size + this.startSize = parseFloat(locValueForKey("startParticleSize", dictionary)); + this.startSizeVar = parseFloat(locValueForKey("startParticleSizeVariance", dictionary)); + this.endSize = parseFloat(locValueForKey("finishParticleSize", dictionary)); + this.endSizeVar = parseFloat(locValueForKey("finishParticleSizeVariance", dictionary)); + + // position + this.setPosition(parseFloat(locValueForKey("sourcePositionx", dictionary)), + parseFloat(locValueForKey("sourcePositiony", dictionary))); + this._posVar.x = parseFloat(locValueForKey("sourcePositionVariancex", dictionary)); + this._posVar.y = parseFloat(locValueForKey("sourcePositionVariancey", dictionary)); + + // Spinning + this.startSpin = parseFloat(locValueForKey("rotationStart", dictionary)); + this.startSpinVar = parseFloat(locValueForKey("rotationStartVariance", dictionary)); + this.endSpin = parseFloat(locValueForKey("rotationEnd", dictionary)); + this.endSpinVar = parseFloat(locValueForKey("rotationEndVariance", dictionary)); + + this.emitterMode = parseInt(locValueForKey("emitterType", dictionary)); + + // Mode A: Gravity + tangential accel + radial accel + if (this.emitterMode === cc.ParticleSystem.MODE_GRAVITY) { + var locModeA = this.modeA; + // gravity + locModeA.gravity.x = parseFloat(locValueForKey("gravityx", dictionary)); + locModeA.gravity.y = parseFloat(locValueForKey("gravityy", dictionary)); + + // speed + locModeA.speed = parseFloat(locValueForKey("speed", dictionary)); + locModeA.speedVar = parseFloat(locValueForKey("speedVariance", dictionary)); + + // radial acceleration + var pszTmp = locValueForKey("radialAcceleration", dictionary); + locModeA.radialAccel = (pszTmp) ? parseFloat(pszTmp) : 0; + + pszTmp = locValueForKey("radialAccelVariance", dictionary); + locModeA.radialAccelVar = (pszTmp) ? parseFloat(pszTmp) : 0; + + // tangential acceleration + pszTmp = locValueForKey("tangentialAcceleration", dictionary); + locModeA.tangentialAccel = (pszTmp) ? parseFloat(pszTmp) : 0; + + pszTmp = locValueForKey("tangentialAccelVariance", dictionary); + locModeA.tangentialAccelVar = (pszTmp) ? parseFloat(pszTmp) : 0; + + // rotation is dir + var locRotationIsDir = locValueForKey("rotationIsDir", dictionary); + if (locRotationIsDir !== null) { + locRotationIsDir = locRotationIsDir.toString().toLowerCase(); + locModeA.rotationIsDir = (locRotationIsDir === "true" || locRotationIsDir === "1"); + } + else { + locModeA.rotationIsDir = false; + } + } else if (this.emitterMode === cc.ParticleSystem.MODE_RADIUS) { + // or Mode B: radius movement + var locModeB = this.modeB; + locModeB.startRadius = parseFloat(locValueForKey("maxRadius", dictionary)); + locModeB.startRadiusVar = parseFloat(locValueForKey("maxRadiusVariance", dictionary)); + locModeB.endRadius = parseFloat(locValueForKey("minRadius", dictionary)); + locModeB.endRadiusVar = 0; + locModeB.rotatePerSecond = parseFloat(locValueForKey("rotatePerSecond", dictionary)); + locModeB.rotatePerSecondVar = parseFloat(locValueForKey("rotatePerSecondVariance", dictionary)); + } else { + cc.log("cc.ParticleSystem.initWithDictionary(): Invalid emitterType in config file"); + return false; + } + + // life span + this.life = parseFloat(locValueForKey("particleLifespan", dictionary)); + this.lifeVar = parseFloat(locValueForKey("particleLifespanVariance", dictionary)); + + // emission Rate + this.emissionRate = this._totalParticles / this.life; + + //don't get the internal texture if a batchNode is used + if (!this._batchNode) { + // Set a compatible default for the alpha transfer + this._opacityModifyRGB = false; + + // texture + // Try to get the texture from the cache + var textureName = locValueForKey("textureFileName", dictionary); + var imgPath = cc.path.changeBasename(this._plistFile, textureName); + var tex = cc.textureCache.getTextureForKey(imgPath); + + if (tex) { + this.setTexture(tex); + } else { + var textureData = locValueForKey("textureImageData", dictionary); + + if (!textureData || textureData.length === 0) { + tex = cc.textureCache.addImage(imgPath); + if (!tex) + return false; + this.setTexture(tex); + } else { + buffer = cc.unzipBase64AsArray(textureData, 1); + if (!buffer) { + cc.log("cc.ParticleSystem: error decoding or ungzipping textureImageData"); + return false; + } + + var imageFormat = cc.getImageFormatByData(buffer); + + if (imageFormat !== cc.FMT_TIFF && imageFormat !== cc.FMT_PNG) { + cc.log("cc.ParticleSystem: unknown image format with Data"); + return false; + } + + var canvasObj = document.createElement("canvas"); + if (imageFormat === cc.FMT_PNG) { + var myPngObj = new cc.PNGReader(buffer); + myPngObj.render(canvasObj); + } else { + var myTIFFObj = cc.tiffReader; + myTIFFObj.parseTIFF(buffer, canvasObj); + } + + cc.textureCache.cacheImage(imgPath, canvasObj); + + var addTexture = cc.textureCache.getTextureForKey(imgPath); + if (!addTexture) + cc.log("cc.ParticleSystem.initWithDictionary() : error loading the texture"); + this.setTexture(addTexture); + } + } + } + ret = true; + } + return ret; + }, + + /** + * Initializes a system with a fixed number of particles + * @param {Number} numberOfParticles + * @return {Boolean} + */ + initWithTotalParticles: function (numberOfParticles) { + this._totalParticles = numberOfParticles; + + var i, locParticles = this._particles; + locParticles.length = 0; + for (i = 0; i < numberOfParticles; i++) { + locParticles[i] = new cc.Particle(); + } + + if (!locParticles) { + cc.log("Particle system: not enough memory"); + return false; + } + this._allocatedParticles = numberOfParticles; + + if (this._batchNode) + for (i = 0; i < this._totalParticles; i++) + locParticles[i].atlasIndex = i; + + // default, active + this._isActive = true; + + // default blend function + this._blendFunc.src = cc.BLEND_SRC; + this._blendFunc.dst = cc.BLEND_DST; + + // default movement type; + this.positionType = cc.ParticleSystem.TYPE_FREE; + + // by default be in mode A: + this.emitterMode = cc.ParticleSystem.MODE_GRAVITY; + + // default: modulate + // XXX: not used + // colorModulate = YES; + this.autoRemoveOnFinish = false; + + //for batchNode + this._transformSystemDirty = false; + + // udpate after action in run! + this.scheduleUpdateWithPriority(1); + this._renderCmd._initWithTotalParticles(numberOfParticles); + return true; + }, + + /** + * Unschedules the "update" method. + * @function + * @see scheduleUpdate(); + */ + destroyParticleSystem: function () { + this.unscheduleUpdate(); + }, + + /** + * Add a particle to the emitter + * @return {Boolean} + */ + addParticle: function () { + if (this.isFull()) + return false; + + var particle = this._renderCmd.addParticle(); + this.initParticle(particle); + ++this.particleCount; + return true; + }, + + /** + * Initializes a particle + * @param {cc.Particle} particle + */ + initParticle: function (particle) { + var locRandomMinus11 = cc.randomMinus1To1; + // timeToLive + // no negative life. prevent division by 0 + particle.timeToLive = this.life + this.lifeVar * locRandomMinus11(); + particle.timeToLive = Math.max(0, particle.timeToLive); + + // position + particle.pos.x = this._sourcePosition.x + this._posVar.x * locRandomMinus11(); + particle.pos.y = this._sourcePosition.y + this._posVar.y * locRandomMinus11(); + + // Color + var start, end; + var locStartColor = this._startColor, locStartColorVar = this._startColorVar; + var locEndColor = this._endColor, locEndColorVar = this._endColorVar; + start = { + r: cc.clampf(locStartColor.r + locStartColorVar.r * locRandomMinus11(), 0, 255), + g: cc.clampf(locStartColor.g + locStartColorVar.g * locRandomMinus11(), 0, 255), + b: cc.clampf(locStartColor.b + locStartColorVar.b * locRandomMinus11(), 0, 255), + a: cc.clampf(locStartColor.a + locStartColorVar.a * locRandomMinus11(), 0, 255) + }; + end = { + r: cc.clampf(locEndColor.r + locEndColorVar.r * locRandomMinus11(), 0, 255), + g: cc.clampf(locEndColor.g + locEndColorVar.g * locRandomMinus11(), 0, 255), + b: cc.clampf(locEndColor.b + locEndColorVar.b * locRandomMinus11(), 0, 255), + a: cc.clampf(locEndColor.a + locEndColorVar.a * locRandomMinus11(), 0, 255) + }; + + particle.color = start; + var locParticleDeltaColor = particle.deltaColor, locParticleTimeToLive = particle.timeToLive; + locParticleDeltaColor.r = (end.r - start.r) / locParticleTimeToLive; + locParticleDeltaColor.g = (end.g - start.g) / locParticleTimeToLive; + locParticleDeltaColor.b = (end.b - start.b) / locParticleTimeToLive; + locParticleDeltaColor.a = (end.a - start.a) / locParticleTimeToLive; + + // size + var startS = this.startSize + this.startSizeVar * locRandomMinus11(); + startS = Math.max(0, startS); // No negative value + + particle.size = startS; + if (this.endSize === cc.ParticleSystem.START_SIZE_EQUAL_TO_END_SIZE) { + particle.deltaSize = 0; + } else { + var endS = this.endSize + this.endSizeVar * locRandomMinus11(); + endS = Math.max(0, endS); // No negative values + particle.deltaSize = (endS - startS) / locParticleTimeToLive; + } + + // rotation + var startA = this.startSpin + this.startSpinVar * locRandomMinus11(); + var endA = this.endSpin + this.endSpinVar * locRandomMinus11(); + particle.rotation = startA; + particle.deltaRotation = (endA - startA) / locParticleTimeToLive; + + // position + if (this.positionType === cc.ParticleSystem.TYPE_FREE) + particle.startPos = this.convertToWorldSpace(this._pointZeroForParticle); + else if (this.positionType === cc.ParticleSystem.TYPE_RELATIVE) { + particle.startPos.x = this._position.x; + particle.startPos.y = this._position.y; + } + + // direction + var a = cc.degreesToRadians(this.angle + this.angleVar * locRandomMinus11()); + + // Mode Gravity: A + if (this.emitterMode === cc.ParticleSystem.MODE_GRAVITY) { + var locModeA = this.modeA, locParticleModeA = particle.modeA; + var s = locModeA.speed + locModeA.speedVar * locRandomMinus11(); + + // direction + locParticleModeA.dir.x = Math.cos(a); + locParticleModeA.dir.y = Math.sin(a); + cc.pMultIn(locParticleModeA.dir, s); + + // radial accel + locParticleModeA.radialAccel = locModeA.radialAccel + locModeA.radialAccelVar * locRandomMinus11(); + + // tangential accel + locParticleModeA.tangentialAccel = locModeA.tangentialAccel + locModeA.tangentialAccelVar * locRandomMinus11(); + + // rotation is dir + if (locModeA.rotationIsDir) + particle.rotation = -cc.radiansToDegrees(cc.pToAngle(locParticleModeA.dir)); + } else { + // Mode Radius: B + var locModeB = this.modeB, locParitlceModeB = particle.modeB; + + // Set the default diameter of the particle from the source position + var startRadius = locModeB.startRadius + locModeB.startRadiusVar * locRandomMinus11(); + var endRadius = locModeB.endRadius + locModeB.endRadiusVar * locRandomMinus11(); + + locParitlceModeB.radius = startRadius; + locParitlceModeB.deltaRadius = (locModeB.endRadius === cc.ParticleSystem.START_RADIUS_EQUAL_TO_END_RADIUS) ? 0 : (endRadius - startRadius) / locParticleTimeToLive; + + locParitlceModeB.angle = a; + locParitlceModeB.degreesPerSecond = cc.degreesToRadians(locModeB.rotatePerSecond + locModeB.rotatePerSecondVar * locRandomMinus11()); + } + }, + + /** + * stop emitting particles. Running particles will continue to run until they die + */ + stopSystem: function () { + this._isActive = false; + this._elapsed = this.duration; + this._emitCounter = 0; + }, + + /** + * Kill all living particles. + */ + resetSystem: function () { + this._isActive = true; + this._elapsed = 0; + var locParticles = this._particles; + for (this._particleIdx = 0; this._particleIdx < this.particleCount; ++this._particleIdx) + locParticles[this._particleIdx].timeToLive = 0; + }, + + /** + * whether or not the system is full + * @return {Boolean} + */ + isFull: function () { + return (this.particleCount >= this._totalParticles); + }, + + /** + * should be overridden by subclasses + * @param {cc.Particle} particle + * @param {cc.Point} newPosition + */ + updateQuadWithParticle: function (particle, newPosition) { + this._renderCmd.updateQuadWithParticle(particle, newPosition); + }, + + /** + * should be overridden by subclasses + */ + postStep: function () { + this._renderCmd.postStep(); + }, + + /** + * update emitter's status + * @override + * @param {Number} dt delta time + */ + update:function (dt) { + if (this._isActive && this.emissionRate) { + var rate = 1.0 / this.emissionRate; + //issue #1201, prevent bursts of particles, due to too high emitCounter + if (this.particleCount < this._totalParticles) + this._emitCounter += dt; + + while ((this.particleCount < this._totalParticles) && (this._emitCounter > rate)) { + this.addParticle(); + this._emitCounter -= rate; + } + + this._elapsed += dt; + if (this.duration !== -1 && this.duration < this._elapsed) + this.stopSystem(); + } + this._particleIdx = 0; + + var currentPosition = cc.Particle.TemporaryPoints[0]; + if (this.positionType === cc.ParticleSystem.TYPE_FREE) { + cc.pIn(currentPosition, this.convertToWorldSpace(this._pointZeroForParticle)); + } else if (this.positionType === cc.ParticleSystem.TYPE_RELATIVE) { + currentPosition.x = this._position.x; + currentPosition.y = this._position.y; + } + + if (this._visible) { + // Used to reduce memory allocation / creation within the loop + var tpa = cc.Particle.TemporaryPoints[1], + tpb = cc.Particle.TemporaryPoints[2], + tpc = cc.Particle.TemporaryPoints[3]; + + var locParticles = this._particles; + while (this._particleIdx < this.particleCount) { + + // Reset the working particles + cc.pZeroIn(tpa); + cc.pZeroIn(tpb); + cc.pZeroIn(tpc); + + var selParticle = locParticles[this._particleIdx]; + + // life + selParticle.timeToLive -= dt; + + if (selParticle.timeToLive > 0) { + // Mode A: gravity, direction, tangential accel & radial accel + if (this.emitterMode === cc.ParticleSystem.MODE_GRAVITY) { + + var tmp = tpc, radial = tpa, tangential = tpb; + + // radial acceleration + if (selParticle.pos.x || selParticle.pos.y) { + cc.pIn(radial, selParticle.pos); + cc.pNormalizeIn(radial); + } else { + cc.pZeroIn(radial); + } + + cc.pIn(tangential, radial); + cc.pMultIn(radial, selParticle.modeA.radialAccel); + + // tangential acceleration + var newy = tangential.x; + tangential.x = -tangential.y; + tangential.y = newy; + + cc.pMultIn(tangential, selParticle.modeA.tangentialAccel); + + cc.pIn(tmp, radial); + cc.pAddIn(tmp, tangential); + cc.pAddIn(tmp, this.modeA.gravity); + cc.pMultIn(tmp, dt); + cc.pAddIn(selParticle.modeA.dir, tmp); + + + cc.pIn(tmp, selParticle.modeA.dir); + cc.pMultIn(tmp, dt); + cc.pAddIn(selParticle.pos, tmp); + } else { + // Mode B: radius movement + var selModeB = selParticle.modeB; + // Update the angle and radius of the particle. + selModeB.angle += selModeB.degreesPerSecond * dt; + selModeB.radius += selModeB.deltaRadius * dt; + + selParticle.pos.x = -Math.cos(selModeB.angle) * selModeB.radius; + selParticle.pos.y = -Math.sin(selModeB.angle) * selModeB.radius; + } + + // color + this._renderCmd._updateDeltaColor(selParticle, dt); + + // size + selParticle.size += (selParticle.deltaSize * dt); + selParticle.size = Math.max(0, selParticle.size); + + // angle + selParticle.rotation += (selParticle.deltaRotation * dt); + + // + // update values in quad + // + var newPos = tpa; + if (this.positionType === cc.ParticleSystem.TYPE_FREE || this.positionType === cc.ParticleSystem.TYPE_RELATIVE) { + var diff = tpb; + cc.pIn(diff, currentPosition); + cc.pSubIn(diff, selParticle.startPos); + + cc.pIn(newPos, selParticle.pos); + cc.pSubIn(newPos, diff); + } else { + cc.pIn(newPos, selParticle.pos); + } + + // translate newPos to correct position, since matrix transform isn't performed in batchnode + // don't update the particle with the new position information, it will interfere with the radius and tangential calculations + if (this._batchNode) { + newPos.x += this._position.x; + newPos.y += this._position.y; + } + this._renderCmd.updateParticlePosition(selParticle, newPos); + + // update particle counter + ++this._particleIdx; + } else { + // life < 0 + var currentIndex = selParticle.atlasIndex; + if (this._particleIdx !== this.particleCount - 1) { + var deadParticle = locParticles[this._particleIdx]; + locParticles[this._particleIdx] = locParticles[this.particleCount - 1]; + locParticles[this.particleCount - 1] = deadParticle; + } + if (this._batchNode) { + //disable the switched particle + this._batchNode.disableParticle(this.atlasIndex + currentIndex); + //switch indexes + locParticles[this.particleCount - 1].atlasIndex = currentIndex; + } + + --this.particleCount; + if (this.particleCount === 0 && this.autoRemoveOnFinish) { + this.unscheduleUpdate(); + this._parent.removeChild(this, true); + return; + } + } + } + this._transformSystemDirty = false; + } + + if (!this._batchNode) + this.postStep(); + }, + + /** + * update emitter's status (dt = 0) + */ + updateWithNoTime: function () { + this.update(0); + }, + + // + // return the string found by key in dict. + // @param {string} key + // @param {object} dict + // @return {String} "" if not found; return the string if found. + // @private + // + _valueForKey: function (key, dict) { + if (dict) { + var pString = dict[key]; + return pString != null ? pString : ""; + } + return ""; + }, + + _updateBlendFunc: function () { + if (this._batchNode) { + cc.log("Can't change blending functions when the particle is being batched"); + return; + } + + var locTexture = this._texture; + if (locTexture && locTexture instanceof cc.Texture2D) { + this._opacityModifyRGB = false; + var locBlendFunc = this._blendFunc; + if (locBlendFunc.src === cc.BLEND_SRC && locBlendFunc.dst === cc.BLEND_DST) { + if (locTexture.hasPremultipliedAlpha()) { + this._opacityModifyRGB = true; + } else { + locBlendFunc.src = cc.SRC_ALPHA; + locBlendFunc.dst = cc.ONE_MINUS_SRC_ALPHA; + } + } + } + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * + * @return {cc.ParticleSystem} + */ + clone: function () { + var retParticle = new cc.ParticleSystem(); + + // self, not super + if (retParticle.initWithTotalParticles(this.getTotalParticles())) { + // angle + retParticle.setAngle(this.getAngle()); + retParticle.setAngleVar(this.getAngleVar()); + + // duration + retParticle.setDuration(this.getDuration()); + + // blend function + var blend = this.getBlendFunc(); + retParticle.setBlendFunc(blend.src, blend.dst); + + // color + retParticle.setStartColor(this.getStartColor()); + + retParticle.setStartColorVar(this.getStartColorVar()); + + retParticle.setEndColor(this.getEndColor()); + + retParticle.setEndColorVar(this.getEndColorVar()); + + // this size + retParticle.setStartSize(this.getStartSize()); + retParticle.setStartSizeVar(this.getStartSizeVar()); + retParticle.setEndSize(this.getEndSize()); + retParticle.setEndSizeVar(this.getEndSizeVar()); + + // position + retParticle.setPosition(cc.p(this.x, this.y)); + retParticle.setPosVar(cc.p(this.getPosVar().x, this.getPosVar().y)); + + retParticle.setPositionType(this.getPositionType()); + + // Spinning + retParticle.setStartSpin(this.getStartSpin() || 0); + retParticle.setStartSpinVar(this.getStartSpinVar() || 0); + retParticle.setEndSpin(this.getEndSpin() || 0); + retParticle.setEndSpinVar(this.getEndSpinVar() || 0); + + retParticle.setEmitterMode(this.getEmitterMode()); + + // Mode A: Gravity + tangential accel + radial accel + if (this.getEmitterMode() === cc.ParticleSystem.MODE_GRAVITY) { + // gravity + var gra = this.getGravity(); + retParticle.setGravity(cc.p(gra.x, gra.y)); + + // speed + retParticle.setSpeed(this.getSpeed()); + retParticle.setSpeedVar(this.getSpeedVar()); + + // radial acceleration + retParticle.setRadialAccel(this.getRadialAccel()); + retParticle.setRadialAccelVar(this.getRadialAccelVar()); + + // tangential acceleration + retParticle.setTangentialAccel(this.getTangentialAccel()); + retParticle.setTangentialAccelVar(this.getTangentialAccelVar()); + + } else if (this.getEmitterMode() === cc.ParticleSystem.MODE_RADIUS) { + // or Mode B: radius movement + retParticle.setStartRadius(this.getStartRadius()); + retParticle.setStartRadiusVar(this.getStartRadiusVar()); + retParticle.setEndRadius(this.getEndRadius()); + retParticle.setEndRadiusVar(this.getEndRadiusVar()); + + retParticle.setRotatePerSecond(this.getRotatePerSecond()); + retParticle.setRotatePerSecondVar(this.getRotatePerSecondVar()); + } + + // life span + retParticle.setLife(this.getLife()); + retParticle.setLifeVar(this.getLifeVar()); + + // emission Rate + retParticle.setEmissionRate(this.getEmissionRate()); + + //don't get the internal texture if a batchNode is used + if (!this.getBatchNode()) { + // Set a compatible default for the alpha transfer + retParticle.setOpacityModifyRGB(this.isOpacityModifyRGB()); + // texture + var texture = this.getTexture(); + if (texture) { + var size = texture.getContentSize(); + retParticle.setTextureWithRect(texture, cc.rect(0, 0, size.width, size.height)); + } + } + } + return retParticle; + }, + + /** + *

Sets a new CCSpriteFrame as particle.
+ * WARNING: this method is experimental. Use setTextureWithRect instead. + *

+ * @param {cc.SpriteFrame} spriteFrame + */ + setDisplayFrame: function (spriteFrame) { + if (!spriteFrame) + return; + + var locOffset = spriteFrame.getOffsetInPixels(); + if (locOffset.x !== 0 || locOffset.y !== 0) + cc.log("cc.ParticleSystem.setDisplayFrame(): QuadParticle only supports SpriteFrames with no offsets"); + + // update texture before updating texture rect + var texture = spriteFrame.getTexture(), locTexture = this._texture; + if (locTexture !== texture) + this.setTexture(texture); + }, + + /** + * Sets a new texture with a rect. The rect is in Points. + * @param {cc.Texture2D} texture + * @param {cc.Rect} rect + */ + setTextureWithRect: function (texture, rect) { + var locTexture = this._texture; + if (locTexture !== texture) { + this._texture = texture; + this._updateBlendFunc(); + } + this.initTexCoordsWithRect(rect); + }, + + /** + * listen the event that coming to foreground on Android (An empty function for native) + * @param {cc.Class} obj + */ + listenBackToForeground: function (obj) { + //do nothing + } +}); + +var _p = cc.ParticleSystem.prototype; + +// Extended properties +/** @expose */ +_p.opacityModifyRGB; +cc.defineGetterSetter(_p, "opacityModifyRGB", _p.isOpacityModifyRGB, _p.setOpacityModifyRGB); +/** @expose */ +_p.batchNode; +cc.defineGetterSetter(_p, "batchNode", _p.getBatchNode, _p.setBatchNode); +/** @expose */ +_p.drawMode; +cc.defineGetterSetter(_p, "drawMode", _p.getDrawMode, _p.setDrawMode); +/** @expose */ +_p.shapeType; +cc.defineGetterSetter(_p, "shapeType", _p.getShapeType, _p.setShapeType); +/** @expose */ +_p.active; +cc.defineGetterSetter(_p, "active", _p.isActive); +/** @expose */ +_p.sourcePos; +cc.defineGetterSetter(_p, "sourcePos", _p.getSourcePosition, _p.setSourcePosition); +/** @expose */ +_p.posVar; +cc.defineGetterSetter(_p, "posVar", _p.getPosVar, _p.setPosVar); +/** @expose */ +_p.gravity; +cc.defineGetterSetter(_p, "gravity", _p.getGravity, _p.setGravity); +/** @expose */ +_p.speed; +cc.defineGetterSetter(_p, "speed", _p.getSpeed, _p.setSpeed); +/** @expose */ +_p.speedVar; +cc.defineGetterSetter(_p, "speedVar", _p.getSpeedVar, _p.setSpeedVar); +/** @expose */ +_p.tangentialAccel; +cc.defineGetterSetter(_p, "tangentialAccel", _p.getTangentialAccel, _p.setTangentialAccel); +/** @expose */ +_p.tangentialAccelVar; +cc.defineGetterSetter(_p, "tangentialAccelVar", _p.getTangentialAccelVar, _p.setTangentialAccelVar); +/** @expose */ +_p.radialAccel; +cc.defineGetterSetter(_p, "radialAccel", _p.getRadialAccel, _p.setRadialAccel); +/** @expose */ +_p.radialAccelVar; +cc.defineGetterSetter(_p, "radialAccelVar", _p.getRadialAccelVar, _p.setRadialAccelVar); +/** @expose */ +_p.rotationIsDir; +cc.defineGetterSetter(_p, "rotationIsDir", _p.getRotationIsDir, _p.setRotationIsDir); +/** @expose */ +_p.startRadius; +cc.defineGetterSetter(_p, "startRadius", _p.getStartRadius, _p.setStartRadius); +/** @expose */ +_p.startRadiusVar; +cc.defineGetterSetter(_p, "startRadiusVar", _p.getStartRadiusVar, _p.setStartRadiusVar); +/** @expose */ +_p.endRadius; +cc.defineGetterSetter(_p, "endRadius", _p.getEndRadius, _p.setEndRadius); +/** @expose */ +_p.endRadiusVar; +cc.defineGetterSetter(_p, "endRadiusVar", _p.getEndRadiusVar, _p.setEndRadiusVar); +/** @expose */ +_p.rotatePerS; +cc.defineGetterSetter(_p, "rotatePerS", _p.getRotatePerSecond, _p.setRotatePerSecond); +/** @expose */ +_p.rotatePerSVar; +cc.defineGetterSetter(_p, "rotatePerSVar", _p.getRotatePerSecondVar, _p.setRotatePerSecondVar); +/** @expose */ +_p.startColor; +cc.defineGetterSetter(_p, "startColor", _p.getStartColor, _p.setStartColor); +/** @expose */ +_p.startColorVar; +cc.defineGetterSetter(_p, "startColorVar", _p.getStartColorVar, _p.setStartColorVar); +/** @expose */ +_p.endColor; +cc.defineGetterSetter(_p, "endColor", _p.getEndColor, _p.setEndColor); +/** @expose */ +_p.endColorVar; +cc.defineGetterSetter(_p, "endColorVar", _p.getEndColorVar, _p.setEndColorVar); +/** @expose */ +_p.totalParticles; +cc.defineGetterSetter(_p, "totalParticles", _p.getTotalParticles, _p.setTotalParticles); +/** @expose */ +_p.texture; +cc.defineGetterSetter(_p, "texture", _p.getTexture, _p.setTexture); + + +/** + *

return the string found by key in dict.
+ * This plist files can be create manually or with Particle Designer:
+ * http://particledesigner.71squared.com/
+ *

+ * @deprecated since v3.0 please use new cc.ParticleSysytem(plistFile) instead. + * @param {String|Number} plistFile + * @return {cc.ParticleSystem} + */ +cc.ParticleSystem.create = function (plistFile) { + return new cc.ParticleSystem(plistFile); +}; + +/** + *

return the string found by key in dict.
+ * This plist files can be create manually or with Particle Designer:
+ * http://particledesigner.71squared.com/
+ *

+ * @deprecated since v3.0 please use new cc.ParticleSysytem(plistFile) instead. + * @function + * @param {String|Number} plistFile + * @return {cc.ParticleSystem} + */ +cc.ParticleSystem.createWithTotalParticles = cc.ParticleSystem.create; + +// Different modes +/** + * Mode A:Gravity + Tangential Accel + Radial Accel + * @Class + * @Construct + * @param {cc.Point} [gravity=] Gravity value. + * @param {Number} [speed=0] speed of each particle. + * @param {Number} [speedVar=0] speed variance of each particle. + * @param {Number} [tangentialAccel=0] tangential acceleration of each particle. + * @param {Number} [tangentialAccelVar=0] tangential acceleration variance of each particle. + * @param {Number} [radialAccel=0] radial acceleration of each particle. + * @param {Number} [radialAccelVar=0] radial acceleration variance of each particle. + * @param {boolean} [rotationIsDir=false] + */ +cc.ParticleSystem.ModeA = function (gravity, speed, speedVar, tangentialAccel, tangentialAccelVar, radialAccel, radialAccelVar, rotationIsDir) { + /** Gravity value. Only available in 'Gravity' mode. */ + this.gravity = gravity ? gravity : cc.p(0, 0); + /** speed of each particle. Only available in 'Gravity' mode. */ + this.speed = speed || 0; + /** speed variance of each particle. Only available in 'Gravity' mode. */ + this.speedVar = speedVar || 0; + /** tangential acceleration of each particle. Only available in 'Gravity' mode. */ + this.tangentialAccel = tangentialAccel || 0; + /** tangential acceleration variance of each particle. Only available in 'Gravity' mode. */ + this.tangentialAccelVar = tangentialAccelVar || 0; + /** radial acceleration of each particle. Only available in 'Gravity' mode. */ + this.radialAccel = radialAccel || 0; + /** radial acceleration variance of each particle. Only available in 'Gravity' mode. */ + this.radialAccelVar = radialAccelVar || 0; + /** set the rotation of each particle to its direction Only available in 'Gravity' mode. */ + this.rotationIsDir = rotationIsDir || false; +}; + +/** + * Mode B: circular movement (gravity, radial accel and tangential accel don't are not used in this mode) + * @Class + * @Construct + * @param {Number} [startRadius=0] The starting radius of the particles. + * @param {Number} [startRadiusVar=0] The starting radius variance of the particles. + * @param {Number} [endRadius=0] The ending radius of the particles. + * @param {Number} [endRadiusVar=0] The ending radius variance of the particles. + * @param {Number} [rotatePerSecond=0] Number of degrees to rotate a particle around the source pos per second. + * @param {Number} [rotatePerSecondVar=0] Variance in degrees for rotatePerSecond. + */ +cc.ParticleSystem.ModeB = function (startRadius, startRadiusVar, endRadius, endRadiusVar, rotatePerSecond, rotatePerSecondVar) { + /** The starting radius of the particles. Only available in 'Radius' mode. */ + this.startRadius = startRadius || 0; + /** The starting radius variance of the particles. Only available in 'Radius' mode. */ + this.startRadiusVar = startRadiusVar || 0; + /** The ending radius of the particles. Only available in 'Radius' mode. */ + this.endRadius = endRadius || 0; + /** The ending radius variance of the particles. Only available in 'Radius' mode. */ + this.endRadiusVar = endRadiusVar || 0; + /** Number of degress to rotate a particle around the source pos per second. Only available in 'Radius' mode. */ + this.rotatePerSecond = rotatePerSecond || 0; + /** Variance in degrees for rotatePerSecond. Only available in 'Radius' mode. */ + this.rotatePerSecondVar = rotatePerSecondVar || 0; +}; + +/** + * Shape Mode of Particle Draw + * @constant + * @type Number + */ +cc.ParticleSystem.SHAPE_MODE = 0; + +/** + * Texture Mode of Particle Draw + * @constant + * @type Number + */ +cc.ParticleSystem.TEXTURE_MODE = 1; + +/** + * Star Shape for ShapeMode of Particle + * @constant + * @type Number + */ +cc.ParticleSystem.STAR_SHAPE = 0; + +/** + * Ball Shape for ShapeMode of Particle + * @constant + * @type Number + */ +cc.ParticleSystem.BALL_SHAPE = 1; + +/** + * The Particle emitter lives forever + * @constant + * @type Number + */ +cc.ParticleSystem.DURATION_INFINITY = -1; + +/** + * The starting size of the particle is equal to the ending size + * @constant + * @type Number + */ +cc.ParticleSystem.START_SIZE_EQUAL_TO_END_SIZE = -1; + +/** + * The starting radius of the particle is equal to the ending radius + * @constant + * @type Number + */ +cc.ParticleSystem.START_RADIUS_EQUAL_TO_END_RADIUS = -1; + +/** + * Gravity mode (A mode) + * @constant + * @type Number + */ +cc.ParticleSystem.MODE_GRAVITY = 0; + +/** + * Radius mode (B mode) + * @constant + * @type Number + */ +cc.ParticleSystem.MODE_RADIUS = 1; + +/** + * Living particles are attached to the world and are unaffected by emitter repositioning. + * @constant + * @type Number + */ +cc.ParticleSystem.TYPE_FREE = 0; + +/** + * Living particles are attached to the world but will follow the emitter repositioning.
+ * Use case: Attach an emitter to an sprite, and you want that the emitter follows the sprite. + * @constant + * @type Number + */ +cc.ParticleSystem.TYPE_RELATIVE = 1; + +/** + * Living particles are attached to the emitter and are translated along with it. + * @constant + * @type Number + */ +cc.ParticleSystem.TYPE_GROUPED = 2; diff --git a/frameworks/cocos2d-html5/cocos2d/particle/CCParticleSystemCanvasRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/particle/CCParticleSystemCanvasRenderCmd.js new file mode 100644 index 0000000..5f602e9 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/particle/CCParticleSystemCanvasRenderCmd.js @@ -0,0 +1,206 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * ParticleSystem's canvas render command + */ +(function () { + cc.ParticleSystem.CanvasRenderCmd = function (renderable) { + this._rootCtor(renderable); + this._needDraw = true; + + this._drawMode = cc.ParticleSystem.TEXTURE_MODE; + this._shapeType = cc.ParticleSystem.BALL_SHAPE; + + this._pointRect = cc.rect(0, 0, 0, 0); + this._tintCache = null; + }; + var proto = cc.ParticleSystem.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + proto.constructor = cc.ParticleSystem.CanvasRenderCmd; + + proto.getDrawMode = function () { + return this._drawMode; + }; + + proto.setDrawMode = function (drawMode) { + this._drawMode = drawMode; + }; + + proto.getShapeType = function () { + return this._shapeType; + }; + + proto.setShapeType = function (shapeType) { + this._shapeType = shapeType; + }; + + proto.setBatchNode = function (batchNode) { + if (this._batchNode !== batchNode) { + this._node._batchNode = batchNode; + } + }; + + proto.updateQuadWithParticle = function (particle, newPosition) { + //do nothing + }; + + proto.updateParticlePosition = function (particle, position) { + cc.pIn(particle.drawPos, position); + }; + + proto.rendering = function (ctx, scaleX, scaleY) { + //TODO: need refactor rendering for performance + var wrapper = ctx || cc._renderContext, context = wrapper.getContext(), + node = this._node, pointRect = this._pointRect; + + wrapper.setTransform(this._worldTransform, scaleX, scaleY); + wrapper.save(); + if (node.isBlendAdditive()) + context.globalCompositeOperation = 'lighter'; + else + context.globalCompositeOperation = 'source-over'; + + var i, particle, lpx, alpha; + var particleCount = this._node.particleCount, particles = this._node._particles; + if (node.drawMode !== cc.ParticleSystem.SHAPE_MODE && node._texture) { + // Delay drawing until the texture is fully loaded by the browser + if (!node._texture._textureLoaded) { + wrapper.restore(); + return; + } + var element = node._texture.getHtmlElementObj(); + if (!element.width || !element.height) { + wrapper.restore(); + return; + } + + var drawElement = element; + for (i = 0; i < particleCount; i++) { + particle = particles[i]; + lpx = (0 | (particle.size * 0.5)); + + alpha = particle.color.a / 255; + if (alpha === 0) continue; + context.globalAlpha = alpha; + + context.save(); + context.translate((0 | particle.drawPos.x), -(0 | particle.drawPos.y)); + + var size = Math.floor(particle.size / 4) * 4; + var w = pointRect.width; + var h = pointRect.height; + + context.scale(Math.max((1 / w) * size, 0.000001), Math.max((1 / h) * size, 0.000001)); + if (particle.rotation) + context.rotate(cc.degreesToRadians(particle.rotation)); + + drawElement = particle.isChangeColor ? this._changeTextureColor(node._texture, particle.color, this._pointRect) : element; + context.drawImage(drawElement, -(0 | (w / 2)), -(0 | (h / 2))); + context.restore(); + } + } else { + var drawTool = cc._drawingUtil; + for (i = 0; i < particleCount; i++) { + particle = particles[i]; + lpx = (0 | (particle.size * 0.5)); + alpha = particle.color.a / 255; + if (alpha === 0) continue; + context.globalAlpha = alpha; + + context.save(); + context.translate(0 | particle.drawPos.x, -(0 | particle.drawPos.y)); + if (node.shapeType === cc.ParticleSystem.STAR_SHAPE) { + if (particle.rotation) + context.rotate(cc.degreesToRadians(particle.rotation)); + drawTool.drawStar(wrapper, lpx, particle.color); + } else + drawTool.drawColorBall(wrapper, lpx, particle.color); + context.restore(); + } + } + wrapper.restore(); + cc.g_NumberOfDraws++; + }; + + proto._changeTextureColor = function (texture, color, rect) { + if (!this._tintCache) { + this._tintCache = document.createElement("canvas"); + } + var tintCache = this._tintCache; + var textureContentSize = texture.getContentSize(); + tintCache.width = textureContentSize.width; + tintCache.height = textureContentSize.height; + return texture._generateColorTexture(color.r, color.g, color.b, rect, tintCache); + }; + + proto.initTexCoordsWithRect = function (pointRect) { + this._pointRect = pointRect; + }; + + proto.setTotalParticles = function (tp) { + //cc.assert(tp <= this._allocatedParticles, "Particle: resizing particle array only supported for quads"); + this._node._totalParticles = (tp < 200) ? tp : 200; + }; + + proto.addParticle = function () { + var node = this._node, + particles = node._particles, + particle; + if (node.particleCount < particles.length) { + particle = particles[node.particleCount]; + } else { + particle = new cc.Particle(); + particles.push(particle); + } + return particle; + }; + + proto._setupVBO = function () { + }; + proto._allocMemory = function () { + return true; + }; + + proto.postStep = function () { + }; + + proto._setBlendAdditive = function () { + var locBlendFunc = this._node._blendFunc; + locBlendFunc.src = cc.BLEND_SRC; + locBlendFunc.dst = cc.BLEND_DST; + }; + + proto._initWithTotalParticles = function (totalParticles) { + }; + proto._updateDeltaColor = function (selParticle, dt) { + if (!this._node._dontTint) { + var deltaColor = selParticle.deltaColor; + selParticle.color.r += deltaColor.r * dt; + selParticle.color.g += deltaColor.g * dt; + selParticle.color.b += deltaColor.b * dt; + selParticle.color.a += deltaColor.a * dt; + selParticle.isChangeColor = deltaColor.r !== 0 || deltaColor.g !== 0 || deltaColor.b !== 0; + } + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/particle/CCParticleSystemWebGLRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/particle/CCParticleSystemWebGLRenderCmd.js new file mode 100644 index 0000000..54b1236 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/particle/CCParticleSystemWebGLRenderCmd.js @@ -0,0 +1,420 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + /** + * ParticleSystem's WebGL render command + */ + cc.ParticleSystem.WebGLRenderCmd = function (renderable) { + this._rootCtor(renderable); + this._needDraw = true; + + this._matrix = null; + + this._buffersVBO = [0, 0]; + this._quads = []; + this._indices = []; + this._quadsArrayBuffer = null; + }; + var proto = cc.ParticleSystem.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + proto.constructor = cc.ParticleSystem.WebGLRenderCmd; + + proto.getDrawMode = function () { + }; + proto.setDrawMode = function (drawMode) { + }; + proto.getShapeType = function () { + }; + proto.setShapeType = function (shapeType) { + }; + + proto.setBatchNode = function (batchNode) { + var node = this._node; + if (node._batchNode !== batchNode) { + var oldBatch = node._batchNode; + node._batchNode = batchNode; //weak reference + + if (batchNode) { + var locParticles = node._particles; + for (var i = 0; i < node._totalParticles; i++) + locParticles[i].atlasIndex = i; + } + + // NEW: is self render ? + if (!batchNode) { + this._allocMemory(); + this.initIndices(node._totalParticles); + node.setTexture(oldBatch.getTexture()); + this._setupVBO(); + + } else if (!oldBatch) { + // OLD: was it self render cleanup ? + // copy current state to batch + node._batchNode.textureAtlas._copyQuadsToTextureAtlas(this._quads, node.atlasIndex); + + //delete buffer + cc._renderContext.deleteBuffer(this._buffersVBO[1]); //where is re-bindBuffer code? + } + } + }; + + proto.initIndices = function (totalParticles) { + var locIndices = this._indices; + for (var i = 0, len = totalParticles; i < len; ++i) { + var i6 = i * 6; + var i4 = i * 4; + locIndices[i6 + 0] = i4 + 0; + locIndices[i6 + 1] = i4 + 1; + locIndices[i6 + 2] = i4 + 2; + + locIndices[i6 + 5] = i4 + 1; + locIndices[i6 + 4] = i4 + 2; + locIndices[i6 + 3] = i4 + 3; + } + }; + + proto.isDifferentTexture = function (texture1, texture2) { + return (texture1 === texture2); + }; + + proto.updateParticlePosition = function (particle, position) { + // IMPORTANT: newPos may not be used as a reference here! (as it is just the temporary tpa point) + // the implementation of updateQuadWithParticle must use + // the x and y values directly + this.updateQuadWithParticle(particle, position); + }; + + proto.updateQuadWithParticle = function (particle, newPosition) { + var quad = null, node = this._node; + if (node._batchNode) { + var batchQuads = node._batchNode.textureAtlas.quads; + quad = batchQuads[node.atlasIndex + particle.atlasIndex]; + node._batchNode.textureAtlas.dirty = true; + } else + quad = this._quads[node._particleIdx]; + + var r, g, b, a; + if (node._opacityModifyRGB) { + r = 0 | (particle.color.r * particle.color.a / 255); + g = 0 | (particle.color.g * particle.color.a / 255); + b = 0 | (particle.color.b * particle.color.a / 255); + } else { + r = 0 | (particle.color.r ); + g = 0 | (particle.color.g ); + b = 0 | (particle.color.b ); + } + a = 0 | (particle.color.a ); + + var blColors = quad.bl.colors, brColors = quad.br.colors, tlColors = quad.tl.colors, trColors = quad.tr.colors; + blColors.r = brColors.r = tlColors.r = trColors.r = r; + blColors.g = brColors.g = tlColors.g = trColors.g = g; + blColors.b = brColors.b = tlColors.b = trColors.b = b; + blColors.a = brColors.a = tlColors.a = trColors.a = a; + + // vertices + var size_2 = particle.size / 2; + if (particle.rotation) { + var x1 = -size_2, y1 = -size_2; + + var x2 = size_2, y2 = size_2; + var x = newPosition.x, y = newPosition.y; + + var rad = -cc.degreesToRadians(particle.rotation); + var cr = Math.cos(rad), sr = Math.sin(rad); + var ax = x1 * cr - y1 * sr + x; + var ay = x1 * sr + y1 * cr + y; + var bx = x2 * cr - y1 * sr + x; + var by = x2 * sr + y1 * cr + y; + var cx = x2 * cr - y2 * sr + x; + var cy = x2 * sr + y2 * cr + y; + var dx = x1 * cr - y2 * sr + x; + var dy = x1 * sr + y2 * cr + y; + + // bottom-left + quad.bl.vertices.x = ax; + quad.bl.vertices.y = ay; + + // bottom-right vertex: + quad.br.vertices.x = bx; + quad.br.vertices.y = by; + + // top-left vertex: + quad.tl.vertices.x = dx; + quad.tl.vertices.y = dy; + + // top-right vertex: + quad.tr.vertices.x = cx; + quad.tr.vertices.y = cy; + } else { + // bottom-left vertex: + quad.bl.vertices.x = newPosition.x - size_2; + quad.bl.vertices.y = newPosition.y - size_2; + + // bottom-right vertex: + quad.br.vertices.x = newPosition.x + size_2; + quad.br.vertices.y = newPosition.y - size_2; + + // top-left vertex: + quad.tl.vertices.x = newPosition.x - size_2; + quad.tl.vertices.y = newPosition.y + size_2; + + // top-right vertex: + quad.tr.vertices.x = newPosition.x + size_2; + quad.tr.vertices.y = newPosition.y + size_2; + } + }; + + proto.rendering = function (ctx) { + var node = this._node; + if (!node._texture) + return; + + var gl = ctx || cc._renderContext; + + if (!this._matrix) { + this._matrix = new cc.math.Matrix4(); + this._matrix.identity(); + } + var wt = this._worldTransform; + this._matrix.mat[0] = wt.a; + this._matrix.mat[4] = wt.c; + this._matrix.mat[12] = wt.tx; + this._matrix.mat[1] = wt.b; + this._matrix.mat[5] = wt.d; + this._matrix.mat[13] = wt.ty; + + this._glProgramState.apply(this._matrix); + + cc.glBindTexture2D(node._texture); + cc.glBlendFuncForParticle(node._blendFunc.src, node._blendFunc.dst); + + // + // Using VBO without VAO + // + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_POSITION); + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_COLOR); + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_TEX_COORDS); + + gl.bindBuffer(gl.ARRAY_BUFFER, this._buffersVBO[0]); + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 3, gl.FLOAT, false, 24, 0); // vertices + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, gl.UNSIGNED_BYTE, true, 24, 12); // colors + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, gl.FLOAT, false, 24, 16); // tex coords + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._buffersVBO[1]); + gl.drawElements(gl.TRIANGLES, node._particleIdx * 6, gl.UNSIGNED_SHORT, 0); + }; + + proto.initTexCoordsWithRect = function (pointRect) { + var node = this._node; + var texture = node.texture; + var scaleFactor = cc.contentScaleFactor(); + // convert to pixels coords + var rect = cc.rect( + pointRect.x * scaleFactor, + pointRect.y * scaleFactor, + pointRect.width * scaleFactor, + pointRect.height * scaleFactor); + + var wide = pointRect.width; + var high = pointRect.height; + + if (texture) { + wide = texture.pixelsWidth; + high = texture.pixelsHeight; + } + + var left, bottom, right, top; + if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) { + left = (rect.x * 2 + 1) / (wide * 2); + bottom = (rect.y * 2 + 1) / (high * 2); + right = left + (rect.width * 2 - 2) / (wide * 2); + top = bottom + (rect.height * 2 - 2) / (high * 2); + } else { + left = rect.x / wide; + bottom = rect.y / high; + right = left + rect.width / wide; + top = bottom + rect.height / high; + } + + // Important. Texture in cocos2d are inverted, so the Y component should be inverted + var temp = top; + top = bottom; + bottom = temp; + + var quads; + var start = 0, end = 0; + if (node._batchNode) { + quads = node._batchNode.textureAtlas.quads; + start = node.atlasIndex; + end = node.atlasIndex + node._totalParticles; + } else { + quads = this._quads; + start = 0; + end = node._totalParticles; + } + + for (var i = start; i < end; i++) { + if (!quads[i]) + quads[i] = cc.V3F_C4B_T2F_QuadZero(); + + // bottom-left vertex: + var selQuad = quads[i]; + selQuad.bl.texCoords.u = left; + selQuad.bl.texCoords.v = bottom; + // bottom-right vertex: + selQuad.br.texCoords.u = right; + selQuad.br.texCoords.v = bottom; + // top-left vertex: + selQuad.tl.texCoords.u = left; + selQuad.tl.texCoords.v = top; + // top-right vertex: + selQuad.tr.texCoords.u = right; + selQuad.tr.texCoords.v = top; + } + }; + + proto.setTotalParticles = function (tp) { + var node = this._node; + // If we are setting the total numer of particles to a number higher + // than what is allocated, we need to allocate new arrays + if (tp > node._allocatedParticles) { + var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT; + // Allocate new memory + this._indices = new Uint16Array(tp * 6); + var locQuadsArrayBuffer = new ArrayBuffer(tp * quadSize); + //TODO need fix + // Assign pointers + var locParticles = node._particles; + locParticles.length = 0; + var locQuads = this._quads; + locQuads.length = 0; + for (var j = 0; j < tp; j++) { + locParticles[j] = new cc.Particle(); + locQuads[j] = new cc.V3F_C4B_T2F_Quad(null, null, null, null, locQuadsArrayBuffer, j * quadSize); + } + node._allocatedParticles = tp; + node._totalParticles = tp; + + // Init particles + if (node._batchNode) { + for (var i = 0; i < tp; i++) + locParticles[i].atlasIndex = i; + } + + this._quadsArrayBuffer = locQuadsArrayBuffer; + this.initIndices(tp); + this._setupVBO(); + + //set the texture coord + if (node._texture) { + this.initTexCoordsWithRect(cc.rect(0, 0, node._texture.width, node._texture.height)); + } + } else + node._totalParticles = tp; + node.resetSystem(); + }; + + proto.addParticle = function () { + var node = this._node, + particles = node._particles; + return particles[node.particleCount]; + }; + + proto._setupVBO = function () { + var node = this; + var gl = cc._renderContext; + + //gl.deleteBuffer(this._buffersVBO[0]); + this._buffersVBO[0] = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, this._buffersVBO[0]); + gl.bufferData(gl.ARRAY_BUFFER, this._quadsArrayBuffer, gl.DYNAMIC_DRAW); + + this._buffersVBO[1] = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._buffersVBO[1]); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this._indices, gl.STATIC_DRAW); + + //cc.checkGLErrorDebug(); + }; + + proto._allocMemory = function () { + var node = this._node; + //cc.assert((!this._quads && !this._indices), "Memory already allocated"); + if (node._batchNode) { + cc.log("cc.ParticleSystem._allocMemory(): Memory should not be allocated when not using batchNode"); + return false; + } + + var quadSize = cc.V3F_C4B_T2F_Quad.BYTES_PER_ELEMENT; + var totalParticles = node._totalParticles; + var locQuads = this._quads; + locQuads.length = 0; + this._indices = new Uint16Array(totalParticles * 6); + var locQuadsArrayBuffer = new ArrayBuffer(quadSize * totalParticles); + + for (var i = 0; i < totalParticles; i++) + locQuads[i] = new cc.V3F_C4B_T2F_Quad(null, null, null, null, locQuadsArrayBuffer, i * quadSize); + if (!locQuads || !this._indices) { + cc.log("cocos2d: Particle system: not enough memory"); + return false; + } + this._quadsArrayBuffer = locQuadsArrayBuffer; + return true; + }; + + proto.postStep = function () { + var gl = cc._renderContext; + gl.bindBuffer(gl.ARRAY_BUFFER, this._buffersVBO[0]); + gl.bufferSubData(gl.ARRAY_BUFFER, 0, this._quadsArrayBuffer); + }; + + proto._setBlendAdditive = function () { + var locBlendFunc = this._node._blendFunc; + if (this._texture && !this._texture.hasPremultipliedAlpha()) { + locBlendFunc.src = cc.SRC_ALPHA; + locBlendFunc.dst = cc.ONE_MINUS_SRC_ALPHA; + } else { + locBlendFunc.src = cc.BLEND_SRC; + locBlendFunc.dst = cc.BLEND_DST; + } + }; + + proto._initWithTotalParticles = function (totalParticles) { + // allocating data space + if (!this._allocMemory()) + return false; + + this.initIndices(totalParticles); + this._setupVBO(); + + this._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR); + }; + + proto._updateDeltaColor = function (selParticle, dt) { + selParticle.color.r += selParticle.deltaColor.r * dt; + selParticle.color.g += selParticle.deltaColor.g * dt; + selParticle.color.b += selParticle.deltaColor.b * dt; + selParticle.color.a += selParticle.deltaColor.a * dt; + selParticle.isChangeColor = true; + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/particle/CCTIFFReader.js b/frameworks/cocos2d-html5/cocos2d/particle/CCTIFFReader.js new file mode 100644 index 0000000..1854865 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/particle/CCTIFFReader.js @@ -0,0 +1,692 @@ +/**************************************************************************** + Copyright (c) 2011 Gordon P. Hemsley + http://gphemsley.org/ + + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * cc.tiffReader is a singleton object, it's a tiff file reader, it can parse byte array to draw into a canvas + * @class + * @name cc.tiffReader + */ +cc.tiffReader = /** @lends cc.tiffReader# */{ + _littleEndian: false, + _tiffData: null, + _fileDirectories: [], + + getUint8: function (offset) { + return this._tiffData[offset]; + }, + + getUint16: function (offset) { + if (this._littleEndian) + return (this._tiffData[offset + 1] << 8) | (this._tiffData[offset]); + else + return (this._tiffData[offset] << 8) | (this._tiffData[offset + 1]); + }, + + getUint32: function (offset) { + var a = this._tiffData; + if (this._littleEndian) + return (a[offset + 3] << 24) | (a[offset + 2] << 16) | (a[offset + 1] << 8) | (a[offset]); + else + return (a[offset] << 24) | (a[offset + 1] << 16) | (a[offset + 2] << 8) | (a[offset + 3]); + }, + + checkLittleEndian: function () { + var BOM = this.getUint16(0); + + if (BOM === 0x4949) { + this.littleEndian = true; + } else if (BOM === 0x4D4D) { + this.littleEndian = false; + } else { + console.log(BOM); + throw TypeError("Invalid byte order value."); + } + + return this.littleEndian; + }, + + hasTowel: function () { + // Check for towel. + if (this.getUint16(2) !== 42) { + throw RangeError("You forgot your towel!"); + return false; + } + + return true; + }, + + getFieldTypeName: function (fieldType) { + var typeNames = this.fieldTypeNames; + if (fieldType in typeNames) { + return typeNames[fieldType]; + } + return null; + }, + + getFieldTagName: function (fieldTag) { + var tagNames = this.fieldTagNames; + + if (fieldTag in tagNames) { + return tagNames[fieldTag]; + } else { + console.log("Unknown Field Tag:", fieldTag); + return "Tag" + fieldTag; + } + }, + + getFieldTypeLength: function (fieldTypeName) { + if (['BYTE', 'ASCII', 'SBYTE', 'UNDEFINED'].indexOf(fieldTypeName) !== -1) { + return 1; + } else if (['SHORT', 'SSHORT'].indexOf(fieldTypeName) !== -1) { + return 2; + } else if (['LONG', 'SLONG', 'FLOAT'].indexOf(fieldTypeName) !== -1) { + return 4; + } else if (['RATIONAL', 'SRATIONAL', 'DOUBLE'].indexOf(fieldTypeName) !== -1) { + return 8; + } + return null; + }, + + getFieldValues: function (fieldTagName, fieldTypeName, typeCount, valueOffset) { + var fieldValues = []; + var fieldTypeLength = this.getFieldTypeLength(fieldTypeName); + var fieldValueSize = fieldTypeLength * typeCount; + + if (fieldValueSize <= 4) { + // The value is stored at the big end of the valueOffset. + if (this.littleEndian === false) + fieldValues.push(valueOffset >>> ((4 - fieldTypeLength) * 8)); + else + fieldValues.push(valueOffset); + } else { + for (var i = 0; i < typeCount; i++) { + var indexOffset = fieldTypeLength * i; + if (fieldTypeLength >= 8) { + if (['RATIONAL', 'SRATIONAL'].indexOf(fieldTypeName) !== -1) { + // Numerator + fieldValues.push(this.getUint32(valueOffset + indexOffset)); + // Denominator + fieldValues.push(this.getUint32(valueOffset + indexOffset + 4)); + } else { + cc.log("Can't handle this field type or size"); + } + } else { + fieldValues.push(this.getBytes(fieldTypeLength, valueOffset + indexOffset)); + } + } + } + + if (fieldTypeName === 'ASCII') { + fieldValues.forEach(function (e, i, a) { + a[i] = String.fromCharCode(e); + }); + } + return fieldValues; + }, + + getBytes: function (numBytes, offset) { + if (numBytes <= 0) { + cc.log("No bytes requested"); + } else if (numBytes <= 1) { + return this.getUint8(offset); + } else if (numBytes <= 2) { + return this.getUint16(offset); + } else if (numBytes <= 3) { + return this.getUint32(offset) >>> 8; + } else if (numBytes <= 4) { + return this.getUint32(offset); + } else { + cc.log("Too many bytes requested"); + } + }, + + getBits: function (numBits, byteOffset, bitOffset) { + bitOffset = bitOffset || 0; + var extraBytes = Math.floor(bitOffset / 8); + var newByteOffset = byteOffset + extraBytes; + var totalBits = bitOffset + numBits; + var shiftRight = 32 - numBits; + var shiftLeft,rawBits; + + if (totalBits <= 0) { + console.log("No bits requested"); + } else if (totalBits <= 8) { + shiftLeft = 24 + bitOffset; + rawBits = this.getUint8(newByteOffset); + } else if (totalBits <= 16) { + shiftLeft = 16 + bitOffset; + rawBits = this.getUint16(newByteOffset); + } else if (totalBits <= 32) { + shiftLeft = bitOffset; + rawBits = this.getUint32(newByteOffset); + } else { + console.log( "Too many bits requested" ); + } + + return { + 'bits': ((rawBits << shiftLeft) >>> shiftRight), + 'byteOffset': newByteOffset + Math.floor(totalBits / 8), + 'bitOffset': totalBits % 8 + }; + }, + + parseFileDirectory: function (byteOffset) { + var numDirEntries = this.getUint16(byteOffset); + var tiffFields = []; + + for (var i = byteOffset + 2, entryCount = 0; entryCount < numDirEntries; i += 12, entryCount++) { + var fieldTag = this.getUint16(i); + var fieldType = this.getUint16(i + 2); + var typeCount = this.getUint32(i + 4); + var valueOffset = this.getUint32(i + 8); + + var fieldTagName = this.getFieldTagName(fieldTag); + var fieldTypeName = this.getFieldTypeName(fieldType); + var fieldValues = this.getFieldValues(fieldTagName, fieldTypeName, typeCount, valueOffset); + + tiffFields[fieldTagName] = { type: fieldTypeName, values: fieldValues }; + } + + this._fileDirectories.push(tiffFields); + + var nextIFDByteOffset = this.getUint32(i); + if (nextIFDByteOffset !== 0x00000000) { + this.parseFileDirectory(nextIFDByteOffset); + } + }, + + clampColorSample: function(colorSample, bitsPerSample) { + var multiplier = Math.pow(2, 8 - bitsPerSample); + + return Math.floor((colorSample * multiplier) + (multiplier - 1)); + }, + + /** + * @function + * @param {Array} tiffData + * @param {HTMLCanvasElement} canvas + * @returns {*} + */ + parseTIFF: function (tiffData, canvas) { + canvas = canvas || document.createElement('canvas'); + + this._tiffData = tiffData; + this.canvas = canvas; + + this.checkLittleEndian(); + + if (!this.hasTowel()) { + return; + } + + var firstIFDByteOffset = this.getUint32(4); + + this._fileDirectories.length = 0; + this.parseFileDirectory(firstIFDByteOffset); + + var fileDirectory = this._fileDirectories[0]; + + var imageWidth = fileDirectory['ImageWidth'].values[0]; + var imageLength = fileDirectory['ImageLength'].values[0]; + + this.canvas.width = imageWidth; + this.canvas.height = imageLength; + + var strips = []; + + var compression = (fileDirectory['Compression']) ? fileDirectory['Compression'].values[0] : 1; + + var samplesPerPixel = fileDirectory['SamplesPerPixel'].values[0]; + + var sampleProperties = []; + + var bitsPerPixel = 0; + var hasBytesPerPixel = false; + + fileDirectory['BitsPerSample'].values.forEach(function (bitsPerSample, i, bitsPerSampleValues) { + sampleProperties[i] = { + bitsPerSample: bitsPerSample, + hasBytesPerSample: false, + bytesPerSample: undefined + }; + + if ((bitsPerSample % 8) === 0) { + sampleProperties[i].hasBytesPerSample = true; + sampleProperties[i].bytesPerSample = bitsPerSample / 8; + } + + bitsPerPixel += bitsPerSample; + }, this); + + if ((bitsPerPixel % 8) === 0) { + hasBytesPerPixel = true; + var bytesPerPixel = bitsPerPixel / 8; + } + + var stripOffsetValues = fileDirectory['StripOffsets'].values; + var numStripOffsetValues = stripOffsetValues.length; + + // StripByteCounts is supposed to be required, but see if we can recover anyway. + if (fileDirectory['StripByteCounts']) { + var stripByteCountValues = fileDirectory['StripByteCounts'].values; + } else { + cc.log("Missing StripByteCounts!"); + + // Infer StripByteCounts, if possible. + if (numStripOffsetValues === 1) { + var stripByteCountValues = [Math.ceil((imageWidth * imageLength * bitsPerPixel) / 8)]; + } else { + throw Error("Cannot recover from missing StripByteCounts"); + } + } + + // Loop through strips and decompress as necessary. + for (var i = 0; i < numStripOffsetValues; i++) { + var stripOffset = stripOffsetValues[i]; + strips[i] = []; + + var stripByteCount = stripByteCountValues[i]; + + // Loop through pixels. + for (var byteOffset = 0, bitOffset = 0, jIncrement = 1, getHeader = true, pixel = [], numBytes = 0, sample = 0, currentSample = 0; + byteOffset < stripByteCount; byteOffset += jIncrement) { + // Decompress strip. + switch (compression) { + // Uncompressed + case 1: + // Loop through samples (sub-pixels). + for (var m = 0, pixel = []; m < samplesPerPixel; m++) { + if (sampleProperties[m].hasBytesPerSample) { + // XXX: This is wrong! + var sampleOffset = sampleProperties[m].bytesPerSample * m; + pixel.push(this.getBytes(sampleProperties[m].bytesPerSample, stripOffset + byteOffset + sampleOffset)); + } else { + var sampleInfo = this.getBits(sampleProperties[m].bitsPerSample, stripOffset + byteOffset, bitOffset); + pixel.push(sampleInfo.bits); + byteOffset = sampleInfo.byteOffset - stripOffset; + bitOffset = sampleInfo.bitOffset; + + throw RangeError("Cannot handle sub-byte bits per sample"); + } + } + + strips[i].push(pixel); + + if (hasBytesPerPixel) { + jIncrement = bytesPerPixel; + } else { + jIncrement = 0; + throw RangeError("Cannot handle sub-byte bits per pixel"); + } + break; + + // CITT Group 3 1-Dimensional Modified Huffman run-length encoding + case 2: + // XXX: Use PDF.js code? + break; + + // Group 3 Fax + case 3: + // XXX: Use PDF.js code? + break; + + // Group 4 Fax + case 4: + // XXX: Use PDF.js code? + break; + + // LZW + case 5: + // XXX: Use PDF.js code? + break; + + // Old-style JPEG (TIFF 6.0) + case 6: + // XXX: Use PDF.js code? + break; + + // New-style JPEG (TIFF Specification Supplement 2) + case 7: + // XXX: Use PDF.js code? + break; + + // PackBits + case 32773: + // Are we ready for a new block? + if (getHeader) { + getHeader = false; + + var blockLength = 1; + var iterations = 1; + + // The header byte is signed. + var header = this.getInt8(stripOffset + byteOffset); + + if ((header >= 0) && (header <= 127)) { // Normal pixels. + blockLength = header + 1; + } else if ((header >= -127) && (header <= -1)) { // Collapsed pixels. + iterations = -header + 1; + } else /*if (header === -128)*/ { // Placeholder byte? + getHeader = true; + } + } else { + var currentByte = this.getUint8(stripOffset + byteOffset); + + // Duplicate bytes, if necessary. + for (var m = 0; m < iterations; m++) { + if (sampleProperties[sample].hasBytesPerSample) { + // We're reading one byte at a time, so we need to handle multi-byte samples. + currentSample = (currentSample << (8 * numBytes)) | currentByte; + numBytes++; + + // Is our sample complete? + if (numBytes === sampleProperties[sample].bytesPerSample) { + pixel.push(currentSample); + currentSample = numBytes = 0; + sample++; + } + } else { + throw RangeError("Cannot handle sub-byte bits per sample"); + } + + // Is our pixel complete? + if (sample === samplesPerPixel) { + strips[i].push(pixel); + pixel = []; + sample = 0; + } + } + + blockLength--; + + // Is our block complete? + if (blockLength === 0) { + getHeader = true; + } + } + + jIncrement = 1; + break; + + // Unknown compression algorithm + default: + // Do not attempt to parse the image data. + break; + } + } + } + + if (canvas.getContext) { + var ctx = this.canvas.getContext("2d"); + + // Set a default fill style. + ctx.fillStyle = "rgba(255, 255, 255, 0)"; + + // If RowsPerStrip is missing, the whole image is in one strip. + var rowsPerStrip = fileDirectory['RowsPerStrip'] ? fileDirectory['RowsPerStrip'].values[0] : imageLength; + + var numStrips = strips.length; + + var imageLengthModRowsPerStrip = imageLength % rowsPerStrip; + var rowsInLastStrip = (imageLengthModRowsPerStrip === 0) ? rowsPerStrip : imageLengthModRowsPerStrip; + + var numRowsInStrip = rowsPerStrip; + var numRowsInPreviousStrip = 0; + + var photometricInterpretation = fileDirectory['PhotometricInterpretation'].values[0]; + + var extraSamplesValues = []; + var numExtraSamples = 0; + + if (fileDirectory['ExtraSamples']) { + extraSamplesValues = fileDirectory['ExtraSamples'].values; + numExtraSamples = extraSamplesValues.length; + } + + if (fileDirectory['ColorMap']) { + var colorMapValues = fileDirectory['ColorMap'].values; + var colorMapSampleSize = Math.pow(2, sampleProperties[0].bitsPerSample); + } + + // Loop through the strips in the image. + for (var i = 0; i < numStrips; i++) { + // The last strip may be short. + if ((i + 1) === numStrips) { + numRowsInStrip = rowsInLastStrip; + } + + var numPixels = strips[i].length; + var yPadding = numRowsInPreviousStrip * i; + + // Loop through the rows in the strip. + for (var y = 0, j = 0; y < numRowsInStrip, j < numPixels; y++) { + // Loop through the pixels in the row. + for (var x = 0; x < imageWidth; x++, j++) { + var pixelSamples = strips[i][j]; + + var red = 0; + var green = 0; + var blue = 0; + var opacity = 1.0; + + if (numExtraSamples > 0) { + for (var k = 0; k < numExtraSamples; k++) { + if (extraSamplesValues[k] === 1 || extraSamplesValues[k] === 2) { + // Clamp opacity to the range [0,1]. + opacity = pixelSamples[3 + k] / 256; + + break; + } + } + } + + switch (photometricInterpretation) { + // Bilevel or Grayscale + // WhiteIsZero + case 0: + if (sampleProperties[0].hasBytesPerSample) { + var invertValue = Math.pow(0x10, sampleProperties[0].bytesPerSample * 2); + } + + // Invert samples. + pixelSamples.forEach(function (sample, index, samples) { + samples[index] = invertValue - sample; + }); + + // Bilevel or Grayscale + // BlackIsZero + case 1: + red = green = blue = this.clampColorSample(pixelSamples[0], sampleProperties[0].bitsPerSample); + break; + + // RGB Full Color + case 2: + red = this.clampColorSample(pixelSamples[0], sampleProperties[0].bitsPerSample); + green = this.clampColorSample(pixelSamples[1], sampleProperties[1].bitsPerSample); + blue = this.clampColorSample(pixelSamples[2], sampleProperties[2].bitsPerSample); + break; + + // RGB Color Palette + case 3: + if (colorMapValues === undefined) { + throw Error("Palette image missing color map"); + } + + var colorMapIndex = pixelSamples[0]; + + red = this.clampColorSample(colorMapValues[colorMapIndex], 16); + green = this.clampColorSample(colorMapValues[colorMapSampleSize + colorMapIndex], 16); + blue = this.clampColorSample(colorMapValues[(2 * colorMapSampleSize) + colorMapIndex], 16); + break; + + // Unknown Photometric Interpretation + default: + throw RangeError('Unknown Photometric Interpretation:', photometricInterpretation); + break; + } + + ctx.fillStyle = "rgba(" + red + ", " + green + ", " + blue + ", " + opacity + ")"; + ctx.fillRect(x, yPadding + y, 1, 1); + } + } + + numRowsInPreviousStrip = numRowsInStrip; + } + } + + return this.canvas; + }, + + // See: http://www.digitizationguidelines.gov/guidelines/TIFF_Metadata_Final.pdf + // See: http://www.digitalpreservation.gov/formats/content/tiff_tags.shtml + fieldTagNames: { + // TIFF Baseline + 0x013B: 'Artist', + 0x0102: 'BitsPerSample', + 0x0109: 'CellLength', + 0x0108: 'CellWidth', + 0x0140: 'ColorMap', + 0x0103: 'Compression', + 0x8298: 'Copyright', + 0x0132: 'DateTime', + 0x0152: 'ExtraSamples', + 0x010A: 'FillOrder', + 0x0121: 'FreeByteCounts', + 0x0120: 'FreeOffsets', + 0x0123: 'GrayResponseCurve', + 0x0122: 'GrayResponseUnit', + 0x013C: 'HostComputer', + 0x010E: 'ImageDescription', + 0x0101: 'ImageLength', + 0x0100: 'ImageWidth', + 0x010F: 'Make', + 0x0119: 'MaxSampleValue', + 0x0118: 'MinSampleValue', + 0x0110: 'Model', + 0x00FE: 'NewSubfileType', + 0x0112: 'Orientation', + 0x0106: 'PhotometricInterpretation', + 0x011C: 'PlanarConfiguration', + 0x0128: 'ResolutionUnit', + 0x0116: 'RowsPerStrip', + 0x0115: 'SamplesPerPixel', + 0x0131: 'Software', + 0x0117: 'StripByteCounts', + 0x0111: 'StripOffsets', + 0x00FF: 'SubfileType', + 0x0107: 'Threshholding', + 0x011A: 'XResolution', + 0x011B: 'YResolution', + + // TIFF Extended + 0x0146: 'BadFaxLines', + 0x0147: 'CleanFaxData', + 0x0157: 'ClipPath', + 0x0148: 'ConsecutiveBadFaxLines', + 0x01B1: 'Decode', + 0x01B2: 'DefaultImageColor', + 0x010D: 'DocumentName', + 0x0150: 'DotRange', + 0x0141: 'HalftoneHints', + 0x015A: 'Indexed', + 0x015B: 'JPEGTables', + 0x011D: 'PageName', + 0x0129: 'PageNumber', + 0x013D: 'Predictor', + 0x013F: 'PrimaryChromaticities', + 0x0214: 'ReferenceBlackWhite', + 0x0153: 'SampleFormat', + 0x022F: 'StripRowCounts', + 0x014A: 'SubIFDs', + 0x0124: 'T4Options', + 0x0125: 'T6Options', + 0x0145: 'TileByteCounts', + 0x0143: 'TileLength', + 0x0144: 'TileOffsets', + 0x0142: 'TileWidth', + 0x012D: 'TransferFunction', + 0x013E: 'WhitePoint', + 0x0158: 'XClipPathUnits', + 0x011E: 'XPosition', + 0x0211: 'YCbCrCoefficients', + 0x0213: 'YCbCrPositioning', + 0x0212: 'YCbCrSubSampling', + 0x0159: 'YClipPathUnits', + 0x011F: 'YPosition', + + // EXIF + 0x9202: 'ApertureValue', + 0xA001: 'ColorSpace', + 0x9004: 'DateTimeDigitized', + 0x9003: 'DateTimeOriginal', + 0x8769: 'Exif IFD', + 0x9000: 'ExifVersion', + 0x829A: 'ExposureTime', + 0xA300: 'FileSource', + 0x9209: 'Flash', + 0xA000: 'FlashpixVersion', + 0x829D: 'FNumber', + 0xA420: 'ImageUniqueID', + 0x9208: 'LightSource', + 0x927C: 'MakerNote', + 0x9201: 'ShutterSpeedValue', + 0x9286: 'UserComment', + + // IPTC + 0x83BB: 'IPTC', + + // ICC + 0x8773: 'ICC Profile', + + // XMP + 0x02BC: 'XMP', + + // GDAL + 0xA480: 'GDAL_METADATA', + 0xA481: 'GDAL_NODATA', + + // Photoshop + 0x8649: 'Photoshop' + }, + + fieldTypeNames: { + 0x0001: 'BYTE', + 0x0002: 'ASCII', + 0x0003: 'SHORT', + 0x0004: 'LONG', + 0x0005: 'RATIONAL', + 0x0006: 'SBYTE', + 0x0007: 'UNDEFINED', + 0x0008: 'SSHORT', + 0x0009: 'SLONG', + 0x000A: 'SRATIONAL', + 0x000B: 'FLOAT', + 0x000C: 'DOUBLE' + } +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/cocos2d/physics/CCPhysicsDebugNode.js b/frameworks/cocos2d-html5/cocos2d/physics/CCPhysicsDebugNode.js new file mode 100644 index 0000000..003bfe3 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/physics/CCPhysicsDebugNode.js @@ -0,0 +1,212 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2012 Scott Lembcke and Howling Moon Software + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/* + IMPORTANT - READ ME! + + This file sets pokes around in the private API a lot to provide efficient + debug rendering given nothing more than reference to a Chipmunk space. + It is not recommended to write rendering code like this in your own games + as the private API may change with little or no warning. + */ + +/** + * Converts an array of numbers into an array of vectors(x,y) + * @function + * @param {Array} verts + * @return {Array} + */ +cc.__convertVerts = function (verts) { + var ret = []; + for (var i = 0; i < verts.length / 2; i++) { + ret[i] = {x:verts[i * 2], y:verts[i * 2 + 1]}; + } + return ret; +}; + +/** + * color for body + * @function + * @param {cp.Body} body + * @return {cc.color} + */ +cc.ColorForBody = function (body) { + if (body.isRogue() || body.isSleeping()) { + return cc.color(128, 128, 128, 128); + } else if (body.nodeIdleTime > body.space.sleepTimeThreshold) { + return cc.color(84, 84, 84, 128); + } else { + return cc.color(255, 0, 0, 128); + } +}; + +/** + * draw shape + * @param {cp.Shape} shape + * @param renderer + */ +cc.DrawShape = function (shape, renderer) { + var body = shape.body; + var color = cc.ColorForBody(body); + switch (shape.collisionCode) { + case cp.CircleShape.prototype.collisionCode: + this.drawDot(shape.tc, Math.max(shape.r, 1.0), color); + this.drawSegment(shape.tc, cp.v.add(shape.tc, cp.v.mult(body.rot, shape.r)), 1.0, color); + break; + case cp.SegmentShape.prototype.collisionCode: + this.drawSegment(shape.ta, shape.tb, Math.max(shape.r, 2.0), color); + break; + case cp.PolyShape.prototype.collisionCode: + var line = cc.color(color.r, color.g, color.b, cc.lerp(color.a, 255, 0.5)); + this.drawPoly(cc.__convertVerts(shape.tVerts), color, 1.0, line); + break; + default: + cc.log("cc.DrawShape(): Bad assertion in DrawShape()"); + break; + } +}; + +/** + * draw constraint + * @param {cp.Constraint} constraint + * @param renderer + */ +cc.DrawConstraint = function (constraint, renderer) { + var body_a = constraint.a; + var body_b = constraint.b; + var a, b; + + if (constraint instanceof cp.PinJoint) { + a = body_a.local2World(constraint.anchr1); + b = body_b.local2World(constraint.anchr2); + this.drawDot(a, 3.0, cc.CONSTRAINT_COLOR); + this.drawDot(b, 3.0, cc.CONSTRAINT_COLOR); + this.drawSegment(a, b, 1.0, cc.CONSTRAINT_COLOR); + } else if (constraint instanceof cp.SlideJoint) { + a = body_a.local2World(constraint.anchr1); + b = body_b.local2World(constraint.anchr2); + + this.drawDot(a, 3.0, cc.CONSTRAINT_COLOR); + this.drawDot(b, 3.0, cc.CONSTRAINT_COLOR); + this.drawSegment(a, b, 1.0, cc.CONSTRAINT_COLOR); + } else if (constraint instanceof cp.PivotJoint) { + a = body_a.local2World(constraint.anchr1); + b = body_b.local2World(constraint.anchr2); + this.drawDot(a, 3.0, cc.CONSTRAINT_COLOR); + this.drawDot(b, 3.0, cc.CONSTRAINT_COLOR); + } else if (constraint instanceof cp.GrooveJoint) { + a = body_a.local2World(constraint.grv_a); + b = body_a.local2World(constraint.grv_b); + var c = body_b.local2World(constraint.anchr2); + + this.drawDot(c, 3.0, cc.CONSTRAINT_COLOR); + this.drawSegment(a, b, 1.0, cc.CONSTRAINT_COLOR); + } else if (constraint instanceof cp.DampedSpring) { + // TODO + } else { + //printf("Cannot draw constraint\n"); + } +}; + +/** + * @constant + * @type {cc.color} + */ +cc.CONSTRAINT_COLOR = cc.color(0, 255, 0, 128); + + +/** + *

A Node that draws the components of a physics engine.
+ * Supported physics engines:
+ * - Chipmunk
+ * - Objective-Chipmunk

+ * + * @class + * @extends cc.DrawNode + * + * @property {cp.Space} space Physic world space + */ +cc.PhysicsDebugNode = cc.DrawNode.extend({ + _space:null, + _className:"PhysicsDebugNode", + + /** + * constructor of cc.PhysicsDebugNode + * @param {cp.Space} space + */ + ctor: function (space) { + cc.DrawNode.prototype.ctor.call(this); + this._space = space; + }, + + /** + * get space + * @returns {cp.Space} + */ + getSpace:function () { + return this._space; + }, + + /** + * set space + * @param {cp.Space} space + */ + setSpace:function (space) { + this._space = space; + }, + + /** + * draw + * @param {object} context + */ + draw:function (context) { + if (!this._space) + return; + + this._space.eachShape(cc.DrawShape.bind(this)); + this._space.eachConstraint(cc.DrawConstraint.bind(this)); + cc.DrawNode.prototype.draw.call(this); + this.clear(); + }, + + _createRenderCmd: function(){ + if(cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return new cc.PhysicsDebugNode.CanvasRenderCmd(this); + else + return new cc.PhysicsDebugNode.WebGLRenderCmd(this); + } +}); + +/** + * Create a debug node for a regular Chipmunk space. + * @deprecated since v3.0, please use new cc.PhysicsDebugNode(space) + * @param {cp.Space} space + * @return {cc.PhysicsDebugNode} + */ +cc.PhysicsDebugNode.create = function (space) { + return new cc.PhysicsDebugNode(space); +}; diff --git a/frameworks/cocos2d-html5/cocos2d/physics/CCPhysicsDebugNodeCanvasRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/physics/CCPhysicsDebugNodeCanvasRenderCmd.js new file mode 100644 index 0000000..1adf9a3 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/physics/CCPhysicsDebugNodeCanvasRenderCmd.js @@ -0,0 +1,52 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * cc.PhysicsDebugNode's rendering objects of Canvas + */ +(function () { + cc.PhysicsDebugNode.CanvasRenderCmd = function (renderableObject) { + this._rootCtor(renderableObject); + this._buffer = renderableObject._buffer; + this._needDraw = true; + }; + + var proto = cc.PhysicsDebugNode.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + proto.constructor = cc.PhysicsDebugNode.CanvasRenderCmd; + + proto.rendering = function (ctx, scaleX, scaleY) { + var node = this._node; + if (!node._space) + return; + node._space.eachShape(cc.DrawShape.bind(node)); + node._space.eachConstraint(cc.DrawConstraint.bind(node)); + cc.DrawNode.CanvasRenderCmd.prototype.rendering.call(this, ctx, scaleX, scaleY); + node.clear(); + }; + + proto._drawDot = cc.DrawNode.CanvasRenderCmd.prototype._drawDot; + proto._drawSegment = cc.DrawNode.CanvasRenderCmd.prototype._drawSegment; + proto._drawPoly = cc.DrawNode.CanvasRenderCmd.prototype._drawPoly; + +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/physics/CCPhysicsDebugNodeWebGLRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/physics/CCPhysicsDebugNodeWebGLRenderCmd.js new file mode 100644 index 0000000..0b8a185 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/physics/CCPhysicsDebugNodeWebGLRenderCmd.js @@ -0,0 +1,62 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * cc.PhysicsDebugNode's rendering objects of WebGL + */ +(function () { + cc.PhysicsDebugNode.WebGLRenderCmd = function (renderableObject) { + this._rootCtor(renderableObject); + this._needDraw = true; + this._matrix = new cc.math.Matrix4(); + this._matrix.identity(); + }; + + cc.PhysicsDebugNode.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + cc.PhysicsDebugNode.WebGLRenderCmd.prototype.constructor = cc.PhysicsDebugNode.WebGLRenderCmd; + + cc.PhysicsDebugNode.WebGLRenderCmd.prototype.rendering = function (ctx) { + var node = this._node; + if (!node._space) + return; + + node._space.eachShape(cc.DrawShape.bind(node)); + node._space.eachConstraint(cc.DrawConstraint.bind(node)); + + var wt = this._worldTransform; + this._matrix.mat[0] = wt.a; + this._matrix.mat[4] = wt.c; + this._matrix.mat[12] = wt.tx; + this._matrix.mat[1] = wt.b; + this._matrix.mat[5] = wt.d; + this._matrix.mat[13] = wt.ty; + + //cc.DrawNode.prototype.draw.call(node); + cc.glBlendFunc(node._blendFunc.src, node._blendFunc.dst); + this._glProgramState.apply(this._matrix); + node._render(); + + node.clear(); + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/physics/CCPhysicsSprite.js b/frameworks/cocos2d-html5/cocos2d/physics/CCPhysicsSprite.js new file mode 100644 index 0000000..dde3546 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/physics/CCPhysicsSprite.js @@ -0,0 +1,442 @@ +/** + * Copyright (c) 2012 Scott Lembcke and Howling Moon Software + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2011-2012 cocos2d-x.org + * Copyright (c) 2013-2014 Chukong Technologies Inc. + * + * 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. + */ + +/** A CCSprite subclass that is bound to a physics body. + It works with: + - Chipmunk: Preprocessor macro CC_ENABLE_CHIPMUNK_INTEGRATION should be defined + - Objective-Chipmunk: Preprocessor macro CC_ENABLE_CHIPMUNK_INTEGRATION should be defined + - Box2d: Preprocessor macro CC_ENABLE_BOX2D_INTEGRATION should be defined + + Features and Limitations: + - Scale and Skew properties are ignored. + - Position and rotation are going to updated from the physics body + - If you update the rotation or position manually, the physics body will be updated + - You can't eble both Chipmunk support and Box2d support at the same time. Only one can be enabled at compile time + */ +(function () { + var box2dAPI = { + _ignoreBodyRotation: false, + _body: null, + _PTMRatio: 32, + _rotation: 1, + /** + * Create a PhysicsSprite with filename and rect + * Constructor of cc.PhysicsSprite for Box2d + * @param {String|cc.Texture2D|cc.SpriteFrame} fileName + * @param {cc.Rect} rect + * @example + * + * 1.Create a sprite with image path and rect + * var physicsSprite1 = new cc.PhysicsSprite("res/HelloHTML5World.png"); + * var physicsSprite2 = new cc.PhysicsSprite("res/HelloHTML5World.png",cc.rect(0,0,480,320)); + * + * 2.Create a sprite with a sprite frame name. Must add "#" before fame name. + * var physicsSprite = new cc.PhysicsSprite('#grossini_dance_01.png'); + * + * 3.Create a sprite with a sprite frame + * var spriteFrame = cc.spriteFrameCache.getSpriteFrame("grossini_dance_01.png"); + * var physicsSprite = new cc.PhysicsSprite(spriteFrame); + * + * 4.Creates a sprite with an existing texture contained in a CCTexture2D object + * After creation, the rect will be the size of the texture, and the offset will be (0,0). + * var texture = cc.textureCache.addImage("HelloHTML5World.png"); + * var physicsSprite1 = new cc.PhysicsSprite(texture); + * var physicsSprite2 = new cc.PhysicsSprite(texture, cc.rect(0,0,480,320)); + * + */ + ctor: function (fileName, rect) { + cc.Sprite.prototype.ctor.call(this); + + if (fileName === undefined) { + cc.PhysicsSprite.prototype.init.call(this); + } else if (cc.isString(fileName)) { + if (fileName[0] === "#") { + //init with a sprite frame name + var frameName = fileName.substr(1, fileName.length - 1); + var spriteFrame = cc.spriteFrameCache.getSpriteFrame(frameName); + this.initWithSpriteFrame(spriteFrame); + } else { + //init with filename and rect + this.init(fileName, rect); + } + } else if (cc.isObject(fileName)) { + if (fileName instanceof cc.Texture2D) { + //init with texture and rect + this.initWithTexture(fileName, rect); + } else if (fileName instanceof cc.SpriteFrame) { + //init with a sprite frame + this.initWithSpriteFrame(fileName); + } + } + //this._transformCmd = new cc.PhysicsSpriteTransformCmdCanvas(this); + //cc.rendererCanvas.pushRenderCommand(this._transformCmd); + }, + + //visit: function(){ + // cc.Sprite.prototype.visit.call(this); + // cc.rendererCanvas.pushRenderCommand(this._transformCmd); + //}, + + /** + * set body + * @param {Box2D.Dynamics.b2Body} body + */ + setBody: function (body) { + this._body = body; + }, + + /** + * get body + * @return {Box2D.Dynamics.b2Body} + */ + getBody: function () { + return this._body; + }, + + /** + * set PTM ratio + * @param {Number} r + */ + setPTMRatio: function (r) { + this._PTMRatio = r; + }, + + /** + * get PTM ration + * @return {Number} + */ + getPTMRatio: function () { + return this._PTMRatio; + }, + + /** + * get position + * @return {cc.Point} + */ + getPosition: function () { + var pos = this._body.GetPosition(); + var locPTMRatio = this._PTMRatio; + return cc.p(pos.x * locPTMRatio, pos.y * locPTMRatio); + }, + + /** + * set position + * @param {cc.Point} p + */ + setPosition: function (p) { + var angle = this._body.GetAngle(); + var locPTMRatio = this._PTMRatio; + this._body.setTransform(Box2D.b2Vec2(p.x / locPTMRatio, p.y / locPTMRatio), angle); + this.setNodeDirty(); + }, + + /** + * get rotation + * @return {Number} + */ + getRotation: function () { + return (this._ignoreBodyRotation ? cc.radiansToDegrees(this._rotationRadians) : cc.radiansToDegrees(this._body.GetAngle())); + }, + + /** + * set rotation + * @param {Number} r + */ + setRotation: function (r) { + if (this._ignoreBodyRotation) { + this._rotation = r; + } else { + var locBody = this._body; + var p = locBody.GetPosition(); + locBody.SetTransform(p, cc.degreesToRadians(r)); + } + this.setNodeDirty(); + }, + + _syncPosition: function () { + var locPosition = this._position, + pos = this._body.GetPosition(), + x = pos.x * this._PTMRatio, + y = pos.y * this._PTMRatio; + if (locPosition.x !== pos.x || locPosition.y !== pos.y) { + cc.Sprite.prototype.setPosition.call(this, x, y); + } + }, + _syncRotation: function () { + this._rotationRadians = this._body.GetAngle(); + var a = cc.radiansToDegrees(this._rotationRadians); + if (this._rotationX !== a) { + cc.Sprite.prototype.setRotation.call(this, a); + } + }, + + /** + * set whether to ingore body's rotation + * @param {Boolean} b + */ + setIgnoreBodyRotation: function (b) { + this._ignoreBodyRotation = b; + } + }; + + var chipmunkAPI = { + _ignoreBodyRotation: false, + _body: null, //physics body + _rotation: 1, + + /** + * Create a PhysicsSprite with filename and rect + * Constructor of cc.PhysicsSprite for chipmunk + * @param {String|cc.Texture2D|cc.SpriteFrame} fileName + * @param {cc.Rect} rect + * @example + * + * 1.Create a sprite with image path and rect + * var physicsSprite1 = new cc.PhysicsSprite("res/HelloHTML5World.png"); + * var physicsSprite2 = new cc.PhysicsSprite("res/HelloHTML5World.png",cc.rect(0,0,480,320)); + * + * 2.Create a sprite with a sprite frame name. Must add "#" before frame name. + * var physicsSprite = new cc.PhysicsSprite('#grossini_dance_01.png'); + * + * 3.Create a sprite with a sprite frame + * var spriteFrame = cc.spriteFrameCache.getSpriteFrame("grossini_dance_01.png"); + * var physicsSprite = new cc.PhysicsSprite(spriteFrame); + * + * 4.Creates a sprite with an exsiting texture contained in a CCTexture2D object + * After creation, the rect will be the size of the texture, and the offset will be (0,0). + * var texture = cc.textureCache.addImage("HelloHTML5World.png"); + * var physicsSprite1 = new cc.PhysicsSprite(texture); + * var physicsSprite2 = new cc.PhysicsSprite(texture, cc.rect(0,0,480,320)); + * + */ + ctor: function (fileName, rect) { + cc.Sprite.prototype.ctor.call(this); + + if (fileName === undefined) { + cc.PhysicsSprite.prototype.init.call(this); + } else if (cc.isString(fileName)) { + if (fileName[0] === "#") { + //init with a sprite frame name + var frameName = fileName.substr(1, fileName.length - 1); + var spriteFrame = cc.spriteFrameCache.getSpriteFrame(frameName); + this.initWithSpriteFrame(spriteFrame); + } else { + //init with filename and rect + this.init(fileName, rect); + } + } else if (cc.isObject(fileName)) { + if (fileName instanceof cc.Texture2D) { + //init with texture and rect + this.initWithTexture(fileName, rect); + } else if (fileName instanceof cc.SpriteFrame) { + //init with a sprite frame + this.initWithSpriteFrame(fileName); + } + } + + cc.renderer.pushRenderCommand(this._renderCmd); + }, + + visit: function () { + cc.renderer.pushRenderCommand(this._renderCmd); + cc.Sprite.prototype.visit.call(this); + }, + + /** + * set body + * @param {cp.Body} body + */ + setBody: function (body) { + this._body = body; + }, + + /** + * get body + * @returns {cp.Body} + */ + getBody: function () { + return this._body; + }, + + /** + * get position + * @return {cc.Point} + */ + getPosition: function () { + var locBody = this._body; + return {x: locBody.p.x, y: locBody.p.y}; + }, + + /** + * get position x + * @return {Number} + */ + getPositionX: function () { + return this._body.p.x; + }, + + /** + * get position y + * @return {Number} + */ + getPositionY: function () { + return this._body.p.y; + }, + + /** + * set position + * @param {cc.Point|Number}newPosOrxValue + * @param {Number}yValue + */ + setPosition: function (newPosOrxValue, yValue) { + if (yValue === undefined) { + this._body.p.x = newPosOrxValue.x; + this._body.p.y = newPosOrxValue.y; + } else { + this._body.p.x = newPosOrxValue; + this._body.p.y = yValue; + } + }, + + /** + * set position x + * @param {Number} xValue + */ + setPositionX: function (xValue) { + this._body.p.x = xValue; + }, + + /** + * set position y + * @param {Number} yValue + */ + setPositionY: function (yValue) { + this._body.p.y = yValue; + }, + + _syncPosition: function () { + var locPosition = this._position, locBody = this._body; + if (locPosition.x !== locBody.p.x || locPosition.y !== locBody.p.y) { + cc.Sprite.prototype.setPosition.call(this, locBody.p.x, locBody.p.y); + } + }, + + /** + * get rotation + * @return {Number} + */ + getRotation: function () { + return this._ignoreBodyRotation ? this._rotationX : -cc.radiansToDegrees(this._body.a); + }, + + /** + * set rotation + * @param {Number} r + */ + setRotation: function (r) { + if (this._ignoreBodyRotation) { + cc.Sprite.prototype.setRotation.call(this, r); + } else { + this._body.a = -cc.degreesToRadians(r); + } + }, + _syncRotation: function () { + var a = -cc.radiansToDegrees(this._body.a); + if (this._rotationX !== a) { + cc.Sprite.prototype.setRotation.call(this, a); + } + }, + + /** + * get the affine transform matrix of node to parent coordinate frame + * @return {cc.AffineTransform} + */ + getNodeToParentTransform: function () { + return this._renderCmd.getNodeToParentTransform(); + }, + + /** + * whether dirty + * @return {Boolean} + */ + isDirty: function () { + return !this._body.isSleeping(); + }, + setDirty: function () { + }, + + /** + * set whether to ignore rotation of body + * @param {Boolean} b + */ + setIgnoreBodyRotation: function (b) { + this._ignoreBodyRotation = b; + }, + + _createRenderCmd: function () { + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return new cc.PhysicsSprite.CanvasRenderCmd(this); + else + return new cc.PhysicsSprite.WebGLRenderCmd(this); + } + }; + /** + * @class + */ + cc.PhysicsSprite = cc.Sprite.extend(chipmunkAPI); + cc.PhysicsSprite._className = "PhysicsSprite"; + var _p = cc.PhysicsSprite.prototype; + // Extended properties + /** @expose */ + _p.body; + cc.defineGetterSetter(_p, "body", _p.getBody, _p.setBody); + /** @expose */ + _p.dirty; + cc.defineGetterSetter(_p, "dirty", _p.isDirty, _p.setDirty); + + + /** + * Create a PhysicsSprite with filename and rect + * @deprecated since v3.0, please use new cc.PhysicsSprite(fileName, rect) instead + * @param {String|cc.Texture2D|cc.SpriteFrame} fileName + * @param {cc.Rect} rect + * @return {cc.PhysicsSprite} + */ + cc.PhysicsSprite.create = function (fileName, rect) { + return new cc.PhysicsSprite(fileName, rect); + }; + + /** + * @deprecated since v3.0, please use new cc.PhysicsSprite(spriteFrameName) instead + * @type {Function} + */ + cc.PhysicsSprite.createWithSpriteFrameName = cc.PhysicsSprite.create; + + /** + * @deprecated since v3.0, please use new cc.PhysicsSprite(spriteFrame) instead + * @type {Function} + */ + cc.PhysicsSprite.createWithSpriteFrame = cc.PhysicsSprite.create; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/physics/CCPhysicsSpriteCanvasRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/physics/CCPhysicsSpriteCanvasRenderCmd.js new file mode 100644 index 0000000..09279e6 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/physics/CCPhysicsSpriteCanvasRenderCmd.js @@ -0,0 +1,50 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * cc.PhysicsSprite's rendering objects of Canvas + */ +(function () { + cc.PhysicsSprite.CanvasRenderCmd = function (renderableObject) { + this._spriteCmdCtor(renderableObject); + this._needDraw = true; + }; + + var proto = cc.PhysicsSprite.CanvasRenderCmd.prototype = Object.create(cc.Sprite.CanvasRenderCmd.prototype); + proto.constructor = cc.PhysicsSprite.CanvasRenderCmd; + + proto.rendering = function (ctx, scaleX, scaleY) { + // This is a special class + // Sprite can not obtain sign + // So here must to calculate of each frame + var node = this._node; + node._syncPosition(); + if (!node._ignoreBodyRotation) + node._syncRotation(); + this.transform(this.getParentRenderCmd()); + + cc.Sprite.CanvasRenderCmd.prototype.rendering.call(this, ctx, scaleX, scaleY); + }; + +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/physics/CCPhysicsSpriteWebGLRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/physics/CCPhysicsSpriteWebGLRenderCmd.js new file mode 100644 index 0000000..25fc120 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/physics/CCPhysicsSpriteWebGLRenderCmd.js @@ -0,0 +1,51 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * cc.PhysicsSprite's rendering objects of WebGL + */ +(function () { + cc.PhysicsSprite.WebGLRenderCmd = function (renderableObject) { + this._spriteCmdCtor(renderableObject); + this._needDraw = true; + }; + + var proto = cc.PhysicsSprite.WebGLRenderCmd.prototype = Object.create(cc.Sprite.WebGLRenderCmd.prototype); + proto.constructor = cc.PhysicsSprite.WebGLRenderCmd; + + proto.spUploadData = cc.Sprite.WebGLRenderCmd.prototype.uploadData; + + proto.uploadData = function (f32buffer, ui32buffer, vertexDataOffset) { + // This is a special class + // Sprite can not obtain sign + // So here must to calculate of each frame + var node = this._node; + node._syncPosition(); + if (!node._ignoreBodyRotation) + node._syncRotation(); + this.transform(this.getParentRenderCmd(), true); + + return this.spUploadData(f32buffer, ui32buffer, vertexDataOffset); + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/progress-timer/CCActionProgressTimer.js b/frameworks/cocos2d-html5/cocos2d/progress-timer/CCActionProgressTimer.js new file mode 100644 index 0000000..026af89 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/progress-timer/CCActionProgressTimer.js @@ -0,0 +1,227 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (C) 2010 Lam Pham + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * Progress to percentage + * @class + * @extends cc.ActionInterval + * @param {Number} duration duration in seconds + * @param {Number} percent + * @example + * var to = new cc.ProgressTo(2, 100); + */ +cc.ProgressTo = cc.ActionInterval.extend(/** @lends cc.ProgressTo# */{ + _to:0, + _from:0, + + /** + * Creates a ProgressTo action with a duration and a percent + * Constructor of cc.ProgressTo + * @param {Number} duration duration in seconds + * @param {Number} percent + */ + ctor: function(duration, percent){ + cc.ActionInterval.prototype.ctor.call(this); + this._to = 0; + this._from = 0; + + percent !== undefined && this.initWithDuration(duration, percent); + }, + + /** Initializes with a duration and a percent + * @param {Number} duration duration in seconds + * @param {Number} percent + * @return {Boolean} + */ + initWithDuration:function (duration, percent) { + if (cc.ActionInterval.prototype.initWithDuration.call(this, duration)) { + this._to = percent; + return true; + } + return false; + }, + /** + * return a new cc.ProgressTo, all the configuration is the same as the original + * @returns {cc.ProgressTo} + */ + clone:function(){ + var action = new cc.ProgressTo(); + action.initWithDuration(this._duration, this._to); + return action; + }, + /** + * reverse hasn't been supported + * @returns {null} + */ + reverse: function(){ + cc.log("cc.ProgressTo.reverse(): reverse hasn't been supported."); + return null; + }, + + /** + * start with a target + * @param {cc.Node} target + */ + startWithTarget:function (target) { + cc.ActionInterval.prototype.startWithTarget.call(this, target); + this._from = target.percentage; + }, + + /** + * custom update + * @param {Number} time time in seconds + */ + update:function (time) { + if (this.target instanceof cc.ProgressTimer) + this.target.percentage = this._from + (this._to - this._from) * time; + } +}); + +/** + * Creates and initializes with a duration and a percent + * @function + * @param {Number} duration duration in seconds + * @param {Number} percent + * @return {cc.ProgressTo} + * @example + * // example + * var to = cc.progressTo(2, 100); + */ +cc.progressTo = function (duration, percent) { + return new cc.ProgressTo(duration, percent); +}; +/** + * Please use cc.progressTo instead + * Creates and initializes with a duration and a percent + * @static + * @deprecated since v3.0,please use cc.progressTo instead. + * @param {Number} duration duration in seconds + * @param {Number} percent + * @return {cc.ProgressTo} + */ +cc.ProgressTo.create = cc.progressTo; + +/** + * Progress from a percentage to another percentage + * @class + * @extends cc.ActionInterval + * @param {Number} duration duration in seconds + * @param {Number} fromPercentage + * @param {Number} toPercentage + * @example + * var fromTo = new cc.ProgressFromTo(2, 100.0, 0.0); + */ +cc.ProgressFromTo = cc.ActionInterval.extend(/** @lends cc.ProgressFromTo# */{ + _to:0, + _from:0, + + /** + * Creates and initializes the action with a duration, a "from" percentage and a "to" percentage + * Constructor of cc.ProgressFromTo + * @param {Number} duration duration in seconds + * @param {Number} fromPercentage + * @param {Number} toPercentage + */ + ctor:function(duration, fromPercentage, toPercentage){ + cc.ActionInterval.prototype.ctor.call(this); + this._to = 0; + this._from = 0; + + toPercentage !== undefined && this.initWithDuration(duration, fromPercentage, toPercentage); + }, + + /** Initializes the action with a duration, a "from" percentage and a "to" percentage + * @param {Number} duration duration in seconds + * @param {Number} fromPercentage + * @param {Number} toPercentage + * @return {Boolean} + */ + initWithDuration:function (duration, fromPercentage, toPercentage) { + if (cc.ActionInterval.prototype.initWithDuration.call(this, duration)) { + this._to = toPercentage; + this._from = fromPercentage; + return true; + } + return false; + }, + /** + * return a new cc.ProgressTo, all the configuration is the same as the original + * @returns {cc.ProgressFromTo} + */ + clone:function(){ + var action = new cc.ProgressFromTo(); + action.initWithDuration(this._duration, this._from, this._to); + return action; + }, + + /** + * @return {cc.ActionInterval} + */ + reverse:function () { + return cc.progressFromTo(this._duration, this._to, this._from); + }, + + /** + * start with a target + * @param {cc.Node} target + */ + startWithTarget:function (target) { + cc.ActionInterval.prototype.startWithTarget.call(this, target); + }, + + /** + * @param {Number} time time in seconds + */ + update:function (time) { + if (this.target instanceof cc.ProgressTimer) + this.target.percentage = this._from + (this._to - this._from) * time; + } +}); + +/** Creates and initializes the action with a duration, a "from" percentage and a "to" percentage + * @function + * @param {Number} duration duration in seconds + * @param {Number} fromPercentage + * @param {Number} toPercentage + * @return {cc.ProgressFromTo} + * @example + * // example + * var fromTo = cc.progressFromTo(2, 100.0, 0.0); + */ +cc.progressFromTo = function (duration, fromPercentage, toPercentage) { + return new cc.ProgressFromTo(duration, fromPercentage, toPercentage); +}; +/** + * Creates and initializes the action with a duration, a "from" percentage and a "to" percentage + * @static + * @deprecated since v3.0,please use cc.ProgressFromTo(duration, fromPercentage, toPercentage) instead. + * @param {Number} duration duration in seconds + * @param {Number} fromPercentage + * @param {Number} toPercentage + * @return {cc.ProgressFromTo} + */ +cc.ProgressFromTo.create = cc.progressFromTo; diff --git a/frameworks/cocos2d-html5/cocos2d/progress-timer/CCProgressTimer.js b/frameworks/cocos2d-html5/cocos2d/progress-timer/CCProgressTimer.js new file mode 100644 index 0000000..abeeb16 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/progress-timer/CCProgressTimer.js @@ -0,0 +1,363 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2010 Lam Pham + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * cc.Progresstimer is a subclass of cc.Node.
+ * It renders the inner sprite according to the percentage.
+ * The progress can be Radial, Horizontal or vertical. + * @class + * @extends cc.Node + * + * @property {cc.Point} midPoint

- Midpoint is used to modify the progress start position.
+ * If you're using radials type then the midpoint changes the center point
+ * If you're using bar type the the midpoint changes the bar growth
+ * it expands from the center but clamps to the sprites edge so:
+ * you want a left to right then set the midpoint all the way to cc.p(0,y)
+ * you want a right to left then set the midpoint all the way to cc.p(1,y)
+ * you want a bottom to top then set the midpoint all the way to cc.p(x,0)
+ * you want a top to bottom then set the midpoint all the way to cc.p(x,1)

+ * @property {cc.Point} barChangeRate - This allows the bar type to move the component at a specific rate. + * @property {enum} type - Type of the progress timer: cc.ProgressTimer.TYPE_RADIAL|cc.ProgressTimer.TYPE_BAR. + * @property {Number} percentage - Percentage to change progress, from 0 to 100. + * @property {cc.Sprite} sprite - The sprite to show the progress percentage. + * @property {Boolean} reverseDir - Indicate whether the direction is reversed. + * + */ +cc.ProgressTimer = cc.Node.extend(/** @lends cc.ProgressTimer# */{ + _type:null, + _percentage:0.0, + _sprite:null, + + _midPoint:null, + _barChangeRate:null, + _reverseDirection:false, + _className:"ProgressTimer", + + /** + * constructor of cc.cc.ProgressTimer + * @function + * @param {cc.Sprite} sprite + */ + ctor: function(sprite){ + cc.Node.prototype.ctor.call(this); + + this._type = cc.ProgressTimer.TYPE_RADIAL; + this._percentage = 0.0; + this._midPoint = cc.p(0, 0); + this._barChangeRate = cc.p(0, 0); + this._reverseDirection = false; + this._sprite = null; + + sprite && this.initWithSprite(sprite); + }, + + onEnter: function () { + this._super(); + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + this._renderCmd.initCmd(); + this._renderCmd._updateProgress(); + } + }, + + cleanup: function () { + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + this._renderCmd.releaseData(); + } + this._super(); + }, + + /** + * Midpoint is used to modify the progress start position. + * If you're using radials type then the midpoint changes the center point + * If you're using bar type the the midpoint changes the bar growth + * it expands from the center but clamps to the sprites edge so: + * you want a left to right then set the midpoint all the way to cc.p(0,y) + * you want a right to left then set the midpoint all the way to cc.p(1,y) + * you want a bottom to top then set the midpoint all the way to cc.p(x,0) + * you want a top to bottom then set the midpoint all the way to cc.p(x,1) + * @return {cc.Point} + */ + getMidpoint:function () { + return cc.p(this._midPoint.x, this._midPoint.y); + }, + + /** + * Midpoint setter + * @param {cc.Point} mpoint + */ + setMidpoint:function (mpoint) { + this._midPoint = cc.pClamp(mpoint, cc.p(0, 0), cc.p(1, 1)); + }, + + /** + * This allows the bar type to move the component at a specific rate + * Set the component to 0 to make sure it stays at 100%. + * For example you want a left to right bar but not have the height stay 100% + * Set the rate to be cc.p(0,1); and set the midpoint to = cc.p(0,.5f); + * @return {cc.Point} + */ + getBarChangeRate:function () { + return cc.p(this._barChangeRate.x, this._barChangeRate.y); + }, + + /** + * @param {cc.Point} barChangeRate + */ + setBarChangeRate:function (barChangeRate) { + this._barChangeRate = cc.pClamp(barChangeRate, cc.p(0, 0), cc.p(1, 1)); + }, + + /** + * Change the percentage to change progress + * @return {cc.ProgressTimer.TYPE_RADIAL|cc.ProgressTimer.TYPE_BAR} + */ + getType:function () { + return this._type; + }, + + /** + * Percentages are from 0 to 100 + * @return {Number} + */ + getPercentage:function () { + return this._percentage; + }, + + /** + * The image to show the progress percentage, retain + * @return {cc.Sprite} + */ + getSprite:function () { + return this._sprite; + }, + + /** + * from 0-100 + * @param {Number} percentage + */ + setPercentage:function (percentage) { + if (this._percentage !== percentage) { + this._percentage = cc.clampf(percentage, 0, 100); + this._renderCmd._updateProgress(); + } + }, + /** + * only use for jsbinding + * @param bValue + */ + setOpacityModifyRGB:function (bValue) { + }, + /** + * only use for jsbinding + * @returns {boolean} + */ + isOpacityModifyRGB:function () { + return false; + }, + /** + * return if reverse direction + * @returns {boolean} + */ + isReverseDirection:function () { + return this._reverseDirection; + }, + + /** + * set color of sprite + * @param {cc.Color} color + */ + setColor:function (color) { + this._sprite.color = color; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.colorDirty); + }, + + /** + * set opacity of sprite + * @param {Number} opacity + */ + setOpacity:function (opacity) { + this._sprite.opacity = opacity; + //this._renderCmd._updateColor(); + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.opacityDirty); + }, + + /** + * return color of sprite + * @return {cc.Color} + */ + getColor:function () { + return this._sprite.color; + }, + + /** + * return Opacity of sprite + * @return {Number} + */ + getOpacity:function () { + return this._sprite.opacity; + }, + + /** + * set reverse cc.ProgressTimer + * @function + * @param {Boolean} reverse + */ + setReverseProgress: function(reverse){ + if (this._reverseDirection !== reverse){ + this._reverseDirection = reverse; + this._renderCmd.resetVertexData(); + } + }, + + /** + * set sprite for cc.ProgressTimer + * @function + * @param {cc.Sprite} sprite + */ + setSprite: function(sprite){ + if (this._sprite !== sprite) { + this._sprite = sprite; + if(sprite) { + this.setContentSize(sprite.width, sprite.height); + sprite.ignoreAnchorPointForPosition(true); + } + else { + this.setContentSize(0,0); + } + this._renderCmd.resetVertexData(); + } + }, + + /** + * set Progress type of cc.ProgressTimer + * @function + * @param {cc.ProgressTimer.TYPE_RADIAL|cc.ProgressTimer.TYPE_BAR} type + */ + setType: function(type){ + if (type !== this._type){ + this._type = type; + this._renderCmd.resetVertexData(); + } + }, + + /** + * Reverse Progress setter + * @function + * @param {Boolean} reverse + */ + setReverseDirection: function(reverse){ + if (this._reverseDirection !== reverse){ + this._reverseDirection = reverse; + this._renderCmd.resetVertexData(); + } + }, + + /** + * Initializes a progress timer with the sprite as the shape the timer goes through + * @function + * @param {cc.Sprite} sprite + * @return {Boolean} + */ + initWithSprite: function(sprite){ + this.percentage = 0; + this.setAnchorPoint(0.5,0.5); + + this._type = cc.ProgressTimer.TYPE_RADIAL; + this._reverseDirection = false; + this.midPoint = cc.p(0.5, 0.5); + this.barChangeRate = cc.p(1, 1); + this.setSprite(sprite); + this._renderCmd.resetVertexData(); + return true; + }, + + _createRenderCmd: function(){ + if(cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return new cc.ProgressTimer.CanvasRenderCmd(this); + else + return new cc.ProgressTimer.WebGLRenderCmd(this); + } +}); + +// Extended properties +var _p = cc.ProgressTimer.prototype; + +/** @expose */ +_p.midPoint; +cc.defineGetterSetter(_p, "midPoint", _p.getMidpoint, _p.setMidpoint); +/** @expose */ +_p.barChangeRate; +cc.defineGetterSetter(_p, "barChangeRate", _p.getBarChangeRate, _p.setBarChangeRate); +/** @expose */ +_p.type; +cc.defineGetterSetter(_p, "type", _p.getType, _p.setType); +/** @expose */ +_p.percentage; +cc.defineGetterSetter(_p, "percentage", _p.getPercentage, _p.setPercentage); +/** @expose */ +_p.sprite; +cc.defineGetterSetter(_p, "sprite", _p.getSprite, _p.setSprite); +/** @expose */ +_p.reverseDir; +cc.defineGetterSetter(_p, "reverseDir", _p.isReverseDirection, _p.setReverseDirection); + + +/** + * create a progress timer object with image file name that renders the inner sprite according to the percentage + * @deprecated since v3.0,please use new cc.ProgressTimer(sprite) instead. + * @param {cc.Sprite} sprite + * @return {cc.ProgressTimer} + */ +cc.ProgressTimer.create = function (sprite) { + return new cc.ProgressTimer(sprite); +}; + +/** + * @constant + * @type Number + */ +cc.ProgressTimer.TEXTURE_COORDS_COUNT = 4; + +/** + * @constant + * @type Number + */ +cc.ProgressTimer.TEXTURE_COORDS = 0x4b; + +/** + * Radial Counter-Clockwise + * @type Number + * @constant + */ +cc.ProgressTimer.TYPE_RADIAL = 0; + +/** + * Bar + * @type Number + * @constant + */ +cc.ProgressTimer.TYPE_BAR = 1; diff --git a/frameworks/cocos2d-html5/cocos2d/progress-timer/CCProgressTimerCanvasRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/progress-timer/CCProgressTimerCanvasRenderCmd.js new file mode 100644 index 0000000..4dc2a71 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/progress-timer/CCProgressTimerCanvasRenderCmd.js @@ -0,0 +1,286 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * cc.ProgressTimer's rendering objects of Canvas + */ +(function () { + cc.ProgressTimer.CanvasRenderCmd = function (renderableObject) { + this._rootCtor(renderableObject); + this._needDraw = true; + + this._PI180 = Math.PI / 180; + this._barRect = cc.rect(0, 0, 0, 0); + this._origin = cc.p(0, 0); + this._radius = 0; + this._startAngle = 270; + this._endAngle = 270; + this._counterClockWise = false; + this._canUseDirtyRegion = true; + }; + + var proto = cc.ProgressTimer.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + proto.constructor = cc.ProgressTimer.CanvasRenderCmd; + + proto.rendering = function (ctx, scaleX, scaleY) { + var wrapper = ctx || cc._renderContext, context = wrapper.getContext(), node = this._node, locSprite = node._sprite; + var locTextureCoord = locSprite._renderCmd._textureCoord, alpha = locSprite._renderCmd._displayedOpacity / 255; + + if (locTextureCoord.width === 0 || locTextureCoord.height === 0) + return; + if (!locSprite._texture || !locTextureCoord.validRect || alpha === 0) + return; + + wrapper.setTransform(this._worldTransform, scaleX, scaleY); + wrapper.setCompositeOperation(locSprite._blendFuncStr); + wrapper.setGlobalAlpha(alpha); + + var locRect = locSprite._rect, locOffsetPosition = locSprite._offsetPosition; + var locX = locOffsetPosition.x, + locY = -locOffsetPosition.y - locRect.height, + locWidth = locRect.width, + locHeight = locRect.height; + + wrapper.save(); + if (locSprite._flippedX) { + locX = -locX - locWidth; + context.scale(-1, 1); + } + if (locSprite._flippedY) { + locY = locOffsetPosition.y; + context.scale(1, -1); + } + + //clip + if (node._type === cc.ProgressTimer.TYPE_BAR) { + var locBarRect = this._barRect; + context.beginPath(); + context.rect(locBarRect.x, locBarRect.y, locBarRect.width, locBarRect.height); + context.clip(); + context.closePath(); + } else if (node._type === cc.ProgressTimer.TYPE_RADIAL) { + var locOriginX = this._origin.x; + var locOriginY = this._origin.y; + context.beginPath(); + context.arc(locOriginX, locOriginY, this._radius, this._PI180 * this._startAngle, this._PI180 * this._endAngle, this._counterClockWise); + context.lineTo(locOriginX, locOriginY); + context.clip(); + context.closePath(); + } + + //draw sprite + var texture = locSprite._renderCmd._textureToRender || locSprite._texture; + var image = texture.getHtmlElementObj(); + if (locSprite._renderCmd._colorized) { + context.drawImage(image, + 0, 0, locTextureCoord.width, locTextureCoord.height, + locX, locY, locWidth, locHeight); + } else { + context.drawImage(image, + locTextureCoord.renderX, locTextureCoord.renderY, locTextureCoord.width, locTextureCoord.height, + locX, locY, locWidth, locHeight); + } + wrapper.restore(); + cc.g_NumberOfDraws++; + }; + + proto.releaseData = function () { + }; + + proto.resetVertexData = function () { + }; + + proto._updateProgress = function () { + this.setDirtyFlag(cc.Node._dirtyFlags.contentDirty); + var node = this._node; + var locSprite = node._sprite; + var sw = locSprite.width, sh = locSprite.height; + var locMidPoint = node._midPoint; + + if (node._type === cc.ProgressTimer.TYPE_RADIAL) { + this._radius = Math.round(Math.sqrt(sw * sw + sh * sh)); + var locStartAngle, locEndAngle, locCounterClockWise = false, locOrigin = this._origin; + locOrigin.x = sw * locMidPoint.x; + locOrigin.y = -sh * locMidPoint.y; + + if (node._reverseDirection) { + locEndAngle = 270; + locStartAngle = 270 - 3.6 * node._percentage; + } else { + locStartAngle = -90; + locEndAngle = -90 + 3.6 * node._percentage; + } + + if (locSprite._flippedX) { + locOrigin.x -= sw * (node._midPoint.x * 2); + locStartAngle = -locStartAngle; + locEndAngle = -locEndAngle; + locStartAngle -= 180; + locEndAngle -= 180; + locCounterClockWise = !locCounterClockWise; + } + if (locSprite._flippedY) { + locOrigin.y += sh * (node._midPoint.y * 2); + locCounterClockWise = !locCounterClockWise; + locStartAngle = -locStartAngle; + locEndAngle = -locEndAngle; + } + + this._startAngle = locStartAngle; + this._endAngle = locEndAngle; + this._counterClockWise = locCounterClockWise; + } else { + var locBarChangeRate = node._barChangeRate; + var percentageF = node._percentage / 100; + var locBarRect = this._barRect; + + var drewSize = cc.size((sw * (1 - locBarChangeRate.x)), (sh * (1 - locBarChangeRate.y))); + var drawingSize = cc.size((sw - drewSize.width) * percentageF, (sh - drewSize.height) * percentageF); + var currentDrawSize = cc.size(drewSize.width + drawingSize.width, drewSize.height + drawingSize.height); + + var startPoint = cc.p(sw * locMidPoint.x, sh * locMidPoint.y); + + var needToLeft = startPoint.x - currentDrawSize.width / 2; + if ((locMidPoint.x > 0.5) && (currentDrawSize.width / 2 >= sw - startPoint.x)) + needToLeft = sw - currentDrawSize.width; + + var needToTop = startPoint.y - currentDrawSize.height / 2; + if ((locMidPoint.y > 0.5) && (currentDrawSize.height / 2 >= sh - startPoint.y)) + needToTop = sh - currentDrawSize.height; + + //left pos + locBarRect.x = 0; + var flipXNeed = 1; + if (locSprite._flippedX) { + locBarRect.x -= currentDrawSize.width; + flipXNeed = -1; + } + + if (needToLeft > 0) + locBarRect.x += needToLeft * flipXNeed; + + //right pos + locBarRect.y = 0; + var flipYNeed = 1; + if (locSprite._flippedY) { + locBarRect.y += currentDrawSize.height; + flipYNeed = -1; + } + + if (needToTop > 0) + locBarRect.y -= needToTop * flipYNeed; + + //clip width and clip height + locBarRect.width = currentDrawSize.width; + locBarRect.height = -currentDrawSize.height; + } + }; + + proto._syncStatus = function (parentCmd) { + var node = this._node; + if (!node._sprite) + return; + var flags = cc.Node._dirtyFlags, locFlag = this._dirtyFlag; + var parentNode = parentCmd ? parentCmd._node : null; + + if (parentNode && parentNode._cascadeColorEnabled && (parentCmd._dirtyFlag & flags.colorDirty)) + locFlag |= flags.colorDirty; + + if (parentNode && parentNode._cascadeOpacityEnabled && (parentCmd._dirtyFlag & flags.opacityDirty)) + locFlag |= flags.opacityDirty; + + if (parentCmd && (parentCmd._dirtyFlag & flags.transformDirty)) + locFlag |= flags.transformDirty; + + this._dirtyFlag = locFlag; + + var spriteCmd = node._sprite._renderCmd; + var spriteFlag = spriteCmd._dirtyFlag; + + var colorDirty = spriteFlag & flags.colorDirty, + opacityDirty = spriteFlag & flags.opacityDirty; + + if (colorDirty) { + spriteCmd._syncDisplayColor(); + spriteCmd._dirtyFlag &= ~flags.colorDirty; + this._dirtyFlag &= ~flags.colorDirty; + } + + if (opacityDirty) { + spriteCmd._syncDisplayOpacity(); + spriteCmd._dirtyFlag &= ~flags.opacityDirty; + this._dirtyFlag &= ~flags.opacityDirty; + } + + if (colorDirty || opacityDirty) { + spriteCmd._updateColor(); + } + + if (locFlag & flags.transformDirty) { + //update the transform + this.transform(parentCmd); + } + + if (locFlag & flags.orderDirty) { + this._dirtyFlag &= ~flags.orderDirty; + } + }; + + proto.updateStatus = function () { + var node = this._node; + if (!node._sprite) + return; + var flags = cc.Node._dirtyFlags, locFlag = this._dirtyFlag; + var spriteCmd = node._sprite._renderCmd; + var spriteFlag = spriteCmd._dirtyFlag; + + var colorDirty = spriteFlag & flags.colorDirty, + opacityDirty = spriteFlag & flags.opacityDirty; + + if (colorDirty) { + spriteCmd._updateDisplayColor(); + spriteCmd._dirtyFlag = spriteCmd._dirtyFlag & flags.colorDirty ^ spriteCmd._dirtyFlag; + this._dirtyFlag = this._dirtyFlag & flags.colorDirty ^ this._dirtyFlag; + } + + if (opacityDirty) { + spriteCmd._updateDisplayOpacity(); + spriteCmd._dirtyFlag = spriteCmd._dirtyFlag & flags.opacityDirty ^ spriteCmd._dirtyFlag; + this._dirtyFlag = this._dirtyFlag & flags.opacityDirty ^ this._dirtyFlag; + } + + if (colorDirty || opacityDirty) { + spriteCmd._updateColor(); + } + + if (locFlag & flags.transformDirty) { + //update the transform + this.transform(this.getParentRenderCmd(), true); + } + if(locFlag & flags.contentDirty) { + this._notifyRegionStatus && this._notifyRegionStatus(cc.Node.CanvasRenderCmd.RegionStatus.Dirty); + } + this._dirtyFlag = 0; + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/progress-timer/CCProgressTimerWebGLRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/progress-timer/CCProgressTimerWebGLRenderCmd.js new file mode 100644 index 0000000..8a89913 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/progress-timer/CCProgressTimerWebGLRenderCmd.js @@ -0,0 +1,542 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * cc.ProgressTimer's rendering objects of WebGL + */ +(function () { + var MAX_VERTEX_COUNT = 8; + + cc.ProgressTimer.WebGLRenderCmd = function (renderableObject) { + this._rootCtor(renderableObject); + this._needDraw = true; + this._progressDirty = true; + + this._bl = cc.p(); + this._tr = cc.p(); + this._transformUpdating = false; + + this.initCmd(); + }; + + var proto = cc.ProgressTimer.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + proto.constructor = cc.ProgressTimer.WebGLRenderCmd; + + proto.transform = function (parentCmd, recursive) { + this.originTransform(parentCmd, recursive); + var sp = this._node._sprite; + sp._renderCmd.transform(this, recursive); + + var lx = sp._offsetPosition.x, rx = lx + sp._rect.width, + by = sp._offsetPosition.y, ty = by + sp._rect.height, + wt = this._worldTransform; + this._bl.x = lx * wt.a + by * wt.c + wt.tx; + this._bl.y = lx * wt.b + by * wt.d + wt.ty; + this._tr.x = rx * wt.a + ty * wt.c + wt.tx; + this._tr.y = rx * wt.b + ty * wt.d + wt.ty; + + this._transformUpdating = true; + this._updateProgressData(); + this._transformUpdating = false; + }; + + proto.rendering = function (ctx) { + var node = this._node; + var context = ctx || cc._renderContext; + if (this._vertexDataCount === 0 || !node._sprite) + return; + + this._glProgramState.apply(); + this._shaderProgram._updateProjectionUniform(); + + var blendFunc = node._sprite._blendFunc; + cc.glBlendFunc(blendFunc.src, blendFunc.dst); + cc.glBindTexture2D(node._sprite.texture); + context.bindBuffer(context.ARRAY_BUFFER, this._vertexWebGLBuffer); + + context.enableVertexAttribArray(cc.VERTEX_ATTRIB_POSITION); + context.enableVertexAttribArray(cc.VERTEX_ATTRIB_COLOR); + context.enableVertexAttribArray(cc.VERTEX_ATTRIB_TEX_COORDS); + + if (this._vertexDataDirty) { + context.bufferSubData(context.ARRAY_BUFFER, 0, this._float32View); + this._vertexDataDirty = false; + } + var locVertexDataLen = cc.V3F_C4B_T2F.BYTES_PER_ELEMENT; + context.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 3, context.FLOAT, false, locVertexDataLen, 0); + context.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, context.UNSIGNED_BYTE, true, locVertexDataLen, 12); + context.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, context.FLOAT, false, locVertexDataLen, 16); + + if (node._type === cc.ProgressTimer.TYPE_RADIAL) + context.drawArrays(context.TRIANGLE_FAN, 0, this._vertexDataCount); + else if (node._type === cc.ProgressTimer.TYPE_BAR) { + if (!node._reverseDirection) + context.drawArrays(context.TRIANGLE_STRIP, 0, this._vertexDataCount); + else { + context.drawArrays(context.TRIANGLE_STRIP, 0, this._vertexDataCount / 2); + context.drawArrays(context.TRIANGLE_STRIP, 4, this._vertexDataCount / 2); + // 2 draw calls + cc.g_NumberOfDraws++; + } + } + cc.g_NumberOfDraws++; + }; + + proto._syncStatus = function (parentCmd) { + var node = this._node; + if (!node._sprite) + return; + var flags = cc.Node._dirtyFlags, locFlag = this._dirtyFlag; + var parentNode = parentCmd ? parentCmd._node : null; + + if (parentNode && parentNode._cascadeColorEnabled && (parentCmd._dirtyFlag & flags.colorDirty)) + locFlag |= flags.colorDirty; + if (parentNode && parentNode._cascadeOpacityEnabled && (parentCmd._dirtyFlag & flags.opacityDirty)) + locFlag |= flags.opacityDirty; + if (parentCmd && (parentCmd._dirtyFlag & flags.transformDirty)) + locFlag |= flags.transformDirty; + this._dirtyFlag = locFlag; + + var spriteCmd = node._sprite._renderCmd; + var spriteFlag = spriteCmd._dirtyFlag; + + var colorDirty = (locFlag | spriteFlag) & flags.colorDirty, + opacityDirty = (locFlag | spriteFlag) & flags.opacityDirty; + + if (colorDirty) { + spriteCmd._syncDisplayColor(); + spriteCmd._dirtyFlag &= ~flags.colorDirty; + this._dirtyFlag &= ~flags.colorDirty; + } + + if (opacityDirty) { + spriteCmd._syncDisplayOpacity(); + spriteCmd._dirtyFlag &= ~flags.opacityDirty; + this._dirtyFlag &= ~flags.opacityDirty; + } + + if (colorDirty || opacityDirty) { + this._updateColor(); + } + + if (locFlag & flags.transformDirty) { + //update the transform + this.transform(parentCmd); + } + + if (locFlag & flags.textureDirty) { + this._updateProgressData(); + this._dirtyFlag &= ~flags.textureDirty; + } + + spriteCmd._dirtyFlag = 0; + }; + + proto.updateStatus = function () { + var node = this._node; + if (!node._sprite) + return; + var flags = cc.Node._dirtyFlags, locFlag = this._dirtyFlag; + var spriteCmd = node._sprite._renderCmd; + var spriteFlag = spriteCmd._dirtyFlag; + + var colorDirty = (locFlag | spriteFlag) & flags.colorDirty, + opacityDirty = (locFlag | spriteFlag) & flags.opacityDirty; + + if (colorDirty) { + spriteCmd._updateDisplayColor(); + spriteCmd._dirtyFlag = spriteCmd._dirtyFlag & flags.colorDirty ^ spriteCmd._dirtyFlag; + this._dirtyFlag = this._dirtyFlag & flags.colorDirty ^ this._dirtyFlag; + } + + if (opacityDirty) { + spriteCmd._updateDisplayOpacity(); + spriteCmd._dirtyFlag = spriteCmd._dirtyFlag & flags.opacityDirty ^ spriteCmd._dirtyFlag; + this._dirtyFlag = this._dirtyFlag & flags.opacityDirty ^ this._dirtyFlag; + } + + if (colorDirty || opacityDirty) { + this._updateColor(); + } + + if (locFlag & flags.transformDirty) { + //update the transform + this.transform(this.getParentRenderCmd(), true); + } + + if (locFlag & flags.orderDirty) { + this._dirtyFlag = this._dirtyFlag & flags.orderDirty ^ this._dirtyFlag; + } + + if (locFlag & flags.textureDirty) { + this._updateProgressData(); + this._dirtyFlag = this._dirtyFlag & flags.textureDirty ^ this._dirtyFlag; + } + }; + + proto.releaseData = function () { + if (this._vertexData) { + //release all previous information + var webglBuffer = this._vertexWebGLBuffer; + setTimeout(function () { + cc._renderContext.deleteBuffer(webglBuffer); + }, 0.1); + this._vertexWebGLBuffer = null; + this._vertexData = null; + this._float32View = null; + this._vertexArrayBuffer = null; + this._vertexDataCount = 0; + } + }; + + proto.initCmd = function () { + if (!this._vertexData) { + this._vertexWebGLBuffer = cc._renderContext.createBuffer(); + + var vertexDataLen = cc.V3F_C4B_T2F.BYTES_PER_ELEMENT; + this._vertexArrayBuffer = new ArrayBuffer(MAX_VERTEX_COUNT * vertexDataLen); + this._float32View = new Float32Array(this._vertexArrayBuffer); + this._vertexData = []; + for (var i = 0; i < MAX_VERTEX_COUNT; i++) { + this._vertexData[i] = new cc.V3F_C4B_T2F(null, null, null, this._vertexArrayBuffer, i * vertexDataLen); + } + + // Init buffer data + gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexWebGLBuffer); + gl.bufferData(gl.ARRAY_BUFFER, this._float32View, gl.DYNAMIC_DRAW); + + this._vertexDataCount = 0; + this._vertexDataDirty = true; + + //shader program + this._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_SPRITE_POSITION_TEXTURECOLOR); + } + }; + + proto.resetVertexData = function () { + this._vertexDataCount = 0; + }; + + proto._updateProgressData = function () { + var node = this._node; + var locType = node._type; + if (locType === cc.ProgressTimer.TYPE_RADIAL) + this._updateRadial(); + else if (locType === cc.ProgressTimer.TYPE_BAR) + this._updateBar(); + this._vertexDataDirty = true; + }; + + proto._updateProgress = function () { + this.setDirtyFlag(cc.Node._dirtyFlags.textureDirty); + }; + + /** + *

+ * Update does the work of mapping the texture onto the triangles for the bar
+ * It now doesn't occur the cost of free/alloc data every update cycle.
+ * It also only changes the percentage point but no other points if they have not been modified.
+ *
+ * It now deals with flipped texture. If you run into this problem, just use the
+ * sprite property and enable the methods flipX, flipY.
+ *

+ * @private + */ + proto._updateBar = function () { + var node = this._node; + if (!node._sprite) + return; + + var i, alpha = node._percentage / 100.0; + var locBarChangeRate = node._barChangeRate; + var alphaOffset = cc.pMult(cc.p((1.0 - locBarChangeRate.x) + alpha * locBarChangeRate.x, + (1.0 - locBarChangeRate.y) + alpha * locBarChangeRate.y), 0.5); + var min = cc.pSub(node._midPoint, alphaOffset), max = cc.pAdd(node._midPoint, alphaOffset); + + if (min.x < 0) { + max.x += -min.x; + min.x = 0; + } + + if (max.x > 1) { + min.x -= max.x - 1; + max.x = 1; + } + + if (min.y < 0) { + max.y += -min.y; + min.y = 0; + } + + if (max.y > 1) { + min.y -= max.y - 1; + max.y = 1; + } + + var locVertexData; + if (!this._reverseDirection) { + if (!this._vertexDataCount) { + this._vertexDataCount = 4; + } + locVertexData = this._vertexData; + // TOPLEFT + this._textureCoordFromAlphaPoint(locVertexData[0].texCoords, min.x, max.y); + this._vertexFromAlphaPoint(locVertexData[0].vertices, min.x, max.y); + + // BOTLEFT + this._textureCoordFromAlphaPoint(locVertexData[1].texCoords, min.x, min.y); + this._vertexFromAlphaPoint(locVertexData[1].vertices, min.x, min.y); + + // TOPRIGHT + this._textureCoordFromAlphaPoint(locVertexData[2].texCoords, max.x, max.y); + this._vertexFromAlphaPoint(locVertexData[2].vertices, max.x, max.y); + + // BOTRIGHT + this._textureCoordFromAlphaPoint(locVertexData[3].texCoords, max.x, min.y); + this._vertexFromAlphaPoint(locVertexData[3].vertices, max.x, min.y); + } else { + locVertexData = this._vertexData; + if (!this._vertexDataCount) { + this._vertexDataCount = 8; + // TOPLEFT 1 + this._textureCoordFromAlphaPoint(locVertexData[0].texCoords, 0, 1); + this._vertexFromAlphaPoint(locVertexData[0].vertices, 0, 1); + + // BOTLEFT 1 + this._textureCoordFromAlphaPoint(locVertexData[1].texCoords, 0, 0); + this._vertexFromAlphaPoint(locVertexData[1].vertices, 0, 0); + + // TOPRIGHT 2 + this._textureCoordFromAlphaPoint(locVertexData[6].texCoords, 1, 1); + this._vertexFromAlphaPoint(locVertexData[6].vertices, 1, 1); + + // BOTRIGHT 2 + this._textureCoordFromAlphaPoint(locVertexData[7].texCoords, 1, 0); + this._vertexFromAlphaPoint(locVertexData[7].vertices, 1, 0); + } + + // TOPRIGHT 1 + this._textureCoordFromAlphaPoint(locVertexData[2].texCoords, min.x, max.y); + this._vertexFromAlphaPoint(locVertexData[2].vertices, min.x, max.y); + + // BOTRIGHT 1 + this._textureCoordFromAlphaPoint(locVertexData[3].texCoords, min.x, min.y); + this._vertexFromAlphaPoint(locVertexData[3].vertices, min.x, min.y); + + // TOPLEFT 2 + this._textureCoordFromAlphaPoint(locVertexData[4].texCoords, max.x, max.y); + this._vertexFromAlphaPoint(locVertexData[4].vertices, max.x, max.y); + + // BOTLEFT 2 + this._textureCoordFromAlphaPoint(locVertexData[5].texCoords, max.x, min.y); + this._vertexFromAlphaPoint(locVertexData[5].vertices, max.x, min.y); + } + this._updateColor(); + }; + + /** + *

+ * Update does the work of mapping the texture onto the triangles
+ * It now doesn't occur the cost of free/alloc data every update cycle.
+ * It also only changes the percentage point but no other points if they have not been modified.
+ *
+ * It now deals with flipped texture. If you run into this problem, just use the
+ * sprite property and enable the methods flipX, flipY.
+ *

+ * @private + */ + proto._updateRadial = function () { + var node = this._node; + if (!node._sprite) + return; + + var i, locMidPoint = node._midPoint; + var alpha = node._percentage / 100; + var angle = 2 * (cc.PI) * ( node._reverseDirection ? alpha : 1.0 - alpha); + + // We find the vector to do a hit detection based on the percentage + // We know the first vector is the one @ 12 o'clock (top,mid) so we rotate + // from that by the progress angle around the m_tMidpoint pivot + var topMid = cc.p(locMidPoint.x, 1); + var percentagePt = cc.pRotateByAngle(topMid, locMidPoint, angle); + + var index = 0; + var hit; + + if (alpha === 0) { + // More efficient since we don't always need to check intersection + // If the alpha is zero then the hit point is top mid and the index is 0. + hit = topMid; + index = 0; + } else if (alpha === 1) { + // More efficient since we don't always need to check intersection + // If the alpha is one then the hit point is top mid and the index is 4. + hit = topMid; + index = 4; + } else { + // We run a for loop checking the edges of the texture to find the + // intersection point + // We loop through five points since the top is split in half + + var min_t = cc.FLT_MAX; + var locProTextCoordsCount = cc.ProgressTimer.TEXTURE_COORDS_COUNT; + for (i = 0; i <= locProTextCoordsCount; ++i) { + var pIndex = (i + (locProTextCoordsCount - 1)) % locProTextCoordsCount; + + var edgePtA = this._boundaryTexCoord(i % locProTextCoordsCount); + var edgePtB = this._boundaryTexCoord(pIndex); + + // Remember that the top edge is split in half for the 12 o'clock position + // Let's deal with that here by finding the correct endpoints + if (i === 0) + edgePtB = cc.pLerp(edgePtA, edgePtB, 1 - locMidPoint.x); + else if (i === 4) + edgePtA = cc.pLerp(edgePtA, edgePtB, 1 - locMidPoint.x); + + // retPoint are returned by ccpLineIntersect + var retPoint = cc.p(0, 0); + if (cc.pLineIntersect(edgePtA, edgePtB, locMidPoint, percentagePt, retPoint)) { + // Since our hit test is on rays we have to deal with the top edge + // being in split in half so we have to test as a segment + if ((i === 0 || i === 4)) { + // s represents the point between edgePtA--edgePtB + if (!(0 <= retPoint.x && retPoint.x <= 1)) + continue; + } + // As long as our t isn't negative we are at least finding a + // correct hitpoint from m_tMidpoint to percentagePt. + if (retPoint.y >= 0) { + // Because the percentage line and all the texture edges are + // rays we should only account for the shortest intersection + if (retPoint.y < min_t) { + min_t = retPoint.y; + index = i; + } + } + } + } + + // Now that we have the minimum magnitude we can use that to find our intersection + hit = cc.pAdd(locMidPoint, cc.pMult(cc.pSub(percentagePt, locMidPoint), min_t)); + } + + // The size of the vertex data is the index from the hitpoint + // the 3 is for the m_tMidpoint, 12 o'clock point and hitpoint position. + var sameIndexCount = true; + if (this._vertexDataCount !== index + 3) { + sameIndexCount = false; + this._vertexDataCount = index + 3; + } + + this._updateColor(); + + var locVertexData = this._vertexData; + if (this._transformUpdating || !sameIndexCount) { + // First we populate the array with the m_tMidpoint, then all + // vertices/texcoords/colors of the 12 'o clock start and edges and the hitpoint + this._textureCoordFromAlphaPoint(locVertexData[0].texCoords, locMidPoint.x, locMidPoint.y); + this._vertexFromAlphaPoint(locVertexData[0].vertices, locMidPoint.x, locMidPoint.y); + + this._textureCoordFromAlphaPoint(locVertexData[1].texCoords, topMid.x, topMid.y); + this._vertexFromAlphaPoint(locVertexData[1].vertices, topMid.x, topMid.y); + + for (i = 0; i < index; i++) { + var alphaPoint = this._boundaryTexCoord(i); + this._textureCoordFromAlphaPoint(locVertexData[i + 2].texCoords, alphaPoint.x, alphaPoint.y); + this._vertexFromAlphaPoint(locVertexData[i + 2].vertices, alphaPoint.x, alphaPoint.y); + } + } + + // hitpoint will go last + this._textureCoordFromAlphaPoint(locVertexData[this._vertexDataCount - 1].texCoords, hit.x, hit.y); + this._vertexFromAlphaPoint(locVertexData[this._vertexDataCount - 1].vertices, hit.x, hit.y); + }; + + proto._boundaryTexCoord = function (index) { + if (index < cc.ProgressTimer.TEXTURE_COORDS_COUNT) { + var locProTextCoords = cc.ProgressTimer.TEXTURE_COORDS; + if (this._node._reverseDirection) + return cc.p((locProTextCoords >> (7 - (index << 1))) & 1, (locProTextCoords >> (7 - ((index << 1) + 1))) & 1); + else + return cc.p((locProTextCoords >> ((index << 1) + 1)) & 1, (locProTextCoords >> (index << 1)) & 1); + } + return cc.p(0, 0); + }; + + proto._textureCoordFromAlphaPoint = function (coords, ax, ay) { + var locSprite = this._node._sprite; + if (!locSprite) { + coords.u = 0; + coords.v = 0; + return; + } + var uvs = locSprite._renderCmd._vertices, + bl = uvs[1], + tr = uvs[2]; + var min = cc.p(bl.u, bl.v); + var max = cc.p(tr.u, tr.v); + + // Fix bug #1303 so that progress timer handles sprite frame texture rotation + if (locSprite.textureRectRotated) { + var temp = ax; + ax = ay; + ay = temp; + } + coords.u = min.x * (1 - ax) + max.x * ax; + coords.v = min.y * (1 - ay) + max.y * ay; + }; + + proto._vertexFromAlphaPoint = function (vertex, ax, ay) { + vertex.x = this._bl.x * (1 - ax) + this._tr.x * ax; + vertex.y = this._bl.y * (1 - ay) + this._tr.y * ay; + vertex.z = this._node._vertexZ; + }; + + proto._updateColor = function () { + var sp = this._node._sprite; + if (!this._vertexDataCount || !sp) + return; + + var color = this._displayedColor; + var spColor = sp._renderCmd._displayedColor; + var r = spColor.r; + var g = spColor.g; + var b = spColor.b; + var a = sp._renderCmd._displayedOpacity / 255; + if (sp._opacityModifyRGB) { + r *= a; + g *= a; + b *= a; + } + color.r = r; + color.g = g; + color.b = b; + color.a = sp._renderCmd._displayedOpacity; + var locVertexData = this._vertexData; + for (var i = 0, len = this._vertexDataCount; i < len; ++i) { + locVertexData[i].colors = color; + } + this._vertexDataDirty = true; + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/render-texture/CCRenderTexture.js b/frameworks/cocos2d-html5/cocos2d/render-texture/CCRenderTexture.js new file mode 100644 index 0000000..5cc00ae --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/render-texture/CCRenderTexture.js @@ -0,0 +1,425 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2009 Jason Booth + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * enum for jpg + * @constant + * @type Number + */ +cc.IMAGE_FORMAT_JPEG = 0; +/** + * enum for png + * @constant + * @type Number + */ +cc.IMAGE_FORMAT_PNG = 1; +/** + * enum for raw + * @constant + * @type Number + */ +cc.IMAGE_FORMAT_RAWDATA = 9; + +/** + * @param {Number} x + * @return {Number} + * Constructor + */ +cc.NextPOT = function (x) { + x = x - 1; + x = x | (x >> 1); + x = x | (x >> 2); + x = x | (x >> 4); + x = x | (x >> 8); + x = x | (x >> 16); + return x + 1; +}; + +/** + * cc.RenderTexture is a generic rendering target. To render things into it,
+ * simply construct a render target, call begin on it, call visit on any cocos
+ * scenes or objects to render them, and call end. For convenience, render texture
+ * adds a sprite as it's display child with the results, so you can simply add
+ * the render texture to your scene and treat it like any other CocosNode.
+ * There are also functions for saving the render texture to disk in PNG or JPG format. + * @class + * @extends cc.Node + * + * @property {cc.Sprite} sprite - The sprite. + * @property {cc.Sprite} clearFlags - Code for "auto" update. + * @property {Number} clearDepthVal - Clear depth value. + * @property {Boolean} autoDraw - Indicate auto draw mode activate or not. + * @property {Number} clearStencilVal - Clear stencil value. + * @property {cc.Color} clearColorVal - Clear color value, valid only when "autoDraw" is true. + */ +cc.RenderTexture = cc.Node.extend(/** @lends cc.RenderTexture# */{ + sprite: null, + + // + //

Code for "auto" update
+ // Valid flags: GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT, GL_STENCIL_BUFFER_BIT.
+ // They can be OR'ed. Valid when "autoDraw is YES.

+ // @public + // + clearFlags: 0, + + clearDepthVal: 0, + autoDraw: false, + + _texture: null, + _pixelFormat: 0, + + clearStencilVal: 0, + _clearColor: null, + + _className: "RenderTexture", + + /** + * creates a RenderTexture object with width and height in Points and a pixel format, only RGB and RGBA formats are valid + * Constructor of cc.RenderTexture for Canvas + * @param {Number} width + * @param {Number} height + * @param {cc.IMAGE_FORMAT_JPEG|cc.IMAGE_FORMAT_PNG|cc.IMAGE_FORMAT_RAWDATA} format + * @param {Number} depthStencilFormat + * @example + * // Example + * var rt = new cc.RenderTexture(width, height, format, depthStencilFormat) + * @function + */ + ctor: function (width, height, format, depthStencilFormat) { + cc.Node.prototype.ctor.call(this); + this._cascadeColorEnabled = true; + this._cascadeOpacityEnabled = true; + this._pixelFormat = cc.Texture2D.PIXEL_FORMAT_RGBA8888; + this._clearColor = new cc.Color(0, 0, 0, 255); + + if (width !== undefined && height !== undefined) { + format = format || cc.Texture2D.PIXEL_FORMAT_RGBA8888; + depthStencilFormat = depthStencilFormat || 0; + this.initWithWidthAndHeight(width, height, format, depthStencilFormat); + } + this.setAnchorPoint(0, 0); + }, + + _createRenderCmd: function () { + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return new cc.RenderTexture.CanvasRenderCmd(this); + else + return new cc.RenderTexture.WebGLRenderCmd(this); + }, + + visit: function (parent) { + var cmd = this._renderCmd, parentCmd = parent ? parent._renderCmd : null; + + // quick return if not visible + if (!this._visible) { + cmd._propagateFlagsDown(parentCmd); + return; + } + + var renderer = cc.renderer; + + cmd.visit(parentCmd); + renderer.pushRenderCommand(cmd); + this.sprite.visit(this); + cmd._dirtyFlag = 0; + }, + + /** + * Clear RenderTexture. + * @function + */ + cleanup: function () { + cc.Node.prototype.onExit.call(this); + this._renderCmd.cleanup(); + }, + + /** + * Gets the sprite + * @return {cc.Sprite} + */ + getSprite: function () { + return this.sprite; + }, + + /** + * Set the sprite + * @param {cc.Sprite} sprite + */ + setSprite: function (sprite) { + this.sprite = sprite; + }, + + /** + * Used for grab part of screen to a texture. + * @param {cc.Point} rtBegin + * @param {cc.Rect} fullRect + * @param {cc.Rect} fullViewport + */ + setVirtualViewport: function (rtBegin, fullRect, fullViewport) { + this._renderCmd.setVirtualViewport(rtBegin, fullRect, fullViewport); + }, + + /** + * Initializes the instance of cc.RenderTexture + * @function + * @param {Number} width + * @param {Number} height + * @param {cc.IMAGE_FORMAT_JPEG|cc.IMAGE_FORMAT_PNG|cc.IMAGE_FORMAT_RAWDATA} [format] + * @param {Number} [depthStencilFormat] + * @return {Boolean} + */ + initWithWidthAndHeight: function (width, height, format, depthStencilFormat) { + return this._renderCmd.initWithWidthAndHeight(width, height, format, depthStencilFormat); + }, + + /** + * starts grabbing + * @function + */ + begin: function () { + cc.renderer._turnToCacheMode(this.__instanceId); + this._renderCmd.begin(); + }, + /** + * starts rendering to the texture while clearing the texture first.
+ * This is more efficient then calling -clear first and then -begin + * @param {Number} r red 0-255 + * @param {Number} g green 0-255 + * @param {Number} b blue 0-255 + * @param {Number} a alpha 0-255 0 is transparent + * @param {Number} [depthValue=] + * @param {Number} [stencilValue=] + */ + beginWithClear: function (r, g, b, a, depthValue, stencilValue) { + //todo: only for WebGL? + var gl = cc._renderContext; + depthValue = depthValue || gl.COLOR_BUFFER_BIT; + stencilValue = stencilValue || (gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + + this._beginWithClear(r, g, b, a, depthValue, stencilValue, (gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT)); + }, + + _beginWithClear: function (r, g, b, a, depthValue, stencilValue, flags) { + this.begin(); + this._renderCmd._beginWithClear(r, g, b, a, depthValue, stencilValue, flags); + }, + + /** + * ends grabbing + * @function + */ + end: function () { + this._renderCmd.end(); + }, + + /** + * clears the texture with a color + * @param {Number|cc.Rect} r red 0-255 + * @param {Number} g green 0-255 + * @param {Number} b blue 0-255 + * @param {Number} a alpha 0-255 + */ + clear: function (r, g, b, a) { + this.beginWithClear(r, g, b, a); + this.end(); + }, + + /** + * clears the texture with rect. + * @function + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + */ + clearRect: function (x, y, width, height) { + this._renderCmd.clearRect(x, y, width, height); + }, + + /** + * clears the texture with a specified depth value + * @function + * @param {Number} depthValue + */ + clearDepth: function (depthValue) { + this._renderCmd.clearDepth(depthValue); + }, + + /** + * clears the texture with a specified stencil value + * @function + * @param {Number} stencilValue + */ + clearStencil: function (stencilValue) { + this._renderCmd.clearStencil(stencilValue); + }, + + /** + * Valid flags: GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT, GL_STENCIL_BUFFER_BIT. They can be OR'ed. Valid when "autoDraw is YES. + * @return {Number} + */ + getClearFlags: function () { + return this.clearFlags; + }, + + /** + * Set the clearFlags + * @param {Number} clearFlags + */ + setClearFlags: function (clearFlags) { + this.clearFlags = clearFlags; + }, + + /** + * Clear color value. Valid only when "autoDraw" is true. + * @function + * @return {cc.Color} + */ + getClearColor: function () { + return this._clearColor; + }, + + /** + * Set the clear color value. Valid only when "autoDraw" is true. + * @function + * @param {cc.Color} clearColor The clear color + */ + setClearColor: function (clearColor) { + var locClearColor = this._clearColor; + locClearColor.r = clearColor.r; + locClearColor.g = clearColor.g; + locClearColor.b = clearColor.b; + locClearColor.a = clearColor.a; + this._renderCmd.updateClearColor(clearColor); + }, + + /** + * Value for clearDepth. Valid only when autoDraw is true. + * @return {Number} + */ + getClearDepth: function () { + return this.clearDepthVal; + }, + + /** + * Set value for clearDepth. Valid only when autoDraw is true. + * @param {Number} clearDepth + */ + setClearDepth: function (clearDepth) { + this.clearDepthVal = clearDepth; + }, + + /** + * Value for clear Stencil. Valid only when autoDraw is true + * @return {Number} + */ + getClearStencil: function () { + return this.clearStencilVal; + }, + + /** + * Set value for clear Stencil. Valid only when autoDraw is true + * @return {Number} + */ + setClearStencil: function (clearStencil) { + this.clearStencilVal = clearStencil; + }, + + /** + * When enabled, it will render its children into the texture automatically. Disabled by default for compatibility reasons.
+ * Will be enabled in the future. + * @return {Boolean} + */ + isAutoDraw: function () { + return this.autoDraw; + }, + + /** + * When enabled, it will render its children into the texture automatically. Disabled by default for compatibility reasons.
+ * Will be enabled in the future. + * @return {Boolean} + */ + setAutoDraw: function (autoDraw) { + this.autoDraw = autoDraw; + }, + + //---- some stub functions for jsb + /** + * saves the texture into a file using JPEG format. The file will be saved in the Documents folder. + * Returns YES if the operation is successful. + * (doesn't support in HTML5) + * @param {Number} filePath + * @param {Number} format + */ + saveToFile: function (filePath, format) { + cc.log("saveToFile isn't supported on Cocos2d-Html5"); + }, + + /** + * creates a new CCImage from with the texture's data. Caller is responsible for releasing it by calling delete. + * @return {*} + */ + newCCImage: function (flipImage) { + cc.log("saveToFile isn't supported on cocos2d-html5"); + return null; + }, + + /** + * Listen "come to background" message, and save render texture. It only has effect on Android. + * @param {cc.Class} obj + */ + listenToBackground: function (obj) { + }, + + /** + * Listen "come to foreground" message and restore the frame buffer object. It only has effect on Android. + * @param {cc.Class} obj + */ + listenToForeground: function (obj) { + } +}); + +var _p = cc.RenderTexture.prototype; +// Extended +/** @expose */ +_p.clearColorVal; +cc.defineGetterSetter(_p, "clearColorVal", _p.getClearColor, _p.setClearColor); + + +/** + * creates a RenderTexture object with width and height in Points and a pixel format, only RGB and RGBA formats are valid + * @deprecated since v3.0 please use new cc.RenderTexture() instead. + * @param {Number} width + * @param {Number} height + * @param {cc.IMAGE_FORMAT_JPEG|cc.IMAGE_FORMAT_PNG|cc.IMAGE_FORMAT_RAWDATA} format + * @param {Number} depthStencilFormat + * @return {cc.RenderTexture} + */ +cc.RenderTexture.create = function (width, height, format, depthStencilFormat) { + return new cc.RenderTexture(width, height, format, depthStencilFormat); +}; diff --git a/frameworks/cocos2d-html5/cocos2d/render-texture/CCRenderTextureCanvasRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/render-texture/CCRenderTextureCanvasRenderCmd.js new file mode 100644 index 0000000..c55f9e7 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/render-texture/CCRenderTextureCanvasRenderCmd.js @@ -0,0 +1,105 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + cc.RenderTexture.CanvasRenderCmd = function (renderableObject) { + this._rootCtor(renderableObject); + this._needDraw = false; + this._clearColorStr = "rgba(255,255,255,1)"; + + this._cacheCanvas = document.createElement('canvas'); + this._cacheContext = new cc.CanvasContextWrapper(this._cacheCanvas.getContext('2d')); + }; + + var proto = cc.RenderTexture.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + proto.constructor = cc.RenderTexture.CanvasRenderCmd; + + proto.cleanup = function () { + this._cacheContext = null; + this._cacheCanvas = null; + }; + + proto.clearStencil = function (stencilValue) { + }; + + proto.setVirtualViewport = function (rtBegin, fullRect, fullViewport) { + }; + + proto.updateClearColor = function (clearColor) { + this._clearColorStr = "rgba(" + (0 | clearColor.r) + "," + (0 | clearColor.g) + "," + (0 | clearColor.b) + "," + clearColor.a / 255 + ")"; + }; + + proto.initWithWidthAndHeight = function (width, height, format, depthStencilFormat) { + var node = this._node; + var locCacheCanvas = this._cacheCanvas, locScaleFactor = cc.contentScaleFactor(); + locCacheCanvas.width = 0 | (width * locScaleFactor); + locCacheCanvas.height = 0 | (height * locScaleFactor); + + var texture = new cc.Texture2D(); + texture.initWithElement(locCacheCanvas); + texture.handleLoadedTexture(); + + var locSprite = node.sprite = new cc.Sprite(texture); + locSprite.setBlendFunc(cc.ONE, cc.ONE_MINUS_SRC_ALPHA); + // Disabled by default. + node.autoDraw = false; + // add sprite for backward compatibility + node.addChild(locSprite); + return true; + }; + + proto.begin = function () { + }; + + proto._beginWithClear = function (r, g, b, a, depthValue, stencilValue, flags) { + r = r || 0; + g = g || 0; + b = b || 0; + a = isNaN(a) ? 255 : a; + + var context = this._cacheContext.getContext(); + var locCanvas = this._cacheCanvas; + context.setTransform(1, 0, 0, 1, 0, 0); + this._cacheContext.setFillStyle("rgba(" + (0 | r) + "," + (0 | g) + "," + (0 | b) + "," + a / 255 + ")"); + context.clearRect(0, 0, locCanvas.width, locCanvas.height); + context.fillRect(0, 0, locCanvas.width, locCanvas.height); + }; + + proto.end = function () { + var node = this._node; + + var scale = cc.contentScaleFactor(); + cc.renderer._renderingToCacheCanvas(this._cacheContext, node.__instanceId, scale, scale); + var spriteRenderCmd = node.sprite._renderCmd; + spriteRenderCmd._notifyRegionStatus && spriteRenderCmd._notifyRegionStatus(cc.Node.CanvasRenderCmd.RegionStatus.Dirty); + }; + + proto.clearRect = function (x, y, width, height) { + this._cacheContext.clearRect(x, y, width, -height); + }; + + proto.clearDepth = function (depthValue) { + cc.log("clearDepth isn't supported on Cocos2d-Html5"); + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/render-texture/CCRenderTextureWebGLRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/render-texture/CCRenderTextureWebGLRenderCmd.js new file mode 100644 index 0000000..9abe91f --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/render-texture/CCRenderTextureWebGLRenderCmd.js @@ -0,0 +1,366 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + cc.RenderTexture.WebGLRenderCmd = function (renderableObject) { + this._rootCtor(renderableObject); + this._needDraw = true; + + this._fBO = null; + this._oldFBO = null; + this._textureCopy = null; + this._depthRenderBuffer = null; + + this._rtTextureRect = new cc.Rect(); + this._fullRect = new cc.Rect(); + this._fullViewport = new cc.Rect(); + }; + + var proto = cc.RenderTexture.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + proto.constructor = cc.RenderTexture.WebGLRenderCmd; + + proto.setVirtualViewport = function (rtBegin, fullRect, fullViewport) { + this._rtTextureRect.x = rtBegin.x; + this._rtTextureRect.y = rtBegin.y; + + this._fullRect = fullRect; + this._fullViewport = fullViewport; + }; + + proto.needDraw = function () { + return this._needDraw && this._node.autoDraw; + }; + + proto.rendering = function (ctx) { + var gl = ctx || cc._renderContext; + var node = this._node; + if (node.autoDraw) { + node.begin(); + + var locClearFlags = node.clearFlags; + if (locClearFlags) { + var oldClearColor = [0.0, 0.0, 0.0, 0.0]; + var oldDepthClearValue = 0.0; + var oldStencilClearValue = 0; + + // backup and set + if (locClearFlags & gl.COLOR_BUFFER_BIT) { + oldClearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE); + gl.clearColor(node._clearColor.r / 255, node._clearColor.g / 255, node._clearColor.b / 255, node._clearColor.a / 255); + } + + if (locClearFlags & gl.DEPTH_BUFFER_BIT) { + oldDepthClearValue = gl.getParameter(gl.DEPTH_CLEAR_VALUE); + gl.clearDepth(node.clearDepthVal); + } + + if (locClearFlags & gl.STENCIL_BUFFER_BIT) { + oldStencilClearValue = gl.getParameter(gl.STENCIL_CLEAR_VALUE); + gl.clearStencil(node.clearStencilVal); + } + + // clear + gl.clear(locClearFlags); + + // restore + if (locClearFlags & gl.COLOR_BUFFER_BIT) + gl.clearColor(oldClearColor[0], oldClearColor[1], oldClearColor[2], oldClearColor[3]); + + if (locClearFlags & gl.DEPTH_BUFFER_BIT) + gl.clearDepth(oldDepthClearValue); + + if (locClearFlags & gl.STENCIL_BUFFER_BIT) + gl.clearStencil(oldStencilClearValue); + } + + //! make sure all children are drawn + node.sortAllChildren(); + var locChildren = node._children; + for (var i = 0; i < locChildren.length; i++) { + var getChild = locChildren[i]; + if (getChild !== node.sprite) { + getChild.visit(node.sprite); //TODO it's very Strange + } + } + node.end(); + } + }; + + proto.clearStencil = function (stencilValue) { + var gl = cc._renderContext; + // save old stencil value + var stencilClearValue = gl.getParameter(gl.STENCIL_CLEAR_VALUE); + + gl.clearStencil(stencilValue); + gl.clear(gl.STENCIL_BUFFER_BIT); + + // restore clear color + gl.clearStencil(stencilClearValue); + }; + + proto.cleanup = function () { + var node = this._node; + //node.sprite = null; + this._textureCopy = null; + + var gl = cc._renderContext; + gl.deleteFramebuffer(this._fBO); + if (this._depthRenderBuffer) + gl.deleteRenderbuffer(this._depthRenderBuffer); + }; + + proto.updateClearColor = function (clearColor) { + }; + + proto.initWithWidthAndHeight = function (width, height, format, depthStencilFormat) { + var node = this._node; + if (format === cc.Texture2D.PIXEL_FORMAT_A8) + cc.log("cc.RenderTexture._initWithWidthAndHeightForWebGL() : only RGB and RGBA formats are valid for a render texture;"); + + var gl = cc._renderContext, locScaleFactor = cc.contentScaleFactor(); + this._fullRect = new cc.Rect(0, 0, width, height); + this._fullViewport = new cc.Rect(0, 0, width, height); + + width = 0 | (width * locScaleFactor); + height = 0 | (height * locScaleFactor); + + this._oldFBO = gl.getParameter(gl.FRAMEBUFFER_BINDING); + + // textures must be power of two squared + var powW, powH; + + if (cc.configuration.supportsNPOT()) { + powW = width; + powH = height; + } else { + powW = cc.NextPOT(width); + powH = cc.NextPOT(height); + } + + //void *data = malloc(powW * powH * 4); + var dataLen = powW * powH * 4; + var data = new Uint8Array(dataLen); + //memset(data, 0, (int)(powW * powH * 4)); + for (var i = 0; i < powW * powH * 4; i++) + data[i] = 0; + + this._pixelFormat = format; + + var locTexture = node._texture = new cc.Texture2D(); + if (!node._texture) + return false; + + locTexture.initWithData(data, node._pixelFormat, powW, powH, cc.size(width, height)); + //free( data ); + + var oldRBO = gl.getParameter(gl.RENDERBUFFER_BINDING); + + if (cc.configuration.checkForGLExtension("GL_QCOM")) { + this._textureCopy = new cc.Texture2D(); + if (!this._textureCopy) + return false; + this._textureCopy.initWithData(data, node._pixelFormat, powW, powH, cc.size(width, height)); + } + + // generate FBO + this._fBO = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, this._fBO); + + // associate texture with FBO + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, locTexture._webTextureObj, 0); + + if (depthStencilFormat !== 0) { + //create and attach depth buffer + this._depthRenderBuffer = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, this._depthRenderBuffer); + gl.renderbufferStorage(gl.RENDERBUFFER, depthStencilFormat, powW, powH); + if (depthStencilFormat === gl.DEPTH_STENCIL) + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, this._depthRenderBuffer); + else if (depthStencilFormat === gl.STENCIL_INDEX || depthStencilFormat === gl.STENCIL_INDEX8) + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, this._depthRenderBuffer); + else if (depthStencilFormat === gl.DEPTH_COMPONENT16) + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this._depthRenderBuffer); + } + + // check if it worked (probably worth doing :) ) + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) + cc.log("Could not attach texture to the framebuffer"); + + locTexture.setAliasTexParameters(); + + var locSprite = node.sprite = new cc.Sprite(locTexture); + locSprite.scaleY = -1; + locSprite.setBlendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); + + gl.bindRenderbuffer(gl.RENDERBUFFER, oldRBO); + gl.bindFramebuffer(gl.FRAMEBUFFER, this._oldFBO); + + // Disabled by default. + node.autoDraw = false; + + // add sprite for backward compatibility + node.addChild(locSprite); + return true; + }; + + proto.begin = function () { + var node = this._node; + // Save the current matrix + cc.kmGLMatrixMode(cc.KM_GL_PROJECTION); + cc.kmGLPushMatrix(); + cc.kmGLMatrixMode(cc.KM_GL_MODELVIEW); + cc.kmGLPushMatrix(); + + var gl = cc._renderContext; + + var director = cc.director; + director.setProjection(director.getProjection()); + + var texSize = node._texture.getContentSizeInPixels(); + + // Calculate the adjustment ratios based on the old and new projections + var size = cc.director.getWinSizeInPixels(); + var widthRatio = size.width / texSize.width; + var heightRatio = size.height / texSize.height; + + var orthoMatrix = cc.math.Matrix4.createOrthographicProjection(-1.0 / widthRatio, 1.0 / widthRatio, + -1.0 / heightRatio, 1.0 / heightRatio, -1, 1); + cc.kmGLMultMatrix(orthoMatrix); + + //calculate viewport + var viewport = new cc.Rect(0, 0, 0, 0); + viewport.width = this._fullViewport.width; + viewport.height = this._fullViewport.height; + var viewPortRectWidthRatio = viewport.width / this._fullRect.width; + var viewPortRectHeightRatio = viewport.height / this._fullRect.height; + viewport.x = (this._fullRect.x - this._rtTextureRect.x) * viewPortRectWidthRatio; + viewport.y = (this._fullRect.y - this._rtTextureRect.y) * viewPortRectHeightRatio; + gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height); + + this._oldFBO = gl.getParameter(gl.FRAMEBUFFER_BINDING); + gl.bindFramebuffer(gl.FRAMEBUFFER, this._fBO);//Will direct drawing to the frame buffer created above + + /* Certain Qualcomm Adreno gpu's will retain data in memory after a frame buffer switch which corrupts the render to the texture. + * The solution is to clear the frame buffer before rendering to the texture. However, calling glClear has the unintended result of clearing the current texture. + * Create a temporary texture to overcome this. At the end of CCRenderTexture::begin(), switch the attached texture to the second one, call glClear, + * and then switch back to the original texture. This solution is unnecessary for other devices as they don't have the same issue with switching frame buffers. + */ + if (cc.configuration.checkForGLExtension("GL_QCOM")) { + // -- bind a temporary texture so we can clear the render buffer without losing our texture + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this._textureCopy._webTextureObj, 0); + //cc.checkGLErrorDebug(); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, node._texture._webTextureObj, 0); + } + }; + + proto._beginWithClear = function (r, g, b, a, depthValue, stencilValue, flags) { + r = r / 255; + g = g / 255; + b = b / 255; + a = a / 255; + + var gl = cc._renderContext; + + // save clear color + var clearColor = [0.0, 0.0, 0.0, 0.0]; + var depthClearValue = 0.0; + var stencilClearValue = 0; + + if (flags & gl.COLOR_BUFFER_BIT) { + clearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE); + gl.clearColor(r, g, b, a); + } + + if (flags & gl.DEPTH_BUFFER_BIT) { + depthClearValue = gl.getParameter(gl.DEPTH_CLEAR_VALUE); + gl.clearDepth(depthValue); + } + + if (flags & gl.STENCIL_BUFFER_BIT) { + stencilClearValue = gl.getParameter(gl.STENCIL_CLEAR_VALUE); + gl.clearStencil(stencilValue); + } + + gl.clear(flags); + + // restore + if (flags & gl.COLOR_BUFFER_BIT) + gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + + if (flags & gl.DEPTH_BUFFER_BIT) + gl.clearDepth(depthClearValue); + + if (flags & gl.STENCIL_BUFFER_BIT) + gl.clearStencil(stencilClearValue); + }; + + proto.end = function () { + var node = this._node; + cc.renderer._renderingToBuffer(node.__instanceId); + + var gl = cc._renderContext; + var director = cc.director; + gl.bindFramebuffer(gl.FRAMEBUFFER, this._oldFBO); + + //restore viewport + director.setViewport(); + cc.kmGLMatrixMode(cc.KM_GL_PROJECTION); + cc.kmGLPopMatrix(); + cc.kmGLMatrixMode(cc.KM_GL_MODELVIEW); + cc.kmGLPopMatrix(); + + /* var size = director.getWinSizeInPixels(); + + // restore viewport + gl.viewport(0, 0, size.width * cc.contentScaleFactor(), size.height * cc.contentScaleFactor()); + + // special viewport for 3d projection + retina display + if (director.getProjection() == cc.Director.PROJECTION_3D && cc.contentScaleFactor() != 1) { + gl.viewport((-size.width / 2), (-size.height / 2), (size.width * cc.contentScaleFactor()), (size.height * cc.contentScaleFactor())); + } + + director.setProjection(director.getProjection());*/ + }; + + proto.clearRect = function (x, y, width, height) { + //TODO need to implement + }; + + proto.clearDepth = function (depthValue) { + var node = this._node; + node.begin(); + + var gl = cc._renderContext; + //! save old depth value + var depthClearValue = gl.getParameter(gl.DEPTH_CLEAR_VALUE); + + gl.clearDepth(depthValue); + gl.clear(gl.DEPTH_BUFFER_BIT); + + // restore clear color + gl.clearDepth(depthClearValue); + node.end(); + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/shaders/CCGLProgram.js b/frameworks/cocos2d-html5/cocos2d/shaders/CCGLProgram.js new file mode 100644 index 0000000..0cf9f47 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/shaders/CCGLProgram.js @@ -0,0 +1,902 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright 2011 Jeff Lamarche + Copyright 2012 Goffredo Marocchi + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * Class that implements a WebGL program + * @class + * @extends cc.Class + */ +cc.GLProgram = cc.Class.extend(/** @lends cc.GLProgram# */{ + _glContext: null, + _programObj: null, + _vertShader: null, + _fragShader: null, + _uniforms: null, + _hashForUniforms: null, + _usesTime: false, + _projectionUpdated: -1, + + // Uniform cache + _updateUniform: function (name) { + if (!name) + return false; + + var updated = false; + var element = this._hashForUniforms[name]; + var args; + if (Array.isArray(arguments[1])) { + args = arguments[1]; + } else { + args = new Array(arguments.length - 1); + for (var i = 1; i < arguments.length; i += 1) { + args[i - 1] = arguments[i]; + } + } + + if (!element || element.length !== args.length) { + this._hashForUniforms[name] = [].concat(args); + updated = true; + } else { + for (var i = 0; i < args.length; i += 1) { + // Array and Typed Array inner values could be changed, so we must update them + if (args[i] !== element[i] || typeof args[i] === 'object') { + element[i] = args[i]; + updated = true; + } + } + } + + return updated; + }, + + _description: function () { + return ""; + }, + + _compileShader: function (shader, type, source) { + if (!source || !shader) + return false; + + var preStr = cc.GLProgram._isHighpSupported() ? "precision highp float;\n" : "precision mediump float;\n"; + source = preStr + + "uniform mat4 CC_PMatrix; \n" + + "uniform mat4 CC_MVMatrix; \n" + + "uniform mat4 CC_MVPMatrix; \n" + + "uniform vec4 CC_Time; \n" + + "uniform vec4 CC_SinTime; \n" + + "uniform vec4 CC_CosTime; \n" + + "uniform vec4 CC_Random01; \n" + + "uniform sampler2D CC_Texture0; \n" + + "//CC INCLUDES END \n" + source; + + this._glContext.shaderSource(shader, source); + this._glContext.compileShader(shader); + var status = this._glContext.getShaderParameter(shader, this._glContext.COMPILE_STATUS); + + if (!status) { + cc.log("cocos2d: ERROR: Failed to compile shader:\n" + this._glContext.getShaderSource(shader)); + if (type === this._glContext.VERTEX_SHADER) + cc.log("cocos2d: \n" + this.vertexShaderLog()); + else + cc.log("cocos2d: \n" + this.fragmentShaderLog()); + } + return ( status === true ); + }, + + /** + * Create a cc.GLProgram object + * @param {String} vShaderFileName + * @param {String} fShaderFileName + * @returns {cc.GLProgram} + */ + ctor: function (vShaderFileName, fShaderFileName, glContext) { + this._uniforms = {}; + this._hashForUniforms = {}; + this._glContext = glContext || cc._renderContext; + + vShaderFileName && fShaderFileName && this.init(vShaderFileName, fShaderFileName); + }, + + /** + * destroy program + */ + destroyProgram: function () { + this._vertShader = null; + this._fragShader = null; + this._uniforms = null; + this._hashForUniforms = null; + + this._glContext.deleteProgram(this._programObj); + }, + + /** + * Initializes the cc.GLProgram with a vertex and fragment with string + * @param {String} vertShaderStr + * @param {String} fragShaderStr + * @return {Boolean} + */ + initWithVertexShaderByteArray: function (vertShaderStr, fragShaderStr) { + var locGL = this._glContext; + this._programObj = locGL.createProgram(); + //cc.checkGLErrorDebug(); + + this._vertShader = null; + this._fragShader = null; + + if (vertShaderStr) { + this._vertShader = locGL.createShader(locGL.VERTEX_SHADER); + if (!this._compileShader(this._vertShader, locGL.VERTEX_SHADER, vertShaderStr)) { + cc.log("cocos2d: ERROR: Failed to compile vertex shader"); + } + } + + // Create and compile fragment shader + if (fragShaderStr) { + this._fragShader = locGL.createShader(locGL.FRAGMENT_SHADER); + if (!this._compileShader(this._fragShader, locGL.FRAGMENT_SHADER, fragShaderStr)) { + cc.log("cocos2d: ERROR: Failed to compile fragment shader"); + } + } + + if (this._vertShader) + locGL.attachShader(this._programObj, this._vertShader); + cc.checkGLErrorDebug(); + + if (this._fragShader) + locGL.attachShader(this._programObj, this._fragShader); + + if (Object.keys(this._hashForUniforms).length > 0) this._hashForUniforms = {}; + + cc.checkGLErrorDebug(); + return true; + }, + + /** + * Initializes the cc.GLProgram with a vertex and fragment with string + * @param {String} vertShaderStr + * @param {String} fragShaderStr + * @return {Boolean} + */ + initWithString: function (vertShaderStr, fragShaderStr) { + return this.initWithVertexShaderByteArray(vertShaderStr, fragShaderStr); + }, + + /** + * Initializes the CCGLProgram with a vertex and fragment with contents of filenames + * @param {String} vShaderFilename + * @param {String} fShaderFileName + * @return {Boolean} + */ + initWithVertexShaderFilename: function (vShaderFilename, fShaderFileName) { + var vertexSource = cc.loader.getRes(vShaderFilename); + if (!vertexSource) throw new Error("Please load the resource firset : " + vShaderFilename); + var fragmentSource = cc.loader.getRes(fShaderFileName); + if (!fragmentSource) throw new Error("Please load the resource firset : " + fShaderFileName); + return this.initWithVertexShaderByteArray(vertexSource, fragmentSource); + }, + + /** + * Initializes the CCGLProgram with a vertex and fragment with contents of filenames + * @param {String} vShaderFilename + * @param {String} fShaderFileName + * @return {Boolean} + */ + init: function (vShaderFilename, fShaderFileName) { + return this.initWithVertexShaderFilename(vShaderFilename, fShaderFileName); + }, + + /** + * It will add a new attribute to the shader + * @param {String} attributeName + * @param {Number} index + */ + addAttribute: function (attributeName, index) { + this._glContext.bindAttribLocation(this._programObj, index, attributeName); + }, + + /** + * links the glProgram + * @return {Boolean} + */ + link: function () { + if (!this._programObj) { + cc.log("cc.GLProgram.link(): Cannot link invalid program"); + return false; + } + + this._glContext.linkProgram(this._programObj); + + if (this._vertShader) + this._glContext.deleteShader(this._vertShader); + if (this._fragShader) + this._glContext.deleteShader(this._fragShader); + + this._vertShader = null; + this._fragShader = null; + + if (cc.game.config[cc.game.CONFIG_KEY.debugMode]) { + var status = this._glContext.getProgramParameter(this._programObj, this._glContext.LINK_STATUS); + if (!status) { + cc.log("cocos2d: ERROR: Failed to link program: " + this._glContext.getProgramInfoLog(this._programObj)); + cc.glDeleteProgram(this._programObj); + this._programObj = null; + return false; + } + } + + return true; + }, + + /** + * it will call glUseProgram() + */ + use: function () { + cc.glUseProgram(this._programObj); + }, + + /** + * It will create 4 uniforms: + * cc.UNIFORM_PMATRIX + * cc.UNIFORM_MVMATRIX + * cc.UNIFORM_MVPMATRIX + * cc.UNIFORM_SAMPLER + */ + updateUniforms: function () { + this._addUniformLocation(cc.UNIFORM_PMATRIX_S); + this._addUniformLocation(cc.UNIFORM_MVMATRIX_S); + this._addUniformLocation(cc.UNIFORM_MVPMATRIX_S); + this._addUniformLocation(cc.UNIFORM_TIME_S); + this._addUniformLocation(cc.UNIFORM_SINTIME_S); + this._addUniformLocation(cc.UNIFORM_COSTIME_S); + this._addUniformLocation(cc.UNIFORM_RANDOM01_S); + this._addUniformLocation(cc.UNIFORM_SAMPLER_S); + this._usesTime = (this._uniforms[cc.UNIFORM_TIME_S] != null || this._uniforms[cc.UNIFORM_SINTIME_S] != null || this._uniforms[cc.UNIFORM_COSTIME_S] != null); + + this.use(); + // Since sample most probably won't change, set it to 0 now. + this.setUniformLocationWith1i(this._uniforms[cc.UNIFORM_SAMPLER_S], 0); + }, + + _addUniformLocation: function (name) { + var location = this._glContext.getUniformLocation(this._programObj, name); + if (location) location._name = name; + this._uniforms[name] = location; + return location; + }, + + /** + * calls retrieves the named uniform location for this shader program. + * @param {String} name + * @returns {Number} + */ + getUniformLocationForName: function (name) { + if (!name) + throw new Error("cc.GLProgram.getUniformLocationForName(): uniform name should be non-null"); + if (!this._programObj) + throw new Error("cc.GLProgram.getUniformLocationForName(): Invalid operation. Cannot get uniform location when program is not initialized"); + + var location = this._uniforms[name] || this._addUniformLocation(name); + return location; + }, + + /** + * get uniform MVP matrix + * @returns {WebGLUniformLocation} + */ + getUniformMVPMatrix: function () { + return this._uniforms[cc.UNIFORM_MVPMATRIX_S]; + }, + + /** + * get uniform sampler + * @returns {WebGLUniformLocation} + */ + getUniformSampler: function () { + return this._uniforms[cc.UNIFORM_SAMPLER_S]; + }, + + /** + * calls glUniform1i only if the values are different than the previous call for this same shader program. + * @param {WebGLUniformLocation|String} location + * @param {Number} i1 + */ + setUniformLocationWith1i: function (location, i1) { + var isString = typeof location === 'string'; + var name = isString ? location : location && location._name; + if (name) { + if (this._updateUniform(name, i1)) { + if (isString) location = this.getUniformLocationForName(name); + this._glContext.uniform1i(location, i1); + } + } else { + this._glContext.uniform1i(location, i1); + } + }, + + /** + * calls glUniform2i only if the values are different than the previous call for this same shader program. + * @param {WebGLUniformLocation|String} location + * @param {Number} i1 + * @param {Number} i2 + */ + setUniformLocationWith2i: function (location, i1, i2) { + var isString = typeof location === 'string'; + var name = isString ? location : location && location._name; + if (name) { + if (this._updateUniform(name, i1, i2)) { + if (isString) location = this.getUniformLocationForName(name); + this._glContext.uniform2i(location, i1, i2); + } + } else { + this._glContext.uniform2i(location, i1, i2); + } + }, + + /** + * calls glUniform3i only if the values are different than the previous call for this same shader program. + * @param {WebGLUniformLocation|String} location + * @param {Number} i1 + * @param {Number} i2 + * @param {Number} i3 + */ + setUniformLocationWith3i: function (location, i1, i2, i3) { + var isString = typeof location === 'string'; + var name = isString ? location : location && location._name; + if (name) { + if (this._updateUniform(name, i1, i2, i3)) { + if (isString) location = this.getUniformLocationForName(name); + this._glContext.uniform3i(location, i1, i2, i3); + } + } else { + this._glContext.uniform3i(location, i1, i2, i3); + } + }, + + /** + * calls glUniform4i only if the values are different than the previous call for this same shader program. + * @param {WebGLUniformLocation|String} location + * @param {Number} i1 + * @param {Number} i2 + * @param {Number} i3 + * @param {Number} i4 + */ + setUniformLocationWith4i: function (location, i1, i2, i3, i4) { + var isString = typeof location === 'string'; + var name = isString ? location : location && location._name; + if (name) { + if (this._updateUniform(name, i1, i2, i3, i4)) { + if (isString) location = this.getUniformLocationForName(name); + this._glContext.uniform4i(location, i1, i2, i3, i4); + } + } else { + this._glContext.uniform4i(location, i1, i2, i3, i4); + } + }, + + /** + * calls glUniform2iv + * @param {WebGLUniformLocation|String} location + * @param {Int32Array} intArray + * @param {Number} numberOfArrays + */ + setUniformLocationWith2iv: function (location, intArray) { + var isString = typeof location === 'string'; + var name = isString ? location : location && location._name; + if (name) { + if (this._updateUniform(name, intArray)) { + if (isString) location = this.getUniformLocationForName(name); + this._glContext.uniform2iv(location, intArray); + } + } else { + this._glContext.uniform2iv(location, intArray); + } + }, + + /** + * calls glUniform3iv + * @param {WebGLUniformLocation|String} location + * @param {Int32Array} intArray + */ + setUniformLocationWith3iv: function (location, intArray) { + var isString = typeof location === 'string'; + var name = isString ? location : location && location._name; + if (name) { + if (this._updateUniform(name, intArray)) { + if (isString) location = this.getUniformLocationForName(name); + this._glContext.uniform3iv(location, intArray); + } + } else { + this._glContext.uniform3iv(location, intArray); + } + }, + + /** + * calls glUniform4iv + * @param {WebGLUniformLocation|String} location + * @param {Int32Array} intArray + */ + setUniformLocationWith4iv: function (location, intArray) { + var isString = typeof location === 'string'; + var name = isString ? location : location && location._name; + if (name) { + if (this._updateUniform(name, intArray)) { + if (isString) location = this.getUniformLocationForName(name); + this._glContext.uniform4iv(location, intArray); + } + } else { + this._glContext.uniform4iv(location, intArray); + } + }, + + /** + * calls glUniform1i only if the values are different than the previous call for this same shader program. + * @param {WebGLUniformLocation|String} location + * @param {Number} i1 + */ + setUniformLocationI32: function (location, i1) { + this.setUniformLocationWith1i(location, i1); + }, + + /** + * calls glUniform1f only if the values are different than the previous call for this same shader program. + * @param {WebGLUniformLocation|String} location + * @param {Number} f1 + */ + setUniformLocationWith1f: function (location, f1) { + var isString = typeof location === 'string'; + var name = isString ? location : location && location._name; + if (name) { + if (this._updateUniform(name, f1)) { + if (isString) location = this.getUniformLocationForName(name); + this._glContext.uniform1f(location, f1); + } + } else { + this._glContext.uniform1f(location, f1); + } + }, + + /** + * calls glUniform2f only if the values are different than the previous call for this same shader program. + * @param {WebGLUniformLocation|String} location + * @param {Number} f1 + * @param {Number} f2 + */ + setUniformLocationWith2f: function (location, f1, f2) { + var isString = typeof location === 'string'; + var name = isString ? location : location && location._name; + if (name) { + if (this._updateUniform(name, f1, f2)) { + if (isString) location = this.getUniformLocationForName(name); + this._glContext.uniform2f(location, f1, f2); + } + } else { + this._glContext.uniform2f(location, f1, f2); + } + }, + + /** + * calls glUniform3f only if the values are different than the previous call for this same shader program. + * @param {WebGLUniformLocation|String} location + * @param {Number} f1 + * @param {Number} f2 + * @param {Number} f3 + */ + setUniformLocationWith3f: function (location, f1, f2, f3) { + var isString = typeof location === 'string'; + var name = isString ? location : location && location._name; + if (name) { + if (this._updateUniform(name, f1, f2, f3)) { + if (isString) location = this.getUniformLocationForName(name); + this._glContext.uniform3f(location, f1, f2, f3); + } + } else { + this._glContext.uniform3f(location, f1, f2, f3); + } + }, + + /** + * calls glUniform4f only if the values are different than the previous call for this same shader program. + * @param {WebGLUniformLocation|String} location + * @param {Number} f1 + * @param {Number} f2 + * @param {Number} f3 + * @param {Number} f4 + */ + setUniformLocationWith4f: function (location, f1, f2, f3, f4) { + var isString = typeof location === 'string'; + var name = isString ? location : location && location._name; + if (name) { + if (this._updateUniform(name, f1, f2, f3, f4)) { + if (isString) location = this.getUniformLocationForName(name); + this._glContext.uniform4f(location, f1, f2, f3, f4); + } + } else { + this._glContext.uniform4f(location, f1, f2, f3, f4); + cc.log('uniform4f', f1, f2, f3, f4); + } + }, + + /** + * calls glUniform2fv + * @param {WebGLUniformLocation|String} location + * @param {Float32Array} floatArray + */ + setUniformLocationWith2fv: function (location, floatArray) { + var isString = typeof location === 'string'; + var name = isString ? location : location && location._name; + if (name) { + if (this._updateUniform(name, floatArray)) { + if (isString) location = this.getUniformLocationForName(name); + this._glContext.uniform2fv(location, floatArray); + } + } else { + this._glContext.uniform2fv(location, floatArray); + } + }, + + /** + * calls glUniform3fv + * @param {WebGLUniformLocation|String} location + * @param {Float32Array} floatArray + */ + setUniformLocationWith3fv: function (location, floatArray) { + var isString = typeof location === 'string'; + var name = isString ? location : location && location._name; + if (name) { + if (this._updateUniform(name, floatArray)) { + if (isString) location = this.getUniformLocationForName(name); + this._glContext.uniform3fv(location, floatArray); + } + } else { + this._glContext.uniform3fv(location, floatArray); + } + }, + + /** + * calls glUniform4fv + * @param {WebGLUniformLocation|String} location + * @param {Float32Array} floatArray + */ + setUniformLocationWith4fv: function (location, floatArray) { + var isString = typeof location === 'string'; + var name = isString ? location : location && location._name; + if (name) { + if (this._updateUniform(name, floatArray)) { + if (isString) location = this.getUniformLocationForName(name); + this._glContext.uniform4fv(location, floatArray); + } + } else { + this._glContext.uniform4fv(location, floatArray); + cc.log('uniform4fv', floatArray); + } + }, + + /** + * calls glUniformMatrix2fv + * @param {WebGLUniformLocation|String} location + * @param {Float32Array} matrixArray + */ + setUniformLocationWithMatrix2fv: function (location, matrixArray) { + var isString = typeof location === 'string'; + var name = isString ? location : location && location._name; + if (name) { + if (this._updateUniform(name, matrixArray)) { + if (isString) location = this.getUniformLocationForName(name); + this._glContext.uniformMatrix2fv(location, false, matrixArray); + } + } else { + this._glContext.uniformMatrix2fv(location, false, matrixArray); + } + }, + + /** + * calls glUniformMatrix3fv + * @param {WebGLUniformLocation|String} location + * @param {Float32Array} matrixArray + */ + setUniformLocationWithMatrix3fv: function (location, matrixArray) { + var isString = typeof location === 'string'; + var name = isString ? location : location && location._name; + if (name) { + if (this._updateUniform(name, matrixArray)) { + if (isString) location = this.getUniformLocationForName(name); + this._glContext.uniformMatrix3fv(location, false, matrixArray); + } + } else { + this._glContext.uniformMatrix3fv(location, false, matrixArray); + } + }, + + /** + * calls glUniformMatrix4fv + * @param {WebGLUniformLocation|String} location + * @param {Float32Array} matrixArray + */ + setUniformLocationWithMatrix4fv: function (location, matrixArray) { + var isString = typeof location === 'string'; + var name = isString ? location : location && location._name; + if (name) { + if (this._updateUniform(name, matrixArray)) { + if (isString) location = this.getUniformLocationForName(name); + this._glContext.uniformMatrix4fv(location, false, matrixArray); + } + } else { + this._glContext.uniformMatrix4fv(location, false, matrixArray); + } + }, + + setUniformLocationF32: function () { + if (arguments.length < 2) + return; + + switch (arguments.length) { + case 2: + this.setUniformLocationWith1f(arguments[0], arguments[1]); + break; + case 3: + this.setUniformLocationWith2f(arguments[0], arguments[1], arguments[2]); + break; + case 4: + this.setUniformLocationWith3f(arguments[0], arguments[1], arguments[2], arguments[3]); + break; + case 5: + this.setUniformLocationWith4f(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]); + break; + } + }, + + /** + * will update the builtin uniforms if they are different than the previous call for this same shader program. + */ + setUniformsForBuiltins: function () { + var matrixP = new cc.math.Matrix4(); + var matrixMV = new cc.math.Matrix4(); + var matrixMVP = new cc.math.Matrix4(); + + cc.kmGLGetMatrix(cc.KM_GL_PROJECTION, matrixP); + cc.kmGLGetMatrix(cc.KM_GL_MODELVIEW, matrixMV); + + cc.kmMat4Multiply(matrixMVP, matrixP, matrixMV); + + this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX_S], matrixP.mat, 1); + this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_MVMATRIX_S], matrixMV.mat, 1); + this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_MVPMATRIX_S], matrixMVP.mat, 1); + + if (this._usesTime) { + var director = cc.director; + // This doesn't give the most accurate global time value. + // Cocos2D doesn't store a high precision time value, so this will have to do. + // Getting Mach time per frame per shader using time could be extremely expensive. + var time = director.getTotalFrames() * director.getAnimationInterval(); + + this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_TIME_S], time / 10.0, time, time * 2, time * 4); + this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_SINTIME_S], time / 8.0, time / 4.0, time / 2.0, Math.sin(time)); + this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_COSTIME_S], time / 8.0, time / 4.0, time / 2.0, Math.cos(time)); + } + + if (this._uniforms[cc.UNIFORM_RANDOM01_S] !== -1) + this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_RANDOM01_S], Math.random(), Math.random(), Math.random(), Math.random()); + }, + + _setUniformsForBuiltinsForRenderer: function (node) { + if (!node || !node._renderCmd) + return; + + var matrixP = new cc.math.Matrix4(); + //var matrixMV = new cc.kmMat4(); + var matrixMVP = new cc.math.Matrix4(); + + cc.kmGLGetMatrix(cc.KM_GL_PROJECTION, matrixP); + //cc.kmGLGetMatrix(cc.KM_GL_MODELVIEW, node._stackMatrix); + + cc.kmMat4Multiply(matrixMVP, matrixP, node._renderCmd._stackMatrix); + + this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX_S], matrixP.mat, 1); + this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_MVMATRIX_S], node._renderCmd._stackMatrix.mat, 1); + this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_MVPMATRIX_S], matrixMVP.mat, 1); + + if (this._usesTime) { + var director = cc.director; + // This doesn't give the most accurate global time value. + // Cocos2D doesn't store a high precision time value, so this will have to do. + // Getting Mach time per frame per shader using time could be extremely expensive. + var time = director.getTotalFrames() * director.getAnimationInterval(); + + this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_TIME_S], time / 10.0, time, time * 2, time * 4); + this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_SINTIME_S], time / 8.0, time / 4.0, time / 2.0, Math.sin(time)); + this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_COSTIME_S], time / 8.0, time / 4.0, time / 2.0, Math.cos(time)); + } + + if (this._uniforms[cc.UNIFORM_RANDOM01_S] !== -1) + this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_RANDOM01_S], Math.random(), Math.random(), Math.random(), Math.random()); + }, + + /** + * will update the MVP matrix on the MVP uniform if it is different than the previous call for this same shader program. + */ + setUniformForModelViewProjectionMatrix: function () { + this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_MVPMATRIX_S], + cc.getMat4MultiplyValue(cc.projection_matrix_stack.top, cc.modelview_matrix_stack.top)); + }, + + setUniformForModelViewProjectionMatrixWithMat4: function (swapMat4) { + cc.kmMat4Multiply(swapMat4, cc.projection_matrix_stack.top, cc.modelview_matrix_stack.top); + this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_MVPMATRIX_S], swapMat4.mat); + }, + + setUniformForModelViewAndProjectionMatrixWithMat4: function () { + this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_MVMATRIX_S], cc.modelview_matrix_stack.top.mat); + this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX_S], cc.projection_matrix_stack.top.mat); + }, + + _setUniformForMVPMatrixWithMat4: function (modelViewMatrix) { + if (!modelViewMatrix) + throw new Error("modelView matrix is undefined."); + this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_MVMATRIX_S], modelViewMatrix.mat); + this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX_S], cc.projection_matrix_stack.top.mat); + }, + + _updateProjectionUniform: function () { + var stack = cc.projection_matrix_stack; + if (stack.lastUpdated !== this._projectionUpdated) { + this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX_S], false, stack.top.mat); + this._projectionUpdated = stack.lastUpdated; + } + }, + + /** + * returns the vertexShader error log + * @return {String} + */ + vertexShaderLog: function () { + return this._glContext.getShaderInfoLog(this._vertShader); + }, + + /** + * returns the vertexShader error log + * @return {String} + */ + getVertexShaderLog: function () { + return this._glContext.getShaderInfoLog(this._vertShader); + }, + + /** + * returns the fragmentShader error log + * @returns {String} + */ + getFragmentShaderLog: function () { + return this._glContext.getShaderInfoLog(this._vertShader); + }, + + /** + * returns the fragmentShader error log + * @return {String} + */ + fragmentShaderLog: function () { + return this._glContext.getShaderInfoLog(this._fragShader); + }, + + /** + * returns the program error log + * @return {String} + */ + programLog: function () { + return this._glContext.getProgramInfoLog(this._programObj); + }, + + /** + * returns the program error log + * @return {String} + */ + getProgramLog: function () { + return this._glContext.getProgramInfoLog(this._programObj); + }, + + /** + * reload all shaders, this function is designed for android
+ * when opengl context lost, so don't call it. + */ + reset: function () { + this._vertShader = null; + this._fragShader = null; + if (Object.keys(this._uniforms).length > 0) this._uniforms = {}; + + // it is already deallocated by android + //ccGLDeleteProgram(m_uProgram); + this._glContext.deleteProgram(this._programObj); + this._programObj = null; + + // Purge uniform hash + if (Object.keys(this._hashForUniforms).length > 0) this._hashForUniforms = {}; + }, + + /** + * get WebGLProgram object + * @return {WebGLProgram} + */ + getProgram: function () { + return this._programObj; + }, + + /** + * Currently JavaScript Bindings (JSB), in some cases, needs to use retain and release. This is a bug in JSB, + * and the ugly workaround is to use retain/release. So, these 2 methods were added to be compatible with JSB. + * This is a hack, and should be removed once JSB fixes the retain/release bug + */ + retain: function () { + }, + release: function () { + } +}); + +/** + * Create a cc.GLProgram object + * @deprecated since v3.0, please use new cc.GLProgram(vShaderFileName, fShaderFileName) instead + * @param {String} vShaderFileName + * @param {String} fShaderFileName + * @returns {cc.GLProgram} + */ +cc.GLProgram.create = function (vShaderFileName, fShaderFileName) { + return new cc.GLProgram(vShaderFileName, fShaderFileName); +}; + +cc.GLProgram._highpSupported = null; + +cc.GLProgram._isHighpSupported = function () { + var ctx = cc._renderContext; + if (ctx.getShaderPrecisionFormat && cc.GLProgram._highpSupported == null) { + var highp = ctx.getShaderPrecisionFormat(ctx.FRAGMENT_SHADER, ctx.HIGH_FLOAT); + cc.GLProgram._highpSupported = highp.precision !== 0; + } + return cc.GLProgram._highpSupported; +}; + +/** + *

+ * Sets the shader program for this node + * + * Since v2.0, each rendering node must set its shader program. + * It should be set in initialize phase. + *

+ * @function + * @param {cc.Node} node + * @param {cc.GLProgram} program The shader program which fetches from CCShaderCache. + * @example + * cc.setGLProgram(node, cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR)); + */ +cc.setProgram = function (node, program) { + node.shaderProgram = program; + + var children = node.children; + if (!children) + return; + + for (var i = 0; i < children.length; i++) + cc.setProgram(children[i], program); +}; diff --git a/frameworks/cocos2d-html5/cocos2d/shaders/CCGLProgramState.js b/frameworks/cocos2d-html5/cocos2d/shaders/CCGLProgramState.js new file mode 100644 index 0000000..c5bc2fd --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/shaders/CCGLProgramState.js @@ -0,0 +1,303 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +var types = + { + GL_FLOAT: 0, + GL_INT: 1, + GL_FLOAT_VEC2: 2, + GL_FLOAT_VEC3: 3, + GL_FLOAT_VEC4: 4, + GL_FLOAT_MAT4: 5, + GL_CALLBACK: 6, + GL_TEXTURE: 7 + }; + + + +cc.UniformValue = function (uniform, glprogram) { + this._uniform = uniform; + this._glprogram = glprogram; + + this._value = null; + this._type = -1; +}; + +cc.UniformValue.prototype = { + setFloat: function setFloat(value) { + this._value = value; + this._type = types.GL_FLOAT; + }, + + setInt: function setInt(value) { + this._value = value; + this._type = types.GL_INT; + }, + + setVec2: function setVec2(v1, v2) { + this._value = [v1, v2]; + this._type = types.GL_FLOAT_VEC2; + }, + + setVec2v: function setVec2v(value) { + this._value = value.slice(0); + this._type = types.GL_FLOAT_VEC2; + }, + + setVec3: function setVec3(v1, v2, v3) { + this._value = [v1, v2, v3]; + this._type = types.GL_FLOAT_VEC3; + }, + + setVec3v: function setVec3v(value) { + this._value = value.slice(0); + this._type = types.GL_FLOAT_VEC3; + }, + + setVec4: function setVec4(v1, v2, v3, v4) { + this._value = [v1, v2, v3, v4]; + this._type = types.GL_FLOAT_VEC4; + }, + + setVec4v: function setVec4v(value) { + this._value = value.slice(0); + this._type = types.GL_FLOAT_VEC4; + }, + + setMat4: function setMat4(value) { + this._value = value.slice(0); + this._type = types.GL_FLOAT_MAT4; + }, + + setCallback: function setCallback(fn) { + this._value = fn; + this._type = types.GL_CALLBACK; + }, + + setTexture: function setTexture(textureId, textureUnit) { + this._value = textureUnit; + this._textureId = textureId; + this._type = types.GL_TEXTURE; + }, + + apply: function apply() { + switch (this._type) { + case types.GL_INT: + this._glprogram.setUniformLocationWith1i(this._uniform._location, this._value); + break; + case types.GL_FLOAT: + this._glprogram.setUniformLocationWith1f(this._uniform._location, this._value); + break; + case types.GL_FLOAT_VEC2: + this._glprogram.setUniformLocationWith2fv(this._uniform._location, this._value); + break; + case types.GL_FLOAT_VEC3: + this._glprogram.setUniformLocationWith3fv(this._uniform._location, this._value); + break; + case types.GL_FLOAT_VEC4: + this._glprogram.setUniformLocationWith4fv(this._uniform._location, this._value); + break; + case types.GL_FLOAT_MAT4: + this._glprogram.setUniformLocationWithMatrix4fv(this._uniform._location, this._value); + break; + case types.GL_CALLBACK: + this._value(this._glprogram, this._uniform); + break; + case types.GL_TEXTURE: + this._glprogram.setUniformLocationWith1i(this._uniform._location, this._value); + cc.glBindTexture2DN(this._value, this._textureId); + break; + default: + ; + } + }, +}; + +cc.GLProgramState = function (glprogram) { + this._glprogram = glprogram; + this._uniforms = {}; + this._boundTextureUnits = {}; + this._textureUnitIndex = 1; // Start at 1, as CC_Texture0 is bound to 0 + + var activeUniforms = glprogram._glContext.getProgramParameter(glprogram._programObj, + glprogram._glContext.ACTIVE_UNIFORMS); + + for (var i = 0; i < activeUniforms; i += 1) { + var uniform = glprogram._glContext.getActiveUniform(glprogram._programObj, i); + if (uniform.name.indexOf("CC_") !== 0) { + uniform._location = glprogram._glContext.getUniformLocation(glprogram._programObj, uniform.name); + uniform._location._name = uniform.name; + var uniformValue = new cc.UniformValue(uniform, glprogram); + this._uniforms[uniform.name] = uniformValue; + } + } +}; + +cc.GLProgramState.prototype = { + apply: function apply(modelView) { + this._glprogram.use(); + if (modelView) { + this._glprogram._setUniformForMVPMatrixWithMat4(modelView); + } + + for (var name in this._uniforms) { + this._uniforms[name].apply(); + }; + }, + + setGLProgram: function setGLProgram(glprogram) { + this._glprogram = glprogram; + }, + + getGLProgram: function getGLProgram() { + return this._glprogram; + }, + + getUniformCount: function getUniformCount() { + return this._uniforms.length; + }, + + getUniformValue: function getUniformValue(uniform) { + return this._uniforms[uniform]; + }, + + setUniformInt: function setUniformInt(uniform, value) { + var v = this.getUniformValue(uniform); + if (v) { + v.setInt(value); + } else { + cc.log("cocos2d: warning: Uniform not found: " + uniform); + } + }, + + setUniformFloat: function setUniformFloat(uniform, value) { + var v = this.getUniformValue(uniform); + if (v) { + v.setFloat(value); + } else { + cc.log("cocos2d: warning: Uniform not found: " + uniform); + } + }, + + setUniformVec2: function setUniformVec2(uniform, v1, v2) { + var v = this.getUniformValue(uniform); + if (v) { + v.setVec2(v1, v2); + } else { + cc.log("cocos2d: warning: Uniform not found: " + uniform); + } + }, + + setUniformVec2v: function setUniformVec2v(uniform, value) { + var v = this.getUniformValue(uniform); + if (v) { + v.setVec2v(value); + } else { + cc.log("cocos2d: warning: Uniform not found: " + uniform); + } + }, + + setUniformVec3: function setUniformVec3(uniform, v1, v2, v3) { + var v = this.getUniformValue(uniform); + if (v) { + v.setVec3(v1, v2, v3); + } else { + cc.log("cocos2d: warning: Uniform not found: " + uniform); + } + }, + + setUniformVec3v: function setUniformVec3v(uniform, value) { + var v = this.getUniformValue(uniform); + if (v) { + v.setVec3v(value); + } else { + cc.log("cocos2d: warning: Uniform not found: " + uniform); + } + }, + + setUniformVec4: function setUniformVec4(uniform, v1, v2, v3, v4) { + var v = this.getUniformValue(uniform); + if (v) { + v.setVec4(v1, v2, v3, v4); + } else { + cc.log("cocos2d: warning: Uniform not found: " + uniform); + } + }, + + setUniformVec4v: function setUniformVec4v(uniform, value) { + var v = this.getUniformValue(uniform); + if (v) { + v.setVec4v(value); + } else { + cc.log("cocos2d: warning: Uniform not found: " + uniform); + } + }, + + + setUniformMat4: function setUniformMat4(uniform, value) { + var v = this.getUniformValue(uniform); + if (v) { + v.setMat4(value); + } else { + cc.log("cocos2d: warning: Uniform not found: " + uniform); + } + + }, + + setUniformCallback: function setUniformCallback(uniform, callback) { + var v = this.getUniformValue(uniform); + if (v) { + v.setCallback(callback); + } else { + cc.log("cocos2d: warning: Uniform not found: " + uniform); + } + + }, + + setUniformTexture: function setUniformTexture(uniform, texture) { + var uniformValue = this.getUniformValue(uniform); + if (uniformValue) { + var textureUnit = this._boundTextureUnits[uniform]; + if (textureUnit) { + uniformValue.setTexture(texture, textureUnit); + } else { + uniformValue.setTexture(texture, this._textureUnitIndex); + this._boundTextureUnits[uniform] = this._textureUnitIndex++; + } + } + } +}; + +cc.GLProgramState._cache = {}; +cc.GLProgramState.getOrCreateWithGLProgram = function (glprogram) { + var programState = cc.GLProgramState._cache[glprogram.__instanceId]; + if (!programState) { + programState = new cc.GLProgramState(glprogram); + cc.GLProgramState._cache[glprogram.__instanceId] = programState; + } + + return programState; +}; diff --git a/frameworks/cocos2d-html5/cocos2d/shaders/CCGLStateCache.js b/frameworks/cocos2d-html5/cocos2d/shaders/CCGLStateCache.js new file mode 100644 index 0000000..626c587 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/shaders/CCGLStateCache.js @@ -0,0 +1,276 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +cc._currentProjectionMatrix = -1; + +if (cc.ENABLE_GL_STATE_CACHE) { + cc.MAX_ACTIVETEXTURE = 16; + + cc._currentShaderProgram = -1; + cc._currentBoundTexture = [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]; + cc._blendingSource = -1; + cc._blendingDest = -1; + cc._GLServerState = 0; + if(cc.TEXTURE_ATLAS_USE_VAO) + cc._uVAO = 0; +} + +// GL State Cache functions + +/** + * Invalidates the GL state cache.
+ * If CC_ENABLE_GL_STATE_CACHE it will reset the GL state cache. + * @function + */ +cc.glInvalidateStateCache = function () { + cc.kmGLFreeAll(); + cc._currentProjectionMatrix = -1; + if (cc.ENABLE_GL_STATE_CACHE) { + cc._currentShaderProgram = -1; + for (var i = 0; i < cc.MAX_ACTIVETEXTURE; i++) { + cc._currentBoundTexture[i] = -1; + } + cc._blendingSource = -1; + cc._blendingDest = -1; + cc._GLServerState = 0; + } +}; + +/** + * Uses the GL program in case program is different than the current one.
+ * If CC_ENABLE_GL_STATE_CACHE is disabled, it will the glUseProgram() directly. + * @function + * @param {WebGLProgram} program + */ +cc.glUseProgram = cc.ENABLE_GL_STATE_CACHE ? function (program) { + if (program !== cc._currentShaderProgram) { + cc._currentShaderProgram = program; + cc._renderContext.useProgram(program); + } +} : function (program) { + cc._renderContext.useProgram(program); +}; + +/** + * Deletes the GL program. If it is the one that is being used, it invalidates it.
+ * If CC_ENABLE_GL_STATE_CACHE is disabled, it will the glDeleteProgram() directly. + * @function + * @param {WebGLProgram} program + */ +cc.glDeleteProgram = function (program) { + if (cc.ENABLE_GL_STATE_CACHE) { + if (program === cc._currentShaderProgram) + cc._currentShaderProgram = -1; + } + gl.deleteProgram(program); +}; + +/** + * @function + * @param {Number} sfactor + * @param {Number} dfactor + */ +cc.setBlending = function (sfactor, dfactor) { + var ctx = cc._renderContext; + if ((sfactor === ctx.ONE) && (dfactor === ctx.ZERO)) { + ctx.disable(ctx.BLEND); + } else { + ctx.enable(ctx.BLEND); + cc._renderContext.blendFunc(sfactor,dfactor); + //TODO need fix for WebGL + //ctx.blendFuncSeparate(ctx.SRC_ALPHA, dfactor, sfactor, dfactor); + } +}; + +/** + * Uses a blending function in case it not already used.
+ * If CC_ENABLE_GL_STATE_CACHE is disabled, it will the glBlendFunc() directly. + * @function + * @param {Number} sfactor + * @param {Number} dfactor + */ +cc.glBlendFunc = cc.ENABLE_GL_STATE_CACHE ? function (sfactor, dfactor) { + if ((sfactor !== cc._blendingSource) || (dfactor !== cc._blendingDest)) { + cc._blendingSource = sfactor; + cc._blendingDest = dfactor; + cc.setBlending(sfactor, dfactor); + } +} : cc.setBlending; + +/** + * @function + * @param {Number} sfactor + * @param {Number} dfactor + */ +cc.glBlendFuncForParticle = function(sfactor, dfactor) { + if ((sfactor !== cc._blendingSource) || (dfactor !== cc._blendingDest)) { + cc._blendingSource = sfactor; + cc._blendingDest = dfactor; + var ctx = cc._renderContext; + if ((sfactor === ctx.ONE) && (dfactor === ctx.ZERO)) { + ctx.disable(ctx.BLEND); + } else { + ctx.enable(ctx.BLEND); + //TODO need fix for WebGL + ctx.blendFuncSeparate(ctx.SRC_ALPHA, dfactor, sfactor, dfactor); + } + } +}; + +/** + * Resets the blending mode back to the cached state in case you used glBlendFuncSeparate() or glBlendEquation().
+ * If CC_ENABLE_GL_STATE_CACHE is disabled, it will just set the default blending mode using GL_FUNC_ADD. + * @function + */ +cc.glBlendResetToCache = function () { + var ctx = cc._renderContext; + ctx.blendEquation(ctx.FUNC_ADD); + if (cc.ENABLE_GL_STATE_CACHE) + cc.setBlending(cc._blendingSource, cc._blendingDest); + else + cc.setBlending(ctx.BLEND_SRC, ctx.BLEND_DST); +}; + +/** + * sets the projection matrix as dirty + * @function + */ +cc.setProjectionMatrixDirty = function () { + cc._currentProjectionMatrix = -1; +}; + +/** + * If the texture is not already bound, it binds it.
+ * If CC_ENABLE_GL_STATE_CACHE is disabled, it will call glBindTexture() directly. + * @function + * @param {cc.Texture2D} textureId + */ +cc.glBindTexture2D = function (textureId) { + cc.glBindTexture2DN(0, textureId); +}; + +/** + * If the texture is not already bound to a given unit, it binds it.
+ * If CC_ENABLE_GL_STATE_CACHE is disabled, it will call glBindTexture() directly. + * @function + * @param {Number} textureUnit + * @param {cc.Texture2D} textureId + */ +cc.glBindTexture2DN = cc.ENABLE_GL_STATE_CACHE ? function (textureUnit, textureId) { + if (cc._currentBoundTexture[textureUnit] === textureId) + return; + cc._currentBoundTexture[textureUnit] = textureId; + + var ctx = cc._renderContext; + ctx.activeTexture(ctx.TEXTURE0 + textureUnit); + if(textureId) + ctx.bindTexture(ctx.TEXTURE_2D, textureId._webTextureObj); + else + ctx.bindTexture(ctx.TEXTURE_2D, null); +} : function (textureUnit, textureId) { + var ctx = cc._renderContext; + ctx.activeTexture(ctx.TEXTURE0 + textureUnit); + if(textureId) + ctx.bindTexture(ctx.TEXTURE_2D, textureId._webTextureObj); + else + ctx.bindTexture(ctx.TEXTURE_2D, null); +}; + +/** + * It will delete a given texture. If the texture was bound, it will invalidate the cached.
+ * If CC_ENABLE_GL_STATE_CACHE is disabled, it will call glDeleteTextures() directly. + * @function + * @param {WebGLTexture} textureId + */ +cc.glDeleteTexture = function (textureId) { + cc.glDeleteTextureN(0, textureId); +}; + +/** + * It will delete a given texture. If the texture was bound, it will invalidate the cached for the given texture unit.
+ * If CC_ENABLE_GL_STATE_CACHE is disabled, it will call glDeleteTextures() directly. + * @function + * @param {Number} textureUnit + * @param {WebGLTexture} textureId + */ +cc.glDeleteTextureN = function (textureUnit, textureId) { + if (cc.ENABLE_GL_STATE_CACHE) { + if (textureId === cc._currentBoundTexture[ textureUnit ]) + cc._currentBoundTexture[ textureUnit ] = -1; + } + cc._renderContext.deleteTexture(textureId._webTextureObj); +}; + +/** + * If the vertex array is not already bound, it binds it.
+ * If CC_ENABLE_GL_STATE_CACHE is disabled, it will call glBindVertexArray() directly. + * @function + * @param {Number} vaoId + */ +cc.glBindVAO = function (vaoId) { + if (!cc.TEXTURE_ATLAS_USE_VAO) + return; + + if (cc.ENABLE_GL_STATE_CACHE) { + if (cc._uVAO !== vaoId) { + cc._uVAO = vaoId; + //TODO need fixed + //glBindVertexArray(vaoId); + } + } else { + //glBindVertexArray(vaoId); + } +}; + +/** + * It will enable / disable the server side GL states.
+ * If CC_ENABLE_GL_STATE_CACHE is disabled, it will call glEnable() directly. + * @function + * @param {Number} flags + */ +cc.glEnable = function (flags) { + if (cc.ENABLE_GL_STATE_CACHE) { + /*var enabled; + + */ + /* GL_BLEND */ + /* + if ((enabled = (flags & cc.GL_BLEND)) != (cc._GLServerState & cc.GL_BLEND)) { + if (enabled) { + cc._renderContext.enable(cc._renderContext.BLEND); + cc._GLServerState |= cc.GL_BLEND; + } else { + cc._renderContext.disable(cc._renderContext.BLEND); + cc._GLServerState &= ~cc.GL_BLEND; + } + }*/ + } else { + /*if ((flags & cc.GL_BLEND)) + cc._renderContext.enable(cc._renderContext.BLEND); + else + cc._renderContext.disable(cc._renderContext.BLEND);*/ + } +}; diff --git a/frameworks/cocos2d-html5/cocos2d/shaders/CCShaderCache.js b/frameworks/cocos2d-html5/cocos2d/shaders/CCShaderCache.js new file mode 100644 index 0000000..462f208 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/shaders/CCShaderCache.js @@ -0,0 +1,322 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * cc.shaderCache is a singleton object that stores manages GL shaders + * @class + * @name cc.shaderCache + */ +cc.shaderCache = /** @lends cc.shaderCache# */{ + + /** + * @public + * @constant + * @type {Number} + */ + TYPE_POSITION_TEXTURECOLOR: 0, + /** + * @public + * @constant + * @type {Number} + */ + TYPE_POSITION_TEXTURECOLOR_ALPHATEST: 1, + /** + * @public + * @constant + * @type {Number} + */ + TYPE_POSITION_COLOR: 2, + /** + * @public + * @constant + * @type {Number} + */ + TYPE_POSITION_TEXTURE: 3, + /** + * @public + * @constant + * @type {Number} + */ + TYPE_POSITION_TEXTURE_UCOLOR: 4, + /** + * @public + * @constant + * @type {Number} + */ + TYPE_POSITION_TEXTURE_A8COLOR: 5, + /** + * @public + * @constant + * @type {Number} + */ + TYPE_POSITION_UCOLOR: 6, + /** + * @public + * @constant + * @type {Number} + */ + TYPE_POSITION_LENGTH_TEXTURECOLOR: 7, + /** + * @public + * @constant + * @type {Number} + */ + TYPE_SPRITE_POSITION_TEXTURECOLOR: 8, + /** + * @public + * @constant + * @type {Number} + */ + TYPE_SPRITE_POSITION_TEXTURECOLOR_ALPHATEST: 9, + /** + * @public + * @constant + * @type {Number} + */ + TYPE_SPRITE_POSITION_COLOR: 10, + /** + * @public + * @constant + * @type {Number} + */ + TYPE_SPRITE_POSITION_TEXTURECOLOR_GRAY: 11, + /** + * @public + * @constant + * @type {Number} + */ + TYPE_MAX: 11, + + _keyMap: [ + cc.SHADER_POSITION_TEXTURECOLOR, + cc.SHADER_POSITION_TEXTURECOLORALPHATEST, + cc.SHADER_POSITION_COLOR, + cc.SHADER_POSITION_TEXTURE, + cc.SHADER_POSITION_TEXTURE_UCOLOR, + cc.SHADER_POSITION_TEXTUREA8COLOR, + cc.SHADER_POSITION_UCOLOR, + cc.SHADER_POSITION_LENGTHTEXTURECOLOR, + cc.SHADER_SPRITE_POSITION_TEXTURECOLOR, + cc.SHADER_SPRITE_POSITION_TEXTURECOLORALPHATEST, + cc.SHADER_SPRITE_POSITION_COLOR, + cc.SHADER_SPRITE_POSITION_TEXTURECOLOR_GRAY + ], + + _programs: {}, + + _init: function () { + this.loadDefaultShaders(); + return true; + }, + + _loadDefaultShader: function (program, type) { + switch (type) { + case cc.SHADER_POSITION_TEXTURECOLOR: + program.initWithVertexShaderByteArray(cc.SHADER_POSITION_TEXTURE_COLOR_VERT, cc.SHADER_POSITION_TEXTURE_COLOR_FRAG); + program.addAttribute(cc.ATTRIBUTE_NAME_POSITION, cc.VERTEX_ATTRIB_POSITION); + program.addAttribute(cc.ATTRIBUTE_NAME_COLOR, cc.VERTEX_ATTRIB_COLOR); + program.addAttribute(cc.ATTRIBUTE_NAME_TEX_COORD, cc.VERTEX_ATTRIB_TEX_COORDS); + break; + case cc.SHADER_SPRITE_POSITION_TEXTURECOLOR: + program.initWithVertexShaderByteArray(cc.SHADER_SPRITE_POSITION_TEXTURE_COLOR_VERT, cc.SHADER_POSITION_TEXTURE_COLOR_FRAG); + program.addAttribute(cc.ATTRIBUTE_NAME_POSITION, cc.VERTEX_ATTRIB_POSITION); + program.addAttribute(cc.ATTRIBUTE_NAME_COLOR, cc.VERTEX_ATTRIB_COLOR); + program.addAttribute(cc.ATTRIBUTE_NAME_TEX_COORD, cc.VERTEX_ATTRIB_TEX_COORDS); + break; + case cc.SHADER_SPRITE_POSITION_TEXTURECOLOR_GRAY: + program.initWithVertexShaderByteArray(cc.SHADER_SPRITE_POSITION_TEXTURE_COLOR_VERT, cc.SHADER_SPRITE_POSITION_TEXTURE_COLOR_GRAY_FRAG); + program.addAttribute(cc.ATTRIBUTE_NAME_POSITION, cc.VERTEX_ATTRIB_POSITION); + program.addAttribute(cc.ATTRIBUTE_NAME_COLOR, cc.VERTEX_ATTRIB_COLOR); + program.addAttribute(cc.ATTRIBUTE_NAME_TEX_COORD, cc.VERTEX_ATTRIB_TEX_COORDS); + break; + case cc.SHADER_POSITION_TEXTURECOLORALPHATEST: + program.initWithVertexShaderByteArray(cc.SHADER_POSITION_TEXTURE_COLOR_VERT, cc.SHADER_POSITION_TEXTURE_COLOR_ALPHATEST_FRAG); + program.addAttribute(cc.ATTRIBUTE_NAME_POSITION, cc.VERTEX_ATTRIB_POSITION); + program.addAttribute(cc.ATTRIBUTE_NAME_COLOR, cc.VERTEX_ATTRIB_COLOR); + program.addAttribute(cc.ATTRIBUTE_NAME_TEX_COORD, cc.VERTEX_ATTRIB_TEX_COORDS); + break; + case cc.SHADER_SPRITE_POSITION_TEXTURECOLORALPHATEST: + program.initWithVertexShaderByteArray(cc.SHADER_SPRITE_POSITION_TEXTURE_COLOR_VERT, cc.SHADER_POSITION_TEXTURE_COLOR_ALPHATEST_FRAG); + program.addAttribute(cc.ATTRIBUTE_NAME_POSITION, cc.VERTEX_ATTRIB_POSITION); + program.addAttribute(cc.ATTRIBUTE_NAME_COLOR, cc.VERTEX_ATTRIB_COLOR); + program.addAttribute(cc.ATTRIBUTE_NAME_TEX_COORD, cc.VERTEX_ATTRIB_TEX_COORDS); + break; + case cc.SHADER_POSITION_COLOR: + program.initWithVertexShaderByteArray(cc.SHADER_POSITION_COLOR_VERT, cc.SHADER_POSITION_COLOR_FRAG); + program.addAttribute(cc.ATTRIBUTE_NAME_POSITION, cc.VERTEX_ATTRIB_POSITION); + program.addAttribute(cc.ATTRIBUTE_NAME_COLOR, cc.VERTEX_ATTRIB_COLOR); + break; + case cc.SHADER_SPRITE_POSITION_COLOR: + program.initWithVertexShaderByteArray(cc.SHADER_SPRITE_POSITION_COLOR_VERT, cc.SHADER_POSITION_COLOR_FRAG); + program.addAttribute(cc.ATTRIBUTE_NAME_POSITION, cc.VERTEX_ATTRIB_POSITION); + program.addAttribute(cc.ATTRIBUTE_NAME_COLOR, cc.VERTEX_ATTRIB_COLOR); + break; + case cc.SHADER_POSITION_TEXTURE: + program.initWithVertexShaderByteArray(cc.SHADER_POSITION_TEXTURE_VERT, cc.SHADER_POSITION_TEXTURE_FRAG); + program.addAttribute(cc.ATTRIBUTE_NAME_POSITION, cc.VERTEX_ATTRIB_POSITION); + program.addAttribute(cc.ATTRIBUTE_NAME_TEX_COORD, cc.VERTEX_ATTRIB_TEX_COORDS); + break; + case cc.SHADER_POSITION_TEXTURE_UCOLOR: + program.initWithVertexShaderByteArray(cc.SHADER_POSITION_TEXTURE_UCOLOR_VERT, cc.SHADER_POSITION_TEXTURE_UCOLOR_FRAG); + program.addAttribute(cc.ATTRIBUTE_NAME_POSITION, cc.VERTEX_ATTRIB_POSITION); + program.addAttribute(cc.ATTRIBUTE_NAME_TEX_COORD, cc.VERTEX_ATTRIB_TEX_COORDS); + break; + case cc.SHADER_POSITION_TEXTUREA8COLOR: + program.initWithVertexShaderByteArray(cc.SHADER_POSITION_TEXTURE_A8COLOR_VERT, cc.SHADER_POSITION_TEXTURE_A8COLOR_FRAG); + program.addAttribute(cc.ATTRIBUTE_NAME_POSITION, cc.VERTEX_ATTRIB_POSITION); + program.addAttribute(cc.ATTRIBUTE_NAME_COLOR, cc.VERTEX_ATTRIB_COLOR); + program.addAttribute(cc.ATTRIBUTE_NAME_TEX_COORD, cc.VERTEX_ATTRIB_TEX_COORDS); + break; + case cc.SHADER_POSITION_UCOLOR: + program.initWithVertexShaderByteArray(cc.SHADER_POSITION_UCOLOR_VERT, cc.SHADER_POSITION_UCOLOR_FRAG); + program.addAttribute("aVertex", cc.VERTEX_ATTRIB_POSITION); + break; + case cc.SHADER_POSITION_LENGTHTEXTURECOLOR: + program.initWithVertexShaderByteArray(cc.SHADER_POSITION_COLOR_LENGTH_TEXTURE_VERT, cc.SHADER_POSITION_COLOR_LENGTH_TEXTURE_FRAG); + program.addAttribute(cc.ATTRIBUTE_NAME_POSITION, cc.VERTEX_ATTRIB_POSITION); + program.addAttribute(cc.ATTRIBUTE_NAME_TEX_COORD, cc.VERTEX_ATTRIB_TEX_COORDS); + program.addAttribute(cc.ATTRIBUTE_NAME_COLOR, cc.VERTEX_ATTRIB_COLOR); + break; + default: + cc.log("cocos2d: cc.shaderCache._loadDefaultShader, error shader type"); + return; + } + + program.link(); + program.updateUniforms(); + + //cc.checkGLErrorDebug(); + }, + + /** + * loads the default shaders + */ + loadDefaultShaders: function () { + for (var i = 0; i < this.TYPE_MAX; ++i) { + var key = this._keyMap[i]; + this.programForKey(key); + } + }, + + /** + * reload the default shaders + */ + reloadDefaultShaders: function () { + // reset all programs and reload them + + // Position Texture Color shader + var program = this.programForKey(cc.SHADER_POSITION_TEXTURECOLOR); + program.reset(); + this._loadDefaultShader(program, cc.SHADER_POSITION_TEXTURECOLOR); + + // Sprite Position Texture Color shader + program = this.programForKey(cc.SHADER_SPRITE_POSITION_TEXTURECOLOR); + program.reset(); + this._loadDefaultShader(program, cc.SHADER_SPRITE_POSITION_TEXTURECOLOR); + + // Position Texture Color alpha test + program = this.programForKey(cc.SHADER_POSITION_TEXTURECOLORALPHATEST); + program.reset(); + this._loadDefaultShader(program, cc.SHADER_POSITION_TEXTURECOLORALPHATEST); + + // Sprite Position Texture Color alpha shader + program = this.programForKey(cc.SHADER_SPRITE_POSITION_TEXTURECOLORALPHATEST); + program.reset(); + this._loadDefaultShader(program, cc.SHADER_SPRITE_POSITION_TEXTURECOLORALPHATEST); + + // + // Position, Color shader + // + program = this.programForKey(cc.SHADER_POSITION_COLOR); + program.reset(); + this._loadDefaultShader(program, cc.SHADER_POSITION_COLOR); + + // + // Position Texture shader + // + program = this.programForKey(cc.SHADER_POSITION_TEXTURE); + program.reset(); + this._loadDefaultShader(program, cc.SHADER_POSITION_TEXTURE); + + //Position Texture Gray shader + program = this.programForKey(cc.SHADER_SPRITE_POSITION_TEXTURE_COLOR_GRAY_FRAG); + program.reset(); + this._loadDefaultShader(program, cc.SHADER_SPRITE_POSITION_TEXTURE_COLOR_GRAY_FRAG); + + // + // Position, Texture attribs, 1 Color as uniform shader + // + program = this.programForKey(cc.SHADER_POSITION_TEXTURE_UCOLOR); + program.reset(); + this._loadDefaultShader(program, cc.SHADER_POSITION_TEXTURE_UCOLOR); + + // + // Position Texture A8 Color shader + // + program = this.programForKey(cc.SHADER_POSITION_TEXTUREA8COLOR); + program.reset(); + this._loadDefaultShader(program, cc.SHADER_POSITION_TEXTUREA8COLOR); + + // + // Position and 1 color passed as a uniform (to similate glColor4ub ) + // + program = this.programForKey(cc.SHADER_POSITION_UCOLOR); + program.reset(); + this._loadDefaultShader(program, cc.SHADER_POSITION_UCOLOR); + }, + + /** + * returns a GL program for a given key + * @param {String} key + */ + programForKey: function (key) { + if (!this._programs[key]) { + var program = new cc.GLProgram(); + this._loadDefaultShader(program, key); + this._programs[key] = program; + } + + return this._programs[key]; + }, + + /** + * returns a GL program for a shader name + * @param {String} shaderName + * @return {cc.GLProgram} + */ + getProgram: function (shaderName) { + return this.programForKey(shaderName); + }, + + /** + * adds a CCGLProgram to the cache for a given name + * @param {cc.GLProgram} program + * @param {String} key + */ + addProgram: function (program, key) { + this._programs[key] = program; + } +}; diff --git a/frameworks/cocos2d-html5/cocos2d/shaders/CCShaders.js b/frameworks/cocos2d-html5/cocos2d/shaders/CCShaders.js new file mode 100644 index 0000000..eb656c5 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/shaders/CCShaders.js @@ -0,0 +1,314 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2016 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +//-----------------------Shader_Position_uColor Shader Source-------------------------- +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_UCOLOR_FRAG = + "precision lowp float;\n" + + "varying vec4 v_fragmentColor;\n" + + "void main() \n" + + "{ \n" + + " gl_FragColor = v_fragmentColor; \n" + + "}\n"; +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_UCOLOR_VERT = + "attribute vec4 a_position;\n" + + "uniform vec4 u_color;\n" + + "uniform float u_pointSize;\n" + + "varying lowp vec4 v_fragmentColor; \n" + + "void main(void) \n" + + "{\n" + //+ " gl_Position = CC_MVPMatrix * a_position; \n" + + " gl_Position = (CC_PMatrix * CC_MVMatrix) * a_position; \n" + + " gl_PointSize = u_pointSize; \n" + + " v_fragmentColor = u_color; \n" + + "}"; + +//---------------------Shader_PositionColor Shader Source----------------------- +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_COLOR_FRAG = + "precision lowp float; \n" + + "varying vec4 v_fragmentColor; \n" + + "void main() \n" + + "{ \n" + + " gl_FragColor = v_fragmentColor; \n" + + "} "; + +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_COLOR_VERT = + "attribute vec4 a_position;\n" + + "attribute vec4 a_color;\n" + + "varying lowp vec4 v_fragmentColor;\n" + + "void main()\n" + + "{\n" + + " gl_Position = (CC_PMatrix * CC_MVMatrix) * a_position; \n" + + " v_fragmentColor = a_color; \n" + + "}"; + +cc.SHADER_SPRITE_POSITION_COLOR_VERT = + "attribute vec4 a_position;\n" + + "attribute vec4 a_color;\n" + + "varying lowp vec4 v_fragmentColor;\n" + + "void main()\n" + + "{\n" + + " gl_Position = CC_PMatrix * a_position; \n" + + " v_fragmentColor = a_color; \n" + + "}"; + +// --------------------- Shader_PositionColorLengthTexture Shader source------------------------ +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_COLOR_LENGTH_TEXTURE_FRAG = + "// #extension GL_OES_standard_derivatives : enable\n" + + "varying mediump vec4 v_color;\n" + + "varying mediump vec2 v_texcoord;\n" + + "void main() \n" + + "{ \n" + + "// #if defined GL_OES_standard_derivatives \n" + + "// gl_FragColor = v_color*smoothstep(0.0, length(fwidth(v_texcoord)), 1.0 - length(v_texcoord)); \n" + + "// #else \n" + + "gl_FragColor = v_color * step(0.0, 1.0 - length(v_texcoord)); \n" + + "// #endif \n" + + "}"; + +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_COLOR_LENGTH_TEXTURE_VERT = + "attribute mediump vec4 a_position; \n" + + "attribute mediump vec2 a_texcoord; \n" + + "attribute mediump vec4 a_color; \n" + + "varying mediump vec4 v_color; \n" + + "varying mediump vec2 v_texcoord; \n" + + "void main() \n" + + "{ \n" + + " v_color = a_color;//vec4(a_color.rgb * a_color.a, a_color.a); \n" + + " v_texcoord = a_texcoord; \n" + //+ " gl_Position = CC_MVPMatrix * a_position; \n" + + " gl_Position = (CC_PMatrix * CC_MVMatrix) * a_position; \n" + + "}"; + +// ----------------------Shader_PositionTexture Shader Source------------------------------------- +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_TEXTURE_FRAG = + "precision lowp float; \n" + + "varying vec2 v_texCoord; \n" + + "void main() \n" + + "{ \n" + + " gl_FragColor = texture2D(CC_Texture0, v_texCoord); \n" + + "}"; + +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_TEXTURE_VERT = + "attribute vec4 a_position; \n" + + "attribute vec2 a_texCoord; \n" + + "varying mediump vec2 v_texCoord; \n" + + "void main() \n" + + "{ \n" + //+ " gl_Position = CC_MVPMatrix * a_position; \n" + + " gl_Position = (CC_PMatrix * CC_MVMatrix) * a_position; \n" + + " v_texCoord = a_texCoord; \n" + + "}"; + +// ------------------------Shader_PositionTexture_uColor Shader Source------------------------------- +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_TEXTURE_UCOLOR_FRAG = + "precision lowp float; \n" + + "uniform vec4 u_color; \n" + + "varying vec2 v_texCoord; \n" + + "void main() \n" + + "{ \n" + + " gl_FragColor = texture2D(CC_Texture0, v_texCoord) * u_color; \n" + + "}"; + +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_TEXTURE_UCOLOR_VERT = + "attribute vec4 a_position;\n" + + "attribute vec2 a_texCoord; \n" + + "varying mediump vec2 v_texCoord; \n" + + "void main() \n" + + "{ \n" + //+ " gl_Position = CC_MVPMatrix * a_position; \n" + + " gl_Position = (CC_PMatrix * CC_MVMatrix) * a_position; \n" + + " v_texCoord = a_texCoord; \n" + + "}"; + +//---------------------Shader_PositionTextureA8Color Shader source------------------------------- +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_TEXTURE_A8COLOR_FRAG = + "precision lowp float; \n" + + "varying vec4 v_fragmentColor; \n" + + "varying vec2 v_texCoord; \n" + + "void main() \n" + + "{ \n" + + " gl_FragColor = vec4( v_fragmentColor.rgb, \n" // RGB from uniform + + " v_fragmentColor.a * texture2D(CC_Texture0, v_texCoord).a \n" // A from texture and uniform + + " ); \n" + + "}"; + +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_TEXTURE_A8COLOR_VERT = + "attribute vec4 a_position; \n" + + "attribute vec2 a_texCoord; \n" + + "attribute vec4 a_color; \n" + + "varying lowp vec4 v_fragmentColor; \n" + + "varying mediump vec2 v_texCoord; \n" + + "void main() \n" + + "{ \n" + + " gl_Position = (CC_PMatrix * CC_MVMatrix) * a_position; \n" + + " v_fragmentColor = a_color; \n" + + " v_texCoord = a_texCoord; \n" + + "}"; + +// ------------------------Shader_PositionTextureColor Shader source------------------------------------ +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_TEXTURE_COLOR_FRAG = + "precision lowp float;\n" + + "varying vec4 v_fragmentColor; \n" + + "varying vec2 v_texCoord; \n" + + "void main() \n" + + "{ \n" + + " gl_FragColor = v_fragmentColor * texture2D(CC_Texture0, v_texCoord); \n" + + "}"; + +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_TEXTURE_COLOR_VERT = + "attribute vec4 a_position; \n" + + "attribute vec2 a_texCoord; \n" + + "attribute vec4 a_color; \n" + + "varying lowp vec4 v_fragmentColor; \n" + + "varying mediump vec2 v_texCoord; \n" + + "void main() \n" + + "{ \n" + + " gl_Position = (CC_PMatrix * CC_MVMatrix) * a_position; \n" + + " v_fragmentColor = a_color; \n" + + " v_texCoord = a_texCoord; \n" + + "}"; + +/** + * @constant + * @type {String} + */ +cc.SHADER_SPRITE_POSITION_TEXTURE_COLOR_VERT = + "attribute vec4 a_position; \n" + + "attribute vec2 a_texCoord; \n" + + "attribute vec4 a_color; \n" + + "varying lowp vec4 v_fragmentColor; \n" + + "varying mediump vec2 v_texCoord; \n" + + "void main() \n" + + "{ \n" + + " gl_Position = CC_PMatrix * a_position; \n" + + " v_fragmentColor = a_color; \n" + + " v_texCoord = a_texCoord; \n" + + "}"; + +cc.SHADER_SPRITE_POSITION_TEXTURE_COLOR_GRAY_FRAG = + "precision lowp float;\n" + + "varying vec4 v_fragmentColor; \n" + + "varying vec2 v_texCoord; \n" + + "void main() \n" + + "{ \n" + + " vec4 c = texture2D(CC_Texture0, v_texCoord); \n" + + " gl_FragColor.xyz = vec3(0.2126*c.r + 0.7152*c.g + 0.0722*c.b); \n" + +" gl_FragColor.w = c.w ; \n" + + "}"; +//-----------------------Shader_PositionTextureColorAlphaTest_frag Shader Source---------------------------- +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_TEXTURE_COLOR_ALPHATEST_FRAG = + "precision lowp float; \n" + + "varying vec4 v_fragmentColor; \n" + + "varying vec2 v_texCoord; \n" + + "uniform float CC_alpha_value; \n" + + "void main() \n" + + "{ \n" + + " vec4 texColor = texture2D(CC_Texture0, v_texCoord); \n" + // mimic: glAlphaFunc(GL_GREATER) + //pass if ( incoming_pixel >= CC_alpha_value ) => fail if incoming_pixel < CC_alpha_value + + " if ( texColor.a <= CC_alpha_value ) \n" + + " discard; \n" + + " gl_FragColor = texColor * v_fragmentColor; \n" + + "}"; + +//-----------------------ShaderEx_SwitchMask_frag Shader Source---------------------------- +/** + * @constant + * @type {String} + */ +cc.SHADEREX_SWITCHMASK_FRAG = + "precision lowp float; \n" + + "varying vec4 v_fragmentColor; \n" + + "varying vec2 v_texCoord; \n" + + "uniform sampler2D u_texture; \n" + + "uniform sampler2D u_mask; \n" + + "void main() \n" + + "{ \n" + + " vec4 texColor = texture2D(u_texture, v_texCoord); \n" + + " vec4 maskColor = texture2D(u_mask, v_texCoord); \n" + + " vec4 finalColor = vec4(texColor.r, texColor.g, texColor.b, maskColor.a * texColor.a); \n" + + " gl_FragColor = v_fragmentColor * finalColor; \n" + + "}"; diff --git a/frameworks/cocos2d-html5/cocos2d/shape-nodes/CCDrawNode.js b/frameworks/cocos2d-html5/cocos2d/shape-nodes/CCDrawNode.js new file mode 100644 index 0000000..48caafa --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/shape-nodes/CCDrawNode.js @@ -0,0 +1,1037 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2012 Scott Lembcke and Howling Moon Software + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + *

CCDrawNode
+ * Node that draws dots, segments and polygons.
+ * Faster than the "drawing primitives" since it draws everything in one single batch.

+ * @class + * @name cc.DrawNode + * @extends cc.Node + */ +cc.DrawNode = cc.Node.extend(/** @lends cc.DrawNode# */{ +//TODO need refactor + + _buffer: null, + _blendFunc: null, + _lineWidth: 1, + _drawColor: null, + + /** + * Gets the blend func + * @returns {Object} + */ + getBlendFunc: function () { + return this._blendFunc; + }, + + /** + * Set the blend func + * @param blendFunc + * @param dst + */ + setBlendFunc: function (blendFunc, dst) { + if (dst === undefined) { + this._blendFunc.src = blendFunc.src; + this._blendFunc.dst = blendFunc.dst; + } else { + this._blendFunc.src = blendFunc; + this._blendFunc.dst = dst; + } + }, + + /** + * line width setter + * @param {Number} width + */ + setLineWidth: function (width) { + this._lineWidth = width; + }, + + /** + * line width getter + * @returns {Number} + */ + getLineWidth: function () { + return this._lineWidth; + }, + + /** + * draw color setter + * @param {cc.Color} color + */ + setDrawColor: function (color) { + var locDrawColor = this._drawColor; + locDrawColor.r = color.r; + locDrawColor.g = color.g; + locDrawColor.b = color.b; + locDrawColor.a = (color.a == null) ? 255 : color.a; + }, + + /** + * draw color getter + * @returns {cc.Color} + */ + getDrawColor: function () { + return cc.color(this._drawColor.r, this._drawColor.g, this._drawColor.b, this._drawColor.a); + } +}); + +/** + * Creates a DrawNode + * @deprecated since v3.0 please use `new cc.DrawNode()` instead. + * @return {cc.DrawNode} + */ +cc.DrawNode.create = function () { + return new cc.DrawNode(); +}; + +cc.DrawNode.TYPE_DOT = 0; +cc.DrawNode.TYPE_SEGMENT = 1; +cc.DrawNode.TYPE_POLY = 2; + +cc.game.addEventListener(cc.game.EVENT_RENDERER_INITED, function () { + + function pMultOut(pin, floatVar, pout) { + pout.x = pin.x * floatVar; + pout.y = pin.y * floatVar; + } + + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) { + + cc._DrawNodeElement = function (type, verts, fillColor, lineWidth, lineColor, lineCap, isClosePolygon, isFill, isStroke) { + var _t = this; + _t.type = type; + _t.verts = verts || null; + _t.fillColor = fillColor || null; + _t.lineWidth = lineWidth || 0; + _t.lineColor = lineColor || null; + _t.lineCap = lineCap || "butt"; + _t.isClosePolygon = isClosePolygon || false; + _t.isFill = isFill || false; + _t.isStroke = isStroke || false; + }; + + cc.extend(cc.DrawNode.prototype, /** @lends cc.DrawNode# */{ + _className: "DrawNodeCanvas", + + /** + *

The cc.DrawNodeCanvas's constructor.
+ * This function will automatically be invoked when you create a node using new construction: "var node = new cc.DrawNodeCanvas()".
+ * Override it to extend its behavior, remember to call "this._super()" in the extended "ctor" function.

+ */ + ctor: function () { + cc.Node.prototype.ctor.call(this); + var locCmd = this._renderCmd; + locCmd._buffer = this._buffer = []; + locCmd._drawColor = this._drawColor = cc.color(255, 255, 255, 255); + locCmd._blendFunc = this._blendFunc = new cc.BlendFunc(cc.SRC_ALPHA, cc.ONE_MINUS_SRC_ALPHA); + + this.init(); + this._localBB = new cc.Rect(); + }, + + setLocalBB: function (rectorX, y, width, height) { + var localBB = this._localBB; + if (y === undefined) { + localBB.x = rectorX.x; + localBB.y = rectorX.y; + localBB.width = rectorX.width; + localBB.height = rectorX.height; + } else { + localBB.x = rectorX; + localBB.y = y; + localBB.width = width; + localBB.height = height; + } + }, + /** + * draws a rectangle given the origin and destination point measured in points. + * @param {cc.Point} origin + * @param {cc.Point} destination + * @param {cc.Color} fillColor + * @param {Number} lineWidth + * @param {cc.Color} lineColor + */ + drawRect: function (origin, destination, fillColor, lineWidth, lineColor) { + lineWidth = (lineWidth == null) ? this._lineWidth : lineWidth; + lineColor = lineColor || this.getDrawColor(); + if (lineColor.a == null) + lineColor.a = 255; + + var vertices = [ + origin, + cc.p(destination.x, origin.y), + destination, + cc.p(origin.x, destination.y) + ]; + var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY); + element.verts = vertices; + element.lineWidth = lineWidth; + element.lineColor = lineColor; + element.isClosePolygon = true; + element.isStroke = true; + element.lineCap = "butt"; + element.fillColor = fillColor; + if (fillColor) { + if (fillColor.a == null) + fillColor.a = 255; + element.isFill = true; + } + this._buffer.push(element); + }, + + /** + * draws a circle given the center, radius and number of segments. + * @override + * @param {cc.Point} center center of circle + * @param {Number} radius + * @param {Number} angle angle in radians + * @param {Number} segments + * @param {Boolean} drawLineToCenter + * @param {Number} lineWidth + * @param {cc.Color} color + */ + drawCircle: function (center, radius, angle, segments, drawLineToCenter, lineWidth, color) { + lineWidth = lineWidth || this._lineWidth; + color = color || this.getDrawColor(); + if (color.a == null) + color.a = 255; + + var coef = 2.0 * Math.PI / segments; + var vertices = []; + for (var i = 0; i <= segments; i++) { + var rads = i * coef; + var j = radius * Math.cos(rads + angle) + center.x; + var k = radius * Math.sin(rads + angle) + center.y; + vertices.push(cc.p(j, k)); + } + if (drawLineToCenter) { + vertices.push(cc.p(center.x, center.y)); + } + + var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY); + element.verts = vertices; + element.lineWidth = lineWidth; + element.lineColor = color; + element.isClosePolygon = true; + element.isStroke = true; + this._buffer.push(element); + }, + + /** + * draws a quad bezier path + * @override + * @param {cc.Point} origin + * @param {cc.Point} control + * @param {cc.Point} destination + * @param {Number} segments + * @param {Number} lineWidth + * @param {cc.Color} color + */ + drawQuadBezier: function (origin, control, destination, segments, lineWidth, color) { + lineWidth = lineWidth || this._lineWidth; + color = color || this.getDrawColor(); + if (color.a == null) + color.a = 255; + + var vertices = [], t = 0.0; + for (var i = 0; i < segments; i++) { + var x = Math.pow(1 - t, 2) * origin.x + 2.0 * (1 - t) * t * control.x + t * t * destination.x; + var y = Math.pow(1 - t, 2) * origin.y + 2.0 * (1 - t) * t * control.y + t * t * destination.y; + vertices.push(cc.p(x, y)); + t += 1.0 / segments; + } + vertices.push(cc.p(destination.x, destination.y)); + + var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY); + element.verts = vertices; + element.lineWidth = lineWidth; + element.lineColor = color; + element.isStroke = true; + element.lineCap = "round"; + this._buffer.push(element); + }, + + /** + * draws a cubic bezier path + * @override + * @param {cc.Point} origin + * @param {cc.Point} control1 + * @param {cc.Point} control2 + * @param {cc.Point} destination + * @param {Number} segments + * @param {Number} lineWidth + * @param {cc.Color} color + */ + drawCubicBezier: function (origin, control1, control2, destination, segments, lineWidth, color) { + lineWidth = lineWidth || this._lineWidth; + color = color || this.getDrawColor(); + if (color.a == null) + color.a = 255; + + var vertices = [], t = 0; + for (var i = 0; i < segments; i++) { + var x = Math.pow(1 - t, 3) * origin.x + 3.0 * Math.pow(1 - t, 2) * t * control1.x + 3.0 * (1 - t) * t * t * control2.x + t * t * t * destination.x; + var y = Math.pow(1 - t, 3) * origin.y + 3.0 * Math.pow(1 - t, 2) * t * control1.y + 3.0 * (1 - t) * t * t * control2.y + t * t * t * destination.y; + vertices.push(cc.p(x, y)); + t += 1.0 / segments; + } + vertices.push(cc.p(destination.x, destination.y)); + + var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY); + element.verts = vertices; + element.lineWidth = lineWidth; + element.lineColor = color; + element.isStroke = true; + element.lineCap = "round"; + this._buffer.push(element); + }, + + /** + * draw a CatmullRom curve + * @override + * @param {Array} points + * @param {Number} segments + * @param {Number} [lineWidth] + * @param {cc.Color} [color] + */ + drawCatmullRom: function (points, segments, lineWidth, color) { + this.drawCardinalSpline(points, 0.5, segments, lineWidth, color); + }, + + /** + * draw a cardinal spline path + * @override + * @param {Array} config + * @param {Number} tension + * @param {Number} segments + * @param {Number} [lineWidth] + * @param {cc.Color} [color] + */ + drawCardinalSpline: function (config, tension, segments, lineWidth, color) { + lineWidth = lineWidth || this._lineWidth; + color = color || this.getDrawColor(); + if (color.a == null) + color.a = 255; + + var vertices = [], p, lt, deltaT = 1.0 / config.length; + for (var i = 0; i < segments + 1; i++) { + var dt = i / segments; + // border + if (dt === 1) { + p = config.length - 1; + lt = 1; + } else { + p = 0 | (dt / deltaT); + lt = (dt - deltaT * p) / deltaT; + } + + // Interpolate + var newPos = cc.cardinalSplineAt( + cc.getControlPointAt(config, p - 1), + cc.getControlPointAt(config, p - 0), + cc.getControlPointAt(config, p + 1), + cc.getControlPointAt(config, p + 2), + tension, lt); + vertices.push(newPos); + } + + var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY); + element.verts = vertices; + element.lineWidth = lineWidth; + element.lineColor = color; + element.isStroke = true; + element.lineCap = "round"; + this._buffer.push(element); + }, + + /** + * draw a dot at a position, with a given radius and color + * @param {cc.Point} pos + * @param {Number} radius + * @param {cc.Color} [color] + */ + drawDot: function (pos, radius, color) { + color = color || this.getDrawColor(); + if (color.a == null) + color.a = 255; + var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_DOT); + element.verts = [pos]; + element.lineWidth = radius; + element.fillColor = color; + this._buffer.push(element); + }, + + /** + * draws an array of points. + * @override + * @param {Array} points point of array + * @param {Number} radius + * @param {cc.Color} [color] + */ + drawDots: function (points, radius, color) { + if (!points || points.length == 0) + return; + color = color || this.getDrawColor(); + if (color.a == null) + color.a = 255; + for (var i = 0, len = points.length; i < len; i++) + this.drawDot(points[i], radius, color); + }, + + /** + * draw a segment with a radius and color + * @param {cc.Point} from + * @param {cc.Point} to + * @param {Number} [lineWidth] + * @param {cc.Color} [color] + */ + drawSegment: function (from, to, lineWidth, color) { + lineWidth = lineWidth || this._lineWidth; + color = color || this.getDrawColor(); + if (color.a == null) + color.a = 255; + var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY); + element.verts = [from, to]; + element.lineWidth = lineWidth * 2; + element.lineColor = color; + element.isStroke = true; + element.lineCap = "round"; + this._buffer.push(element); + }, + + /** + * draw a polygon with a fill color and line color without copying the vertex list + * @param {Array} verts + * @param {cc.Color|null} fillColor Fill color or `null` for a hollow polygon. + * @param {Number} [lineWidth] + * @param {cc.Color} [color] + */ + drawPoly_: function (verts, fillColor, lineWidth, color) { + lineWidth = (lineWidth == null ) ? this._lineWidth : lineWidth; + color = color || this.getDrawColor(); + if (color.a == null) + color.a = 255; + var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY); + + element.verts = verts; + element.fillColor = fillColor; + element.lineWidth = lineWidth; + element.lineColor = color; + element.isClosePolygon = true; + element.isStroke = true; + element.lineCap = "round"; + if (fillColor) + element.isFill = true; + this._buffer.push(element); + }, + + /** + * draw a polygon with a fill color and line color, copying the vertex list + * @param {Array} verts + * @param {cc.Color|null} fillColor Fill color or `null` for a hollow polygon. + * @param {Number} [lineWidth] + * @param {cc.Color} [lineColor] + */ + drawPoly: function (verts, fillColor, lineWidth, lineColor) { + var vertsCopy = []; + for (var i = 0; i < verts.length; i++) { + vertsCopy.push(cc.p(verts[i].x, verts[i].y)); + } + return this.drawPoly_(vertsCopy, fillColor, lineWidth, lineColor); + }, + + /** + * Clear the geometry in the node's buffer. + */ + clear: function () { + this._buffer.length = 0; + }, + + _createRenderCmd: function () { + return new cc.DrawNode.CanvasRenderCmd(this); + } + }); + } + else if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + + // 9600 vertices by default configurable in ccConfig.js + // 20 is 2 float for position, 4 int for color and 2 float for uv + var _sharedBuffer = null; + var FLOAT_PER_VERTEX = 2 + 1 + 2; + var VERTEX_BYTE = FLOAT_PER_VERTEX * 4; + var FLOAT_PER_TRIANGLE = 3 * FLOAT_PER_VERTEX; + var TRIANGLE_BYTES = FLOAT_PER_TRIANGLE * 4; + var MAX_INCREMENT = 200; + + var _vertices = [], + _from = cc.p(), + _to = cc.p(), + _color = new Uint32Array(1); + + // Used in drawSegment + var _n = cc.p(), _t = cc.p(), _nw = cc.p(), _tw = cc.p(), + _extrude = []; + + cc.extend(cc.DrawNode.prototype, { + _bufferCapacity: 0, + _vertexCount: 0, + + _offset: 0, + _occupiedSize: 0, + _f32Buffer: null, + _ui32Buffer: null, + + _dirty: false, + _className: "DrawNodeWebGL", + + manualRelease: false, + + ctor: function (capacity, manualRelease) { + cc.Node.prototype.ctor.call(this); + + if (!_sharedBuffer) { + _sharedBuffer = new GlobalVertexBuffer(cc._renderContext, cc.DRAWNODE_TOTAL_VERTICES * VERTEX_BYTE); + } + + this._renderCmd._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_LENGTHTEXTURECOLOR); + this._blendFunc = new cc.BlendFunc(cc.SRC_ALPHA, cc.ONE_MINUS_SRC_ALPHA); + this._drawColor = cc.color(255, 255, 255, 255); + + this._bufferCapacity = capacity || 64; + this.manualRelease = manualRelease; + + this._dirty = true; + }, + + onEnter: function () { + cc.Node.prototype.onEnter.call(this); + if (this._occupiedSize < this._bufferCapacity) { + this._ensureCapacity(this._bufferCapacity); + } + }, + + onExit: function () { + if (!this.manualRelease) { + this.release(); + } + cc.Node.prototype.onExit.call(this); + }, + + release: function () { + if (this._occupiedSize > 0) { + this._vertexCount = 0; + _sharedBuffer.freeBuffer(this._offset, VERTEX_BYTE * this._occupiedSize); + this._occupiedSize = 0; + } + }, + + _ensureCapacity: function (count) { + var _t = this; + var prev = _t._occupiedSize; + var prevOffset = _t._offset; + if (count > prev || _t._bufferCapacity > prev) { + var request = Math.max(Math.min(prev + prev, MAX_INCREMENT), count, _t._bufferCapacity); + // free previous buffer + if (prev !== 0) { + _sharedBuffer.freeBuffer(prevOffset, VERTEX_BYTE * prev); + _t._occupiedSize = 0; + } + var offset = _t._offset = _sharedBuffer.requestBuffer(VERTEX_BYTE * request); + if (offset >= 0) { + _t._occupiedSize = _t._bufferCapacity = request; + // 5 floats per vertex + _t._f32Buffer = new Float32Array(_sharedBuffer.data, offset, FLOAT_PER_VERTEX * _t._occupiedSize); + _t._ui32Buffer = new Uint32Array(_sharedBuffer.data, offset, FLOAT_PER_VERTEX * _t._occupiedSize); + + // Copy old data + if (prev !== 0 && prevOffset !== offset) { + // offset is in byte, we need to transform to float32 index + var last = prevOffset / 4 + prev * FLOAT_PER_VERTEX; + for (var i = offset / 4, j = prevOffset / 4; j < last; i++, j++) { + _sharedBuffer.dataArray[i] = _sharedBuffer.dataArray[j]; + } + } + + return true; + } + else { + cc.warn('Failed to allocate buffer for DrawNode: buffer for ' + request + ' vertices requested'); + return false; + } + } + else { + return true; + } + }, + + drawRect: function (origin, destination, fillColor, lineWidth, lineColor) { + lineWidth = (lineWidth == null) ? this._lineWidth : lineWidth; + lineColor = lineColor || this._drawColor; + _vertices.length = 0; + _vertices.push(origin.x, origin.y, destination.x, origin.y, destination.x, destination.y, origin.x, destination.y); + if (fillColor == null) + this._drawSegments(_vertices, lineWidth, lineColor, true); + else + this.drawPoly(_vertices, fillColor, lineWidth, lineColor); + _vertices.length = 0; + }, + + drawCircle: function (center, radius, angle, segments, drawLineToCenter, lineWidth, color) { + lineWidth = lineWidth || this._lineWidth; + color = color || this._drawColor; + var coef = 2.0 * Math.PI / segments, i, len; + _vertices.length = 0; + for (i = 0; i <= segments; i++) { + var rads = i * coef; + var j = radius * Math.cos(rads + angle) + center.x; + var k = radius * Math.sin(rads + angle) + center.y; + _vertices.push(j, k); + } + if (drawLineToCenter) + _vertices.push(center.x, center.y); + + lineWidth *= 0.5; + for (i = 0, len = _vertices.length - 2; i < len; i += 2) { + _from.x = _vertices[i]; + _from.y = _vertices[i + 1]; + _to.x = _vertices[i + 2]; + _to.y = _vertices[i + 3]; + this.drawSegment(_from, _to, lineWidth, color); + } + _vertices.length = 0; + }, + + drawQuadBezier: function (origin, control, destination, segments, lineWidth, color) { + lineWidth = lineWidth || this._lineWidth; + color = color || this._drawColor; + var t = 0.0; + _vertices.length = 0; + for (var i = 0; i < segments; i++) { + var x = Math.pow(1 - t, 2) * origin.x + 2.0 * (1 - t) * t * control.x + t * t * destination.x; + var y = Math.pow(1 - t, 2) * origin.y + 2.0 * (1 - t) * t * control.y + t * t * destination.y; + _vertices.push(x, y); + t += 1.0 / segments; + } + _vertices.push(destination.x, destination.y); + this._drawSegments(_vertices, lineWidth, color, false); + _vertices.length = 0; + }, + + drawCubicBezier: function (origin, control1, control2, destination, segments, lineWidth, color) { + lineWidth = lineWidth || this._lineWidth; + color = color || this._drawColor; + var t = 0; + _vertices.length = 0; + for (var i = 0; i < segments; i++) { + var x = Math.pow(1 - t, 3) * origin.x + 3.0 * Math.pow(1 - t, 2) * t * control1.x + 3.0 * (1 - t) * t * t * control2.x + t * t * t * destination.x; + var y = Math.pow(1 - t, 3) * origin.y + 3.0 * Math.pow(1 - t, 2) * t * control1.y + 3.0 * (1 - t) * t * t * control2.y + t * t * t * destination.y; + _vertices.push(x, y); + t += 1.0 / segments; + } + _vertices.push(destination.x, destination.y); + this._drawSegments(_vertices, lineWidth, color, false); + _vertices.length = 0; + }, + + drawCatmullRom: function (points, segments, lineWidth, color) { + this.drawCardinalSpline(points, 0.5, segments, lineWidth, color); + }, + + drawCardinalSpline: function (config, tension, segments, lineWidth, color) { + lineWidth = lineWidth || this._lineWidth; + color = color || this._drawColor; + var p, lt, deltaT = 1.0 / config.length; + _vertices.length = 0; + + for (var i = 0; i < segments + 1; i++) { + var dt = i / segments; + + // border + if (dt === 1) { + p = config.length - 1; + lt = 1; + } else { + p = 0 | (dt / deltaT); + lt = (dt - deltaT * p) / deltaT; + } + + // Interpolate + cc.cardinalSplineAt( + cc.getControlPointAt(config, p - 1), + cc.getControlPointAt(config, p - 0), + cc.getControlPointAt(config, p + 1), + cc.getControlPointAt(config, p + 2), + tension, lt, _from); + _vertices.push(_from.x, _from.y); + } + + lineWidth *= 0.5; + for (var j = 0, len = _vertices.length - 2; j < len; j += 2) { + _from.x = _vertices[j]; + _from.y = _vertices[j + 1]; + _to.x = _vertices[j + 2]; + _to.y = _vertices[j + 3]; + this.drawSegment(_from, _to, lineWidth, color); + } + _vertices.length = 0; + }, + + drawDots: function (points, radius, color) { + if (!points || points.length === 0) + return; + color = color || this._drawColor; + for (var i = 0, len = points.length; i < len; i++) { + this.drawDot(points[i], radius, color); + } + }, + + _render: function () { + var gl = cc._renderContext; + if (this._offset < 0 || this._vertexCount <= 0) { + return; + } + + if (this._dirty) { + // bindBuffer is done in updateSubData + _sharedBuffer.updateSubData(this._offset, this._f32Buffer); + this._dirty = false; + } + else { + gl.bindBuffer(gl.ARRAY_BUFFER, _sharedBuffer.vertexBuffer); + } + + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_POSITION); + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_COLOR); + gl.enableVertexAttribArray(cc.VERTEX_ATTRIB_TEX_COORDS); + + // vertex + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, gl.FLOAT, false, VERTEX_BYTE, 0); + // color + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, gl.UNSIGNED_BYTE, true, VERTEX_BYTE, 8); + // texcood + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, gl.FLOAT, false, VERTEX_BYTE, 12); + + gl.drawArrays(gl.TRIANGLES, this._offset / VERTEX_BYTE, this._vertexCount); + cc.incrementGLDraws(1); + //cc.checkGLErrorDebug(); + }, + + appendVertexData: function (x, y, color, u, v) { + var f32Buffer = this._f32Buffer; + // Float offset = byte offset / 4 + vertex count * floats by vertex + var offset = this._vertexCount * FLOAT_PER_VERTEX; + f32Buffer[offset] = x; + f32Buffer[offset + 1] = y; + _color[0] = ((color.a << 24) | (color.b << 16) | (color.g << 8) | color.r); + this._ui32Buffer[offset + 2] = _color[0]; + f32Buffer[offset + 3] = u; + f32Buffer[offset + 4] = v; + this._vertexCount++; + }, + + drawDot: function (pos, radius, color) { + color = color || this._drawColor; + if (color.a == null) + color.a = 255; + var l = pos.x - radius, + b = pos.y - radius, + r = pos.x + radius, + t = pos.y + radius; + + var vertexCount = 2 * 3; + var succeed = this._ensureCapacity(this._vertexCount + vertexCount); + if (!succeed) + return; + + // lb, lt, rt, lb, rt, rb + this.appendVertexData(l, b, color, -1, -1); + this.appendVertexData(l, t, color, -1, 1); + this.appendVertexData(r, t, color, 1, 1); + this.appendVertexData(l, b, color, -1, -1); + this.appendVertexData(r, t, color, 1, 1); + this.appendVertexData(r, b, color, 1, -1); + + this._dirty = true; + }, + + drawSegment: function (from, to, radius, color) { + color = color || this.getDrawColor(); + if (color.a == null) + color.a = 255; + radius = radius || (this._lineWidth * 0.5); + var vertexCount = 6 * 3; + var succeed = this._ensureCapacity(this._vertexCount + vertexCount); + if (!succeed) + return; + + var a = from, b = to; + // var n = normalize(perp(sub(b, a))) + _n.x = a.y - b.y; _n.y = b.x - a.x; + cc.pNormalizeIn(_n); + // var t = perp(n); + _t.x = -_n.y; _t.y = _n.x; + // var nw = mult(n, radius), tw = mult(t, radius); + pMultOut(_n, radius, _nw); + pMultOut(_t, radius, _tw); + + // var v0 = sub(b, add(nw, tw)); uv0 = neg(add(n, t)) + var v0x = b.x - _nw.x - _tw.x, v0y = b.y - _nw.y - _tw.y, u0 = -(_n.x + _t.x), v0 = -(_n.y + _t.y); + // var v1 = add(b, sub(nw, tw)); uv1 = sub(n, t) + var v1x = b.x + _nw.x - _tw.x, v1y = b.y + _nw.y - _tw.y, u1 = _n.x - _t.x, v1 = _n.y - _t.y; + // var v2 = sub(b, nw); uv2 = neg(n) + var v2x = b.x - _nw.x, v2y = b.y - _nw.y, u2 = -_n.x, v2 = -_n.y; + // var v3 = add(b, nw); uv3 = n + var v3x = b.x + _nw.x, v3y = b.y + _nw.y, u3 = _n.x, v3 = _n.y; + // var v4 = sub(a, nw); uv4 = neg(n) + var v4x = a.x - _nw.x, v4y = a.y - _nw.y, u4 = u2, v4 = v2; + // var v5 = add(a, nw); uv5 = n + var v5x = a.x + _nw.x, v5y = a.y + _nw.y, u5 = _n.x, v5 = _n.y; + // var v6 = sub(a, sub(nw, tw)); uv6 = sub(t, n) + var v6x = a.x - _nw.x + _tw.x, v6y = a.y - _nw.y + _tw.y, u6 = _t.x - _n.x, v6 = _t.y - _n.y; + // var v7 = add(a, add(nw, tw)); uv7 = add(n, t) + var v7x = a.x + _nw.x + _tw.x, v7y = a.y + _nw.y + _tw.y, u7 = _n.x + _t.x, v7 = _n.y + _t.y; + + this.appendVertexData(v0x, v0y, color, u0, v0); + this.appendVertexData(v1x, v1y, color, u1, v1); + this.appendVertexData(v2x, v2y, color, u2, v2); + + this.appendVertexData(v3x, v3y, color, u3, v3); + this.appendVertexData(v1x, v1y, color, u1, v1); + this.appendVertexData(v2x, v2y, color, u2, v2); + + this.appendVertexData(v3x, v3y, color, u3, v3); + this.appendVertexData(v4x, v4y, color, u4, v4); + this.appendVertexData(v2x, v2y, color, u2, v2); + + this.appendVertexData(v3x, v3y, color, u3, v3); + this.appendVertexData(v4x, v4y, color, u4, v4); + this.appendVertexData(v5x, v5y, color, u5, v5); + + this.appendVertexData(v6x, v6y, color, u6, v6); + this.appendVertexData(v4x, v4y, color, u4, v4); + this.appendVertexData(v5x, v5y, color, u5, v5); + + this.appendVertexData(v6x, v6y, color, u6, v6); + this.appendVertexData(v7x, v7y, color, u7, v7); + this.appendVertexData(v5x, v5y, color, u5, v5); + this._dirty = true; + }, + + drawPoly: function (verts, fillColor, borderWidth, borderColor) { + // Backward compatibility + if (typeof verts[0] === 'object') { + _vertices.length = 0; + for (var i = 0; i < verts.length; i++) { + _vertices.push(verts[i].x, verts[i].y); + } + verts = _vertices; + } + + if (fillColor == null) { + this._drawSegments(verts, borderWidth, borderColor, true); + return; + } + if (fillColor.a == null) + fillColor.a = 255; + if (borderColor.a == null) + borderColor.a = 255; + borderWidth = (borderWidth == null) ? this._lineWidth : borderWidth; + borderWidth *= 0.5; + var v0x, v0y, v1x, v1y, v2x, v2y, + factor, offx, offy, + i, count = verts.length; + _extrude.length = 0; + for (i = 0; i < count; i += 2) { + v0x = verts[(i - 2 + count) % count]; + v0y = verts[(i - 1 + count) % count]; + v1x = verts[i]; + v1y = verts[i + 1]; + v2x = verts[(i + 2) % count]; + v2y = verts[(i + 3) % count]; + // var n1 = normalize(perp(sub(v1, v0))); + // var n2 = normalize(perp(sub(v2, v1))); + _n.x = v0y - v1y; _n.y = v1x - v0x; + _nw.x = v1y - v2y; _nw.y = v2x - v1x; + cc.pNormalizeIn(_n); + cc.pNormalizeIn(_nw); + // var offset = mult(add(n1, n2), 1.0 / (dot(n1, n2) + 1.0)); + factor = _n.x * _nw.x + _n.y * _nw.y + 1; + offx = (_n.x + _nw.x) / factor; + offy = (_n.y + _nw.y) / factor; + // extrude[i] = {offset: offset, n: n2}; + _extrude.push(offx, offy, _nw.x, _nw.y); + } + // The actual input vertex count + count = count / 2; + var outline = (borderWidth > 0.0), triangleCount = 3 * count - 2, vertexCount = 3 * triangleCount; + var succeed = this._ensureCapacity(this._vertexCount + vertexCount); + if (!succeed) + return; + + var inset = (outline == false ? 0.5 : 0.0); + for (i = 0; i < count - 2; i++) { + // v0 = sub(verts[0], multi(extrude[0].offset, inset)); + v0x = verts[0] - _extrude[0] * inset; + v0y = verts[1] - _extrude[1] * inset; + // v1 = sub(verts[i + 1], multi(extrude[i + 1].offset, inset)); + v1x = verts[i * 2 + 2] - _extrude[(i + 1) * 4] * inset; + v1y = verts[i * 2 + 3] - _extrude[(i + 1) * 4 + 1] * inset; + // v2 = sub(verts[i + 2], multi(extrude[i + 2].offset, inset)); + v2x = verts[i * 2 + 4] - _extrude[(i + 2) * 4] * inset; + v2y = verts[i * 2 + 5] - _extrude[(i + 2) * 4 + 1] * inset; + + this.appendVertexData(v0x, v0y, fillColor, 0, 0); + this.appendVertexData(v1x, v1y, fillColor, 0, 0); + this.appendVertexData(v2x, v2y, fillColor, 0, 0); + } + + var off0x, off0y, off1x, off1y, + bw = outline ? borderWidth : 0.5, + color = outline ? borderColor : fillColor, + in0x, in0y, in1x, in1y, out0x, out0y, out1x, out1y; + for (i = 0; i < count; i++) { + var j = (i + 1) % count; + v0x = verts[i * 2]; + v0y = verts[i * 2 + 1]; + v1x = verts[j * 2]; + v1y = verts[j * 2 + 1]; + + _n.x = _extrude[i * 4 + 2]; + _n.y = _extrude[i * 4 + 3]; + _nw.x = outline ? -_n.x : 0; + _nw.y = outline ? -_n.y : 0; + off0x = _extrude[i * 4]; + off0y = _extrude[i * 4 + 1]; + off1x = _extrude[j * 4]; + off1y = _extrude[j * 4 + 1]; + + in0x = v0x - off0x * bw; in0y = v0y - off0y * bw; + in1x = v1x - off1x * bw; in1y = v1y - off1y * bw; + out0x = v0x + off0x * bw; out0y = v0y + off0y * bw; + out1x = v1x + off1x * bw; out1y = v1y + off1y * bw; + + this.appendVertexData(in0x, in0y, color, _nw.x, _nw.y); + this.appendVertexData(in1x, in1y, color, _nw.x, _nw.y); + this.appendVertexData(out1x, out1y, color, _n.x, _n.y); + + this.appendVertexData(in0x, in0y, color, _nw.x, _nw.y); + this.appendVertexData(out0x, out0y, color, _n.x, _n.y); + this.appendVertexData(out1x, out1y, color, _n.x, _n.y); + } + _extrude.length = 0; + _vertices.length = 0; + this._dirty = true; + }, + + _drawSegments: function (verts, borderWidth, borderColor, closePoly) { + borderWidth = (borderWidth == null) ? this._lineWidth : borderWidth; + if (borderWidth <= 0) + return; + + borderColor = borderColor || this._drawColor; + if (borderColor.a == null) + borderColor.a = 255; + borderWidth *= 0.5; + + var v0x, v0y, v1x, v1y, v2x, v2y, + factor, offx, offy, + i, count = verts.length; + _extrude.length = 0; + for (i = 0; i < count; i += 2) { + v0x = verts[(i - 2 + count) % count]; + v0y = verts[(i - 1 + count) % count]; + v1x = verts[i]; + v1y = verts[i + 1]; + v2x = verts[(i + 2) % count]; + v2y = verts[(i + 3) % count]; + // var n1 = normalize(perp(sub(v1, v0))); + // var n2 = normalize(perp(sub(v2, v1))); + _n.x = v0y - v1y; _n.y = v1x - v0x; + _nw.x = v1y - v2y; _nw.y = v2x - v1x; + cc.pNormalizeIn(_n); + cc.pNormalizeIn(_nw); + // var offset = multi(add(n1, n2), 1.0 / (dot(n1, n2) + 1.0)); + factor = _n.x * _nw.x + _n.y * _nw.y + 1; + offx = (_n.x + _nw.x) / factor; + offy = (_n.y + _nw.y) / factor; + // extrude[i] = {offset: offset, n: n2}; + _extrude.push(offx, offy, _nw.x, _nw.y); + } + + // The actual input vertex count + count = count / 2; + var triangleCount = 3 * count - 2, vertexCount = 3 * triangleCount; + var succeed = this._ensureCapacity(this._vertexCount + vertexCount); + if (!succeed) + return; + + var len = closePoly ? count : count - 1, + off0x, off0y, off1x, off1y, + in0x, in0y, in1x, in1y, out0x, out0y, out1x, out1y; + for (i = 0; i < len; i++) { + var j = (i + 1) % count; + v0x = verts[i * 2]; + v0y = verts[i * 2 + 1]; + v1x = verts[j * 2]; + v1y = verts[j * 2 + 1]; + + _n.x = _extrude[i * 4 + 2]; + _n.y = _extrude[i * 4 + 3]; + off0x = _extrude[i * 4]; + off0y = _extrude[i * 4 + 1]; + off1x = _extrude[j * 4]; + off1y = _extrude[j * 4 + 1]; + in0x = v0x - off0x * borderWidth; in0y = v0y - off0y * borderWidth; + in1x = v1x - off1x * borderWidth; in1y = v1y - off1y * borderWidth; + out0x = v0x + off0x * borderWidth; out0y = v0y + off0y * borderWidth; + out1x = v1x + off1x * borderWidth; out1y = v1y + off1y * borderWidth; + + this.appendVertexData(in0x, in0y, borderColor, -_n.x, -_n.y); + this.appendVertexData(in1x, in1y, borderColor, -_n.x, -_n.y); + this.appendVertexData(out1x, out1y, borderColor, _n.x, _n.y); + + this.appendVertexData(in0x, in0y, borderColor, -_n.x, -_n.y); + this.appendVertexData(out0x, out0y, borderColor, _n.x, _n.y); + this.appendVertexData(out1x, out1y, borderColor, _n.x, _n.y); + } + _extrude.length = 0; + this._dirty = true; + }, + + clear: function () { + this.release(); + this._dirty = true; + }, + + _createRenderCmd: function () { + return new cc.DrawNode.WebGLRenderCmd(this); + } + }); + } +}); diff --git a/frameworks/cocos2d-html5/cocos2d/shape-nodes/CCDrawNodeCanvasRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/shape-nodes/CCDrawNodeCanvasRenderCmd.js new file mode 100644 index 0000000..9218945 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/shape-nodes/CCDrawNodeCanvasRenderCmd.js @@ -0,0 +1,138 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + + cc.DrawNode.CanvasRenderCmd = function (renderableObject) { + this._rootCtor(renderableObject); + this._needDraw = true; + this._buffer = null; + this._drawColor = null; + this._blendFunc = null; + }; + + + cc.DrawNode.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + cc.DrawNode.CanvasRenderCmd.prototype.constructor = cc.DrawNode.CanvasRenderCmd; + + cc.DrawNode.CanvasRenderCmd.prototype.getLocalBB = function () { + var node = this._node; + return node._localBB; + }; + + cc.extend(cc.DrawNode.CanvasRenderCmd.prototype, { + rendering: function (ctx, scaleX, scaleY) { + var wrapper = ctx || cc._renderContext, context = wrapper.getContext(), node = this._node; + var alpha = this._displayedOpacity / 255; + if (alpha === 0) + return; + + wrapper.setTransform(this._worldTransform, scaleX, scaleY); + + //context.save(); + wrapper.setGlobalAlpha(alpha); + if ((this._blendFunc && (this._blendFunc.src === cc.SRC_ALPHA) && (this._blendFunc.dst === cc.ONE))) + wrapper.setCompositeOperation('lighter'); //todo: need refactor + var locBuffer = this._buffer; + for (var i = 0, len = locBuffer.length; i < len; i++) { + var element = locBuffer[i]; + switch (element.type) { + case cc.DrawNode.TYPE_DOT: + this._drawDot(wrapper, element, scaleX, scaleY); + break; + case cc.DrawNode.TYPE_SEGMENT: + this._drawSegment(wrapper, element, scaleX, scaleY); + break; + case cc.DrawNode.TYPE_POLY: + this._drawPoly(wrapper, element, scaleX, scaleY); + break; + } + } + //context.restore(); //todo It can be reserve + }, + + _drawDot: function (wrapper, element) { + var locColor = element.fillColor, locPos = element.verts[0], locRadius = element.lineWidth; + + var ctx = wrapper.getContext(); + wrapper.setFillStyle("rgba(" + (0 | locColor.r) + "," + (0 | locColor.g) + "," + (0 | locColor.b) + "," + locColor.a / 255 + ")"); + + ctx.beginPath(); + ctx.arc(locPos.x, -locPos.y, locRadius, 0, Math.PI * 2, false); + ctx.closePath(); + ctx.fill(); + }, + + _drawSegment: function (wrapper, element, scaleX) { + var locColor = element.lineColor; + var locFrom = element.verts[0], locTo = element.verts[1]; + var locLineWidth = element.lineWidth, locLineCap = element.lineCap; + + var ctx = wrapper.getContext(); + wrapper.setStrokeStyle("rgba(" + (0 | locColor.r) + "," + (0 | locColor.g) + "," + (0 | locColor.b) + "," + locColor.a / 255 + ")"); + + ctx.lineWidth = locLineWidth * scaleX; + ctx.beginPath(); + ctx.lineCap = locLineCap; + ctx.moveTo(locFrom.x, -locFrom.y); + ctx.lineTo(locTo.x, -locTo.y); + ctx.stroke(); + }, + + _drawPoly: function (wrapper, element, scaleX) { + var locVertices = element.verts, locLineCap = element.lineCap; + if (locVertices == null) + return; + + var locFillColor = element.fillColor, locLineWidth = element.lineWidth; + var locLineColor = element.lineColor, locIsClosePolygon = element.isClosePolygon; + var locIsFill = element.isFill, locIsStroke = element.isStroke; + + var ctx = wrapper.getContext(); + var firstPoint = locVertices[0]; + ctx.lineCap = locLineCap; + if (locFillColor) + wrapper.setFillStyle("rgba(" + (0 | locFillColor.r) + "," + (0 | locFillColor.g) + "," + + (0 | locFillColor.b) + "," + locFillColor.a / 255 + ")"); + if (locLineWidth) + ctx.lineWidth = locLineWidth * scaleX; + if (locLineColor) + wrapper.setStrokeStyle("rgba(" + (0 | locLineColor.r) + "," + (0 | locLineColor.g) + "," + + (0 | locLineColor.b) + "," + locLineColor.a / 255 + ")"); + + ctx.beginPath(); + ctx.moveTo(firstPoint.x, -firstPoint.y); + for (var i = 1, len = locVertices.length; i < len; i++) + ctx.lineTo(locVertices[i].x, -locVertices[i].y); + + if (locIsClosePolygon) + ctx.closePath(); + if (locIsFill) + ctx.fill(); + if (locIsStroke) + ctx.stroke(); + } + }); + +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/shape-nodes/CCDrawNodeWebGLRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/shape-nodes/CCDrawNodeWebGLRenderCmd.js new file mode 100644 index 0000000..cd7ea51 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/shape-nodes/CCDrawNodeWebGLRenderCmd.js @@ -0,0 +1,52 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + cc.DrawNode.WebGLRenderCmd = function (renderableObject) { + this._rootCtor(renderableObject); + this._needDraw = true; + this._matrix = new cc.math.Matrix4(); + this._matrix.identity(); + }; + + cc.DrawNode.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + cc.DrawNode.WebGLRenderCmd.prototype.constructor = cc.DrawNode.WebGLRenderCmd; + + cc.DrawNode.WebGLRenderCmd.prototype.rendering = function (ctx) { + var node = this._node; + if (node._vertexCount > 0) { + var wt = this._worldTransform; + this._matrix.mat[0] = wt.a; + this._matrix.mat[4] = wt.c; + this._matrix.mat[12] = wt.tx; + this._matrix.mat[1] = wt.b; + this._matrix.mat[5] = wt.d; + this._matrix.mat[13] = wt.ty; + + cc.glBlendFunc(node._blendFunc.src, node._blendFunc.dst); + this._glProgramState.apply(this._matrix); + node._render(); + } + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/text-input/CCIMEDispatcher.js b/frameworks/cocos2d-html5/cocos2d/text-input/CCIMEDispatcher.js new file mode 100644 index 0000000..e22f6da --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/text-input/CCIMEDispatcher.js @@ -0,0 +1,535 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * IME Keyboard Notification Info structure + * @param {cc.Rect} begin the soft keyboard rectangle when animatin begin + * @param {cc.Rect} end the soft keyboard rectangle when animatin end + * @param {Number} duration the soft keyboard animation duration + */ +cc.IMEKeyboardNotificationInfo = function (begin, end, duration) { + this.begin = begin || cc.rect(0, 0, 0, 0); + this.end = end || cc.rect(0, 0, 0, 0); + this.duration = duration || 0; +}; + +/** + * Input method editor delegate. + * @class + * @extends cc.Class + */ +cc.IMEDelegate = cc.Class.extend(/** @lends cc.IMEDelegate# */{ + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + */ + ctor: function () { + cc.imeDispatcher.addDelegate(this); + }, + /** + * Remove delegate + */ + removeDelegate: function () { + cc.imeDispatcher.removeDelegate(this); + }, + /** + * Remove delegate + * @return {Boolean} + */ + attachWithIME: function () { + return cc.imeDispatcher.attachDelegateWithIME(this); + }, + /** + * Detach with IME + * @return {Boolean} + */ + detachWithIME: function () { + return cc.imeDispatcher.detachDelegateWithIME(this); + }, + + /** + * Decide the delegate instance is ready for receive ime message or not.
+ * Called by CCIMEDispatcher. + * @return {Boolean} + */ + canAttachWithIME: function () { + return false; + }, + + /** + * When the delegate detach with IME, this method call by CCIMEDispatcher. + */ + didAttachWithIME: function () { + }, + + /** + * Decide the delegate instance can stop receive ime message or not. + * @return {Boolean} + */ + canDetachWithIME: function () { + return false; + }, + + /** + * When the delegate detach with IME, this method call by CCIMEDispatcher. + */ + didDetachWithIME: function () { + }, + + /** + * Called by CCIMEDispatcher when some text input from IME. + */ + insertText: function (text, len) { + }, + + /** + * Called by CCIMEDispatcher when user clicked the backward key. + */ + deleteBackward: function () { + }, + + /** + * Called by CCIMEDispatcher for get text which delegate already has. + * @return {String} + */ + getContentText: function () { + return ""; + }, + + ////////////////////////////////////////////////////////////////////////// + // keyboard show/hide notification + ////////////////////////////////////////////////////////////////////////// + keyboardWillShow: function (info) { + }, + keyboardDidShow: function (info) { + }, + keyboardWillHide: function (info) { + }, + keyboardDidHide: function (info) { + } +}); + +/** + * cc.imeDispatcher is a singleton object which manage input message dispatching. + * @class + * @name cc.imeDispatcher + */ +cc.IMEDispatcher = cc.Class.extend(/** @lends cc.imeDispatcher# */{ + _domInputControl: null, + impl: null, + _currentInputString: "", + _lastClickPosition: null, + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + */ + ctor: function () { + this.impl = new cc.IMEDispatcher.Impl(); + this._lastClickPosition = cc.p(0, 0); + }, + + init: function () { + if (cc.sys.isMobile) + return; + this._domInputControl = cc.$("#imeDispatcherInput"); + if (!this._domInputControl) { + this._domInputControl = cc.$new("input"); + this._domInputControl.setAttribute("type", "text"); + this._domInputControl.setAttribute("id", "imeDispatcherInput"); + this._domInputControl.resize(0.0, 0.0); + this._domInputControl.translates(0, 0); + this._domInputControl.style.opacity = "0"; + //this._domInputControl.style.filter = "alpha(opacity = 0)"; + this._domInputControl.style.fontSize = "1px"; + this._domInputControl.setAttribute('tabindex', 2); + this._domInputControl.style.position = "absolute"; + this._domInputControl.style.top = 0; + this._domInputControl.style.left = 0; + document.body.appendChild(this._domInputControl); + } + var selfPointer = this; + //add event listener + this._domInputControl.addEventListener("input", function () { + selfPointer._processDomInputString(selfPointer._domInputControl.value); + }, false); + this._domInputControl.addEventListener("keydown", function (e) { + // ignore tab key + if (e.keyCode === cc.KEY.tab) { + e.stopPropagation(); + e.preventDefault(); + } else if (e.keyCode === cc.KEY.enter) { + selfPointer.dispatchInsertText("\n", 1); + e.stopPropagation(); + e.preventDefault(); + } + }, false); + + if (/msie/i.test(navigator.userAgent)) { + this._domInputControl.addEventListener("keyup", function (e) { + if (e.keyCode === cc.KEY.backspace) { + selfPointer._processDomInputString(selfPointer._domInputControl.value); + } + }, false); + } + + window.addEventListener('mousedown', function (event) { + var tx = event.pageX || 0; + var ty = event.pageY || 0; + + selfPointer._lastClickPosition.x = tx; + selfPointer._lastClickPosition.y = ty; + }, false); + }, + + _processDomInputString: function (text) { + var i, startPos; + var len = this._currentInputString.length < text.length ? this._currentInputString.length : text.length; + for (startPos = 0; startPos < len; startPos++) { + if (text[startPos] !== this._currentInputString[startPos]) + break; + } + var delTimes = this._currentInputString.length - startPos; + var insTimes = text.length - startPos; + for (i = 0; i < delTimes; i++) + this.dispatchDeleteBackward(); + + for (i = 0; i < insTimes; i++) + this.dispatchInsertText(text[startPos + i], 1); + + this._currentInputString = text; + }, + + /** + * Dispatch the input text from ime + * @param {String} text + * @param {Number} len + */ + dispatchInsertText: function (text, len) { + if (!this.impl || !text || len <= 0) + return; + + // there is no delegate attach with ime + if (!this.impl._delegateWithIme) + return; + + this.impl._delegateWithIme.insertText(text, len); + }, + + /** + * Dispatch the delete backward operation + */ + dispatchDeleteBackward: function () { + if (!this.impl) { + return; + } + + // there is no delegate attach with ime + if (!this.impl._delegateWithIme) + return; + + this.impl._delegateWithIme.deleteBackward(); + }, + + /** + * Get the content text, which current CCIMEDelegate which attached with IME has. + * @return {String} + */ + getContentText: function () { + if (this.impl && this.impl._delegateWithIme) { + var pszContentText = this.impl._delegateWithIme.getContentText(); + return (pszContentText) ? pszContentText : ""; + } + return ""; + }, + + /** + * Dispatch keyboard notification + * @param {cc.IMEKeyboardNotificationInfo} info + */ + dispatchKeyboardWillShow: function (info) { + if (this.impl) { + for (var i = 0; i < this.impl._delegateList.length; i++) { + var delegate = this.impl._delegateList[i]; + if (delegate) { + delegate.keyboardWillShow(info); + } + } + } + }, + + /** + * Dispatch keyboard notification + * @param {cc.IMEKeyboardNotificationInfo} info + */ + dispatchKeyboardDidShow: function (info) { + if (this.impl) { + for (var i = 0; i < this.impl._delegateList.length; i++) { + var delegate = this.impl._delegateList[i]; + if (delegate) + delegate.keyboardDidShow(info); + } + } + }, + + /** + * Dispatch keyboard notification + * @param {cc.IMEKeyboardNotificationInfo} info + */ + dispatchKeyboardWillHide: function (info) { + if (this.impl) { + for (var i = 0; i < this.impl._delegateList.length; i++) { + var delegate = this.impl._delegateList[i]; + if (delegate) { + delegate.keyboardWillHide(info); + } + } + } + }, + + /** + * Dispatch keyboard notification + * @param {cc.IMEKeyboardNotificationInfo} info + */ + dispatchKeyboardDidHide: function (info) { + if (this.impl) { + for (var i = 0; i < this.impl._delegateList.length; i++) { + var delegate = this.impl._delegateList[i]; + if (delegate) { + delegate.keyboardDidHide(info); + } + } + } + }, + + /** + * Add delegate to concern ime msg + * @param {cc.IMEDelegate} delegate + * @example + * //example + * cc.imeDispatcher.addDelegate(this); + */ + addDelegate: function (delegate) { + if (!delegate || !this.impl) + return; + + if (this.impl._delegateList.indexOf(delegate) > -1) { + // delegate already in list + return; + } + this.impl._delegateList.splice(0, 0, delegate); + }, + + /** + * Attach the pDeleate with ime. + * @param {cc.IMEDelegate} delegate + * @return {Boolean} If the old delegate can detattach with ime and the new delegate can attach with ime, return true, otherwise return false. + * @example + * //example + * var ret = cc.imeDispatcher.attachDelegateWithIME(this); + */ + attachDelegateWithIME: function (delegate) { + if (!this.impl || !delegate) + return false; + + // if delegate is not in delegate list, return + if (this.impl._delegateList.indexOf(delegate) === -1) + return false; + + if (this.impl._delegateWithIme) { + // if old delegate canDetachWithIME return false + // or delegate canAttachWithIME return false, + // do nothing. + if (!this.impl._delegateWithIme.canDetachWithIME() + || !delegate.canAttachWithIME()) + return false; + + // detach first + var pOldDelegate = this.impl._delegateWithIme; + this.impl._delegateWithIme = null; + pOldDelegate.didDetachWithIME(); + + this._focusDomInput(delegate); + return true; + } + + // havn't delegate attached with IME yet + if (!delegate.canAttachWithIME()) + return false; + + this._focusDomInput(delegate); + return true; + }, + + _focusDomInput: function (delegate) { + if (cc.sys.isMobile) { + this.impl._delegateWithIme = delegate; + delegate.didAttachWithIME(); + //prompt + this._currentInputString = delegate.string || ""; + + var tipMessage = delegate.getTipMessage ? delegate.getTipMessage() : "please enter your word:"; + // wechat cover the prompt function .So need use the Window.prototype.prompt + var userInput; + var win = window.Window; + if (win && win.prototype.prompt && win.prototype.prompt != prompt) { + userInput = win.prototype.prompt.call(window, tipMessage, this._currentInputString); + } else { + userInput = prompt(tipMessage, this._currentInputString); + } + if (userInput != null) + this._processDomInputString(userInput); + this.dispatchInsertText("\n", 1); + } else { + this.impl._delegateWithIme = delegate; + this._currentInputString = delegate.string || ""; + delegate.didAttachWithIME(); + this._domInputControl.focus(); + this._domInputControl.value = this._currentInputString; + this._domInputControlTranslate(); + } + }, + + _domInputControlTranslate: function () { + if (/msie/i.test(navigator.userAgent)) { + this._domInputControl.style.left = this._lastClickPosition.x + "px"; + this._domInputControl.style.top = this._lastClickPosition.y + "px"; + } else { + this._domInputControl.translates(this._lastClickPosition.x, this._lastClickPosition.y); + } + }, + + /** + * Detach the pDeleate with ime. + * @param {cc.IMEDelegate} delegate + * @return {Boolean} If the old delegate can detattach with ime and the new delegate can attach with ime, return true, otherwise return false. + * @example + * //example + * var ret = cc.imeDispatcher.detachDelegateWithIME(this); + */ + detachDelegateWithIME: function (delegate) { + if (!this.impl || !delegate) + return false; + + // if delegate is not the current delegate attached with ime, return + if (this.impl._delegateWithIme !== delegate) + return false; + + if (!delegate.canDetachWithIME()) + return false; + + this.impl._delegateWithIme = null; + delegate.didDetachWithIME(); + cc._canvas.focus(); + return true; + }, + + /** + * Remove the delegate from the delegates who concern ime msg + * @param {cc.IMEDelegate} delegate + * @example + * //example + * cc.imeDispatcher.removeDelegate(this); + */ + removeDelegate: function (delegate) { + if (!this.impl || !delegate) + return; + + // if delegate is not in delegate list, return + if (this.impl._delegateList.indexOf(delegate) === -1) + return; + + if (this.impl._delegateWithIme) { + if (delegate === this.impl._delegateWithIme) { + this.impl._delegateWithIme = null; + } + } + cc.arrayRemoveObject(this.impl._delegateList, delegate); + }, + + /** + * Process keydown's keycode + * @param {Number} keyCode + * @example + * //example + * document.addEventListener("keydown", function (e) { + * cc.imeDispatcher.processKeycode(e.keyCode); + * }); + */ + processKeycode: function (keyCode) { + if (keyCode < 32) { + if (keyCode === cc.KEY.backspace) { + this.dispatchDeleteBackward(); + } else if (keyCode === cc.KEY.enter) { + this.dispatchInsertText("\n", 1); + } else if (keyCode === cc.KEY.tab) { + //tab input + } else if (keyCode === cc.KEY.escape) { + //ESC input + } + } else if (keyCode < 255) { + this.dispatchInsertText(String.fromCharCode(keyCode), 1); + } else { + // + } + } +}); + +/** + * Create the cc.IMEDispatcher.Imp Object.
+ * This is the inner class... + * @class + * @extends cc.Class + * @name cc.IMEDispatcher.Impl + */ +cc.IMEDispatcher.Impl = cc.Class.extend(/** @lends cc.IMEDispatcher.Impl# */{ + _delegateWithIme: null, + _delegateList: null, + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + */ + ctor: function () { + this._delegateList = []; + }, + /** + * Find delegate + * @param {cc.IMEDelegate} delegate + * @return {Number|Null} + */ + findDelegate: function (delegate) { + for (var i = 0; i < this._delegateList.length; i++) { + if (this._delegateList[i] === delegate) + return i; + } + return null; + } +}); + +// Initialize imeDispatcher singleton +cc.imeDispatcher = new cc.IMEDispatcher(); + +document.body ? + cc.imeDispatcher.init() : + window.addEventListener('load', function () { + cc.imeDispatcher.init(); + }, false); diff --git a/frameworks/cocos2d-html5/cocos2d/text-input/CCTextFieldTTF.js b/frameworks/cocos2d-html5/cocos2d/text-input/CCTextFieldTTF.js new file mode 100644 index 0000000..c582afb --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/text-input/CCTextFieldTTF.js @@ -0,0 +1,492 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * Text field delegate + * @class + * @extends cc.Class + */ +cc.TextFieldDelegate = cc.Class.extend(/** @lends cc.TextFieldDelegate# */{ + /** + * If the sender doesn't want to attach with IME, return true; + * @param {cc.TextFieldTTF} sender + * @return {Boolean} + */ + onTextFieldAttachWithIME: function (sender) { + return false; + }, + + /** + * If the sender doesn't want to detach with IME, return true; + * @param {cc.TextFieldTTF} sender + * @return {Boolean} + */ + onTextFieldDetachWithIME: function (sender) { + return false; + }, + + /** + * If the sender doesn't want to insert the text, return true; + * @param {cc.TextFieldTTF} sender + * @param {String} text + * @param {Number} len + * @return {Boolean} + */ + onTextFieldInsertText: function (sender, text, len) { + return false + }, + + /** + * If the sender doesn't want to delete the delText, return true; + * @param {cc.TextFieldTTF} sender + * @param {String} delText + * @param {Number} len + * @return {Boolean} + */ + onTextFieldDeleteBackward: function (sender, delText, len) { + return false; + }, + + /** + * If doesn't want draw sender as default, return true. + * @param {cc.TextFieldTTF} sender + * @return {Boolean} + */ + onDraw: function (sender) { + return false; + } +}); + +/** + * A simple text input field with TTF font. + * @class + * @extends cc.LabelTTF + * + * @property {cc.Node} delegate - Delegate + * @property {Number} charCount - <@readonly> Characators count + * @property {String} placeHolder - Place holder for the field + * @property {cc.Color} colorSpaceHolder + * + * @param {String} placeholder + * @param {cc.Size} dimensions + * @param {Number} alignment + * @param {String} fontName + * @param {Number} fontSize + * + * @example + * //example + * // When five parameters + * var textField = new cc.TextFieldTTF("", cc.size(100,50), cc.TEXT_ALIGNMENT_LEFT,"Arial", 32); + * // When three parameters + * var textField = new cc.TextFieldTTF("", "Arial", 32); + */ +cc.TextFieldTTF = cc.LabelTTF.extend(/** @lends cc.TextFieldTTF# */{ + delegate: null, + colorSpaceHolder: null, + + _colorText: null, + _lens: null, + _inputText: "", + _placeHolder: "", + _charCount: 0, + _className: "TextFieldTTF", + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * creates a cc.TextFieldTTF from a fontName, alignment, dimension and font size. + * @param {String} placeholder + * @param {cc.Size} dimensions + * @param {Number} alignment + * @param {String} fontName + * @param {Number} fontSize + */ + ctor: function (placeholder, dimensions, alignment, fontName, fontSize) { + this.colorSpaceHolder = cc.color(127, 127, 127); + this._colorText = cc.color(255, 255, 255, 255); + cc.LabelTTF.prototype.ctor.call(this); + + if (fontSize !== undefined) { + this.initWithPlaceHolder("", dimensions, alignment, fontName, fontSize); + if (placeholder) + this.setPlaceHolder(placeholder); + } else if (fontName === undefined && alignment !== undefined) { + this.initWithString("", arguments[1], arguments[2]); + if (placeholder) + this.setPlaceHolder(placeholder); + } + }, + + onEnter: function () { + cc.LabelTTF.prototype.onEnter.call(this); + cc.imeDispatcher.addDelegate(this); + }, + + onExit: function () { + cc.LabelTTF.prototype.onExit.call(this); + cc.imeDispatcher.removeDelegate(this); + }, + + /** + * Gets the delegate. + * @return {cc.Node} + */ + getDelegate: function () { + return this.delegate; + }, + + /** + * Set the delegate. + * @param {cc.Node} value + */ + setDelegate: function (value) { + this.delegate = value; + }, + + /** + * Gets the char count. + * @return {Number} + */ + getCharCount: function () { + return this._charCount; + }, + + /** + * Returns the color of space holder. + * @return {cc.Color} + */ + getColorSpaceHolder: function () { + return cc.color(this.colorSpaceHolder); + }, + + /** + * Sets the color of space holder. + * @param {cc.Color} value + */ + setColorSpaceHolder: function (value) { + this.colorSpaceHolder.r = value.r; + this.colorSpaceHolder.g = value.g; + this.colorSpaceHolder.b = value.b; + this.colorSpaceHolder.a = cc.isUndefined(value.a) ? 255 : value.a; + if (!this._inputText.length) + this.setColor(this.colorSpaceHolder); + }, + + /** + * Sets the color of cc.TextFieldTTF's text. + * @param {cc.Color} textColor + */ + setTextColor: function (textColor) { + this._colorText.r = textColor.r; + this._colorText.g = textColor.g; + this._colorText.b = textColor.b; + this._colorText.a = cc.isUndefined(textColor.a) ? 255 : textColor.a; + if (this._inputText.length) + this.setColor(this._colorText); + }, + + /** + * Initializes the cc.TextFieldTTF with a font name, alignment, dimension and font size + * @param {String} placeholder + * @param {cc.Size} dimensions + * @param {Number} alignment + * @param {String} fontName + * @param {Number} fontSize + * @return {Boolean} + * @example + * //example + * var textField = new cc.TextFieldTTF(); + * // When five parameters + * textField.initWithPlaceHolder("", cc.size(100,50), cc.TEXT_ALIGNMENT_LEFT,"Arial", 32); + * // When three parameters + * textField.initWithPlaceHolder("", "Arial", 32); + */ + initWithPlaceHolder: function (placeholder, dimensions, alignment, fontName, fontSize) { + switch (arguments.length) { + case 5: + if (placeholder) + this.setPlaceHolder(placeholder); + return this.initWithString(this._placeHolder, fontName, fontSize, dimensions, alignment); + break; + case 3: + if (placeholder) + this.setPlaceHolder(placeholder); + return this.initWithString(this._placeHolder, arguments[1], arguments[2]); + break; + default: + throw new Error("Argument must be non-nil "); + break; + } + }, + + /** + * Input text property + * @param {String} text + */ + setString: function (text) { + text = String(text); + this._inputText = text || ""; + + // if there is no input text, display placeholder instead + if (!this._inputText.length) { + cc.LabelTTF.prototype.setString.call(this, this._placeHolder); + this.setColor(this.colorSpaceHolder); + } else { + cc.LabelTTF.prototype.setString.call(this, this._inputText); + this.setColor(this._colorText); + } + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) + this._renderCmd._updateTexture(); + this._charCount = this._inputText.length; + }, + + /** + * Gets the string + * @return {String} + */ + getString: function () { + return this._inputText; + }, + + /** + * Set the place holder.
+ * display this string if string equal "". + * @param {String} text + */ + setPlaceHolder: function (text) { + this._placeHolder = text || ""; + if (!this._inputText.length) { + cc.LabelTTF.prototype.setString.call(this, this._placeHolder); + this.setColor(this.colorSpaceHolder); + } + }, + + /** + * Gets the place holder.
+ * default display string. + * @return {String} + */ + getPlaceHolder: function () { + return this._placeHolder; + }, + + /** + * Render function using the canvas 2d context or WebGL context, internal usage only, please do not call this function. + * @param {CanvasRenderingContext2D | WebGLRenderingContext} ctx The render context + */ + draw: function (ctx) { + //console.log("size",this._contentSize); + var context = ctx || cc._renderContext; + if (this.delegate && this.delegate.onDraw(this)) + return; + + cc.LabelTTF.prototype.draw.call(this, context); + }, + + ////////////////////////////////////////////////////////////////////////// + // CCIMEDelegate interface + ////////////////////////////////////////////////////////////////////////// + /** + * Open keyboard and receive input text. + * @return {Boolean} + */ + attachWithIME: function () { + return cc.imeDispatcher.attachDelegateWithIME(this); + }, + + /** + * End text input and close keyboard. + * @return {Boolean} + */ + detachWithIME: function () { + return cc.imeDispatcher.detachDelegateWithIME(this); + }, + + /** + * Return whether to allow attach with IME. + * @return {Boolean} + */ + canAttachWithIME: function () { + return (this.delegate) ? (!this.delegate.onTextFieldAttachWithIME(this)) : true; + }, + + /** + * When the delegate detach with IME, this method call by CCIMEDispatcher. + */ + didAttachWithIME: function () { + }, + + /** + * Return whether to allow detach with IME. + * @return {Boolean} + */ + canDetachWithIME: function () { + return (this.delegate) ? (!this.delegate.onTextFieldDetachWithIME(this)) : true; + }, + + /** + * When the delegate detach with IME, this method call by CCIMEDispatcher. + */ + didDetachWithIME: function () { + }, + + /** + * Delete backward + */ + deleteBackward: function () { + var strLen = this._inputText.length; + if (strLen === 0) + return; + + // get the delete byte number + var deleteLen = 1; // default, erase 1 byte + + if (this.delegate && this.delegate.onTextFieldDeleteBackward(this, this._inputText[strLen - deleteLen], deleteLen)) { + // delegate don't want delete backward + return; + } + + // if delete all text, show space holder string + if (strLen <= deleteLen) { + this._inputText = ""; + this._charCount = 0; + cc.LabelTTF.prototype.setString.call(this, this._placeHolder); + this.setColor(this.colorSpaceHolder); + return; + } + + // set new input text + this.string = this._inputText.substring(0, strLen - deleteLen); + }, + + /** + * Remove delegate + */ + removeDelegate: function () { + cc.imeDispatcher.removeDelegate(this); + }, + + _tipMessage: "please enter your word:", + /** + * Sets the input tip message to show on mobile browser. (mobile Web only) + * @param {string} tipMessage + */ + setTipMessage: function (tipMessage) { + if (tipMessage == null) + return; + this._tipMessage = tipMessage; + }, + + /** + * Gets the input tip message to show on mobile browser. (mobile Web only) + * @returns {string} + */ + getTipMessage: function () { + return this._tipMessage; + }, + + /** + * Append the text.
+ * Input the character. + * @param {String} text + * @param {Number} len + */ + insertText: function (text, len) { + var sInsert = text; + + // insert \n means input end + var pos = sInsert.indexOf('\n'); + if (pos > -1) { + sInsert = sInsert.substring(0, pos); + } + + if (sInsert.length > 0) { + if (this.delegate && this.delegate.onTextFieldInsertText(this, sInsert, sInsert.length)) { + // delegate doesn't want insert text + return; + } + + var sText = this._inputText + sInsert; + this._charCount = sText.length; + this.string = sText; + } + + if (pos === -1) + return; + + // '\n' has inserted, let delegate process first + if (this.delegate && this.delegate.onTextFieldInsertText(this, "\n", 1)) + return; + + // if delegate hasn't process, detach with ime as default + this.detachWithIME(); + }, + + /** + * Gets the input text. + * @return {String} + */ + getContentText: function () { + return this._inputText; + }, + + ////////////////////////////////////////////////////////////////////////// + // keyboard show/hide notification + ////////////////////////////////////////////////////////////////////////// + keyboardWillShow: function (info) { + }, + keyboardDidShow: function (info) { + }, + keyboardWillHide: function (info) { + }, + keyboardDidHide: function (info) { + } +}); + +var _p = cc.TextFieldTTF.prototype; + +// Extended properties +/** @expose */ +_p.charCount; +cc.defineGetterSetter(_p, "charCount", _p.getCharCount); +/** @expose */ +_p.placeHolder; +cc.defineGetterSetter(_p, "placeHolder", _p.getPlaceHolder, _p.setPlaceHolder); + +/** + * Please use new TextFieldTTF instead.
+ * Creates a cc.TextFieldTTF from a fontName, alignment, dimension and font size. + * @deprecated since v3.0 Please use new TextFieldTTF instead. + * @param {String} placeholder + * @param {cc.Size} dimensions + * @param {Number} alignment + * @param {String} fontName + * @param {Number} fontSize + * @return {cc.TextFieldTTF|Null} + */ +cc.TextFieldTTF.create = function (placeholder, dimensions, alignment, fontName, fontSize) { + return new cc.TextFieldTTF(placeholder, dimensions, alignment, fontName, fontSize); +}; + diff --git a/frameworks/cocos2d-html5/cocos2d/tilemap/CCTGAlib.js b/frameworks/cocos2d-html5/cocos2d/tilemap/CCTGAlib.js new file mode 100644 index 0000000..76bd69b --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/tilemap/CCTGAlib.js @@ -0,0 +1,438 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * @constant + * @type Number + */ +cc.TGA_OK = 0; + +/** + * @constant + * @type Number + */ +cc.TGA_ERROR_FILE_OPEN = 1; + +/** + * @constant + * @type Number + */ +cc.TGA_ERROR_READING_FILE = 2; + +/** + * @constant + * @type Number + */ +cc.TGA_ERROR_INDEXED_COLOR = 3; + +/** + * @constant + * @type Number + */ +cc.TGA_ERROR_MEMORY = 4; + +/** + * @constant + * @type Number + */ +cc.TGA_ERROR_COMPRESSED_FILE = 5; + +/** + * TGA format + * @param {Number} status + * @param {Number} type + * @param {Number} pixelDepth + * @param {Number} width map width + * @param {Number} height map height + * @param {Array} imageData raw data + * @param {Number} flipped + * @constructor + */ +cc.ImageTGA = function (status, type, pixelDepth, width, height, imageData, flipped) { + this.status = status || 0; + this.type = type || 0; + this.pixelDepth = pixelDepth || 0; + this.width = width || 0; + this.height = height || 0; + this.imageData = imageData || []; + this.flipped = flipped || 0; +}; + +/** + * load the image header field from stream. We only keep those that matter! + * @param {Array} buffer + * @param {Number} bufSize + * @param {cc.ImageTGA} psInfo + * @return {Boolean} + */ +cc.tgaLoadHeader = function (buffer, bufSize, psInfo) { + var step = 2; + if (step + 1 > bufSize) + return false; + + var binaryReader = new cc.BinaryStreamReader(buffer); + + binaryReader.setOffset(step); + psInfo.type = binaryReader.readByte(); + step += 10; // . step += sizeof(unsigned char) * 2; step += sizeof(signed short) * 4; + + if (step + 4 + 1 > bufSize) + return false; + binaryReader.setOffset(step); + psInfo.width = binaryReader.readUnsignedShort(); + psInfo.height = binaryReader.readUnsignedInteger(); + psInfo.pixelDepth = binaryReader.readByte(); + + step += 5; // . step += sizeof(unsigned char); step += sizeof(signed short) * 2; + if (step + 1 > bufSize) + return false; + + var garbage = binaryReader.readByte(); + psInfo.flipped = 0; + if (garbage & 0x20) + psInfo.flipped = 1; + return true; +}; + +/** + * loads the image pixels. You shouldn't call this function directly. + * @param {Array} buffer + * @param {Number} bufSize + * @param {cc.ImageTGA} psInfo + * @return {Boolean} + */ +cc.tgaLoadImageData = function (buffer, bufSize, psInfo) { + var mode, total, i, aux; + var step = 18; // .size_t step = (sizeof(unsigned char) + sizeof(signed short)) * 6; + + // mode equal the number of components for each pixel + mode = 0 | (psInfo.pixelDepth / 2); + // total is the number of unsigned chars we'll have to read + total = psInfo.height * psInfo.width * mode; + + if (step + total > bufSize) + return false; + + psInfo.imageData = cc.__getSubArray(buffer, step, step + total); + + // mode=3 or 4 implies that the image is RGB(A). However TGA + // stores it as BGR(A) so we'll have to swap R and B. + if (mode >= 3) { + for (i = 0; i < total; i += mode) { + aux = psInfo.imageData[i]; + psInfo.imageData[i] = psInfo.imageData[i + 2]; + psInfo.imageData[i + 2] = aux; + } + } + return true; +}; + +/** + * converts RGB to grayscale + * @param {cc.ImageTGA} psInfo + */ +cc.tgaRGBtogreyscale = function (psInfo) { + var i, j; + + // if the image is already grayscale do nothing + if (psInfo.pixelDepth === 8) + return; + + // compute the number of actual components + var mode = psInfo.pixelDepth / 8; + + // allocate an array for the new image data + var newImageData = new Uint8Array(psInfo.height * psInfo.width); + if (newImageData === null) + return; + + // convert pixels: grayscale = o.30 * R + 0.59 * G + 0.11 * B + for (i = 0, j = 0; j < psInfo.width * psInfo.height; i += mode, j++) + newImageData[j] = (0.30 * psInfo.imageData[i] + 0.59 * psInfo.imageData[i + 1] + 0.11 * psInfo.imageData[i + 2]); + + // reassign pixelDepth and type according to the new image type + psInfo.pixelDepth = 8; + psInfo.type = 3; + // reassigning imageData to the new array. + psInfo.imageData = newImageData; +}; + +/** + * releases the memory used for the image + * @param {cc.ImageTGA} psInfo + */ +cc.tgaDestroy = function (psInfo) { + if (!psInfo) + return; + + psInfo.imageData = null; + psInfo = null; +}; + +/** + * Load RLE image data + * @param buffer + * @param bufSize + * @param psInfo + * @returns {boolean} + */ +cc.tgaLoadRLEImageData = function (buffer, bufSize, psInfo) { + var mode, total, i, index = 0, skip = 0, flag = 0; + var aux = [], runlength = 0; + + var step = 18; // . size_t step = (sizeof(unsigned char) + sizeof(signed short)) * 6; + + // mode equal the number of components for each pixel + mode = psInfo.pixelDepth / 8; + // total is the number of unsigned chars we'll have to read + total = psInfo.height * psInfo.width; + + for (i = 0; i < total; i++) { + // if we have a run length pending, run it + if (runlength !== 0) { + // we do, update the run length count + runlength--; + skip = (flag !== 0); + } else { + // otherwise, read in the run length token + if (step + 1 > bufSize) + break; + runlength = buffer[step]; + step += 1; + + // see if it's a RLE encoded sequence + flag = runlength & 0x80; + if (flag) + runlength -= 128; + skip = 0; + } + + // do we need to skip reading this pixel? + if (!skip) { + // no, read in the pixel data + if (step + mode > bufSize) + break; + aux = cc.__getSubArray(buffer, step, step + mode); + step += mode; + + // mode=3 or 4 implies that the image is RGB(A). However TGA + // stores it as BGR(A) so we'll have to swap R and B. + if (mode >= 3) { + var tmp = aux[0]; + aux[0] = aux[2]; + aux[2] = tmp; + } + } + + // add the pixel to our image + for (var j = 0; j < mode; j++) + psInfo.imageData[index + j] = aux[j]; + + index += mode; + } + + return true; +}; + +/** + * ImageTGA Flip + * @param {cc.ImageTGA} psInfo + */ +cc.tgaFlipImage = function (psInfo) { + // mode equal the number of components for each pixel + var mode = psInfo.pixelDepth / 8; + var rowbytes = psInfo.width * mode; + + for (var y = 0; y < (psInfo.height / 2); y++) { + var row = cc.__getSubArray(psInfo.imageData, y * rowbytes, y * rowbytes + rowbytes); + cc.__setDataToArray(cc.__getSubArray(psInfo.imageData, (psInfo.height - (y + 1)) * rowbytes, rowbytes), psInfo.imageData, y * rowbytes); + cc.__setDataToArray(row, psInfo.imageData, (psInfo.height - (y + 1)) * rowbytes); + } + psInfo.flipped = 0; +}; + +cc.__getSubArray = function (array, start, end) { + if (array instanceof Array) + return array.slice(start, end); + else + return array.subarray(start, end); +}; + +cc.__setDataToArray = function (sourceData, destArray, startIndex) { + for (var i = 0; i < sourceData.length; i++) + destArray[startIndex + i] = sourceData[i]; +}; + +/** + * Binary Stream Reader + * + * @class + * @param binaryData + */ +cc.BinaryStreamReader = cc.Class.extend({ + _binaryData: null, + _offset: 0, + + /** + *

The cc.BinaryStreamReader's constructor.
+ * This function will automatically be invoked when you create a node using new construction: "var node = new cc.BinaryStreamReader()".
+ * Override it to extend its behavior, remember to call "this._super()" in the extended "ctor" function.

+ * @param binaryData + */ + ctor: function (binaryData) { + this._binaryData = binaryData; + }, + + /** + * Set the binaryData. + * @param binaryData + */ + setBinaryData: function (binaryData) { + this._binaryData = binaryData; + this._offset = 0; + }, + + /** + * Gets the binaryData. + * @returns {Object} + */ + getBinaryData: function () { + return this._binaryData; + }, + + _checkSize: function (neededBits) { + if (!(this._offset + Math.ceil(neededBits / 8) < this._data.length)) + throw new Error("Index out of bound"); + }, + + _decodeFloat: function (precisionBits, exponentBits) { + var length = precisionBits + exponentBits + 1; + var size = length >> 3; + this._checkSize(length); + + var bias = Math.pow(2, exponentBits - 1) - 1; + var signal = this._readBits(precisionBits + exponentBits, 1, size); + var exponent = this._readBits(precisionBits, exponentBits, size); + var significand = 0; + var divisor = 2; + var curByte = 0; //length + (-precisionBits >> 3) - 1; + do { + var byteValue = this._readByte(++curByte, size); + var startBit = precisionBits % 8 || 8; + var mask = 1 << startBit; + while (mask >>= 1) { + if (byteValue & mask) + significand += 1 / divisor; + divisor *= 2; + } + } while (precisionBits -= startBit); + + this._offset += size; + + return exponent === (bias << 1) + 1 ? significand ? NaN : signal ? -Infinity : +Infinity + : (1 + signal * -2) * (exponent || significand ? !exponent ? Math.pow(2, -bias + 1) * significand + : Math.pow(2, exponent - bias) * (1 + significand) : 0); + }, + + _readByte: function (i, size) { + return this._data[this._offset + size - i - 1]; + }, + + _decodeInt: function (bits, signed) { + var x = this._readBits(0, bits, bits / 8), max = Math.pow(2, bits); + var result = signed && x >= max / 2 ? x - max : x; + + this._offset += bits / 8; + return result; + }, + + _shl: function (a, b) { + for (++b; --b; a = ((a %= 0x7fffffff + 1) & 0x40000000) === 0x40000000 ? a * 2 : (a - 0x40000000) * 2 + 0x7fffffff + 1) { + } + return a; + }, + + _readBits: function (start, length, size) { + var offsetLeft = (start + length) % 8; + var offsetRight = start % 8; + var curByte = size - (start >> 3) - 1; + var lastByte = size + (-(start + length) >> 3); + var diff = curByte - lastByte; + + var sum = (this._readByte(curByte, size) >> offsetRight) & ((1 << (diff ? 8 - offsetRight : length)) - 1); + + if (diff && offsetLeft) + sum += (this._readByte(lastByte++, size) & ((1 << offsetLeft) - 1)) << (diff-- << 3) - offsetRight; + + while (diff) + sum += this._shl(this._readByte(lastByte++, size), (diff-- << 3) - offsetRight); + + return sum; + }, + + readInteger: function () { + return this._decodeInt(32, true); + }, + + readUnsignedInteger: function () { + return this._decodeInt(32, false); + }, + + readSingle: function () { + return this._decodeFloat(23, 8); + }, + + readShort: function () { + return this._decodeInt(16, true); + }, + + readUnsignedShort: function () { + return this._decodeInt(16, false); + }, + + readByte: function () { + var readByte = this._data[this._offset]; + this._offset += 1; + return readByte; + }, + + readData: function (start, end) { + if (this._binaryData instanceof Array) { + return this._binaryData.slice(start, end); + } else { + //typed array + return this._binaryData.subarray(start, end); + } + }, + + setOffset: function (offset) { + this._offset = offset; + }, + + getOffset: function () { + return this._offset; + } +}); diff --git a/frameworks/cocos2d-html5/cocos2d/tilemap/CCTMXLayer.js b/frameworks/cocos2d-html5/cocos2d/tilemap/CCTMXLayer.js new file mode 100644 index 0000000..e78c8d0 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/tilemap/CCTMXLayer.js @@ -0,0 +1,803 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + *

cc.TMXLayer represents the TMX layer.

+ * + *

It is a subclass of cc.SpriteBatchNode. By default the tiles are rendered using a cc.TextureAtlas.
+ * If you modify a tile on runtime, then, that tile will become a cc.Sprite, otherwise no cc.Sprite objects are created.
+ * The benefits of using cc.Sprite objects as tiles are:
+ * - tiles (cc.Sprite) can be rotated/scaled/moved with a nice API

+ * + *

If the layer contains a property named "cc.vertexz" with an integer (in can be positive or negative),
+ * then all the tiles belonging to the layer will use that value as their OpenGL vertex Z for depth.

+ * + *

On the other hand, if the "cc.vertexz" property has the "automatic" value, then the tiles will use an automatic vertex Z value.
+ * Also before drawing the tiles, GL_ALPHA_TEST will be enabled, and disabled after drawing them. The used alpha func will be:

+ * + * glAlphaFunc( GL_GREATER, value )
+ * + *

"value" by default is 0, but you can change it from Tiled by adding the "cc_alpha_func" property to the layer.
+ * The value 0 should work for most cases, but if you have tiles that are semi-transparent, then you might want to use a different value, like 0.5.

+ * @class + * @extends cc.SpriteBatchNode + * + * @property {Array} tiles - Tiles for layer + * @property {cc.TMXTilesetInfo} tileset - Tileset for layer + * @property {Number} layerOrientation - Layer orientation + * @property {Array} properties - Properties from the layer. They can be added using tilemap editors + * @property {String} layerName - Name of the layer + * @property {Number} layerWidth - Width of the layer + * @property {Number} layerHeight - Height of the layer + * @property {Number} tileWidth - Width of a tile + * @property {Number} tileHeight - Height of a tile + */ +cc.TMXLayer = cc.SpriteBatchNode.extend(/** @lends cc.TMXLayer# */{ + tiles: null, + tileset: null, + layerOrientation: null, + properties: null, + layerName: "", + + _textures: null, + _texGrids: null, + _spriteTiles: null, + + //size of the layer in tiles + _layerSize: null, + _mapTileSize: null, + //TMX Layer supports opacity + _opacity: 255, + _minGID: null, + _maxGID: null, + //Only used when vertexZ is used + _vertexZvalue: null, + _useAutomaticVertexZ: null, + //used for optimization + _reusedTile: null, + _atlasIndexArray: null, + //used for retina display + _contentScaleFactor: null, + + _className:"TMXLayer", + + /** + * Creates a cc.TMXLayer with an tile set info, a layer info and a map info
+ * Constructor of cc.TMXLayer + * @param {cc.TMXTilesetInfo} tilesetInfo + * @param {cc.TMXLayerInfo} layerInfo + * @param {cc.TMXMapInfo} mapInfo + */ + ctor:function (tilesetInfo, layerInfo, mapInfo) { + cc.SpriteBatchNode.prototype.ctor.call(this); + this._descendants = []; + + this._layerSize = cc.size(0, 0); + this._mapTileSize = cc.size(0, 0); + this._spriteTiles = {}; + + if(mapInfo !== undefined) + this.initWithTilesetInfo(tilesetInfo, layerInfo, mapInfo); + }, + + _createRenderCmd: function(){ + if(cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return new cc.TMXLayer.CanvasRenderCmd(this); + else + return new cc.TMXLayer.WebGLRenderCmd(this); + }, + + _fillTextureGrids: function (tileset, texId) { + var tex = this._textures[texId]; + if (!tex.isLoaded()) { + tex.addEventListener("load", function () { + this._fillTextureGrids(tileset, texId); + }, this); + return; + } + if (!tileset.imageSize.width || !tileset.imageSize.height) { + tileset.imageSize.width = tex.width; + tileset.imageSize.height = tex.height; + } + var tw = tileset._tileSize.width, + th = tileset._tileSize.height, + imageW = tex._contentSize.width, + imageH = tex._contentSize.height, + spacing = tileset.spacing, + margin = tileset.margin, + + cols = Math.floor((imageW - margin*2 + spacing) / (tw + spacing)), + rows = Math.floor((imageH - margin*2 + spacing) / (th + spacing)), + count = rows * cols, + + gid = tileset.firstGid, + maxGid = tileset.firstGid + count, + grids = this._texGrids, + grid = null, + override = grids[gid] ? true : false, + + t, l, r, b; + + for (; gid < maxGid; ++gid) { + // Avoid overlapping + if (override && !grids[gid]) { + override = false; + } + if (!override && grids[gid]) { + break; + } + + grid = { + texId: texId, + x: 0, y: 0, width: tw, height: th, + t: 0, l: 0, r: 0, b: 0 + }; + tileset.rectForGID(gid, grid); + grid.t = grid.y / imageH; + grid.l = grid.x / imageW; + grid.r = (grid.x + grid.width) / imageW; + grid.b = (grid.y + grid.height) / imageH; + grids[gid] = grid; + } + }, + + /** + * Initializes a cc.TMXLayer with a tileset info, a layer info and a map info + * @param {cc.TMXTilesetInfo} tilesetInfo + * @param {cc.TMXLayerInfo} layerInfo + * @param {cc.TMXMapInfo} mapInfo + * @return {Boolean} + */ + initWithTilesetInfo:function (tilesetInfo, layerInfo, mapInfo) { + var size = layerInfo._layerSize; + var totalNumberOfTiles = parseInt(size.width * size.height); + + // layerInfo + this.layerName = layerInfo.name; + this.tiles = layerInfo._tiles; + this.properties = layerInfo.properties; + this._layerSize = size; + this._minGID = layerInfo._minGID; + this._maxGID = layerInfo._maxGID; + this._opacity = layerInfo._opacity; + + // tilesetInfo + this.tileset = tilesetInfo; + + // mapInfo + this.layerOrientation = mapInfo.orientation; + this._mapTileSize = mapInfo.getTileSize(); + + var tilesets = mapInfo._tilesets; + if (tilesets) { + this._textures = []; + this._texGrids = []; + var i, len = tilesets.length, tileset, tex; + for (i = 0; i < len; ++i) { + tileset = tilesets[i]; + tex = cc.textureCache.addImage(tileset.sourceImage); + this._textures.push(tex); + this._fillTextureGrids(tileset, i); + if (tileset === tilesetInfo) { + this._texture = tex; + } + } + } + + // offset (after layer orientation is set); + var offset = this._calculateLayerOffset(layerInfo.offset); + this.setPosition(cc.pointPixelsToPoints(offset)); + + // Parse cocos2d properties + this._parseInternalProperties(); + + this.setContentSize(cc.sizePixelsToPoints(cc.size(this._layerSize.width * this._mapTileSize.width, + this._layerSize.height * this._mapTileSize.height))); + this._useAutomaticVertexZ = false; + this._vertexZvalue = 0; + return true; + }, + + /** + * Gets layer size. + * @return {cc.Size} + */ + getLayerSize:function () { + return cc.size(this._layerSize.width, this._layerSize.height); + }, + + /** + * Set layer size + * @param {cc.Size} Var + */ + setLayerSize:function (Var) { + this._layerSize.width = Var.width; + this._layerSize.height = Var.height; + }, + + _getLayerWidth: function () { + return this._layerSize.width; + }, + _setLayerWidth: function (width) { + this._layerSize.width = width; + }, + _getLayerHeight: function () { + return this._layerSize.height; + }, + _setLayerHeight: function (height) { + this._layerSize.height = height; + }, + + /** + * Size of the map's tile (could be different from the tile's size) + * @return {cc.Size} + */ + getMapTileSize:function () { + return cc.size(this._mapTileSize.width,this._mapTileSize.height); + }, + + /** + * Set the map tile size. + * @param {cc.Size} Var + */ + setMapTileSize:function (Var) { + this._mapTileSize.width = Var.width; + this._mapTileSize.height = Var.height; + }, + + _getTileWidth: function () { + return this._mapTileSize.width; + }, + _setTileWidth: function (width) { + this._mapTileSize.width = width; + }, + _getTileHeight: function () { + return this._mapTileSize.height; + }, + _setTileHeight: function (height) { + this._mapTileSize.height = height; + }, + + /** + * Pointer to the map of tiles + * @return {Array} + */ + getTiles:function () { + return this.tiles; + }, + + /** + * Pointer to the map of tiles + * @param {Array} Var + */ + setTiles:function (Var) { + this.tiles = Var; + }, + + /** + * Tile set information for the layer + * @return {cc.TMXTilesetInfo} + */ + getTileset:function () { + return this.tileset; + }, + + /** + * Tile set information for the layer + * @param {cc.TMXTilesetInfo} Var + */ + setTileset:function (Var) { + this.tileset = Var; + }, + + /** + * Layer orientation, which is the same as the map orientation + * @return {Number} + */ + getLayerOrientation:function () { + return this.layerOrientation; + }, + + /** + * Layer orientation, which is the same as the map orientation + * @param {Number} Var + */ + setLayerOrientation:function (Var) { + this.layerOrientation = Var; + }, + + /** + * properties from the layer. They can be added using Tiled + * @return {Array} + */ + getProperties:function () { + return this.properties; + }, + + /** + * properties from the layer. They can be added using Tiled + * @param {Array} Var + */ + setProperties:function (Var) { + this.properties = Var; + }, + + /** + * Return the value for the specific property name + * @param {String} propertyName + * @return {*} + */ + getProperty:function (propertyName) { + return this.properties[propertyName]; + }, + + /** + * Gets the layer name + * @return {String} + */ + getLayerName:function () { + return this.layerName; + }, + + /** + * Set the layer name + * @param {String} layerName + */ + setLayerName:function (layerName) { + this.layerName = layerName; + }, + + /** + *

Dealloc the map that contains the tile position from memory.
+ * Unless you want to know at runtime the tiles positions, you can safely call this method.
+ * If you are going to call layer.getTileGIDAt() then, don't release the map

+ */ + releaseMap:function () { + this._spriteTiles = {}; + }, + + /** + *

Returns the tile (cc.Sprite) at a given a tile coordinate.
+ * The returned cc.Sprite will be already added to the cc.TMXLayer. Don't add it again.
+ * The cc.Sprite can be treated like any other cc.Sprite: rotated, scaled, translated, opacity, color, etc.
+ * You can remove either by calling:
+ * - layer.removeChild(sprite, cleanup);
+ * - or layer.removeTileAt(ccp(x,y));

+ * @param {cc.Point|Number} pos or x + * @param {Number} [y] + * @return {cc.Sprite} + */ + getTileAt: function (pos, y) { + if (pos === undefined) { + throw new Error("cc.TMXLayer.getTileAt(): pos should be non-null"); + } + var x = pos; + if (y === undefined) { + x = pos.x; + y = pos.y; + } + if (x >= this._layerSize.width || y >= this._layerSize.height || x < 0 || y < 0) { + throw new Error("cc.TMXLayer.getTileAt(): invalid position"); + } + if (!this.tiles) { + cc.log("cc.TMXLayer.getTileAt(): TMXLayer: the tiles map has been released"); + return null; + } + + var tile = null, gid = this.getTileGIDAt(x, y); + + // if GID == 0, then no tile is present + if (gid === 0) { + return tile; + } + + var z = 0 | (x + y * this._layerSize.width); + tile = this._spriteTiles[z]; + // tile not created yet. create it + if (!tile) { + var rect = this._texGrids[gid]; + var tex = this._textures[rect.texId]; + rect = cc.rectPixelsToPoints(rect); + + tile = new cc.Sprite(tex, rect); + tile.setPosition(this.getPositionAt(x, y)); + var vertexZ = this._vertexZForPos(x, y); + tile.setVertexZ(vertexZ); + tile.setAnchorPoint(0, 0); + tile.setOpacity(this._opacity); + + this.addChild(tile, vertexZ, z); + } + return tile; + }, + + /** + * Returns the tile gid at a given tile coordinate.
+ * if it returns 0, it means that the tile is empty.
+ * This method requires the the tile map has not been previously released (eg. don't call layer.releaseMap())
+ * @param {cc.Point|Number} pos or x + * @param {Number} [y] + * @return {Number} + */ + getTileGIDAt:function (pos, y) { + if (pos === undefined) { + throw new Error("cc.TMXLayer.getTileGIDAt(): pos should be non-null"); + } + var x = pos; + if (y === undefined) { + x = pos.x; + y = pos.y; + } + if (x >= this._layerSize.width || y >= this._layerSize.height || x < 0 || y < 0) { + throw new Error("cc.TMXLayer.getTileGIDAt(): invalid position"); + } + if (!this.tiles) { + cc.log("cc.TMXLayer.getTileGIDAt(): TMXLayer: the tiles map has been released"); + return null; + } + + var idx = 0 | (x + y * this._layerSize.width); + // Bits on the far end of the 32-bit global tile ID are used for tile flags + var tile = this.tiles[idx]; + + return (tile & cc.TMX_TILE_FLIPPED_MASK) >>> 0; + }, + // XXX: deprecated + // tileGIDAt:getTileGIDAt, + + /** + *

Sets the tile gid (gid = tile global id) at a given tile coordinate.
+ * The Tile GID can be obtained by using the method "tileGIDAt" or by using the TMX editor . Tileset Mgr +1.
+ * If a tile is already placed at that position, then it will be removed.

+ * @param {Number} gid + * @param {cc.Point|Number} posOrX position or x + * @param {Number} flagsOrY flags or y + * @param {Number} [flags] + */ + setTileGID: function(gid, posOrX, flagsOrY, flags) { + if (posOrX === undefined) { + throw new Error("cc.TMXLayer.setTileGID(): pos should be non-null"); + } + var pos; + if (flags !== undefined) { + pos = cc.p(posOrX, flagsOrY); + } else { + pos = posOrX; + flags = flagsOrY; + } + if (pos.x >= this._layerSize.width || pos.y >= this._layerSize.height || pos.x < 0 || pos.y < 0) { + throw new Error("cc.TMXLayer.setTileGID(): invalid position"); + } + if (!this.tiles) { + cc.log("cc.TMXLayer.setTileGID(): TMXLayer: the tiles map has been released"); + return; + } + if (gid !== 0 && gid < this.tileset.firstGid) { + cc.log( "cc.TMXLayer.setTileGID(): invalid gid:" + gid); + return; + } + + flags = flags || 0; + var currentFlags = this.getTileFlagsAt(pos); + var currentGID = this.getTileGIDAt(pos); + + if (currentGID !== gid || currentFlags !== flags) { + var gidAndFlags = (gid | flags) >>> 0; + // setting gid=0 is equal to remove the tile + if (gid === 0) + this.removeTileAt(pos); + else if (currentGID === 0) // empty tile. create a new one + this._updateTileForGID(gidAndFlags, pos); + else { // modifying an existing tile with a non-empty tile + var z = pos.x + pos.y * this._layerSize.width; + var sprite = this.getChildByTag(z); + if (sprite) { + var rect = this._texGrids[gid]; + var tex = this._textures[rect.texId]; + rect = cc.rectPixelsToPoints(rect); + sprite.setTexture(tex); + sprite.setTextureRect(rect, false); + if (flags != null) + this._setupTileSprite(sprite, pos, gidAndFlags); + + this.tiles[z] = gidAndFlags; + } else { + this._updateTileForGID(gidAndFlags, pos); + } + } + } + }, + + addChild: function (child, localZOrder, tag) { + cc.Node.prototype.addChild.call(this, child, localZOrder, tag); + if (tag !== undefined) { + this._spriteTiles[tag] = child; + child._vertexZ = this._vertexZ + cc.renderer.assignedZStep * tag / this.tiles.length; + // child._renderCmd._needDraw = false; + } + }, + + removeChild: function (child, cleanup) { + if (this._spriteTiles[child.tag]) { + this._spriteTiles[child.tag] = null; + // child._renderCmd._needDraw = true; + } + cc.Node.prototype.removeChild.call(this, child, cleanup); + }, + + /** + * lipped tiles can be changed dynamically + * @param {cc.Point|Number} pos or x + * @param {Number} [y] + * @return {Number} + */ + getTileFlagsAt:function (pos, y) { + if(!pos) + throw new Error("cc.TMXLayer.getTileFlagsAt(): pos should be non-null"); + if(y !== undefined) + pos = cc.p(pos, y); + if(pos.x >= this._layerSize.width || pos.y >= this._layerSize.height || pos.x < 0 || pos.y < 0) + throw new Error("cc.TMXLayer.getTileFlagsAt(): invalid position"); + if(!this.tiles){ + cc.log("cc.TMXLayer.getTileFlagsAt(): TMXLayer: the tiles map has been released"); + return null; + } + + var idx = 0 | (pos.x + pos.y * this._layerSize.width); + // Bits on the far end of the 32-bit global tile ID are used for tile flags + var tile = this.tiles[idx]; + + return (tile & cc.TMX_TILE_FLIPPED_ALL) >>> 0; + }, + // XXX: deprecated + // tileFlagAt:getTileFlagsAt, + + /** + * Removes a tile at given tile coordinate + * @param {cc.Point|Number} pos position or x + * @param {Number} [y] + */ + removeTileAt:function (pos, y) { + if (!pos) { + throw new Error("cc.TMXLayer.removeTileAt(): pos should be non-null"); + } + if (y !== undefined) { + pos = cc.p(pos, y); + } + if (pos.x >= this._layerSize.width || pos.y >= this._layerSize.height || pos.x < 0 || pos.y < 0) { + throw new Error("cc.TMXLayer.removeTileAt(): invalid position"); + } + if (!this.tiles) { + cc.log("cc.TMXLayer.removeTileAt(): TMXLayer: the tiles map has been released"); + return; + } + + var gid = this.getTileGIDAt(pos); + if (gid !== 0) { + var z = 0 | (pos.x + pos.y * this._layerSize.width); + // remove tile from GID map + this.tiles[z] = 0; + + // remove it from sprites and/or texture atlas + var sprite = this._spriteTiles[z]; + if (sprite) { + this.removeChild(sprite, true); + } + } + }, + + /** + * Returns the position in pixels of a given tile coordinate + * @param {cc.Point|Number} pos position or x + * @param {Number} [y] + * @return {cc.Point} + */ + getPositionAt:function (pos, y) { + if (y !== undefined) + pos = cc.p(pos, y); + var ret = cc.p(0,0); + switch (this.layerOrientation) { + case cc.TMX_ORIENTATION_ORTHO: + ret = this._positionForOrthoAt(pos); + break; + case cc.TMX_ORIENTATION_ISO: + ret = this._positionForIsoAt(pos); + break; + case cc.TMX_ORIENTATION_HEX: + ret = this._positionForHexAt(pos); + break; + } + return cc.pointPixelsToPoints(ret); + }, + // XXX: Deprecated. For backward compatibility only + // positionAt:getPositionAt, + + _positionForIsoAt:function (pos) { + return cc.p(this._mapTileSize.width / 2 * ( this._layerSize.width + pos.x - pos.y - 1), + this._mapTileSize.height / 2 * (( this._layerSize.height * 2 - pos.x - pos.y) - 2)); + }, + + _positionForOrthoAt:function (pos) { + return cc.p(pos.x * this._mapTileSize.width, + (this._layerSize.height - pos.y - 1) * this._mapTileSize.height); + }, + + _positionForHexAt:function (pos) { + var diffY = (pos.x % 2 === 1) ? (-this._mapTileSize.height / 2) : 0; + return cc.p(pos.x * this._mapTileSize.width * 3 / 4, + (this._layerSize.height - pos.y - 1) * this._mapTileSize.height + diffY); + }, + + _calculateLayerOffset:function (pos) { + var ret = cc.p(0,0); + switch (this.layerOrientation) { + case cc.TMX_ORIENTATION_ORTHO: + ret = cc.p(pos.x * this._mapTileSize.width, -pos.y * this._mapTileSize.height); + break; + case cc.TMX_ORIENTATION_ISO: + ret = cc.p((this._mapTileSize.width / 2) * (pos.x - pos.y), + (this._mapTileSize.height / 2 ) * (-pos.x - pos.y)); + break; + case cc.TMX_ORIENTATION_HEX: + if(pos.x !== 0 || pos.y !== 0) + cc.log("offset for hexagonal map not implemented yet"); + break; + } + return ret; + }, + + _updateTileForGID:function (gid, pos) { + if (!this._texGrids[gid]) { + return; + } + + var idx = 0 | (pos.x + pos.y * this._layerSize.width); + if (idx < this.tiles.length) { + this.tiles[idx] = gid; + } + }, + + //The layer recognizes some special properties, like cc_vertez + _parseInternalProperties:function () { + // if cc_vertex=automatic, then tiles will be rendered using vertexz + var vertexz = this.getProperty("cc_vertexz"); + if (vertexz) { + if (vertexz === "automatic") { + this._useAutomaticVertexZ = true; + var alphaFuncVal = this.getProperty("cc_alpha_func"); + var alphaFuncValue = 0; + if (alphaFuncVal) + alphaFuncValue = parseFloat(alphaFuncVal); + + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { //todo: need move to WebGL render cmd + this.shaderProgram = cc.shaderCache.programForKey(cc.SHADER_SPRITE_POSITION_TEXTURECOLORALPHATEST); + // NOTE: alpha test shader is hard-coded to use the equivalent of a glAlphaFunc(GL_GREATER) comparison + this.shaderProgram.use(); + this.shaderProgram.setUniformLocationWith1f(cc.UNIFORM_ALPHA_TEST_VALUE_S, alphaFuncValue); + } + } else + this._vertexZvalue = parseInt(vertexz, 10); + } + }, + + _setupTileSprite:function (sprite, pos, gid) { + var z = pos.x + pos.y * this._layerSize.width; + var posInPixel = this.getPositionAt(pos); + sprite.setPosition(posInPixel); + sprite.setVertexZ(this._vertexZForPos(pos)); + sprite.setAnchorPoint(0, 0); + sprite.setOpacity(this._opacity); + sprite.setFlippedX(false); + sprite.setFlippedY(false); + sprite.setRotation(0.0); + + // Rotation in tiled is achieved using 3 flipped states, flipping across the horizontal, vertical, and diagonal axes of the tiles. + if ((gid & cc.TMX_TILE_DIAGONAL_FLAG) >>> 0) { + // put the anchor in the middle for ease of rotation. + sprite.setAnchorPoint(0.5, 0.5); + sprite.setPosition(posInPixel.x + sprite.width/2, posInPixel.y + sprite.height/2); + + var flag = (gid & (cc.TMX_TILE_HORIZONTAL_FLAG | cc.TMX_TILE_VERTICAL_FLAG) >>> 0) >>> 0; + // handle the 4 diagonally flipped states. + if (flag === cc.TMX_TILE_HORIZONTAL_FLAG) + sprite.setRotation(90); + else if (flag === cc.TMX_TILE_VERTICAL_FLAG) + sprite.setRotation(270); + else if (flag === (cc.TMX_TILE_VERTICAL_FLAG | cc.TMX_TILE_HORIZONTAL_FLAG) >>> 0) { + sprite.setRotation(90); + sprite.setFlippedX(true); + } else { + sprite.setRotation(270); + sprite.setFlippedX(true); + } + } else { + if ((gid & cc.TMX_TILE_HORIZONTAL_FLAG) >>> 0) { + sprite.setFlippedX(true); + } + if ((gid & cc.TMX_TILE_VERTICAL_FLAG) >>> 0) { + sprite.setFlippedY(true); + } + } + }, + + _vertexZForPos:function (x, y) { + if (y === undefined) { + y = x.y; + x = x.x; + } + var ret = 0; + var maxVal = 0; + if (this._useAutomaticVertexZ) { + switch (this.layerOrientation) { + case cc.TMX_ORIENTATION_ISO: + maxVal = this._layerSize.width + this._layerSize.height; + ret = -(maxVal - (x + y)); + break; + case cc.TMX_ORIENTATION_ORTHO: + ret = -(this._layerSize.height - y); + break; + case cc.TMX_ORIENTATION_HEX: + cc.log("TMX Hexa zOrder not supported"); + break; + default: + cc.log("TMX invalid value"); + break; + } + } else { + ret = this._vertexZvalue; + } + return ret; + } +}); + +var _p = cc.TMXLayer.prototype; + +// Extended properties +/** @expose */ +_p.layerWidth; +cc.defineGetterSetter(_p, "layerWidth", _p._getLayerWidth, _p._setLayerWidth); +/** @expose */ +_p.layerHeight; +cc.defineGetterSetter(_p, "layerHeight", _p._getLayerHeight, _p._setLayerHeight); +/** @expose */ +_p.tileWidth; +cc.defineGetterSetter(_p, "tileWidth", _p._getTileWidth, _p._setTileWidth); +/** @expose */ +_p.tileHeight; +cc.defineGetterSetter(_p, "tileHeight", _p._getTileHeight, _p._setTileHeight); + + +/** + * Creates a cc.TMXLayer with an tile set info, a layer info and a map info + * @deprecated since v3.0 please use new cc.TMXLayer(tilesetInfo, layerInfo, mapInfo) instead. + * @param {cc.TMXTilesetInfo} tilesetInfo + * @param {cc.TMXLayerInfo} layerInfo + * @param {cc.TMXMapInfo} mapInfo + * @return {cc.TMXLayer|Null} + */ +cc.TMXLayer.create = function (tilesetInfo, layerInfo, mapInfo) { + return new cc.TMXLayer(tilesetInfo, layerInfo, mapInfo); +}; diff --git a/frameworks/cocos2d-html5/cocos2d/tilemap/CCTMXLayerCanvasRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/tilemap/CCTMXLayerCanvasRenderCmd.js new file mode 100644 index 0000000..f354284 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/tilemap/CCTMXLayerCanvasRenderCmd.js @@ -0,0 +1,259 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + cc.TMXLayer.CanvasRenderCmd = function (renderable) { + this._rootCtor(renderable); + this._needDraw = true; + }; + + var proto = cc.TMXLayer.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + proto.constructor = cc.TMXLayer.CanvasRenderCmd; + + proto.visit = function (parentCmd) { + var node = this._node, renderer = cc.renderer; + + parentCmd = parentCmd || this.getParentRenderCmd(); + if (parentCmd) { + this._curLevel = parentCmd._curLevel + 1; + } + + // quick return if not visible + if (!node._visible) + return; + + if (isNaN(node._customZ)) { + node._vertexZ = renderer.assignedZ; + renderer.assignedZ += renderer.assignedZStep; + } + + this._syncStatus(parentCmd); + + // Visit children + var children = node._children, child, + spTiles = node._spriteTiles, + i, len = children.length; + if (len > 0) { + node.sortAllChildren(); + // draw children zOrder < 0 + for (i = 0; i < len; i++) { + child = children[i]; + if (child._localZOrder < 0) { + child._renderCmd.visit(this); + } + else { + break; + } + } + + renderer.pushRenderCommand(this); + for (; i < len; i++) { + child = children[i]; + if (child._localZOrder === 0 && spTiles[child.tag]) { + if (isNaN(child._customZ)) { + child._vertexZ = renderer.assignedZ; + renderer.assignedZ += renderer.assignedZStep; + } + child._renderCmd.updateStatus(); + continue; + } + child._renderCmd.visit(this); + } + } else { + renderer.pushRenderCommand(this); + } + this._dirtyFlag = 0; + }; + + proto.rendering = function (ctx, scaleX, scaleY) { + var node = this._node, hasRotation = (node._rotationX || node._rotationY), + layerOrientation = node.layerOrientation, + tiles = node.tiles, + alpha = node._opacity / 255; + + if (!tiles || alpha <= 0) { + return; + } + + var maptw = node._mapTileSize.width, + mapth = node._mapTileSize.height, + tilew = node.tileset._tileSize.width / cc.director._contentScaleFactor, + tileh = node.tileset._tileSize.height / cc.director._contentScaleFactor, + extw = tilew - maptw, + exth = tileh - mapth, + winw = cc.winSize.width, + winh = cc.winSize.height, + rows = node._layerSize.height, + cols = node._layerSize.width, + grids = node._texGrids, + spTiles = node._spriteTiles, + wt = this._worldTransform, + ox = -node._contentSize.width * node._anchorPoint.x, + oy = -node._contentSize.height * node._anchorPoint.y, + a = wt.a, b = wt.b, c = wt.c, d = wt.d, + mapx = ox * a + oy * c + wt.tx, + mapy = ox * b + oy * d + wt.ty; + + var wrapper = ctx || cc._renderContext, context = wrapper.getContext(); + + // Culling + var startCol = 0, startRow = 0, + maxCol = cols, maxRow = rows; + if (!hasRotation && layerOrientation === cc.TMX_ORIENTATION_ORTHO) { + startCol = Math.floor(-(mapx - extw * a) / (maptw * a)); + startRow = Math.floor((mapy - exth * d + mapth * rows * d - winh) / (mapth * d)); + maxCol = Math.ceil((winw - mapx + extw * a) / (maptw * a)); + maxRow = rows - Math.floor(-(mapy + exth * d) / (mapth * d)); + // Adjustment + if (startCol < 0) startCol = 0; + if (startRow < 0) startRow = 0; + if (maxCol > cols) maxCol = cols; + if (maxRow > rows) maxRow = rows; + } + + var i, row, col, colOffset = startRow * cols, z, + gid, grid, tex, cmd, + mask = cc.TMX_TILE_FLIPPED_MASK, + top, left, bottom, right, dw = tilew, dh = tileh, + w = tilew * a, h = tileh * d, gt, gl, gb, gr, + flippedX = false, flippedY = false; + + // Draw culled sprite tiles + z = colOffset + startCol; + for (i in spTiles) { + if (i < z && spTiles[i]) { + cmd = spTiles[i]._renderCmd; + if (spTiles[i]._localZOrder === 0 && !!cmd.rendering) { + cmd.rendering(ctx, scaleX, scaleY); + } + } + else if (i >= z) { + break; + } + } + + wrapper.setTransform(wt, scaleX, scaleY); + wrapper.setGlobalAlpha(alpha); + + for (row = startRow; row < maxRow; ++row) { + for (col = startCol; col < maxCol; ++col) { + z = colOffset + col; + // Skip sprite tiles + if (spTiles[z]) { + cmd = spTiles[z]._renderCmd; + if (spTiles[z]._localZOrder === 0 && !!cmd.rendering) { + cmd.rendering(ctx, scaleX, scaleY); + wrapper.setTransform(wt, scaleX, scaleY); + wrapper.setGlobalAlpha(alpha); + } + continue; + } + + gid = node.tiles[z]; + grid = grids[(gid & mask) >>> 0]; + if (!grid) { + continue; + } + tex = node._textures[grid.texId]; + if (!tex || !tex._htmlElementObj) { + continue; + } + + switch (layerOrientation) { + case cc.TMX_ORIENTATION_ORTHO: + left = col * maptw; + bottom = -(rows - row - 1) * mapth; + break; + case cc.TMX_ORIENTATION_ISO: + left = maptw / 2 * ( cols + col - row - 1); + bottom = -mapth / 2 * ( rows * 2 - col - row - 2); + break; + case cc.TMX_ORIENTATION_HEX: + left = col * maptw * 3 / 4; + bottom = -(rows - row - 1) * mapth + ((col % 2 === 1) ? (-mapth / 2) : 0); + break; + } + right = left + tilew; + top = bottom - tileh; + // TMX_ORIENTATION_ISO trim + if (!hasRotation && layerOrientation === cc.TMX_ORIENTATION_ISO) { + gb = -mapy + bottom * d; + if (gb < -winh - h) { + col += Math.floor((-winh - gb) * 2 / h) - 1; + continue; + } + gr = mapx + right * a; + if (gr < -w) { + col += Math.floor((-gr) * 2 / w) - 1; + continue; + } + gl = mapx + left * a; + gt = -mapy + top * d; + if (gl > winw || gt > 0) { + col = maxCol; + continue; + } + } + + // Rotation and Flip + if (gid > cc.TMX_TILE_DIAGONAL_FLAG) { + flippedX = (gid & cc.TMX_TILE_HORIZONTAL_FLAG) >>> 0; + flippedY = (gid & cc.TMX_TILE_VERTICAL_FLAG) >>> 0; + } + + if (flippedX) { + left = -right; + context.scale(-1, 1); + } + if (flippedY) { + top = -bottom; + context.scale(1, -1); + } + + context.drawImage(tex._htmlElementObj, + grid.x, grid.y, grid.width, grid.height, + left, top, dw, dh); + // Revert flip + if (flippedX) { + context.scale(-1, 1); + } + if (flippedY) { + context.scale(1, -1); + } + cc.g_NumberOfDraws++; + } + colOffset += cols; + } + + // Draw culled sprite tiles + for (i in spTiles) { + if (i > z && spTiles[i]) { + cmd = spTiles[i]._renderCmd; + if (spTiles[i]._localZOrder === 0 && !!cmd.rendering) { + cmd.rendering(ctx, scaleX, scaleY); + } + } + } + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/tilemap/CCTMXLayerWebGLRenderCmd.js b/frameworks/cocos2d-html5/cocos2d/tilemap/CCTMXLayerWebGLRenderCmd.js new file mode 100644 index 0000000..d0e90be --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/tilemap/CCTMXLayerWebGLRenderCmd.js @@ -0,0 +1,259 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + cc.TMXLayer.WebGLRenderCmd = function (renderableObject) { + this._rootCtor(renderableObject); + this._needDraw = true; + this._vertices = [ + {x: 0, y: 0}, + {x: 0, y: 0}, + {x: 0, y: 0}, + {x: 0, y: 0} + ]; + this._color = new Uint32Array(1); + this._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_SPRITE_POSITION_TEXTURECOLORALPHATEST); + + var radian = Math.PI * 90 / 180; + this._sin90 = Math.sin(radian); + this._cos90 = Math.cos(radian); + radian = radian * 3; + this._sin270 = Math.sin(radian); + this._cos270 = Math.cos(radian); + }; + + var proto = cc.TMXLayer.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + proto.constructor = cc.TMXLayer.WebGLRenderCmd; + + proto.uploadData = function (f32buffer, ui32buffer, vertexDataOffset) { + var node = this._node, hasRotation = (node._rotationX || node._rotationY), + layerOrientation = node.layerOrientation, + tiles = node.tiles; + + if (!tiles) { + return 0; + } + + var scalex = cc.view._scaleX, + scaley = cc.view._scaleY, + maptw = node._mapTileSize.width, + mapth = node._mapTileSize.height, + tilew = node.tileset._tileSize.width / cc.director._contentScaleFactor, + tileh = node.tileset._tileSize.height / cc.director._contentScaleFactor, + extw = tilew - maptw, + exth = tileh - mapth, + winw = cc.winSize.width, + winh = cc.winSize.height, + rows = node._layerSize.height, + cols = node._layerSize.width, + grids = node._texGrids, + spTiles = node._spriteTiles, + wt = this._worldTransform, + a = wt.a, b = wt.b, c = wt.c, d = wt.d, tx = wt.tx, ty = wt.ty, + ox = -node._contentSize.width * node._anchorPoint.x, + oy = -node._contentSize.height * node._anchorPoint.y, + mapx = ox * a + oy * c + tx, + mapy = ox * b + oy * d + ty; + + var opacity = node._opacity, + cr = this._displayedColor.r, + cg = this._displayedColor.g, + cb = this._displayedColor.b; + if (node._opacityModifyRGB) { + var ca = opacity / 255; + cr *= ca; + cg *= ca; + cb *= ca; + } + this._color[0] = ((opacity << 24) | (cb << 16) | (cg << 8) | cr); + + // Culling + var startCol = 0, startRow = 0, + maxCol = cols, maxRow = rows; + if (!hasRotation && layerOrientation === cc.TMX_ORIENTATION_ORTHO) { + startCol = Math.floor(-(mapx - extw * a) / (maptw * a)); + startRow = Math.floor((mapy - exth * d + mapth * rows * d - winh) / (mapth * d)); + maxCol = Math.ceil((winw - mapx + extw * a) / (maptw * a)); + maxRow = rows - Math.floor(-(mapy + exth * d) / (mapth * d)); + // Adjustment + if (startCol < 0) startCol = 0; + if (startRow < 0) startRow = 0; + if (maxCol > cols) maxCol = cols; + if (maxRow > rows) maxRow = rows; + } + + var row, col, + offset = vertexDataOffset, + colOffset = startRow * cols, z, gid, grid, + mask = cc.TMX_TILE_FLIPPED_MASK, + i, top, left, bottom, right, + w = tilew * a, h = tileh * d, gt, gl, gb, gr, + wa = a, wb = b, wc = c, wd = d, wtx = tx, wty = ty, // world + flagged = false, flippedX = false, flippedY = false, + vertices = this._vertices; + for (row = startRow; row < maxRow; ++row) { + for (col = startCol; col < maxCol; ++col) { + // No more buffer + if (offset + 24 > f32buffer.length) { + cc.renderer._increaseBatchingSize((offset - vertexDataOffset) / 6); + cc.renderer._batchRendering(); + vertexDataOffset = 0; + offset = 0; + } + + z = colOffset + col; + // Skip sprite tiles + if (spTiles[z]) { + continue; + } + + gid = node.tiles[z]; + grid = grids[(gid & mask) >>> 0]; + if (!grid) { + continue; + } + + // Vertices + switch (layerOrientation) { + case cc.TMX_ORIENTATION_ORTHO: + left = col * maptw; + bottom = (rows - row - 1) * mapth; + z = node._vertexZ + cc.renderer.assignedZStep * z / tiles.length; + break; + case cc.TMX_ORIENTATION_ISO: + left = maptw / 2 * ( cols + col - row - 1); + bottom = mapth / 2 * ( rows * 2 - col - row - 2); + z = node._vertexZ + cc.renderer.assignedZStep * (node.height - bottom) / node.height; + break; + case cc.TMX_ORIENTATION_HEX: + left = col * maptw * 3 / 4; + bottom = (rows - row - 1) * mapth + ((col % 2 === 1) ? (-mapth / 2) : 0); + z = node._vertexZ + cc.renderer.assignedZStep * (node.height - bottom) / node.height; + break; + } + right = left + tilew; + top = bottom + tileh; + // TMX_ORIENTATION_ISO trim + if (!hasRotation && layerOrientation === cc.TMX_ORIENTATION_ISO) { + gb = mapy + bottom * d; + if (gb > winh + h) { + col += Math.floor((gb - winh) * 2 / h) - 1; + continue; + } + gr = mapx + right * a; + if (gr < -w) { + col += Math.floor((-gr) * 2 / w) - 1; + continue; + } + gl = mapx + left * a; + gt = mapy + top * d; + if (gl > winw || gt < 0) { + col = maxCol; + continue; + } + } + // Rotation and Flip + if (gid > cc.TMX_TILE_DIAGONAL_FLAG) { + flagged = true; + flippedX = (gid & cc.TMX_TILE_HORIZONTAL_FLAG) >>> 0; + flippedY = (gid & cc.TMX_TILE_VERTICAL_FLAG) >>> 0; + // if ((gid & cc.TMX_TILE_DIAGONAL_FLAG) >>> 0) { + // var flag = (gid & (cc.TMX_TILE_HORIZONTAL_FLAG | cc.TMX_TILE_VERTICAL_FLAG) >>> 0) >>> 0; + // // handle the 4 diagonally flipped states. + // var la, lb, lc, ld; + // if (flag === cc.TMX_TILE_HORIZONTAL_FLAG || flag === (cc.TMX_TILE_VERTICAL_FLAG | cc.TMX_TILE_HORIZONTAL_FLAG) >>> 0) { + // lb = -(lc = this._sin90); + // la = ld = this._cos90; + // } + // else { + // lb = -(lc = this._sin270); + // la = ld = this._cos270; + // } + // left += grid.width * scalex / 2; + // bottom += grid.height * scaley / 2; + // wa = la * a + lb * c; + // wb = la * b + lb * d; + // wc = lc * a + ld * c; + // wd = lc * b + ld * d; + // wtx = a * left + c * bottom + tx; + // wty = d * bottom + ty + b * left; + // right = right - left; + // top = top - bottom; + // left = -right; + // bottom = -top; + // } + } + + vertices[0].x = left * wa + top * wc + wtx; // tl + vertices[0].y = left * wb + top * wd + wty; + vertices[1].x = left * wa + bottom * wc + wtx; // bl + vertices[1].y = left * wb + bottom * wd + wty; + vertices[2].x = right * wa + top * wc + wtx; // tr + vertices[2].y = right * wb + top * wd + wty; + vertices[3].x = right * wa + bottom * wc + wtx; // br + vertices[3].y = right * wb + bottom * wd + wty; + + for (i = 0; i < 4; ++i) { + f32buffer[offset] = vertices[i].x; + f32buffer[offset + 1] = vertices[i].y; + f32buffer[offset + 2] = z; + ui32buffer[offset + 3] = this._color[0]; + switch (i) { + case 0: // tl + f32buffer[offset + 4] = flippedX ? grid.r : grid.l; + f32buffer[offset + 5] = flippedY ? grid.b : grid.t; + break; + case 1: // bl + f32buffer[offset + 4] = flippedX ? grid.r : grid.l; + f32buffer[offset + 5] = flippedY ? grid.t : grid.b; + break; + case 2: // tr + f32buffer[offset + 4] = flippedX ? grid.l : grid.r; + f32buffer[offset + 5] = flippedY ? grid.b : grid.t; + break; + case 3: // br + f32buffer[offset + 4] = flippedX ? grid.l : grid.r; + f32buffer[offset + 5] = flippedY ? grid.t : grid.b; + break; + } + + offset += 6; + } + if (flagged) { + wa = a; + wb = b; + wc = c; + wd = d; + wtx = tx; + wty = ty; + flippedX = false; + flippedY = false; + flagged = false; + } + } + colOffset += cols; + } + return (offset - vertexDataOffset) / 6; + }; +})(); diff --git a/frameworks/cocos2d-html5/cocos2d/tilemap/CCTMXObjectGroup.js b/frameworks/cocos2d-html5/cocos2d/tilemap/CCTMXObjectGroup.js new file mode 100644 index 0000000..b5d41db --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/tilemap/CCTMXObjectGroup.js @@ -0,0 +1,157 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * cc.TMXObjectGroup represents the TMX object group. + * @class + * @extends cc.Class + * + * @property {Array} properties - Properties from the group. They can be added using tilemap editors + * @property {String} groupName - Name of the group + */ +cc.TMXObjectGroup = cc.Class.extend(/** @lends cc.TMXObjectGroup# */{ + properties: null, + groupName: "", + + _positionOffset: null, + _objects: null, + + /** + *

The cc.TMXObjectGroup's constructor.
+ * This function will automatically be invoked when you create a node using new construction: "var node = new cc.TMXObjectGroup()".
+ * Override it to extend its behavior, remember to call "this._super()" in the extended "ctor" function.

+ */ + ctor:function () { + this.groupName = ""; + this._positionOffset = cc.p(0,0); + this.properties = []; + this._objects = []; + }, + + /** + * Offset position of child objects + * @return {cc.Point} + */ + getPositionOffset:function () { + return cc.p(this._positionOffset); + }, + + /** + * Offset position of child objects + * @param {cc.Point} offset + */ + setPositionOffset:function (offset) { + this._positionOffset.x = offset.x; + this._positionOffset.y = offset.y; + }, + + /** + * List of properties stored in a dictionary + * @return {Array} + */ + getProperties:function () { + return this.properties; + }, + + /** + * List of properties stored in a dictionary + * @param {object} Var + */ + setProperties:function (Var) { + this.properties.push(Var); + }, + + /** + * Gets the Group name. + * @return {String} + */ + getGroupName:function () { + return this.groupName.toString(); + }, + + /** + * Set the Group name + * @param {String} groupName + */ + setGroupName:function (groupName) { + this.groupName = groupName; + }, + + /** + * Return the value for the specific property name + * @param {String} propertyName + * @return {object} + */ + propertyNamed:function (propertyName) { + return this.properties[propertyName]; + }, + + /** + *

Return the dictionary for the specific object name.
+ * It will return the 1st object found on the array for the given name.

+ * @deprecated since v3.4 please use .getObject + * @param {String} objectName + * @return {object|Null} + */ + objectNamed:function (objectName) { + return this.getObject(objectName); + }, + + /** + *

Return the dictionary for the specific object name.
+ * It will return the 1st object found on the array for the given name.

+ * @param {String} objectName + * @return {object|Null} + */ + getObject: function(objectName){ + if (this._objects && this._objects.length > 0) { + var locObjects = this._objects; + for (var i = 0, len = locObjects.length; i < len; i++) { + var name = locObjects[i]["name"]; + if (name && name === objectName) + return locObjects[i]; + } + } + // object not found + return null; + }, + + /** + * Gets the objects. + * @return {Array} + */ + getObjects:function () { + return this._objects; + }, + + /** + * Set the objects. + * @param {object} objects + */ + setObjects:function (objects) { + this._objects.push(objects); + } +}); diff --git a/frameworks/cocos2d-html5/cocos2d/tilemap/CCTMXTiledMap.js b/frameworks/cocos2d-html5/cocos2d/tilemap/CCTMXTiledMap.js new file mode 100644 index 0000000..5ca4107 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/tilemap/CCTMXTiledMap.js @@ -0,0 +1,478 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + Orthogonal orientation + * @constant + * @type Number + */ +cc.TMX_ORIENTATION_ORTHO = 0; + +/** + * Hexagonal orientation + * @constant + * @type Number + */ + +cc.TMX_ORIENTATION_HEX = 1; + +/** + * Isometric orientation + * @constant + * @type Number + */ +cc.TMX_ORIENTATION_ISO = 2; + +/** + *

cc.TMXTiledMap knows how to parse and render a TMX map.

+ * + *

It adds support for the TMX tiled map format used by http://www.mapeditor.org
+ * It supports isometric, hexagonal and orthogonal tiles.
+ * It also supports object groups, objects, and properties.

+ * + *

Features:
+ * - Each tile will be treated as an cc.Sprite
+ * - The sprites are created on demand. They will be created only when you call "layer.getTileAt(position)"
+ * - Each tile can be rotated / moved / scaled / tinted / "opacitied", since each tile is a cc.Sprite
+ * - Tiles can be added/removed in runtime
+ * - The z-order of the tiles can be modified in runtime
+ * - Each tile has an anchorPoint of (0,0)
+ * - The anchorPoint of the TMXTileMap is (0,0)
+ * - The TMX layers will be added as a child
+ * - The TMX layers will be aliased by default
+ * - The tileset image will be loaded using the cc.TextureCache
+ * - Each tile will have a unique tag
+ * - Each tile will have a unique z value. top-left: z=1, bottom-right: z=max z
+ * - Each object group will be treated as an cc.MutableArray
+ * - Object class which will contain all the properties in a dictionary
+ * - Properties can be assigned to the Map, Layer, Object Group, and Object

+ * + *

Limitations:
+ * - It only supports one tileset per layer.
+ * - Embedded images are not supported
+ * - It only supports the XML format (the JSON format is not supported)

+ * + *

Technical description:
+ * Each layer is created using an cc.TMXLayer (subclass of cc.SpriteBatchNode). If you have 5 layers, then 5 cc.TMXLayer will be created,
+ * unless the layer visibility is off. In that case, the layer won't be created at all.
+ * You can obtain the layers (cc.TMXLayer objects) at runtime by:
+ * - map.getChildByTag(tag_number); // 0=1st layer, 1=2nd layer, 2=3rd layer, etc...
+ * - map.getLayer(name_of_the_layer);

+ * + *

Each object group is created using a cc.TMXObjectGroup which is a subclass of cc.MutableArray.
+ * You can obtain the object groups at runtime by:
+ * - map.getObjectGroup(name_of_the_object_group);

+ * + *

Each object is a cc.TMXObject.

+ * + *

Each property is stored as a key-value pair in an cc.MutableDictionary.
+ * You can obtain the properties at runtime by:

+ * + *

map.getProperty(name_of_the_property);
+ * layer.getProperty(name_of_the_property);
+ * objectGroup.getProperty(name_of_the_property);
+ * object.getProperty(name_of_the_property);

+ * @class + * @extends cc.Node + * @param {String} tmxFile tmxFile fileName or content string + * @param {String} resourcePath If tmxFile is a file name ,it is not required.If tmxFile is content string ,it is must required. + + * + * @property {Array} properties - Properties from the map. They can be added using tilemap editors + * @property {Number} mapOrientation - Map orientation + * @property {Array} objectGroups - Object groups of the map + * @property {Number} mapWidth - Width of the map + * @property {Number} mapHeight - Height of the map + * @property {Number} tileWidth - Width of a tile + * @property {Number} tileHeight - Height of a tile + * + * @example + * //example + * 1. + * //create a TMXTiledMap with file name + * var tmxTiledMap = new cc.TMXTiledMap("res/orthogonal-test1.tmx"); + * 2. + * //create a TMXTiledMap with content string and resource path + * var resources = "res/TileMaps"; + * var filePath = "res/TileMaps/orthogonal-test1.tmx"; + * var xmlStr = cc.loader.getRes(filePath); + * var tmxTiledMap = new cc.TMXTiledMap(xmlStr, resources); + */ +cc.TMXTiledMap = cc.Node.extend(/** @lends cc.TMXTiledMap# */{ + properties: null, + mapOrientation: null, + objectGroups: null, + + //the map's size property measured in tiles + _mapSize: null, + _tileSize: null, + //tile properties + _tileProperties: null, + _className: "TMXTiledMap", + + /** + * Creates a TMX Tiled Map with a TMX file or content string.
+ * Constructor of cc.TMXTiledMap + * @param {String} tmxFile tmxFile fileName or content string + * @param {String} resourcePath If tmxFile is a file name ,it is not required.If tmxFile is content string ,it is must required. + */ + ctor:function(tmxFile,resourcePath){ + cc.Node.prototype.ctor.call(this); + this._mapSize = cc.size(0, 0); + this._tileSize = cc.size(0, 0); + + if(resourcePath !== undefined){ + this.initWithXML(tmxFile,resourcePath); + }else if(tmxFile !== undefined){ + this.initWithTMXFile(tmxFile); + } + }, + + /** + * Gets the map size. + * @return {cc.Size} + */ + getMapSize:function () { + return cc.size(this._mapSize.width, this._mapSize.height); + }, + + /** + * Set the map size. + * @param {cc.Size} Var + */ + setMapSize:function (Var) { + this._mapSize.width = Var.width; + this._mapSize.height = Var.height; + }, + + _getMapWidth: function () { + return this._mapSize.width; + }, + _setMapWidth: function (width) { + this._mapSize.width = width; + }, + _getMapHeight: function () { + return this._mapSize.height; + }, + _setMapHeight: function (height) { + this._mapSize.height = height; + }, + + /** + * Gets the tile size. + * @return {cc.Size} + */ + getTileSize:function () { + return cc.size(this._tileSize.width, this._tileSize.height); + }, + + /** + * Set the tile size + * @param {cc.Size} Var + */ + setTileSize:function (Var) { + this._tileSize.width = Var.width; + this._tileSize.height = Var.height; + }, + + _getTileWidth: function () { + return this._tileSize.width; + }, + _setTileWidth: function (width) { + this._tileSize.width = width; + }, + _getTileHeight: function () { + return this._tileSize.height; + }, + _setTileHeight: function (height) { + this._tileSize.height = height; + }, + + /** + * map orientation + * @return {Number} + */ + getMapOrientation:function () { + return this.mapOrientation; + }, + + /** + * map orientation + * @param {Number} Var + */ + setMapOrientation:function (Var) { + this.mapOrientation = Var; + }, + + /** + * object groups + * @return {Array} + */ + getObjectGroups:function () { + return this.objectGroups; + }, + + /** + * object groups + * @param {Array} Var + */ + setObjectGroups:function (Var) { + this.objectGroups = Var; + }, + + /** + * Gets the properties + * @return {object} + */ + getProperties:function () { + return this.properties; + }, + + /** + * Set the properties + * @param {object} Var + */ + setProperties:function (Var) { + this.properties = Var; + }, + + /** + * Initializes the instance of cc.TMXTiledMap with tmxFile + * @param {String} tmxFile + * @return {Boolean} Whether the initialization was successful. + * @example + * //example + * var map = new cc.TMXTiledMap() + * map.initWithTMXFile("hello.tmx"); + */ + initWithTMXFile:function (tmxFile) { + if(!tmxFile || tmxFile.length === 0) + throw new Error("cc.TMXTiledMap.initWithTMXFile(): tmxFile should be non-null or non-empty string."); + this.width = 0; + this.height = 0; + var mapInfo = new cc.TMXMapInfo(tmxFile); + if (!mapInfo) + return false; + + var locTilesets = mapInfo.getTilesets(); + if(!locTilesets || locTilesets.length === 0) + cc.log("cc.TMXTiledMap.initWithTMXFile(): Map not found. Please check the filename."); + this._buildWithMapInfo(mapInfo); + return true; + }, + + /** + * Initializes the instance of cc.TMXTiledMap with tmxString + * @param {String} tmxString + * @param {String} resourcePath + * @return {Boolean} Whether the initialization was successful. + */ + initWithXML:function(tmxString, resourcePath){ + this.width = 0; + this.height = 0; + + var mapInfo = new cc.TMXMapInfo(tmxString, resourcePath); + var locTilesets = mapInfo.getTilesets(); + if(!locTilesets || locTilesets.length === 0) + cc.log("cc.TMXTiledMap.initWithXML(): Map not found. Please check the filename."); + this._buildWithMapInfo(mapInfo); + return true; + }, + + _buildWithMapInfo:function (mapInfo) { + this._mapSize = mapInfo.getMapSize(); + this._tileSize = mapInfo.getTileSize(); + this.mapOrientation = mapInfo.orientation; + this.objectGroups = mapInfo.getObjectGroups(); + this.properties = mapInfo.properties; + this._tileProperties = mapInfo.getTileProperties(); + + var idx = 0; + var layers = mapInfo.getLayers(); + if (layers) { + var layerInfo = null; + for (var i = 0, len = layers.length; i < len; i++) { + layerInfo = layers[i]; + if (layerInfo && layerInfo.visible) { + var child = this._parseLayer(layerInfo, mapInfo); + this.addChild(child, idx, idx); + // update content size with the max size + this.width = Math.max(this.width, child.width); + this.height = Math.max(this.height, child.height); + idx++; + } + } + } + }, + + /** + * Return All layers array. + * @returns {Array} + */ + allLayers: function () { + var retArr = [], locChildren = this._children; + for(var i = 0, len = locChildren.length;i< len;i++){ + var layer = locChildren[i]; + if(layer && layer instanceof cc.TMXLayer) + retArr.push(layer); + } + return retArr; + }, + + /** + * return the TMXLayer for the specific layer + * @param {String} layerName + * @return {cc.TMXLayer} + */ + getLayer:function (layerName) { + if(!layerName || layerName.length === 0) + throw new Error("cc.TMXTiledMap.getLayer(): layerName should be non-null or non-empty string."); + var locChildren = this._children; + for (var i = 0; i < locChildren.length; i++) { + var layer = locChildren[i]; + if (layer && layer.layerName === layerName) + return layer; + } + // layer not found + return null; + }, + + /** + * Return the TMXObjectGroup for the specific group + * @param {String} groupName + * @return {cc.TMXObjectGroup} + */ + getObjectGroup:function (groupName) { + if(!groupName || groupName.length === 0) + throw new Error("cc.TMXTiledMap.getObjectGroup(): groupName should be non-null or non-empty string."); + if (this.objectGroups) { + for (var i = 0; i < this.objectGroups.length; i++) { + var objectGroup = this.objectGroups[i]; + if (objectGroup && objectGroup.groupName === groupName) { + return objectGroup; + } + } + } + // objectGroup not found + return null; + }, + + /** + * Return the value for the specific property name + * @param {String} propertyName + * @return {String} + */ + getProperty:function (propertyName) { + return this.properties[propertyName.toString()]; + }, + + /** + * Return properties dictionary for tile GID + * @param {Number} GID + * @return {object} + * @deprecated + */ + propertiesForGID:function (GID) { + cc.log("propertiesForGID is deprecated. Please use getPropertiesForGID instead."); + return this.getPropertiesForGID[GID]; + }, + + /** + * Return properties dictionary for tile GID + * @param {Number} GID + * @return {object} + */ + getPropertiesForGID: function(GID) { + return this._tileProperties[GID]; + }, + + _parseLayer:function (layerInfo, mapInfo) { + var tileset = this._tilesetForLayer(layerInfo, mapInfo); + var layer = new cc.TMXLayer(tileset, layerInfo, mapInfo); + // tell the layerinfo to release the ownership of the tiles map. + layerInfo.ownTiles = false; + return layer; + }, + + _tilesetForLayer:function (layerInfo, mapInfo) { + var size = layerInfo._layerSize; + var tilesets = mapInfo.getTilesets(); + if (tilesets) { + for (var i = tilesets.length - 1; i >= 0; i--) { + var tileset = tilesets[i]; + if (tileset) { + for (var y = 0; y < size.height; y++) { + for (var x = 0; x < size.width; x++) { + var pos = x + size.width * y; + var gid = layerInfo._tiles[pos]; + if (gid !== 0) { + // Optimization: quick return + // if the layer is invalid (more than 1 tileset per layer) an cc.assert will be thrown later + if (((gid & cc.TMX_TILE_FLIPPED_MASK)>>>0) >= tileset.firstGid) { + return tileset; + } + } + + } + } + } + } + } + + // If all the tiles are 0, return empty tileset + cc.log("cocos2d: Warning: TMX Layer " + layerInfo.name + " has no tiles"); + return null; + } +}); + +var _p = cc.TMXTiledMap.prototype; + +// Extended properties +/** @expose */ +_p.mapWidth; +cc.defineGetterSetter(_p, "mapWidth", _p._getMapWidth, _p._setMapWidth); +/** @expose */ +_p.mapHeight; +cc.defineGetterSetter(_p, "mapHeight", _p._getMapHeight, _p._setMapHeight); +/** @expose */ +_p.tileWidth; +cc.defineGetterSetter(_p, "tileWidth", _p._getTileWidth, _p._setTileWidth); +/** @expose */ +_p.tileHeight; +cc.defineGetterSetter(_p, "tileHeight", _p._getTileHeight, _p._setTileHeight); + + +/** + * Creates a TMX Tiled Map with a TMX file or content string. + * Implementation cc.TMXTiledMap + * @deprecated since v3.0 please use new cc.TMXTiledMap(tmxFile,resourcePath) instead. + * @param {String} tmxFile tmxFile fileName or content string + * @param {String} resourcePath If tmxFile is a file name ,it is not required.If tmxFile is content string ,it is must required. + * @return {cc.TMXTiledMap|undefined} + */ +cc.TMXTiledMap.create = function (tmxFile,resourcePath) { + return new cc.TMXTiledMap(tmxFile,resourcePath); +}; diff --git a/frameworks/cocos2d-html5/cocos2d/tilemap/CCTMXXMLParser.js b/frameworks/cocos2d-html5/cocos2d/tilemap/CCTMXXMLParser.js new file mode 100644 index 0000000..70509e6 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/tilemap/CCTMXXMLParser.js @@ -0,0 +1,954 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * @constant + * @type Number + */ +cc.TMX_PROPERTY_NONE = 0; + +/** + * @constant + * @type Number + */ +cc.TMX_PROPERTY_MAP = 1; + +/** + * @constant + * @type Number + */ +cc.TMX_PROPERTY_LAYER = 2; + +/** + * @constant + * @type Number + */ +cc.TMX_PROPERTY_OBJECTGROUP = 3; + +/** + * @constant + * @type Number + */ +cc.TMX_PROPERTY_OBJECT = 4; + +/** + * @constant + * @type Number + */ +cc.TMX_PROPERTY_TILE = 5; + +/** + * @constant + * @type Number + */ +cc.TMX_TILE_HORIZONTAL_FLAG = 0x80000000; + + +/** + * @constant + * @type Number + */ +cc.TMX_TILE_VERTICAL_FLAG = 0x40000000; + +/** + * @constant + * @type Number + */ +cc.TMX_TILE_DIAGONAL_FLAG = 0x20000000; + +/** + * @constant + * @type Number + */ +cc.TMX_TILE_FLIPPED_ALL = (cc.TMX_TILE_HORIZONTAL_FLAG | cc.TMX_TILE_VERTICAL_FLAG | cc.TMX_TILE_DIAGONAL_FLAG) >>> 0; + +/** + * @constant + * @type Number + */ +cc.TMX_TILE_FLIPPED_MASK = (~(cc.TMX_TILE_FLIPPED_ALL)) >>> 0; + +// Bits on the far end of the 32-bit global tile ID (GID's) are used for tile flags + +/** + *

cc.TMXLayerInfo contains the information about the layers like:
+ * - Layer name
+ * - Layer size
+ * - Layer opacity at creation time (it can be modified at runtime)
+ * - Whether the layer is visible (if it's not visible, then the CocosNode won't be created)
+ *
+ * This information is obtained from the TMX file.

+ * @class + * @extends cc.Class + * + * @property {Array} properties - Properties of the layer info. + */ +cc.TMXLayerInfo = cc.Class.extend(/** @lends cc.TMXLayerInfo# */{ + properties:null, + + name:"", + _layerSize:null, + _tiles:null, + visible:null, + _opacity:null, + ownTiles:true, + _minGID:100000, + _maxGID:0, + offset:null, + + ctor:function () { + this.properties = []; + this.name = ""; + this._layerSize = null; + this._tiles = null; + this.visible = true; + this._opacity = 0; + this.ownTiles = true; + this._minGID = 100000; + this._maxGID = 0; + this.offset = cc.p(0,0); + }, + + /** + * Gets the Properties. + * @return {Array} + */ + getProperties:function () { + return this.properties; + }, + + /** + * Set the Properties. + * @param {object} value + */ + setProperties:function (value) { + this.properties = value; + } +}); + +/** + *

cc.TMXTilesetInfo contains the information about the tilesets like:
+ * - Tileset name
+ * - Tileset spacing
+ * - Tileset margin
+ * - size of the tiles
+ * - Image used for the tiles
+ * - Image size
+ * + * This information is obtained from the TMX file.

+ * @class + * @extends cc.Class + * + * @property {string} name - Tileset name + * @property {number} firstGid - First grid + * @property {number} spacing - Spacing + * @property {number} margin - Margin + * @property {string} sourceImage - Filename containing the tiles (should be sprite sheet / texture atlas) + * @property {cc.Size|null} imageSize - Size in pixels of the image + */ +cc.TMXTilesetInfo = cc.Class.extend(/** @lends cc.TMXTilesetInfo# */{ + + //Tileset name + name:"", + + //First grid + firstGid:0, + _tileSize:null, + + //Spacing + spacing:0, + + //Margin + margin:0, + + //Filename containing the tiles (should be sprite sheet / texture atlas) + sourceImage:"", + + //Size in pixels of the image + imageSize:null, + + ctor:function () { + this._tileSize = cc.size(0, 0); + this.imageSize = cc.size(0, 0); + }, + + /** + * Return rect + * @param {Number} gid + * @return {cc.Rect} + */ + rectForGID:function (gid, result) { + var rect = result || cc.rect(0, 0, 0, 0); + rect.width = this._tileSize.width; + rect.height = this._tileSize.height; + gid &= cc.TMX_TILE_FLIPPED_MASK; + gid = gid - parseInt(this.firstGid, 10); + var max_x = parseInt((this.imageSize.width - this.margin * 2 + this.spacing) / (this._tileSize.width + this.spacing), 10); + rect.x = parseInt((gid % max_x) * (this._tileSize.width + this.spacing) + this.margin, 10); + rect.y = parseInt(parseInt(gid / max_x, 10) * (this._tileSize.height + this.spacing) + this.margin, 10); + return rect; + } +}); + +/** + *

cc.TMXMapInfo contains the information about the map like:
+ *- Map orientation (hexagonal, isometric or orthogonal)
+ *- Tile size
+ *- Map size

+ * + *

And it also contains:
+ * - Layers (an array of TMXLayerInfo objects)
+ * - Tilesets (an array of TMXTilesetInfo objects)
+ * - ObjectGroups (an array of TMXObjectGroupInfo objects)

+ * + *

This information is obtained from the TMX file.

+ * @class + * @extends cc.saxParser + * + * @property {Array} properties - Properties of the map info. + * @property {Number} orientation - Map orientation. + * @property {Object} parentElement - Parent element. + * @property {Number} parentGID - Parent GID. + * @property {Object} layerAttrs - Layer attributes. + * @property {Boolean} storingCharacters - Is reading storing characters stream. + * @property {String} tmxFileName - TMX file name. + * @property {String} currentString - Current string stored from characters stream. + * @property {Number} mapWidth - Width of the map + * @property {Number} mapHeight - Height of the map + * @property {Number} tileWidth - Width of a tile + * @property {Number} tileHeight - Height of a tile + * + * @param {String} tmxFile fileName or content string + * @param {String} resourcePath If tmxFile is a file name ,it is not required.If tmxFile is content string ,it is must required. + * @example + * 1. + * //create a TMXMapInfo with file name + * var tmxMapInfo = new cc.TMXMapInfo("res/orthogonal-test1.tmx"); + * 2. + * //create a TMXMapInfo with content string and resource path + * var resources = "res/TileMaps"; + * var filePath = "res/TileMaps/orthogonal-test1.tmx"; + * var xmlStr = cc.loader.getRes(filePath); + * var tmxMapInfo = new cc.TMXMapInfo(xmlStr, resources); + */ +cc.TMXMapInfo = cc.SAXParser.extend(/** @lends cc.TMXMapInfo# */{ + properties:null, + orientation:null, + parentElement:null, + parentGID:null, + layerAttrs:0, + storingCharacters:false, + tmxFileName:null, + currentString:null, + + _objectGroups:null, + _mapSize:null, + _tileSize:null, + _layers:null, + _tilesets:null, + // tile properties + _tileProperties:null, + _resources:"", + _currentFirstGID:0, + + /** + * Creates a TMX Format with a tmx file or content string
+ * Constructor of cc.TMXMapInfo + * @param {String} tmxFile fileName or content string + * @param {String} resourcePath If tmxFile is a file name ,it is not required.If tmxFile is content string ,it is must required. + */ + ctor:function (tmxFile, resourcePath) { + cc.SAXParser.prototype.ctor.apply(this); + this._mapSize = cc.size(0, 0); + this._tileSize = cc.size(0, 0); + this._layers = []; + this._tilesets = []; + this._objectGroups = []; + this.properties = []; + this._tileProperties = {}; + + this._currentFirstGID = 0; + + if (resourcePath !== undefined) { + this.initWithXML(tmxFile,resourcePath); + } else if(tmxFile !== undefined){ + this.initWithTMXFile(tmxFile); + } + }, + /** + * Gets Map orientation. + * @return {Number} + */ + getOrientation:function () { + return this.orientation; + }, + + /** + * Set the Map orientation. + * @param {Number} value + */ + setOrientation:function (value) { + this.orientation = value; + }, + + /** + * Map width & height + * @return {cc.Size} + */ + getMapSize:function () { + return cc.size(this._mapSize.width,this._mapSize.height); + }, + + /** + * Map width & height + * @param {cc.Size} value + */ + setMapSize:function (value) { + this._mapSize.width = value.width; + this._mapSize.height = value.height; + }, + + _getMapWidth: function () { + return this._mapSize.width; + }, + _setMapWidth: function (width) { + this._mapSize.width = width; + }, + _getMapHeight: function () { + return this._mapSize.height; + }, + _setMapHeight: function (height) { + this._mapSize.height = height; + }, + + /** + * Tiles width & height + * @return {cc.Size} + */ + getTileSize:function () { + return cc.size(this._tileSize.width, this._tileSize.height); + }, + + /** + * Tiles width & height + * @param {cc.Size} value + */ + setTileSize:function (value) { + this._tileSize.width = value.width; + this._tileSize.height = value.height; + }, + + _getTileWidth: function () { + return this._tileSize.width; + }, + _setTileWidth: function (width) { + this._tileSize.width = width; + }, + _getTileHeight: function () { + return this._tileSize.height; + }, + _setTileHeight: function (height) { + this._tileSize.height = height; + }, + + /** + * Layers + * @return {Array} + */ + getLayers:function () { + return this._layers; + }, + + /** + * Layers + * @param {cc.TMXLayerInfo} value + */ + setLayers:function (value) { + this._layers.push(value); + }, + + /** + * tilesets + * @return {Array} + */ + getTilesets:function () { + return this._tilesets; + }, + + /** + * tilesets + * @param {cc.TMXTilesetInfo} value + */ + setTilesets:function (value) { + this._tilesets.push(value); + }, + + /** + * ObjectGroups + * @return {Array} + */ + getObjectGroups:function () { + return this._objectGroups; + }, + + /** + * ObjectGroups + * @param {cc.TMXObjectGroup} value + */ + setObjectGroups:function (value) { + this._objectGroups.push(value); + }, + + /** + * parent element + * @return {Object} + */ + getParentElement:function () { + return this.parentElement; + }, + + /** + * parent element + * @param {Object} value + */ + setParentElement:function (value) { + this.parentElement = value; + }, + + /** + * parent GID + * @return {Number} + */ + getParentGID:function () { + return this.parentGID; + }, + + /** + * parent GID + * @param {Number} value + */ + setParentGID:function (value) { + this.parentGID = value; + }, + + /** + * Layer attribute + * @return {Object} + */ + getLayerAttribs:function () { + return this.layerAttrs; + }, + + /** + * Layer attribute + * @param {Object} value + */ + setLayerAttribs:function (value) { + this.layerAttrs = value; + }, + + /** + * Is reading storing characters stream + * @return {Boolean} + */ + getStoringCharacters:function () { + return this.storingCharacters; + }, + + /** + * Is reading storing characters stream + * @param {Boolean} value + */ + setStoringCharacters:function (value) { + this.storingCharacters = value; + }, + + /** + * Properties + * @return {Array} + */ + getProperties:function () { + return this.properties; + }, + + /** + * Properties + * @param {object} value + */ + setProperties:function (value) { + this.properties = value; + }, + + /** + * Initializes a TMX format with a tmx file + * @param {String} tmxFile + * @return {Element} + */ + initWithTMXFile:function (tmxFile) { + this._internalInit(tmxFile, null); + return this.parseXMLFile(tmxFile); + }, + + /** + * initializes a TMX format with an XML string and a TMX resource path + * @param {String} tmxString + * @param {String} resourcePath + * @return {Boolean} + */ + initWithXML:function (tmxString, resourcePath) { + this._internalInit(null, resourcePath); + return this.parseXMLString(tmxString); + }, + + /** Initalises parsing of an XML file, either a tmx (Map) file or tsx (Tileset) file + * @param {String} tmxFile + * @param {boolean} [isXmlString=false] + * @return {Element} + */ + parseXMLFile:function (tmxFile, isXmlString) { + isXmlString = isXmlString || false; + var xmlStr = isXmlString ? tmxFile : cc.loader.getRes(tmxFile); + if(!xmlStr) throw new Error("Please load the resource first : " + tmxFile); + + var mapXML = this._parseXML(xmlStr); + var i, j; + + // PARSE + var map = mapXML.documentElement; + + var version = map.getAttribute('version'); + var orientationStr = map.getAttribute('orientation'); + + if (map.nodeName === "map") { + if (version !== "1.0" && version !== null) + cc.log("cocos2d: TMXFormat: Unsupported TMX version:" + version); + + if (orientationStr === "orthogonal") + this.orientation = cc.TMX_ORIENTATION_ORTHO; + else if (orientationStr === "isometric") + this.orientation = cc.TMX_ORIENTATION_ISO; + else if (orientationStr === "hexagonal") + this.orientation = cc.TMX_ORIENTATION_HEX; + else if (orientationStr !== null) + cc.log("cocos2d: TMXFomat: Unsupported orientation:" + orientationStr); + + var mapSize = cc.size(0, 0); + mapSize.width = parseFloat(map.getAttribute('width')); + mapSize.height = parseFloat(map.getAttribute('height')); + this.setMapSize(mapSize); + + mapSize = cc.size(0, 0); + mapSize.width = parseFloat(map.getAttribute('tilewidth')); + mapSize.height = parseFloat(map.getAttribute('tileheight')); + this.setTileSize(mapSize); + + // The parent element is the map + var propertyArr = map.querySelectorAll("map > properties > property"); + if (propertyArr) { + var aPropertyDict = {}; + for (i = 0; i < propertyArr.length; i++) { + aPropertyDict[propertyArr[i].getAttribute('name')] = propertyArr[i].getAttribute('value'); + } + this.properties = aPropertyDict; + } + } + + // PARSE + var tilesets = map.getElementsByTagName('tileset'); + if (map.nodeName !== "map") { + tilesets = []; + tilesets.push(map); + } + + for (i = 0; i < tilesets.length; i++) { + var selTileset = tilesets[i]; + // If this is an external tileset then start parsing that + var tsxName = selTileset.getAttribute('source'); + if (tsxName) { + //this._currentFirstGID = parseInt(selTileset.getAttribute('firstgid')); + var tsxPath = isXmlString ? cc.path.join(this._resources, tsxName) : cc.path.changeBasename(tmxFile, tsxName); + this.parseXMLFile(tsxPath); + } else { + var tileset = new cc.TMXTilesetInfo(); + tileset.name = selTileset.getAttribute('name') || ""; + //TODO need fix + //if(this._currentFirstGID === 0){ + tileset.firstGid = parseInt(selTileset.getAttribute('firstgid')) || 0; + //}else{ + // tileset.firstGid = this._currentFirstGID; + // this._currentFirstGID = 0; + //} + + tileset.spacing = parseInt(selTileset.getAttribute('spacing')) || 0; + tileset.margin = parseInt(selTileset.getAttribute('margin')) || 0; + + var tilesetSize = cc.size(0, 0); + tilesetSize.width = parseFloat(selTileset.getAttribute('tilewidth')); + tilesetSize.height = parseFloat(selTileset.getAttribute('tileheight')); + tileset._tileSize = tilesetSize; + + var image = selTileset.getElementsByTagName('image')[0]; + var imagename = image.getAttribute('source'); + var num = -1; + if(this.tmxFileName) + num = this.tmxFileName.lastIndexOf("/"); + if (num !== -1) { + var dir = this.tmxFileName.substr(0, num + 1); + tileset.sourceImage = dir + imagename; + } else { + tileset.sourceImage = this._resources + (this._resources ? "/" : "") + imagename; + } + this.setTilesets(tileset); + + // PARSE + var tiles = selTileset.getElementsByTagName('tile'); + if (tiles) { + for (var tIdx = 0; tIdx < tiles.length; tIdx++) { + var t = tiles[tIdx]; + this.parentGID = parseInt(tileset.firstGid) + parseInt(t.getAttribute('id') || 0); + var tp = t.querySelectorAll("properties > property"); + if (tp) { + var dict = {}; + for (j = 0; j < tp.length; j++) { + var name = tp[j].getAttribute('name'); + dict[name] = tp[j].getAttribute('value'); + } + this._tileProperties[this.parentGID] = dict; + } + } + } + } + } + + // PARSE + var layers = map.getElementsByTagName('layer'); + if (layers) { + for (i = 0; i < layers.length; i++) { + var selLayer = layers[i]; + var data = selLayer.getElementsByTagName('data')[0]; + + var layer = new cc.TMXLayerInfo(); + layer.name = selLayer.getAttribute('name'); + + var layerSize = cc.size(0, 0); + layerSize.width = parseFloat(selLayer.getAttribute('width')); + layerSize.height = parseFloat(selLayer.getAttribute('height')); + layer._layerSize = layerSize; + + var visible = selLayer.getAttribute('visible'); + layer.visible = !(visible == "0"); + + var opacity = selLayer.getAttribute('opacity') || 1; + + if (opacity) + layer._opacity = parseInt(255 * parseFloat(opacity)); + else + layer._opacity = 255; + layer.offset = cc.p(parseFloat(selLayer.getAttribute('x')) || 0, parseFloat(selLayer.getAttribute('y')) || 0); + + var nodeValue = ''; + for (j = 0; j < data.childNodes.length; j++) { + nodeValue += data.childNodes[j].nodeValue + } + nodeValue = nodeValue.trim(); + + // Unpack the tilemap data + var compression = data.getAttribute('compression'); + var encoding = data.getAttribute('encoding'); + if(compression && compression !== "gzip" && compression !== "zlib"){ + cc.log("cc.TMXMapInfo.parseXMLFile(): unsupported compression method"); + return null; + } + var tiles; + switch (compression) { + case 'gzip': + tiles = cc.unzipBase64AsArray(nodeValue, 4); + break; + case 'zlib': + var inflator = new Zlib.Inflate(cc.Codec.Base64.decodeAsArray(nodeValue, 1)); + tiles = cc.uint8ArrayToUint32Array(inflator.decompress()); + break; + case null: + case '': + // Uncompressed + if (encoding === "base64") + tiles = cc.Codec.Base64.decodeAsArray(nodeValue, 4); + else if (encoding === "csv") { + tiles = []; + var csvTiles = nodeValue.split(','); + for (var csvIdx = 0; csvIdx < csvTiles.length; csvIdx++) + tiles.push(parseInt(csvTiles[csvIdx])); + } else { + //XML format + var selDataTiles = data.getElementsByTagName("tile"); + tiles = []; + for (var xmlIdx = 0; xmlIdx < selDataTiles.length; xmlIdx++) + tiles.push(parseInt(selDataTiles[xmlIdx].getAttribute("gid"))); + } + break; + default: + if(this.layerAttrs === cc.TMXLayerInfo.ATTRIB_NONE) + cc.log("cc.TMXMapInfo.parseXMLFile(): Only base64 and/or gzip/zlib maps are supported"); + break; + } + if (tiles) { + layer._tiles = new Uint32Array(tiles); + } + + // The parent element is the last layer + var layerProps = selLayer.querySelectorAll("properties > property"); + if (layerProps) { + var layerProp = {}; + for (j = 0; j < layerProps.length; j++) { + layerProp[layerProps[j].getAttribute('name')] = layerProps[j].getAttribute('value'); + } + layer.properties = layerProp; + } + this.setLayers(layer); + } + } + + // PARSE + var objectGroups = map.getElementsByTagName('objectgroup'); + if (objectGroups) { + for (i = 0; i < objectGroups.length; i++) { + var selGroup = objectGroups[i]; + var objectGroup = new cc.TMXObjectGroup(); + objectGroup.groupName = selGroup.getAttribute('name'); + objectGroup.setPositionOffset(cc.p(parseFloat(selGroup.getAttribute('x')) * this.getTileSize().width || 0, + parseFloat(selGroup.getAttribute('y')) * this.getTileSize().height || 0)); + + var groupProps = selGroup.querySelectorAll("objectgroup > properties > property"); + if (groupProps) { + for (j = 0; j < groupProps.length; j++) { + var groupProp = {}; + groupProp[groupProps[j].getAttribute('name')] = groupProps[j].getAttribute('value'); + // Add the property to the layer + objectGroup.properties = groupProp; + } + } + + var objects = selGroup.querySelectorAll('object'); + var getContentScaleFactor = cc.director.getContentScaleFactor(); + if (objects) { + for (j = 0; j < objects.length; j++) { + var selObj = objects[j]; + // The value for "type" was blank or not a valid class name + // Create an instance of TMXObjectInfo to store the object and its properties + var objectProp = {}; + + // Set the name of the object to the value for "name" + objectProp["name"] = selObj.getAttribute('name') || ""; + + // Assign all the attributes as key/name pairs in the properties dictionary + objectProp["type"] = selObj.getAttribute('type') || ""; + + objectProp["width"] = parseInt(selObj.getAttribute('width')) || 0; + objectProp["height"] = parseInt(selObj.getAttribute('height')) || 0; + + objectProp["x"] = (((selObj.getAttribute('x') || 0) | 0) + objectGroup.getPositionOffset().x) / getContentScaleFactor; + var y = ((selObj.getAttribute('y') || 0) | 0) + objectGroup.getPositionOffset().y / getContentScaleFactor; + // Correct y position. (Tiled uses Flipped, cocos2d uses Standard) + objectProp["y"] = (parseInt(this.getMapSize().height * this.getTileSize().height) - y - objectProp["height"]) / cc.director.getContentScaleFactor(); + + objectProp["rotation"] = parseInt(selObj.getAttribute('rotation')) || 0; + + var docObjProps = selObj.querySelectorAll("properties > property"); + if (docObjProps) { + for (var k = 0; k < docObjProps.length; k++) + objectProp[docObjProps[k].getAttribute('name')] = docObjProps[k].getAttribute('value'); + } + + //polygon + var polygonProps = selObj.querySelectorAll("polygon"); + if(polygonProps && polygonProps.length > 0) { + var selPgPointStr = polygonProps[0].getAttribute('points'); + if(selPgPointStr) + objectProp["points"] = this._parsePointsString(selPgPointStr); + } + + //polyline + var polylineProps = selObj.querySelectorAll("polyline"); + if(polylineProps && polylineProps.length > 0) { + var selPlPointStr = polylineProps[0].getAttribute('points'); + if(selPlPointStr) + objectProp["polylinePoints"] = this._parsePointsString(selPlPointStr); + } + + // Add the object to the objectGroup + objectGroup.setObjects(objectProp); + } + } + + this.setObjectGroups(objectGroup); + } + } + return map; + }, + + _parsePointsString:function(pointsString){ + if(!pointsString) + return null; + + var points = []; + var pointsStr = pointsString.split(' '); + for(var i = 0; i < pointsStr.length; i++){ + var selPointStr = pointsStr[i].split(','); + points.push({'x':selPointStr[0], 'y':selPointStr[1]}); + } + return points; + }, + + /** + * initializes parsing of an XML string, either a tmx (Map) string or tsx (Tileset) string + * @param {String} xmlString + * @return {Boolean} + */ + parseXMLString:function (xmlString) { + return this.parseXMLFile(xmlString, true); + }, + + /** + * Gets the tile properties. + * @return {object} + */ + getTileProperties:function () { + return this._tileProperties; + }, + + /** + * Set the tile properties. + * @param {object} tileProperties + */ + setTileProperties:function (tileProperties) { + this._tileProperties.push(tileProperties); + }, + + /** + * Gets the currentString + * @return {String} + */ + getCurrentString:function () { + return this.currentString; + }, + + /** + * Set the currentString + * @param {String} currentString + */ + setCurrentString:function (currentString) { + this.currentString = currentString; + }, + + /** + * Gets the tmxFileName + * @return {String} + */ + getTMXFileName:function () { + return this.tmxFileName; + }, + + /** + * Set the tmxFileName + * @param {String} fileName + */ + setTMXFileName:function (fileName) { + this.tmxFileName = fileName; + }, + + _internalInit:function (tmxFileName, resourcePath) { + this._tilesets.length = 0; + this._layers.length = 0; + + this.tmxFileName = tmxFileName; + if (resourcePath) + this._resources = resourcePath; + + this._objectGroups.length = 0; + this.properties.length = 0; + this._tileProperties.length = 0; + + // tmp vars + this.currentString = ""; + this.storingCharacters = false; + this.layerAttrs = cc.TMXLayerInfo.ATTRIB_NONE; + this.parentElement = cc.TMX_PROPERTY_NONE; + this._currentFirstGID = 0; + } +}); + +var _p = cc.TMXMapInfo.prototype; + +// Extended properties +/** @expose */ +_p.mapWidth; +cc.defineGetterSetter(_p, "mapWidth", _p._getMapWidth, _p._setMapWidth); +/** @expose */ +_p.mapHeight; +cc.defineGetterSetter(_p, "mapHeight", _p._getMapHeight, _p._setMapHeight); +/** @expose */ +_p.tileWidth; +cc.defineGetterSetter(_p, "tileWidth", _p._getTileWidth, _p._setTileWidth); +/** @expose */ +_p.tileHeight; +cc.defineGetterSetter(_p, "tileHeight", _p._getTileHeight, _p._setTileHeight); + + +/** + * Creates a TMX Format with a tmx file or content string + * @deprecated since v3.0 please use new cc.TMXMapInfo(tmxFile, resourcePath) instead. + * @param {String} tmxFile fileName or content string + * @param {String} resourcePath If tmxFile is a file name ,it is not required.If tmxFile is content string ,it is must required. + * @return {cc.TMXMapInfo} + */ +cc.TMXMapInfo.create = function (tmxFile, resourcePath) { + return new cc.TMXMapInfo(tmxFile, resourcePath); +}; + + +cc.loader.register(["tmx", "tsx"], cc._txtLoader); + + +/** + * @constant + * @type Number + */ +cc.TMXLayerInfo.ATTRIB_NONE = 1 << 0; +/** + * @constant + * @type Number + */ +cc.TMXLayerInfo.ATTRIB_BASE64 = 1 << 1; +/** + * @constant + * @type Number + */ +cc.TMXLayerInfo.ATTRIB_GZIP = 1 << 2; +/** + * @constant + * @type Number + */ +cc.TMXLayerInfo.ATTRIB_ZLIB = 1 << 3; diff --git a/frameworks/cocos2d-html5/cocos2d/transitions/CCTransition.js b/frameworks/cocos2d-html5/cocos2d/transitions/CCTransition.js new file mode 100644 index 0000000..09b1822 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/transitions/CCTransition.js @@ -0,0 +1,1478 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ +/** + * A tag constant for identifying fade scenes + * @constant + * @type Number + */ +cc.SCENE_FADE = 4208917214; + +/** + * horizontal orientation Type where the Left is nearer + * @constant + * @type Number + */ +cc.TRANSITION_ORIENTATION_LEFT_OVER = 0; +/** + * horizontal orientation type where the Right is nearer + * @constant + * @type Number + */ +cc.TRANSITION_ORIENTATION_RIGHT_OVER = 1; +/** + * vertical orientation type where the Up is nearer + * @constant + * @type Number + */ +cc.TRANSITION_ORIENTATION_UP_OVER = 0; +/** + * vertical orientation type where the Bottom is nearer + * @constant + * @type Number + */ +cc.TRANSITION_ORIENTATION_DOWN_OVER = 1; + +/** + * @class + * @extends cc.Scene + * @param {Number} t time in seconds + * @param {cc.Scene} scene the scene to transit with + * @example + * var trans = new TransitionScene(time,scene); + */ +cc.TransitionScene = cc.Scene.extend(/** @lends cc.TransitionScene# */{ + _inScene: null, + _outScene: null, + _duration: null, + _isInSceneOnTop: false, + _isSendCleanupToScene: false, + _className: "TransitionScene", + + /** + * creates a base transition with duration and incoming scene + * Constructor of cc.TransitionScene + * @param {Number} t time in seconds + * @param {cc.Scene} scene the scene to transit with + */ + ctor: function (t, scene) { + cc.Scene.prototype.ctor.call(this); + if (t !== undefined && scene !== undefined) + this.initWithDuration(t, scene); + }, + + //private + _setNewScene: function (dt) { + this.unschedule(this._setNewScene); + // Before replacing, save the "send cleanup to scene" + var director = cc.director; + this._isSendCleanupToScene = director.isSendCleanupToScene(); + director.runScene(this._inScene); + + // enable events while transitions + cc.eventManager.setEnabled(true); + + // issue #267 + this._outScene.visible = true; + }, + + //protected + _sceneOrder: function () { + this._isInSceneOnTop = true; + }, + + /** + * stuff gets drawn here + */ + visit: function () { + if (this._isInSceneOnTop) { + this._outScene.visit(); + this._inScene.visit(); + } else { + this._inScene.visit(); + this._outScene.visit(); + } + cc.Node.prototype.visit.call(this); + }, + + /** + *

+ * Event callback that is invoked every time when cc.TransitionScene enters the 'stage'.
+ * If the TransitionScene enters the 'stage' with a transition, this event is called when the transition starts.
+ * During onEnter you can't access a "sister/brother" node.
+ * If you override onEnter, you must call its parent's onEnter function with this._super(). + *

+ */ + onEnter: function () { + cc.Node.prototype.onEnter.call(this); + + // disable events while transitions + cc.eventManager.setEnabled(false); + + // outScene should not receive the onEnter callback + // only the onExitTransitionDidStart + this._outScene._performRecursive(cc.Node._stateCallbackType.onExitTransitionDidStart); + + this._inScene._performRecursive(cc.Node._stateCallbackType.onEnter); + }, + + /** + *

+ * callback that is called every time the cc.TransitionScene leaves the 'stage'.
+ * If the cc.TransitionScene leaves the 'stage' with a transition, this callback is called when the transition finishes.
+ * During onExit you can't access a sibling node.
+ * If you override onExit, you shall call its parent's onExit with this._super(). + *

+ */ + onExit: function () { + cc.Node.prototype.onExit.call(this); + + // enable events while transitions + cc.eventManager.setEnabled(true); + + this._outScene._performRecursive(cc.Node._stateCallbackType.onExit); + + // _inScene should not receive the onEnter callback + // only the onEnterTransitionDidFinish + this._inScene._performRecursive(cc.Node._stateCallbackType.onEnterTransitionDidFinish); + }, + + /** + * custom cleanup + */ + cleanup: function () { + cc.Node.prototype.cleanup.call(this); + + if (this._isSendCleanupToScene) + this._outScene._performRecursive(cc.Node._stateCallbackType.cleanup); + }, + + /** + * initializes a transition with duration and incoming scene + * @param {Number} t time in seconds + * @param {cc.Scene} scene a scene to transit to + * @return {Boolean} return false if error + */ + initWithDuration: function (t, scene) { + if (!scene) + throw new Error("cc.TransitionScene.initWithDuration(): Argument scene must be non-nil"); + + if (this.init()) { + this._duration = t; + this.attr({ + x: 0, + y: 0, + anchorX: 0, + anchorY: 0 + }); + // retain + this._inScene = scene; + this._outScene = cc.director.getRunningScene(); + if (!this._outScene) { + this._outScene = new cc.Scene(); + this._outScene.init(); + } + + if (this._inScene === this._outScene) + throw new Error("cc.TransitionScene.initWithDuration(): Incoming scene must be different from the outgoing scene"); + + this._sceneOrder(); + return true; + } else { + return false; + } + }, + + /** + * called after the transition finishes + */ + finish: function () { + // clean up + this._inScene.attr({ + visible: true, + x: 0, + y: 0, + scale: 1.0, + rotation: 0.0 + }); + + this._outScene.attr({ + visible: false, + x: 0, + y: 0, + scale: 1.0, + rotation: 0.0 + }); + + //[self schedule:@selector(setNewScene:) interval:0]; + this.schedule(this._setNewScene, 0); + }, + + /** + * set hide the out scene and show in scene + */ + hideOutShowIn: function () { + this._inScene.visible = true; + this._outScene.visible = false; + } +}); +/** + * creates a base transition with duration and incoming scene + * @deprecated since v3.0, please use new cc.TransitionScene(t,scene) instead + * @param {Number} t time in seconds + * @param {cc.Scene} scene the scene to transit with + * @return {cc.TransitionScene|Null} + */ +cc.TransitionScene.create = function (t, scene) { + return new cc.TransitionScene(t, scene); +}; + +/** + * A cc.Transition that supports orientation like.
+ * Possible orientation: LeftOver, RightOver, UpOver, DownOver
+ * useful for when you want to make a transition happen between 2 orientations + * @class + * @extends cc.TransitionScene + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @param {cc.TRANSITION_ORIENTATION_LEFT_OVER|cc.TRANSITION_ORIENTATION_RIGHT_OVER|cc.TRANSITION_ORIENTATION_UP_OVER|cc.TRANSITION_ORIENTATION_DOWN_OVER} orientation + * @example + * var trans = new cc.TransitionSceneOriented(time,scene,orientation); + */ +cc.TransitionSceneOriented = cc.TransitionScene.extend(/** @lends cc.TransitionSceneOriented# */{ + _orientation: 0, + + /** + * Constructor of TransitionSceneOriented + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @param {cc.TRANSITION_ORIENTATION_LEFT_OVER|cc.TRANSITION_ORIENTATION_RIGHT_OVER|cc.TRANSITION_ORIENTATION_UP_OVER|cc.TRANSITION_ORIENTATION_DOWN_OVER} orientation + */ + ctor: function (t, scene, orientation) { + cc.TransitionScene.prototype.ctor.call(this); + orientation != undefined && this.initWithDuration(t, scene, orientation); + }, + /** + * initialize the transition + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @param {cc.TRANSITION_ORIENTATION_LEFT_OVER|cc.TRANSITION_ORIENTATION_RIGHT_OVER|cc.TRANSITION_ORIENTATION_UP_OVER|cc.TRANSITION_ORIENTATION_DOWN_OVER} orientation + * @return {Boolean} + */ + initWithDuration: function (t, scene, orientation) { + if (cc.TransitionScene.prototype.initWithDuration.call(this, t, scene)) { + this._orientation = orientation; + } + return true; + } +}); + +/** + * creates a base transition with duration and incoming scene + * @deprecated since v3.0 ,please use new cc.TransitionSceneOriented(t, scene, orientation) instead. + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @param {cc.TRANSITION_ORIENTATION_LEFT_OVER|cc.TRANSITION_ORIENTATION_RIGHT_OVER|cc.TRANSITION_ORIENTATION_UP_OVER|cc.TRANSITION_ORIENTATION_DOWN_OVER} orientation + * @return {cc.TransitionSceneOriented} + */ +cc.TransitionSceneOriented.create = function (t, scene, orientation) { + return new cc.TransitionSceneOriented(t, scene, orientation); +}; + +/** + * Rotate and zoom out the outgoing scene, and then rotate and zoom in the incoming + * @class + * @extends cc.TransitionScene + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @example + * var trans = new cc.TransitionRotoZoom(t, scene); + */ +cc.TransitionRotoZoom = cc.TransitionScene.extend(/** @lends cc.TransitionRotoZoom# */{ + + /** + * Constructor of TransitionRotoZoom + * @function + * @param {Number} t time in seconds + * @param {cc.Scene} scene + */ + ctor: function (t, scene) { + cc.TransitionScene.prototype.ctor.call(this); + scene && this.initWithDuration(t, scene); + }, + /** + * Custom On Enter callback + * @override + */ + onEnter: function () { + cc.TransitionScene.prototype.onEnter.call(this); + + this._inScene.attr({ + scale: 0.001, + anchorX: 0.5, + anchorY: 0.5 + }); + this._outScene.attr({ + scale: 1.0, + anchorX: 0.5, + anchorY: 0.5 + }); + + var rotoZoom = cc.sequence( + cc.spawn(cc.scaleBy(this._duration / 2, 0.001), + cc.rotateBy(this._duration / 2, 360 * 2)), + cc.delayTime(this._duration / 2)); + + this._outScene.runAction(rotoZoom); + this._inScene.runAction( + cc.sequence(rotoZoom.reverse(), + cc.callFunc(this.finish, this))); + } +}); + +/** + * Creates a Transition rotation and zoom + * @deprecated since v3.0,please use new cc.TransitionRotoZoom(t, scene) instead + * @param {Number} t time in seconds + * @param {cc.Scene} scene the scene to work with + * @return {cc.TransitionRotoZoom} + */ +cc.TransitionRotoZoom.create = function (t, scene) { + return new cc.TransitionRotoZoom(t, scene); +}; + +/** + * Zoom out and jump the outgoing scene, and then jump and zoom in the incoming + * @class + * @extends cc.TransitionScene + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @example + * var trans = new cc.TransitionJumpZoom(t, scene); + */ +cc.TransitionJumpZoom = cc.TransitionScene.extend(/** @lends cc.TransitionJumpZoom# */{ + /** + * Constructor of TransitionJumpZoom + * @param {Number} t time in seconds + * @param {cc.Scene} scene + */ + ctor: function (t, scene) { + cc.TransitionScene.prototype.ctor.call(this); + scene && this.initWithDuration(t, scene); + }, + /** + * Custom on enter + */ + onEnter: function () { + cc.TransitionScene.prototype.onEnter.call(this); + var winSize = cc.director.getWinSize(); + + this._inScene.attr({ + scale: 0.5, + x: winSize.width, + y: 0, + anchorX: 0.5, + anchorY: 0.5 + }); + this._outScene.anchorX = 0.5; + this._outScene.anchorY = 0.5; + + var jump = cc.jumpBy(this._duration / 4, cc.p(-winSize.width, 0), winSize.width / 4, 2); + var scaleIn = cc.scaleTo(this._duration / 4, 1.0); + var scaleOut = cc.scaleTo(this._duration / 4, 0.5); + + var jumpZoomOut = cc.sequence(scaleOut, jump); + var jumpZoomIn = cc.sequence(jump, scaleIn); + + var delay = cc.delayTime(this._duration / 2); + this._outScene.runAction(jumpZoomOut); + this._inScene.runAction(cc.sequence(delay, jumpZoomIn, cc.callFunc(this.finish, this))); + } +}); + +/** + * creates a scene transition that zooms then jump across the screen, the same for the incoming scene + * @deprecated since v3.0,please use new cc.TransitionJumpZoom(t, scene); + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @return {cc.TransitionJumpZoom} + */ +cc.TransitionJumpZoom.create = function (t, scene) { + return new cc.TransitionJumpZoom(t, scene); +}; + +/** + * Move in from to the left the incoming scene. + * @class + * @extends cc.TransitionScene + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @example + * var trans = new cc.TransitionMoveInL(time,scene); + */ +cc.TransitionMoveInL = cc.TransitionScene.extend(/** @lends cc.TransitionMoveInL# */{ + /** + * Constructor of TransitionMoveInL + * @param {Number} t time in seconds + * @param {cc.Scene} scene + */ + ctor: function (t, scene) { + cc.TransitionScene.prototype.ctor.call(this); + scene && this.initWithDuration(t, scene); + }, + /** + * Custom on enter + */ + onEnter: function () { + cc.TransitionScene.prototype.onEnter.call(this); + this.initScenes(); + + var action = this.action(); + this._inScene.runAction( + cc.sequence(this.easeActionWithAction(action), cc.callFunc(this.finish, this)) + ); + }, + + /** + * initializes the scenes + */ + initScenes: function () { + this._inScene.setPosition(-cc.director.getWinSize().width, 0); + }, + + /** + * returns the action that will be performed + */ + action: function () { + return cc.moveTo(this._duration, cc.p(0, 0)); + }, + + /** + * creates an ease action from action + * @param {cc.ActionInterval} action + * @return {cc.EaseOut} + */ + easeActionWithAction: function (action) { + return new cc.EaseOut(action, 2.0); + } +}); + +/** + * creates an action that Move in from to the left the incoming scene. + * @deprecated since v3.0,please use new cc.TransitionMoveInL(t, scene) instead + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @return {cc.TransitionMoveInL} + */ +cc.TransitionMoveInL.create = function (t, scene) { + return new cc.TransitionMoveInL(t, scene); +}; + +/** + * Move in from to the right the incoming scene. + * @class + * @extends cc.TransitionMoveInL + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @example + * var trans = new cc.TransitionMoveInR(time,scene); + */ +cc.TransitionMoveInR = cc.TransitionMoveInL.extend(/** @lends cc.TransitionMoveInR# */{ + /** + * Constructor of TransitionMoveInR + * @param {Number} t time in seconds + * @param {cc.Scene} scene + */ + ctor: function (t, scene) { + cc.TransitionMoveInL.prototype.ctor.call(this); + scene && this.initWithDuration(t, scene); + }, + /** + * Init function + */ + initScenes: function () { + this._inScene.setPosition(cc.director.getWinSize().width, 0); + } +}); + +/** + * create a scene transition that Move in from to the right the incoming scene. + * @deprecated since v3.0,please use new cc.TransitionMoveInR(t, scene) instead + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @return {cc.TransitionMoveInR} + */ +cc.TransitionMoveInR.create = function (t, scene) { + return new cc.TransitionMoveInR(t, scene); +}; + +/** + * Move in from to the top the incoming scene. + * @class + * @extends cc.TransitionMoveInL + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @example + * var trans = new cc.TransitionMoveInT(time,scene); + */ +cc.TransitionMoveInT = cc.TransitionMoveInL.extend(/** @lends cc.TransitionMoveInT# */{ + /** + * Constructor of TransitionMoveInT + * @param {Number} t time in seconds + * @param {cc.Scene} scene + */ + ctor: function (t, scene) { + cc.TransitionMoveInL.prototype.ctor.call(this); + scene && this.initWithDuration(t, scene); + }, + /** + * init function + */ + initScenes: function () { + this._inScene.setPosition(0, cc.director.getWinSize().height); + } +}); + +/** + * Move in from to the top the incoming scene. + * @deprecated since v3.0,please use new cc.TransitionMoveInT(t, scene) instead + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @return {cc.TransitionMoveInT} + */ +cc.TransitionMoveInT.create = function (t, scene) { + return new cc.TransitionMoveInT(t, scene); +}; + +/** + * Move in from to the bottom the incoming scene. + * @class + * @extends cc.TransitionMoveInL + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @example + * var trans = new cc.TransitionMoveInB(time,scene); + */ +cc.TransitionMoveInB = cc.TransitionMoveInL.extend(/** @lends cc.TransitionMoveInB# */{ + /** + * Constructor of TransitionMoveInB + * @param {Number} t time in seconds + * @param {cc.Scene} scene + */ + ctor: function (t, scene) { + cc.TransitionMoveInL.prototype.ctor.call(this); + scene && this.initWithDuration(t, scene); + }, + + /** + * init function + */ + initScenes: function () { + this._inScene.setPosition(0, -cc.director.getWinSize().height); + } +}); + +/** + * create a scene transition that Move in from to the bottom the incoming scene. + * @deprecated since v3.0,please use new cc.TransitionMoveInB(t, scene) instead + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @return {cc.TransitionMoveInB} + */ +cc.TransitionMoveInB.create = function (t, scene) { + return new cc.TransitionMoveInB(t, scene); +}; + +/** + * The adjust factor is needed to prevent issue #442
+ * One solution is to use DONT_RENDER_IN_SUBPIXELS images, but NO
+ * The other issue is that in some transitions (and I don't know why)
+ * the order should be reversed (In in top of Out or vice-versa). + * @constant + * @type Number + */ +cc.ADJUST_FACTOR = 0.5; + +/** + * a transition that a new scene is slided from left + * @class + * @extends cc.TransitionScene + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @example + * var trans = cc.TransitionSlideInL(time,scene); + */ +cc.TransitionSlideInL = cc.TransitionScene.extend(/** @lends cc.TransitionSlideInL# */{ + /** + * Constructor of TransitionSlideInL + * @param {Number} t time in seconds + * @param {cc.Scene} scene + */ + ctor: function (t, scene) { + cc.TransitionScene.prototype.ctor.call(this); + scene && this.initWithDuration(t, scene); + }, + _sceneOrder: function () { + this._isInSceneOnTop = false; + }, + + /** + * custom on enter + */ + onEnter: function () { + cc.TransitionScene.prototype.onEnter.call(this); + this.initScenes(); + + var inA = this.action(); + var outA = this.action(); + + var inAction = cc.sequence(this.easeActionWithAction(inA), cc.callFunc(this.finish, this)); + var outAction = this.easeActionWithAction(outA); + this._inScene.runAction(inAction); + this._outScene.runAction(outAction); + }, + + /** + * initializes the scenes + */ + initScenes: function () { + this._inScene.setPosition(-cc.director.getWinSize().width + cc.ADJUST_FACTOR, 0); + }, + /** + * returns the action that will be performed by the incoming and outgoing scene + * @return {cc.MoveBy} + */ + action: function () { + return cc.moveBy(this._duration, cc.p(cc.director.getWinSize().width - cc.ADJUST_FACTOR, 0)); + }, + + /** + * @param {cc.ActionInterval} action + * @return {*} + */ + easeActionWithAction: function (action) { + return new cc.EaseInOut(action, 2.0); + } +}); + +/** + * create a transition that a new scene is slided from left + * @deprecated since v3.0,please use new cc.TransitionSlideInL(t, scene) instead + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @return {cc.TransitionSlideInL} + */ +cc.TransitionSlideInL.create = function (t, scene) { + return new cc.TransitionSlideInL(t, scene); +}; + +/** + * Slide in the incoming scene from the right border. + * @class + * @extends cc.TransitionSlideInL + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @example + * var trans = new cc.TransitionSlideInR(time,scene); + */ +cc.TransitionSlideInR = cc.TransitionSlideInL.extend(/** @lends cc.TransitionSlideInR# */{ + /** + * Constructor of TransitionSlideInR + * @param {Number} t time in seconds + * @param {cc.Scene} scene + */ + ctor: function (t, scene) { + cc.TransitionSlideInL.prototype.ctor.call(this); + scene && this.initWithDuration(t, scene); + }, + _sceneOrder: function () { + this._isInSceneOnTop = true; + }, + /** + * initializes the scenes + */ + initScenes: function () { + this._inScene.setPosition(cc.director.getWinSize().width - cc.ADJUST_FACTOR, 0); + }, + /** + * returns the action that will be performed by the incoming and outgoing scene + * @return {cc.MoveBy} + */ + action: function () { + return cc.moveBy(this._duration, cc.p(-(cc.director.getWinSize().width - cc.ADJUST_FACTOR), 0)); + } +}); + +/** + * create Slide in the incoming scene from the right border. + * @deprecated since v3.0,please use new cc.TransitionSlideInR(t, scene) instead + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @return {cc.TransitionSlideInR} + */ +cc.TransitionSlideInR.create = function (t, scene) { + return new cc.TransitionSlideInR(t, scene); +}; + +/** + * Slide in the incoming scene from the bottom border. + * @class + * @extends cc.TransitionSlideInL + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @example + * var trans = new cc.TransitionSlideInB(time,scene); + */ +cc.TransitionSlideInB = cc.TransitionSlideInL.extend(/** @lends cc.TransitionSlideInB# */{ + /** + * Constructor of TransitionSlideInB + * @param {Number} t time in seconds + * @param {cc.Scene} scene + */ + ctor: function (t, scene) { + cc.TransitionSlideInL.prototype.ctor.call(this); + scene && this.initWithDuration(t, scene); + }, + _sceneOrder: function () { + this._isInSceneOnTop = false; + }, + + /** + * initializes the scenes + */ + initScenes: function () { + this._inScene.setPosition(0, -(cc.director.getWinSize().height - cc.ADJUST_FACTOR)); + }, + + /** + * returns the action that will be performed by the incoming and outgoing scene + * @return {cc.MoveBy} + */ + action: function () { + return cc.moveBy(this._duration, cc.p(0, cc.director.getWinSize().height - cc.ADJUST_FACTOR)); + } +}); + +/** + * create a Slide in the incoming scene from the bottom border. + * @deprecated since v3.0,please use new cc.TransitionSlideInB(t, scene) instead. + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @return {cc.TransitionSlideInB} + */ +cc.TransitionSlideInB.create = function (t, scene) { + return new cc.TransitionSlideInB(t, scene); +}; + +/** + * Slide in the incoming scene from the top border. + * @class + * @extends cc.TransitionSlideInL + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @example + * var trans = new cc.TransitionSlideInT(time,scene); + */ +cc.TransitionSlideInT = cc.TransitionSlideInL.extend(/** @lends cc.TransitionSlideInT# */{ + /** + * Constructor of TransitionSlideInT + * @param {Number} t time in seconds + * @param {cc.Scene} scene + */ + ctor: function (t, scene) { + cc.TransitionSlideInL.prototype.ctor.call(this); + scene && this.initWithDuration(t, scene); + }, + _sceneOrder: function () { + this._isInSceneOnTop = true; + }, + + /** + * initializes the scenes + */ + initScenes: function () { + this._inScene.setPosition(0, cc.director.getWinSize().height - cc.ADJUST_FACTOR); + }, + + /** + * returns the action that will be performed by the incoming and outgoing scene + * @return {cc.MoveBy} + */ + action: function () { + return cc.moveBy(this._duration, cc.p(0, -(cc.director.getWinSize().height - cc.ADJUST_FACTOR))); + } +}); + +/** + * create a Slide in the incoming scene from the top border. + * @deprecated since v3.0,please use new cc.TransitionSlideInT(t, scene) instead. + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @return {cc.TransitionSlideInT} + */ +cc.TransitionSlideInT.create = function (t, scene) { + return new cc.TransitionSlideInT(t, scene); +}; + +/** + * Shrink the outgoing scene while grow the incoming scene + * @class + * @extends cc.TransitionScene + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @example + * var trans = new cc.TransitionShrinkGrow(time,scene); + */ +cc.TransitionShrinkGrow = cc.TransitionScene.extend(/** @lends cc.TransitionShrinkGrow# */{ + /** + * Constructor of TransitionShrinkGrow + * @param {Number} t time in seconds + * @param {cc.Scene} scene + */ + ctor: function (t, scene) { + cc.TransitionScene.prototype.ctor.call(this); + scene && this.initWithDuration(t, scene); + }, + /** + * Custom on enter + */ + onEnter: function () { + cc.TransitionScene.prototype.onEnter.call(this); + + this._inScene.attr({ + scale: 0.001, + anchorX: 2 / 3.0, + anchorY: 0.5 + }); + this._outScene.attr({ + scale: 1.0, + anchorX: 1 / 3.0, + anchorY: 0.5 + }); + + var scaleOut = cc.scaleTo(this._duration, 0.01); + var scaleIn = cc.scaleTo(this._duration, 1.0); + + this._inScene.runAction(cc.sequence(this.easeActionWithAction(scaleIn), cc.callFunc(this.finish, this))); + this._outScene.runAction(this.easeActionWithAction(scaleOut)); + }, + + /** + * @param action + * @return {cc.EaseOut} + */ + easeActionWithAction: function (action) { + return new cc.EaseOut(action, 2.0); + } +}); + +/** + * Shrink the outgoing scene while grow the incoming scene + * @deprecated since v3.0,please use new cc.TransitionShrinkGrow(t, scene) instead. + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @return {cc.TransitionShrinkGrow} + */ +cc.TransitionShrinkGrow.create = function (t, scene) { + return new cc.TransitionShrinkGrow(t, scene); +}; + +/** + * Fade out the outgoing scene and then fade in the incoming scene. + * @class + * @extends cc.TransitionScene + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @param {cc.TRANSITION_ORIENTATION_LEFT_OVER|cc.TRANSITION_ORIENTATION_RIGHT_OVER|cc.TRANSITION_ORIENTATION_UP_OVER|cc.TRANSITION_ORIENTATION_DOWN_OVER} o + * @example + * var trans = new cc.TransitionFade(time,scene,color) + */ +cc.TransitionFade = cc.TransitionScene.extend(/** @lends cc.TransitionFade# */{ + _color: null, + + /** + * Constructor of TransitionFade + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @param {cc.TRANSITION_ORIENTATION_LEFT_OVER|cc.TRANSITION_ORIENTATION_RIGHT_OVER|cc.TRANSITION_ORIENTATION_UP_OVER|cc.TRANSITION_ORIENTATION_DOWN_OVER} o + */ + ctor: function (t, scene, color) { + cc.TransitionScene.prototype.ctor.call(this); + this._color = cc.color(); + scene && this.initWithDuration(t, scene, color); + }, + + /** + * custom on enter + */ + onEnter: function () { + cc.TransitionScene.prototype.onEnter.call(this); + + var l = new cc.LayerColor(this._color); + this._inScene.visible = false; + + this.addChild(l, 2, cc.SCENE_FADE); + var f = this.getChildByTag(cc.SCENE_FADE); + + var a = cc.sequence( + cc.fadeIn(this._duration / 2), + cc.callFunc(this.hideOutShowIn, this), + cc.fadeOut(this._duration / 2), + cc.callFunc(this.finish, this) + ); + f.runAction(a); + }, + + /** + * custom on exit + */ + onExit: function () { + cc.TransitionScene.prototype.onExit.call(this); + this.removeChildByTag(cc.SCENE_FADE, false); + }, + + /** + * initializes the transition with a duration and with an RGB color + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @param {cc.Color} color + * @return {Boolean} + */ + initWithDuration: function (t, scene, color) { + color = color || cc.color.BLACK; + if (cc.TransitionScene.prototype.initWithDuration.call(this, t, scene)) { + this._color.r = color.r; + this._color.g = color.g; + this._color.b = color.b; + this._color.a = 0; + } + return true; + } +}); + + +/** + * Fade out the outgoing scene and then fade in the incoming scene. + * @deprecated since v3.0,please use new cc.TransitionFade(time,scene,color) instead. + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @param {cc.Color} color + * @return {cc.TransitionFade} + */ +cc.TransitionFade.create = function (t, scene, color) { + return new cc.TransitionFade(t, scene, color); +}; + +/** + * Cross fades two scenes using the cc.RenderTexture object. + * @class + * @extends cc.TransitionScene + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @example + * var trans = new cc.TransitionCrossFade(time,scene); + */ +cc.TransitionCrossFade = cc.TransitionScene.extend(/** @lends cc.TransitionCrossFade# */{ + /** + * Constructor of TransitionCrossFade + * @param {Number} t time in seconds + * @param {cc.Scene} scene + */ + ctor: function (t, scene) { + cc.TransitionScene.prototype.ctor.call(this); + scene && this.initWithDuration(t, scene); + }, + /** + * custom on enter + */ + onEnter: function () { + cc.TransitionScene.prototype.onEnter.call(this); + + // create a transparent color layer + // in which we are going to add our rendertextures + var color = cc.color(0, 0, 0, 0); + var winSize = cc.director.getWinSize(); + var layer = new cc.LayerColor(color); + + // create the first render texture for inScene + var inTexture = new cc.RenderTexture(winSize.width, winSize.height); + + inTexture.sprite.anchorX = 0.5; + inTexture.sprite.anchorY = 0.5; + inTexture.attr({ + x: winSize.width / 2, + y: winSize.height / 2, + anchorX: 0.5, + anchorY: 0.5 + }); + + // render inScene to its texturebuffer + inTexture.begin(); + this._inScene.visit(); + inTexture.end(); + + // create the second render texture for outScene + var outTexture = new cc.RenderTexture(winSize.width, winSize.height); + outTexture.setPosition(winSize.width / 2, winSize.height / 2); + outTexture.sprite.anchorX = outTexture.anchorX = 0.5; + outTexture.sprite.anchorY = outTexture.anchorY = 0.5; + + // render outScene to its texturebuffer + outTexture.begin(); + this._outScene.visit(); + outTexture.end(); + + inTexture.sprite.setBlendFunc(cc.ONE, cc.ONE); // inScene will lay on background and will not be used with alpha + outTexture.sprite.setBlendFunc(cc.SRC_ALPHA, cc.ONE_MINUS_SRC_ALPHA); // we are going to blend outScene via alpha + + // add render textures to the layer + layer.addChild(inTexture); + layer.addChild(outTexture); + + // initial opacity: + inTexture.sprite.opacity = 255; + outTexture.sprite.opacity = 255; + + // create the blend action + var layerAction = cc.sequence( + cc.fadeTo(this._duration, 0), cc.callFunc(this.hideOutShowIn, this), + cc.callFunc(this.finish, this) + ); + + // run the blend action + outTexture.sprite.runAction(layerAction); + + // add the layer (which contains our two rendertextures) to the scene + this.addChild(layer, 2, cc.SCENE_FADE); + }, + + /** + * custom on exit + */ + onExit: function () { + this.removeChildByTag(cc.SCENE_FADE, false); + cc.TransitionScene.prototype.onExit.call(this); + }, +}); + +/** + * Cross fades two scenes using the cc.RenderTexture object. + * @deprecated since v3.0,please use new cc.TransitionCrossFade(t, scene) instead. + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @return {cc.TransitionCrossFade} + */ +cc.TransitionCrossFade.create = function (t, scene) { + return new cc.TransitionCrossFade(t, scene); +}; + +/** + * Turn off the tiles of the outgoing scene in random order + * @class + * @extends cc.TransitionScene + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @example + * var trans = new cc.TransitionTurnOffTiles(time,scene); + */ +cc.TransitionTurnOffTiles = cc.TransitionScene.extend(/** @lends cc.TransitionTurnOffTiles# */{ + _gridProxy: null, + /** + * Constructor of TransitionCrossFade + * @param {Number} t time in seconds + * @param {cc.Scene} scene + */ + ctor: function (t, scene) { + cc.TransitionScene.prototype.ctor.call(this); + this._gridProxy = new cc.NodeGrid(); + scene && this.initWithDuration(t, scene); + }, + + _sceneOrder: function () { + this._isInSceneOnTop = false; + }, + + /** + * custom on enter + */ + onEnter: function () { + cc.TransitionScene.prototype.onEnter.call(this); + this._gridProxy.setTarget(this._outScene); + this._gridProxy._performRecursive(cc.Node._stateCallbackType.onEnter); + + var winSize = cc.director.getWinSize(); + var aspect = winSize.width / winSize.height; + var x = 0 | (12 * aspect); + var y = 12; + var toff = cc.turnOffTiles(this._duration, cc.size(x, y)); + var action = this.easeActionWithAction(toff); + this._gridProxy.runAction(cc.sequence(action, cc.callFunc(this.finish, this), cc.stopGrid())); + }, + + visit: function () { + this._inScene.visit(); + this._gridProxy.visit(); + }, + + /** + * @param {cc.ActionInterval} action + * @return {cc.ActionInterval} + */ + easeActionWithAction: function (action) { + return action; + } +}); + +/** + * Turn off the tiles of the outgoing scene in random order + * @deprecated since v3.0,please use new cc.TransitionTurnOffTiles(t, scene) instead. + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @return {cc.TransitionTurnOffTiles} + */ +cc.TransitionTurnOffTiles.create = function (t, scene) { + return new cc.TransitionTurnOffTiles(t, scene); +}; + +/** + * The odd columns goes upwards while the even columns goes downwards. + * @class + * @extends cc.TransitionScene + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @example + * var trans = new cc.TransitionSplitCols(time,scene); + */ +cc.TransitionSplitCols = cc.TransitionScene.extend(/** @lends cc.TransitionSplitCols# */{ + _gridProxy: null, + + _switchTargetToInscene: function () { + this._gridProxy.setTarget(this._inScene); + }, + + /** + * Constructor of TransitionSplitCols + * @param {Number} t time in seconds + * @param {cc.Scene} scene + */ + ctor: function (t, scene) { + cc.TransitionScene.prototype.ctor.call(this); + this._gridProxy = new cc.NodeGrid(); + scene && this.initWithDuration(t, scene); + }, + /** + * custom on enter + */ + onEnter: function () { + cc.TransitionScene.prototype.onEnter.call(this); + //this._inScene.visible = false; + this._gridProxy.setTarget(this._outScene); + this._gridProxy._performRecursive(cc.Node._stateCallbackType.onEnter); + + var split = this.action(); + var seq = cc.sequence( + split, cc.callFunc(this._switchTargetToInscene, this), split.reverse()); + + this._gridProxy.runAction( + cc.sequence(this.easeActionWithAction(seq), cc.callFunc(this.finish, this), cc.stopGrid()) + ); + }, + + onExit: function () { + this._gridProxy.setTarget(null); + this._gridProxy._performRecursive(cc.Node._stateCallbackType.onExit); + cc.TransitionScene.prototype.onExit.call(this); + }, + + visit: function () { + this._gridProxy.visit(); + }, + + /** + * @param {cc.ActionInterval} action + * @return {cc.EaseInOut} + */ + easeActionWithAction: function (action) { + return new cc.EaseInOut(action, 3.0); + }, + + /** + * @return {*} + */ + action: function () { + return cc.splitCols(this._duration / 2.0, 3); + } +}); + +/** + * The odd columns goes upwards while the even columns goes downwards. + * @deprecated since v3.0,please use new cc.TransitionSplitCols(t, scene) instead. + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @return {cc.TransitionSplitCols} + */ +cc.TransitionSplitCols.create = function (t, scene) { + return new cc.TransitionSplitCols(t, scene); +}; + +/** + * The odd rows goes to the left while the even rows goes to the right. + * @class + * @extends cc.TransitionSplitCols + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @example + * var trans = new cc.TransitionSplitRows(time,scene); + */ +cc.TransitionSplitRows = cc.TransitionSplitCols.extend(/** @lends cc.TransitionSplitRows# */{ + + /** + * Constructor of TransitionSplitRows + * @param {Number} t time in seconds + * @param {cc.Scene} scene + */ + ctor: function (t, scene) { + cc.TransitionSplitCols.prototype.ctor.call(this); + scene && this.initWithDuration(t, scene); + }, + /** + * @return {*} + */ + action: function () { + return cc.splitRows(this._duration / 2.0, 3); + } +}); + +/** + * The odd rows goes to the left while the even rows goes to the right. + * @deprecated since v3.0,please use new cc.TransitionSplitRows(t, scene) instead. + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @return {cc.TransitionSplitRows} + */ +cc.TransitionSplitRows.create = function (t, scene) { + return new cc.TransitionSplitRows(t, scene); +}; + +/** + * Fade the tiles of the outgoing scene from the left-bottom corner the to top-right corner. + * @class + * @extends cc.TransitionScene + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @example + * var trans = new cc.TransitionFadeTR(time,scene); + */ +cc.TransitionFadeTR = cc.TransitionScene.extend(/** @lends cc.TransitionFadeTR# */{ + _gridProxy: null, + /** + * Constructor of TransitionFadeTR + * @param {Number} t time in seconds + * @param {cc.Scene} scene + */ + ctor: function (t, scene) { + cc.TransitionScene.prototype.ctor.call(this); + this._gridProxy = new cc.NodeGrid(); + scene && this.initWithDuration(t, scene); + }, + _sceneOrder: function () { + this._isInSceneOnTop = false; + }, + + /** + * Custom on enter + */ + onEnter: function () { + cc.TransitionScene.prototype.onEnter.call(this); + + this._gridProxy.setTarget(this._outScene); + this._gridProxy._performRecursive(cc.Node._stateCallbackType.onEnter); + + var winSize = cc.director.getWinSize(); + var aspect = winSize.width / winSize.height; + var x = 0 | (12 * aspect); + var y = 12; + + var action = this.actionWithSize(cc.size(x, y)); + this._gridProxy.runAction( + cc.sequence(this.easeActionWithAction(action), cc.callFunc(this.finish, this), cc.stopGrid()) + ); + }, + + visit: function () { + this._inScene.visit(); + this._gridProxy.visit(); + }, + + /** + * @param {cc.ActionInterval} action + * @return {cc.ActionInterval} + */ + easeActionWithAction: function (action) { + return action; + }, + + /** + * @param {cc.Size} size + * @return {*} + */ + actionWithSize: function (size) { + return cc.fadeOutTRTiles(this._duration, size); + } +}); + +/** + * Fade the tiles of the outgoing scene from the left-bottom corner the to top-right corner. + * @deprecated since v3.0 please use new cc.TransitionFadeTR(t, scene) instead. + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @return {cc.TransitionFadeTR} + */ +cc.TransitionFadeTR.create = function (t, scene) { + return new cc.TransitionFadeTR(t, scene); +}; + +/** + * Fade the tiles of the outgoing scene from the top-right corner to the bottom-left corner. + * @class + * @extends cc.TransitionFadeTR + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @example + * var trans = new cc.TransitionFadeBL(time,scene) + */ +cc.TransitionFadeBL = cc.TransitionFadeTR.extend(/** @lends cc.TransitionFadeBL# */{ + /** + * Constructor of TransitionFadeBL + * @param {Number} t time in seconds + * @param {cc.Scene} scene + */ + ctor: function (t, scene) { + cc.TransitionFadeTR.prototype.ctor.call(this); + scene && this.initWithDuration(t, scene); + }, + + /** + * @param {cc.Size} size + * @return {*} + */ + actionWithSize: function (size) { + return cc.fadeOutBLTiles(this._duration, size); + } +}); + +/** + * Fade the tiles of the outgoing scene from the top-right corner to the bottom-left corner. + * @deprecated since v3.0,please use new cc.TransitionFadeBL(t, scene); + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @return {cc.TransitionFadeBL} + */ +cc.TransitionFadeBL.create = function (t, scene) { + return new cc.TransitionFadeBL(t, scene); +}; + +/** + * Fade the tiles of the outgoing scene from the top-right corner to the bottom-left corner. + * @class + * @extends cc.TransitionFadeTR + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @example + * var trans = new cc.TransitionFadeUp(time,scene); + */ +cc.TransitionFadeUp = cc.TransitionFadeTR.extend(/** @lends cc.TransitionFadeUp# */{ + + /** + * Constructor of TransitionFadeUp + * @function + * @param {Number} t time in seconds + * @param {cc.Scene} scene + */ + ctor: function (t, scene) { + cc.TransitionFadeTR.prototype.ctor.call(this); + scene && this.initWithDuration(t, scene); + }, + + /** + * @param {cc.Size} size + * @return {cc.FadeOutUpTiles} + */ + actionWithSize: function (size) { + return new cc.FadeOutUpTiles(this._duration, size); + } +}); + +/** + * Fade the tiles of the outgoing scene from the top-right corner to the bottom-left corner. + * @deprecated since v3.0,please use new cc.TransitionFadeUp(t, scene) instead. + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @return {cc.TransitionFadeUp} + */ +cc.TransitionFadeUp.create = function (t, scene) { + return new cc.TransitionFadeUp(t, scene); +}; + +/** + * Fade the tiles of the outgoing scene from the top to the bottom. + * @class + * @extends cc.TransitionFadeTR + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @example + * var trans = new cc.TransitionFadeDown(time,scene); + */ +cc.TransitionFadeDown = cc.TransitionFadeTR.extend(/** @lends cc.TransitionFadeDown# */{ + + /** + * Constructor of TransitionFadeDown + * @param {Number} t time in seconds + * @param {cc.Scene} scene + */ + ctor: function (t, scene) { + cc.TransitionFadeTR.prototype.ctor.call(this); + scene && this.initWithDuration(t, scene); + }, + + /** + * @param {cc.Size} size + * @return {*} + */ + actionWithSize: function (size) { + return cc.fadeOutDownTiles(this._duration, size); + } +}); + +/** + * Fade the tiles of the outgoing scene from the top to the bottom. + * @deprecated since v3.0,please use new cc.TransitionFadeDown(t, scene) instead. + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @return {cc.TransitionFadeDown} + */ +cc.TransitionFadeDown.create = function (t, scene) { + return new cc.TransitionFadeDown(t, scene); +}; diff --git a/frameworks/cocos2d-html5/cocos2d/transitions/CCTransitionPageTurn.js b/frameworks/cocos2d-html5/cocos2d/transitions/CCTransitionPageTurn.js new file mode 100644 index 0000000..44a6bed --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/transitions/CCTransitionPageTurn.js @@ -0,0 +1,152 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + *

A transition which peels back the bottom right hand corner of a scene
+ * to transition to the scene beneath it simulating a page turn.

+ * + *

This uses a 3DAction so it's strongly recommended that depth buffering
+ * is turned on in cc.director using:

+ * + *

cc.director.setDepthBufferFormat(kDepthBuffer16);

+ * @class + * @extends cc.TransitionScene + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @param {Boolean} backwards + * @example + * var trans = new cc.TransitionPageTurn(t, scene, backwards); + */ +cc.TransitionPageTurn = cc.TransitionScene.extend(/** @lends cc.TransitionPageTurn# */{ + + /** + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @param {Boolean} backwards + */ + ctor: function (t, scene, backwards) { + cc.TransitionScene.prototype.ctor.call(this); + this._gridProxy = new cc.NodeGrid(); + this.initWithDuration(t, scene, backwards); + }, + + /** + * @type Boolean + */ + _back: true, + _gridProxy: null, + _className: "TransitionPageTurn", + + /** + * Creates a base transition with duration and incoming scene.
+ * If back is true then the effect is reversed to appear as if the incoming
+ * scene is being turned from left over the outgoing scene. + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @param {Boolean} backwards + * @return {Boolean} + */ + initWithDuration: function (t, scene, backwards) { + // XXX: needed before [super init] + this._back = backwards; + + if (cc.TransitionScene.prototype.initWithDuration.call(this, t, scene)) { + // do something + } + return true; + }, + + /** + * @param {cc.Size} vector + * @return {cc.ReverseTime|cc.TransitionScene} + */ + actionWithSize: function (vector) { + if (this._back) + return cc.reverseTime(cc.pageTurn3D(this._duration, vector)); // Get hold of the PageTurn3DAction + else + return cc.pageTurn3D(this._duration, vector); // Get hold of the PageTurn3DAction + }, + + /** + * custom on enter + */ + onEnter: function () { + cc.TransitionScene.prototype.onEnter.call(this); + var winSize = cc.director.getWinSize(); + var x, y; + if (winSize.width > winSize.height) { + x = 16; + y = 12; + } else { + x = 12; + y = 16; + } + + var action = this.actionWithSize(cc.size(x, y)), gridProxy = this._gridProxy; + + if (!this._back) { + gridProxy.setTarget(this._outScene); + gridProxy._performRecursive(cc.Node._stateCallbackType.onEnter); + gridProxy.runAction(cc.sequence(action, cc.callFunc(this.finish, this), cc.stopGrid())); + } else { + gridProxy.setTarget(this._inScene); + gridProxy._performRecursive(cc.Node._stateCallbackType.onEnter); + // to prevent initial flicker + this._inScene.visible = false; + gridProxy.runAction( + cc.sequence(action, cc.callFunc(this.finish, this), cc.stopGrid()) + ); + this._inScene.runAction(cc.show()); + } + }, + + visit: function () { + //cc.TransitionScene.prototype.visit.call(this); + if (this._back) + this._outScene.visit(); + else + this._inScene.visit(); + this._gridProxy.visit(); + }, + + _sceneOrder: function () { + this._isInSceneOnTop = this._back; + } +}); + +/** + * Creates a base transition with duration and incoming scene.
+ * If back is true then the effect is reversed to appear as if the incoming
+ * scene is being turned from left over the outgoing scene. + * @deprecated since v3.0,please use new cc.TransitionPageTurn(t, scene, backwards) instead. + * @param {Number} t time in seconds + * @param {cc.Scene} scene + * @param {Boolean} backwards + * @return {cc.TransitionPageTurn} + */ +cc.TransitionPageTurn.create = function (t, scene, backwards) { + return new cc.TransitionPageTurn(t, scene, backwards); +}; diff --git a/frameworks/cocos2d-html5/cocos2d/transitions/CCTransitionProgress.js b/frameworks/cocos2d-html5/cocos2d/transitions/CCTransitionProgress.js new file mode 100644 index 0000000..38e67e2 --- /dev/null +++ b/frameworks/cocos2d-html5/cocos2d/transitions/CCTransitionProgress.js @@ -0,0 +1,452 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * tag for scene redial + * @constant + * @type Number + */ +cc.SCENE_RADIAL = 0xc001; + +/** + * cc.TransitionProgress transition. + * @class + * @extends cc.TransitionScene + * @param {Number} t time + * @param {cc.Scene} scene + * @example + * var trans = new cc.TransitionProgress(time,scene); + */ +cc.TransitionProgress = cc.TransitionScene.extend(/** @lends cc.TransitionProgress# */{ + _to:0, + _from:0, + _sceneToBeModified:null, + _className:"TransitionProgress", + + /** + * @param {Number} t time + * @param {cc.Scene} scene + */ + ctor:function (t, scene) { + cc.TransitionScene.prototype.ctor.call(this); + scene && this.initWithDuration(t, scene); + }, + + _setAttrs: function(node, x, y) { + node.attr({ + x: x, + y: y, + anchorX: 0.5, + anchorY: 0.5 + }); + }, + + /** + * Custom on enter. + * @override + */ + onEnter:function () { + cc.TransitionScene.prototype.onEnter.call(this); + this._setupTransition(); + + // create a transparent color layer + // in which we are going to add our rendertextures + var winSize = cc.director.getWinSize(); + + // create the second render texture for outScene + var texture = new cc.RenderTexture(winSize.width, winSize.height); + texture.sprite.anchorX = 0.5; + texture.sprite.anchorY = 0.5; + this._setAttrs(texture, winSize.width / 2, winSize.height / 2); + + // render outScene to its texturebuffer + texture.clear(0, 0, 0, 1); + texture.begin(); + this._sceneToBeModified.visit(); + texture.end(); + + // Since we've passed the outScene to the texture we don't need it. + if (this._sceneToBeModified === this._outScene) + this.hideOutShowIn(); + + // We need the texture in RenderTexture. + var pNode = this._progressTimerNodeWithRenderTexture(texture); + + // create the blend action + var layerAction = cc.sequence( + cc.progressFromTo(this._duration, this._from, this._to), + cc.callFunc(this.finish, this)); + // run the blend action + pNode.runAction(layerAction); + + // add the layer (which contains our two rendertextures) to the scene + this.addChild(pNode, 2, cc.SCENE_RADIAL); + }, + + /** + * custom on exit + * @override + */ + onExit:function () { + // remove our layer and release all containing objects + this.removeChildByTag(cc.SCENE_RADIAL, true); + cc.TransitionScene.prototype.onExit.call(this); + }, + + _setupTransition:function () { + this._sceneToBeModified = this._outScene; + this._from = 100; + this._to = 0; + }, + + _progressTimerNodeWithRenderTexture:function (texture) { + cc.log("cc.TransitionProgress._progressTimerNodeWithRenderTexture(): should be overridden in subclass"); + return null; + }, + + _sceneOrder:function () { + this._isInSceneOnTop = false; + } +}); + +/** + * create a cc.TransitionProgress object + * @deprecated since v3.0,please use new cc.TransitionProgress(t, scene) instead. + * @function + * @param {Number} t time + * @param {cc.Scene} scene + * @return {cc.TransitionProgress} + */ +cc.TransitionProgress.create = function (t, scene) { + return new cc.TransitionProgress(t, scene); +}; + +/** + * cc.TransitionRadialCCW transition.
+ * A counter clock-wise radial transition to the next scene + * @class + * @extends cc.TransitionProgress + * @param {Number} t time + * @param {cc.Scene} scene + * @example + * var trans = new cc.TransitionProgressRadialCCW(t, scene); + */ +cc.TransitionProgressRadialCCW = cc.TransitionProgress.extend(/** @lends cc.TransitionProgressRadialCCW# */{ + + /** + * @param {Number} t time + * @param {cc.Scene} scene + */ + ctor:function (t, scene) { + cc.TransitionProgress.prototype.ctor.call(this); + scene && this.initWithDuration(t, scene); + }, + + _progressTimerNodeWithRenderTexture:function (texture) { + var size = cc.director.getWinSize(); + + var pNode = new cc.ProgressTimer(texture.sprite); + // but it is flipped upside down so we flip the sprite + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) + pNode.sprite.flippedY = true; + pNode.type = cc.ProgressTimer.TYPE_RADIAL; + + // Return the radial type that we want to use + pNode.reverseDir = false; + pNode.percentage = 100; + this._setAttrs(pNode, size.width / 2, size.height / 2); + + return pNode; + } +}); + +/** + * create a cc.TransitionProgressRadialCCW object + * @deprecated since v3.0,please use new cc.TransitionProgressRadialCCW(t, scene) instead. + * @param {Number} t time + * @param {cc.Scene} scene + * @return {cc.TransitionProgressRadialCCW} + * @example + * var trans = new cc.TransitionProgressRadialCCW(time,scene); + */ +cc.TransitionProgressRadialCCW.create = function (t, scene) { + return new cc.TransitionProgressRadialCCW(t, scene); +}; + +/** + * cc.TransitionRadialCW transition.
+ * A counter colock-wise radial transition to the next scene + * @class + * @extends cc.TransitionProgress + * @param {Number} t time + * @param {cc.Scene} scene + * @example + * var trans = new cc.TransitionProgressRadialCW(t, scene); + */ +cc.TransitionProgressRadialCW = cc.TransitionProgress.extend(/** @lends cc.TransitionProgressRadialCW# */{ + /** + * @param {Number} t time + * @param {cc.Scene} scene + */ + ctor:function (t, scene) { + cc.TransitionProgress.prototype.ctor.call(this); + scene && this.initWithDuration(t, scene); + }, + + _progressTimerNodeWithRenderTexture:function (texture) { + var size = cc.director.getWinSize(); + + var pNode = new cc.ProgressTimer(texture.sprite); + // but it is flipped upside down so we flip the sprite + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) + pNode.sprite.flippedY = true; + pNode.type = cc.ProgressTimer.TYPE_RADIAL; + + // Return the radial type that we want to use + pNode.reverseDir = true; + pNode.percentage = 100; + this._setAttrs(pNode, size.width / 2, size.height / 2); + + return pNode; + } +}); + +/** + * create a cc.TransitionProgressRadialCW object + * @deprecated since v3.0,please use cc.TransitionProgressRadialCW(t, scene) instead. + * @param {Number} t time + * @param {cc.Scene} scene + * @return {cc.TransitionProgressRadialCW} + */ +cc.TransitionProgressRadialCW.create = function (t, scene) { + var tempScene = new cc.TransitionProgressRadialCW(); + if ((tempScene !== null) && (tempScene.initWithDuration(t, scene))) { + return tempScene; + } + return new cc.TransitionProgressRadialCW(t, scene); +}; + +/** + * cc.TransitionProgressHorizontal transition.
+ * A colock-wise radial transition to the next scene + * @class + * @extends cc.TransitionProgress + * @param {Number} t time + * @param {cc.Scene} scene + * @example + * var trans = new cc.TransitionProgressHorizontal(t, scene); + */ +cc.TransitionProgressHorizontal = cc.TransitionProgress.extend(/** @lends cc.TransitionProgressHorizontal# */{ + /** + * @param {Number} t time + * @param {cc.Scene} scene + */ + ctor:function (t, scene) { + cc.TransitionProgress.prototype.ctor.call(this); + scene && this.initWithDuration(t, scene); + }, + + _progressTimerNodeWithRenderTexture:function (texture) { + var size = cc.director.getWinSize(); + + var pNode = new cc.ProgressTimer(texture.sprite); + // but it is flipped upside down so we flip the sprite + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) + pNode.sprite.flippedY = true; + pNode.type = cc.ProgressTimer.TYPE_BAR; + + pNode.midPoint = cc.p(1, 0); + pNode.barChangeRate = cc.p(1, 0); + + pNode.percentage = 100; + this._setAttrs(pNode, size.width / 2, size.height / 2); + + return pNode; + } +}); + +/** + * create a cc.TransitionProgressHorizontal object + * @deprecated since v3.0,please use new cc.TransitionProgressHorizontal(t, scene) instead. + * @param {Number} t time + * @param {cc.Scene} scene + * @return {cc.TransitionProgressHorizontal} + */ +cc.TransitionProgressHorizontal.create = function (t, scene) { + return new cc.TransitionProgressHorizontal(t, scene); +}; + +/** + * cc.TransitionProgressVertical transition. + * @class + * @extends cc.TransitionProgress + * @param {Number} t time + * @param {cc.Scene} scene + * @example + * var trans = new cc.TransitionProgressVertical(t, scene); + */ +cc.TransitionProgressVertical = cc.TransitionProgress.extend(/** @lends cc.TransitionProgressVertical# */{ + + /** + * @param {Number} t time + * @param {cc.Scene} scene + */ + ctor:function (t, scene) { + cc.TransitionProgress.prototype.ctor.call(this); + scene && this.initWithDuration(t, scene); + }, + + _progressTimerNodeWithRenderTexture:function (texture) { + var size = cc.director.getWinSize(); + + var pNode = new cc.ProgressTimer(texture.sprite); + // but it is flipped upside down so we flip the sprite + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) + pNode.sprite.flippedY = true; + pNode.type = cc.ProgressTimer.TYPE_BAR; + + pNode.midPoint = cc.p(0, 0); + pNode.barChangeRate = cc.p(0, 1); + + pNode.percentage = 100; + this._setAttrs(pNode, size.width / 2, size.height / 2); + + return pNode; + } +}); + +/** + * create a cc.TransitionProgressVertical object + * @deprecated since v3.0,please use new cc.TransitionProgressVertical(t, scene) instead. + * @param {Number} t time + * @param {cc.Scene} scene + * @return {cc.TransitionProgressVertical} + */ +cc.TransitionProgressVertical.create = function (t, scene) { + return new cc.TransitionProgressVertical(t, scene); +}; + +/** + * cc.TransitionProgressInOut transition. + * @class + * @extends cc.TransitionProgress + */ +cc.TransitionProgressInOut = cc.TransitionProgress.extend(/** @lends cc.TransitionProgressInOut# */{ + + /** + * The constructor of cc.TransitionProgressInOut. override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {Number} t time + * @param {cc.Scene} scene + */ + ctor:function (t, scene) { + cc.TransitionProgress.prototype.ctor.call(this); + scene && this.initWithDuration(t, scene); + }, + + _progressTimerNodeWithRenderTexture:function (texture) { + var size = cc.director.getWinSize(); + var pNode = new cc.ProgressTimer(texture.sprite); + // but it is flipped upside down so we flip the sprite + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) + pNode.sprite.flippedY = true; + pNode.type = cc.ProgressTimer.TYPE_BAR; + + pNode.midPoint = cc.p(0.5, 0.5); + pNode.barChangeRate = cc.p(1, 1); + + pNode.percentage = 0; + this._setAttrs(pNode, size.width / 2, size.height / 2); + + return pNode; + }, + _sceneOrder:function () { + this._isInSceneOnTop = false; + }, + _setupTransition:function () { + this._sceneToBeModified = this._inScene; + this._from = 0; + this._to = 100; + } +}); + +/** + * create a cc.TransitionProgressInOut object + * @function + * @deprecated + * @param {Number} t time + * @param {cc.Scene} scene + * @return {cc.TransitionProgressInOut} + */ +cc.TransitionProgressInOut.create = function (t, scene) { + return new cc.TransitionProgressInOut(t, scene); +}; + +/** + * cc.TransitionProgressOutIn transition. + * @class + * @extends cc.TransitionProgress + */ +cc.TransitionProgressOutIn = cc.TransitionProgress.extend(/** @lends cc.TransitionProgressOutIn# */{ + + /** + * The constructor of cc.TransitionProgressOutIn. override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {Number} t time + * @param {cc.Scene} scene + */ + ctor:function (t, scene) { + cc.TransitionProgress.prototype.ctor.call(this); + scene && this.initWithDuration(t, scene); + }, + + _progressTimerNodeWithRenderTexture:function (texture) { + var size = cc.director.getWinSize(); + var pNode = new cc.ProgressTimer(texture.sprite); + // but it is flipped upside down so we flip the sprite + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) + pNode.sprite.flippedY = true; + pNode.type = cc.ProgressTimer.TYPE_BAR; + + pNode.midPoint = cc.p(0.5, 0.5); + pNode.barChangeRate = cc.p(1, 1); + + pNode.percentage = 100; + this._setAttrs(pNode, size.width / 2, size.height / 2); + + return pNode; + } +}); + +/** + * create a cc.TransitionProgressOutIn object + * @function + * @deprecated + * @param {Number} t time + * @param {cc.Scene} scene + * @return {cc.TransitionProgressOutIn} + */ +cc.TransitionProgressOutIn.create = function (t, scene) { + return new cc.TransitionProgressOutIn(t, scene); +}; diff --git a/frameworks/cocos2d-html5/extensions/ccb-reader/CCBAnimationManager.js b/frameworks/cocos2d-html5/extensions/ccb-reader/CCBAnimationManager.js new file mode 100644 index 0000000..54ab341 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccb-reader/CCBAnimationManager.js @@ -0,0 +1,774 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + +var _pos = cc.p(); + +cc.BuilderAnimationManagerDelegate = cc.Class.extend({ + completedAnimationSequenceNamed: function (name) { + } +}); + +cc.BuilderAnimationManager = cc.Class.extend({ + _sequences: null, + _nodeSequences: null, + _baseValues: null, + _autoPlaySequenceId: 0, + + _rootNode: null, + _owner: null, + _rootContainerSize: null, + + _delegate: null, + _runningSequence: null, + + _documentOutletNames: null, + _documentOutletNodes: null, + _documentCallbackNames: null, + _documentCallbackNodes: null, + _documentCallbackControlEvents: null, + _documentControllerName: "", + _lastCompletedSequenceName: "", + _keyframeCallbacks: null, + _keyframeCallFuncs: null, + + _animationCompleteCallbackFunc: null, + _target: null, + _jsControlled: false, + + ctor: function () { + this._rootContainerSize = cc.size(0, 0); + this.init(); + }, + + init: function () { + this._sequences = []; + this._nodeSequences = new cc._Dictionary(); + this._baseValues = new cc._Dictionary(); + + this._documentOutletNames = []; + this._documentOutletNodes = []; + this._documentCallbackNames = []; + this._documentCallbackNodes = []; + this._documentCallbackControlEvents = []; + + this._keyframeCallbacks = []; + this._keyframeCallFuncs = {}; + + return true; + }, + + getSequences: function () { + return this._sequences; + }, + + setSequences: function (seqs) { + this._sequences = seqs; + }, + + getAutoPlaySequenceId: function () { + return this._autoPlaySequenceId; + }, + setAutoPlaySequenceId: function (autoPlaySequenceId) { + this._autoPlaySequenceId = autoPlaySequenceId; + }, + + getRootNode: function () { + return this._rootNode; + }, + setRootNode: function (rootNode) { + this._rootNode = rootNode; + }, + + getOwner: function () { + return this._owner; + }, + setOwner: function (owner) { + this._owner = owner; + }, + + addDocumentCallbackNode: function (node) { + this._documentCallbackNodes.push(node); + }, + + addDocumentCallbackName: function (name) { + this._documentCallbackNames.push(name); + }, + + addDocumentCallbackControlEvents: function (controlEvents) { + this._documentCallbackControlEvents.push(controlEvents); + }, + + addDocumentOutletNode: function (node) { + this._documentOutletNodes.push(node); + }, + + addDocumentOutletName: function (name) { + this._documentOutletNames.push(name); + }, + + setDocumentControllerName: function (name) { + this._documentControllerName = name; + }, + + getDocumentControllerName: function () { + return this._documentControllerName; + }, + + getDocumentCallbackNames: function () { + return this._documentCallbackNames; + }, + + getDocumentCallbackNodes: function () { + return this._documentCallbackNodes; + }, + + getDocumentCallbackControlEvents: function () { + return this._documentCallbackControlEvents; + }, + + getDocumentOutletNames: function () { + return this._documentOutletNames; + }, + + getDocumentOutletNodes: function () { + return this._documentOutletNodes; + }, + + getLastCompletedSequenceName: function () { + return this._lastCompletedSequenceName; + }, + + getKeyframeCallbacks: function () { + return this._keyframeCallbacks; + }, + + getRootContainerSize: function () { + return this._rootContainerSize; + }, + setRootContainerSize: function (rootContainerSize) { + this._rootContainerSize = cc.size(rootContainerSize.width, rootContainerSize.height); + }, + + getDelegate: function () { + return this._delegate; + }, + setDelegate: function (delegate) { + this._delegate = delegate; + }, + + getRunningSequenceName: function () { + if (this._runningSequence) + return this._runningSequence.getName(); + return null; + }, + + getContainerSize: function (node) { + if (node) + return node.getContentSize(); + else + return this._rootContainerSize; + }, + + addNode: function (node, seq) { + this._nodeSequences.setObject(seq, node); + }, + setBaseValue: function (value, node, propName) { + var props = this._baseValues.objectForKey(node); + if (!props) { + props = new cc._Dictionary(); + this._baseValues.setObject(props, node); + } + props.setObject(value, propName); + }, + + moveAnimationsFromNode: function (fromNode, toNode) { + // Move base values + var locBaseValues = this._baseValues; + var baseValue = locBaseValues.objectForKey(fromNode); + if (baseValue !== null) { + locBaseValues.setObject(baseValue, toNode); + locBaseValues.removeObjectForKey(fromNode); + } + + // Move seqs + var locNodeSequences = this._nodeSequences; + var seqs = locNodeSequences.objectForKey(fromNode); + if (seqs != null) { + locNodeSequences.setObject(seqs, toNode); + locNodeSequences.removeObjectForKey(fromNode); + } + }, + + getActionForCallbackChannel: function (channel) { + var lastKeyframeTime = 0; + + var actions = []; + var keyframes = channel.getKeyframes(); + var numKeyframes = keyframes.length; + + for (var i = 0; i < numKeyframes; ++i) { + var keyframe = keyframes[i]; + var timeSinceLastKeyframe = keyframe.getTime() - lastKeyframeTime; + lastKeyframeTime = keyframe.getTime(); + if (timeSinceLastKeyframe > 0) { + actions.push(cc.delayTime(timeSinceLastKeyframe)); + } + + var keyVal = keyframe.getValue(); + var selectorName = keyVal[0]; + var selectorTarget = keyVal[1]; + + if (this._jsControlled) { + var callbackName = selectorTarget + ":" + selectorName; //add number to the stream + var callback = this._keyframeCallFuncs[callbackName]; + + if (callback != null) + actions.push(callback); + } else { + var target; + if (selectorTarget === CCB_TARGETTYPE_DOCUMENTROOT) + target = this._rootNode; + else if (selectorTarget === CCB_TARGETTYPE_OWNER) + target = this._owner; + + if (target != null) { + if (selectorName.length > 0) { + var selCallFunc = 0; + + if (target.onResolveCCBCCCallFuncSelector != null) + selCallFunc = target.onResolveCCBCCCallFuncSelector(target, selectorName); + if (selCallFunc === 0) + cc.log("Skipping selector '" + selectorName + "' since no CCBSelectorResolver is present."); + else + actions.push(cc.callFunc(selCallFunc, target)); + } else { + cc.log("Unexpected empty selector."); + } + } + } + } + if (actions.length < 1) + return null; + + return cc.sequence(actions); + }, + getActionForSoundChannel: function (channel) { + var lastKeyframeTime = 0; + + var actions = []; + var keyframes = channel.getKeyframes(); + var numKeyframes = keyframes.length; + + for (var i = 0; i < numKeyframes; ++i) { + var keyframe = keyframes[i]; + var timeSinceLastKeyframe = keyframe.getTime() - lastKeyframeTime; + lastKeyframeTime = keyframe.getTime(); + if (timeSinceLastKeyframe > 0) { + actions.push(cc.delayTime(timeSinceLastKeyframe)); + } + + var keyVal = keyframe.getValue(); + var soundFile = cc.BuilderReader.getResourcePath() + keyVal[0]; + var pitch = parseFloat(keyVal[1]), pan = parseFloat(keyVal[2]), gain = parseFloat(keyVal[3]); + actions.push(cc.BuilderSoundEffect.create(soundFile, pitch, pan, gain)); + } + + if (actions.length < 1) + return null; + + return cc.sequence(actions); + }, + + runAnimationsForSequenceNamed: function (name) { + this.runAnimationsForSequenceIdTweenDuration(this._getSequenceId(name), 0); + }, + + runAnimationsForSequenceNamedTweenDuration: function (name, tweenDuration) { + this.runAnimationsForSequenceIdTweenDuration(this._getSequenceId(name), tweenDuration); + }, + + runAnimationsForSequenceIdTweenDuration: function (nSeqId, tweenDuration) { + if (nSeqId === -1) + throw new Error("cc.BuilderAnimationManager.runAnimationsForSequenceIdTweenDuration(): Sequence id should not be -1"); + tweenDuration = tweenDuration || 0; + + this._rootNode.stopAllActions(); + + var allKeys = this._nodeSequences.allKeys(); + for (var i = 0, len = allKeys.length; i < len; i++) { + var node = allKeys[i]; + node.stopAllActions(); + + var seqs = this._nodeSequences.objectForKey(node); + var seqNodeProps = seqs.objectForKey(nSeqId); + var j; + var seqNodePropNames = []; + if (seqNodeProps) { + var propKeys = seqNodeProps.allKeys(); + for (j = 0; j < propKeys.length; j++) { + var propName = propKeys[j]; + var seqProp = seqNodeProps.objectForKey(propName); + seqNodePropNames.push(propName); + + this._setFirstFrame(node, seqProp, tweenDuration); + this._runAction(node, seqProp, tweenDuration); + } + } + + var nodeBaseValues = this._baseValues.objectForKey(node); + if (nodeBaseValues) { + var baseKeys = nodeBaseValues.allKeys(); + for (j = 0; j < baseKeys.length; j++) { + var selBaseKey = baseKeys[j]; + if (seqNodePropNames.indexOf(selBaseKey) === -1) { + var value = nodeBaseValues.objectForKey(selBaseKey); + if (value != null) + this._setAnimatedProperty(selBaseKey, node, value, tweenDuration); + } + } + } + } + + // Make callback at end of sequence + var seq = this._getSequence(nSeqId); + var completeAction = cc.sequence(cc.delayTime(seq.getDuration() + tweenDuration), + cc.callFunc(this._sequenceCompleted, this)); + this._rootNode.runAction(completeAction); + + // Playback callbacks and sounds + var action; + if (seq.getCallbackChannel()) { + // Build sound actions for channel + action = this.getActionForCallbackChannel(seq.getCallbackChannel()); + if (action) { + this._rootNode.runAction(action); + } + } + + if (seq.getSoundChannel()) { + // Build sound actions for channel + action = this.getActionForSoundChannel(seq.getSoundChannel()); + if (action) { + this._rootNode.runAction(action); + } + } + // Set the running scene + this._runningSequence = this._getSequence(nSeqId); + }, + + runAnimations: function (name, tweenDuration) { + tweenDuration = tweenDuration || 0; + var nSeqId; + if (cc.isString(name)) + nSeqId = this._getSequenceId(name); + else + nSeqId = name; + + this.runAnimationsForSequenceIdTweenDuration(nSeqId, tweenDuration); + }, + + setAnimationCompletedCallback: function (target, callbackFunc) { + this._target = target; + this._animationCompleteCallbackFunc = callbackFunc; + }, + + setCompletedAnimationCallback: function (target, callbackFunc) { + this.setAnimationCompletedCallback(target, callbackFunc); + }, + setCallFunc: function (callFunc, callbackNamed) { + this._keyframeCallFuncs[callbackNamed] = callFunc; + }, + + debug: function () { + }, + + _getBaseValue: function (node, propName) { + var props = this._baseValues.objectForKey(node); + if (props) + return props.objectForKey(propName); + return null; + }, + + _getSequenceId: function (sequenceName) { + var element = null; + var locSequences = this._sequences; + for (var i = 0, len = locSequences.length; i < len; i++) { + element = locSequences[i]; + if (element && element.getName() === sequenceName) + return element.getSequenceId(); + } + return -1; + }, + + _getSequence: function (sequenceId) { + var element = null; + var locSequences = this._sequences; + for (var i = 0, len = locSequences.length; i < len; i++) { + element = locSequences[i]; + if (element && element.getSequenceId() === sequenceId) + return element; + } + return null; + }, + + _getAction: function (keyframe0, keyframe1, propName, node) { + var duration = keyframe1.getTime() - (keyframe0 ? keyframe0.getTime() : 0); + var getArr, type, getValueArr, x, y; + + if (propName === "rotation") { + return cc.BuilderRotateTo.create(duration, keyframe1.getValue()); + } else if (propName === "rotationX") { + return cc.BuilderRotateXTo.create(duration, keyframe1.getValue()); + } else if (propName === "rotationY") { + return cc.BuilderRotateYTo.create(duration, keyframe1.getValue()); + } else if (propName === "opacity") { + return cc.fadeTo(duration, keyframe1.getValue()); + } else if (propName === "color") { + var selColor = keyframe1.getValue(); + return cc.tintTo(duration, selColor.r, selColor.g, selColor.b); + } else if (propName === "visible") { + var isVisible = keyframe1.getValue(); + if (isVisible) { + return cc.sequence(cc.delayTime(duration), cc.show()); + } else { + return cc.sequence(cc.delayTime(duration), cc.hide()); + } + } else if (propName === "displayFrame") { + return cc.sequence(cc.delayTime(duration), cc.BuilderSetSpriteFrame.create(keyframe1.getValue())); + } else if (propName === "position") { + getArr = this._getBaseValue(node, propName); + type = getArr[2]; + + //get relative position + getValueArr = keyframe1.getValue(); + x = getValueArr[0]; + y = getValueArr[1]; + + var containerSize = this.getContainerSize(node.getParent()); + + var absPos = cc.getAbsolutePosition(x, y, type, containerSize, propName); + + return cc.moveTo(duration, absPos); + } else if (propName === "scale") { + getArr = this._getBaseValue(node, propName); + type = getArr[2]; + + //get relative position + getValueArr = keyframe1.getValue(); + x = getValueArr[0]; + y = getValueArr[1]; + + if (type === CCB_SCALETYPE_MULTIPLY_RESOLUTION) { + //TODO need to test + var resolutionScale = cc.BuilderReader.getResolutionScale(); + x *= resolutionScale; + y *= resolutionScale; + } + + return cc.scaleTo(duration, x, y); + } else if (propName === "skew") { + //get relative position + getValueArr = keyframe1.getValue(); + x = getValueArr[0]; + y = getValueArr[1]; + return cc.skewTo(duration, x, y); + } else { + cc.log("BuilderReader: Failed to create animation for property: " + propName); + } + return null; + }, + + _setAnimatedProperty: function (propName, node, value, tweenDuration) { + if (tweenDuration > 0) { + // Create a fake keyframe to generate the action from + var kf1 = new cc.BuilderKeyframe(); + kf1.setValue(value); + kf1.setTime(tweenDuration); + kf1.setEasingType(CCB_KEYFRAME_EASING_LINEAR); + + // Animate + var tweenAction = this._getAction(null, kf1, propName, node); + node.runAction(tweenAction); + } else { + // Just set the value + var getArr, nType, x, y; + if (propName === "position") { + getArr = this._getBaseValue(node, propName); + nType = getArr[2]; + + x = value[0]; + y = value[1]; + cc.getAbsolutePosition(x, y, nType, this.getContainerSize(node.getParent()), propName, _pos); + node._position.x = _pos.x; + node._position.y = _pos.y; + } else if (propName === "scale") { + getArr = this._getBaseValue(node, propName); + nType = getArr[2]; + + x = value[0]; + y = value[1]; + + cc.setRelativeScale(node, x, y, nType, propName); + } else if (propName === "skew") { + x = value[0]; + y = value[1]; + node._skewX = x; + node._skewY = y; + } else { + // [node setValue:value forKey:name]; + // TODO only handle rotation, opacity, displayFrame, color + if (propName === "rotation") { + node.setRotation(value); + } else if (propName === "rotationX") { + node._rotationX = value; + } else if (propName === "rotationY") { + node._rotationY = value; + } else if (propName === "opacity") { + node._realOpacity = value; + } else if (propName === "displayFrame") { + node.setSpriteFrame(value); + } else if (propName === "color") { + if (value.r !== 255 || value.g !== 255 || value.b !== 255) { + node.setColor(value); + } + } else if (propName === "visible") { + value = value || false; + node.setVisible(value); + } else { + cc.log("unsupported property name is " + propName); + return; + } + } + node.setNodeDirty(); + } + }, + + _setFirstFrame: function (node, seqProp, tweenDuration) { + var keyframes = seqProp.getKeyframes(); + + if (keyframes.length === 0) { + // Use base value (no animation) + var baseValue = this._getBaseValue(node, seqProp.getName()); + if (!baseValue) + cc.log("cc.BuilderAnimationManager._setFirstFrame(): No baseValue found for property"); + this._setAnimatedProperty(seqProp.getName(), node, baseValue, tweenDuration); + } else { + // Use first keyframe + var keyframe = keyframes[0]; + this._setAnimatedProperty(seqProp.getName(), node, keyframe.getValue(), tweenDuration); + } + }, + + _getEaseAction: function (action, easingType, easingOpt) { + if (easingType === CCB_KEYFRAME_EASING_LINEAR || easingType === CCB_KEYFRAME_EASING_INSTANT) { + return action; + } else if (easingType === CCB_KEYFRAME_EASING_CUBIC_IN) { + return action.easing(cc.easeIn(easingOpt)); + } else if (easingType === CCB_KEYFRAME_EASING_CUBIC_OUT) { + return action.easing(cc.easeOut(easingOpt)); + } else if (easingType === CCB_KEYFRAME_EASING_CUBIC_INOUT) { + return action.easing(cc.easeInOut(easingOpt)); + } else if (easingType === CCB_KEYFRAME_EASING_BACK_IN) { + return action.easing(cc.easeBackIn()); + } else if (easingType === CCB_KEYFRAME_EASING_BACK_OUT) { + return action.easing(cc.easeBackOut()); + } else if (easingType === CCB_KEYFRAME_EASING_BACK_INOUT) { + return action.easing(cc.easeBackInOut()); + } else if (easingType === CCB_KEYFRAME_EASING_BOUNCE_IN) { + return action.easing(cc.easeBounceIn()); + } else if (easingType === CCB_KEYFRAME_EASING_BOUNCE_OUT) { + return action.easing(cc.easeBounceOut()); + } else if (easingType === CCB_KEYFRAME_EASING_BOUNCE_INOUT) { + return action.easing(cc.easeBounceInOut()); + } else if (easingType === CCB_KEYFRAME_EASING_ELASTIC_IN) { + return action.easing(cc.easeElasticIn(easingOpt)); + } else if (easingType === CCB_KEYFRAME_EASING_ELASTIC_OUT) { + return action.easing(cc.easeElasticOut(easingOpt)); + } else if (easingType === CCB_KEYFRAME_EASING_ELASTIC_INOUT) { + return action.easing(cc.easeElasticInOut(easingOpt)); + } else { + cc.log("BuilderReader: Unknown easing type " + easingType); + return action; + } + }, + + _runAction: function (node, seqProp, tweenDuration) { + var keyframes = seqProp.getKeyframes(); + var numKeyframes = keyframes.length; + + if (numKeyframes > 1) { + // Make an animation! + var actions = []; + + var keyframeFirst = keyframes[0]; + var timeFirst = keyframeFirst.getTime() + tweenDuration; + + if (timeFirst > 0) { + actions.push(cc.delayTime(timeFirst)); + } + + for (var i = 0; i < numKeyframes - 1; ++i) { + var kf0 = keyframes[i]; + var kf1 = keyframes[(i + 1)]; + + var action = this._getAction(kf0, kf1, seqProp.getName(), node); + if (action) { + // Apply easing + action = this._getEaseAction(action, kf0.getEasingType(), kf0.getEasingOpt()); + actions.push(action); + } + } + + node.runAction(cc.sequence(actions)); + } + }, + + _sequenceCompleted: function () { + var locRunningSequence = this._runningSequence; + + var locRunningName = locRunningSequence.getName(); + + if (this._lastCompletedSequenceName != locRunningSequence.getName()) { + this._lastCompletedSequenceName = locRunningSequence.getName(); + } + + var nextSeqId = locRunningSequence.getChainedSequenceId(); + this._runningSequence = null; + + if (nextSeqId !== -1) + this.runAnimations(nextSeqId, 0); + + if (this._delegate) + this._delegate.completedAnimationSequenceNamed(locRunningName); + + if (this._target && this._animationCompleteCallbackFunc) { + this._animationCompleteCallbackFunc.call(this._target); + } + } +}); + + +cc.BuilderSetSpriteFrame = cc.ActionInstant.extend({ + _spriteFrame: null, + + initWithSpriteFrame: function (spriteFrame) { + this._spriteFrame = spriteFrame; + return true; + }, + update: function (time) { + this.target.setSpriteFrame(this._spriteFrame); + } +}); + +cc.BuilderSetSpriteFrame.create = function (spriteFrame) { + var ret = new cc.BuilderSetSpriteFrame(); + if (ret) { + if (ret.initWithSpriteFrame(spriteFrame)) + return ret; + } + return null; +}; + +// +// cc.BuilderRotateTo +// +cc.BuilderRotateTo = cc.ActionInterval.extend({ + _startAngle: 0, + _dstAngle: 0, + _diffAngle: 0, + + initWithDuration: function (duration, angle) { + if (cc.ActionInterval.prototype.initWithDuration.call(this, duration)) { + this._dstAngle = angle; + return true; + } else { + return false; + } + }, + update: function (time) { + this.target.setRotation(this._startAngle + (this._diffAngle * time)); + }, + + startWithTarget: function (node) { + cc.ActionInterval.prototype.startWithTarget.call(this, node); + this._startAngle = this.target.getRotation(); + this._diffAngle = this._dstAngle - this._startAngle; + } +}); + +cc.BuilderRotateTo.create = function (duration, angle) { + var ret = new cc.BuilderRotateTo(); + if (ret) { + if (ret.initWithDuration(duration, angle)) + return ret; + } + return null; +}; + +// +// cc.BuilderRotateXTo +// +cc.BuilderRotateXTo = cc.ActionInterval.extend({ + // TODO: rotationX is not implemented in HTML5 +}); + +cc.BuilderRotateXTo.create = function (duration, angle) { + throw new Error("rotationX has not been implemented in cocos2d-html5"); +}; + +// +// cc.BuilderRotateYTo +// +cc.BuilderRotateYTo = cc.ActionInterval.extend({ + // TODO: rotationX is not implemented in HTML5 +}); + +cc.BuilderRotateYTo.create = function (duration, angle) { + throw new Error("rotationY has not been implemented in cocos2d-html5"); +}; + +// +// cc.BuilderSoundEffect +// +cc.BuilderSoundEffect = cc.ActionInstant.extend({ + init: function (file) { + this._file = file; + return true; + }, + update: function (dt) { + cc.audioEngine.playEffect(this._file); + } +}); +cc.BuilderSoundEffect.create = function (file, pitch, pan, gain) { + var ret = new cc.BuilderSoundEffect(); + if (ret && ret.init(file)) { + return ret; + } + return null; +}; +})(); diff --git a/frameworks/cocos2d-html5/extensions/ccb-reader/CCBKeyframe.js b/frameworks/cocos2d-html5/extensions/ccb-reader/CCBKeyframe.js new file mode 100644 index 0000000..c3eb7af --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccb-reader/CCBKeyframe.js @@ -0,0 +1,60 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +cc.BuilderKeyframe = cc.Class.extend({ + _value:null, + _time:0, + _easingType:0, + _easingOpt:0, + + getValue:function(){ + return this._value; + }, + setValue:function(value){ + this._value = value; + }, + + getTime:function(){ + return this._time; + }, + setTime:function(time){ + this._time = time; + }, + + getEasingType:function(){ + return this._easingType; + }, + setEasingType:function(easingType){ + this._easingType = easingType; + }, + + getEasingOpt:function(){ + return this._easingOpt; + }, + setEasingOpt:function(easingOpt){ + this._easingOpt = easingOpt; + } +}); diff --git a/frameworks/cocos2d-html5/extensions/ccb-reader/CCBReader.js b/frameworks/cocos2d-html5/extensions/ccb-reader/CCBReader.js new file mode 100644 index 0000000..0baa27b --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccb-reader/CCBReader.js @@ -0,0 +1,1105 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +var CCB_VERSION = 5; + +var CCB_PROPTYPE_POSITION = 0; +var CCB_PROPTYPE_SIZE = 1; +var CCB_PROPTYPE_POINT = 2; +var CCB_PROPTYPE_POINTLOCK = 3; +var CCB_PROPTYPE_SCALELOCK = 4; +var CCB_PROPTYPE_DEGREES = 5; +var CCB_PROPTYPE_INTEGER = 6; +var CCB_PROPTYPE_FLOAT = 7; +var CCB_PROPTYPE_FLOATVAR = 8; +var CCB_PROPTYPE_CHECK = 9; +var CCB_PROPTYPE_SPRITEFRAME = 10; +var CCB_PROPTYPE_TEXTURE = 11; +var CCB_PROPTYPE_BYTE = 12; +var CCB_PROPTYPE_COLOR3 = 13; +var CCB_PROPTYPE_COLOR4VAR = 14; +var CCB_PROPTYPE_FLIP = 15; +var CCB_PROPTYPE_BLENDMODE = 16; +var CCB_PROPTYPE_FNTFILE = 17; +var CCB_PROPTYPE_TEXT = 18; +var CCB_PROPTYPE_FONTTTF = 19; +var CCB_PROPTYPE_INTEGERLABELED = 20; +var CCB_PROPTYPE_BLOCK = 21; +var CCB_PROPTYPE_ANIMATION = 22; +var CCB_PROPTYPE_CCBFILE = 23; +var CCB_PROPTYPE_STRING = 24; +var CCB_PROPTYPE_BLOCKCCCONTROL = 25; +var CCB_PROPTYPE_FLOATSCALE = 26; +var CCB_PROPTYPE_FLOATXY = 27; + +var CCB_FLOAT0 = 0; +var CCB_FLOAT1 = 1; +var CCB_FLOAT_MINUS1 = 2; +var CCB_FLOAT05 = 3; +var CCB_FLOAT_INTEGER = 4; +var CCB_FLOAT_FULL = 5; + +var CCB_PLATFORM_ALL = 0; +var CCB_PLATFORM_IOS = 1; +var CCB_PLATFORM_MAC = 2; + +var CCB_TARGETTYPE_NONE = 0; +var CCB_TARGETTYPE_DOCUMENTROOT = 1; +var CCB_TARGETTYPE_OWNER = 2; + +var CCB_KEYFRAME_EASING_INSTANT = 0; +var CCB_KEYFRAME_EASING_LINEAR = 1; +var CCB_KEYFRAME_EASING_CUBIC_IN = 2; +var CCB_KEYFRAME_EASING_CUBIC_OUT = 3; +var CCB_KEYFRAME_EASING_CUBIC_INOUT = 4; +var CCB_KEYFRAME_EASING_ELASTIC_IN = 5; +var CCB_KEYFRAME_EASING_ELASTIC_OUT = 6; +var CCB_KEYFRAME_EASING_ELASTIC_INOUT = 7; +var CCB_KEYFRAME_EASING_BOUNCE_IN = 8; +var CCB_KEYFRAME_EASING_BOUNCE_OUT = 9; +var CCB_KEYFRAME_EASING_BOUNCE_INOUT = 10; +var CCB_KEYFRAME_EASING_BACK_IN = 11; +var CCB_KEYFRAME_EASING_BACK_OUT = 12; +var CCB_KEYFRAME_EASING_BACK_INOUT = 13; + +var CCB_POSITIONTYPE_RELATIVE_BOTTOM_LEFT = 0; +var CCB_POSITIONTYPE_RELATIVE_TOP_LEFT = 1; +var CCB_POSITIONTYPE_RELATIVE_TOP_RIGHT = 2; +var CCB_POSITIONTYPE_RELATIVE_BOTTOM_RIGHT = 3; +var CCB_POSITIONTYPE_PERCENT = 4; +var CCB_POSITIONTYPE_MULTIPLY_RESOLUTION = 5; + +var CCB_SIZETYPE_ABSOLUTE = 0; +var CCB_SIZETYPE_PERCENT = 1; +var CCB_SIZETYPE_RELATIVE_CONTAINER = 2; +var CCB_SIZETYPE_HORIZONTAL_PERCENT = 3; +var CCB_SIZETYPE_VERTICAL_PERCENT = 4; +var CCB_SIZETYPE_MULTIPLY_RESOLUTION = 5; + +var CCB_SCALETYPE_ABSOLUTE = 0; +var CCB_SCALETYPE_MULTIPLY_RESOLUTION = 1; + +/** + * Parse CCBI file which is generated by CocosBuilder + */ +cc.BuilderReader = cc.Class.extend({ + _jsControlled: false, + _data: null, + _ccbRootPath: "", + + _bytes: 0, + _currentByte: 0, + _currentBit: 0, + + _stringCache: null, + _loadedSpriteSheets: null, + + _owner: null, + _animationManager: null, + _animationManagers: null, + _animatedProps: null, + + _ccNodeLoaderLibrary: null, + _ccNodeLoaderListener: null, + _ccbMemberVariableAssigner: null, + _ccbSelectorResolver: null, + + _ownerOutletNames: null, + _ownerOutletNodes: null, + _nodesWithAnimationManagers: null, + _animationManagerForNodes: null, + + _ownerCallbackNames: null, + _ownerCallbackNodes: null, + _ownerCallbackEvents: null, + + _readNodeGraphFromData: false, + + ctor: function (ccNodeLoaderLibrary, ccbMemberVariableAssigner, ccbSelectorResolver, ccNodeLoaderListener) { + this._stringCache = []; + this._loadedSpriteSheets = []; + this._currentBit = -1; + this._currentByte = -1; + + if (arguments.length !== 0) { + if (ccNodeLoaderLibrary instanceof cc.BuilderReader) { + var ccbReader = ccNodeLoaderLibrary; + + /* Borrow data from the 'parent' CCBReader. */ + this._loadedSpriteSheets = ccbReader._loadedSpriteSheets; + this._ccNodeLoaderLibrary = ccbReader._ccNodeLoaderLibrary; + + this._ccbMemberVariableAssigner = ccbReader._ccbMemberVariableAssigner; + this._ccbSelectorResolver = ccbReader._ccbSelectorResolver; + this._ccNodeLoaderListener = ccbReader._ccNodeLoaderListener; + + this._ownerCallbackNames = ccbReader._ownerCallbackNames; + this._ownerCallbackNodes = ccbReader._ownerCallbackNodes; + this._ownerCallbackEvents = ccbReader._ownerCallbackEvents; + this._ownerOutletNames = ccbReader._ownerOutletNames; + this._ownerOutletNodes = ccbReader._ownerOutletNodes; + this._ccbRootPath = ccbReader._ccbRootPath; + } else { + this._ccNodeLoaderLibrary = ccNodeLoaderLibrary; + this._ccbMemberVariableAssigner = ccbMemberVariableAssigner; + this._ccbSelectorResolver = ccbSelectorResolver; + this._ccNodeLoaderListener = ccNodeLoaderListener; + } + } + }, + + getCCBRootPath: function () { + return this._ccbRootPath; + }, + + setCCBRootPath: function (rootPath) { + this._ccbRootPath = rootPath; + }, + + initWithData: function (data, owner) { + //setup action manager + this._animationManager = new cc.BuilderAnimationManager(); + + //setup byte array + //Array replace to CCData in Javascript + this._data = data; + this._bytes = data.length; + this._currentBit = 0; + this._currentByte = 0; + + this._owner = owner; + + //setup resolution scale and container size + this._animationManager.setRootContainerSize(cc.director.getWinSize()); + + return true; + }, + + _loadBinarySync: function (url) { + var self = this; + var req = this.getXMLHttpRequest(); + var errInfo = "load " + url + " failed!"; + req.open('GET', url, false); + var arrayInfo = null; + if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) { + req.setRequestHeader("Accept-Charset", "x-user-defined"); + req.send(null); + if (req.status !== 200) { + cc.log(errInfo); + return null; + } + + var fileContents = cc._convertResponseBodyToText(req["responseBody"]); + if (fileContents) { + arrayInfo = this._stringConvertToArray(fileContents); + this._fileDataCache[url] = arrayInfo; + } + } else { + if (req.overrideMimeType) + req.overrideMimeType('text\/plain; charset=x-user-defined'); + req.send(null); + if (req.status !== 200) { + cc.log(errInfo); + return null; + } + + arrayInfo = this._stringConvertToArray(req.responseText); + this._fileDataCache[url] = arrayInfo; + } + return arrayInfo; + }, + + readNodeGraphFromFile: function (ccbFileName, owner, parentSize, animationManager) { + if (parentSize == null) { + parentSize = cc.director.getWinSize(); + } else if (parentSize instanceof cc.BuilderAnimationManager) { + animationManager = parentSize; + parentSize = cc.director.getWinSize(); + } + + var data = cc.loader.getRes(ccbFileName); + if (!data) { + var realUrl = cc.loader.getUrl(ccbFileName); + data = cc.loader.loadBinarySync(realUrl); + cc.loader.cache[ccbFileName] = data; + } + + return this.readNodeGraphFromData(data, owner, parentSize, animationManager); + }, + + readNodeGraphFromData: function (data, owner, parentSize) { + this.initWithData(data, owner); + var locAnimationManager = this._animationManager; + locAnimationManager.setRootContainerSize(parentSize); + locAnimationManager.setOwner(owner); + + this._ownerOutletNames = []; + this._ownerOutletNodes = []; + this._ownerCallbackNames = []; + this._ownerCallbackNodes = []; + this._ownerCallbackEvents = []; + this._animationManagers = new cc._Dictionary(); + + var nodeGraph = this.readFileWithCleanUp(true); + + if (nodeGraph && locAnimationManager.getAutoPlaySequenceId() !== -1) { + //auto play animations + locAnimationManager.runAnimations(locAnimationManager.getAutoPlaySequenceId(), 0); + } + + if (this._jsControlled) { + var locNodes = []; + var locAnimations = []; + + var locAnimationManagers = this._animationManagers; + var getAllKeys = locAnimationManagers.allKeys(); + for (var i = 0; i < getAllKeys.length; i++) { + locNodes.push(getAllKeys[i]); + locAnimations.push(locAnimationManagers.objectForKey(getAllKeys[i])); + } + + this._nodesWithAnimationManagers = locNodes; + this._animationManagerForNodes = locAnimations; + } + + return nodeGraph; + }, + + createSceneWithNodeGraphFromFile: function (ccbFileName, owner, parentSize, animationManager) { + var node = this.readNodeGraphFromFile(ccbFileName, owner, parentSize, animationManager); + var scene = new cc.Scene(); + scene.addChild(node); + return scene; + }, + + getCCBMemberVariableAssigner: function () { + return this._ccbMemberVariableAssigner; + }, + + getCCBSelectorResolver: function () { + return this._ccbSelectorResolver; + }, + + getAnimationManager: function () { + return this._animationManager; + }, + + setAnimationManager: function (animationManager) { + this._animationManager = animationManager; + }, + + getAnimatedProperties: function () { + return this._animatedProps; + }, + + getLoadedSpriteSheet: function () { + return this._loadedSpriteSheets; + }, + + getOwner: function () { + return this._owner; + }, + + readInt: function (signed) { + var numBits = 0; + var data = this._data[this._currentByte]; + var bit = !!(data & (1 << this._currentBit++)); + while (!bit) { + numBits++; + bit = !!(data & (1 << this._currentBit++)); + if (this._currentBit >= 8) { + this._currentBit = 0; + this._currentByte++; + data = this._data[this._currentByte]; + if (this._currentByte > this._data.length) + throw new Error("out of the data bound"); + } + } + + var current = 0; + for (var a = numBits - 1; a >= 0; a--) { + bit = !!(data & (1 << this._currentBit++)); + if (this._currentBit >= 8) { + this._currentBit = 0; + this._currentByte++; + data = this._data[this._currentByte]; + if (this._currentByte > this._data.length) + throw new Error("out of the data bound"); + } + if (bit) { + current |= 1 << a; + } + } + current |= 1 << numBits; + + var num; + if (signed) { + var s = current % 2; + if (s) { + num = 0 | (current / 2); + } else { + num = 0 | (-current / 2); + } + } else { + num = current - 1; + } + + if (this._currentBit) { + this._currentBit = 0; + this._currentByte++; + } + + return num; + }, + + readByte: function () { + var byteValue = this._data[this._currentByte]; + this._currentByte++; + return byteValue; + }, + + readBool: function () { + return !!this._data[this._currentByte++]; + }, + + readFloat: function () { + var type = this._data[this._currentByte++]; + + switch (type) { + case CCB_FLOAT0: + return 0; + case CCB_FLOAT1: + return 1; + case CCB_FLOAT_MINUS1: + return -1; + case CCB_FLOAT05: + return 0.5; + case CCB_FLOAT_INTEGER: + return this.readInt(true); + default: + /* using a memcpy since the compiler isn't + * doing the float ptr math correctly on device. + */ + var pF = this._decodeFloat(23, 8); //this._bytes + this._currentByte; + //this._currentByte += 4; + return pF; + } + }, + + _decodeFloat: function (precisionBits, exponentBits) { + var length = precisionBits + exponentBits + 1; + var size = length >> 3; + if (this._currentByte + size >= this._data.length) { + throw new Error("Index out of bound"); + } + + var bias = Math.pow(2, exponentBits - 1) - 1; + var signal = this._readBitsOnly(precisionBits + exponentBits, 1, size); + var exponent = this._readBitsOnly(precisionBits, exponentBits, size); + var significand = 0; + var divisor = 2; + var curByte = 0; //length + (-precisionBits >> 3) - 1; + do { + var byteValue = this._data[this._currentByte + size - (++curByte) - 1]; + var startBit = precisionBits % 8 || 8; + var mask = 1 << startBit; + while (mask >>= 1) { + if (byteValue & mask) { + significand += 1 / divisor; + } + divisor *= 2; + } + } while (precisionBits -= startBit); + + this._currentByte += size; + + return exponent === (bias << 1) + 1 ? significand ? NaN : signal ? -Infinity : +Infinity + : (1 + signal * -2) * (exponent || significand ? !exponent ? Math.pow(2, -bias + 1) * significand + : Math.pow(2, exponent - bias) * (1 + significand) : 0); + }, + + _readBitsOnly: function (start, length, size) { + var offsetLeft = (start + length) % 8; + var offsetRight = start % 8; + var curByte = size - (start >> 3) - 1; + var lastByte = size + (-(start + length) >> 3); + var diff = curByte - lastByte; + + var sum = (this._data[this._currentByte + size - curByte - 1] >> offsetRight) & ((1 << (diff ? 8 - offsetRight : length)) - 1); + + if (diff && offsetLeft) { + sum += (this._data[this._currentByte + size - lastByte - 1] & ((1 << offsetLeft) - 1)) << (diff-- << 3) - offsetRight; + lastByte++; + } + + while (diff) { + sum += this._shl(this._data[this._currentByte + size - lastByte - 1], (diff-- << 3) - offsetRight); + lastByte++; + } + + return sum; + }, + + _shl: function (a, b) { + for (++b; --b; a = ((a %= 0x7fffffff + 1) & 0x40000000) === 0x40000000 ? a * 2 : (a - 0x40000000) * 2 + 0x7fffffff + 1); + return a; + }, + + readCachedString: function () { + return this._stringCache[this.readInt(false)]; + }, + + isJSControlled: function () { + return this._jsControlled; + }, + + getOwnerCallbackNames: function () { + return this._ownerCallbackNames; + }, + + getOwnerCallbackNodes: function () { + return this._ownerCallbackNodes; + }, + + getOwnerCallbackControlEvents: function () { + return this._ownerCallbackEvents; + }, + + getOwnerOutletNames: function () { + return this._ownerOutletNames; + }, + + getOwnerOutletNodes: function () { + return this._ownerOutletNodes; + }, + + getNodesWithAnimationManagers: function () { + return this._nodesWithAnimationManagers; + }, + + getAnimationManagersForNodes: function () { + return this._animationManagerForNodes; + }, + + getAnimationManagers: function () { + return this._animationManagers; + }, + + setAnimationManagers: function (animationManagers) { + this._animationManagers = animationManagers; + }, + + addOwnerCallbackName: function (name) { + this._ownerCallbackNames.push(name); + }, + + addOwnerCallbackNode: function (node) { + this._ownerCallbackNodes.push(node); + }, + + addOwnerCallbackControlEvents: function (event) { + this._ownerCallbackEvents.push(event); + }, + + addDocumentCallbackName: function (name) { + this._animationManager.addDocumentCallbackName(name); + }, + + addDocumentCallbackNode: function (node) { + this._animationManager.addDocumentCallbackNode(node); + }, + + addDocumentCallbackControlEvents: function (controlEvents) { + this._animationManager.addDocumentCallbackControlEvents(controlEvents); + }, + + readFileWithCleanUp: function (cleanUp) { + if (!this._readHeader()) + return null; + if (!this._readStringCache()) + return null; + if (!this._readSequences()) + return null; + + var node = this._readNodeGraph(); + this._animationManagers.setObject(this._animationManager, node); + + if (cleanUp) + this._cleanUpNodeGraph(node); + return node; + }, + + addOwnerOutletName: function (name) { + this._ownerOutletNames.push(name); + }, + + addOwnerOutletNode: function (node) { + if (node == null) + return; + + this._ownerOutletNodes.push(node); + }, + + _cleanUpNodeGraph: function (node) { + node.userObject = null; + var getChildren = node.getChildren(); + for (var i = 0, len = getChildren.length; i < len; i++) { + this._cleanUpNodeGraph(getChildren[i]); + } + }, + + _readCallbackKeyframesForSeq: function (seq) { + var numKeyframes = this.readInt(false); + + if (!numKeyframes) + return true; + + var channel = new cc.BuilderSequenceProperty(); + var locJsControlled = this._jsControlled, locAnimationManager = this._animationManager, locKeyframes = channel.getKeyframes(); + for (var i = 0; i < numKeyframes; i++) { + var time = this.readFloat(); + var callbackName = this.readCachedString(); + var callbackType = this.readInt(false); + + var value = [callbackName, callbackType]; + + var keyframe = new cc.BuilderKeyframe(); + keyframe.setTime(time); + keyframe.setValue(value); + + if (locJsControlled) + locAnimationManager.getKeyframeCallbacks().push(callbackType + ":" + callbackName); + + locKeyframes.push(keyframe); + } + + // Assign to sequence + seq.setCallbackChannel(channel); + + return true; + }, + + _readSoundKeyframesForSeq: function (seq) { + var numKeyframes = this.readInt(false); + + if (!numKeyframes) + return true; + + var channel = new cc.BuilderSequenceProperty(); + var locKeyframes = channel.getKeyframes(); + for (var i = 0; i < numKeyframes; i++) { + var time = this.readFloat(); + var soundFile = this.readCachedString(); + var pitch = this.readFloat(); + var pan = this.readFloat(); + var gain = this.readFloat(); + + var value = [soundFile, pitch, pan, gain]; + var keyframe = new cc.BuilderKeyframe(); + keyframe.setTime(time); + keyframe.setValue(value); + + locKeyframes.push(keyframe); + } + + // Assign to sequence + seq.setSoundChannel(channel); + return true; + }, + _readSequences: function () { + var sequences = this._animationManager.getSequences(); + var numSeqs = this.readInt(false); + for (var i = 0; i < numSeqs; i++) { + var seq = new cc.BuilderSequence(); + seq.setDuration(this.readFloat()); + seq.setName(this.readCachedString()); + seq.setSequenceId(this.readInt(false)); + seq.setChainedSequenceId(this.readInt(true)); + + if (!this._readCallbackKeyframesForSeq(seq)) + return false; + if (!this._readSoundKeyframesForSeq(seq)) + return false; + + sequences.push(seq); + } + this._animationManager.setAutoPlaySequenceId(this.readInt(true)); + return true; + }, + + readKeyframe: function (type) { + var keyframe = new cc.BuilderKeyframe(); + keyframe.setTime(this.readFloat()); + var easingType = this.readInt(false); + var easingOpt = 0; + var value = null; + + if (easingType === CCB_KEYFRAME_EASING_CUBIC_IN + || easingType === CCB_KEYFRAME_EASING_CUBIC_OUT + || easingType === CCB_KEYFRAME_EASING_CUBIC_INOUT + || easingType === CCB_KEYFRAME_EASING_ELASTIC_IN + || easingType === CCB_KEYFRAME_EASING_ELASTIC_OUT + || easingType === CCB_KEYFRAME_EASING_ELASTIC_INOUT) { + easingOpt = this.readFloat(); + } + + keyframe.setEasingType(easingType); + keyframe.setEasingOpt(easingOpt); + + if (type === CCB_PROPTYPE_CHECK) { + value = !!this._data[this._currentByte++]; + } else if (type === CCB_PROPTYPE_BYTE) { + value = this._data[this._currentByte++]; + } else if (type === CCB_PROPTYPE_COLOR3) { + value = cc.color(this._data[this._currentByte++], this._data[this._currentByte++], this._data[this._currentByte++]); + } else if (type === CCB_PROPTYPE_FLOATXY) { + value = [this.readFloat(), this.readFloat()]; + } else if (type === CCB_PROPTYPE_DEGREES) { + value = this.readFloat(); + } else if (type === CCB_PROPTYPE_SCALELOCK || type === CCB_PROPTYPE_POSITION || type === CCB_PROPTYPE_FLOATXY) { + value = [this.readFloat(), this.readFloat()]; + } else if (type === CCB_PROPTYPE_SPRITEFRAME) { + var spriteSheet = this.readCachedString(); + var spriteFile = this.readCachedString(); + + if (spriteSheet === "") { + spriteFile = this._ccbRootPath + spriteFile; + var texture = cc.textureCache.addImage(spriteFile); + var locContentSize = texture.getContentSize(); + var bounds = cc.rect(0, 0, locContentSize.width, locContentSize.height); + value = new cc.SpriteFrame(texture, bounds); + } else { + spriteSheet = this._ccbRootPath + spriteSheet; + var frameCache = cc.spriteFrameCache; + // Load the sprite sheet only if it is not loaded + if (this._loadedSpriteSheets.indexOf(spriteSheet) === -1) { + frameCache.addSpriteFrames(spriteSheet); + this._loadedSpriteSheets.push(spriteSheet); + } + value = frameCache.getSpriteFrame(spriteFile); + } + } + keyframe.setValue(value); + return keyframe; + }, + + _readHeader: function () { + /* If no bytes loaded, don't crash about it. */ + if (!this._data) + return false; + + /* Read magic bytes */ + var magicBytes = this._readStringFromBytes(this._currentByte, 4, true); + this._currentByte += 4; + + if (magicBytes !== 'ccbi') { + return false; + } + + /* Read version. */ + var version = this.readInt(false); + if (version !== CCB_VERSION) { + cc.log("WARNING! Incompatible ccbi file version (file: " + version + " reader: " + CCB_VERSION + ")"); + return false; + } + + this._jsControlled = !!this._data[this._currentByte++]; + this._animationManager._jsControlled = this._jsControlled; + // no need to set if it is "jscontrolled". It is obvious. + return true; + }, + + _readStringFromBytes: function (startIndex, strLen, reverse) { + reverse = reverse || false; + var strValue = ""; + var i, locData = this._data; + if (reverse) { + for (i = strLen - 1; i >= 0; i--) + strValue += String.fromCharCode(locData[startIndex + i]); + } else { + for (i = 0; i < strLen; i++) + strValue += String.fromCharCode(locData[startIndex + i]); + } + return strValue; + }, + + _readStringCache: function () { + var numStrings = this.readInt(false); + for (var i = 0; i < numStrings; i++) + this._readStringCacheEntry(); + return true; + }, + + _readStringCacheEntry: function () { + var b0 = this._data[this._currentByte++]; + var b1 = this._data[this._currentByte++]; + + var numBytes = b0 << 8 | b1; + + var str = "", locData = this._data, locCurrentByte = this._currentByte; + for (var i = 0; i < numBytes; i++) { + var hexChar = locData[locCurrentByte + i].toString("16").toUpperCase(); + hexChar = hexChar.length > 1 ? hexChar : "0" + hexChar; + str += "%" + hexChar; + } + str = decodeURIComponent(str); + + this._currentByte += numBytes; + this._stringCache.push(str); + }, + + _readNodeGraph: function (parent) { + /* Read class name. */ + var className = this.readCachedString(); + + var jsControlledName, locJsControlled = this._jsControlled, locActionManager = this._animationManager; + if (locJsControlled) + jsControlledName = this.readCachedString(); + + var memberVarAssignmentType = this.readInt(false); + var memberVarAssignmentName; + if (memberVarAssignmentType !== CCB_TARGETTYPE_NONE) { + memberVarAssignmentName = this.readCachedString(); + } + + var ccNodeLoader = this._ccNodeLoaderLibrary.getCCNodeLoader(className); + if (!ccNodeLoader) { + ccNodeLoader = this._ccNodeLoaderLibrary.getCCNodeLoader("CCNode"); + //cc.log("no corresponding node loader for" + className); + //return null; + } + var node = ccNodeLoader.loadCCNode(parent, this); + + //set root node + if (!locActionManager.getRootNode()) + locActionManager.setRootNode(node); + + if (locJsControlled && node === locActionManager.getRootNode()) { + locActionManager.setDocumentControllerName(jsControlledName); + } + + //read animated properties + var seqs = new cc._Dictionary(); + this._animatedProps = []; + + var i, locAnimatedProps = this._animatedProps; + var numSequence = this.readInt(false); + for (i = 0; i < numSequence; ++i) { + var seqId = this.readInt(false); + var seqNodeProps = new cc._Dictionary(); + + var numProps = this.readInt(false); + + for (var j = 0; j < numProps; ++j) { + var seqProp = new cc.BuilderSequenceProperty(); + seqProp.setName(this.readCachedString()); + seqProp.setType(this.readInt(false)); + + locAnimatedProps.push(seqProp.getName()); + var numKeyframes = this.readInt(false); + var locKeyframes = seqProp.getKeyframes(); + for (var k = 0; k < numKeyframes; ++k) { + var keyFrame = this.readKeyframe(seqProp.getType()); + locKeyframes.push(keyFrame); + } + seqNodeProps.setObject(seqProp, seqProp.getName()); + } + seqs.setObject(seqNodeProps, seqId); + } + + if (seqs.count() > 0) + locActionManager.addNode(node, seqs); + + //read properties + ccNodeLoader.parseProperties(node, parent, this); + + //handle sub ccb files(remove middle node) + var isCCBFileNode = !!node.ccbFileNode; + if (isCCBFileNode) { + var embeddedNode = node.ccbFileNode; + embeddedNode.setPosition(node.getPosition()); + embeddedNode.setRotation(node.getRotation()); + embeddedNode.setScaleX(node.getScaleX()); + embeddedNode.setScaleY(node.getScaleY()); + embeddedNode.setTag(node.getTag()); + embeddedNode.setVisible(true); + //embeddedNode.ignoreAnchorPointForPosition(node.isIgnoreAnchorPointForPosition()); + + locActionManager.moveAnimationsFromNode(node, embeddedNode); + node.ccbFileNode = null; + node = embeddedNode; + } + var target = null, locMemberAssigner = null; + if (memberVarAssignmentType !== CCB_TARGETTYPE_NONE) { + if (!locJsControlled) { + if (memberVarAssignmentType === CCB_TARGETTYPE_DOCUMENTROOT) { + target = locActionManager.getRootNode(); + } else if (memberVarAssignmentType === CCB_TARGETTYPE_OWNER) { + target = this._owner; + } + + if (!target) { + var assigned = false; + + if (target.onAssignCCBMemberVariable) + assigned = target.onAssignCCBMemberVariable(target, memberVarAssignmentName, node); + + locMemberAssigner = this._ccbMemberVariableAssigner; + if (!assigned && locMemberAssigner != null && locMemberAssigner.onAssignCCBMemberVariable) { + locMemberAssigner.onAssignCCBMemberVariable(target, memberVarAssignmentName, node); + } + } + } else { + if (memberVarAssignmentType === CCB_TARGETTYPE_DOCUMENTROOT) { + locActionManager.addDocumentOutletName(memberVarAssignmentName); + locActionManager.addDocumentOutletNode(node); + } else { + this._ownerOutletNames.push(memberVarAssignmentName); + this._ownerOutletNodes.push(node); + } + } + } + + // Assign custom properties. + if (ccNodeLoader.getCustomProperties().length > 0) { + var customAssigned = false; + if (!locJsControlled) { + target = node; + if (target != null && target.onAssignCCBCustomProperty != null) { + var customProperties = ccNodeLoader.getCustomProperties(); + var customPropKeys = customProperties.allKeys(); + for (i = 0; i < customPropKeys.length; i++) { + var customPropValue = customProperties.objectForKey(customPropKeys[i]); + customAssigned = target.onAssignCCBCustomProperty(target, customPropKeys[i], customPropValue); + locMemberAssigner = this._ccbMemberVariableAssigner; + if (!customAssigned && (locMemberAssigner != null) && (locMemberAssigner.onAssignCCBCustomProperty != null)) + customAssigned = locMemberAssigner.onAssignCCBCustomProperty(target, customPropKeys[i], customPropValue); + } + } + } + } + + this._animatedProps = null; + + /* Read and add children. */ + var numChildren = this.readInt(false); + for (i = 0; i < numChildren; i++) { + var child = this._readNodeGraph(node); + node.addChild(child); + } + + // FIX ISSUE #1860: "onNodeLoaded will be called twice if ccb was added as a CCBFile". + // If it's a sub-ccb node, skip notification to CCNodeLoaderListener since it will be + // notified at LINE #734: CCNode * child = this->readNodeGraph(node); + if (!isCCBFileNode) { + // Call onNodeLoaded + if (node != null && node.onNodeLoaded) + node.onNodeLoaded(node, ccNodeLoader); + else if (this._ccNodeLoaderListener != null) + this._ccNodeLoaderListener.onNodeLoaded(node, ccNodeLoader); + } + + return node; + }, + + _readUTF8: function () { + } +}); + +cc.BuilderReader._ccbResolutionScale = 1; +cc.BuilderReader.setResolutionScale = function (scale) { + cc.BuilderReader._ccbResolutionScale = scale; +}; + +cc.BuilderReader.getResolutionScale = function () { + return cc.BuilderReader._ccbResolutionScale; +}; + +cc.BuilderReader.loadAsScene = function (ccbFilePath, owner, parentSize, ccbRootPath) { + ccbRootPath = ccbRootPath || cc.BuilderReader.getResourcePath(); + + var getNode = cc.BuilderReader.load(ccbFilePath, owner, parentSize, ccbRootPath); + + var scene = new cc.Scene(); + scene.addChild(getNode); + return scene; +}; + +cc.BuilderReader._controllerClassCache = {}; +cc.BuilderReader.registerController = function (controllerName, controller) { + cc.BuilderReader._controllerClassCache[controllerName] = cc.Class.extend(controller); +}; +cc.BuilderReader.load = function (ccbFilePath, owner, parentSize, ccbRootPath) { + ccbRootPath = ccbRootPath || cc.BuilderReader.getResourcePath(); + var reader = new cc.BuilderReader(cc.NodeLoaderLibrary.newDefaultCCNodeLoaderLibrary()); + reader.setCCBRootPath(ccbRootPath); + if ((ccbFilePath.length < 5) || (ccbFilePath.toLowerCase().lastIndexOf(".ccbi") !== ccbFilePath.length - 5)) + ccbFilePath = ccbFilePath + ".ccbi"; + + var node = reader.readNodeGraphFromFile(ccbFilePath, owner, parentSize); + var i; + var callbackName, callbackNode, callbackControlEvents, outletName, outletNode; + // Assign owner callbacks & member variables + if (owner) { + // Callbacks + var ownerCallbackNames = reader.getOwnerCallbackNames(); + var ownerCallbackNodes = reader.getOwnerCallbackNodes(); + var ownerCallbackControlEvents = reader.getOwnerCallbackControlEvents(); + for (i = 0; i < ownerCallbackNames.length; i++) { + callbackName = ownerCallbackNames[i]; + callbackNode = ownerCallbackNodes[i]; + callbackControlEvents = ownerCallbackControlEvents[i]; + if (callbackNode instanceof cc.ControlButton) + callbackNode.addTargetWithActionForControlEvents(owner, owner[callbackName], callbackControlEvents); //register all type of events + else + callbackNode.setCallback(owner[callbackName], owner); + } + + // Variables + var ownerOutletNames = reader.getOwnerOutletNames(); + var ownerOutletNodes = reader.getOwnerOutletNodes(); + for (i = 0; i < ownerOutletNames.length; i++) { + outletName = ownerOutletNames[i]; + outletNode = ownerOutletNodes[i]; + owner[outletName] = outletNode; + } + } + + var nodesWithAnimationManagers = reader.getNodesWithAnimationManagers(); + var animationManagersForNodes = reader.getAnimationManagersForNodes(); + if (!nodesWithAnimationManagers || !animationManagersForNodes) + return node; + + var controllerClassCache = cc.BuilderReader._controllerClassCache; + // Attach animation managers to nodes and assign root node callbacks and member variables + for (i = 0; i < nodesWithAnimationManagers.length; i++) { + var innerNode = nodesWithAnimationManagers[i]; + var animationManager = animationManagersForNodes[i]; + + var j; + innerNode.animationManager = animationManager; + + var controllerName = animationManager.getDocumentControllerName(); + if (!controllerName) continue; + + // Create a controller + var controllerClass = controllerClassCache[controllerName]; + if (!controllerClass) throw new Error("Can not find controller : " + controllerName); + var controller = new controllerClass(); + controller.controllerName = controllerName; + + innerNode.controller = controller; + controller.rootNode = innerNode; + + // Callbacks + var documentCallbackNames = animationManager.getDocumentCallbackNames(); + var documentCallbackNodes = animationManager.getDocumentCallbackNodes(); + var documentCallbackControlEvents = animationManager.getDocumentCallbackControlEvents(); + for (j = 0; j < documentCallbackNames.length; j++) { + callbackName = documentCallbackNames[j]; + callbackNode = documentCallbackNodes[j]; + callbackControlEvents = documentCallbackControlEvents[j]; + if (callbackNode instanceof cc.ControlButton) + callbackNode.addTargetWithActionForControlEvents(controller, controller[callbackName], callbackControlEvents); //register all type of events + else + callbackNode.setCallback(controller[callbackName], controller); + } + + // Variables + var documentOutletNames = animationManager.getDocumentOutletNames(); + var documentOutletNodes = animationManager.getDocumentOutletNodes(); + for (j = 0; j < documentOutletNames.length; j++) { + outletName = documentOutletNames[j]; + outletNode = documentOutletNodes[j]; + + controller[outletName] = outletNode; + } + + if (controller.onDidLoadFromCCB && cc.isFunction(controller.onDidLoadFromCCB)) + controller.onDidLoadFromCCB(); + + // Setup timeline callbacks + var keyframeCallbacks = animationManager.getKeyframeCallbacks(); + for (j = 0; j < keyframeCallbacks.length; j++) { + var callbackSplit = keyframeCallbacks[j].split(":"); + var callbackType = callbackSplit[0]; + var kfCallbackName = callbackSplit[1]; + + if (callbackType == 1) { // Document callback + animationManager.setCallFunc(cc.callFunc(controller[kfCallbackName], controller), keyframeCallbacks[j]); + } else if (callbackType == 2 && owner) {// Owner callback + animationManager.setCallFunc(cc.callFunc(owner[kfCallbackName], owner), keyframeCallbacks[j]); + } + } + } + + //auto play animations + animationManager.runAnimations(animationManager.getAutoPlaySequenceId(), 0); + + return node; +}; + +cc.BuilderReader._resourcePath = ""; +cc.BuilderReader.setResourcePath = function (rootPath) { + cc.BuilderReader._resourcePath = rootPath; +}; + +cc.BuilderReader.getResourcePath = function () { + return cc.BuilderReader._resourcePath; +}; + +cc.BuilderReader.lastPathComponent = function (pathStr) { + var slashPos = pathStr.lastIndexOf("/"); + if (slashPos !== -1) { + return pathStr.substring(slashPos + 1, pathStr.length - slashPos); + } + return pathStr; +}; + +cc.BuilderReader.deletePathExtension = function (pathStr) { + var dotPos = pathStr.lastIndexOf("."); + if (dotPos !== -1) { + return pathStr.substring(0, dotPos); + } + return pathStr; +}; + +cc.BuilderReader.toLowerCase = function (sourceStr) { + return sourceStr.toLowerCase(); +}; + +cc.BuilderReader.endsWith = function (sourceStr, ending) { + if (sourceStr.length >= ending.length) + return (sourceStr.lastIndexOf(ending) === 0); + else + return false; +}; + +cc.BuilderReader.concat = function (stringA, stringB) { + return stringA + stringB; +}; + +cc.loader.register(["ccbi"], cc._binaryLoader); diff --git a/frameworks/cocos2d-html5/extensions/ccb-reader/CCBReaderUtil.js b/frameworks/cocos2d-html5/extensions/ccb-reader/CCBReaderUtil.js new file mode 100644 index 0000000..0de9c4f --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccb-reader/CCBReaderUtil.js @@ -0,0 +1,70 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +cc.NodeLoaderListener = cc.Class.extend({ + onNodeLoaded: function (node, nodeLoader) { + } +}); + +cc.BuilderSelectorResolver = cc.Class.extend({ + onResolveCCBCCMenuItemSelector: function (target, selectorName) { + }, + onResolveCCBCCCallFuncSelector: function (target, selectorName) { + }, + onResolveCCBCCControlSelector: function (target, selectorName) { + } +}); + +cc.BuilderScriptOwnerProtocol = cc.Class.extend({ + createNew: function () { + } +}); + +cc.BuilderMemberVariableAssigner = cc.Class.extend({ + /** + * The callback function of assigning member variable.
+ * @note The member variable must be CCNode or its subclass. + * @param {Object} target The custom class + * @param {string} memberVariableName The name of the member variable. + * @param {cc.Node} node The member variable. + * @return {Boolean} Whether the assignment was successful. + */ + onAssignCCBMemberVariable: function (target, memberVariableName, node) { + return false; + }, + + /** + * The callback function of assigning custom properties. + * @note The member variable must be Integer, Float, Boolean or String. + * @param {Object} target The custom class. + * @param {string} memberVariableName The name of the member variable. + * @param {*} value The value of the property. + * @return {Boolean} Whether the assignment was successful. + */ + onAssignCCBCustomProperty: function (target, memberVariableName, value) { + return false; + } +}); diff --git a/frameworks/cocos2d-html5/extensions/ccb-reader/CCBRelativePositioning.js b/frameworks/cocos2d-html5/extensions/ccb-reader/CCBRelativePositioning.js new file mode 100644 index 0000000..ed0d29a --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccb-reader/CCBRelativePositioning.js @@ -0,0 +1,64 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +cc.getAbsolutePosition = function (x, y, type, containerSize, propName, out) { + var absPt = out || cc.p(0, 0); + if (type === CCB_POSITIONTYPE_RELATIVE_BOTTOM_LEFT) { + absPt.x = x; + absPt.y = y; + } else if (type === CCB_POSITIONTYPE_RELATIVE_TOP_LEFT) { + absPt.x = x; + absPt.y = containerSize.height - y; + } else if (type === CCB_POSITIONTYPE_RELATIVE_TOP_RIGHT) { + absPt.x = containerSize.width - x; + absPt.y = containerSize.height - y; + } else if (type === CCB_POSITIONTYPE_RELATIVE_BOTTOM_RIGHT) { + absPt.x = containerSize.width - x; + absPt.y = y; + } else if (type === CCB_POSITIONTYPE_PERCENT) { + absPt.x = (containerSize.width * x / 100.0); + absPt.y = (containerSize.height * y / 100.0); + } else if (type === CCB_POSITIONTYPE_MULTIPLY_RESOLUTION) { + var resolutionScale = cc.BuilderReader.getResolutionScale(); + absPt.x = x * resolutionScale; + absPt.y = y * resolutionScale; + } + return absPt; +}; + +cc.setRelativeScale = function (node, scaleX, scaleY, type, propName) { + if (!node) + throw new Error("cc.setRelativeScale(): node should be non-null"); + + if (type === CCB_POSITIONTYPE_MULTIPLY_RESOLUTION) { + var resolutionScale = cc.BuilderReader.getResolutionScale(); + + scaleX *= resolutionScale; + scaleY *= resolutionScale; + } + + node.setScale(scaleX, scaleY); +}; diff --git a/frameworks/cocos2d-html5/extensions/ccb-reader/CCBSequence.js b/frameworks/cocos2d-html5/extensions/ccb-reader/CCBSequence.js new file mode 100644 index 0000000..a1bc0e2 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccb-reader/CCBSequence.js @@ -0,0 +1,114 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +cc.BuilderSequence = cc.Class.extend({ + _duration:0, + _name:"", + _sequenceId:0, + _chainedSequenceId:0, + _callbackChannel:null, + _soundChannel:null, + + ctor:function(){ + this._name = ""; + }, + + getDuration:function(){ + return this._duration; + }, + setDuration:function(duration){ + this._duration = duration; + }, + + getName:function(){ + return this._name; + }, + setName:function(name){ + this._name = name; + }, + + getSequenceId:function(){ + return this._sequenceId; + }, + setSequenceId:function(sequenceId){ + this._sequenceId = sequenceId; + }, + + getChainedSequenceId:function(){ + return this._chainedSequenceId; + }, + setChainedSequenceId:function(chainedSequenceId){ + this._chainedSequenceId = chainedSequenceId; + }, + + getCallbackChannel:function() { + return this._callbackChannel; + }, + setCallbackChannel:function(channel) { + this._callbackChannel = channel; + }, + + getSoundChannel:function() { + return this._soundChannel; + }, + setSoundChannel:function(channel) { + this._soundChannel = channel; + } +}); + +cc.BuilderSequenceProperty = cc.Class.extend({ + _name : null, + _type:0, + _keyFrames:null, + + ctor:function(){ + this.init(); + }, + + init:function(){ + this._keyFrames = []; + this._name = ""; + }, + + getName:function(){ + return this._name; + }, + + setName :function(name){ + this._name = name; + }, + + getType:function(){ + return this._type; + }, + setType :function(type){ + this._type = type; + }, + + getKeyframes:function(){ + return this._keyFrames; + } +}); \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/ccb-reader/CCBValue.js b/frameworks/cocos2d-html5/extensions/ccb-reader/CCBValue.js new file mode 100644 index 0000000..dee4c7b --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccb-reader/CCBValue.js @@ -0,0 +1,60 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +cc.INT_VALUE = 0; + +cc.FLOAT_VALUE = 1; + +cc.POINTER_VALUE = 2; + +cc.BOOL_VALUE = 3; + +cc.UNSIGNEDCHAR_VALUE = 4; + +cc.BuilderValue = cc.Class.extend({ + _value: null, + _type: 0, + + getIntValue: function () { + }, + getFloatValue: function () { + }, + getBoolValue: function () { + }, + getByteValue: function () { + }, + getPointer: function () { + }, + + getValue: function () { + return this._value; + } +}); + +cc.BuilderValue.create = function (value) { + return new cc.BuilderValue(); +}; + diff --git a/frameworks/cocos2d-html5/extensions/ccb-reader/CCControlLoader.js b/frameworks/cocos2d-html5/extensions/ccb-reader/CCControlLoader.js new file mode 100644 index 0000000..3b1a5d6 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccb-reader/CCControlLoader.js @@ -0,0 +1,320 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +var PROPERTY_CCBFILE = "ccbFile"; + +cc.BuilderFileLoader = cc.NodeLoader.extend({ + _createCCNode: function (parent, ccbReader) { + var node = new cc.Node(); + node.ccbFileNode = null; + return node; + }, + onHandlePropTypeCCBFile: function (node, parent, propertyName, ccbFileNode, ccbReader) { + if (propertyName === PROPERTY_CCBFILE) { + node.ccbFileNode = ccbFileNode; + } else { + cc.NodeLoader.prototype.onHandlePropTypeCCBFile.call(this, node, parent, propertyName, ccbFileNode, ccbReader); + } + } +}); + +cc.BuilderFileLoader.loader = function () { + return new cc.BuilderFileLoader(); +}; + +var PROPERTY_ENABLED = "enabled"; +var PROPERTY_SELECTED = "selected"; +var PROPERTY_CCCONTROL = "ccControl"; + +cc.ControlLoader = cc.NodeLoader.extend({ + _createCCNode: function (parent, ccbReander) { + }, + onHandlePropTypeBlockCCControl: function (node, parent, propertyName, blockCCControlData, ccbReader) { + if (propertyName === PROPERTY_CCCONTROL) { + node.addTargetWithActionForControlEvents(blockCCControlData.target, blockCCControlData.selCCControlHandler, blockCCControlData.controlEvents); + } else { + cc.NodeLoader.prototype.onHandlePropTypeBlockCCControl.call(this, node, parent, propertyName, blockCCControlData, ccbReader); + } + }, + onHandlePropTypeCheck: function (node, parent, propertyName, check, ccbReader) { + if (propertyName === PROPERTY_ENABLED) { + node.setEnabled(check); + } else if (propertyName === PROPERTY_SELECTED) { + node.setSelected(check); + } else { + cc.NodeLoader.prototype.onHandlePropTypeCheck.call(this, node, parent, propertyName, check, ccbReader); + } + } +}); + +var PROPERTY_ZOOMONTOUCHDOWN = "zoomOnTouchDown"; +var PROPERTY_TITLE_NORMAL = "title|1"; +var PROPERTY_TITLE_HIGHLIGHTED = "title|2"; +var PROPERTY_TITLE_DISABLED = "title|3"; +var PROPERTY_TITLECOLOR_NORMAL = "titleColor|1"; +var PROPERTY_TITLECOLOR_HIGHLIGHTED = "titleColor|2"; +var PROPERTY_TITLECOLOR_DISABLED = "titleColor|3"; +var PROPERTY_TITLETTF_NORMAL = "titleTTF|1"; +var PROPERTY_TITLETTF_HIGHLIGHTED = "titleTTF|2"; +var PROPERTY_TITLETTF_DISABLED = "titleTTF|3"; +var PROPERTY_TITLETTFSIZE_NORMAL = "titleTTFSize|1"; +var PROPERTY_TITLETTFSIZE_HIGHLIGHTED = "titleTTFSize|2"; +var PROPERTY_TITLETTFSIZE_DISABLED = "titleTTFSize|4"; +var PROPERTY_LABELANCHORPOINT = "labelAnchorPoint"; +var PROPERTY_PREFEREDSIZE = "preferedSize"; // TODO Should be = "preferredSize". This is a typo in cocos2d-iphone, cocos2d-x and CocosBuilder! +var PROPERTY_BACKGROUNDSPRITEFRAME_NORMAL = "backgroundSpriteFrame|1"; +var PROPERTY_BACKGROUNDSPRITEFRAME_HIGHLIGHTED = "backgroundSpriteFrame|2"; +var PROPERTY_BACKGROUNDSPRITEFRAME_DISABLED = "backgroundSpriteFrame|3"; + +cc.ControlButtonLoader = cc.ControlLoader.extend({ + _createCCNode: function (parent, ccbReader) { + return new cc.ControlButton(); + }, + + onHandlePropTypeCheck: function (node, parent, propertyName, check, ccbReader) { + if (propertyName === PROPERTY_ZOOMONTOUCHDOWN) { + node.zoomOnTouchDown = check; + } else { + cc.ControlLoader.prototype.onHandlePropTypeCheck.call(this, node, parent, propertyName, check, ccbReader); + } + }, + onHandlePropTypeString: function (node, parent, propertyName, stringValue, ccbReader) { + if (propertyName === PROPERTY_TITLE_NORMAL) { + node.setTitleForState(stringValue, cc.CONTROL_STATE_NORMAL); + } else if (propertyName === PROPERTY_TITLE_HIGHLIGHTED) { + node.setTitleForState(stringValue, cc.CONTROL_STATE_HIGHLIGHTED); + } else if (propertyName === PROPERTY_TITLE_DISABLED) { + node.setTitleForState(stringValue, cc.CONTROL_STATE_DISABLED); + } else { + cc.ControlLoader.prototype.onHandlePropTypeString.call(this, node, parent, propertyName, stringValue, ccbReader); + } + }, + onHandlePropTypeFontTTF: function (node, parent, propertyName, fontTTF, ccbReader) { + if (propertyName === PROPERTY_TITLETTF_NORMAL) { + node.setTitleTTFForState(fontTTF, cc.CONTROL_STATE_NORMAL); + } else if (propertyName === PROPERTY_TITLETTF_HIGHLIGHTED) { + node.setTitleTTFForState(fontTTF, cc.CONTROL_STATE_HIGHLIGHTED); + } else if (propertyName === PROPERTY_TITLETTF_DISABLED) { + node.setTitleTTFForState(fontTTF, cc.CONTROL_STATE_DISABLED); + } else { + cc.ControlLoader.prototype.onHandlePropTypeFontTTF.call(this, node, parent, propertyName, fontTTF, ccbReader); + } + }, + onHandlePropTypeFloatScale: function (node, parent, propertyName, floatScale, ccbReader) { + if (propertyName === PROPERTY_TITLETTFSIZE_NORMAL) { + node.setTitleTTFSizeForState(floatScale, cc.CONTROL_STATE_NORMAL); + } else if (propertyName === PROPERTY_TITLETTFSIZE_HIGHLIGHTED) { + node.setTitleTTFSizeForState(floatScale, cc.CONTROL_STATE_HIGHLIGHTED); + } else if (propertyName === PROPERTY_TITLETTFSIZE_DISABLED) { + node.setTitleTTFSizeForState(floatScale, cc.CONTROL_STATE_DISABLED); + } else { + cc.ControlLoader.prototype.onHandlePropTypeFloatScale.call(this, node, parent, propertyName, floatScale, ccbReader); + } + }, + onHandlePropTypePoint: function (node, parent, propertyName, point, ccbReader) { + if (propertyName === PROPERTY_LABELANCHORPOINT) { + node.setLabelAnchorPoint(point); + } else { + cc.ControlLoader.prototype.onHandlePropTypePoint.call(this, node, parent, propertyName, point, ccbReader); + } + }, + onHandlePropTypeSize: function (node, parent, propertyName, size, ccbReader) { + if (propertyName === PROPERTY_PREFEREDSIZE) { + node.setPreferredSize(size); + } else { + cc.ControlLoader.prototype.onHandlePropTypeSize.call(this, node, parent, propertyName, size, ccbReader); + } + }, + onHandlePropTypeSpriteFrame: function (node, parent, propertyName, spriteFrame, ccbReader) { + if (propertyName === PROPERTY_BACKGROUNDSPRITEFRAME_NORMAL) { + if (spriteFrame != null) { + node.setBackgroundSpriteFrameForState(spriteFrame, cc.CONTROL_STATE_NORMAL); + } + } else if (propertyName === PROPERTY_BACKGROUNDSPRITEFRAME_HIGHLIGHTED) { + if (spriteFrame != null) { + node.setBackgroundSpriteFrameForState(spriteFrame, cc.CONTROL_STATE_HIGHLIGHTED); + } + } else if (propertyName === PROPERTY_BACKGROUNDSPRITEFRAME_DISABLED) { + if (spriteFrame != null) { + node.setBackgroundSpriteFrameForState(spriteFrame, cc.CONTROL_STATE_DISABLED); + } + } else { + cc.ControlLoader.prototype.onHandlePropTypeSpriteFrame.call(this, node, parent, propertyName, spriteFrame, ccbReader); + } + }, + onHandlePropTypeColor3: function (node, parent, propertyName, ccColor3B, ccbReader) { + if (propertyName === PROPERTY_TITLECOLOR_NORMAL) { + node.setTitleColorForState(ccColor3B, cc.CONTROL_STATE_NORMAL); + } else if (propertyName === PROPERTY_TITLECOLOR_HIGHLIGHTED) { + node.setTitleColorForState(ccColor3B, cc.CONTROL_STATE_HIGHLIGHTED); + } else if (propertyName === PROPERTY_TITLECOLOR_DISABLED) { + node.setTitleColorForState(ccColor3B, cc.CONTROL_STATE_DISABLED); + } else { + cc.ControlLoader.prototype.onHandlePropTypeColor3.call(this, node, parent, propertyName, ccColor3B, ccbReader); + } + } +}); + +cc.ControlButtonLoader.loader = function () { + return new cc.ControlButtonLoader(); +}; + +var PROPERTY_CONTAINER = "container"; +var PROPERTY_DIRECTION = "direction"; +var PROPERTY_CLIPSTOBOUNDS = "clipsToBounds"; +var PROPERTY_BOUNCES = "bounces"; +var PROPERTY_SCALE = "scale"; + +cc.ScrollViewLoader = cc.NodeLoader.extend({ + _createCCNode: function (parent, ccbReader) { + return new cc.ScrollView(); + }, + + onHandlePropTypeSize: function (node, parent, propertyName, size, ccbReader) { + if (propertyName === PROPERTY_CONTENTSIZE) { + node.setViewSize(size); + } else { + cc.NodeLoader.prototype.onHandlePropTypeSize.call(this, node, parent, propertyName, size, ccbReader); + } + }, + + onHandlePropTypeCCBFile: function (node, parent, propertyName, ccbFileNode, ccbReader) { + if (propertyName === PROPERTY_CONTAINER) { + node.setContainer(ccbFileNode); + node.updateInset(); + } else { + cc.NodeLoader.prototype.onHandlePropTypeCCBFile.call(this, node, parent, propertyName, ccbFileNode, ccbReader); + } + }, + onHandlePropTypeCheck: function (node, parent, propertyName, check, ccbReader) { + if (propertyName === PROPERTY_CLIPSTOBOUNDS) { + node.setClippingToBounds(check); + } else if (propertyName === PROPERTY_BOUNCES) { + node.setBounceable(check); + } else { + cc.NodeLoader.prototype.onHandlePropTypeCheck.call(this, node, parent, propertyName, check, ccbReader); + } + }, + onHandlePropTypeFloat: function (node, parent, propertyName, floatValue, ccbReader) { + if (propertyName === PROPERTY_SCALE) { + node.setScale(floatValue); + } else { + cc.NodeLoader.prototype.onHandlePropTypeFloat.call(this, node, parent, propertyName, floatValue, ccbReader); + } + }, + onHandlePropTypeIntegerLabeled: function (node, parent, propertyName, integerLabeled, ccbReader) { + if (propertyName === PROPERTY_DIRECTION) { + node.setDirection(integerLabeled); + } else { + cc.NodeLoader.prototype.onHandlePropTypeIntegerLabeled.call(this, node, parent, propertyName, integerLabeled, ccbReader); + } + } +}); + +cc.ScrollViewLoader.loader = function () { + return new cc.ScrollViewLoader(); +}; + +var PROPERTY_CONTENTSIZE = "contentSize"; +var PROPERTY_SPRITEFRAME = "spriteFrame"; +var PROPERTY_COLOR = "color"; +var PROPERTY_OPACITY = "opacity"; +var PROPERTY_BLENDFUNC = "blendFunc"; +var PROPERTY_INSETLEFT = "insetLeft"; +var PROPERTY_INSETTOP = "insetTop"; +var PROPERTY_INSETRIGHT = "insetRight"; +var PROPERTY_INSETBOTTOM = "insetBottom"; + +cc.Scale9SpriteLoader = cc.NodeLoader.extend({ + _createCCNode: function (parent, ccbReader) { + var sprite = new cc.Scale9Sprite(); + + sprite.setAnchorPoint(0, 0); + + return sprite; + }, + + onHandlePropTypeColor3: function (node, parent, propertyName, ccColor3B, ccbReader) { + if (propertyName === PROPERTY_COLOR) { + if (ccColor3B.r !== 255 || ccColor3B.g !== 255 || ccColor3B.b !== 255) { + node.setColor(ccColor3B); + } + } else { + cc.NodeLoader.prototype.onHandlePropTypeColor3.call(this, node, parent, propertyName, ccColor3B, ccbReader); + } + }, + onHandlePropTypeByte: function (node, parent, propertyName, byteValue, ccbReader) { + if (propertyName === PROPERTY_OPACITY) { + node.setOpacity(byteValue); + } else { + cc.NodeLoader.prototype.onHandlePropTypeByte.call(this, node, parent, propertyName, byteValue, ccbReader); + } + }, + onHandlePropTypeBlendFunc: function (node, parent, propertyName, ccBlendFunc, ccbReader) { + if (propertyName === PROPERTY_BLENDFUNC) { + // TODO Not exported by CocosBuilder yet! + // node.setBlendFunc(ccBlendFunc); + } else { + cc.NodeLoader.prototype.onHandlePropTypeBlendFunc.call(this, node, parent, propertyName, ccBlendFunc, ccbReader); + } + }, + onHandlePropTypeSpriteFrame: function (node, parent, propertyName, spriteFrame, ccbReader) { + if (propertyName === PROPERTY_SPRITEFRAME) { + node.setSpriteFrame(spriteFrame); + } else { + cc.NodeLoader.prototype.onHandlePropTypeSpriteFrame.call(this, node, parent, propertyName, spriteFrame, ccbReader); + } + }, + onHandlePropTypeSize: function (node, parent, propertyName, size, ccbReader) { + if (propertyName === PROPERTY_CONTENTSIZE) { + //node.setContentSize(size); + } else if (propertyName === PROPERTY_PREFEREDSIZE) { + node.setPreferredSize(size); + } else { + cc.NodeLoader.prototype.onHandlePropTypeSize.call(this, node, parent, propertyName, size, ccbReader); + } + }, + onHandlePropTypeFloat: function (node, parent, propertyName, floatValue, ccbReader) { + if (propertyName === PROPERTY_INSETLEFT) { + node.setInsetLeft(floatValue); + } else if (propertyName === PROPERTY_INSETTOP) { + node.setInsetTop(floatValue); + } else if (propertyName === PROPERTY_INSETRIGHT) { + node.setInsetRight(floatValue); + } else if (propertyName === PROPERTY_INSETBOTTOM) { + node.setInsetBottom(floatValue); + } else { + cc.NodeLoader.prototype.onHandlePropTypeFloat.call(this, node, parent, propertyName, floatValue, ccbReader); + } + } +}); + +cc.Scale9SpriteLoader.loader = function () { + return new cc.Scale9SpriteLoader(); +}; + + + diff --git a/frameworks/cocos2d-html5/extensions/ccb-reader/CCNodeLoader.js b/frameworks/cocos2d-html5/extensions/ccb-reader/CCNodeLoader.js new file mode 100644 index 0000000..afa434b --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccb-reader/CCNodeLoader.js @@ -0,0 +1,919 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +var PROPERTY_POSITION = "position"; +var PROPERTY_CONTENTSIZE = "contentSize"; +var PROPERTY_SKEW = "skew"; +var PROPERTY_ANCHORPOINT = "anchorPoint"; +var PROPERTY_SCALE = "scale"; +var PROPERTY_ROTATION = "rotation"; +var PROPERTY_TAG = "tag"; +var PROPERTY_IGNOREANCHORPOINTFORPOSITION = "ignoreAnchorPointForPosition"; +var PROPERTY_VISIBLE = "visible"; + +var ASSERT_FAIL_UNEXPECTED_PROPERTY = function (propertyName) { + cc.log("Unexpected property: '" + propertyName + "'!"); +}; + +var ASSERT_FAIL_UNEXPECTED_PROPERTYTYPE = function (propertyName) { + cc.log("Unexpected property type: '" + propertyName + "'!"); +}; + +function BlockData(selMenuHander, target) { + this.selMenuHander = selMenuHander; + this.target = target; +} + +function BlockCCControlData(selCCControlHandler, target, controlEvents) { + this.selCCControlHandler = selCCControlHandler; + this.target = target; + this.controlEvents = controlEvents; +} + +cc.NodeLoader = cc.Class.extend({ + _customProperties: null, + _pt: null, + _size: null, + _arr2: null, + + ctor: function () { + this._customProperties = new cc._Dictionary(); + this._pt = cc.p(); + this._size = cc.size(); + this._arr2 = new Array(2); + this._blockControlData = { + selCCControlHandler: null, + target: null, + controlEvents: null + }; + }, + + loadCCNode: function (parent, ccbReader) { + return this._createCCNode(parent, ccbReader); + //this.parseProperties(node, parent, ccbReader); + //return node; + }, + + parseProperties: function (node, parent, ccbReader) { + var numRegularProps = ccbReader.readInt(false); + var numExturaProps = ccbReader.readInt(false); + var propertyCount = numRegularProps + numExturaProps; + + for (var i = 0; i < propertyCount; i++) { + var isExtraProp = (i >= numRegularProps); + var type = ccbReader.readInt(false); + var propertyName = ccbReader.readCachedString(); + + // Check if the property can be set for this platform + var setProp = false; + + var platform = ccbReader._data[ccbReader._currentByte++]; + if ((platform === CCB_PLATFORM_ALL) || (platform === CCB_PLATFORM_IOS) || (platform === CCB_PLATFORM_MAC)) + setProp = true; + + //forward properties for sub ccb files + if (isExtraProp) { + if (node.ccbFileNode) { + node = node.ccbFileNode; + //skip properties that doesn't have a value to override + var getExtraPropsNames = node.userObject; + setProp = getExtraPropsNames.indexOf(propertyName) !== -1; + } else if (node === ccbReader._animationManager.getRootNode()) { + var extraPropsNames = node.userObject; + if (!extraPropsNames) { + extraPropsNames = []; + node.userObject = extraPropsNames; + } + extraPropsNames.push(propertyName); + } + } + + switch (type) { + case CCB_PROPTYPE_POSITION: + { + var position = this.parsePropTypePosition(node, parent, ccbReader, propertyName); + if (setProp) + this.onHandlePropTypePosition(node, parent, propertyName, position, ccbReader); + break; + } + case CCB_PROPTYPE_POINT: + { + var point = this.parsePropTypePoint(node, parent, ccbReader); + if (setProp) + this.onHandlePropTypePoint(node, parent, propertyName, point, ccbReader); + break; + } + case CCB_PROPTYPE_POINTLOCK: + { + var pointLock = this.parsePropTypePointLock(node, parent, ccbReader); + if (setProp) + this.onHandlePropTypePointLock(node, parent, propertyName, pointLock, ccbReader); + break; + } + case CCB_PROPTYPE_SIZE: + { + var size = this.parsePropTypeSize(node, parent, ccbReader); + if (setProp) + this.onHandlePropTypeSize(node, parent, propertyName, size, ccbReader); + break; + } + case CCB_PROPTYPE_SCALELOCK: + { + var scaleLock = this.parsePropTypeScaleLock(node, parent, ccbReader, propertyName); + if (setProp) + this.onHandlePropTypeScaleLock(node, parent, propertyName, scaleLock, ccbReader); + break; + } + case CCB_PROPTYPE_FLOATXY: + { + var xy = this.parsePropTypeFloatXY(node, parent, ccbReader); + if (setProp) + this.onHandlePropTypeFloatXY(node, parent, propertyName, xy, ccbReader); + break; + } + + case CCB_PROPTYPE_FLOAT: + { + var f = this.parsePropTypeFloat(node, parent, ccbReader); + if (setProp) { + this.onHandlePropTypeFloat(node, parent, propertyName, f, ccbReader); + } + break; + } + case CCB_PROPTYPE_DEGREES: + { + var degrees = this.parsePropTypeDegrees(node, parent, ccbReader, propertyName); + if (setProp) { + this.onHandlePropTypeDegrees(node, parent, propertyName, degrees, ccbReader); + } + break; + } + case CCB_PROPTYPE_FLOATSCALE: + { + var floatScale = this.parsePropTypeFloatScale(node, parent, ccbReader); + if (setProp) { + this.onHandlePropTypeFloatScale(node, parent, propertyName, floatScale, ccbReader); + } + break; + } + case CCB_PROPTYPE_INTEGER: + { + var integer = this.parsePropTypeInteger(node, parent, ccbReader); + if (setProp) { + this.onHandlePropTypeInteger(node, parent, propertyName, integer, ccbReader); + } + break; + } + case CCB_PROPTYPE_INTEGERLABELED: + { + var integerLabeled = this.parsePropTypeIntegerLabeled(node, parent, ccbReader); + if (setProp) { + this.onHandlePropTypeIntegerLabeled(node, parent, propertyName, integerLabeled, ccbReader); + } + break; + } + case CCB_PROPTYPE_FLOATVAR: + { + var floatVar = this.parsePropTypeFloatVar(node, parent, ccbReader); + if (setProp) { + this.onHandlePropTypeFloatVar(node, parent, propertyName, floatVar, ccbReader); + } + break; + } + case CCB_PROPTYPE_CHECK: + { + var check = this.parsePropTypeCheck(node, parent, ccbReader, propertyName); + if (setProp) { + this.onHandlePropTypeCheck(node, parent, propertyName, check, ccbReader); + } + break; + } + case CCB_PROPTYPE_SPRITEFRAME: + { + var ccSpriteFrame = this.parsePropTypeSpriteFrame(node, parent, ccbReader, propertyName); + if (setProp) { + this.onHandlePropTypeSpriteFrame(node, parent, propertyName, ccSpriteFrame, ccbReader); + } + break; + } + case CCB_PROPTYPE_ANIMATION: + { + var ccAnimation = this.parsePropTypeAnimation(node, parent, ccbReader); + if (setProp) { + this.onHandlePropTypeAnimation(node, parent, propertyName, ccAnimation, ccbReader); + } + break; + } + case CCB_PROPTYPE_TEXTURE: + { + var ccTexture2D = this.parsePropTypeTexture(node, parent, ccbReader); + if (setProp) { + this.onHandlePropTypeTexture(node, parent, propertyName, ccTexture2D, ccbReader); + } + break; + } + case CCB_PROPTYPE_BYTE: + { + var byteValue = this.parsePropTypeByte(node, parent, ccbReader, propertyName); + if (setProp) { + this.onHandlePropTypeByte(node, parent, propertyName, byteValue, ccbReader); + } + break; + } + case CCB_PROPTYPE_COLOR3: + { + var color = this.parsePropTypeColor3(node, parent, ccbReader, propertyName); + if (setProp) { + this.onHandlePropTypeColor3(node, parent, propertyName, color, ccbReader); + } + break; + } + case CCB_PROPTYPE_COLOR4VAR: + { + var color4FVar = this.parsePropTypeColor4FVar(node, parent, ccbReader); + if (setProp) { + this.onHandlePropTypeColor4FVar(node, parent, propertyName, color4FVar, ccbReader); + } + break; + } + case CCB_PROPTYPE_FLIP: + { + var flip = this.parsePropTypeFlip(node, parent, ccbReader); + if (setProp) { + this.onHandlePropTypeFlip(node, parent, propertyName, flip, ccbReader); + } + break; + } + case CCB_PROPTYPE_BLENDMODE: + { + var blendFunc = this.parsePropTypeBlendFunc(node, parent, ccbReader); + if (setProp) { + this.onHandlePropTypeBlendFunc(node, parent, propertyName, blendFunc, ccbReader); + } + break; + } + case CCB_PROPTYPE_FNTFILE: + { + var fntFile = ccbReader.getCCBRootPath() + this.parsePropTypeFntFile(node, parent, ccbReader); + if (setProp) { + this.onHandlePropTypeFntFile(node, parent, propertyName, fntFile, ccbReader); + } + break; + } + case CCB_PROPTYPE_FONTTTF: + { + var fontTTF = this.parsePropTypeFontTTF(node, parent, ccbReader); + if (setProp) { + this.onHandlePropTypeFontTTF(node, parent, propertyName, fontTTF, ccbReader); + } + break; + } + case CCB_PROPTYPE_STRING: + { + var stringValue = this.parsePropTypeString(node, parent, ccbReader); + if (setProp) { + this.onHandlePropTypeString(node, parent, propertyName, stringValue, ccbReader); + } + break; + } + case CCB_PROPTYPE_TEXT: + { + var textValue = this.parsePropTypeText(node, parent, ccbReader); + if (setProp) { + this.onHandlePropTypeText(node, parent, propertyName, textValue, ccbReader); + } + break; + } + case CCB_PROPTYPE_BLOCK: + { + var blockData = this.parsePropTypeBlock(node, parent, ccbReader); + if (setProp) { + this.onHandlePropTypeBlock(node, parent, propertyName, blockData, ccbReader); + } + break; + } + case CCB_PROPTYPE_BLOCKCCCONTROL: + { + var blockCCControlData = this.parsePropTypeBlockCCControl(node, parent, ccbReader); + if (setProp && blockCCControlData != null) { + this.onHandlePropTypeBlockCCControl(node, parent, propertyName, blockCCControlData, ccbReader); + } + break; + } + case CCB_PROPTYPE_CCBFILE: + { + var ccbFileNode = this.parsePropTypeCCBFile(node, parent, ccbReader); + if (setProp) { + this.onHandlePropTypeCCBFile(node, parent, propertyName, ccbFileNode, ccbReader); + } + break; + } + default: + ASSERT_FAIL_UNEXPECTED_PROPERTYTYPE(type); + break; + } + } + }, + + getCustomProperties: function () { + return this._customProperties; + }, + + _createCCNode: function (parent, ccbReader) { + return new cc.Node(); + }, + + parsePropTypePosition: function (node, parent, ccbReader, propertyName) { + var x = ccbReader.readFloat(); + var y = ccbReader.readFloat(); + + var type = ccbReader.readInt(false); + + // var containerSize = ccbReader._animationManager.getContainerSize(parent); + var containerSize = parent ? parent._contentSize : ccbReader._animationManager._rootContainerSize; + cc.getAbsolutePosition(x, y, type, containerSize, propertyName, this._pt); + node.setPosition(this._pt); + + if (ccbReader._animatedProps.indexOf(propertyName) > -1) { + var baseValue = [x, y, type]; + ccbReader._animationManager.setBaseValue(baseValue, node, propertyName); + } + return this._pt; + }, + + parsePropTypePoint: function (node, parent, ccbReader) { + this._pt.x = ccbReader.readFloat(); + this._pt.y = ccbReader.readFloat(); + return this._pt; + }, + + parsePropTypePointLock: function (node, parent, ccbReader) { + this._pt.x = ccbReader.readFloat(); + this._pt.y = ccbReader.readFloat(); + return this._pt; + }, + + parsePropTypeSize: function (node, parent, ccbReader) { + var width = ccbReader.readFloat(); + var height = ccbReader.readFloat(); + + var type = ccbReader.readInt(false); + + // var containerSize = ccbReader._animationManager.getContainerSize(parent); + var containerSize = parent ? parent._contentSize : ccbReader._animationManager._rootContainerSize; + + switch (type) { + case CCB_SIZETYPE_ABSOLUTE: + /* Nothing. */ + break; + case CCB_SIZETYPE_RELATIVE_CONTAINER: + width = containerSize.width - width; + height = containerSize.height - height; + break; + case CCB_SIZETYPE_PERCENT: + width = (containerSize.width * width / 100.0); + height = (containerSize.height * height / 100.0); + break; + case CCB_SIZETYPE_HORIZONTAL_PERCENT: + width = (containerSize.width * width / 100.0); + break; + case CCB_SIZETYPE_VERTICAL_PERCENT: + height = (containerSize.height * height / 100.0); + break; + case CCB_SIZETYPE_MULTIPLY_RESOLUTION: + var resolutionScale = cc.BuilderReader.getResolutionScale(); + width *= resolutionScale; + height *= resolutionScale; + break; + default: + cc.log("Unknown CCB type."); + break; + } + this._size.width = width; + this._size.height = height; + return this._size; + }, + + parsePropTypeScaleLock: function (node, parent, ccbReader, propertyName) { + var x = ccbReader.readFloat(); + var y = ccbReader.readFloat(); + + var type = ccbReader.readInt(false); + + // cc.setRelativeScale(node, x, y, type, propertyName); + + if (ccbReader._animatedProps.indexOf(propertyName) > -1) { + ccbReader._animationManager.setBaseValue([x, y, type], node, propertyName); + } + + if (type === CCB_SCALETYPE_MULTIPLY_RESOLUTION) { + var resolutionScale = cc.BuilderReader.getResolutionScale(); + x *= resolutionScale; + y *= resolutionScale; + } + this._pt.x = x; + this._pt.y = y; + return this._pt; + }, + + parsePropTypeFloat: function (node, parent, ccbReader) { + return ccbReader.readFloat(); + }, + + parsePropTypeDegrees: function (node, parent, ccbReader, propertyName) { + var degrees = ccbReader.readFloat(); + if (ccbReader._animatedProps.indexOf(propertyName) > -1) { + ccbReader._animationManager.setBaseValue(degrees, node, propertyName); + } + return degrees; + }, + + parsePropTypeFloatScale: function (node, parent, ccbReader) { + var f = ccbReader.readFloat(); + + var type = ccbReader.readInt(false); + + if (type === CCB_SCALETYPE_MULTIPLY_RESOLUTION) { + f *= cc.BuilderReader.getResolutionScale(); + } + + return f; + }, + + parsePropTypeInteger: function (node, parent, ccbReader) { + return ccbReader.readInt(true); + }, + + parsePropTypeIntegerLabeled: function (node, parent, ccbReader) { + return ccbReader.readInt(true); + }, + + parsePropTypeFloatVar: function (node, parent, ccbReader) { + this._arr2[0] = ccbReader.readFloat(); + this._arr2[1] = ccbReader.readFloat(); + return this._arr2; + }, + + parsePropTypeCheck: function (node, parent, ccbReader, propertyName) { + var check = !!ccbReader._data[ccbReader._currentByte++]; + if (ccbReader._animatedProps.indexOf(propertyName) > -1) { + ccbReader._animationManager.setBaseValue(check, node, propertyName); + } + return check; + }, + + parsePropTypeSpriteFrame: function (node, parent, ccbReader, propertyName) { + var spriteSheet = ccbReader.readCachedString(); + var spriteFile = ccbReader.readCachedString(); + + var spriteFrame; + if (spriteFile) { + if (spriteSheet.length === 0) { + spriteFile = ccbReader._ccbRootPath + spriteFile; + var texture = cc.textureCache.addImage(spriteFile); + + var locContentSize = texture.getContentSize(); + var bounds = cc.rect(0, 0, locContentSize.width, locContentSize.height); + spriteFrame = new cc.SpriteFrame(texture, bounds); + } else { + var frameCache = cc.spriteFrameCache; + spriteSheet = ccbReader._ccbRootPath + spriteSheet; + //load the sprite sheet only if it is not loaded + if (ccbReader._loadedSpriteSheets.indexOf(spriteSheet) === -1) { + frameCache.addSpriteFrames(spriteSheet); + ccbReader._loadedSpriteSheets.push(spriteSheet); + } + spriteFrame = frameCache.getSpriteFrame(spriteFile); + } + if (ccbReader._animatedProps.indexOf(propertyName) > -1) { + ccbReader._animationManager.setBaseValue(spriteFrame, node, propertyName); + } + } + + return spriteFrame; + }, + + parsePropTypeAnimation: function (node, parent, ccbReader) { + var animationFile = ccbReader._ccbRootPath + ccbReader.readCachedString(); + var animation = ccbReader.readCachedString(); + + var ccAnimation = null; + + // Support for stripping relative file paths, since ios doesn't currently + // know what to do with them, since its pulling from bundle. + // Eventually this should be handled by a client side asset manager + // interface which figured out what resources to load. + // TODO Does this problem exist in C++? + animation = cc.BuilderReader.lastPathComponent(animation); + animationFile = cc.BuilderReader.lastPathComponent(animationFile); + + if (animation) { + var animationCache = cc.animationCache; + animationCache.addAnimations(animationFile); + + ccAnimation = animationCache.getAnimation(animation); + } + return ccAnimation; + }, + + parsePropTypeTexture: function (node, parent, ccbReader) { + var spriteFile = ccbReader._ccbRootPath + ccbReader.readCachedString(); + + if (spriteFile) + return cc.textureCache.addImage(spriteFile); + return null; + }, + + parsePropTypeByte: function (node, parent, ccbReader, propertyName) { + var ret = ccbReader._data[ccbReader._currentByte++]; + if (ccbReader._animatedProps.indexOf(propertyName) > -1) { + ccbReader._animationManager.setBaseValue(ret, node, propertyName); + } + return ret; + }, + + parsePropTypeColor3: function (node, parent, ccbReader, propertyName) { + var red = ccbReader._data[ccbReader._currentByte++]; + var green = ccbReader._data[ccbReader._currentByte++]; + var blue = ccbReader._data[ccbReader._currentByte++]; + var color = cc.color(red, green, blue); + if (ccbReader._animatedProps.indexOf(propertyName) > -1) { + ccbReader._animationManager.setBaseValue(color, node, propertyName); + } + return color; + }, + + parsePropTypeColor4FVar: function (node, parent, ccbReader) { + //TODO Color4F doesn't supports on HTML5 + var red = 0 | (ccbReader.readFloat() * 255); + var green = 0 | (ccbReader.readFloat() * 255); + var blue = 0 | (ccbReader.readFloat() * 255); + var alpha = ccbReader.readFloat(); + alpha = alpha <= 1 ? (0 | (alpha * 255)) : alpha; + var redVar = 0 | (ccbReader.readFloat() * 255); + var greenVar = 0 | (ccbReader.readFloat() * 255); + var blueVar = 0 | (ccbReader.readFloat() * 255); + var alphaVar = ccbReader.readFloat(); + alphaVar = alphaVar <= 1 ? (0 | (alphaVar * 255)) : alphaVar; + + this._arr2[0] = {r: red, g: green, b: blue, a: alpha}; + this._arr2[1] = {r: redVar, g: greenVar, b: blueVar, a: alphaVar}; + + return this._arr2; + }, + + parsePropTypeFlip: function (node, parent, ccbReader) { + this._arr2[0] = !!ccbReader._data[ccbReader._currentByte++]; + this._arr2[1] = !!ccbReader._data[ccbReader._currentByte++]; + + return this._arr2; + }, + + parsePropTypeBlendFunc: function (node, parent, ccbReader) { + var source = ccbReader.readInt(false); + var destination = ccbReader.readInt(false); + + return new cc.BlendFunc(source, destination); + }, + + parsePropTypeFntFile: function (node, parent, ccbReader) { + return ccbReader.readCachedString(); + }, + + parsePropTypeString: function (node, parent, ccbReader) { + return ccbReader.readCachedString(); + }, + + parsePropTypeText: function (node, parent, ccbReader) { + return ccbReader.readCachedString(); + }, + + parsePropTypeFontTTF: function (node, parent, ccbReader) { + return ccbReader.readCachedString(); + //var ttfEnding = ".ttf"; + + //TODO Fix me if it is wrong + /* If the fontTTF comes with the ".ttf" extension, prepend the absolute path. + * System fonts come without the ".ttf" extension and do not need the path prepended. */ + /*if (cc.CCBReader.endsWith(fontTTF.toLowerCase(), ttfEnding)) { + fontTTF = ccbReader.getCCBRootPath() + fontTTF; + }*/ + }, + + parsePropTypeBlock: function (node, parent, ccbReader) { + var selectorName = ccbReader.readCachedString(); + var selectorTarget = ccbReader.readInt(false); + + if (selectorTarget !== CCB_TARGETTYPE_NONE) { + var target = null; + if (!ccbReader._jsControlled) { + if (selectorTarget === CCB_TARGETTYPE_DOCUMENTROOT) { + target = ccbReader._animationManager._rootNode; + } else if (selectorTarget === CCB_TARGETTYPE_OWNER) { + target = ccbReader._owner; + } + + if (target !== null) { + if (selectorName.length > 0) { + var selMenuHandler = 0; + + //var targetAsCCBSelectorResolver = target; + if (target.onResolveCCBCCMenuItemSelector) + selMenuHandler = target.onResolveCCBCCMenuItemSelector(target, selectorName); + + if (selMenuHandler === 0) { + var ccbSelectorResolver = ccbReader._ccbSelectorResolver; + if (ccbSelectorResolver) + selMenuHandler = ccbSelectorResolver.onResolveCCBCCMenuItemSelector(target, selectorName); + } + + if (selMenuHandler === 0) { + cc.log("Skipping selector '" + selectorName + "' since no CCBSelectorResolver is present."); + } else { + return new BlockData(selMenuHandler, target); + } + } else { + cc.log("Unexpected empty selector."); + } + } else { + cc.log("Unexpected NULL target for selector."); + } + } else { + if (selectorTarget === CCB_TARGETTYPE_DOCUMENTROOT) { + ccbReader.addDocumentCallbackNode(node); + ccbReader.addDocumentCallbackName(selectorName); + ccbReader.addDocumentCallbackControlEvents(0); + } else { + ccbReader.addOwnerCallbackNode(node); + ccbReader.addOwnerCallbackName(selectorName); + ccbReader.addOwnerCallbackControlEvents(0); + } + } + } + return null; + }, + + parsePropTypeBlockCCControl: function (node, parent, ccbReader) { + var selectorName = ccbReader.readCachedString(); + var selectorTarget = ccbReader.readInt(false); + var controlEvents = ccbReader.readInt(false); + + if (selectorTarget !== CCB_TARGETTYPE_NONE) { + if (!ccbReader._jsControlled) { + var target = null; + if (selectorTarget === CCB_TARGETTYPE_DOCUMENTROOT) { + target = ccbReader._animationManager._rootNode; + } else if (selectorTarget === CCB_TARGETTYPE_OWNER) { + target = ccbReader._owner; + } + + if (target !== null) { + if (selectorName.length > 0) { + var selCCControlHandler = 0; + + if (target.onResolveCCBCCControlSelector) { + selCCControlHandler = target.onResolveCCBCCControlSelector(target, selectorName); + } + if (selCCControlHandler === 0) { + var ccbSelectorResolver = ccbReader._ccbSelectorResolver; + if (ccbSelectorResolver != null) { + selCCControlHandler = ccbSelectorResolver.onResolveCCBCCControlSelector(target, selectorName); + } + } + + if (selCCControlHandler === 0) { + cc.log("Skipping selector '" + selectorName + "' since no CCBSelectorResolver is present."); + } else { + this._blockControlData.selCCControlHandler = selCCControlHandler; + this._blockControlData.target = target; + this._blockControlData.controlEvents = controlEvents; + return this._blockControlData; + } + } else { + cc.log("Unexpected empty selector."); + } + } else { + cc.log("Unexpected NULL target for selector."); + } + } else { + if (selectorTarget === CCB_TARGETTYPE_DOCUMENTROOT) { + ccbReader.addDocumentCallbackNode(node); + ccbReader.addDocumentCallbackName(selectorName); + ccbReader.addDocumentCallbackControlEvents(controlEvents); + } else { + ccbReader.addOwnerCallbackNode(node); + ccbReader.addOwnerCallbackName(selectorName); + ccbReader.addOwnerCallbackControlEvents(controlEvents); + } + } + } + return null; + }, + + parsePropTypeCCBFile: function (node, parent, ccbReader) { + var ccbFileName = ccbReader._ccbRootPath + ccbReader.readCachedString(); + + /* Change path extension to .ccbi. */ + var ccbFileWithoutPathExtension = cc.BuilderReader.deletePathExtension(ccbFileName); + ccbFileName = ccbFileWithoutPathExtension + ".ccbi"; + + var myCCBReader = new cc.BuilderReader(ccbReader); + + var bytes = cc.loader.getRes(ccbFileName); + if (!bytes) { + var realUrl = cc.loader.getUrl(ccbFileName); + realUrl = hlddz.convertToDownloadURL(realUrl); + bytes = cc.loader.loadBinarySync(realUrl); + cc.loader.cache[ccbFileName] = bytes; + } + + myCCBReader.initWithData(bytes, ccbReader._owner); + myCCBReader._animationManager.setRootContainerSize(parent._contentSize); + myCCBReader.setAnimationManagers(ccbReader._animationManagers); + + myCCBReader._animationManager.setOwner(ccbReader._owner); + var ccbFileNode = myCCBReader.readFileWithCleanUp(false); + ccbReader.setAnimationManagers(myCCBReader._animationManagers); + + if (ccbFileNode && myCCBReader._animationManager._autoPlaySequenceId !== -1) + myCCBReader._animationManager.runAnimations(myCCBReader._animationManager._autoPlaySequenceId, 0); + + return ccbFileNode; + }, + + parsePropTypeFloatXY: function (node, parent, ccbReader) { + this._pt.x = ccbReader.readFloat(); + this._pt.y = ccbReader.readFloat(); + return this._pt; + }, + + onHandlePropTypePosition: function (node, parent, propertyName, position, ccbReader) { + if (propertyName === PROPERTY_POSITION) { + node.setPosition(position); + } else { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + } + }, + + onHandlePropTypePoint: function (node, parent, propertyName, position, ccbReader) { + if (propertyName === PROPERTY_ANCHORPOINT) { + node.setAnchorPoint(position); + } else { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + } + }, + + onHandlePropTypePointLock: function (node, parent, propertyName, pointLock, ccbReader) { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + }, + + onHandlePropTypeSize: function (node, parent, propertyName, sizeValue, ccbReader) { + if (propertyName === PROPERTY_CONTENTSIZE) { + node.setContentSize(sizeValue); + } else { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + } + }, + + onHandlePropTypeScaleLock: function (node, parent, propertyName, scaleLock, ccbReader) { + if (propertyName === PROPERTY_SCALE) { + node.setScale(scaleLock.x, scaleLock.y); + } else { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + } + }, + onHandlePropTypeFloatXY: function (node, parent, propertyName, xy, ccbReader) { + if (propertyName === PROPERTY_SKEW) { + node._skewX = xy.x; + node._skewY = xy.y; + } else { + var nameX = propertyName + "X"; + var nameY = propertyName + "Y"; + if (!node[nameX] || !node[nameY]) + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + //TODO throw an error when source code was confused + node[nameX](xy.x); + node[nameY](xy.y); + } + }, + onHandlePropTypeFloat: function (node, parent, propertyName, floatValue, ccbReader) { + //ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + // It may be a custom property, add it to custom property dictionary. + this._customProperties.setObject(floatValue, propertyName); + }, + + onHandlePropTypeDegrees: function (node, parent, propertyName, degrees, ccbReader) { + if (propertyName === PROPERTY_ROTATION) { + node.setRotation(degrees); + } else { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + } + }, + + onHandlePropTypeFloatScale: function (node, parent, propertyName, floatScale, ccbReader) { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + }, + + onHandlePropTypeInteger: function (node, parent, propertyName, integer, ccbReader) { + if (propertyName === PROPERTY_TAG) { + node.tag = integer; + } else { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + } + }, + + onHandlePropTypeIntegerLabeled: function (node, parent, propertyName, integerLabeled, ccbReader) { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + }, + + onHandlePropTypeFloatVar: function (node, parent, propertyName, floatVar, ccbReader) { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + }, + + onHandlePropTypeCheck: function (node, parent, propertyName, check, ccbReader) { + if (propertyName === PROPERTY_VISIBLE) { + node._visible = check; + } else if (propertyName === PROPERTY_IGNOREANCHORPOINTFORPOSITION) { + node._ignoreAnchorPointForPosition = check; + } else { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + } + }, + + onHandlePropTypeSpriteFrame: function (node, parent, propertyName, spriteFrame, ccbReader) { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + }, + + onHandlePropTypeAnimation: function (node, parent, propertyName, ccAnimation, ccbReader) { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + }, + + onHandlePropTypeTexture: function (node, parent, propertyName, ccTexture2D, ccbReader) { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + }, + onHandlePropTypeByte: function (node, parent, propertyName, byteValue, ccbReader) { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + }, + onHandlePropTypeColor3: function (node, parent, propertyName, ccColor3B, ccbReader) { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + }, + onHandlePropTypeColor4FVar: function (node, parent, propertyName, ccColor4FVar, ccbReader) { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + }, + onHandlePropTypeFlip: function (node, parent, propertyName, flip, ccbReader) { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + }, + onHandlePropTypeBlendFunc: function (node, parent, propertyName, ccBlendFunc, ccbReader) { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + }, + onHandlePropTypeFntFile: function (node, parent, propertyName, fntFile, ccbReader) { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + }, + onHandlePropTypeString: function (node, parent, propertyName, strValue, ccbReader) { + //ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + // It may be a custom property, add it to custom property dictionary. + this._customProperties.setObject(strValue, propertyName); + }, + onHandlePropTypeText: function (node, parent, propertyName, textValue, ccbReader) { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + }, + onHandlePropTypeFontTTF: function (node, parent, propertyName, fontTTF, ccbReader) { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + }, + onHandlePropTypeBlock: function (node, parent, propertyName, blockData, ccbReader) { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + }, + onHandlePropTypeBlockCCControl: function (node, parent, propertyName, blockCCControlData, ccbReader) { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + }, + onHandlePropTypeCCBFile: function (node, parent, propertyName, ccbFileNode, ccbReader) { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + } +}); + +cc.NodeLoader.loader = function () { + return new cc.NodeLoader(); +}; diff --git a/frameworks/cocos2d-html5/extensions/ccb-reader/CCNodeLoaderLibrary.js b/frameworks/cocos2d-html5/extensions/ccb-reader/CCNodeLoaderLibrary.js new file mode 100644 index 0000000..487dc7b --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccb-reader/CCNodeLoaderLibrary.js @@ -0,0 +1,101 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +cc.NodeLoaderLibrary = cc.Class.extend({ + _ccNodeLoaders:null, + + ctor:function(){ + this._ccNodeLoaders = {}; + }, + + registerDefaultCCNodeLoaders:function(){ + this.registerCCNodeLoader("CCNode", cc.NodeLoader.loader()); + this.registerCCNodeLoader("CCLayer", cc.LayerLoader.loader()); + this.registerCCNodeLoader("CCLayerColor", cc.LayerColorLoader.loader()); + this.registerCCNodeLoader("CCLayerGradient", cc.LayerGradientLoader.loader()); + this.registerCCNodeLoader("CCSprite", cc.SpriteLoader.loader()); + this.registerCCNodeLoader("CCLabelBMFont", cc.LabelBMFontLoader.loader()); + this.registerCCNodeLoader("CCLabelTTF", cc.LabelTTFLoader.loader()); + this.registerCCNodeLoader("CCScale9Sprite", cc.Scale9SpriteLoader.loader()); + this.registerCCNodeLoader("CCScrollView", cc.ScrollViewLoader.loader()); + this.registerCCNodeLoader("CCBFile", cc.BuilderFileLoader.loader()); + this.registerCCNodeLoader("CCMenu", cc.MenuLoader.loader()); + this.registerCCNodeLoader("CCMenuItemImage", cc.MenuItemImageLoader.loader()); + this.registerCCNodeLoader("CCControlButton", cc.ControlButtonLoader.loader()); + this.registerCCNodeLoader("CCParticleSystemQuad", cc.ParticleSystemLoader.loader()); + }, + + registerCCNodeLoader:function(className,ccNodeLoader){ + this._ccNodeLoaders[className] = ccNodeLoader; + }, + + unregisterCCNodeLoader:function(className){ + if(this._ccNodeLoaders[className]){ + delete this._ccNodeLoaders[className]; + } + }, + + getCCNodeLoader:function(className){ + if(this._ccNodeLoaders[className]) + return this._ccNodeLoaders[className]; + return null; + }, + + purge:function(releaseCCNodeLoaders){ + if(releaseCCNodeLoaders) { + for(var className in this._ccNodeLoaders) { + delete this._ccNodeLoaders[className]; + } + } + this._ccNodeLoaders = {}; + } +}); + +cc.NodeLoaderLibrary.sSharedCCNodeLoaderLibrary = null; +cc.NodeLoaderLibrary.library = function(){ + return new cc.NodeLoaderLibrary(); +}; + +cc.NodeLoaderLibrary.sharedCCNodeLoaderLibrary = function(){ + if(cc.NodeLoaderLibrary.sSharedCCNodeLoaderLibrary == null) { + cc.NodeLoaderLibrary.sSharedCCNodeLoaderLibrary = new cc.NodeLoaderLibrary(); + cc.NodeLoaderLibrary.sSharedCCNodeLoaderLibrary.registerDefaultCCNodeLoaders(); + } + return cc.NodeLoaderLibrary.sSharedCCNodeLoaderLibrary; +}; + +cc.NodeLoaderLibrary.purgeSharedCCNodeLoaderLibrary = function(){ + cc.NodeLoaderLibrary.sSharedCCNodeLoaderLibrary = null; +}; + +cc.NodeLoaderLibrary.newDefaultCCNodeLoaderLibrary = function(){ + var ccNodeLoaderLibrary = cc.NodeLoaderLibrary.library(); + ccNodeLoaderLibrary.registerDefaultCCNodeLoaders(); + return ccNodeLoaderLibrary; +}; + + + diff --git a/frameworks/cocos2d-html5/extensions/ccb-reader/CCSpriteLoader.js b/frameworks/cocos2d-html5/extensions/ccb-reader/CCSpriteLoader.js new file mode 100644 index 0000000..857b2bb --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccb-reader/CCSpriteLoader.js @@ -0,0 +1,544 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +var PROPERTY_FLIP = "flip"; +var PROPERTY_DISPLAYFRAME = "displayFrame"; +var PROPERTY_COLOR = "color"; +var PROPERTY_OPACITY = "opacity"; +var PROPERTY_BLENDFUNC = "blendFunc"; + +cc.SpriteLoader = cc.NodeLoader.extend({ + _createCCNode:function (parent, ccbReader) { + return new cc.Sprite(); + }, + + onHandlePropTypeColor3:function (node, parent, propertyName, ccColor3B, ccbReader) { + if (propertyName === PROPERTY_COLOR) { + if(ccColor3B.r !== 255 || ccColor3B.g !== 255 || ccColor3B.b !== 255){ + node.setColor(ccColor3B); + } + } else { + cc.NodeLoader.prototype.onHandlePropTypeColor3.call(this, node, parent, propertyName, ccColor3B, ccbReader); + } + }, + onHandlePropTypeByte:function (node, parent, propertyName, byteValue, ccbReader) { + if (propertyName === PROPERTY_OPACITY) { + node.setOpacity(byteValue); + } else { + cc.NodeLoader.prototype.onHandlePropTypeByte.call(this, node, parent, propertyName, byteValue, ccbReader); + } + }, + onHandlePropTypeBlendFunc:function (node, parent, propertyName, ccbBlendFunc, ccbReader) { + if (propertyName === PROPERTY_BLENDFUNC) { + node.setBlendFunc(ccbBlendFunc); + } else { + cc.NodeLoader.prototype.onHandlePropTypeBlendFunc.call(this, node, parent, propertyName, ccbBlendFunc, ccbReader); + } + }, + onHandlePropTypeSpriteFrame:function (node, parent, propertyName, ccSpriteFrame, ccbReader) { + if (propertyName === PROPERTY_DISPLAYFRAME) { + if(ccSpriteFrame) + node.setSpriteFrame(ccSpriteFrame); + else + cc.log("ERROR: SpriteFrame is null"); + } else { + cc.NodeLoader.prototype.onHandlePropTypeSpriteFrame.call(this, node, parent, propertyName, ccSpriteFrame, ccbReader); + } + }, + onHandlePropTypeFlip:function (node, parent, propertyName, flip, ccbReader) { + if (propertyName === PROPERTY_FLIP) { + node.setFlippedX(flip[0]); + node.setFlippedY(flip[1]); + } else { + cc.NodeLoader.prototype.onHandlePropTypeFlip.call(this, node, parent, propertyName, flip, ccbReader); + } + } +}); + +cc.SpriteLoader.loader = function () { + return new cc.SpriteLoader(); +}; + +var PROPERTY_TOUCH_ENABLED = "touchEnabled"; +var PROPERTY_IS_TOUCH_ENABLED = "isTouchEnabled"; +var PROPERTY_ACCELEROMETER_ENABLED = "accelerometerEnabled"; +var PROPERTY_IS_ACCELEROMETER_ENABLED = "isAccelerometerEnabled"; +var PROPERTY_IS_MOUSE_ENABLED = "isMouseEnabled"; +var PROPERTY_MOUSE_ENABLED = "mouseEnabled"; +var PROPERTY_KEYBOARD_ENABLED = "keyboardEnabled"; +var PROPERTY_IS_KEYBOARD_ENABLED = "isKeyboardEnabled"; + +cc.LayerLoader = cc.NodeLoader.extend({ + _createCCNode:function (parent, ccbReader) { + + var layer = new cc.Layer(); + + layer.setContentSize(0,0); + + return layer; + }, + onHandlePropTypeCheck:function (node, parent, propertyName, check, ccbReader) { + if (propertyName === PROPERTY_TOUCH_ENABLED || propertyName === PROPERTY_IS_TOUCH_ENABLED) { + //node.setTouchEnabled(check); + } else if (propertyName === PROPERTY_ACCELEROMETER_ENABLED || propertyName === PROPERTY_IS_ACCELEROMETER_ENABLED) { + //node.setAccelerometerEnabled(check); + } else if (propertyName === PROPERTY_MOUSE_ENABLED || propertyName === PROPERTY_IS_MOUSE_ENABLED ) { + //node.setMouseEnabled(check); + } else if (propertyName === PROPERTY_KEYBOARD_ENABLED || propertyName === PROPERTY_IS_KEYBOARD_ENABLED) { + // TODO XXX + if(node.setKeyboardEnabled && !cc.sys.isNative) { + node.setKeyboardEnabled(check); + } else { + cc.log("The property '" + PROPERTY_IS_KEYBOARD_ENABLED + "' is not supported!"); + // This comes closest: ((CCLayer *)pNode).setKeypadEnabled(pCheck); + } + } else { + cc.NodeLoader.prototype.onHandlePropTypeCheck.call(this, node, parent, propertyName, check, ccbReader); + } + } +}); + +cc.LayerLoader.loader = function () { + return new cc.LayerLoader(); +}; + + +cc.LayerColorLoader = cc.LayerLoader.extend({ + _createCCNode:function (parent, ccbReader) { + return new cc.LayerColor(); + }, + + onHandlePropTypeColor3:function (node, parent, propertyName, ccColor3B, ccbReader) { + if (propertyName === PROPERTY_COLOR) { + node.setColor(ccColor3B); + } else { + cc.LayerLoader.prototype.onHandlePropTypeColor3.call(this, node, parent, propertyName, ccColor3B, ccbReader); + } + }, + onHandlePropTypeByte:function (node, parent, propertyName, byteValue, ccbReader) { + if (propertyName === PROPERTY_OPACITY) { + node.setOpacity(byteValue); + } else { + cc.LayerLoader.prototype.onHandlePropTypeByte.call(this, node, parent, propertyName, byteValue, ccbReader); + } + }, + onHandlePropTypeBlendFunc:function (node, parent, propertyName, ccBlendFunc, ccbReader) { + if (propertyName === PROPERTY_BLENDFUNC) { + node.setBlendFunc(ccBlendFunc); + } else { + cc.LayerLoader.prototype.onHandlePropTypeBlendFunc.call(this, node, parent, propertyName, ccBlendFunc, ccbReader); + } + } +}); + +cc.LayerColorLoader.loader = function () { + return new cc.LayerColorLoader(); +}; + +var PROPERTY_STARTCOLOR = "startColor"; +var PROPERTY_ENDCOLOR = "endColor"; +var PROPERTY_STARTOPACITY = "startOpacity"; +var PROPERTY_ENDOPACITY = "endOpacity"; +var PROPERTY_VECTOR = "vector"; + +cc.LayerGradientLoader = cc.LayerLoader.extend({ + _createCCNode:function (parent, ccbReader) { + return new cc.LayerGradient(); + }, + onHandlePropTypeColor3:function (node, parent, propertyName, ccColor3B, ccbReader) { + if (propertyName === PROPERTY_STARTCOLOR) { + node.setStartColor(ccColor3B); + } else if (propertyName === PROPERTY_ENDCOLOR) { + node.setEndColor(ccColor3B); + } else { + cc.LayerLoader.prototype.onHandlePropTypeColor3.call(this, node, parent, propertyName, ccColor3B, ccbReader); + } + }, + onHandlePropTypeByte:function (node, parent, propertyName, byteValue, ccbReader) { + if (propertyName === PROPERTY_STARTOPACITY) { + node.setStartOpacity(byteValue); + } else if (propertyName === PROPERTY_ENDOPACITY) { + node.setEndOpacity(byteValue); + } else { + cc.LayerLoader.prototype.onHandlePropTypeByte.call(this, node, parent, propertyName, byteValue, ccbReader); + } + }, + onHandlePropTypePoint:function (node, parent, propertyName, point, ccbReader) { + if (propertyName === PROPERTY_VECTOR) { + node.setVector(point); + // TODO Not passed along the ccbi file. + // node.setCompressedInterpolation(true); + } else { + cc.LayerLoader.prototype.onHandlePropTypePoint.call(this, node, parent, propertyName, point, ccbReader); + } + }, + onHandlePropTypeBlendFunc:function (node, parent, propertyName, ccBlendFunc, ccbReader) { + if (propertyName === PROPERTY_BLENDFUNC) { + node.setBlendFunc(ccBlendFunc); + } else { + cc.LayerLoader.prototype.onHandlePropTypeBlendFunc.call(this, node, parent, propertyName, ccBlendFunc, ccbReader); + } + } +}); + +cc.LayerGradientLoader.loader = function () { + return new cc.LayerGradientLoader(); +}; + +cc.MenuLoader = cc.LayerLoader.extend({ + _createCCNode:function (parent, ccbReader) { + var menu = new cc.Menu(); + + menu.setContentSize(0,0); + + return menu; + } +}); + +cc.MenuLoader.loader = function () { + return new cc.MenuLoader(); +}; + +var PROPERTY_BLOCK = "block"; +var PROPERTY_ISENABLED = "isEnabled"; + +cc.MenuItemLoader = cc.NodeLoader.extend({ + _createCCNode:function (parent, ccbReader) { + return null; + }, + + onHandlePropTypeBlock:function (node, parent, propertyName, blockData, ccbReader) { + if (propertyName === PROPERTY_BLOCK) { + if (null != blockData) { // Add this condition to allow CCMenuItemImage without target/selector predefined + node.setTarget(blockData.selMenuHander, blockData.target); + } + } else { + cc.NodeLoader.prototype.onHandlePropTypeBlock.call(this, node, parent, propertyName, blockData, ccbReader); + } + }, + onHandlePropTypeCheck:function (node, parent, propertyName, check, ccbReader) { + if (propertyName === PROPERTY_ISENABLED) { + node.setEnabled(check); + } else { + cc.NodeLoader.prototype.onHandlePropTypeCheck.call(this, node, parent, propertyName, check, ccbReader); + } + } +}); + +var PROPERTY_NORMALDISPLAYFRAME = "normalSpriteFrame"; +var PROPERTY_SELECTEDDISPLAYFRAME = "selectedSpriteFrame"; +var PROPERTY_DISABLEDDISPLAYFRAME = "disabledSpriteFrame"; + +cc.MenuItemImageLoader = cc.MenuItemLoader.extend({ + _createCCNode:function (parent, ccbReader) { + return new cc.MenuItemImage(); + }, + + onHandlePropTypeSpriteFrame:function (node, parent, propertyName, spriteFrame, ccbReader) { + if (propertyName === PROPERTY_NORMALDISPLAYFRAME) { + if (spriteFrame != null) { + node.setNormalSpriteFrame(spriteFrame); + } + } else if (propertyName === PROPERTY_SELECTEDDISPLAYFRAME) { + if (spriteFrame != null) { + node.setSelectedSpriteFrame(spriteFrame); + } + } else if (propertyName === PROPERTY_DISABLEDDISPLAYFRAME) { + if (spriteFrame != null) { + node.setDisabledSpriteFrame(spriteFrame); + } + } else { + cc.MenuItemLoader.prototype.onHandlePropTypeSpriteFrame.call(this, node, parent, propertyName, spriteFrame, ccbReader); + } + } +}); + +cc.MenuItemImageLoader.loader = function () { + return new cc.MenuItemImageLoader(); +}; + +var PROPERTY_FONTNAME = "fontName"; +var PROPERTY_FONTSIZE = "fontSize"; +var PROPERTY_HORIZONTALALIGNMENT = "horizontalAlignment"; +var PROPERTY_VERTICALALIGNMENT = "verticalAlignment"; +var PROPERTY_STRING = "string"; +var PROPERTY_DIMENSIONS = "dimensions"; + +cc.LabelTTFLoader = cc.NodeLoader.extend({ + _createCCNode:function (parent, ccbReader) { + return new cc.LabelTTF(); + }, + onHandlePropTypeColor3:function (node, parent, propertyName, ccColor3B, ccbReader) { + if (propertyName === PROPERTY_COLOR) { + if(ccColor3B.r !== 255 || ccColor3B.g !== 255 || ccColor3B.b !== 255){ + node.setColor(ccColor3B); + } + } else { + cc.NodeLoader.prototype.onHandlePropTypeColor3.call(this, node, parent, propertyName, ccColor3B, ccbReader); + } + }, + onHandlePropTypeByte:function (node, parent, propertyName, byteValue, ccbReader) { + if (propertyName === PROPERTY_OPACITY) { + node.setOpacity(byteValue); + } else { + cc.NodeLoader.prototype.onHandlePropTypeByte.call(this, node, parent, propertyName, byteValue, ccbReader); + } + }, + onHandlePropTypeBlendFunc:function (node, parent, propertyName, ccBlendFunc, ccbReader) { + if (propertyName === PROPERTY_BLENDFUNC) { + node.setBlendFunc(ccBlendFunc); + } else { + cc.NodeLoader.prototype.onHandlePropTypeBlendFunc.call(this, node, parent, propertyName, ccBlendFunc, ccbReader); + } + }, + onHandlePropTypeFontTTF:function (node, parent, propertyName, fontTTF, ccbReader) { + if (propertyName === PROPERTY_FONTNAME) { + node.setFontName(fontTTF); + } else { + cc.NodeLoader.prototype.onHandlePropTypeFontTTF.call(this, node, parent, propertyName, fontTTF, ccbReader); + } + }, + onHandlePropTypeText:function (node, parent, propertyName, textValue, ccbReader) { + if (propertyName === PROPERTY_STRING) { + node.setString(textValue); + } else { + cc.NodeLoader.prototype.onHandlePropTypeText.call(this, node, parent, propertyName, textValue, ccbReader); + } + }, + onHandlePropTypeFloatScale:function (node, parent, propertyName, floatScale, ccbReader) { + if (propertyName === PROPERTY_FONTSIZE) { + node.setFontSize(floatScale); + } else { + cc.NodeLoader.prototype.onHandlePropTypeFloatScale.call(this, node, parent, propertyName, floatScale, ccbReader); + } + }, + onHandlePropTypeIntegerLabeled:function (node, parent, propertyName, integerLabeled, ccbReader) { + if (propertyName === PROPERTY_HORIZONTALALIGNMENT) { + node.setHorizontalAlignment(integerLabeled); + } else if (propertyName === PROPERTY_VERTICALALIGNMENT) { + node.setVerticalAlignment(integerLabeled); + } else { + cc.NodeLoader.prototype.onHandlePropTypeIntegerLabeled.call(this, node, parent, propertyName, integerLabeled, ccbReader); + } + }, + onHandlePropTypeSize:function (node, parent, propertyName, size, ccbReader) { + if (propertyName === PROPERTY_DIMENSIONS) { + node.setDimensions(size); + } else { + cc.NodeLoader.prototype.onHandlePropTypeSize.call(this, node, parent, propertyName, size, ccbReader); + } + } +}); + +cc.LabelTTFLoader.loader = function () { + return new cc.LabelTTFLoader(); +}; + +var PROPERTY_FNTFILE = "fntFile"; + +cc.LabelBMFontLoader = cc.NodeLoader.extend({ + _createCCNode:function (parent, ccbReader) { + return new cc.LabelBMFont(); + }, + + onHandlePropTypeColor3:function (node, parent, propertyName, ccColor3B, ccbReader) { + if (propertyName === PROPERTY_COLOR) { + if(ccColor3B.r !== 255 || ccColor3B.g !== 255 || ccColor3B.b !== 255){ + node.setColor(ccColor3B); + } + } else { + cc.NodeLoader.prototype.onHandlePropTypeColor3.call(this, node, parent, propertyName, ccColor3B, ccbReader); + } + }, + onHandlePropTypeByte:function (node, parent, propertyName, byteValue, ccbReader) { + if (propertyName === PROPERTY_OPACITY) { + node.setOpacity(byteValue); + } else { + cc.NodeLoader.prototype.onHandlePropTypeByte.call(this, node, parent, propertyName, byteValue, ccbReader); + } + }, + onHandlePropTypeBlendFunc:function (node, parent, propertyName, ccBlendFunc, ccbReader) { + if (propertyName === PROPERTY_BLENDFUNC) { + node.setBlendFunc(ccBlendFunc); + } else { + cc.NodeLoader.prototype.onHandlePropTypeBlendFunc.call(this, node, parent, propertyName, ccBlendFunc, ccbReader); + } + }, + onHandlePropTypeFntFile:function (node, parent, propertyName, fntFile, ccbReader) { + if (propertyName === PROPERTY_FNTFILE) { + node.setFntFile(fntFile); + } else { + cc.NodeLoader.prototype.onHandlePropTypeFntFile.call(this, node, parent, propertyName, fntFile, ccbReader); + } + }, + onHandlePropTypeText:function (node, parent, propertyName, textValue, ccbReader) { + if (propertyName === PROPERTY_STRING) { + node.setString(textValue); + } else { + cc.NodeLoader.prototype.onHandlePropTypeText.call(this, node, parent, propertyName, textValue, ccbReader); + } + } +}); + +cc.LabelBMFontLoader.loader = function () { + return new cc.LabelBMFontLoader(); +}; + +var PROPERTY_EMITERMODE = "emitterMode"; +var PROPERTY_POSVAR = "posVar"; +var PROPERTY_EMISSIONRATE = "emissionRate"; +var PROPERTY_DURATION = "duration"; +var PROPERTY_TOTALPARTICLES = "totalParticles"; +var PROPERTY_LIFE = "life"; +var PROPERTY_STARTSIZE = "startSize"; +var PROPERTY_ENDSIZE = "endSize"; +var PROPERTY_STARTSPIN = "startSpin"; +var PROPERTY_ENDSPIN = "endSpin"; +var PROPERTY_ANGLE = "angle"; +var PROPERTY_GRAVITY = "gravity"; +var PROPERTY_SPEED = "speed"; +var PROPERTY_TANGENTIALACCEL = "tangentialAccel"; +var PROPERTY_RADIALACCEL = "radialAccel"; +var PROPERTY_TEXTURE = "texture"; +var PROPERTY_STARTRADIUS = "startRadius"; +var PROPERTY_ENDRADIUS = "endRadius"; +var PROPERTY_ROTATEPERSECOND = "rotatePerSecond"; + +cc.ParticleSystemLoader = cc.NodeLoader.extend({ + _createCCNode:function (parent, ccbReader) { + return new cc.ParticleSystem(); + }, + + onHandlePropTypeIntegerLabeled:function (node, parent, propertyName, integerLabeled, ccbReader) { + if (propertyName === PROPERTY_EMITERMODE) { + node.setEmitterMode(integerLabeled); + } else { + cc.NodeLoader.prototype.onHandlePropTypeIntegerLabeled.call(this, node, parent, propertyName, integerLabeled, ccbReader); + } + }, + onHandlePropTypePoint:function (node, parent, propertyName, point, ccbReader) { + if (propertyName === PROPERTY_POSVAR) { + node.setPosVar(point); + } else if (propertyName === PROPERTY_GRAVITY) { + node.setGravity(point); + } else { + cc.NodeLoader.prototype.onHandlePropTypePoint.call(this, node, parent, propertyName, point, ccbReader); + } + }, + onHandlePropTypeFloat:function (node, parent, propertyName, floatValue, ccbReader) { + if (propertyName === PROPERTY_EMISSIONRATE) { + node.setEmissionRate(floatValue); + } else if (propertyName === PROPERTY_DURATION) { + node.setDuration(floatValue); + } else { + cc.NodeLoader.prototype.onHandlePropTypeFloat.call(this, node, parent, propertyName, floatValue, ccbReader); + } + }, + onHandlePropTypeInteger:function (node, parent, propertyName, integerValue, ccbReader) { + if (propertyName === PROPERTY_TOTALPARTICLES) { + node.setTotalParticles(integerValue); + } else { + cc.NodeLoader.prototype.onHandlePropTypeInteger.call(this, node, parent, propertyName, integerValue, ccbReader); + } + }, + onHandlePropTypeFloatVar:function (node, parent, propertyName, floatVar, ccbReader) { + if (propertyName === PROPERTY_LIFE) { + node.setLife(floatVar[0]); + node.setLifeVar(floatVar[1]); + } else if (propertyName === PROPERTY_STARTSIZE) { + node.setStartSize(floatVar[0]); + node.setStartSizeVar(floatVar[1]); + } else if (propertyName === PROPERTY_ENDSIZE) { + node.setEndSize(floatVar[0]); + node.setEndSizeVar(floatVar[1]); + } else if (propertyName === PROPERTY_STARTSPIN) { + node.setStartSpin(floatVar[0]); + node.setStartSpinVar(floatVar[1]); + } else if (propertyName === PROPERTY_ENDSPIN) { + node.setEndSpin(floatVar[0]); + node.setEndSpinVar(floatVar[1]); + } else if (propertyName === PROPERTY_ANGLE) { + node.setAngle(floatVar[0]); + node.setAngleVar(floatVar[1]); + } else if (propertyName === PROPERTY_SPEED) { + node.setSpeed(floatVar[0]); + node.setSpeedVar(floatVar[1]); + } else if (propertyName === PROPERTY_TANGENTIALACCEL) { + node.setTangentialAccel(floatVar[0]); + node.setTangentialAccelVar(floatVar[1]); + } else if (propertyName === PROPERTY_RADIALACCEL) { + node.setRadialAccel(floatVar[0]); + node.setRadialAccelVar(floatVar[1]); + } else if (propertyName === PROPERTY_STARTRADIUS) { + node.setStartRadius(floatVar[0]); + node.setStartRadiusVar(floatVar[1]); + } else if (propertyName === PROPERTY_ENDRADIUS) { + node.setEndRadius(floatVar[0]); + node.setEndRadiusVar(floatVar[1]); + } else if (propertyName === PROPERTY_ROTATEPERSECOND) { + node.setRotatePerSecond(floatVar[0]); + node.setRotatePerSecondVar(floatVar[1]); + } else { + cc.NodeLoader.prototype.onHandlePropTypeFloatVar.call(this, node, parent, propertyName, floatVar, ccbReader); + } + }, + onHandlePropTypeColor4FVar:function (node, parent, propertyName, ccColor4FVar, ccbReader) { + if (propertyName === PROPERTY_STARTCOLOR) { + node.setStartColor(ccColor4FVar[0]); + node.setStartColorVar(ccColor4FVar[1]); + } else if (propertyName === PROPERTY_ENDCOLOR) { + node.setEndColor(ccColor4FVar[0]); + node.setEndColorVar(ccColor4FVar[1]); + } else { + cc.NodeLoader.prototype.onHandlePropTypeColor4FVar.call(this, node, parent, propertyName, ccColor4FVar, ccbReader); + } + }, + onHandlePropTypeBlendFunc:function (node, parent, propertyName, ccBlendFunc, ccbReader) { + if (propertyName === PROPERTY_BLENDFUNC) { + node.setBlendFunc(ccBlendFunc); + } else { + cc.NodeLoader.prototype.onHandlePropTypeBlendFunc.call(this, node, parent, propertyName, ccBlendFunc, ccbReader); + } + }, + onHandlePropTypeTexture:function (node, parent, propertyName, ccTexture2D, ccbReader) { + if (propertyName === PROPERTY_TEXTURE) { + node.setTexture(ccTexture2D); + } else { + cc.NodeLoader.prototype.onHandlePropTypeTexture.call(this, node, parent, propertyName, ccTexture2D, ccbReader); + } + } +}); + +cc.ParticleSystemLoader.loader = function () { + return new cc.ParticleSystemLoader(); +}; + + + + + + + diff --git a/frameworks/cocos2d-html5/extensions/ccpool/CCPool.js b/frameworks/cocos2d-html5/extensions/ccpool/CCPool.js new file mode 100644 index 0000000..2159c09 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccpool/CCPool.js @@ -0,0 +1,146 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + *

+ * cc.pool is a singleton object serves as an object cache pool.
+ * It can helps you to improve your game performance for objects which need frequent release and recreate operations
+ * Some common use case is : + * 1. Bullets in game (die very soon, massive creation and recreation, no side effect on other objects) + * 2. Blocks in candy crash (massive creation and recreation) + * etc... + *

+ * + * @example + * var sp = new cc.Sprite("a.png"); + * this.addChild(sp); + * cc.pool.putInPool(sp); + * + * cc.pool.getFromPool(cc.Sprite, "a.png"); + * @class + * @name cc.pool + */ +cc.pool = /** @lends cc.pool# */{ + _pool: {}, + + _releaseCB: function () { + this.release(); + }, + + _autoRelease: function (obj) { + var running = obj._running === undefined ? false : !obj._running; + cc.director.getScheduler().schedule(this._releaseCB, obj, 0, 0, 0, running) + }, + + /** + * Put the obj in pool + * @param obj + */ + putInPool: function (obj) { + var pid = obj.constructor.prototype['__pid']; + if (!pid) { + var desc = {writable: true, enumerable: false, configurable: true}; + desc.value = ClassManager.getNewID(); + Object.defineProperty(obj.constructor.prototype, '__pid', desc); + } + if (!this._pool[pid]) { + this._pool[pid] = []; + } + // JSB retain to avoid being auto released + obj.retain && obj.retain(); + // User implementation for disable the object + obj.unuse && obj.unuse(); + this._pool[pid].push(obj); + }, + + /** + * Check if this kind of obj has already in pool + * @param objClass + * @returns {boolean} if this kind of obj is already in pool return true,else return false; + */ + hasObject: function (objClass) { + var pid = objClass.prototype['__pid']; + var list = this._pool[pid]; + if (!list || list.length === 0) { + return false; + } + return true; + }, + + /** + * Remove the obj if you want to delete it; + * @param obj + */ + removeObject: function (obj) { + var pid = obj.constructor.prototype['__pid']; + if (pid) { + var list = this._pool[pid]; + if (list) { + for (var i = 0; i < list.length; i++) { + if (obj === list[i]) { + // JSB release to avoid memory leak + obj.release && obj.release(); + list.splice(i, 1); + } + } + } + } + }, + + /** + * Get the obj from pool + * @param args + * @returns {*} call the reuse function an return the obj + */ + getFromPool: function (objClass/*,args*/) { + if (this.hasObject(objClass)) { + var pid = objClass.prototype['__pid']; + var list = this._pool[pid]; + var args = Array.prototype.slice.call(arguments); + args.shift(); + var obj = list.pop(); + // User implementation for re-enable the object + obj.reuse && obj.reuse.apply(obj, args); + // JSB release to avoid memory leak + cc.sys.isNative && obj.release && this._autoRelease(obj); + return obj; + } + }, + + /** + * remove all objs in pool and reset the pool + */ + drainAllPools: function () { + for (var i in this._pool) { + for (var j = 0; j < this._pool[i].length; j++) { + var obj = this._pool[i][j]; + // JSB release to avoid memory leak + obj.release && obj.release(); + } + } + this._pool = {}; + } +}; diff --git a/frameworks/cocos2d-html5/extensions/ccui/base-classes/CCProtectedNode.js b/frameworks/cocos2d-html5/extensions/ccui/base-classes/CCProtectedNode.js new file mode 100644 index 0000000..1925572 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/base-classes/CCProtectedNode.js @@ -0,0 +1,305 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * A class inhert from cc.Node, use for saving some protected children in other list. + * @class + * @extends cc.Node + */ +cc.ProtectedNode = cc.Node.extend(/** @lends cc.ProtectedNode# */{ + _protectedChildren: null, + _reorderProtectedChildDirty: false, + + _insertProtectedChild: function (child, z) { + this._reorderProtectedChildDirty = true; + this._protectedChildren.push(child); + child._setLocalZOrder(z); + }, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @function + */ + ctor: function () { + cc.Node.prototype.ctor.call(this); + this._protectedChildren = []; + }, + + visit: function (parent) { + var cmd = this._renderCmd, parentCmd = parent ? parent._renderCmd : null; + + // quick return if not visible + if (!this._visible) { + cmd._propagateFlagsDown(parentCmd); + return; + } + + var renderer = cc.renderer; + var i, children = this._children, len = children.length, child; + var j, pChildren = this._protectedChildren, pLen = pChildren.length, pChild; + + cmd.visit(parentCmd); + + var locGrid = this.grid; + if (locGrid && locGrid._active) + locGrid.beforeDraw(); + + if (this._reorderChildDirty) this.sortAllChildren(); + if (this._reorderProtectedChildDirty) this.sortAllProtectedChildren(); + + // draw children zOrder < 0 + for (i = 0; i < len; i++) { + child = children[i]; + if (child._localZOrder < 0) { + child.visit(this); + } + else { + break; + } + } + for (j = 0; j < pLen; j++) { + pChild = pChildren[j]; + if (pChild && pChild._localZOrder < 0) { + cmd._changeProtectedChild(pChild); + pChild.visit(this); + } + else + break; + } + + renderer.pushRenderCommand(cmd); + + for (; i < len; i++) { + children[i].visit(this); + } + for (; j < pLen; j++) { + pChild = pChildren[j]; + if (!pChild) continue; + cmd._changeProtectedChild(pChild); + pChild.visit(this); + } + + if (locGrid && locGrid._active) + locGrid.afterDraw(this); + + cmd._dirtyFlag = 0; + }, + + /** + *

+ * Adds a child to the container with z order and tag
+ * If the child is added to a 'running' node, then 'onEnter' and 'onEnterTransitionDidFinish' will be called immediately.
+ *

+ * @param {cc.Node} child A child node + * @param {Number} [localZOrder] Z order for drawing priority. Please refer to `setLocalZOrder(int)` + * @param {Number} [tag] An integer to identify the node easily. Please refer to `setTag(int)` + */ + addProtectedChild: function (child, localZOrder, tag) { + cc.assert(child != null, "child must be non-nil"); + cc.assert(!child.parent, "child already added. It can't be added again"); + + localZOrder = localZOrder || child.getLocalZOrder(); + if (tag) + child.setTag(tag); + + this._insertProtectedChild(child, localZOrder); + child.setParent(this); + child.setOrderOfArrival(cc.s_globalOrderOfArrival); + + if (this._running) { + child._performRecursive(cc.Node._stateCallbackType.onEnter); + // prevent onEnterTransitionDidFinish to be called twice when a node is added in onEnter + if (this._isTransitionFinished) + child._performRecursive(cc.Node._stateCallbackType.onEnterTransitionDidFinish); + } + if (this._cascadeColorEnabled) + this._renderCmd.setCascadeColorEnabledDirty(); + if (this._cascadeOpacityEnabled) + this._renderCmd.setCascadeOpacityEnabledDirty(); + }, + + /** + * Gets a child from the container with its tag + * @param {Number} tag An identifier to find the child node. + * @return {cc.Node} a Node object whose tag equals to the input parameter + */ + getProtectedChildByTag: function (tag) { + cc.assert(tag !== cc.NODE_TAG_INVALID, "Invalid tag"); + var locChildren = this._protectedChildren; + for (var i = 0, len = locChildren.length; i < len; i++) + if (locChildren.getTag() === tag) + return locChildren[i]; + return null; + }, + + /** + * Removes a child from the container. It will also cleanup all running actions depending on the cleanup parameter. + * @param {cc.Node} child The child node which will be removed. + * @param {Boolean} [cleanup=true] true if all running actions and callbacks on the child node will be cleanup, false otherwise. + */ + removeProtectedChild: function (child, cleanup) { + if (cleanup == null) + cleanup = true; + var locChildren = this._protectedChildren; + if (locChildren.length === 0) + return; + var idx = locChildren.indexOf(child); + if (idx > -1) { + if (this._running) { + child._performRecursive(cc.Node._stateCallbackType.onExitTransitionDidStart); + child._performRecursive(cc.Node._stateCallbackType.onExit); + } + + // If you don't do cleanup, the child's actions will not get removed and the + // its scheduledSelectors_ dict will not get released! + if (cleanup) + child._performRecursive(cc.Node._stateCallbackType.cleanup); + + // set parent nil at the end + child.setParent(null); + locChildren.splice(idx, 1); + } + }, + + /** + * Removes a child from the container by tag value.
+ * It will also cleanup all running actions depending on the cleanup parameter + * @param {Number} tag + * @param {Boolean} [cleanup=true] + */ + removeProtectedChildByTag: function (tag, cleanup) { + cc.assert(tag !== cc.NODE_TAG_INVALID, "Invalid tag"); + + if (cleanup == null) + cleanup = true; + + var child = this.getProtectedChildByTag(tag); + + if (child == null) + cc.log("cocos2d: removeChildByTag(tag = %d): child not found!", tag); + else + this.removeProtectedChild(child, cleanup); + }, + + /** + * Removes all children from the container with a cleanup. + * @see cc.ProtectedNode#removeAllProtectedChildrenWithCleanup + */ + removeAllProtectedChildren: function () { + this.removeAllProtectedChildrenWithCleanup(true); + }, + + /** + * Removes all children from the container, and do a cleanup to all running actions depending on the cleanup parameter. + * @param {Boolean} [cleanup=true] true if all running actions on all children nodes should be cleanup, false otherwise. + */ + removeAllProtectedChildrenWithCleanup: function (cleanup) { + if (cleanup == null) + cleanup = true; + var locChildren = this._protectedChildren; + // not using detachChild improves speed here + for (var i = 0, len = locChildren.length; i < len; i++) { + var child = locChildren[i]; + // IMPORTANT: + // -1st do onExit + // -2nd cleanup + if (this._running) { + child._performRecursive(cc.Node._stateCallbackType.onExitTransitionDidStart); + child._performRecursive(cc.Node._stateCallbackType.onExit); + } + + if (cleanup) + child._performRecursive(cc.Node._stateCallbackType.cleanup); + // set parent nil at the end + child.setParent(null); + } + locChildren.length = 0; + }, + + /** + * Reorders a child according to a new z value. + * @param {cc.Node} child An already added child node. It MUST be already added. + * @param {Number} localZOrder Z order for drawing priority. Please refer to setLocalZOrder(int) + */ + reorderProtectedChild: function (child, localZOrder) { + cc.assert(child != null, "Child must be non-nil"); + this._reorderProtectedChildDirty = true; + child.setOrderOfArrival(cc.s_globalOrderOfArrival++); + child._setLocalZOrder(localZOrder); + }, + + /** + *

+ * Sorts the children array once before drawing, instead of every time when a child is added or reordered.
+ * This approach can improves the performance massively.
+ * @note Don't call this manually unless a child added needs to be removed in the same frame + *

+ */ + sortAllProtectedChildren: function () { + if (this._reorderProtectedChildDirty) { + var _children = this._protectedChildren; + + // insertion sort + var i, j, len = _children.length, tmp; + for (i = 1; i < len; i++) { + tmp = _children[i]; + j = i - 1; + + //continue moving element downwards while zOrder is smaller or when zOrder is the same but mutatedIndex is smaller + while (j >= 0) { + if (tmp._localZOrder < _children[j]._localZOrder) { + _children[j + 1] = _children[j]; + } else if (tmp._localZOrder === _children[j]._localZOrder && tmp.arrivalOrder < _children[j].arrivalOrder) { + _children[j + 1] = _children[j]; + } else + break; + j--; + } + _children[j + 1] = tmp; + } + + //don't need to check children recursively, that's done in visit of each child + this._reorderProtectedChildDirty = false; + } + }, + + _changePosition: function () { + }, + + _createRenderCmd: function () { + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return new cc.ProtectedNode.CanvasRenderCmd(this); + else + return new cc.ProtectedNode.WebGLRenderCmd(this); + } +}); + +/** + * create a cc.ProtectedNode object; + * @deprecated since v3.0, please use new cc.ProtectedNode() instead. + * @return cc.ProtectedNode + */ +cc.ProtectedNode.create = function () { + return new cc.ProtectedNode(); +}; diff --git a/frameworks/cocos2d-html5/extensions/ccui/base-classes/CCProtectedNodeCanvasRenderCmd.js b/frameworks/cocos2d-html5/extensions/ccui/base-classes/CCProtectedNodeCanvasRenderCmd.js new file mode 100644 index 0000000..1ae8b1e --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/base-classes/CCProtectedNodeCanvasRenderCmd.js @@ -0,0 +1,169 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + cc.ProtectedNode.RenderCmd = { + _updateDisplayColor: function (parentColor) { + var node = this._node; + var locDispColor = this._displayedColor, locRealColor = node._realColor; + var i, len, selChildren, item; + if (this._cascadeColorEnabledDirty && !node._cascadeColorEnabled) { + locDispColor.r = locRealColor.r; + locDispColor.g = locRealColor.g; + locDispColor.b = locRealColor.b; + var whiteColor = new cc.Color(255, 255, 255, 255); + selChildren = node._children; + for (i = 0, len = selChildren.length; i < len; i++) { + item = selChildren[i]; + if (item && item._renderCmd) + item._renderCmd._updateDisplayColor(whiteColor); + } + this._cascadeColorEnabledDirty = false; + } else { + if (parentColor === undefined) { + var locParent = node._parent; + if (locParent && locParent._cascadeColorEnabled) + parentColor = locParent.getDisplayedColor(); + else + parentColor = cc.color.WHITE; + } + locDispColor.r = 0 | (locRealColor.r * parentColor.r / 255.0); + locDispColor.g = 0 | (locRealColor.g * parentColor.g / 255.0); + locDispColor.b = 0 | (locRealColor.b * parentColor.b / 255.0); + if (node._cascadeColorEnabled) { + selChildren = node._children; + for (i = 0, len = selChildren.length; i < len; i++) { + item = selChildren[i]; + if (item && item._renderCmd) { + item._renderCmd._updateDisplayColor(locDispColor); + item._renderCmd._updateColor(); + } + } + } + selChildren = node._protectedChildren; + for (i = 0, len = selChildren.length; i < len; i++) { + item = selChildren[i]; + if (item && item._renderCmd) { + item._renderCmd._updateDisplayColor(locDispColor); + item._renderCmd._updateColor(); + } + } + } + this._dirtyFlag = this._dirtyFlag & cc.Node._dirtyFlags.colorDirty ^ this._dirtyFlag; + }, + + _updateDisplayOpacity: function (parentOpacity) { + var node = this._node; + var i, len, selChildren, item; + if (this._cascadeOpacityEnabledDirty && !node._cascadeOpacityEnabled) { + this._displayedOpacity = node._realOpacity; + selChildren = node._children; + for (i = 0, len = selChildren.length; i < len; i++) { + item = selChildren[i]; + if (item && item._renderCmd) + item._renderCmd._updateDisplayOpacity(255); + } + this._cascadeOpacityEnabledDirty = false; + } else { + if (parentOpacity === undefined) { + var locParent = node._parent; + parentOpacity = 255; + if (locParent && locParent._cascadeOpacityEnabled) + parentOpacity = locParent.getDisplayedOpacity(); + } + this._displayedOpacity = node._realOpacity * parentOpacity / 255.0; + if (node._cascadeOpacityEnabled) { + selChildren = node._children; + for (i = 0, len = selChildren.length; i < len; i++) { + item = selChildren[i]; + if (item && item._renderCmd) { + item._renderCmd._updateDisplayOpacity(this._displayedOpacity); + item._renderCmd._updateColor(); + } + } + } + selChildren = node._protectedChildren; + for (i = 0, len = selChildren.length; i < len; i++) { + item = selChildren[i]; + if (item && item._renderCmd) { + item._renderCmd._updateDisplayOpacity(this._displayedOpacity); + item._renderCmd._updateColor(); + } + } + } + this._dirtyFlag = this._dirtyFlag & cc.Node._dirtyFlags.opacityDirty ^ this._dirtyFlag; + }, + + _changeProtectedChild: function (child) { + var cmd = child._renderCmd, + dirty = cmd._dirtyFlag, + flags = cc.Node._dirtyFlags; + + if (this._dirtyFlag & flags.colorDirty) + dirty |= flags.colorDirty; + + if (this._dirtyFlag & flags.opacityDirty) + dirty |= flags.opacityDirty; + + var colorDirty = dirty & flags.colorDirty, + opacityDirty = dirty & flags.opacityDirty; + + if (colorDirty) + cmd._updateDisplayColor(this._displayedColor); + if (opacityDirty) + cmd._updateDisplayOpacity(this._displayedOpacity); + if (colorDirty || opacityDirty) + cmd._updateColor(); + } + }; + + cc.ProtectedNode.CanvasRenderCmd = function (renderable) { + this._rootCtor(renderable); + this._cachedParent = null; + this._cacheDirty = false; + }; + + var proto = cc.ProtectedNode.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + cc.inject(cc.ProtectedNode.RenderCmd, proto); + proto.constructor = cc.ProtectedNode.CanvasRenderCmd; + proto._pNodeCmdCtor = cc.ProtectedNode.CanvasRenderCmd; + + proto.transform = function (parentCmd, recursive) { + var node = this._node; + + if (node._changePosition) + node._changePosition(); + + this.originTransform(parentCmd, recursive); + + var i, len, locChildren = node._protectedChildren; + if (recursive && locChildren && locChildren.length !== 0) { + for (i = 0, len = locChildren.length; i < len; i++) { + locChildren[i]._renderCmd.transform(this, recursive); + } + } + }; + + proto.pNodeTransform = proto.transform; +})(); diff --git a/frameworks/cocos2d-html5/extensions/ccui/base-classes/CCProtectedNodeWebGLRenderCmd.js b/frameworks/cocos2d-html5/extensions/ccui/base-classes/CCProtectedNodeWebGLRenderCmd.js new file mode 100644 index 0000000..b22907d --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/base-classes/CCProtectedNodeWebGLRenderCmd.js @@ -0,0 +1,50 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + if (!cc.Node.WebGLRenderCmd) + return; + cc.ProtectedNode.WebGLRenderCmd = function (renderable) { + this._rootCtor(renderable); + }; + + var proto = cc.ProtectedNode.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + cc.inject(cc.ProtectedNode.RenderCmd, proto); + proto.constructor = cc.ProtectedNode.WebGLRenderCmd; + proto._pNodeCmdCtor = cc.ProtectedNode.WebGLRenderCmd; + + proto.transform = function (parentCmd, recursive) { + this.originTransform(parentCmd, recursive); + + var i, len, + locChildren = this._node._protectedChildren; + if (recursive && locChildren && locChildren.length !== 0) { + for (i = 0, len = locChildren.length; i < len; i++) { + locChildren[i]._renderCmd.transform(this, recursive); + } + } + }; + + proto.pNodeTransform = proto.transform; +})(); diff --git a/frameworks/cocos2d-html5/extensions/ccui/base-classes/UIScale9Sprite.js b/frameworks/cocos2d-html5/extensions/ccui/base-classes/UIScale9Sprite.js new file mode 100644 index 0000000..95df94c --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/base-classes/UIScale9Sprite.js @@ -0,0 +1,936 @@ +/* global ccui */ + +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2012 Neofect. All rights reserved. + Copyright (c) 2016 zilongshanren. All rights reserved. + + http://www.cocos2d-x.org + + 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. + + Created by Jung Sang-Taik on 2012-03-16 + ****************************************************************************/ +(function () { + +var dataPool = { + _pool: {}, + _lengths: [], + put: function (data) { + var length = data.length; + if (!this._pool[length]) { + this._pool[length] = [data]; + this._lengths.push(length); + this._lengths.sort(); + } + else { + this._pool[length].push(data); + } + }, + get: function (length) { + var id; + for (var i = 0; i < this._lengths.length; i++) { + if (this._lengths[i] >= length) { + id = this._lengths[i]; + break; + } + } + if (id) { + return this._pool[id].pop(); + } + else { + return undefined; + } + } +}; + +var FIX_ARTIFACTS_BY_STRECHING_TEXEL = cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL, cornerId = [], webgl; + + +var simpleQuadGenerator = { + _rebuildQuads_base: function (sprite, spriteFrame, contentSize, isTrimmedContentSize) { + //build vertices + var vertices = sprite._vertices, + wt = sprite._renderCmd._worldTransform, + l, b, r, t; + if (isTrimmedContentSize) { + l = 0; + b = 0; + r = contentSize.width; + t = contentSize.height; + } else { + var originalSize = spriteFrame._originalSize; + var rect = spriteFrame._rect; + var offset = spriteFrame._offset; + var scaleX = contentSize.width / originalSize.width; + var scaleY = contentSize.height / originalSize.height; + var trimmLeft = offset.x + (originalSize.width - rect.width) / 2; + var trimmRight = offset.x - (originalSize.width - rect.width) / 2; + var trimmedBottom = offset.y + (originalSize.height - rect.height) / 2; + var trimmedTop = offset.y - (originalSize.height - rect.height) / 2; + + l = trimmLeft * scaleX; + b = trimmedBottom * scaleY; + r = contentSize.width + trimmRight * scaleX; + t = contentSize.height + trimmedTop * scaleY; + } + + if (vertices.length < 8) { + dataPool.put(vertices); + vertices = dataPool.get(8) || new Float32Array(8); + sprite._vertices = vertices; + } + // bl, br, tl, tr + if (webgl) { + vertices[0] = l * wt.a + b * wt.c + wt.tx; + vertices[1] = l * wt.b + b * wt.d + wt.ty; + vertices[2] = r * wt.a + b * wt.c + wt.tx; + vertices[3] = r * wt.b + b * wt.d + wt.ty; + vertices[4] = l * wt.a + t * wt.c + wt.tx; + vertices[5] = l * wt.b + t * wt.d + wt.ty; + vertices[6] = r * wt.a + t * wt.c + wt.tx; + vertices[7] = r * wt.b + t * wt.d + wt.ty; + } + else { + vertices[0] = l; + vertices[1] = b; + vertices[2] = r; + vertices[3] = b; + vertices[4] = l; + vertices[5] = t; + vertices[6] = r; + vertices[7] = t; + } + + cornerId[0] = 0; + cornerId[1] = 2; + cornerId[2] = 4; + cornerId[3] = 6; + + //build uvs + if (sprite._uvsDirty) { + this._calculateUVs(sprite, spriteFrame); + } + + sprite._vertCount = 4; + }, + + _calculateUVs: function (sprite, spriteFrame) { + var uvs = sprite._uvs; + var atlasWidth = spriteFrame._texture._pixelsWide; + var atlasHeight = spriteFrame._texture._pixelsHigh; + var textureRect = spriteFrame._rect; + textureRect = cc.rectPointsToPixels(textureRect); + + if (uvs.length < 8) { + dataPool.put(uvs); + uvs = dataPool.get(8) || new Float32Array(8); + sprite._uvs = uvs; + } + + //uv computation should take spritesheet into account. + var l, b, r, t; + var texelCorrect = FIX_ARTIFACTS_BY_STRECHING_TEXEL ? 0.5 : 0; + + if (spriteFrame._rotated) { + l = (textureRect.x + texelCorrect) / atlasWidth; + b = (textureRect.y + textureRect.width - texelCorrect) / atlasHeight; + r = (textureRect.x + textureRect.height - texelCorrect) / atlasWidth; + t = (textureRect.y + texelCorrect) / atlasHeight; + uvs[0] = l; uvs[1] = t; + uvs[2] = l; uvs[3] = b; + uvs[4] = r; uvs[5] = t; + uvs[6] = r; uvs[7] = b; + } + else { + l = (textureRect.x + texelCorrect) / atlasWidth; + b = (textureRect.y + textureRect.height - texelCorrect) / atlasHeight; + r = (textureRect.x + textureRect.width - texelCorrect) / atlasWidth; + t = (textureRect.y + texelCorrect) / atlasHeight; + uvs[0] = l; uvs[1] = b; + uvs[2] = r; uvs[3] = b; + uvs[4] = l; uvs[5] = t; + uvs[6] = r; uvs[7] = t; + } + } +}; + +var scale9QuadGenerator = { + x: new Array(4), + y: new Array(4), + _rebuildQuads_base: function (sprite, spriteFrame, contentSize, insetLeft, insetRight, insetTop, insetBottom) { + //build vertices + var vertices = sprite._vertices; + var wt = sprite._renderCmd._worldTransform; + var leftWidth, centerWidth, rightWidth; + var topHeight, centerHeight, bottomHeight; + var rect = spriteFrame._rect; + + leftWidth = insetLeft; + rightWidth = insetRight; + centerWidth = rect.width - leftWidth - rightWidth; + topHeight = insetTop; + bottomHeight = insetBottom; + centerHeight = rect.height - topHeight - bottomHeight; + + var preferSize = contentSize; + var sizableWidth = preferSize.width - leftWidth - rightWidth; + var sizableHeight = preferSize.height - topHeight - bottomHeight; + var xScale = preferSize.width / (leftWidth + rightWidth); + var yScale = preferSize.height / (topHeight + bottomHeight); + xScale = xScale > 1 ? 1 : xScale; + yScale = yScale > 1 ? 1 : yScale; + sizableWidth = sizableWidth < 0 ? 0 : sizableWidth; + sizableHeight = sizableHeight < 0 ? 0 : sizableHeight; + var x = this.x; + var y = this.y; + x[0] = 0; + x[1] = leftWidth * xScale; + x[2] = x[1] + sizableWidth; + x[3] = preferSize.width; + y[0] = 0; + y[1] = bottomHeight * yScale; + y[2] = y[1] + sizableHeight; + y[3] = preferSize.height; + + if (vertices.length < 32) { + dataPool.put(vertices); + vertices = dataPool.get(32) || new Float32Array(32); + sprite._vertices = vertices; + } + var offset = 0, row, col; + if (webgl) { + for (row = 0; row < 4; row++) { + for (col = 0; col < 4; col++) { + vertices[offset] = x[col] * wt.a + y[row] * wt.c + wt.tx; + vertices[offset+1] = x[col] * wt.b + y[row] * wt.d + wt.ty; + offset += 2; + } + } + } + else { + for (row = 0; row < 4; row++) { + for (col = 0; col < 4; col++) { + vertices[offset] = x[col]; + vertices[offset+1] = y[row]; + offset += 2; + } + } + } + + cornerId[0] = 0; + cornerId[1] = 6; + cornerId[2] = 24; + cornerId[3] = 30; + + //build uvs + if (sprite._uvsDirty) { + this._calculateUVs(sprite, spriteFrame, insetLeft, insetRight, insetTop, insetBottom); + } + }, + + _calculateUVs: function (sprite, spriteFrame, insetLeft, insetRight, insetTop, insetBottom) { + var uvs = sprite._uvs; + var rect = spriteFrame._rect; + var atlasWidth = spriteFrame._texture._pixelsWide; + var atlasHeight = spriteFrame._texture._pixelsHigh; + + //caculate texture coordinate + var leftWidth, centerWidth, rightWidth; + var topHeight, centerHeight, bottomHeight; + var textureRect = spriteFrame._rect; + textureRect = cc.rectPointsToPixels(textureRect); + rect = cc.rectPointsToPixels(rect); + var scale = cc.contentScaleFactor(); + + leftWidth = insetLeft * scale; + rightWidth = insetRight * scale; + centerWidth = rect.width - leftWidth - rightWidth; + topHeight = insetTop * scale; + bottomHeight = insetBottom * scale; + centerHeight = rect.height - topHeight - bottomHeight; + + if (uvs.length < 32) { + dataPool.put(uvs); + uvs = dataPool.get(32) || new Float32Array(32); + sprite._uvs = uvs; + } + + //uv computation should take spritesheet into account. + var u = this.x; + var v = this.y; + var texelCorrect = FIX_ARTIFACTS_BY_STRECHING_TEXEL ? 0.5 : 0; + var offset = 0, row, col; + + if (spriteFrame._rotated) { + u[0] = (textureRect.x + texelCorrect) / atlasWidth; + u[1] = (bottomHeight + textureRect.x) / atlasWidth; + u[2] = (bottomHeight + centerHeight + textureRect.x) / atlasWidth; + u[3] = (textureRect.x + textureRect.height - texelCorrect) / atlasWidth; + + v[3] = (textureRect.y + texelCorrect) / atlasHeight; + v[2] = (leftWidth + textureRect.y) / atlasHeight; + v[1] = (leftWidth + centerWidth + textureRect.y) / atlasHeight; + v[0] = (textureRect.y + textureRect.width - texelCorrect) / atlasHeight; + + for (row = 0; row < 4; row++) { + for (col = 0; col < 4; col++) { + uvs[offset] = u[row]; + uvs[offset+1] = v[3-col]; + offset += 2; + } + } + } + else { + u[0] = (textureRect.x + texelCorrect) / atlasWidth; + u[1] = (leftWidth + textureRect.x) / atlasWidth; + u[2] = (leftWidth + centerWidth + textureRect.x) / atlasWidth; + u[3] = (textureRect.x + textureRect.width - texelCorrect) / atlasWidth; + + v[3] = (textureRect.y + texelCorrect) / atlasHeight; + v[2] = (topHeight + textureRect.y) / atlasHeight; + v[1] = (topHeight + centerHeight + textureRect.y) / atlasHeight; + v[0] = (textureRect.y + textureRect.height - texelCorrect) / atlasHeight; + + for (row = 0; row < 4; row++) { + for (col = 0; col < 4; col++) { + uvs[offset] = u[col]; + uvs[offset+1] = v[row]; + offset += 2; + } + } + } + } +}; + +/** + *

+ * A 9-slice sprite for cocos2d UI.
+ *
+ * 9-slice scaling allows you to specify how scaling is applied
+ * to specific areas of a sprite. With 9-slice scaling (3x3 grid),
+ * you can ensure that the sprite does not become distorted when
+ * scaled.
+ * @see http://yannickloriot.com/library/ios/cccontrolextension/Classes/CCScale9Sprite.html
+ *

+ * @class + * @extends cc.Node + * + * @property {cc.Size} preferredSize - The preferred size of the 9-slice sprite + * @property {cc.Rect} capInsets - The cap insets of the 9-slice sprite + * @property {Number} insetLeft - The left inset of the 9-slice sprite + * @property {Number} insetTop - The top inset of the 9-slice sprite + * @property {Number} insetRight - The right inset of the 9-slice sprite + * @property {Number} insetBottom - The bottom inset of the 9-slice sprite + */ + +ccui.Scale9Sprite = cc.Scale9Sprite = cc.Node.extend(/** @lends ccui.Scale9Sprite# */{ + //resource data, could be async loaded. + _spriteFrame: null, + _scale9Image: null, + + //scale 9 data + _insetLeft: 0, + _insetRight: 0, + _insetTop: 0, + _insetBottom: 0, + //blend function + _blendFunc: null, + //sliced or simple + _renderingType: 1, + //bright or not + _brightState: 0, + _opacityModifyRGB: false, + //rendering quads shared by canvas and webgl + _rawVerts: null, + _rawUvs: null, + _vertices: null, + _uvs: null, + _vertCount: 0, + _quadsDirty: true, + _uvsDirty: true, + _isTriangle: false, + _isTrimmedContentSize: false, + _textureLoaded: false, + + //v3.3 + _flippedX: false, + _flippedY: false, + _className: "Scale9Sprite", + + /** + * Constructor function. + * @function + * @param {string|cc.SpriteFrame} file file name of texture or a SpriteFrame + * @param {cc.Rect} rectOrCapInsets + * @param {cc.Rect} capInsets + * @returns {Scale9Sprite} + */ + ctor: function (file, rectOrCapInsets, capInsets) { + cc.Node.prototype.ctor.call(this); + + //for async texture load + this._loader = new cc.Sprite.LoadManager(); + + this._renderCmd.setState(this._brightState); + this._blendFunc = cc.BlendFunc._alphaPremultiplied(); + this.setAnchorPoint(cc.p(0.5, 0.5)); + // Init vertex data for simple + this._rawVerts = null; + this._rawUvs = null; + this._vertices = dataPool.get(8) || new Float32Array(8); + this._uvs = dataPool.get(8) || new Float32Array(8); + + if (file !== undefined) { + if (file instanceof cc.SpriteFrame) + this.initWithSpriteFrame(file, rectOrCapInsets); + else { + var frame = cc.spriteFrameCache.getSpriteFrame(file); + if (frame) + this.initWithSpriteFrame(frame, rectOrCapInsets); + else + this.initWithFile(file, rectOrCapInsets, capInsets); + } + } + + + if (webgl === undefined) { + webgl = cc._renderType === cc.game.RENDER_TYPE_WEBGL; + } + }, + + textureLoaded: function () { + return this._textureLoaded; + }, + + getCapInsets: function () { + return cc.rect(this._capInsetsInternal); + }, + + _asyncSetCapInsets: function () { + this.removeEventListener('load', this._asyncSetCapInsets, this); + this.setCapInsets(this._cacheCapInsets); + this._cacheCapInsets = null; + }, + + setCapInsets: function (capInsets) { + // Asynchronous loading texture requires this data + // This data does not take effect immediately, so it does not affect the existing texture. + if (!this.loaded()) { + this._cacheCapInsets = capInsets; + this.removeEventListener('load', this._asyncSetCapInsets, this); + this.addEventListener('load', this._asyncSetCapInsets, this); + return false; + } + + this._capInsetsInternal = capInsets; + this._updateCapInsets(this._spriteFrame._rect, this._capInsetsInternal); + }, + + _updateCapInsets: function (rect, capInsets) { + if(!capInsets || !rect || cc._rectEqualToZero(capInsets)) { + rect = rect || {x:0, y:0, width: this._contentSize.width, height: this._contentSize.height}; + this._capInsetsInternal = cc.rect(rect.width /3, + rect.height /3, + rect.width /3, + rect.height /3); + } else { + this._capInsetsInternal = capInsets; + } + + if(!cc._rectEqualToZero(rect)) { + this._insetLeft = this._capInsetsInternal.x; + this._insetTop = this._capInsetsInternal.y; + this._insetRight = rect.width - this._insetLeft - this._capInsetsInternal.width; + this._insetBottom = rect.height - this._insetTop - this._capInsetsInternal.height; + } + }, + + + initWithFile: function (file, rect, capInsets) { + if (file instanceof cc.Rect) { + file = arguments[1]; + capInsets = arguments[0]; + rect = cc.rect(0, 0, 0, 0); + } else { + rect = rect || cc.rect(0, 0, 0, 0); + capInsets = capInsets || cc.rect(0, 0, 0, 0); + } + + if(!file) + throw new Error("ccui.Scale9Sprite.initWithFile(): file should be non-null"); + + var texture = cc.textureCache.getTextureForKey(file); + if (!texture) { + texture = cc.textureCache.addImage(file); + } + + var locLoaded = texture.isLoaded(); + this._textureLoaded = locLoaded; + this._loader.clear(); + if (!locLoaded) { + this._loader.once(texture, function () { + this.initWithFile(file, rect, capInsets); + this.dispatchEvent("load"); + }, this); + return false; + } + + //in this function, the texture already make sure is loaded. + if( cc._rectEqualToZero(rect)) { + var textureSize = texture.getContentSize(); + rect = cc.rect(0, 0, textureSize.width, textureSize.height); + } + this.setTexture(texture, rect); + this._updateCapInsets(rect, capInsets); + + return true; + }, + + updateWithBatchNode: function (batchNode, originalRect, rotated, capInsets) { + if (!batchNode) { + return false; + } + + var texture = batchNode.getTexture(); + this._loader.clear(); + if (!texture.isLoaded()) { + this._loader.once(texture, function () { + this.updateWithBatchNode(batchNode, originalRect, rotated, capInsets); + this.dispatchEvent("load"); + }, this); + return false; + } + + this.setTexture(texture, originalRect); + this._updateCapInsets(originalRect, capInsets); + + return true; + }, + + + /** + * Initializes a 9-slice sprite with an sprite frame + * @param spriteFrameOrSFName The sprite frame object. + */ + initWithSpriteFrame: function (spriteFrame, capInsets) { + this.setSpriteFrame(spriteFrame); + + capInsets = capInsets || cc.rect(0, 0, 0, 0); + + this._updateCapInsets(spriteFrame._rect, capInsets); + }, + + initWithSpriteFrameName: function (spriteFrameName, capInsets) { + if(!spriteFrameName) + throw new Error("ccui.Scale9Sprite.initWithSpriteFrameName(): spriteFrameName should be non-null"); + capInsets = capInsets || cc.rect(0, 0, 0, 0); + + var frame = cc.spriteFrameCache.getSpriteFrame(spriteFrameName); + if (frame == null) { + cc.log("ccui.Scale9Sprite.initWithSpriteFrameName(): can't find the sprite frame by spriteFrameName"); + return false; + } + this.setSpriteFrame(frame); + + capInsets = capInsets || cc.rect(0, 0, 0, 0); + + this._updateCapInsets(frame._rect, capInsets); + }, + + loaded: function () { + if (this._spriteFrame === null) { + return false; + } else { + return this._spriteFrame.textureLoaded(); + } + }, + + /** + * Change the texture file of 9 slice sprite + * + * @param textureOrTextureFile The name of the texture file. + */ + setTexture: function (texture, rect) { + var spriteFrame = new cc.SpriteFrame(texture, rect); + this.setSpriteFrame(spriteFrame); + }, + + _updateBlendFunc: function () { + // it's possible to have an untextured sprite + var blendFunc = this._blendFunc; + if (!this._spriteFrame || !this._spriteFrame._texture.hasPremultipliedAlpha()) { + if (blendFunc.src === cc.ONE && blendFunc.dst === cc.BLEND_DST) { + blendFunc.src = cc.SRC_ALPHA; + } + this._opacityModifyRGB = false; + } else { + if (blendFunc.src === cc.SRC_ALPHA && blendFunc.dst === cc.BLEND_DST) { + blendFunc.src = cc.ONE; + } + this._opacityModifyRGB = true; + } + }, + + setOpacityModifyRGB: function (value) { + if (this._opacityModifyRGB !== value) { + this._opacityModifyRGB = value; + this._renderCmd._setColorDirty(); + } + }, + + isOpacityModifyRGB: function () { + return this._opacityModifyRGB; + }, + + /** + * Change the sprite frame of 9 slice sprite + * + * @param spriteFrameOrSFFileName The name of the texture file. + */ + setSpriteFrame: function (spriteFrame) { + if (spriteFrame) { + this._spriteFrame = spriteFrame; + this._quadsDirty = true; + this._uvsDirty = true; + var self = this; + var onResourceDataLoaded = function () { + if (cc.sizeEqualToSize(self._contentSize, cc.size(0, 0))) { + self.setContentSize(self._spriteFrame._rect); + } + self._textureLoaded = true; + self._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.contentDirty); + cc.renderer.childrenOrderDirty = true; + }; + self._textureLoaded = spriteFrame.textureLoaded(); + if (self._textureLoaded) { + onResourceDataLoaded(); + } else { + this._loader.clear(); + this._loader.once(spriteFrame, function () { + onResourceDataLoaded(); + this.dispatchEvent("load"); + }, this); + } + } + }, + + /** + * Sets the source blending function. + * + * @param blendFunc A structure with source and destination factor to specify pixel arithmetic. e.g. {GL_ONE, GL_ONE}, {GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA}. + */ + setBlendFunc: function (blendFunc, dst) { + if (dst === undefined) { + this._blendFunc.src = blendFunc.src || cc.BLEND_SRC; + this._blendFunc.dst = blendFunc.dst || cc.BLEND_DST; + } + else { + this._blendFunc.src = blendFunc || cc.BLEND_SRC; + this._blendFunc.dst = dst || cc.BLEND_DST; + } + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.contentDirty); + }, + + /** + * Returns the blending function that is currently being used. + * + * @return A BlendFunc structure with source and destination factor which specified pixel arithmetic. + */ + getBlendFunc: function () { + return new cc.BlendFunc(this._blendFunc.src, this._blendFunc.dst); + }, + + setPreferredSize: function (preferredSize) { + if (!preferredSize || cc.sizeEqualToSize(this._contentSize, preferredSize)) return; + this.setContentSize(preferredSize); + }, + + getPreferredSize: function () { + return this.getContentSize(); + }, + + // overrides + setContentSize: function (width, height) { + if (height === undefined) { + height = width.height; + width = width.width; + } + if (width === this._contentSize.width && height === this._contentSize.height) { + return; + } + + cc.Node.prototype.setContentSize.call(this, width, height); + this._quadsDirty = true; + }, + + getContentSize: function () { + if(this._renderingType === ccui.Scale9Sprite.RenderingType.SIMPLE) { + if(this._spriteFrame) { + return this._spriteFrame._originalSize; + } + return cc.size(this._contentSize); + } else { + return cc.size(this._contentSize); + } + }, + + _setWidth: function (value) { + cc.Node.prototype._setWidth.call(this, value); + this._quadsDirty = true; + }, + + _setHeight: function (value) { + cc.Node.prototype._setHeight.call(this, value); + this._quadsDirty = true; + }, + + /** + * Change the state of 9-slice sprite. + * @see `State` + * @param state A enum value in State. + */ + setState: function (state) { + this._brightState = state; + this._renderCmd.setState(state); + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.contentDirty); + }, + + /** + * Query the current bright state. + * @return @see `State` + */ + getState: function () { + return this._brightState; + }, + + /** + * change the rendering type, could be simple or slice + * @return @see `RenderingType` + */ + setRenderingType: function (type) { + if (this._renderingType === type) return; + + this._renderingType = type; + this._quadsDirty = true; + this._uvsDirty = true; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.contentDirty); + }, + /** + * get the rendering type, could be simple or slice + * @return @see `RenderingType` + */ + getRenderingType: function () { + return this._renderingType; + }, + /** + * change the left border of 9 slice sprite, it should be specified before trimmed. + * @param insetLeft left border. + */ + setInsetLeft: function (insetLeft) { + this._insetLeft = insetLeft; + this._quadsDirty = true; + this._uvsDirty = true; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.contentDirty); + }, + /** + * get the left border of 9 slice sprite, the result is specified before trimmed. + * @return left border. + */ + getInsetLeft: function () { + return this._insetLeft; + }, + /** + * change the top border of 9 slice sprite, it should be specified before trimmed. + * @param insetTop top border. + */ + setInsetTop: function (insetTop) { + this._insetTop = insetTop; + this._quadsDirty = true; + this._uvsDirty = true; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.contentDirty); + }, + + /** + * get the top border of 9 slice sprite, the result is specified before trimmed. + * @return top border. + */ + getInsetTop: function () { + return this._insetTop; + }, + + /** + * change the right border of 9 slice sprite, it should be specified before trimmed. + * @param insetRight right border. + */ + setInsetRight: function (insetRight) { + this._insetRight = insetRight; + this._quadsDirty = true; + this._uvsDirty = true; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.contentDirty); + }, + + /** + * get the right border of 9 slice sprite, the result is specified before trimmed. + * @return right border. + */ + getInsetRight: function () { + return this._insetRight; + }, + + /** + * change the bottom border of 9 slice sprite, it should be specified before trimmed. + * @param insetBottom bottom border. + */ + setInsetBottom: function (insetBottom) { + this._insetBottom = insetBottom; + this._quadsDirty = true; + this._uvsDirty = true; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.contentDirty); + }, + /** + * get the bottom border of 9 slice sprite, the result is specified before trimmed. + * @return bottom border. + */ + getInsetBottom: function () { + return this._insetBottom; + }, + + _rebuildQuads: function () { + if (!this._spriteFrame || !this._spriteFrame._textureLoaded) { + return; + } + + this._updateBlendFunc(); + + this._isTriangle = false; + switch (this._renderingType) { + case RenderingType.SIMPLE: + simpleQuadGenerator._rebuildQuads_base(this, this._spriteFrame, this._contentSize, this._isTrimmedContentSize); + break; + case RenderingType.SLICED: + scale9QuadGenerator._rebuildQuads_base(this, this._spriteFrame, this._contentSize, this._insetLeft, this._insetRight, this._insetTop, this._insetBottom); + break; + default: + this._quadsDirty = false; + this._uvsDirty = false; + cc.error('Can not generate quad'); + return; + } + + + this._quadsDirty = false; + this._uvsDirty = false; + }, + + _createRenderCmd: function () { + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return new ccui.Scale9Sprite.CanvasRenderCmd(this); + else + return new ccui.Scale9Sprite.WebGLRenderCmd(this); + } +}); + +var _p = ccui.Scale9Sprite.prototype; +cc.EventHelper.prototype.apply(_p); + +// Extended properties +/** @expose */ +_p.preferredSize; +cc.defineGetterSetter(_p, "preferredSize", _p.getPreferredSize, _p.setPreferredSize); +/** @expose */ +_p.capInsets; +cc.defineGetterSetter(_p, "capInsets", _p.getCapInsets, _p.setCapInsets); +/** @expose */ +_p.insetLeft; +cc.defineGetterSetter(_p, "insetLeft", _p.getInsetLeft, _p.setInsetLeft); +/** @expose */ +_p.insetTop; +cc.defineGetterSetter(_p, "insetTop", _p.getInsetTop, _p.setInsetTop); +/** @expose */ +_p.insetRight; +cc.defineGetterSetter(_p, "insetRight", _p.getInsetRight, _p.setInsetRight); +/** @expose */ +_p.insetBottom; +cc.defineGetterSetter(_p, "insetBottom", _p.getInsetBottom, _p.setInsetBottom); + +_p = null; + +/** + * Creates a 9-slice sprite with a texture file, a delimitation zone and + * with the specified cap insets. + * @deprecated since v3.0, please use new ccui.Scale9Sprite(file, rect, capInsets) instead. + * @param {String|cc.SpriteFrame} file file name of texture or a cc.Sprite object + * @param {cc.Rect} rect the rect of the texture + * @param {cc.Rect} capInsets the cap insets of ccui.Scale9Sprite + * @returns {ccui.Scale9Sprite} + */ +ccui.Scale9Sprite.create = function (file, rect, capInsets) { + return new ccui.Scale9Sprite(file, rect, capInsets); +}; + +/** + * create a ccui.Scale9Sprite with Sprite frame. + * @deprecated since v3.0, please use "new ccui.Scale9Sprite(spriteFrame, capInsets)" instead. + * @param {cc.SpriteFrame} spriteFrame + * @param {cc.Rect} capInsets + * @returns {ccui.Scale9Sprite} + */ +ccui.Scale9Sprite.createWithSpriteFrame = function (spriteFrame, capInsets) { + return new ccui.Scale9Sprite(spriteFrame, capInsets); +}; + +/** + * create a ccui.Scale9Sprite with a Sprite frame name + * @deprecated since v3.0, please use "new ccui.Scale9Sprite(spriteFrameName, capInsets)" instead. + * @param {string} spriteFrameName + * @param {cc.Rect} capInsets + * @returns {Scale9Sprite} + */ +ccui.Scale9Sprite.createWithSpriteFrameName = function (spriteFrameName, capInsets) { + return new ccui.Scale9Sprite(spriteFrameName, capInsets); +}; + +/** + * @ignore + */ +ccui.Scale9Sprite.POSITIONS_CENTRE = 0; +ccui.Scale9Sprite.POSITIONS_TOP = 1; +ccui.Scale9Sprite.POSITIONS_LEFT = 2; +ccui.Scale9Sprite.POSITIONS_RIGHT = 3; +ccui.Scale9Sprite.POSITIONS_BOTTOM = 4; +ccui.Scale9Sprite.POSITIONS_TOPRIGHT = 5; +ccui.Scale9Sprite.POSITIONS_TOPLEFT = 6; +ccui.Scale9Sprite.POSITIONS_BOTTOMRIGHT = 7; + +ccui.Scale9Sprite.state = {NORMAL: 0, GRAY: 1}; + +var RenderingType = ccui.Scale9Sprite.RenderingType = { + /** + * @property {Number} SIMPLE + */ + SIMPLE: 0, + /** + * @property {Number} SLICED + */ + SLICED: 1 +}; +})(); diff --git a/frameworks/cocos2d-html5/extensions/ccui/base-classes/UIScale9SpriteCanvasRenderCmd.js b/frameworks/cocos2d-html5/extensions/ccui/base-classes/UIScale9SpriteCanvasRenderCmd.js new file mode 100644 index 0000000..12f1bca --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/base-classes/UIScale9SpriteCanvasRenderCmd.js @@ -0,0 +1,154 @@ +/**************************************************************************** + Copyright (c) 2013-2016 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function() { + ccui.Scale9Sprite.CanvasRenderCmd = function (renderable) { + cc.Node.CanvasRenderCmd.call(this, renderable); + this._needDraw = true; + + this._state = ccui.Scale9Sprite.state.NORMAL; + this._originalTexture = this._textureToRender = null; + }; + + var proto = ccui.Scale9Sprite.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + proto.constructor = ccui.Scale9Sprite.CanvasRenderCmd; + + proto.transform = function(parentCmd, recursive){ + this.originTransform(parentCmd, recursive); + this._node._rebuildQuads(); + }; + + proto.needDraw = function () { + return this._needDraw && this._node.loaded(); + }; + + proto._updateDisplayColor = function(parentColor){ + cc.Node.RenderCmd.prototype._updateDisplayColor.call(this, parentColor); + this._originalTexture = this._textureToRender = null; + }; + + proto.setState = function(state){ + if(this._state === state) return; + + this._state = state; + this._originalTexture = this._textureToRender = null; + }; + + proto._setColorDirty = function () { + this.setDirtyFlag(cc.Node._dirtyFlags.colorDirty | cc.Node._dirtyFlags.opacityDirty); + }; + + proto.rendering = function (ctx, scaleX, scaleY) { + var node = this._node; + var locDisplayOpacity = this._displayedOpacity; + var alpha = locDisplayOpacity/ 255; + var locTexture = null; + if (node._spriteFrame) locTexture = node._spriteFrame._texture; + if (!node.loaded() || locDisplayOpacity === 0) + return; + if (this._textureToRender === null || this._originalTexture !== locTexture) { + this._textureToRender = this._originalTexture = locTexture; + if (cc.Scale9Sprite.state.GRAY === this._state) { + this._textureToRender = this._textureToRender._generateGrayTexture(); + } + var color = node.getDisplayedColor(); + if (locTexture && (color.r !== 255 || color.g !==255 || color.b !== 255)) + this._textureToRender = this._textureToRender._generateColorTexture(color.r,color.g,color.b); + } + + var wrapper = ctx || cc._renderContext, context = wrapper.getContext(); + wrapper.setTransform(this._worldTransform, scaleX, scaleY); + wrapper.setCompositeOperation(cc.Node.CanvasRenderCmd._getCompositeOperationByBlendFunc(node._blendFunc)); + wrapper.setGlobalAlpha(alpha); + + if (this._textureToRender) { + if (node._quadsDirty) { + node._rebuildQuads(); + } + var sx,sy,sw,sh; + var x, y, w,h; + var textureWidth = this._textureToRender._pixelsWide; + var textureHeight = this._textureToRender._pixelsHigh; + var image = this._textureToRender._htmlElementObj; + var vertices = node._vertices; + var uvs = node._uvs; + var i = 0, off = 0; + + if (node._renderingType === cc.Scale9Sprite.RenderingType.SLICED) { + // Sliced use a special vertices layout 16 vertices for 9 quads + for (var r = 0; r < 3; ++r) { + for (var c = 0; c < 3; ++c) { + off = r*8 + c*2; + x = vertices[off]; + y = vertices[off+1]; + w = vertices[off+10] - x; + h = vertices[off+11] - y; + y = - y - h; + + sx = uvs[off] * textureWidth; + sy = uvs[off+11] * textureHeight; + sw = (uvs[off+10] - uvs[off]) * textureWidth; + sh = (uvs[off+1] - uvs[off+11]) * textureHeight; + + if (sw > 0 && sh > 0 && w > 0 && h > 0) { + context.drawImage(image, + sx, sy, sw, sh, + x, y, w, h); + } + } + } + cc.g_NumberOfDraws += 9; + } else { + var quadCount = Math.floor(node._vertCount / 4); + for (i = 0, off = 0; i < quadCount; i++) { + x = vertices[off]; + y = vertices[off+1]; + w = vertices[off+6] - x; + h = vertices[off+7] - y; + y = - y - h; + + sx = uvs[off] * textureWidth; + sy = uvs[off+7] * textureHeight; + sw = (uvs[off+6] - uvs[off]) * textureWidth; + sh = (uvs[off+1] - uvs[off+7]) * textureHeight; + + + if (this._textureToRender._pattern !== '') { + wrapper.setFillStyle(context.createPattern(image, this._textureToRender._pattern)); + context.fillRect(x, y, w, h); + } else { + if (sw > 0 && sh > 0 && w > 0 && h > 0) { + context.drawImage(image, + sx, sy, sw, sh, + x, y, w, h); + } + } + off += 8; + } + cc.g_NumberOfDraws += quadCount; + } + } + }; + +})(); diff --git a/frameworks/cocos2d-html5/extensions/ccui/base-classes/UIScale9SpriteWebGLRenderCmd.js b/frameworks/cocos2d-html5/extensions/ccui/base-classes/UIScale9SpriteWebGLRenderCmd.js new file mode 100644 index 0000000..16df4ee --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/base-classes/UIScale9SpriteWebGLRenderCmd.js @@ -0,0 +1,159 @@ +/**************************************************************************** + Copyright (c) 2013-2016 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function() { + if(!cc.Node.WebGLRenderCmd) return; + + ccui.Scale9Sprite.WebGLRenderCmd = function (renderable) { + cc.Node.WebGLRenderCmd.call(this, renderable); + + this._needDraw = true; + + this._color = new Uint32Array(1); + this._dirty = false; + this._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_SPRITE_POSITION_TEXTURECOLOR); + }; + + + + var Scale9Sprite = ccui.Scale9Sprite; + var proto = ccui.Scale9Sprite.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + proto.constructor = ccui.Scale9Sprite.WebGLRenderCmd; + + proto.needDraw = function () { + return this._needDraw && this._node.loaded(); + }; + + proto._uploadSliced = function (vertices, uvs, color, z, f32buffer, ui32buffer, offset) { + var off; + for (var r = 0; r < 3; ++r) { + for (var c = 0; c < 3; ++c) { + off = r*8 + c*2; + // lb + f32buffer[offset] = vertices[off]; + f32buffer[offset+1] = vertices[off+1]; + f32buffer[offset+2] = z; + ui32buffer[offset+3] = color[0]; + f32buffer[offset+4] = uvs[off]; + f32buffer[offset+5] = uvs[off+1]; + offset += 6; + // rb + f32buffer[offset] = vertices[off+2]; + f32buffer[offset + 1] = vertices[off+3]; + f32buffer[offset + 2] = z; + ui32buffer[offset + 3] = color[0]; + f32buffer[offset + 4] = uvs[off+2]; + f32buffer[offset + 5] = uvs[off+3]; + offset += 6; + // lt + f32buffer[offset] = vertices[off+8]; + f32buffer[offset + 1] = vertices[off+9]; + f32buffer[offset + 2] = z; + ui32buffer[offset + 3] = color[0]; + f32buffer[offset + 4] = uvs[off+8]; + f32buffer[offset + 5] = uvs[off+9]; + offset += 6; + // rt + f32buffer[offset] = vertices[off+10]; + f32buffer[offset + 1] = vertices[off+11]; + f32buffer[offset + 2] = z; + ui32buffer[offset + 3] = color[0]; + f32buffer[offset + 4] = uvs[off+10]; + f32buffer[offset + 5] = uvs[off+11]; + offset += 6; + } + } + return 36; + }; + + proto.transform = function (parentCmd, recursive) { + this.originTransform(parentCmd, recursive); + this._node._rebuildQuads(); + }; + + proto._setColorDirty = function () { + }; + + proto.uploadData = function (f32buffer, ui32buffer, vertexDataOffset){ + var node = this._node; + if (this._displayedOpacity === 0) { + return 0; + } + + // Rebuild vertex data + if (node._quadsDirty) { + node._rebuildQuads(); + } + + // Color & z + var opacity = this._displayedOpacity; + var r = this._displayedColor.r, + g = this._displayedColor.g, + b = this._displayedColor.b; + if (node._opacityModifyRGB) { + var a = opacity / 255; + r *= a; + g *= a; + b *= a; + } + this._color[0] = ((opacity<<24) | (b<<16) | (g<<8) | r); + var z = node._vertexZ; + + // Upload data + var vertices = node._vertices; + var uvs = node._uvs; + var types = Scale9Sprite.RenderingType; + var offset = vertexDataOffset; + var len = 0; + switch (node._renderingType) { + case types.SIMPLE: + // Inline for performance + len = this._node._vertCount; + for (var i = 0, srcOff = 0; i < len; i++, srcOff += 2) { + f32buffer[offset] = vertices[srcOff]; + f32buffer[offset + 1] = vertices[srcOff+1]; + f32buffer[offset + 2] = z; + ui32buffer[offset + 3] = this._color[0]; + f32buffer[offset + 4] = uvs[srcOff]; + f32buffer[offset + 5] = uvs[srcOff+1]; + offset += 6; + } + break; + case types.SLICED: + len = this._uploadSliced(vertices, uvs, this._color, z, f32buffer, ui32buffer, offset); + break; + } + return len; + }; + + proto.setState = function (state) { + if (state === Scale9Sprite.state.NORMAL) { + this._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_SPRITE_POSITION_TEXTURECOLOR); + } else if (state === Scale9Sprite.state.GRAY) { + this._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_SPRITE_POSITION_TEXTURECOLOR_GRAY); + } + }; + + +})(); diff --git a/frameworks/cocos2d-html5/extensions/ccui/base-classes/UIWidget.js b/frameworks/cocos2d-html5/extensions/ccui/base-classes/UIWidget.js new file mode 100644 index 0000000..b01ac52 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/base-classes/UIWidget.js @@ -0,0 +1,2087 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +ccui._FocusNavigationController = cc.Class.extend({ + _keyboardListener: null, + _firstFocusedWidget: null, + _enableFocusNavigation: false, + _keyboardEventPriority: 1, + + enableFocusNavigation: function (flag) { + if (this._enableFocusNavigation === flag) + return; + + this._enableFocusNavigation = flag; + if (flag) + this._addKeyboardEventListener(); + else + this._removeKeyboardEventListener(); + }, + + _setFirstFocsuedWidget: function (widget) { + this._firstFocusedWidget = widget; + }, + + _onKeyPressed: function (keyCode, event) { + if (this._enableFocusNavigation && this._firstFocusedWidget) { + if (keyCode === cc.KEY.dpadDown) { + this._firstFocusedWidget = this._firstFocusedWidget.findNextFocusedWidget(ccui.Widget.DOWN, this._firstFocusedWidget); + } + if (keyCode === cc.KEY.dpadUp) { + this._firstFocusedWidget = this._firstFocusedWidget.findNextFocusedWidget(ccui.Widget.UP, this._firstFocusedWidget); + } + if (keyCode === cc.KEY.dpadLeft) { + this._firstFocusedWidget = this._firstFocusedWidget.findNextFocusedWidget(ccui.Widget.LEFT, this._firstFocusedWidget); + } + if (keyCode === cc.KEY.dpadRight) { + this._firstFocusedWidget = this._firstFocusedWidget.findNextFocusedWidget(ccui.Widget.RIGHT, this._firstFocusedWidget); + } + } + }, + + _addKeyboardEventListener: function () { + if (!this._keyboardListener) { + this._keyboardListener = cc.EventListener.create({ + event: cc.EventListener.KEYBOARD, + onKeyReleased: this._onKeyPressed.bind(this) + }); + cc.eventManager.addListener(this._keyboardListener, this._keyboardEventPriority); + } + }, + + _removeKeyboardEventListener: function () { + if (this._keyboardListener) { + cc.eventManager.removeEventListener(this._keyboardListener); + this._keyboardListener = null; + } + } +}); + +ccui.__LAYOUT_COMPONENT_NAME = "__ui_layout"; + +/** + * The base class for ccui controls and layout + * @sample + * var uiWidget = new ccui.Widget(); + * this.addChild(uiWidget); + * @class + * @extends ccui.ProtectedNode + * + * @property {Number} xPercent - Position x in percentage of width + * @property {Number} yPercent - Position y in percentage of height + * @property {Number} widthPercent - Width in percentage of parent width + * @property {Number} heightPercent - Height in percentage of parent height + * @property {ccui.Widget} widgetParent - <@readonly> The direct parent when it's a widget also, otherwise equals null + * @property {Boolean} enabled - Indicate whether the widget is enabled + * @property {Boolean} focused - Indicate whether the widget is focused + * @property {ccui.Widget.SIZE_ABSOLUTE|ccui.Widget.SIZE_PERCENT} sizeType - The size type of the widget + * @property {ccui.Widget.TYPE_WIDGET|ccui.Widget.TYPE_CONTAINER} widgetType - <@readonly> The type of the widget + * @property {Boolean} touchEnabled - Indicate whether touch events are enabled + * @property {Boolean} updateEnabled - Indicate whether the update function is scheduled + * @property {Boolean} bright - Indicate whether the widget is bright + * @property {String} name - The name of the widget + * @property {Number} actionTag - The action tag of the widget + */ +ccui.Widget = ccui.ProtectedNode.extend(/** @lends ccui.Widget# */{ + _enabled: true, ///< Highest control of widget + _bright: true, ///< is this widget bright + _touchEnabled: false, ///< is this widget touch endabled + + _brightStyle: null, ///< bright style + + _touchBeganPosition: null, ///< touch began point + _touchMovePosition: null, ///< touch moved point + _touchEndPosition: null, ///< touch ended point + + _touchEventListener: null, + _touchEventSelector: null, + + _name: "default", + _widgetType: null, + _actionTag: 0, + _customSize: null, + _layoutParameterDictionary: null, + _layoutParameterType: 0, + + _focused: false, + _focusEnabled: true, + + _ignoreSize: false, + _affectByClipping: false, + + _sizeType: null, + _sizePercent: null, + _positionType: null, + _positionPercent: null, + _hit: false, + _nodes: null, + _touchListener: null, + _className: "Widget", + _flippedX: false, + _flippedY: false, + _opacity: 255, + _highlight: false, + + _touchEventCallback: null, + _clickEventListener: null, + + _propagateTouchEvents: true, + _unifySize: false, + + _callbackName: null, + _callbackType: null, + _usingLayoutComponent: false, + _inViewRect: true, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @function + */ + ctor: function () { + cc.ProtectedNode.prototype.ctor.call(this); + this._brightStyle = ccui.Widget.BRIGHT_STYLE_NONE; + this._touchBeganPosition = cc.p(0, 0); + this._touchMovePosition = cc.p(0, 0); + this._touchEndPosition = cc.p(0, 0); + this._widgetType = ccui.Widget.TYPE_WIDGET; + this._customSize = cc.size(0, 0); + this._layoutParameterDictionary = {}; + this._sizeType = ccui.Widget.SIZE_ABSOLUTE; + this._sizePercent = cc.p(0, 0); + this._positionType = ccui.Widget.POSITION_ABSOLUTE; + this._positionPercent = cc.p(0, 0); + this._nodes = []; + this._layoutParameterType = ccui.LayoutParameter.NONE; + ccui.Widget.prototype.init.call(this); + }, + + /** + * initializes state of widget. please do not call this function by yourself, you should pass the parameters to constructor to initialize it
. + * @returns {boolean} + */ + init: function () { + this._layoutParameterDictionary = {}; + this._initRenderer(); + this.setBright(true); + + this.onFocusChanged = this.onFocusChange; + this.onNextFocusedWidget = null; + this.setAnchorPoint(cc.p(0.5, 0.5)); + + this.ignoreContentAdaptWithSize(true); + return true; + }, + + /** + * Calls updateSizeAndPosition and its parent's onEnter + * @override + */ + onEnter: function () { + var locListener = this._touchListener; + if (locListener && !locListener._isRegistered() && this._touchEnabled) + cc.eventManager.addListener(locListener, this); + if(!this._usingLayoutComponent) + this.updateSizeAndPosition(); + if (this._sizeDirty) + this._onSizeChanged(); + cc.ProtectedNode.prototype.onEnter.call(this); + }, + + /** + * Calls unscheduleUpdate and its parent's onExit + * @override + */ + onExit: function () { + this.unscheduleUpdate(); + cc.ProtectedNode.prototype.onExit.call(this); + }, + + _getOrCreateLayoutComponent: function(){ + var layoutComponent = this.getComponent(ccui.__LAYOUT_COMPONENT_NAME); + if (null == layoutComponent){ + layoutComponent = new ccui.LayoutComponent(); + this.addComponent(layoutComponent); + } + return layoutComponent; + }, + + /** + * The direct parent when it's a widget also, otherwise equals null + * @returns {ccui.Widget|null} + */ + getWidgetParent: function () { + var widget = this.getParent(); + if (widget instanceof ccui.Widget) + return widget; + return null; + }, + + _updateContentSizeWithTextureSize: function (size) { + if(this._unifySize){ + this.setContentSize(size); + return; + } + this.setContentSize(this._ignoreSize ? size : this._customSize); + }, + + _isAncestorsEnabled: function () { + var parentWidget = this._getAncensterWidget(this); + if (parentWidget == null) + return true; + if (parentWidget && !parentWidget.isEnabled()) + return false; + + return parentWidget._isAncestorsEnabled(); + }, + + /** + * Allow widget touch events to propagate to its parents. Set false will disable propagation + * @since v3.2 + * @param {Boolean} isPropagate + */ + setPropagateTouchEvents: function (isPropagate) { + this._propagateTouchEvents = isPropagate; + }, + + /** + * Return whether the widget is propagate touch events to its parents or not + * @since v3.2 + * @returns {boolean} + */ + isPropagateTouchEvents: function () { + return this._propagateTouchEvents; + }, + + /** + * Specify widget to swallow touches or not + * @since v3.2 + * @param {Boolean} swallow + */ + setSwallowTouches: function (swallow) { + if (this._touchListener) + this._touchListener.setSwallowTouches(swallow); + }, + + /** + * Return whether the widget is swallowing touch or not + * @since v3.2 + * @returns {boolean} + */ + isSwallowTouches: function () { + if (this._touchListener) { + //return true; //todo need test + return this._touchListener.isSwallowTouches(); + } + return false; + }, + + _getAncensterWidget: function (node) { + if (null == node) + return null; + + var parent = node.getParent(); + if (null == parent) + return null; + + if (parent instanceof ccui.Widget) + return parent; + else + return this._getAncensterWidget(parent.getParent()); + }, + + _isAncestorsVisible: function (node) { + if (null == node) + return true; + + var parent = node.getParent(); + + if (parent && !parent.isVisible()) + return false; + return this._isAncestorsVisible(parent); + }, + + /** + *

+ * Sets whether the widget is enabled
+ * true if the widget is enabled, widget may be touched , false if the widget is disabled, widget cannot be touched.
+ * The default value is true, a widget is default to enabled + *

+ * @param {Boolean} enabled + */ + setEnabled: function (enabled) { + this._enabled = enabled; + this.setBright(enabled); + }, + + /** + * initializes renderer of widget. + */ + _initRenderer: function () { + }, + + /** + * Sets _customSize of ccui.Widget, if ignoreSize is true, the content size is its renderer's contentSize, otherwise the content size is parameter. + * and updates size percent by parent content size. At last, updates its children's size and position. + * @param {cc.Size|Number} contentSize content size or width of content size + * @param {Number} [height] + * @override + */ + setContentSize: function(contentSize, height){ + cc.Node.prototype.setContentSize.call(this, contentSize, height); + + var locWidth = this._contentSize.width; + var locHeight = this._contentSize.height; + + this._customSize.width = locWidth; + this._customSize.height = locHeight; + if(this._unifySize){ + //unify size logic + } else if (this._ignoreSize) { + this._contentSize = this.getVirtualRendererSize(); + } + if (!this._usingLayoutComponent && this._running) { + var widgetParent = this.getWidgetParent(); + var pSize = widgetParent ? widgetParent.getContentSize() : this._parent.getContentSize(); + this._sizePercent.x = (pSize.width > 0.0) ? locWidth / pSize.width : 0.0; + this._sizePercent.y = (pSize.height > 0.0) ? locHeight / pSize.height : 0.0; + } + + if (this._running) { + this._onSizeChanged(); + } else { + this._sizeDirty = true; + } + }, + + _setWidth: function (w) { + if (w === this._contentSize.width) { + return; + } + cc.Node.prototype._setWidth.call(this, w); + this._customSize.width = w; + if(this._unifySize){ + //unify size logic + } else if (this._ignoreSize) { + this._contentSize = this.getVirtualRendererSize(); + } + + if (!this._usingLayoutComponent && this._running) { + var widgetParent = this.getWidgetParent(); + var locWidth = widgetParent ? widgetParent.width : this._parent.width; + this._sizePercent.x = locWidth > 0 ? this._customSize.width / locWidth : 0; + } + + if (this._running) { + this._onSizeChanged(); + } else { + this._sizeDirty = true; + } + }, + _setHeight: function (h) { + if (h === this._contentSize.height) { + return; + } + + cc.Node.prototype._setHeight.call(this, h); + this._customSize.height = h; + if(this._unifySize){ + //unify size logic + } else if (this._ignoreSize) { + this._contentSize = this.getVirtualRendererSize(); + } + + if (!this._usingLayoutComponent && this._running) { + var widgetParent = this.getWidgetParent(); + var locH = widgetParent ? widgetParent.height : this._parent.height; + this._sizePercent.y = locH > 0 ? this._customSize.height / locH : 0; + } + + if (this._running) { + this._onSizeChanged(); + } else { + this._sizeDirty = true; + } + }, + + /** + * Changes the percent that is widget's percent size + * @param {cc.Point} percent that is widget's percent size, width and height value from 0 to 1. + */ + setSizePercent: function (percent) { + if(this._usingLayoutComponent){ + var component = this._getOrCreateLayoutComponent(); + component.setUsingPercentContentSize(true); + component.setPercentContentSize(percent); + component.refreshLayout(); + return; + } + + this._sizePercent.x = percent.x; + this._sizePercent.y = percent.y; + var width = this._customSize.width, height = this._customSize.height; + if (this._running) { + var widgetParent = this.getWidgetParent(); + if (widgetParent) { + width = widgetParent.width * percent.x; + height = widgetParent.height * percent.y; + } else { + width = this._parent.width * percent.x; + height = this._parent.height * percent.y; + } + } + if (this._ignoreSize) + this.setContentSize(this.getVirtualRendererSize()); + else + this.setContentSize(width, height); + + this._customSize.width = width; + this._customSize.height = height; + }, + + _setWidthPercent: function (percent) { + this._sizePercent.x = percent; + var width = this._customSize.width; + if (this._running) { + var widgetParent = this.getWidgetParent(); + width = (widgetParent ? widgetParent.width : this._parent.width) * percent; + } + if (this._ignoreSize) + this._setWidth(this.getVirtualRendererSize().width); + else + this._setWidth(width); + this._customSize.width = width; + }, + _setHeightPercent: function (percent) { + this._sizePercent.y = percent; + var height = this._customSize.height; + if (this._running) { + var widgetParent = this.getWidgetParent(); + height = (widgetParent ? widgetParent.height : this._parent.height) * percent; + } + if (this._ignoreSize) + this._setHeight(this.getVirtualRendererSize().height); + else + this._setHeight(height); + this._customSize.height = height; + }, + + /** + * updates its size by size type and its position by position type. + * @param {cc.Size} [parentSize] parent size + */ + updateSizeAndPosition: function (parentSize) { + if (!parentSize) { + var widgetParent = this.getWidgetParent(); + if (widgetParent) + parentSize = widgetParent.getLayoutSize(); + else + parentSize = this._parent.getContentSize(); + } + + switch (this._sizeType) { + case ccui.Widget.SIZE_ABSOLUTE: + if (this._ignoreSize) + this.setContentSize(this.getVirtualRendererSize()); + else + this.setContentSize(this._customSize); + this._sizePercent.x = (parentSize.width > 0) ? this._customSize.width / parentSize.width : 0; + this._sizePercent.y = (parentSize.height > 0) ? this._customSize.height / parentSize.height : 0; + break; + case ccui.Widget.SIZE_PERCENT: + var cSize = cc.size(parentSize.width * this._sizePercent.x, parentSize.height * this._sizePercent.y); + if (this._ignoreSize) + this.setContentSize(this.getVirtualRendererSize()); + else + this.setContentSize(cSize); + this._customSize.width = cSize.width; + this._customSize.height = cSize.height; + break; + default: + break; + } + this._onSizeChanged(); + var absPos = this.getPosition(); + switch (this._positionType) { + case ccui.Widget.POSITION_ABSOLUTE: + if (parentSize.width <= 0 || parentSize.height <= 0) { + this._positionPercent.x = this._positionPercent.y = 0; + } else { + this._positionPercent.x = absPos.x / parentSize.width; + this._positionPercent.y = absPos.y / parentSize.height; + } + break; + case ccui.Widget.POSITION_PERCENT: + absPos = cc.p(parentSize.width * this._positionPercent.x, parentSize.height * this._positionPercent.y); + break; + default: + break; + } + if (this._parent instanceof ccui.ImageView) { + var renderer = this._parent._imageRenderer; + if (renderer && !renderer._textureLoaded) + return; + } + this.setPosition(absPos); + }, + + /**TEXTURE_RES_TYPE + * Changes the size type of widget. + * @param {ccui.Widget.SIZE_ABSOLUTE|ccui.Widget.SIZE_PERCENT} type that is widget's size type + */ + setSizeType: function (type) { + this._sizeType = type; + if (this._usingLayoutComponent) { + var component = this._getOrCreateLayoutComponent(); + component.setUsingPercentContentSize(this._sizeType === ccui.SIZE_PERCENT); + } + }, + + /** + * Gets the size type of widget. + * @returns {ccui.Widget.SIZE_ABSOLUTE|ccui.Widget.SIZE_PERCENT} that is widget's size type + */ + getSizeType: function () { + return this._sizeType; + }, + + /** + * Ignore the widget size + * @param {Boolean} ignore true that widget will ignore it's size, use texture size, false otherwise. Default value is true. + */ + ignoreContentAdaptWithSize: function (ignore) { + if(this._unifySize){ + this.setContentSize(this._customSize); + return; + } + + if (this._ignoreSize === ignore) + return; + + this._ignoreSize = ignore; + this.setContentSize(ignore ? this.getVirtualRendererSize() : this._customSize); + //this._onSizeChanged(); + }, + + /** + * Gets whether ignore the content size (custom size) + * @returns {boolean} true that widget will ignore it's size, use texture size, false otherwise. + */ + isIgnoreContentAdaptWithSize: function () { + return this._ignoreSize; + }, + + /** + * Get custom size of ccui.Widget + * @returns {cc.Size} + */ + getCustomSize: function () { + return cc.size(this._customSize); + }, + + /** + * Gets layout size of ccui.Widget. + * @returns {cc.Size} + */ + getLayoutSize: function () { + return cc.size(this._contentSize); + }, + + /** + * Returns size percent of ccui.Widget + * @returns {cc.Point} + */ + getSizePercent: function () { + if(this._usingLayoutComponent){ + var component = this._getOrCreateLayoutComponent(); + this._sizePercent = component.getPercentContentSize(); + } + return this._sizePercent; + }, + _getWidthPercent: function () { + return this._sizePercent.x; + }, + _getHeightPercent: function () { + return this._sizePercent.y; + }, + + /** + * Gets world position of ccui.Widget. + * @returns {cc.Point} world position of ccui.Widget. + */ + getWorldPosition: function () { + return this.convertToWorldSpace(cc.p(this._anchorPoint.x * this._contentSize.width, this._anchorPoint.y * this._contentSize.height)); + }, + + /** + * Gets the Virtual Renderer of widget. + * @returns {ccui.Widget} + */ + getVirtualRenderer: function () { + return this; + }, + + /** + * Gets the content size of widget. Content size is widget's texture size. + */ + getVirtualRendererSize: function () { + return cc.size(this._contentSize); + }, + + /** + * call back function called when size changed. + */ + _onSizeChanged: function () { + if(!this._usingLayoutComponent){ + var locChildren = this.getChildren(); + for (var i = 0, len = locChildren.length; i < len; i++) { + var child = locChildren[i]; + if (child instanceof ccui.Widget) + child.updateSizeAndPosition(); + } + this._sizeDirty = false; + } + }, + + /** + * Sets whether the widget is touch enabled. The default value is false, a widget is default to touch disabled + * @param {Boolean} enable true if the widget is touch enabled, false if the widget is touch disabled. + */ + setTouchEnabled: function (enable) { + if (this._touchEnabled === enable) + return; + + this._touchEnabled = enable; //TODO need consider remove and re-add. + if (this._touchEnabled) { + if (!this._touchListener) + this._touchListener = cc.EventListener.create({ + event: cc.EventListener.TOUCH_ONE_BY_ONE, + swallowTouches: true, + onTouchBegan: this.onTouchBegan.bind(this), + onTouchMoved: this.onTouchMoved.bind(this), + onTouchEnded: this.onTouchEnded.bind(this) + }); + cc.eventManager.addListener(this._touchListener, this); + } else { + cc.eventManager.removeListener(this._touchListener); + } + }, + + /** + * Returns whether or not touch is enabled. + * @returns {boolean} true if the widget is touch enabled, false if the widget is touch disabled. + */ + isTouchEnabled: function () { + return this._touchEnabled; + }, + + /** + * Determines if the widget is highlighted + * @returns {boolean} true if the widget is highlighted, false if the widget is not highlighted . + */ + isHighlighted: function () { + return this._highlight; + }, + + /** + * Sets whether the widget is highlighted. The default value is false, a widget is default to not highlighted + * @param highlight true if the widget is highlighted, false if the widget is not highlighted. + */ + setHighlighted: function (highlight) { + if (highlight === this._highlight) + return; + this._highlight = highlight; + if (this._bright) { + if (this._highlight) + this.setBrightStyle(ccui.Widget.BRIGHT_STYLE_HIGH_LIGHT); + else + this.setBrightStyle(ccui.Widget.BRIGHT_STYLE_NORMAL); + } else + this._onPressStateChangedToDisabled(); + }, + + /** + * Determines if the widget is on focused + * @returns {boolean} whether the widget is focused or not + */ + isFocused: function () { + return this._focused; + }, + + /** + * Sets whether the widget is on focused + * The default value is false, a widget is default to not on focused + * @param {boolean} focus pass true to let the widget get focus or pass false to let the widget lose focus + */ + setFocused: function (focus) { + this._focused = focus; + //make sure there is only one focusedWidget + if (focus) { + ccui.Widget._focusedWidget = this; + if (ccui.Widget._focusNavigationController) + ccui.Widget._focusNavigationController._setFirstFocsuedWidget(this); + } + }, + + /** + * returns whether the widget could accept focus. + * @returns {boolean} true represent the widget could accept focus, false represent the widget couldn't accept focus + */ + isFocusEnabled: function () { + return this._focusEnabled; + }, + + /** + * sets whether the widget could accept focus. + * @param {Boolean} enable true represent the widget could accept focus, false represent the widget couldn't accept focus + */ + setFocusEnabled: function (enable) { + this._focusEnabled = enable; + }, + + /** + *

+ * When a widget is in a layout, you could call this method to get the next focused widget within a specified direction.
+ * If the widget is not in a layout, it will return itself + *

+ * @param direction the direction to look for the next focused widget in a layout + * @param current the current focused widget + * @return the next focused widget in a layout + */ + findNextFocusedWidget: function (direction, current) { + if (null === this.onNextFocusedWidget || null == this.onNextFocusedWidget(direction)) { + var isLayout = current instanceof ccui.Layout; + if (this.isFocused() || isLayout) { + var layout = this.getParent(); + if (null === layout || !(layout instanceof ccui.Layout)) { + //the outer layout's default behaviour is : loop focus + if (isLayout) + return current.findNextFocusedWidget(direction, current); + return current; + } else + return layout.findNextFocusedWidget(direction, current); + } else + return current; + } else { + var getFocusWidget = this.onNextFocusedWidget(direction); + this.dispatchFocusEvent(this, getFocusWidget); + return getFocusWidget; + } + }, + + /** + * when a widget calls this method, it will get focus immediately. + */ + requestFocus: function () { + if (this === ccui.Widget._focusedWidget) + return; + this.dispatchFocusEvent(ccui.Widget._focusedWidget, this); + }, + + /** + * no matter what widget object you call this method on , it will return you the exact one focused widget + */ + getCurrentFocusedWidget: function () { + return ccui.Widget._focusedWidget; + }, + + /** + *

+ * When a widget lose/get focus, this method will be called. Be Caution when you provide your own version,
+ * you must call widget.setFocused(true/false) to change the focus state of the current focused widget; + *

+ */ + onFocusChanged: null, + + /** + * use this function to manually specify the next focused widget regards to each direction + */ + onNextFocusedWidget: null, + + /** + * Sends the touch event to widget's parent, its subclass will override it, e.g. ccui.ScrollView, ccui.PageView + * @param {Number} eventType + * @param {ccui.Widget} sender + * @param {cc.Touch} touch + */ + interceptTouchEvent: function (eventType, sender, touch) { + var widgetParent = this.getWidgetParent(); + if (widgetParent) + widgetParent.interceptTouchEvent(eventType, sender, touch); + }, + + /** + * This method is called when a focus change event happens + * @param {ccui.Widget} widgetLostFocus + * @param {ccui.Widget} widgetGetFocus + */ + onFocusChange: function (widgetLostFocus, widgetGetFocus) { + //only change focus when there is indeed a get&lose happens + if (widgetLostFocus) + widgetLostFocus.setFocused(false); + if (widgetGetFocus) + widgetGetFocus.setFocused(true); + }, + + /** + * Dispatch a EventFocus through a EventDispatcher + * @param {ccui.Widget} widgetLostFocus + * @param {ccui.Widget} widgetGetFocus + */ + dispatchFocusEvent: function (widgetLostFocus, widgetGetFocus) { + //if the widgetLoseFocus doesn't get focus, it will use the previous focused widget instead + if (widgetLostFocus && !widgetLostFocus.isFocused()) + widgetLostFocus = ccui.Widget._focusedWidget; + + if (widgetGetFocus !== widgetLostFocus) { + if (widgetGetFocus && widgetGetFocus.onFocusChanged) + widgetGetFocus.onFocusChanged(widgetLostFocus, widgetGetFocus); + if (widgetLostFocus && widgetGetFocus.onFocusChanged) + widgetLostFocus.onFocusChanged(widgetLostFocus, widgetGetFocus); + cc.eventManager.dispatchEvent(new cc.EventFocus(widgetLostFocus, widgetGetFocus)); + } + }, + + /** + * Sets whether the widget is bright. The default value is true, a widget is default to bright + * @param {Boolean} bright true if the widget is bright, false if the widget is dark. + */ + setBright: function (bright) { + this._bright = bright; + if (this._bright) { + this._brightStyle = ccui.Widget.BRIGHT_STYLE_NONE; + this.setBrightStyle(ccui.Widget.BRIGHT_STYLE_NORMAL); + } else + this._onPressStateChangedToDisabled(); + }, + + /** + * To set the bright style of ccui.Widget. + * @param {Number} style BRIGHT_NORMAL the widget is normal state, BRIGHT_HIGHLIGHT the widget is height light state. + */ + setBrightStyle: function (style) { + if (this._brightStyle === style) + return; + + style = style || ccui.Widget.BRIGHT_STYLE_NORMAL; + this._brightStyle = style; + switch (this._brightStyle) { + case ccui.Widget.BRIGHT_STYLE_NORMAL: + this._onPressStateChangedToNormal(); + break; + case ccui.Widget.BRIGHT_STYLE_HIGH_LIGHT: + this._onPressStateChangedToPressed(); + break; + default: + break; + } + }, + + _onPressStateChangedToNormal: function () { + }, + + _onPressStateChangedToPressed: function () { + }, + + _onPressStateChangedToDisabled: function () { + }, + + _updateChildrenDisplayedRGBA: function () { + this.setColor(this.getColor()); + this.setOpacity(this.getOpacity()); + }, + + /** + * A call back function when widget lost of focus. + */ + didNotSelectSelf: function () { + }, + + /** + *

+ * The callback of touch began event.
+ * If the bounding box of ccui.Widget contains the touch point, it will do the following things:
+ * 1. sets highlight state,
+ * 2. sends event to parent widget by interceptTouchEvent
+ * 3. calls the callback of touch began event.
+ * 4. returns true,
+ * otherwise returns false directly.
+ *

+ * @override + * @param {cc.Touch} touch + * @param {cc.Event} event + * @returns {boolean} + */ + onTouchBegan: function (touch, event) { + this._hit = false; + if (this.isVisible() && this.isEnabled() && this._isAncestorsEnabled() && this._isAncestorsVisible(this)) { + var touchPoint = touch.getLocation(); + this._touchBeganPosition.x = touchPoint.x; + this._touchBeganPosition.y = touchPoint.y; + if (this.hitTest(this._touchBeganPosition) && this.isClippingParentContainsPoint(this._touchBeganPosition)) + this._hit = true; + } + if (!this._hit) { + return false; + } + this.setHighlighted(true); + + /* + * Propagate touch events to its parents + */ + if (this._propagateTouchEvents) { + this.propagateTouchEvent(ccui.Widget.TOUCH_BEGAN, this, touch); + } + + this._pushDownEvent(); + return true; + }, + + propagateTouchEvent: function (event, sender, touch) { + var widgetParent = this.getWidgetParent(); + if (widgetParent) { + widgetParent.interceptTouchEvent(event, sender, touch); + } + }, + + /** + *

+ * The callback of touch moved event.
+ * It sets the highlight state by touch, sends event to parent widget by interceptTouchEvent and calls the callback of touch moved event. + *

+ * @param {cc.Touch} touch + * @param {cc.Event} event + */ + onTouchMoved: function (touch, event) { + var touchPoint = touch.getLocation(); + this._touchMovePosition.x = touchPoint.x; + this._touchMovePosition.y = touchPoint.y; + this.setHighlighted(this.hitTest(touchPoint)); + /* + * Propagate touch events to its parents + */ + if (this._propagateTouchEvents) + this.propagateTouchEvent(ccui.Widget.TOUCH_MOVED, this, touch); + this._moveEvent(); + }, + + /** + *

+ * The callback of touch end event + * It sends event to parent widget by interceptTouchEvent, + * calls the callback of touch end event (highlight= true) or touch canceled event (highlight= false). + * sets the highlight state to false , + *

+ * @param touch + * @param event + */ + onTouchEnded: function (touch, event) { + var touchPoint = touch.getLocation(); + this._touchEndPosition.x = touchPoint.x; + this._touchEndPosition.y = touchPoint.y; + /* + * Propagate touch events to its parents + */ + if (this._propagateTouchEvents) + this.propagateTouchEvent(ccui.Widget.TOUCH_ENDED, this, touch); + + var highlight = this._highlight; + this.setHighlighted(false); + if (highlight) + this._releaseUpEvent(); + else + this._cancelUpEvent(); + }, + + /** + * A call back function called when widget is selected, and on touch canceled. + * @param {cc.Point} touchPoint + */ + onTouchCancelled: function (touchPoint) { + this.setHighlighted(false); + this._cancelUpEvent(); + }, + + /** + * A call back function called when widget is selected, and on touch long clicked. + * @param {cc.Point} touchPoint + */ + onTouchLongClicked: function (touchPoint) { + this.longClickEvent(); + }, + + //call back function called widget's state changed to dark. + _pushDownEvent: function () { + if (this._touchEventCallback) + this._touchEventCallback(this, ccui.Widget.TOUCH_BEGAN); + if (this._touchEventListener && this._touchEventSelector) + this._touchEventSelector.call(this._touchEventListener, this, ccui.Widget.TOUCH_BEGAN); + }, + + _moveEvent: function () { + if (this._touchEventCallback) + this._touchEventCallback(this, ccui.Widget.TOUCH_MOVED); + if (this._touchEventListener && this._touchEventSelector) + this._touchEventSelector.call(this._touchEventListener, this, ccui.Widget.TOUCH_MOVED); + }, + + _releaseUpEvent: function () { + if (this._touchEventCallback) + this._touchEventCallback(this, ccui.Widget.TOUCH_ENDED); + if (this._touchEventListener && this._touchEventSelector) + this._touchEventSelector.call(this._touchEventListener, this, ccui.Widget.TOUCH_ENDED); + if (this._clickEventListener) + this._clickEventListener(this); + }, + + _cancelUpEvent: function () { + if (this._touchEventCallback) + this._touchEventCallback(this, ccui.Widget.TOUCH_CANCELED); + if (this._touchEventListener && this._touchEventSelector) + this._touchEventSelector.call(this._touchEventListener, this, ccui.Widget.TOUCH_CANCELED); + }, + + longClickEvent: function () { + //TODO it will implement in v3.1 + }, + + /** + * Sets the touch event target/selector of the ccui.Widget + * @param {Function} selector + * @param {Object} target + */ + addTouchEventListener: function (selector, target) { + if (target === undefined) + this._touchEventCallback = selector; + else { + this._touchEventSelector = selector; + this._touchEventListener = target; + } + }, + + addClickEventListener: function (callback) { + this._clickEventListener = callback; + }, + + /** + * Checks a point if is in widget's space + * @param {cc.Point} pt + * @returns {boolean} true if the point is in widget's space, false otherwise. + */ + hitTest: function (pt) { + var bb = cc.rect(0, 0, this._contentSize.width, this._contentSize.height); + return cc.rectContainsPoint(bb, this.convertToNodeSpace(pt)); + }, + + /** + * returns whether clipping parent widget contains point. + * @param {cc.Point} pt location point + * @returns {Boolean} + */ + isClippingParentContainsPoint: function (pt) { + this._affectByClipping = false; + var parent = this.getParent(); + var clippingParent = null; + while (parent) { + if (parent instanceof ccui.Layout) { + if (parent.isClippingEnabled()) { + this._affectByClipping = true; + clippingParent = parent; + break; + } + } + parent = parent.getParent(); + } + + if (!this._affectByClipping) + return true; + + if (clippingParent) { + if (clippingParent.hitTest(pt)) + return clippingParent.isClippingParentContainsPoint(pt); + return false; + } + return true; + }, + + /** + * Calls the checkChildInfo of widget's parent, its subclass will override it. + * @param {number} handleState + * @param {ccui.Widget} sender + * @param {cc.Point} touchPoint + */ + checkChildInfo: function (handleState, sender, touchPoint) { + var widgetParent = this.getWidgetParent(); + if (widgetParent) + widgetParent.checkChildInfo(handleState, sender, touchPoint); + }, + + /** + * Changes the position (x,y) of the widget . + * The original point (0,0) is at the left-bottom corner of screen. + * @override + * @param {cc.Point|Number} pos + * @param {Number} [posY] + */ + setPosition: function (pos, posY) { + if (!this._usingLayoutComponent && this._running) { + var widgetParent = this.getWidgetParent(); + if (widgetParent) { + var pSize = widgetParent.getContentSize(); + if (pSize.width <= 0 || pSize.height <= 0) { + this._positionPercent.x = 0; + this._positionPercent.y = 0; + } else { + if (posY === undefined) { + this._positionPercent.x = pos.x / pSize.width; + this._positionPercent.y = pos.y / pSize.height; + } else { + this._positionPercent.x = pos / pSize.width; + this._positionPercent.y = posY / pSize.height; + } + } + } + } + + cc.Node.prototype.setPosition.call(this, pos, posY); + //this._positionType = ccui.Widget.POSITION_ABSOLUTE; + }, + + setPositionX: function (x) { + if (this._running) { + var widgetParent = this.getWidgetParent(); + if (widgetParent) { + var pw = widgetParent.width; + if (pw <= 0) + this._positionPercent.x = 0; + else + this._positionPercent.x = x / pw; + } + } + + cc.Node.prototype.setPositionX.call(this, x); + }, + setPositionY: function (y) { + if (this._running) { + var widgetParent = this.getWidgetParent(); + if (widgetParent) { + var ph = widgetParent.height; + if (ph <= 0) + this._positionPercent.y = 0; + else + this._positionPercent.y = y / ph; + } + } + + cc.Node.prototype.setPositionY.call(this, y); + }, + + /** + * Changes the position (x,y) of the widget + * @param {cc.Point} percent + */ + setPositionPercent: function (percent) { + if (this._usingLayoutComponent){ + var component = this._getOrCreateLayoutComponent(); + component.setPositionPercentX(percent.x); + component.setPositionPercentY(percent.y); + component.refreshLayout(); + return; + }else{ + this._setXPercent(percent.x); + this._setYPercent(percent.y); + } + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }, + _setXPercent: function (percent) { + if (this._usingLayoutComponent){ + var component = this._getOrCreateLayoutComponent(); + component.setPositionPercentX(percent.x); + component.refreshLayout(); + return; + } + this._positionPercent.x = percent; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }, + _setYPercent: function (percent) { + if (this._usingLayoutComponent){ + var component = this._getOrCreateLayoutComponent(); + component.setPositionPercentY(percent.x); + component.refreshLayout(); + return; + } + this._positionPercent.y = percent; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }, + + /** + * Gets the percent (x,y) of the widget + * @returns {cc.Point} The percent (x,y) of the widget in OpenGL coordinates + */ + getPositionPercent: function () { + if (this._usingLayoutComponent) { + var component = this._getOrCreateLayoutComponent(); + this._positionPercent.x = component.getPositionPercentX(); + this._positionPercent.y = component.getPositionPercentY(); + } + return cc.p(this._positionPercent); + }, + + _getXPercent: function () { + if (this._usingLayoutComponent) { + var component = this._getOrCreateLayoutComponent(); + this._positionPercent.x = component.getPositionPercentX(); + this._positionPercent.y = component.getPositionPercentY(); + } + return this._positionPercent.x; + }, + _getYPercent: function () { + if (this._usingLayoutComponent) { + var component = this._getOrCreateLayoutComponent(); + this._positionPercent.x = component.getPositionPercentX(); + this._positionPercent.y = component.getPositionPercentY(); + } + return this._positionPercent.y; + }, + + /** + * Changes the position type of the widget + * @param {Number} type the position type of widget + */ + setPositionType: function (type) { + this._positionType = type; + if(this._usingLayoutComponent){ + var component = this._getOrCreateLayoutComponent(); + if (type === ccui.POSITION_ABSOLUTE){ + component.setPositionPercentXEnabled(false); + component.setPositionPercentYEnabled(false); + } else { + component.setPositionPercentXEnabled(true); + component.setPositionPercentYEnabled(true); + } + } + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }, + + /** + * Gets the position type of the widget + * @returns {Number} the position type of widget + */ + getPositionType: function () { + return this._positionType; + }, + + /** + * Sets whether the widget should be flipped horizontally or not. + * @param {Boolean} flipX true if the widget should be flipped horizontally, false otherwise. + */ + setFlippedX: function (flipX) { + var realScale = this.getScaleX(); + this._flippedX = flipX; + this.setScaleX(realScale); + }, + + /** + *

+ * Returns the flag which indicates whether the widget is flipped horizontally or not.
+ * It only flips the texture of the widget, and not the texture of the widget's children.
+ * Also, flipping the texture doesn't alter the anchorPoint.
+ * If you want to flip the anchorPoint too, and/or to flip the children too use:
+ * widget.setScaleX(sprite.getScaleX() * -1); + *

+ * @returns {Boolean} true if the widget is flipped horizontally, false otherwise. + */ + isFlippedX: function () { + return this._flippedX; + }, + + /** + * Sets whether the widget should be flipped vertically or not. + * @param {Boolean} flipY true if the widget should be flipped vertically, false otherwise. + */ + setFlippedY: function (flipY) { + var realScale = this.getScaleY(); + this._flippedY = flipY; + this.setScaleY(realScale); + }, + + /** + *

+ * Return the flag which indicates whether the widget is flipped vertically or not.
+ * It only flips the texture of the widget, and not the texture of the widget's children.
+ * Also, flipping the texture doesn't alter the anchorPoint.
+ * If you want to flip the anchorPoint too, and/or to flip the children too use:
+ * widget.setScaleY(widget.getScaleY() * -1); + *

+ * @returns {Boolean} true if the widget is flipped vertically, false otherwise. + */ + isFlippedY: function () { + return this._flippedY; + }, + + _adaptRenderers: function () { + }, + + /** + * Determines if the widget is bright + * @returns {boolean} true if the widget is bright, false if the widget is dark. + */ + isBright: function () { + return this._bright; + }, + + /** + * Determines if the widget is enabled + * @returns {boolean} + */ + isEnabled: function () { + return this._enabled; + }, + + /** + * Gets the left boundary position of this widget. + * @returns {number} + */ + getLeftBoundary: function () { + return this.getPositionX() - this._getAnchorX() * this._contentSize.width; + }, + + /** + * Gets the bottom boundary position of this widget. + * @returns {number} + */ + getBottomBoundary: function () { + return this.getPositionY() - this._getAnchorY() * this._contentSize.height; + }, + + /** + * Gets the right boundary position of this widget. + * @returns {number} + */ + getRightBoundary: function () { + return this.getLeftBoundary() + this._contentSize.width; + }, + + /** + * Gets the top boundary position of this widget. + * @returns {number} + */ + getTopBoundary: function () { + return this.getBottomBoundary() + this._contentSize.height; + }, + + /** + * Gets the position of touch began event. + * @returns {cc.Point} + */ + getTouchBeganPosition: function () { + return cc.p(this._touchBeganPosition); + }, + + /** + * Gets the position of touch moved event + * @returns {cc.Point} + */ + getTouchMovePosition: function () { + return cc.p(this._touchMovePosition); + }, + + /** + * Gets the position of touch end event + * @returns {cc.Point} + */ + getTouchEndPosition: function () { + return cc.p(this._touchEndPosition); + }, + + /** + * get widget type + * @returns {ccui.Widget.TYPE_WIDGET|ccui.Widget.TYPE_CONTAINER} + */ + getWidgetType: function () { + return this._widgetType; + }, + + /** + * Gets LayoutParameter of widget. + * @param {ccui.LayoutParameter} parameter + */ + setLayoutParameter: function (parameter) { + if (!parameter) + return; + this._layoutParameterDictionary[parameter.getLayoutType()] = parameter; + this._layoutParameterType = parameter.getLayoutType(); + }, + + /** + * Gets layout parameter + * @param {ccui.LayoutParameter.NONE|ccui.LayoutParameter.LINEAR|ccui.LayoutParameter.RELATIVE} type + * @returns {ccui.LayoutParameter} + */ + getLayoutParameter: function (type) { + type = type || this._layoutParameterType; + return this._layoutParameterDictionary[type]; + }, + + /** + * Returns the "class name" of widget. + * @returns {string} + */ + getDescription: function () { + return "Widget"; + }, + + /** + * Clones a new widget. + * @returns {ccui.Widget} + */ + clone: function () { + var clonedWidget = this._createCloneInstance(); + clonedWidget._copyProperties(this); + clonedWidget._copyClonedWidgetChildren(this); + return clonedWidget; + }, + + _createCloneInstance: function () { + return new ccui.Widget(); + }, + + _copyClonedWidgetChildren: function (model) { + var widgetChildren = model.getChildren(); + for (var i = 0; i < widgetChildren.length; i++) { + var locChild = widgetChildren[i]; + if (locChild instanceof ccui.Widget) + this.addChild(locChild.clone()); + } + }, + + _copySpecialProperties: function (model) { + }, + + _copyProperties: function (widget) { + this.setEnabled(widget.isEnabled()); + this.setVisible(widget.isVisible()); + this.setBright(widget.isBright()); + this.setTouchEnabled(widget.isTouchEnabled()); + this.setLocalZOrder(widget.getLocalZOrder()); + this.setTag(widget.getTag()); + this.setName(widget.getName()); + this.setActionTag(widget.getActionTag()); + + this._ignoreSize = widget._ignoreSize; + + this.setContentSize(widget._contentSize); + this._customSize.width = widget._customSize.width; + this._customSize.height = widget._customSize.height; + + this._copySpecialProperties(widget); + this._sizeType = widget.getSizeType(); + this._sizePercent.x = widget._sizePercent.x; + this._sizePercent.y = widget._sizePercent.y; + + this._positionType = widget._positionType; + this._positionPercent.x = widget._positionPercent.x; + this._positionPercent.y = widget._positionPercent.y; + + this.setPosition(widget.getPosition()); + this.setAnchorPoint(widget.getAnchorPoint()); + this.setScaleX(widget.getScaleX()); + this.setScaleY(widget.getScaleY()); + this.setRotation(widget.getRotation()); + this.setRotationX(widget.getRotationX()); + this.setRotationY(widget.getRotationY()); + this.setFlippedX(widget.isFlippedX()); + this.setFlippedY(widget.isFlippedY()); + this.setColor(widget.getColor()); + this.setOpacity(widget.getOpacity()); + + this._touchEventCallback = widget._touchEventCallback; + this._touchEventListener = widget._touchEventListener; + this._touchEventSelector = widget._touchEventSelector; + this._clickEventListener = widget._clickEventListener; + this._focused = widget._focused; + this._focusEnabled = widget._focusEnabled; + this._propagateTouchEvents = widget._propagateTouchEvents; + + for (var key in widget._layoutParameterDictionary) { + var parameter = widget._layoutParameterDictionary[key]; + if (parameter) + this.setLayoutParameter(parameter.clone()); + } + }, + + /*temp action*/ + setActionTag: function (tag) { + this._actionTag = tag; + }, + + getActionTag: function () { + return this._actionTag; + }, + + /** + * Gets the left boundary position of this widget. + * @deprecated since v3.0, please use getLeftBoundary instead. + * @returns {number} + */ + getLeftInParent: function () { + cc.log("getLeftInParent is deprecated. Please use getLeftBoundary instead."); + return this.getLeftBoundary(); + }, + + /** + * Gets the bottom boundary position of this widget. + * @deprecated since v3.0, please use getBottomBoundary instead. + * @returns {number} + */ + getBottomInParent: function () { + cc.log("getBottomInParent is deprecated. Please use getBottomBoundary instead."); + return this.getBottomBoundary(); + }, + + /** + * Gets the right boundary position of this widget. + * @deprecated since v3.0, please use getRightBoundary instead. + * @returns {number} + */ + getRightInParent: function () { + cc.log("getRightInParent is deprecated. Please use getRightBoundary instead."); + return this.getRightBoundary(); + }, + + /** + * Gets the top boundary position of this widget. + * @deprecated since v3.0, please use getTopBoundary instead. + * @returns {number} + */ + getTopInParent: function () { + cc.log("getTopInParent is deprecated. Please use getTopBoundary instead."); + return this.getTopBoundary(); + }, + + /** + * Gets the touch end point of widget when widget is selected. + * @deprecated since v3.0, please use getTouchEndPosition instead. + * @returns {cc.Point} the touch end point. + */ + getTouchEndPos: function () { + cc.log("getTouchEndPos is deprecated. Please use getTouchEndPosition instead."); + return this.getTouchEndPosition(); + }, + + /** + *Gets the touch move point of widget when widget is selected. + * @deprecated since v3.0, please use getTouchMovePosition instead. + * @returns {cc.Point} the touch move point. + */ + getTouchMovePos: function () { + cc.log("getTouchMovePos is deprecated. Please use getTouchMovePosition instead."); + return this.getTouchMovePosition(); + }, + + /** + * Checks a point if in parent's area. + * @deprecated since v3.0, please use isClippingParentContainsPoint instead. + * @param {cc.Point} pt + * @returns {Boolean} + */ + clippingParentAreaContainPoint: function (pt) { + cc.log("clippingParentAreaContainPoint is deprecated. Please use isClippingParentContainsPoint instead."); + this.isClippingParentContainsPoint(pt); + }, + + /** + * Gets the touch began point of widget when widget is selected. + * @deprecated since v3.0, please use getTouchBeganPosition instead. + * @returns {cc.Point} the touch began point. + */ + getTouchStartPos: function () { + cc.log("getTouchStartPos is deprecated. Please use getTouchBeganPosition instead."); + return this.getTouchBeganPosition(); + }, + + /** + * Changes the size that is widget's size + * @deprecated since v3.0, please use setContentSize instead. + * @param {cc.Size} size that is widget's size + */ + setSize: function (size) { + this.setContentSize(size); + }, + + /** + * Returns size of widget + * @deprecated since v3.0, please use getContentSize instead. + * @returns {cc.Size} + */ + getSize: function () { + return this.getContentSize(); + }, + + /** + * Adds a node for widget (this function is deleted in -x) + * @param {cc.Node} node + * @param {Number} zOrder + * @param {Number} tag + * @deprecated since v3.0, please use addChild instead. + */ + addNode: function (node, zOrder, tag) { + if (node instanceof ccui.Widget) { + cc.log("Please use addChild to add a Widget."); + return; + } + cc.Node.prototype.addChild.call(this, node, zOrder, tag); + this._nodes.push(node); + }, + + /** + * Gets node by tag + * @deprecated since v3.0, please use getChildByTag instead. + * @param {Number} tag + * @returns {cc.Node} + */ + getNodeByTag: function (tag) { + var _nodes = this._nodes; + for (var i = 0; i < _nodes.length; i++) { + var node = _nodes[i]; + if (node && node.getTag() === tag) { + return node; + } + } + return null; + }, + + /** + * Returns all children. + * @deprecated since v3.0, please use getChildren instead. + * @returns {Array} + */ + getNodes: function () { + return this._nodes; + }, + + /** + * Removes a node from ccui.Widget + * @deprecated since v3.0, please use removeChild instead. + * @param {cc.Node} node + * @param {Boolean} cleanup + */ + removeNode: function (node, cleanup) { + cc.Node.prototype.removeChild.call(this, node, cleanup); + cc.arrayRemoveObject(this._nodes, node); + }, + + _getNormalGLProgram: function () { + return cc.shaderCache.programForKey(cc.SHADER_SPRITE_POSITION_TEXTURECOLOR); + }, + + _getGrayGLProgram: function () { + return cc.shaderCache.programForKey(cc.SHADER_SPRITE_POSITION_TEXTURECOLOR_GRAY); + }, + + /** + * Removes node by tag + * @deprecated since v3.0, please use removeChildByTag instead. + * @param {Number} tag + * @param {Boolean} [cleanup] + */ + removeNodeByTag: function (tag, cleanup) { + var node = this.getChildByTag(tag); + if (!node) + cc.log("cocos2d: removeNodeByTag(tag = %d): child not found!", tag); + else + this.removeChild(node, cleanup); + }, + + /** + * Removes all node + * @deprecated since v3.0, please use removeAllChildren instead. + */ + removeAllNodes: function () { + for (var i = 0; i < this._nodes.length; i++) { + var node = this._nodes[i]; + cc.Node.prototype.removeChild.call(this, node); + } + this._nodes.length = 0; + }, + + _findLayout: function () { + cc.renderer.childrenOrderDirty = true; + var layout = this._parent; + while (layout) { + if (layout._doLayout) { + layout._doLayoutDirty = true; + break; + } else + layout = layout._parent; + } + }, + + /** + * @since v3.2 + * @returns {boolean} true represent the widget use Unify Size, false represent the widget couldn't use Unify Size + */ + isUnifySizeEnabled: function(){ + return this._unifySize; + }, + + /** + * @since v3.2 + * @param {Boolean} enable enable Unify Size of a widget + */ + setUnifySizeEnabled: function(enable){ + this._unifySize = enable; + }, + + //v3.3 + _ccEventCallback: null, + /** + * Set a event handler to the widget in order to use cocostudio editor and framework + * @since v3.3 + * @param {function} callback + */ + addCCSEventListener: function (callback) { + this._ccEventCallback = callback; + }, + + //override the scale functions. + setScaleX: function (scaleX) { + if (this._flippedX) + scaleX = scaleX * -1; + cc.Node.prototype.setScaleX.call(this, scaleX); + }, + setScaleY: function (scaleY) { + if (this._flippedY) + scaleY = scaleY * -1; + cc.Node.prototype.setScaleY.call(this, scaleY); + }, + setScale: function (scaleX, scaleY) { + if (scaleY === undefined) + scaleY = scaleX; + this.setScaleX(scaleX); + this.setScaleY(scaleY); + }, + + getScaleX: function () { + var originalScale = cc.Node.prototype.getScaleX.call(this); + if (this._flippedX) + originalScale = originalScale * -1.0; + return originalScale; + }, + getScaleY: function () { + var originalScale = cc.Node.prototype.getScaleY.call(this); + if (this._flippedY) + originalScale = originalScale * -1.0; + return originalScale; + }, + getScale: function () { + if (this.getScaleX() !== this.getScaleY()) + cc.log("Widget#scale. ScaleX != ScaleY. Don't know which one to return"); + return this.getScaleX(); + }, + + /** + * Sets callback name to widget. + * @since v3.3 + * @param {String} callbackName + */ + setCallbackName: function (callbackName) { + this._callbackName = callbackName; + }, + + /** + * Gets callback name of widget + * @since v3.3 + * @returns {String|Null} + */ + getCallbackName: function () { + return this._callbackName; + }, + + /** + * Sets callback type to widget + * @since v3.3 + * @param {String} callbackType + */ + setCallbackType: function (callbackType) { + this._callbackType = callbackType; + }, + + /** + * Gets callback type of widget + * @since v3.3 + * @returns {String|null} + */ + getCallbackType: function () { + return this._callbackType; + }, + + /** + * Whether enable layout component of a widget + * @since v3.3 + * @param {Boolean} enable enable layout Component of a widget + */ + setLayoutComponentEnabled: function(enable){ + this._usingLayoutComponent = enable; + }, + + /** + * Returns whether enable layout component of a widget + * @return {Boolean} true represent the widget use Layout Component, false represent the widget couldn't use Layout Component. + */ + isLayoutComponentEnabled: function(){ + return this._usingLayoutComponent; + }, + + + _createRenderCmd: function () { + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) + return new ccui.Widget.WebGLRenderCmd(this); + else + return new ccui.Widget.CanvasRenderCmd(this); + } +}); + +var _p = ccui.Widget.prototype; + +// Extended properties +/** @expose */ +_p.xPercent; +cc.defineGetterSetter(_p, "xPercent", _p._getXPercent, _p._setXPercent); +/** @expose */ +_p.yPercent; +cc.defineGetterSetter(_p, "yPercent", _p._getYPercent, _p._setYPercent); +/** @expose */ +_p.widthPercent; +cc.defineGetterSetter(_p, "widthPercent", _p._getWidthPercent, _p._setWidthPercent); +/** @expose */ +_p.heightPercent; +cc.defineGetterSetter(_p, "heightPercent", _p._getHeightPercent, _p._setHeightPercent); +/** @expose */ +_p.widgetParent; +cc.defineGetterSetter(_p, "widgetParent", _p.getWidgetParent); +/** @expose */ +_p.enabled; +cc.defineGetterSetter(_p, "enabled", _p.isEnabled, _p.setEnabled); +/** @expose */ +_p.focused; +cc.defineGetterSetter(_p, "focused", _p.isFocused, _p.setFocused); +/** @expose */ +_p.sizeType; +cc.defineGetterSetter(_p, "sizeType", _p.getSizeType, _p.setSizeType); +/** @expose */ +_p.widgetType; +cc.defineGetterSetter(_p, "widgetType", _p.getWidgetType); +/** @expose */ +_p.touchEnabled; +cc.defineGetterSetter(_p, "touchEnabled", _p.isTouchEnabled, _p.setTouchEnabled); +/** @expose */ +_p.updateEnabled; +cc.defineGetterSetter(_p, "updateEnabled", _p.isUpdateEnabled, _p.setUpdateEnabled); +/** @expose */ +_p.bright; +cc.defineGetterSetter(_p, "bright", _p.isBright, _p.setBright); +/** @expose */ +_p.name; +cc.defineGetterSetter(_p, "name", _p.getName, _p.setName); +/** @expose */ +_p.actionTag; +cc.defineGetterSetter(_p, "actionTag", _p.getActionTag, _p.setActionTag); +/** @expose */ +_p.opacity; +cc.defineGetterSetter(_p, "opacity", _p.getOpacity, _p.setOpacity); + +_p = null; + +/** + * allocates and initializes a UIWidget. + * @deprecated + * @return {ccui.Widget} + */ +ccui.Widget.create = function () { + return new ccui.Widget(); +}; + +ccui.Widget._focusedWidget = null; //both layout & widget will be stored in this variable +ccui.Widget._focusNavigationController = null; + +/** + * call this method with parameter true to enable the Android Dpad focus navigation feature + * @note it doesn't implemented on Web + * @param {Boolean} enable set true to enable dpad focus navigation, otherwise disable dpad focus navigation + */ +ccui.Widget.enableDpadNavigation = function (enable) { + if (enable) { + if (null == ccui.Widget._focusNavigationController) { + ccui.Widget._focusNavigationController = new ccui._FocusNavigationController(); + if (ccui.Widget._focusedWidget) { + ccui.Widget._focusNavigationController._setFirstFocsuedWidget(ccui.Widget._focusedWidget); + } + } + ccui.Widget._focusNavigationController.enableFocusNavigation(true); + } else { + if (ccui.Widget._focusNavigationController) { + ccui.Widget._focusNavigationController.enableFocusNavigation(false); + ccui.Widget._focusNavigationController = null; + } + } +}; + +/** + * Gets the focused widget of current stage. + * @function + * @returns {null|ccui.Widget} + */ +ccui.Widget.getCurrentFocusedWidget = function () { + return ccui.Widget._focusedWidget; +}; + +// Constants +//bright style +/** + * None bright style of ccui.Widget. + * @constant + * @type {number} + */ +ccui.Widget.BRIGHT_STYLE_NONE = -1; +/** + * Normal bright style of ccui.Widget. + * @constant + * @type {number} + */ +ccui.Widget.BRIGHT_STYLE_NORMAL = 0; +/** + * Light bright style of ccui.Widget. + * @constant + * @type {number} + */ +ccui.Widget.BRIGHT_STYLE_HIGH_LIGHT = 1; + +//widget type +/** + * The type code of Widget for ccui controls. + * @constant + * @type {number} + */ +ccui.Widget.TYPE_WIDGET = 0; +/** + * The type code of Container for ccui controls. + * @constant + * @type {number} + */ +ccui.Widget.TYPE_CONTAINER = 1; + +//Focus Direction +/** + * The left of Focus direction for ccui.Widget + * @constant + * @type {number} + */ +ccui.Widget.LEFT = 0; +/** + * The right of Focus direction for ccui.Widget + * @constant + * @type {number} + */ +ccui.Widget.RIGHT = 1; +/** + * The up of Focus direction for ccui.Widget + * @constant + * @type {number} + */ +ccui.Widget.UP = 2; +/** + * The down of Focus direction for ccui.Widget + * @constant + * @type {number} + */ +ccui.Widget.DOWN = 3; + +//texture resource type +/** + * The image file texture type of ccui.Widget loads. + * @constant + * @type {number} + */ +ccui.Widget.LOCAL_TEXTURE = 0; +/** + * The sprite frame texture type of ccui.Widget loads. + * @constant + * @type {number} + */ +ccui.Widget.PLIST_TEXTURE = 1; + +//touch event type +/** + * The touch began type of ccui.Widget's touch event + * @constant + * @type {number} + */ +ccui.Widget.TOUCH_BEGAN = 0; +/** + * The touch moved type of ccui.Widget's touch event + * @constant + * @type {number} + */ +ccui.Widget.TOUCH_MOVED = 1; +/** + * The touch end type of ccui.Widget's touch event + * @constant + * @type {number} + */ +ccui.Widget.TOUCH_ENDED = 2; +/** + * The touch canceled type of ccui.Widget's touch event + * @constant + * @type {number} + */ +ccui.Widget.TOUCH_CANCELED = 3; + +//size type +/** + * The absolute of ccui.Widget's size type. + * @constant + * @type {number} + */ +ccui.Widget.SIZE_ABSOLUTE = 0; +/** + * The percent of ccui.Widget's size type. + * @constant + * @type {number} + */ +ccui.Widget.SIZE_PERCENT = 1; + +//position type +/** + * The absolute of ccui.Widget's position type. + * @constant + * @type {number} + */ +ccui.Widget.POSITION_ABSOLUTE = 0; +/** + * The percent of ccui.Widget's position type. + * @constant + * @type {number} + */ +ccui.Widget.POSITION_PERCENT = 1; diff --git a/frameworks/cocos2d-html5/extensions/ccui/base-classes/UIWidgetRenderCmd.js b/frameworks/cocos2d-html5/extensions/ccui/base-classes/UIWidgetRenderCmd.js new file mode 100644 index 0000000..fca76f9 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/base-classes/UIWidgetRenderCmd.js @@ -0,0 +1,126 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +cc.game.addEventListener(cc.game.EVENT_RENDERER_INITED, function () { + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) { + ccui.Widget.CanvasRenderCmd = function (renderable) { + this._pNodeCmdCtor(renderable); + this._needDraw = false; + }; + + var proto = ccui.Widget.CanvasRenderCmd.prototype = Object.create(cc.ProtectedNode.CanvasRenderCmd.prototype); + proto.constructor = ccui.Widget.CanvasRenderCmd; + + proto.visit = function (parentCmd) { + var node = this._node, renderer = cc.renderer; + + parentCmd = parentCmd || this.getParentRenderCmd(); + if (parentCmd) + this._curLevel = parentCmd._curLevel + 1; + + if (isNaN(node._customZ)) { + node._vertexZ = renderer.assignedZ; + renderer.assignedZ += renderer.assignedZStep; + } + + node._adaptRenderers(); + this._syncStatus(parentCmd); + }; + + proto.transform = function (parentCmd, recursive) { + if (!this._transform) { + this._transform = {a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0}; + this._worldTransform = {a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0}; + } + + var node = this._node; + if (node._visible && node._running) { + node._adaptRenderers(); + if(!this._usingLayoutComponent){ + var widgetParent = node.getWidgetParent(); + if (widgetParent) { + var parentSize = widgetParent.getContentSize(); + if (parentSize.width !== 0 && parentSize.height !== 0) { + node._position.x = parentSize.width * node._positionPercent.x; + node._position.y = parentSize.height * node._positionPercent.y; + } + } + } + this.pNodeTransform(parentCmd, recursive); + } + }; + + proto.widgetTransform = proto.transform; + } else { + ccui.Widget.WebGLRenderCmd = function (renderable) { + this._pNodeCmdCtor(renderable); + this._needDraw = false; + }; + + var proto = ccui.Widget.WebGLRenderCmd.prototype = Object.create(cc.ProtectedNode.WebGLRenderCmd.prototype); + proto.constructor = ccui.Widget.WebGLRenderCmd; + + proto.visit = function (parentCmd) { + var node = this._node, renderer = cc.renderer; + + parentCmd = parentCmd || this.getParentRenderCmd(); + if (parentCmd) + this._curLevel = parentCmd._curLevel + 1; + + if (isNaN(node._customZ)) { + node._vertexZ = renderer.assignedZ; + renderer.assignedZ += renderer.assignedZStep; + } + + node._adaptRenderers(); + this._syncStatus(parentCmd); + }; + + proto.transform = function (parentCmd, recursive) { + if (!this._transform) { + this._transform = {a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0}; + this._worldTransform = {a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0}; + } + + var node = this._node; + if (node._visible && node._running) { + node._adaptRenderers(); + + if(!this._usingLayoutComponent) { + var widgetParent = node.getWidgetParent(); + if (widgetParent) { + var parentSize = widgetParent.getContentSize(); + if (parentSize.width !== 0 && parentSize.height !== 0) { + node._position.x = parentSize.width * node._positionPercent.x; + node._position.y = parentSize.height * node._positionPercent.y; + } + } + } + this.pNodeTransform(parentCmd, recursive); + } + }; + + proto.widgetTransform = proto.transform; + } +}); diff --git a/frameworks/cocos2d-html5/extensions/ccui/layouts/UIHBox.js b/frameworks/cocos2d-html5/extensions/ccui/layouts/UIHBox.js new file mode 100644 index 0000000..f808668 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/layouts/UIHBox.js @@ -0,0 +1,55 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The horizontal box of Cocos UI. Its layout type is ccui.Layout.LINEAR_HORIZONTAL. + * @class + * @extends ccui.Layout + */ +ccui.HBox = ccui.Layout.extend(/** @lends ccui.HBox# */{ + /** + * The constructor of ccui.HBox + * @function + * @param {cc.Size} [size] + */ + ctor: function(size){ + ccui.Layout.prototype.ctor.call(this); + this.setLayoutType(ccui.Layout.LINEAR_HORIZONTAL); + + if(size) { + this.setContentSize(size); + } + } +}); + +/** + * Creates a HBox object + * @deprecated since v3.0, please use new ccui.HBox(size) instead. + * @param {cc.Size} size + * @returns {ccui.HBox} + */ +ccui.HBox.create = function(size){ + return new ccui.HBox(size); +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/ccui/layouts/UILayout.js b/frameworks/cocos2d-html5/extensions/ccui/layouts/UILayout.js new file mode 100644 index 0000000..34361aa --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/layouts/UILayout.js @@ -0,0 +1,1571 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * ccui.Layout is the base class of ccui.PageView and ccui.ScrollView, it does layout by layout manager + * and clips area by its _clippingStencil when clippingEnabled is true. + * @class + * @extends ccui.Widget + * + * @property {Boolean} clippingEnabled - Indicate whether clipping is enabled + * @property {ccui.Layout.CLIPPING_STENCIL|ccui.Layout.CLIPPING_SCISSOR} clippingType + * @property {ccui.Layout.ABSOLUTE|ccui.Layout.LINEAR_VERTICAL|ccui.Layout.LINEAR_HORIZONTAL|ccui.Layout.RELATIVE} layoutType + * + */ +ccui.Layout = ccui.Widget.extend(/** @lends ccui.Layout# */{ + _clippingEnabled: false, + _backGroundScale9Enabled: null, + _backGroundImage: null, + _backGroundImageFileName: null, + _backGroundImageCapInsets: null, + _colorType: null, + _bgImageTexType: ccui.Widget.LOCAL_TEXTURE, + _colorRender: null, + _gradientRender: null, + _color: null, + _startColor: null, + _endColor: null, + _alongVector: null, + _opacity: 255, + _backGroundImageTextureSize: null, + _layoutType: null, + _doLayoutDirty: true, + _clippingRectDirty: true, + _clippingType: null, + _clippingStencil: null, + _scissorRectDirty: false, + _clippingRect: null, + _clippingParent: null, + _className: "Layout", + _backGroundImageColor: null, + _finalPositionX: 0, + _finalPositionY: 0, + + _backGroundImageOpacity: 0, + + _loopFocus: false, //whether enable loop focus or not + __passFocusToChild: true, //on default, it will pass the focus to the next nearest widget + _isFocusPassing: false, //when finding the next focused widget, use this variable to pass focus between layout & widget + _isInterceptTouch: false, + + /** + * Allocates and initializes an UILayout. + * Constructor of ccui.Layout + * @function + * @example + * // example + * var uiLayout = new ccui.Layout(); + */ + ctor: function () { + this._layoutType = ccui.Layout.ABSOLUTE; + this._widgetType = ccui.Widget.TYPE_CONTAINER; + this._clippingType = ccui.Layout.CLIPPING_SCISSOR; + this._colorType = ccui.Layout.BG_COLOR_NONE; + + ccui.Widget.prototype.ctor.call(this); + + this.ignoreContentAdaptWithSize(false); + this.setContentSize(cc.size(0, 0)); + this.setAnchorPoint(0, 0); + this.onPassFocusToChild = this._findNearestChildWidgetIndex.bind(this); + + this._backGroundImageCapInsets = cc.rect(0, 0, 0, 0); + + this._color = cc.color(255, 255, 255, 255); + this._startColor = cc.color(255, 255, 255, 255); + this._endColor = cc.color(255, 255, 255, 255); + this._alongVector = cc.p(0, -1); + this._backGroundImageTextureSize = cc.size(0, 0); + + this._clippingRect = cc.rect(0, 0, 0, 0); + this._backGroundImageColor = cc.color(255, 255, 255, 255); + }, + + /** + * Calls its parent's onEnter, and calls its clippingStencil's onEnter if clippingStencil isn't null. + * @override + */ + onEnter: function () { + ccui.Widget.prototype.onEnter.call(this); + if (this._clippingStencil) + this._clippingStencil._performRecursive(cc.Node._stateCallbackType.onEnter); + this._doLayoutDirty = true; + this._clippingRectDirty = true; + }, + + /** + * Calls its parent's onExit, and calls its clippingStencil's onExit if clippingStencil isn't null. + * @override + */ + onExit: function () { + ccui.Widget.prototype.onExit.call(this); + if (this._clippingStencil) + this._clippingStencil._performRecursive(cc.Node._stateCallbackType.onExit); + }, + + /** + *

+ * Calls adaptRenderers (its subclass will override it.) and do layout. + * If clippingEnabled is true, it will clip/scissor area. + *

+ * @override + * @param {cc.Node} [parent] + */ + visit: function (parent) { + var cmd = this._renderCmd, parentCmd = parent ? parent._renderCmd : null; + + // quick return if not visible + if (!this._visible) { + cmd._propagateFlagsDown(parentCmd); + return; + } + + this._adaptRenderers(); + this._doLayout(); + + var renderer = cc.renderer; + cmd.visit(parentCmd); + + var stencilClipping = this._clippingEnabled && this._clippingType === ccui.Layout.CLIPPING_STENCIL; + var scissorClipping = this._clippingEnabled && this._clippingType === ccui.Layout.CLIPPING_SCISSOR; + + if (stencilClipping) { + cmd.stencilClippingVisit(parentCmd); + } + else if (scissorClipping) { + cmd.scissorClippingVisit(parentCmd); + } + + var i, children = this._children, len = children.length, child; + var j, pChildren = this._protectedChildren, pLen = pChildren.length, pChild; + + if (this._reorderChildDirty) this.sortAllChildren(); + if (this._reorderProtectedChildDirty) this.sortAllProtectedChildren(); + // draw children zOrder < 0 + for (i = 0; i < len; i++) { + child = children[i]; + if (child._localZOrder < 0) { + child.visit(this); + } + else break; + } + for (j = 0; j < pLen; j++) { + pChild = pChildren[j]; + if (pChild._localZOrder < 0) { + cmd._changeProtectedChild(pChild); + pChild.visit(this); + } + else break; + } + // draw children zOrder >= 0 + for (; i < len; i++) { + children[i].visit(this); + } + for (; j < pLen; j++) { + pChild = pChildren[j]; + cmd._changeProtectedChild(pChild); + pChild.visit(this); + } + + if (stencilClipping) { + cmd.postStencilVisit(); + } + else if (scissorClipping) { + cmd.postScissorVisit(); + } + + cmd._dirtyFlag = 0; + }, + + /** + * If a layout is loop focused which means that the focus movement will be inside the layout + * @param {Boolean} loop pass true to let the focus movement loop inside the layout + */ + setLoopFocus: function (loop) { + this._loopFocus = loop; + }, + + /** + * Gets whether enable focus loop + * @returns {boolean} If focus loop is enabled, then it will return true, otherwise it returns false. The default value is false. + */ + isLoopFocus: function () { + return this._loopFocus; + }, + + /** + * Specifies whether the layout pass its focus to its child + * @param pass To specify whether the layout pass its focus to its child + */ + setPassFocusToChild: function (pass) { + this.__passFocusToChild = pass; + }, + + /** + * Returns whether the layout will pass the focus to its children or not. The default value is true + * @returns {boolean} To query whether the layout will pass the focus to its children or not. The default value is true + */ + isPassFocusToChild: function () { + return this.__passFocusToChild; + }, + + /** + * When a widget is in a layout, you could call this method to get the next focused widget within a specified direction. + * If the widget is not in a layout, it will return itself + * @param {Number} direction the direction to look for the next focused widget in a layout + * @param {ccui.Widget} current the current focused widget + * @returns {ccui.Widget} return the index of widget in the layout + */ + findNextFocusedWidget: function (direction, current) { + if (this._isFocusPassing || this.isFocused()) { + var parent = this.getParent(); + this._isFocusPassing = false; + if (this.__passFocusToChild) { + var w = this._passFocusToChild(direction, current); + if (w instanceof ccui.Layout && parent) { + parent._isFocusPassing = true; + return parent.findNextFocusedWidget(direction, this); + } + return w; + } + + if (null == parent || !(parent instanceof ccui.Layout)) + return this; + parent._isFocusPassing = true; + return parent.findNextFocusedWidget(direction, this); + } else if (current.isFocused() || current instanceof ccui.Layout) { + if (this._layoutType === ccui.Layout.LINEAR_HORIZONTAL) { + switch (direction) { + case ccui.Widget.LEFT: + return this._getPreviousFocusedWidget(direction, current); + break; + case ccui.Widget.RIGHT: + return this._getNextFocusedWidget(direction, current); + break; + case ccui.Widget.DOWN: + case ccui.Widget.UP: + if (this._isLastWidgetInContainer(this, direction)) { + if (this._isWidgetAncestorSupportLoopFocus(current, direction)) + return ccui.Widget.prototype.findNextFocusedWidget.call(this, direction, this); + return current; + } else { + return ccui.Widget.prototype.findNextFocusedWidget.call(this, direction, this); + } + break; + default: + cc.assert(0, "Invalid Focus Direction"); + return current; + } + } else if (this._layoutType === ccui.Layout.LINEAR_VERTICAL) { + switch (direction) { + case ccui.Widget.LEFT: + case ccui.Widget.RIGHT: + if (this._isLastWidgetInContainer(this, direction)) { + if (this._isWidgetAncestorSupportLoopFocus(current, direction)) + return ccui.Widget.prototype.findNextFocusedWidget.call(this, direction, this); + return current; + } + else + return ccui.Widget.prototype.findNextFocusedWidget.call(this, direction, this); + break; + case ccui.Widget.DOWN: + return this._getNextFocusedWidget(direction, current); + break; + case ccui.Widget.UP: + return this._getPreviousFocusedWidget(direction, current); + break; + default: + cc.assert(0, "Invalid Focus Direction"); + return current; + } + } else { + cc.assert(0, "Un Supported Layout type, please use VBox and HBox instead!!!"); + return current; + } + } else + return current; + }, + + /** + * To specify a user-defined functor to decide which child widget of the layout should get focused + * @function + * @param {Number} direction + * @param {ccui.Widget} current + */ + onPassFocusToChild: null, + + /** + * Adds a widget to the container. + * @param {ccui.Widget} widget + * @param {Number} [zOrder] + * @param {Number|string} [tag] tag or name + * @override + */ + addChild: function (widget, zOrder, tag) { + if ((widget instanceof ccui.Widget)) { + this._supplyTheLayoutParameterLackToChild(widget); + } + ccui.Widget.prototype.addChild.call(this, widget, zOrder, tag); + this._doLayoutDirty = true; + }, + + /** + * Removes child widget from ccui.Layout, and sets the layout dirty flag to true. + * @param {ccui.Widget} widget + * @param {Boolean} [cleanup=true] + * @override + */ + removeChild: function (widget, cleanup) { + ccui.Widget.prototype.removeChild.call(this, widget, cleanup); + this._doLayoutDirty = true; + }, + + /** + * Removes all children from the container with a cleanup, and sets the layout dirty flag to true. + * @param {Boolean} cleanup + */ + removeAllChildren: function (cleanup) { + ccui.Widget.prototype.removeAllChildren.call(this, cleanup); + this._doLayoutDirty = true; + }, + + /** + * Removes all children from the container, do a cleanup to all running actions depending on the cleanup parameter, + * and sets the layout dirty flag to true. + * @param {Boolean} cleanup true if all running actions on all children nodes should be cleanup, false otherwise. + */ + removeAllChildrenWithCleanup: function (cleanup) { + ccui.Widget.prototype.removeAllChildrenWithCleanup.call(this, cleanup); + this._doLayoutDirty = true; + }, + + /** + * Gets if layout is clipping enabled. + * @returns {Boolean} if layout is clipping enabled. + */ + isClippingEnabled: function () { + return this._clippingEnabled; + }, + + /** + * Changes if layout can clip it's content and locChild. + * If you really need this, please enable it. But it would reduce the rendering efficiency. + * @param {Boolean} able clipping enabled. + */ + setClippingEnabled: function (able) { + if (able === this._clippingEnabled) + return; + this._clippingEnabled = able; + switch (this._clippingType) { + case ccui.Layout.CLIPPING_SCISSOR: + case ccui.Layout.CLIPPING_STENCIL: + if (able) { + this._clippingStencil = new cc.DrawNode(); + this._renderCmd.rebindStencilRendering(this._clippingStencil); + if (this._running) + this._clippingStencil._performRecursive(cc.Node._stateCallbackType.onEnter); + this._setStencilClippingSize(this._contentSize); + } else { + if (this._running && this._clippingStencil) + this._clippingStencil._performRecursive(cc.Node._stateCallbackType.onExit); + this._clippingStencil = null; + } + break; + default: + break; + } + }, + + /** + * Sets clipping type to ccui.Layout + * @param {ccui.Layout.CLIPPING_STENCIL|ccui.Layout.CLIPPING_SCISSOR} type + */ + setClippingType: function (type) { + if (type === this._clippingType) + return; + var clippingEnabled = this.isClippingEnabled(); + this.setClippingEnabled(false); + this._clippingType = type; + this.setClippingEnabled(clippingEnabled); + }, + + /** + * Gets clipping type of ccui.Layout + * @returns {ccui.Layout.CLIPPING_STENCIL|ccui.Layout.CLIPPING_SCISSOR} + */ + getClippingType: function () { + return this._clippingType; + }, + + _setStencilClippingSize: function (size) { + if (this._clippingEnabled) { + var rect = []; + rect[0] = cc.p(0, 0); + rect[1] = cc.p(size.width, 0); + rect[2] = cc.p(size.width, size.height); + rect[3] = cc.p(0, size.height); + var green = cc.color.GREEN; + this._clippingStencil.clear(); + this._clippingStencil.setLocalBB && this._clippingStencil.setLocalBB(0, 0, size.width, size.height); + this._clippingStencil.drawPoly(rect, 4, green, 0, green); + } + }, + + _getClippingRect: function () { + if (this._clippingRectDirty) { + var worldPos = this.convertToWorldSpace(cc.p(0, 0)); + var t = this.getNodeToWorldTransform(); + var scissorWidth = this._contentSize.width * t.a; + var scissorHeight = this._contentSize.height * t.d; + var parentClippingRect; + var parent = this; + + while (parent) { + parent = parent.getParent(); + if (parent && parent instanceof ccui.Layout && parent.isClippingEnabled()) { + this._clippingParent = parent; + break; + } + } + + if (this._clippingParent) { + parentClippingRect = this._clippingParent._getClippingRect(); + + this._clippingRect.x = Math.max(worldPos.x, parentClippingRect.x); + this._clippingRect.y = Math.max(worldPos.y, parentClippingRect.y); + + var right = Math.min(worldPos.x + scissorWidth, parentClippingRect.x + parentClippingRect.width); + var top = Math.min(worldPos.y + scissorHeight, parentClippingRect.y + parentClippingRect.height); + + this._clippingRect.width = Math.max(0.0, right - this._clippingRect.x); + this._clippingRect.height = Math.max(0.0, top - this._clippingRect.y); + } else { + this._clippingRect.x = worldPos.x; + this._clippingRect.y = worldPos.y; + this._clippingRect.width = scissorWidth; + this._clippingRect.height = scissorHeight; + } + this._clippingRectDirty = false; + } + return this._clippingRect; + }, + + _onSizeChanged: function () { + ccui.Widget.prototype._onSizeChanged.call(this); + var locContentSize = this._contentSize; + this._setStencilClippingSize(locContentSize); + this._doLayoutDirty = true; + this._clippingRectDirty = true; + if (this._backGroundImage) { + this._backGroundImage.setPosition(locContentSize.width * 0.5, locContentSize.height * 0.5); + if (this._backGroundScale9Enabled && this._backGroundImage instanceof ccui.Scale9Sprite) + this._backGroundImage.setPreferredSize(locContentSize); + } + if (this._colorRender) + this._colorRender.setContentSize(locContentSize); + if (this._gradientRender) + this._gradientRender.setContentSize(locContentSize); + }, + + /** + * Sets background image use scale9 renderer. + * @param {Boolean} able true that use scale9 renderer, false otherwise. + */ + setBackGroundImageScale9Enabled: function (able) { + if (this._backGroundScale9Enabled === able) + return; + this.removeProtectedChild(this._backGroundImage); + this._backGroundImage = null; + this._backGroundScale9Enabled = able; + this._addBackGroundImage(); + this.setBackGroundImage(this._backGroundImageFileName, this._bgImageTexType); + this.setBackGroundImageCapInsets(this._backGroundImageCapInsets); + }, + + /** + * Get whether background image is use scale9 renderer. + * @returns {Boolean} + */ + isBackGroundImageScale9Enabled: function () { + return this._backGroundScale9Enabled; + }, + + /** + * Sets a background image for layout + * @param {String} fileName + * @param {ccui.Widget.LOCAL_TEXTURE|ccui.Widget.PLIST_TEXTURE} texType + */ + setBackGroundImage: function (fileName, texType) { + if (!fileName) + return; + texType = texType || ccui.Widget.LOCAL_TEXTURE; + if (this._backGroundImage === null) { + this._addBackGroundImage(); + this.setBackGroundImageScale9Enabled(this._backGroundScale9Enabled); + } + this._backGroundImageFileName = fileName; + this._bgImageTexType = texType; + var locBackgroundImage = this._backGroundImage; + switch (this._bgImageTexType) { + case ccui.Widget.LOCAL_TEXTURE: + locBackgroundImage.initWithFile(fileName); + break; + case ccui.Widget.PLIST_TEXTURE: + locBackgroundImage.initWithSpriteFrameName(fileName); + break; + default: + break; + } + if (this._backGroundScale9Enabled) + locBackgroundImage.setPreferredSize(this._contentSize); + + this._backGroundImageTextureSize = locBackgroundImage.getContentSize(); + locBackgroundImage.setPosition(this._contentSize.width * 0.5, this._contentSize.height * 0.5); + this._updateBackGroundImageColor(); + }, + + /** + * Sets a background image CapInsets for layout, if the background image is a scale9 render. + * @param {cc.Rect} capInsets capinsets of background image. + */ + setBackGroundImageCapInsets: function (capInsets) { + if (!capInsets) + return; + var locInsets = this._backGroundImageCapInsets; + locInsets.x = capInsets.x; + locInsets.y = capInsets.y; + locInsets.width = capInsets.width; + locInsets.height = capInsets.height; + if (this._backGroundScale9Enabled) + this._backGroundImage.setCapInsets(capInsets); + }, + + /** + * Gets background image capinsets of ccui.Layout. + * @returns {cc.Rect} + */ + getBackGroundImageCapInsets: function () { + return cc.rect(this._backGroundImageCapInsets); + }, + + _supplyTheLayoutParameterLackToChild: function (locChild) { + if (!locChild) { + return; + } + switch (this._layoutType) { + case ccui.Layout.ABSOLUTE: + break; + case ccui.Layout.LINEAR_HORIZONTAL: + case ccui.Layout.LINEAR_VERTICAL: + var layoutParameter = locChild.getLayoutParameter(ccui.LayoutParameter.LINEAR); + if (!layoutParameter) + locChild.setLayoutParameter(new ccui.LinearLayoutParameter()); + break; + case ccui.Layout.RELATIVE: + var layoutParameter = locChild.getLayoutParameter(ccui.LayoutParameter.RELATIVE); + if (!layoutParameter) + locChild.setLayoutParameter(new ccui.RelativeLayoutParameter()); + break; + default: + break; + } + }, + + _addBackGroundImage: function () { + var contentSize = this._contentSize; + if (this._backGroundScale9Enabled) { + this._backGroundImage = new ccui.Scale9Sprite(); + this._backGroundImage.setPreferredSize(contentSize); + } else + this._backGroundImage = new cc.Sprite(); + this.addProtectedChild(this._backGroundImage, ccui.Layout.BACKGROUND_IMAGE_ZORDER, -1); + this._backGroundImage.setPosition(contentSize.width * 0.5, contentSize.height * 0.5); + }, + + /** + * Remove the background image of ccui.Layout. + */ + removeBackGroundImage: function () { + if (!this._backGroundImage) + return; + this.removeProtectedChild(this._backGroundImage); + this._backGroundImage = null; + this._backGroundImageFileName = ""; + this._backGroundImageTextureSize.width = 0; + this._backGroundImageTextureSize.height = 0; + }, + + /** + * Sets Color Type for ccui.Layout. + * @param {ccui.Layout.BG_COLOR_NONE|ccui.Layout.BG_COLOR_SOLID|ccui.Layout.BG_COLOR_GRADIENT} type + */ + setBackGroundColorType: function (type) { + if (this._colorType === type) + return; + switch (this._colorType) { + case ccui.Layout.BG_COLOR_NONE: + if (this._colorRender) { + this.removeProtectedChild(this._colorRender); + this._colorRender = null; + } + if (this._gradientRender) { + this.removeProtectedChild(this._gradientRender); + this._gradientRender = null; + } + break; + case ccui.Layout.BG_COLOR_SOLID: + if (this._colorRender) { + this.removeProtectedChild(this._colorRender); + this._colorRender = null; + } + break; + case ccui.Layout.BG_COLOR_GRADIENT: + if (this._gradientRender) { + this.removeProtectedChild(this._gradientRender); + this._gradientRender = null; + } + break; + default: + break; + } + this._colorType = type; + switch (this._colorType) { + case ccui.Layout.BG_COLOR_NONE: + break; + case ccui.Layout.BG_COLOR_SOLID: + this._colorRender = new cc.LayerColor(); + this._colorRender.setContentSize(this._contentSize); + this._colorRender.setOpacity(this._opacity); + this._colorRender.setColor(this._color); + this.addProtectedChild(this._colorRender, ccui.Layout.BACKGROUND_RENDERER_ZORDER, -1); + break; + case ccui.Layout.BG_COLOR_GRADIENT: + this._gradientRender = new cc.LayerGradient(cc.color(255, 0, 0, 255), cc.color(0, 255, 0, 255)); + this._gradientRender.setContentSize(this._contentSize); + this._gradientRender.setOpacity(this._opacity); + this._gradientRender.setStartColor(this._startColor); + this._gradientRender.setEndColor(this._endColor); + this._gradientRender.setVector(this._alongVector); + this.addProtectedChild(this._gradientRender, ccui.Layout.BACKGROUND_RENDERER_ZORDER, -1); + break; + default: + break; + } + }, + + /** + * Get background color type of ccui.Layout. + * @returns {ccui.Layout.BG_COLOR_NONE|ccui.Layout.BG_COLOR_SOLID|ccui.Layout.BG_COLOR_GRADIENT} + */ + getBackGroundColorType: function () { + return this._colorType; + }, + + /** + * Sets background color for layout, if color type is Layout.COLOR_SOLID + * @param {cc.Color} color + * @param {cc.Color} [endColor] + */ + setBackGroundColor: function (color, endColor) { + if (!endColor) { + this._color.r = color.r; + this._color.g = color.g; + this._color.b = color.b; + if (this._colorRender) + this._colorRender.setColor(color); + } else { + this._startColor.r = color.r; + this._startColor.g = color.g; + this._startColor.b = color.b; + if (this._gradientRender) + this._gradientRender.setStartColor(color); + + this._endColor.r = endColor.r; + this._endColor.g = endColor.g; + this._endColor.b = endColor.b; + if (this._gradientRender) + this._gradientRender.setEndColor(endColor); + } + }, + + /** + * Gets background color of ccui.Layout, if color type is Layout.COLOR_SOLID. + * @returns {cc.Color} + */ + getBackGroundColor: function () { + var tmpColor = this._color; + return cc.color(tmpColor.r, tmpColor.g, tmpColor.b, tmpColor.a); + }, + + /** + * Gets background start color of ccui.Layout + * @returns {cc.Color} + */ + getBackGroundStartColor: function () { + var tmpColor = this._startColor; + return cc.color(tmpColor.r, tmpColor.g, tmpColor.b, tmpColor.a); + }, + + /** + * Gets background end color of ccui.Layout + * @returns {cc.Color} + */ + getBackGroundEndColor: function () { + var tmpColor = this._endColor; + return cc.color(tmpColor.r, tmpColor.g, tmpColor.b, tmpColor.a); + }, + + /** + * Sets background opacity to ccui.Layout. + * @param {number} opacity + */ + setBackGroundColorOpacity: function (opacity) { + this._opacity = opacity; + switch (this._colorType) { + case ccui.Layout.BG_COLOR_NONE: + break; + case ccui.Layout.BG_COLOR_SOLID: + this._colorRender.setOpacity(opacity); + break; + case ccui.Layout.BG_COLOR_GRADIENT: + this._gradientRender.setOpacity(opacity); + break; + default: + break; + } + }, + + /** + * Get background opacity value of ccui.Layout. + * @returns {Number} + */ + getBackGroundColorOpacity: function () { + return this._opacity; + }, + + /** + * Sets background color vector for layout, if color type is Layout.COLOR_GRADIENT + * @param {cc.Point} vector + */ + setBackGroundColorVector: function (vector) { + this._alongVector.x = vector.x; + this._alongVector.y = vector.y; + if (this._gradientRender) { + this._gradientRender.setVector(vector); + } + }, + + /** + * Gets background color vector of ccui.Layout, if color type is Layout.COLOR_GRADIENT + * @returns {cc.Point} + */ + getBackGroundColorVector: function () { + return this._alongVector; + }, + + /** + * Sets backGround image color + * @param {cc.Color} color + */ + setBackGroundImageColor: function (color) { + this._backGroundImageColor.r = color.r; + this._backGroundImageColor.g = color.g; + this._backGroundImageColor.b = color.b; + + this._updateBackGroundImageColor(); + }, + + /** + * Sets backGround image Opacity + * @param {Number} opacity + */ + setBackGroundImageOpacity: function (opacity) { + this._backGroundImageColor.a = opacity; + this.getBackGroundImageColor(); + }, + + /** + * Gets backGround image color + * @returns {cc.Color} + */ + getBackGroundImageColor: function () { + var color = this._backGroundImageColor; + return cc.color(color.r, color.g, color.b, color.a); + }, + + /** + * Gets backGround image opacity + * @returns {Number} + */ + getBackGroundImageOpacity: function () { + return this._backGroundImageColor.a; + }, + + _updateBackGroundImageColor: function () { + if (this._backGroundImage) + this._backGroundImage.setColor(this._backGroundImageColor); + }, + + /** + * Gets background image texture size. + * @returns {cc.Size} + */ + getBackGroundImageTextureSize: function () { + return this._backGroundImageTextureSize; + }, + + /** + * Sets LayoutType to ccui.Layout, LayoutManager will do layout by layout type.. + * @param {ccui.Layout.ABSOLUTE|ccui.Layout.LINEAR_VERTICAL|ccui.Layout.LINEAR_HORIZONTAL|ccui.Layout.RELATIVE} type + */ + setLayoutType: function (type) { + this._layoutType = type; + var layoutChildrenArray = this._children; + var locChild = null; + for (var i = 0; i < layoutChildrenArray.length; i++) { + locChild = layoutChildrenArray[i]; + if (locChild instanceof ccui.Widget) + this._supplyTheLayoutParameterLackToChild(locChild); + } + this._doLayoutDirty = true; + }, + + /** + * Gets LayoutType of ccui.Layout. + * @returns {null} + */ + getLayoutType: function () { + return this._layoutType; + }, + + /** + * request to refresh widget layout, it will do layout at visit calls + */ + requestDoLayout: function () { + this._doLayoutDirty = true; + }, + + _doLayout: function () { + if (!this._doLayoutDirty) + return; + + this.sortAllChildren(); + + var executant = ccui.getLayoutManager(this._layoutType); + if (executant) + executant._doLayout(this); + this._doLayoutDirty = false; + }, + + _getLayoutContentSize: function () { + return this.getContentSize(); + }, + + _getLayoutElements: function () { + return this.getChildren(); + }, + + _updateBackGroundImageOpacity: function () { + if (this._backGroundImage) + this._backGroundImage.setOpacity(this._backGroundImageOpacity); + }, + + _updateBackGroundImageRGBA: function () { + if (this._backGroundImage) { + this._backGroundImage.setColor(this._backGroundImageColor); + this._backGroundImage.setOpacity(this._backGroundImageOpacity); + } + }, + + /** + * Gets the content size of the layout, it will accumulate all its children's content size + * @returns {cc.Size} + * @private + */ + _getLayoutAccumulatedSize: function () { + var children = this.getChildren(); + var layoutSize = cc.size(0, 0); + var widgetCount = 0, locSize; + for (var i = 0, len = children.length; i < len; i++) { + var layout = children[i]; + if (null !== layout && layout instanceof ccui.Layout) { + locSize = layout._getLayoutAccumulatedSize(); + layoutSize.width += locSize.width; + layoutSize.height += locSize.height; + } else { + if (layout instanceof ccui.Widget) { + widgetCount++; + var m = layout.getLayoutParameter().getMargin(); + locSize = layout.getContentSize(); + layoutSize.width += locSize.width + (m.right + m.left) * 0.5; + layoutSize.height += locSize.height + (m.top + m.bottom) * 0.5; + } + } + } + + //substract extra size + var type = this.getLayoutType(); + if (type === ccui.Layout.LINEAR_HORIZONTAL) + layoutSize.height = layoutSize.height - layoutSize.height / widgetCount * (widgetCount - 1); + + if (type === ccui.Layout.LINEAR_VERTICAL) + layoutSize.width = layoutSize.width - layoutSize.width / widgetCount * (widgetCount - 1); + return layoutSize; + }, + + /** + * When the layout get focused, it the layout pass the focus to its child, it will use this method to determine which child
+ * will get the focus. The current algorithm to determine which child will get focus is nearest-distance-priority algorithm + * @param {Number} direction next focused widget direction + * @param {ccui.Widget} baseWidget + * @returns {Number} + * @private + */ + _findNearestChildWidgetIndex: function (direction, baseWidget) { + if (baseWidget == null || baseWidget === this) + return this._findFirstFocusEnabledWidgetIndex(); + + var index = 0, locChildren = this.getChildren(); + var count = locChildren.length, widgetPosition; + + var distance = cc.FLT_MAX, found = 0; + if (direction === ccui.Widget.LEFT || direction === ccui.Widget.RIGHT || direction === ccui.Widget.DOWN || direction === ccui.Widget.UP) { + widgetPosition = this._getWorldCenterPoint(baseWidget); + while (index < count) { + var w = locChildren[index]; + if (w && w instanceof ccui.Widget && w.isFocusEnabled()) { + var length = (w instanceof ccui.Layout) ? w._calculateNearestDistance(baseWidget) + : cc.pLength(cc.pSub(this._getWorldCenterPoint(w), widgetPosition)); + if (length < distance) { + found = index; + distance = length; + } + } + index++; + } + return found; + } + cc.log("invalid focus direction!"); + return 0; + }, + + /** + * When the layout get focused, it the layout pass the focus to its child, it will use this method to determine which child + * will get the focus. The current algorithm to determine which child will get focus is farthest-distance-priority algorithm + * @param {Number} direction next focused widget direction + * @param {ccui.Widget} baseWidget + * @returns {Number} The index of child widget in the container + * @private + */ + _findFarthestChildWidgetIndex: function (direction, baseWidget) { + if (baseWidget == null || baseWidget === this) + return this._findFirstFocusEnabledWidgetIndex(); + + var index = 0, locChildren = this.getChildren(); + var count = locChildren.length; + + var distance = -cc.FLT_MAX, found = 0; + if (direction === ccui.Widget.LEFT || direction === ccui.Widget.RIGHT || direction === ccui.Widget.DOWN || direction === ccui.Widget.UP) { + var widgetPosition = this._getWorldCenterPoint(baseWidget); + while (index < count) { + var w = locChildren[index]; + if (w && w instanceof ccui.Widget && w.isFocusEnabled()) { + var length = (w instanceof ccui.Layout) ? w._calculateFarthestDistance(baseWidget) + : cc.pLength(cc.pSub(this._getWorldCenterPoint(w), widgetPosition)); + if (length > distance) { + found = index; + distance = length; + } + } + index++; + } + return found; + } + cc.log("invalid focus direction!!!"); + return 0; + }, + + /** + * calculate the nearest distance between the baseWidget and the children of the layout + * @param {ccui.Widget} baseWidget the base widget which will be used to calculate the distance between the layout's children and itself + * @returns {Number} return the nearest distance between the baseWidget and the layout's children + * @private + */ + _calculateNearestDistance: function (baseWidget) { + var distance = cc.FLT_MAX; + var widgetPosition = this._getWorldCenterPoint(baseWidget); + var locChildren = this._children; + + for (var i = 0, len = locChildren.length; i < len; i++) { + var widget = locChildren[i], length; + if (widget instanceof ccui.Layout) + length = widget._calculateNearestDistance(baseWidget); + else { + if (widget instanceof ccui.Widget && widget.isFocusEnabled()) + length = cc.pLength(cc.pSub(this._getWorldCenterPoint(widget), widgetPosition)); + else + continue; + } + if (length < distance) + distance = length; + } + return distance; + }, + + /** + * calculate the farthest distance between the baseWidget and the children of the layout + * @param baseWidget + * @returns {number} + * @private + */ + _calculateFarthestDistance: function (baseWidget) { + var distance = -cc.FLT_MAX; + var widgetPosition = this._getWorldCenterPoint(baseWidget); + var locChildren = this._children; + + for (var i = 0, len = locChildren.length; i < len; i++) { + var layout = locChildren[i]; + var length; + if (layout instanceof ccui.Layout) + length = layout._calculateFarthestDistance(baseWidget); + else { + if (layout instanceof ccui.Widget && layout.isFocusEnabled()) { + var wPosition = this._getWorldCenterPoint(layout); + length = cc.pLength(cc.pSub(wPosition, widgetPosition)); + } else + continue; + } + + if (length > distance) + distance = length; + } + return distance; + }, + + /** + * when a layout pass the focus to it's child, use this method to determine which algorithm to use, nearest or farthest distance algorithm or not + * @param direction + * @param baseWidget + * @private + */ + _findProperSearchingFunctor: function (direction, baseWidget) { + if (baseWidget === undefined) + return; + + var previousWidgetPosition = this._getWorldCenterPoint(baseWidget); + var widgetPosition = this._getWorldCenterPoint(this._findFirstNonLayoutWidget()); + if (direction === ccui.Widget.LEFT) { + this.onPassFocusToChild = (previousWidgetPosition.x > widgetPosition.x) ? this._findNearestChildWidgetIndex + : this._findFarthestChildWidgetIndex; + } else if (direction === ccui.Widget.RIGHT) { + this.onPassFocusToChild = (previousWidgetPosition.x > widgetPosition.x) ? this._findFarthestChildWidgetIndex + : this._findNearestChildWidgetIndex; + } else if (direction === ccui.Widget.DOWN) { + this.onPassFocusToChild = (previousWidgetPosition.y > widgetPosition.y) ? this._findNearestChildWidgetIndex + : this._findFarthestChildWidgetIndex; + } else if (direction === ccui.Widget.UP) { + this.onPassFocusToChild = (previousWidgetPosition.y < widgetPosition.y) ? this._findNearestChildWidgetIndex + : this._findFarthestChildWidgetIndex; + } else + cc.log("invalid direction!"); + }, + + /** + * find the first non-layout widget in this layout + * @returns {ccui.Widget} + * @private + */ + _findFirstNonLayoutWidget: function () { + var locChildren = this._children; + for (var i = 0, len = locChildren.length; i < len; i++) { + var child = locChildren[i]; + if (child instanceof ccui.Layout) { + var widget = child._findFirstNonLayoutWidget(); + if (widget) + return widget; + } else { + if (child instanceof ccui.Widget) + return child; + } + } + return null; + }, + + /** + * find the first focus enabled widget index in the layout, it will recursive searching the child widget + * @returns {number} + * @private + */ + _findFirstFocusEnabledWidgetIndex: function () { + var index = 0, locChildren = this.getChildren(); + var count = locChildren.length; + while (index < count) { + var w = locChildren[index]; + if (w && w instanceof ccui.Widget && w.isFocusEnabled()) + return index; + index++; + } + return 0; + }, + + /** + * find a focus enabled child Widget in the layout by index + * @param index + * @returns {*} + * @private + */ + _findFocusEnabledChildWidgetByIndex: function (index) { + var widget = this._getChildWidgetByIndex(index); + if (widget) { + if (widget.isFocusEnabled()) + return widget; + index = index + 1; + return this._findFocusEnabledChildWidgetByIndex(index); + } + return null; + }, + + /** + * get the center point of a widget in world space + * @param {ccui.Widget} widget + * @returns {cc.Point} + * @private + */ + _getWorldCenterPoint: function (widget) { + //FIXEDME: we don't need to calculate the content size of layout anymore + var widgetSize = widget instanceof ccui.Layout ? widget._getLayoutAccumulatedSize() : widget.getContentSize(); + return widget.convertToWorldSpace(cc.p(widgetSize.width / 2, widgetSize.height / 2)); + }, + + /** + * this method is called internally by nextFocusedWidget. When the dir is Right/Down, then this method will be called + * @param {Number} direction + * @param {ccui.Widget} current the current focused widget + * @returns {ccui.Widget} the next focused widget + * @private + */ + _getNextFocusedWidget: function (direction, current) { + var nextWidget = null, locChildren = this._children; + var previousWidgetPos = locChildren.indexOf(current); + previousWidgetPos = previousWidgetPos + 1; + if (previousWidgetPos < locChildren.length) { + nextWidget = this._getChildWidgetByIndex(previousWidgetPos); + //handle widget + if (nextWidget) { + if (nextWidget.isFocusEnabled()) { + if (nextWidget instanceof ccui.Layout) { + nextWidget._isFocusPassing = true; + return nextWidget.findNextFocusedWidget(direction, nextWidget); + } else { + this.dispatchFocusEvent(current, nextWidget); + return nextWidget; + } + } else + return this._getNextFocusedWidget(direction, nextWidget); + } else + return current; + } else { + if (this._loopFocus) { + if (this._checkFocusEnabledChild()) { + previousWidgetPos = 0; + nextWidget = this._getChildWidgetByIndex(previousWidgetPos); + if (nextWidget.isFocusEnabled()) { + if (nextWidget instanceof ccui.Layout) { + nextWidget._isFocusPassing = true; + return nextWidget.findNextFocusedWidget(direction, nextWidget); + } else { + this.dispatchFocusEvent(current, nextWidget); + return nextWidget; + } + } else + return this._getNextFocusedWidget(direction, nextWidget); + } else + return (current instanceof ccui.Layout) ? current : ccui.Widget._focusedWidget; + } else { + if (this._isLastWidgetInContainer(current, direction)) { + if (this._isWidgetAncestorSupportLoopFocus(this, direction)) + return ccui.Widget.prototype.findNextFocusedWidget.call(this, direction, this); + return (current instanceof ccui.Layout) ? current : ccui.Widget._focusedWidget; + } else + return ccui.Widget.prototype.findNextFocusedWidget.call(this, direction, this); + } + } + }, + + /** + * this method is called internally by nextFocusedWidget. When the dir is Left/Up, then this method will be called + * @param direction + * @param {ccui.Widget} current the current focused widget + * @returns {ccui.Widget} the next focused widget + * @private + */ + _getPreviousFocusedWidget: function (direction, current) { + var nextWidget = null, locChildren = this._children; + var previousWidgetPos = locChildren.indexOf(current); + previousWidgetPos = previousWidgetPos - 1; + if (previousWidgetPos >= 0) { + nextWidget = this._getChildWidgetByIndex(previousWidgetPos); + if (nextWidget.isFocusEnabled()) { + if (nextWidget instanceof ccui.Layout) { + nextWidget._isFocusPassing = true; + return nextWidget.findNextFocusedWidget(direction, nextWidget); + } + this.dispatchFocusEvent(current, nextWidget); + return nextWidget; + } else + return this._getPreviousFocusedWidget(direction, nextWidget); //handling the disabled widget, there is no actual focus lose or get, so we don't need any envet + } else { + if (this._loopFocus) { + if (this._checkFocusEnabledChild()) { + previousWidgetPos = locChildren.length - 1; + nextWidget = this._getChildWidgetByIndex(previousWidgetPos); + if (nextWidget.isFocusEnabled()) { + if (nextWidget instanceof ccui.Layout) { + nextWidget._isFocusPassing = true; + return nextWidget.findNextFocusedWidget(direction, nextWidget); + } else { + this.dispatchFocusEvent(current, nextWidget); + return nextWidget; + } + } else + return this._getPreviousFocusedWidget(direction, nextWidget); + } else + return (current instanceof ccui.Layout) ? current : ccui.Widget._focusedWidget; + } else { + if (this._isLastWidgetInContainer(current, direction)) { + if (this._isWidgetAncestorSupportLoopFocus(this, direction)) + return ccui.Widget.prototype.findNextFocusedWidget.call(this, direction, this); + return (current instanceof ccui.Layout) ? current : ccui.Widget._focusedWidget; + } else + return ccui.Widget.prototype.findNextFocusedWidget.call(this, direction, this); + } + } + }, + + /** + * find the nth element in the _children array. Only the Widget descendant object will be returned + * @param {Number} index + * @returns {ccui.Widget} + * @private + */ + _getChildWidgetByIndex: function (index) { + var locChildren = this._children; + var size = locChildren.length, count = 0, oldIndex = index; + while (index < size) { + var firstChild = locChildren[index]; + if (firstChild && firstChild instanceof ccui.Widget) + return firstChild; + count++; + index++; + } + + var begin = 0; + while (begin < oldIndex) { + var child = locChildren[begin]; + if (child && child instanceof ccui.Widget) + return child; + count++; + begin++; + } + return null; + }, + + /** + * whether it is the last element according to all their parents + * @param {ccui.Widget} widget + * @param {Number} direction + * @returns {Boolean} + * @private + */ + _isLastWidgetInContainer: function (widget, direction) { + var parent = widget.getParent(); + if (parent == null || !(parent instanceof ccui.Layout)) + return true; + + var container = parent.getChildren(); + var index = container.indexOf(widget); + if (parent.getLayoutType() === ccui.Layout.LINEAR_HORIZONTAL) { + if (direction === ccui.Widget.LEFT) { + if (index === 0) + return this._isLastWidgetInContainer(parent, direction); + else + return false; + } + if (direction === ccui.Widget.RIGHT) { + if (index === container.length - 1) + return this._isLastWidgetInContainer(parent, direction); + else + return false; + } + if (direction === ccui.Widget.DOWN) + return this._isLastWidgetInContainer(parent, direction); + + if (direction === ccui.Widget.UP) + return this._isLastWidgetInContainer(parent, direction); + } else if (parent.getLayoutType() === ccui.Layout.LINEAR_VERTICAL) { + if (direction === ccui.Widget.UP) { + if (index === 0) + return this._isLastWidgetInContainer(parent, direction); + else + return false; + } + if (direction === ccui.Widget.DOWN) { + if (index === container.length - 1) + return this._isLastWidgetInContainer(parent, direction); + else + return false; + } + if (direction === ccui.Widget.LEFT) + return this._isLastWidgetInContainer(parent, direction); + + if (direction === ccui.Widget.RIGHT) + return this._isLastWidgetInContainer(parent, direction); + } else { + cc.log("invalid layout Type"); + return false; + } + }, + + /** + * Lookup any parent widget with a layout type as the direction, if the layout is loop focused, then return true, otherwise it returns false. + * @param {ccui.Widget} widget + * @param {Number} direction + * @returns {Boolean} + * @private + */ + _isWidgetAncestorSupportLoopFocus: function (widget, direction) { + var parent = widget.getParent(); + if (parent == null || !(parent instanceof ccui.Layout)) + return false; + if (parent.isLoopFocus()) { + var layoutType = parent.getLayoutType(); + if (layoutType === ccui.Layout.LINEAR_HORIZONTAL) { + if (direction === ccui.Widget.LEFT || direction === ccui.Widget.RIGHT) + return true; + else + return this._isWidgetAncestorSupportLoopFocus(parent, direction); + } + if (layoutType === ccui.Layout.LINEAR_VERTICAL) { + if (direction === ccui.Widget.DOWN || direction === ccui.Widget.UP) + return true; + else + return this._isWidgetAncestorSupportLoopFocus(parent, direction); + } else { + cc.assert(0, "invalid layout type"); + return false; + } + } else + return this._isWidgetAncestorSupportLoopFocus(parent, direction); + }, + + /** + * pass the focus to the layout's next focus enabled child + * @param {Number} direction + * @param {ccui.Widget} current + * @returns {ccui.Widget} + * @private + */ + _passFocusToChild: function (direction, current) { + if (this._checkFocusEnabledChild()) { + var previousWidget = ccui.Widget.getCurrentFocusedWidget(); + this._findProperSearchingFunctor(direction, previousWidget); + var index = this.onPassFocusToChild(direction, previousWidget); + + var widget = this._getChildWidgetByIndex(index); + if (widget instanceof ccui.Layout) { + widget._isFocusPassing = true; + return widget.findNextFocusedWidget(direction, widget); + } else { + this.dispatchFocusEvent(current, widget); + return widget; + } + } else + return this; + }, + + /** + * If there are no focus enabled child in the layout, it will return false, otherwise it returns true + * @returns {boolean} + * @private + */ + _checkFocusEnabledChild: function () { + var locChildren = this._children; + for (var i = 0, len = locChildren.length; i < len; i++) { + var widget = locChildren[i]; + if (widget && widget instanceof ccui.Widget && widget.isFocusEnabled()) + return true; + } + return false; + }, + + /** + * Returns the "class name" of widget. + * @returns {string} + */ + getDescription: function () { + return "Layout"; + }, + + _createCloneInstance: function () { + return new ccui.Layout(); + }, + + _copyClonedWidgetChildren: function (model) { + ccui.Widget.prototype._copyClonedWidgetChildren.call(this, model); + }, + + _copySpecialProperties: function (layout) { + if (!(layout instanceof ccui.Layout)) + return; + this.setBackGroundImageScale9Enabled(layout._backGroundScale9Enabled); + this.setBackGroundImage(layout._backGroundImageFileName, layout._bgImageTexType); + this.setBackGroundImageCapInsets(layout._backGroundImageCapInsets); + this.setBackGroundColorType(layout._colorType); + this.setBackGroundColor(layout._color); + this.setBackGroundColor(layout._startColor, layout._endColor); + this.setBackGroundColorOpacity(layout._opacity); + this.setBackGroundColorVector(layout._alongVector); + this.setLayoutType(layout._layoutType); + this.setClippingEnabled(layout._clippingEnabled); + this.setClippingType(layout._clippingType); + this._loopFocus = layout._loopFocus; + this.__passFocusToChild = layout.__passFocusToChild; + this._isInterceptTouch = layout._isInterceptTouch; + }, + + /** + * force refresh widget layout + */ + forceDoLayout: function () { + this.requestDoLayout(); + this._doLayout(); + }, + + _createRenderCmd: function () { + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) + return new ccui.Layout.WebGLRenderCmd(this); + else + return new ccui.Layout.CanvasRenderCmd(this); + } +}); + +var _p = ccui.Layout.prototype; + +// Extended properties +/** @expose */ +_p.clippingEnabled; +cc.defineGetterSetter(_p, "clippingEnabled", _p.isClippingEnabled, _p.setClippingEnabled); +/** @expose */ +_p.clippingType; +cc.defineGetterSetter(_p, "clippingType", null, _p.setClippingType); +/** @expose */ +_p.layoutType; +cc.defineGetterSetter(_p, "layoutType", _p.getLayoutType, _p.setLayoutType); + +_p = null; + +/** + * allocates and initializes a UILayout. + * @deprecated since v3.0, please use new ccui.Layout() instead. + * @return {ccui.Layout} + */ +ccui.Layout.create = function () { + return new ccui.Layout(); +}; + +// Constants + +//layoutBackGround color type +/** + * The None of ccui.Layout's background color type + * @constant + * @type {number} + */ +ccui.Layout.BG_COLOR_NONE = 0; +/** + * The solid of ccui.Layout's background color type, it will use a LayerColor to draw the background. + * @constant + * @type {number} + */ +ccui.Layout.BG_COLOR_SOLID = 1; +/** + * The gradient of ccui.Layout's background color type, it will use a LayerGradient to draw the background. + * @constant + * @type {number} + */ +ccui.Layout.BG_COLOR_GRADIENT = 2; + +//Layout type +/** + * The absolute of ccui.Layout's layout type. + * @type {number} + * @constant + */ +ccui.Layout.ABSOLUTE = 0; +/** + * The vertical of ccui.Layout's layout type. + * @type {number} + * @constant + */ +ccui.Layout.LINEAR_VERTICAL = 1; +/** + * The horizontal of ccui.Layout's layout type. + * @type {number} + * @constant + */ +ccui.Layout.LINEAR_HORIZONTAL = 2; +/** + * The relative of ccui.Layout's layout type. + * @type {number} + * @constant + */ +ccui.Layout.RELATIVE = 3; + +//Layout clipping type +/** + * The stencil of ccui.Layout's clipping type. + * @type {number} + * @constant + */ +ccui.Layout.CLIPPING_STENCIL = 0; +/** + * The scissor of ccui.Layout's clipping type. + * @type {number} + * @constant + */ +ccui.Layout.CLIPPING_SCISSOR = 1; + +/** + * The zOrder value of ccui.Layout's image background. + * @type {number} + * @constant + */ +ccui.Layout.BACKGROUND_IMAGE_ZORDER = -1; +/** + * The zOrder value of ccui.Layout's color background. + * @type {number} + * @constant + */ +ccui.Layout.BACKGROUND_RENDERER_ZORDER = -2; diff --git a/frameworks/cocos2d-html5/extensions/ccui/layouts/UILayoutCanvasRenderCmd.js b/frameworks/cocos2d-html5/extensions/ccui/layouts/UILayoutCanvasRenderCmd.js new file mode 100644 index 0000000..7d847cc --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/layouts/UILayoutCanvasRenderCmd.js @@ -0,0 +1,103 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + ccui.Layout.CanvasRenderCmd = function (renderable) { + this._pNodeCmdCtor(renderable); + this._needDraw = false; + + this._rendererSaveCmd = null; + this._rendererClipCmd = null; + this._rendererRestoreCmd = null; + }; + + var proto = ccui.Layout.CanvasRenderCmd.prototype = Object.create(ccui.ProtectedNode.CanvasRenderCmd.prototype); + proto.constructor = ccui.Layout.CanvasRenderCmd; + proto._layoutCmdCtor = ccui.Layout.CanvasRenderCmd; + + proto._onRenderSaveCmd = function (ctx, scaleX, scaleY) { + var wrapper = ctx || cc._renderContext, context = wrapper.getContext(); + wrapper.save(); + wrapper.save(); + wrapper.setTransform(this._worldTransform, scaleX, scaleY); + var buffer = this._node._clippingStencil._renderCmd._buffer; + + for (var i = 0, bufLen = buffer.length; i < bufLen; i++) { + var element = buffer[i], vertices = element.verts; + var firstPoint = vertices[0]; + context.beginPath(); + context.moveTo(firstPoint.x, -firstPoint.y); + for (var j = 1, len = vertices.length; j < len; j++) + context.lineTo(vertices[j].x, -vertices[j].y); + context.closePath(); + } + }; + + proto._onRenderClipCmd = function (ctx) { + var wrapper = ctx || cc._renderContext, context = wrapper.getContext(); + wrapper.restore(); + context.clip(); + }; + + proto._onRenderRestoreCmd = function (ctx) { + var wrapper = ctx || cc._renderContext, context = wrapper.getContext(); + + wrapper.restore(); + }; + + proto.rebindStencilRendering = function (stencil) { + stencil._renderCmd.rendering = this.__stencilDraw; + stencil._renderCmd._canUseDirtyRegion = true; + }; + + proto.__stencilDraw = function (ctx, scaleX, scaleY) { //Only for Canvas + //do nothing, rendering in layout + }; + + proto.stencilClippingVisit = proto.scissorClippingVisit = function (parentCmd) { + var node = this._node; + if (!node._clippingStencil || !node._clippingStencil.isVisible()) + return; + + if (!this._rendererSaveCmd) { + this._rendererSaveCmd = new cc.CustomRenderCmd(this, this._onRenderSaveCmd); + this._rendererClipCmd = new cc.CustomRenderCmd(this, this._onRenderClipCmd); + this._rendererRestoreCmd = new cc.CustomRenderCmd(this, this._onRenderRestoreCmd); + } + + cc.renderer.pushRenderCommand(this._rendererSaveCmd); + node._clippingStencil.visit(this); + + cc.renderer.pushRenderCommand(this._rendererClipCmd); + }; + + proto.postStencilVisit = proto.postScissorVisit = function () { + cc.renderer.pushRenderCommand(this._rendererRestoreCmd); + }; + + ccui.Layout.CanvasRenderCmd._getSharedCache = function () { + return (cc.ClippingNode._sharedCache) || (cc.ClippingNode._sharedCache = document.createElement("canvas")); + }; +})(); diff --git a/frameworks/cocos2d-html5/extensions/ccui/layouts/UILayoutComponent.js b/frameworks/cocos2d-html5/extensions/ccui/layouts/UILayoutComponent.js new file mode 100644 index 0000000..3a910f6 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/layouts/UILayoutComponent.js @@ -0,0 +1,598 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +ccui.LayoutComponent_ReferencePoint = { + BOTTOM_LEFT: 0, + TOP_LEFT: 1, + BOTTOM_RIGHT: 2, + TOP_RIGHT: 3 +}; +ccui.LayoutComponent_PositionType = { + Position: 0, + RelativePosition: 1, + PreRelativePosition: 2, + PreRelativePositionEnable: 3 +}; +ccui.LayoutComponent_SizeType = { + Size: 0, + PreSize: 1, + PreSizeEnable: 2 +}; + +//refactor since v3.3 +ccui.LayoutComponent = cc.Component.extend({ + _horizontalEdge: 0, + _verticalEdge: 0, + + _leftMargin: 0, + _rightMargin: 0, + _bottomMargin: 0, + _topMargin: 0, + + _usingPositionPercentX: false, + _positionPercentX: 0, + _usingPositionPercentY: false, + _positionPercentY: 0, + + _usingStretchWidth: false, + _usingStretchHeight: false, + + _percentWidth: 0, + _usingPercentWidth: false, + + _percentHeight: 0, + _usingPercentHeight: false, + + _actived: true, + _isPercentOnly: false, + + ctor: function () { + this._name = ccui.LayoutComponent.NAME; + }, + + init: function () { + var ret = true; + + if (!cc.Component.prototype.init.call(this)) { + return false; + } + + //put layout component initalized code here + + return ret; + }, + + getPercentContentSize: function () { + return cc.p(this._percentWidth, this._percentHeight); + }, + setPercentContentSize: function (percent) { + this.setPercentWidth(percent.x); + this.setPercentHeight(percent.y); + }, + + setUsingPercentContentSize: function (isUsed) { + this._usingPercentWidth = this._usingPercentHeight = isUsed; + }, + + //old + SetActiveEnable: function (enable) { + this._actived = enable; + }, + + //v3.3 + getUsingPercentContentSize: function () { + return this._usingPercentWidth && this._usingPercentHeight; + }, + + //position & margin + getAnchorPosition: function () { + return this._owner.getAnchorPoint(); + }, + + setAnchorPosition: function (point, y) { + var oldRect = this._owner.getBoundingBox(); + this._owner.setAnchorPoint(point, y); + var newRect = this._owner.getBoundingBox(); + var offSetX = oldRect.x - newRect.x, offSetY = oldRect.y - newRect.y; + + var ownerPosition = this._owner.getPosition(); + ownerPosition.x += offSetX; + ownerPosition.y += offSetY; + this.setPosition(ownerPosition); + }, + + getPosition: function () { + return this._owner.getPosition(); + }, + + setPosition: function (position, y) { + var parent = this._getOwnerParent(), x; + if (parent != null) { + if (y === undefined) { + x = position.x; + y = position.y; + } else + x = position; + var parentSize = parent.getContentSize(); + + if (parentSize.width !== 0) + this._positionPercentX = x / parentSize.width; + else { + this._positionPercentX = 0; + if (this._usingPositionPercentX) + x = 0; + } + + if (parentSize.height !== 0) + this._positionPercentY = y / parentSize.height; + else { + this._positionPercentY = 0; + if (this._usingPositionPercentY) + y = 0; + } + + this._owner.setPosition(x, y); + this._refreshHorizontalMargin(); + this._refreshVerticalMargin(); + } else + this._owner.setPosition(position, y); + }, + + isPositionPercentXEnabled: function () { + return this._usingPositionPercentX; + }, + setPositionPercentXEnabled: function (isUsed) { + this._usingPositionPercentX = isUsed; + if (this._usingPositionPercentX) + this._horizontalEdge = ccui.LayoutComponent.horizontalEdge.NONE; + }, + + getPositionPercentX: function () { + return this._positionPercentX; + }, + setPositionPercentX: function (percentMargin) { + this._positionPercentX = percentMargin; + + var parent = this._getOwnerParent(); + if (parent !== null) { + this._owner.setPositionX(parent.width * this._positionPercentX); + this._refreshHorizontalMargin(); + } + }, + + isPositionPercentYEnabled: function () { + return this._usingPositionPercentY; + }, + setPositionPercentYEnabled: function (isUsed) { + this._usingPositionPercentY = isUsed; + if (this._usingPositionPercentY) + this._verticalEdge = ccui.LayoutComponent.verticalEdge.NONE; + }, + + getPositionPercentY: function () { + return this._positionPercentY; + }, + setPositionPercentY: function (percentMargin) { + this._positionPercentY = percentMargin; + + var parent = this._getOwnerParent(); + if (parent !== null) { + this._owner.setPositionY(parent.height * this._positionPercentY); + this._refreshVerticalMargin(); + } + }, + + getHorizontalEdge: function () { + return this._horizontalEdge; + }, + setHorizontalEdge: function (hEdge) { + this._horizontalEdge = hEdge; + if (this._horizontalEdge !== ccui.LayoutComponent.horizontalEdge.NONE) + this._usingPositionPercentX = false; + + var parent = this._getOwnerParent(); + if (parent !== null) { + var ownerPoint = this._owner.getPosition(); + var parentSize = parent.getContentSize(); + if (parentSize.width !== 0) + this._positionPercentX = ownerPoint.x / parentSize.width; + else { + this._positionPercentX = 0; + ownerPoint.x = 0; + if (this._usingPositionPercentX) + this._owner.setPosition(ownerPoint); + } + this._refreshHorizontalMargin(); + } + }, + + getVerticalEdge: function () { + return this._verticalEdge; + }, + setVerticalEdge: function (vEdge) { + this._verticalEdge = vEdge; + if (this._verticalEdge !== ccui.LayoutComponent.verticalEdge.NONE) + this._usingPositionPercentY = false; + + var parent = this._getOwnerParent(); + if (parent !== null) { + var ownerPoint = this._owner.getPosition(); + var parentSize = parent.getContentSize(); + if (parentSize.height !== 0) + this._positionPercentY = ownerPoint.y / parentSize.height; + else { + this._positionPercentY = 0; + ownerPoint.y = 0; + if (this._usingPositionPercentY) + this._owner.setPosition(ownerPoint); + } + this._refreshVerticalMargin(); + } + }, + + getLeftMargin: function () { + return this._leftMargin; + }, + setLeftMargin: function (margin) { + this._leftMargin = margin; + }, + + getRightMargin: function () { + return this._rightMargin; + }, + setRightMargin: function (margin) { + this._rightMargin = margin; + }, + + getTopMargin: function () { + return this._topMargin; + }, + setTopMargin: function (margin) { + this._topMargin = margin; + }, + + getBottomMargin: function () { + return this._bottomMargin; + }, + setBottomMargin: function (margin) { + this._bottomMargin = margin; + }, + + //size & + getSize: function () { + return this.getOwner().getContentSize(); + }, + setSize: function (size) { + var parent = this._getOwnerParent(); + if (parent !== null) { + var ownerSize = size, parentSize = parent.getContentSize(); + + if (parentSize.width !== 0) + this._percentWidth = ownerSize.width / parentSize.width; + else { + this._percentWidth = 0; + if (this._usingPercentWidth) + ownerSize.width = 0; + } + + if (parentSize.height !== 0) + this._percentHeight = ownerSize.height / parentSize.height; + else { + this._percentHeight = 0; + if (this._usingPercentHeight) + ownerSize.height = 0; + } + + this._owner.setContentSize(ownerSize); + + this._refreshHorizontalMargin(); + this._refreshVerticalMargin(); + } + else + this._owner.setContentSize(size); + }, + + isPercentWidthEnabled: function () { + return this._usingPercentWidth; + }, + setPercentWidthEnabled: function (isUsed) { + this._usingPercentWidth = isUsed; + if (this._usingPercentWidth) + this._usingStretchWidth = false; + }, + + getSizeWidth: function () { + return this._owner.width; + }, + setSizeWidth: function (width) { + var ownerSize = this._owner.getContentSize(); + ownerSize.width = width; + + var parent = this._getOwnerParent(); + if (parent !== null) { + var parentSize = parent.getContentSize(); + if (parentSize.width !== 0) + this._percentWidth = ownerSize.width / parentSize.width; + else { + this._percentWidth = 0; + if (this._usingPercentWidth) + ownerSize.width = 0; + } + this._owner.setContentSize(ownerSize); + this._refreshHorizontalMargin(); + } else + this._owner.setContentSize(ownerSize); + }, + + getPercentWidth: function () { + return this._percentWidth; + }, + setPercentWidth: function (percentWidth) { + this._percentWidth = percentWidth; + + var parent = this._getOwnerParent(); + if (parent !== null) { + var ownerSize = this._owner.getContentSize(); + ownerSize.width = parent.width * this._percentWidth; + this._owner.setContentSize(ownerSize); + this._refreshHorizontalMargin(); + } + }, + + isPercentHeightEnabled: function () { + return this._usingPercentHeight; + }, + setPercentHeightEnabled: function (isUsed) { + this._usingPercentHeight = isUsed; + if (this._usingPercentHeight) + this._usingStretchHeight = false; + }, + + getSizeHeight: function () { + return this._owner.height; + }, + setSizeHeight: function (height) { + var ownerSize = this._owner.getContentSize(); + ownerSize.height = height; + + var parent = this._getOwnerParent(); + if (parent !== null) { + var parentSize = parent.getContentSize(); + if (parentSize.height !== 0) + this._percentHeight = ownerSize.height / parentSize.height; + else { + this._percentHeight = 0; + if (this._usingPercentHeight) + ownerSize.height = 0; + } + this._owner.setContentSize(ownerSize); + this._refreshVerticalMargin(); + } + else + this._owner.setContentSize(ownerSize); + }, + + getPercentHeight: function () { + return this._percentHeight; + }, + setPercentHeight: function (percentHeight) { + this._percentHeight = percentHeight; + + var parent = this._getOwnerParent(); + if (parent !== null) { + var ownerSize = this._owner.getContentSize(); + ownerSize.height = parent.height * this._percentHeight; + this._owner.setContentSize(ownerSize); + this._refreshVerticalMargin(); + } + }, + + isStretchWidthEnabled: function () { + return this._usingStretchWidth; + }, + setStretchWidthEnabled: function (isUsed) { + this._usingStretchWidth = isUsed; + if (this._usingStretchWidth) + this._usingPercentWidth = false; + }, + + isStretchHeightEnabled: function () { + return this._usingStretchHeight; + }, + setStretchHeightEnabled: function (isUsed) { + this._usingStretchHeight = isUsed; + if (this._usingStretchHeight) + this._usingPercentHeight = false; + }, + + setPercentOnlyEnabled: function(enable){ + this._isPercentOnly = enable; + }, + + setActiveEnabled: function (enable) { + this._actived = enable; + }, + refreshLayout: function () { + if(!this._actived) + return; + + var parent = this._getOwnerParent(); + if (parent === null) + return; + + var parentSize = parent.getContentSize(), locOwner = this._owner; + var ownerAnchor = locOwner.getAnchorPoint(), ownerSize = locOwner.getContentSize(); + var ownerPosition = locOwner.getPosition(); + + switch (this._horizontalEdge) { + case ccui.LayoutComponent.horizontalEdge.NONE: + if (this._usingStretchWidth && !this._isPercentOnly) { + ownerSize.width = parentSize.width * this._percentWidth; + ownerPosition.x = this._leftMargin + ownerAnchor.x * ownerSize.width; + } else { + if (this._usingPositionPercentX) + ownerPosition.x = parentSize.width * this._positionPercentX; + if (this._usingPercentWidth) + ownerSize.width = parentSize.width * this._percentWidth; + } + break; + case ccui.LayoutComponent.horizontalEdge.LEFT: + if(this._isPercentOnly) + break; + if (this._usingPercentWidth || this._usingStretchWidth) + ownerSize.width = parentSize.width * this._percentWidth; + ownerPosition.x = this._leftMargin + ownerAnchor.x * ownerSize.width; + break; + case ccui.LayoutComponent.horizontalEdge.RIGHT: + if(this._isPercentOnly) + break; + if (this._usingPercentWidth || this._usingStretchWidth) + ownerSize.width = parentSize.width * this._percentWidth; + ownerPosition.x = parentSize.width - (this._rightMargin + (1 - ownerAnchor.x) * ownerSize.width); + break; + case ccui.LayoutComponent.horizontalEdge.CENTER: + if(this._isPercentOnly) + break; + if (this._usingStretchWidth) { + ownerSize.width = parentSize.width - this._leftMargin - this._rightMargin; + if (ownerSize.width < 0) + ownerSize.width = 0; + ownerPosition.x = this._leftMargin + ownerAnchor.x * ownerSize.width; + } else { + if (this._usingPercentWidth) + ownerSize.width = parentSize.width * this._percentWidth; + ownerPosition.x = parentSize.width * this._positionPercentX; + } + break; + default: + break; + } + + switch (this._verticalEdge) { + case ccui.LayoutComponent.verticalEdge.NONE: + if (this._usingStretchHeight && !this._isPercentOnly) { + ownerSize.height = parentSize.height * this._percentHeight; + ownerPosition.y = this._bottomMargin + ownerAnchor.y * ownerSize.height; + } else { + if (this._usingPositionPercentY) + ownerPosition.y = parentSize.height * this._positionPercentY; + if (this._usingPercentHeight) + ownerSize.height = parentSize.height * this._percentHeight; + } + break; + case ccui.LayoutComponent.verticalEdge.BOTTOM: + if(this._isPercentOnly) + break; + if (this._usingPercentHeight || this._usingStretchHeight) + ownerSize.height = parentSize.height * this._percentHeight; + ownerPosition.y = this._bottomMargin + ownerAnchor.y * ownerSize.height; + break; + case ccui.LayoutComponent.verticalEdge.TOP: + if(this._isPercentOnly) + break; + if (this._usingPercentHeight || this._usingStretchHeight) + ownerSize.height = parentSize.height * this._percentHeight; + ownerPosition.y = parentSize.height - (this._topMargin + (1 - ownerAnchor.y) * ownerSize.height); + break; + case ccui.LayoutComponent.verticalEdge.CENTER: + if(this._isPercentOnly) + break; + if (this._usingStretchHeight) { + ownerSize.height = parentSize.height - this._topMargin - this._bottomMargin; + if (ownerSize.height < 0) + ownerSize.height = 0; + ownerPosition.y = this._bottomMargin + ownerAnchor.y * ownerSize.height; + } else { + if(this._usingPercentHeight) + ownerSize.height = parentSize.height * this._percentHeight; + ownerPosition.y = parentSize.height * this._positionPercentY; + } + break; + default: + break; + } + + locOwner.setPosition(ownerPosition); + locOwner.setContentSize(ownerSize); + + if(locOwner instanceof ccui.PageView){ + locOwner.forceDoLayout(); + + var layoutVector = locOwner.getPages(); + for(var i=0; i 0) { + for (var i = 0, len = locChildren.length; i < len; i++) { + this._widget = locChildren[i]; + + var layoutParameter = this._widget.getLayoutParameter(); + if (layoutParameter){ + if (layoutParameter._put) + continue; + + var ret = this._calculateFinalPositionWithRelativeWidget(layout); + if (!ret) + continue; + + this._calculateFinalPositionWithRelativeAlign(); + + this._widget.setPosition(this._finalPositionX, this._finalPositionY); + layoutParameter._put = true; + } + } + this._unlayoutChildCount--; + } + this._widgetChildren.length = 0; + }, + + _getAllWidgets: function (layout) { + var container = layout._getLayoutElements(); + var locWidgetChildren = this._widgetChildren; + locWidgetChildren.length = 0; + for (var i = 0, len = container.length; i < len; i++) { + var child = container[i]; + if (child && child instanceof ccui.Widget) { + var layoutParameter = child.getLayoutParameter(); + layoutParameter._put = false; + this._unlayoutChildCount++; + locWidgetChildren.push(child); + } + } + return locWidgetChildren; + }, + + _getRelativeWidget: function (widget) { + var relativeWidget = null; + var layoutParameter = widget.getLayoutParameter(); + var relativeName = layoutParameter.getRelativeToWidgetName(); + + if (relativeName && relativeName.length !== 0) { + var locChildren = this._widgetChildren; + for (var i = 0, len = locChildren.length; i < len; i++) { + var child = locChildren[i]; + if (child){ + var rlayoutParameter = child.getLayoutParameter(); + if (rlayoutParameter && rlayoutParameter.getRelativeName() === relativeName) { + relativeWidget = child; + this._relativeWidgetLP = rlayoutParameter; + break; + } + } + } + } + return relativeWidget; + }, + + _calculateFinalPositionWithRelativeWidget: function (layout) { + var locWidget = this._widget; + var ap = locWidget.getAnchorPoint(); + var cs = locWidget.getContentSize(); + + this._finalPositionX = 0.0; + this._finalPositionY = 0.0; + + var relativeWidget = this._getRelativeWidget(locWidget); + var layoutParameter = locWidget.getLayoutParameter(); + var align = layoutParameter.getAlign(); + var layoutSize = layout._getLayoutContentSize(); + + switch (align) { + case ccui.RelativeLayoutParameter.NONE: + case ccui.RelativeLayoutParameter.PARENT_TOP_LEFT: + this._finalPositionX = ap.x * cs.width; + this._finalPositionY = layoutSize.height - ((1.0 - ap.y) * cs.height); + break; + case ccui.RelativeLayoutParameter.PARENT_TOP_CENTER_HORIZONTAL: + this._finalPositionX = layoutSize.width * 0.5 - cs.width * (0.5 - ap.x); + this._finalPositionY = layoutSize.height - ((1.0 - ap.y) * cs.height); + break; + case ccui.RelativeLayoutParameter.PARENT_TOP_RIGHT: + this._finalPositionX = layoutSize.width - ((1.0 - ap.x) * cs.width); + this._finalPositionY = layoutSize.height - ((1.0 - ap.y) * cs.height); + break; + case ccui.RelativeLayoutParameter.PARENT_LEFT_CENTER_VERTICAL: + this._finalPositionX = ap.x * cs.width; + this._finalPositionY = layoutSize.height * 0.5 - cs.height * (0.5 - ap.y); + break; + case ccui.RelativeLayoutParameter.CENTER_IN_PARENT: + this._finalPositionX = layoutSize.width * 0.5 - cs.width * (0.5 - ap.x); + this._finalPositionY = layoutSize.height * 0.5 - cs.height * (0.5 - ap.y); + break; + case ccui.RelativeLayoutParameter.PARENT_RIGHT_CENTER_VERTICAL: + this._finalPositionX = layoutSize.width - ((1.0 - ap.x) * cs.width); + this._finalPositionY = layoutSize.height * 0.5 - cs.height * (0.5 - ap.y); + break; + case ccui.RelativeLayoutParameter.PARENT_LEFT_BOTTOM: + this._finalPositionX = ap.x * cs.width; + this._finalPositionY = ap.y * cs.height; + break; + case ccui.RelativeLayoutParameter.PARENT_BOTTOM_CENTER_HORIZONTAL: + this._finalPositionX = layoutSize.width * 0.5 - cs.width * (0.5 - ap.x); + this._finalPositionY = ap.y * cs.height; + break; + case ccui.RelativeLayoutParameter.PARENT_RIGHT_BOTTOM: + this._finalPositionX = layoutSize.width - ((1.0 - ap.x) * cs.width); + this._finalPositionY = ap.y * cs.height; + break; + + case ccui.RelativeLayoutParameter.LOCATION_ABOVE_LEFTALIGN: + if (relativeWidget) { + if (this._relativeWidgetLP && !this._relativeWidgetLP._put) + return false; + this._finalPositionY = relativeWidget.getTopBoundary() + ap.y * cs.height; + this._finalPositionX = relativeWidget.getLeftBoundary() + ap.x * cs.width; + } + break; + case ccui.RelativeLayoutParameter.LOCATION_ABOVE_CENTER: + if (relativeWidget) { + if (this._relativeWidgetLP && !this._relativeWidgetLP._put) + return false; + var rbs = relativeWidget.getContentSize(); + this._finalPositionY = relativeWidget.getTopBoundary() + ap.y * cs.height; + this._finalPositionX = relativeWidget.getLeftBoundary() + rbs.width * 0.5 + ap.x * cs.width - cs.width * 0.5; + } + break; + case ccui.RelativeLayoutParameter.LOCATION_ABOVE_RIGHTALIGN: + if (relativeWidget) { + if (this._relativeWidgetLP && !this._relativeWidgetLP._put) + return false; + this._finalPositionY = relativeWidget.getTopBoundary() + ap.y * cs.height; + this._finalPositionX = relativeWidget.getRightBoundary() - (1.0 - ap.x) * cs.width; + } + break; + case ccui.RelativeLayoutParameter.LOCATION_LEFT_OF_TOPALIGN: + if (relativeWidget) { + if (this._relativeWidgetLP && !this._relativeWidgetLP._put) + return false; + this._finalPositionY = relativeWidget.getTopBoundary() - (1.0 - ap.y) * cs.height; + this._finalPositionX = relativeWidget.getLeftBoundary() - (1.0 - ap.x) * cs.width; + } + break; + case ccui.RelativeLayoutParameter.LOCATION_LEFT_OF_CENTER: + if (relativeWidget) { + if (this._relativeWidgetLP && !this._relativeWidgetLP._put) + return false; + var rbs = relativeWidget.getContentSize(); + this._finalPositionX = relativeWidget.getLeftBoundary() - (1.0 - ap.x) * cs.width; + this._finalPositionY = relativeWidget.getBottomBoundary() + rbs.height * 0.5 + ap.y * cs.height - cs.height * 0.5; + } + break; + case ccui.RelativeLayoutParameter.LOCATION_LEFT_OF_BOTTOMALIGN: + if (relativeWidget) { + if (this._relativeWidgetLP && !this._relativeWidgetLP._put) + return false; + this._finalPositionY = relativeWidget.getBottomBoundary() + ap.y * cs.height; + this._finalPositionX = relativeWidget.getLeftBoundary() - (1.0 - ap.x) * cs.width; + } + break; + case ccui.RelativeLayoutParameter.LOCATION_RIGHT_OF_TOPALIGN: + if (relativeWidget) { + if (this._relativeWidgetLP && !this._relativeWidgetLP._put) + return false; + this._finalPositionY = relativeWidget.getTopBoundary() - (1.0 - ap.y) * cs.height; + this._finalPositionX = relativeWidget.getRightBoundary() + ap.x * cs.width; + } + break; + case ccui.RelativeLayoutParameter.LOCATION_RIGHT_OF_CENTER: + if (relativeWidget) { + if (this._relativeWidgetLP && !this._relativeWidgetLP._put) + return false; + var rbs = relativeWidget.getContentSize(); + var locationRight = relativeWidget.getRightBoundary(); + this._finalPositionX = locationRight + ap.x * cs.width; + this._finalPositionY = relativeWidget.getBottomBoundary() + rbs.height * 0.5 + ap.y * cs.height - cs.height * 0.5; + } + break; + case ccui.RelativeLayoutParameter.LOCATION_RIGHT_OF_BOTTOMALIGN: + if (relativeWidget) { + if (this._relativeWidgetLP && !this._relativeWidgetLP._put) + return false; + this._finalPositionY = relativeWidget.getBottomBoundary() + ap.y * cs.height; + this._finalPositionX = relativeWidget.getRightBoundary() + ap.x * cs.width; + } + break; + case ccui.RelativeLayoutParameter.LOCATION_BELOW_LEFTALIGN: + if (relativeWidget) { + if (this._relativeWidgetLP && !this._relativeWidgetLP._put) + return false; + this._finalPositionY = relativeWidget.getBottomBoundary() - (1.0 - ap.y) * cs.height; + this._finalPositionX = relativeWidget.getLeftBoundary() + ap.x * cs.width; + } + break; + case ccui.RelativeLayoutParameter.LOCATION_BELOW_CENTER: + if (relativeWidget) { + if (this._relativeWidgetLP && !this._relativeWidgetLP._put) + return false; + var rbs = relativeWidget.getContentSize(); + this._finalPositionY = relativeWidget.getBottomBoundary() - (1.0 - ap.y) * cs.height; + this._finalPositionX = relativeWidget.getLeftBoundary() + rbs.width * 0.5 + ap.x * cs.width - cs.width * 0.5; + } + break; + case ccui.RelativeLayoutParameter.LOCATION_BELOW_RIGHTALIGN: + if (relativeWidget) { + if (this._relativeWidgetLP && !this._relativeWidgetLP._put) + return false; + this._finalPositionY = relativeWidget.getBottomBoundary() - (1.0 - ap.y) * cs.height; + this._finalPositionX = relativeWidget.getRightBoundary() - (1.0 - ap.x) * cs.width; + } + break; + default: + break; + } + return true; + }, + + _calculateFinalPositionWithRelativeAlign: function () { + var layoutParameter = this._widget.getLayoutParameter(); + + var mg = layoutParameter.getMargin(); + var align = layoutParameter.getAlign(); + + //handle margin + switch (align) { + case ccui.RelativeLayoutParameter.NONE: + case ccui.RelativeLayoutParameter.PARENT_TOP_LEFT: + this._finalPositionX += mg.left; + this._finalPositionY -= mg.top; + break; + case ccui.RelativeLayoutParameter.PARENT_TOP_CENTER_HORIZONTAL: + this._finalPositionY -= mg.top; + break; + case ccui.RelativeLayoutParameter.PARENT_TOP_RIGHT: + this._finalPositionX -= mg.right; + this._finalPositionY -= mg.top; + break; + case ccui.RelativeLayoutParameter.PARENT_LEFT_CENTER_VERTICAL: + this._finalPositionX += mg.left; + break; + case ccui.RelativeLayoutParameter.CENTER_IN_PARENT: + break; + case ccui.RelativeLayoutParameter.PARENT_RIGHT_CENTER_VERTICAL: + this._finalPositionX -= mg.right; + break; + case ccui.RelativeLayoutParameter.PARENT_LEFT_BOTTOM: + this._finalPositionX += mg.left; + this._finalPositionY += mg.bottom; + break; + case ccui.RelativeLayoutParameter.PARENT_BOTTOM_CENTER_HORIZONTAL: + this._finalPositionY += mg.bottom; + break; + case ccui.RelativeLayoutParameter.PARENT_RIGHT_BOTTOM: + this._finalPositionX -= mg.right; + this._finalPositionY += mg.bottom; + break; + case ccui.RelativeLayoutParameter.LOCATION_ABOVE_LEFTALIGN: + this._finalPositionY += mg.bottom; + this._finalPositionX += mg.left; + break; + case ccui.RelativeLayoutParameter.LOCATION_ABOVE_RIGHTALIGN: + this._finalPositionY += mg.bottom; + this._finalPositionX -= mg.right; + break; + case ccui.RelativeLayoutParameter.LOCATION_ABOVE_CENTER: + this._finalPositionY += mg.bottom; + break; + case ccui.RelativeLayoutParameter.LOCATION_LEFT_OF_TOPALIGN: + this._finalPositionX -= mg.right; + this._finalPositionY -= mg.top; + break; + case ccui.RelativeLayoutParameter.LOCATION_LEFT_OF_BOTTOMALIGN: + this._finalPositionX -= mg.right; + this._finalPositionY += mg.bottom; + break; + case ccui.RelativeLayoutParameter.LOCATION_LEFT_OF_CENTER: + this._finalPositionX -= mg.right; + break; + case ccui.RelativeLayoutParameter.LOCATION_RIGHT_OF_TOPALIGN: + this._finalPositionX += mg.left; + this._finalPositionY -= mg.top; + break; + case ccui.RelativeLayoutParameter.LOCATION_RIGHT_OF_BOTTOMALIGN: + this._finalPositionX += mg.left; + this._finalPositionY += mg.bottom; + break; + case ccui.RelativeLayoutParameter.LOCATION_RIGHT_OF_CENTER: + this._finalPositionX += mg.left; + break; + case ccui.RelativeLayoutParameter.LOCATION_BELOW_LEFTALIGN: + this._finalPositionY -= mg.top; + this._finalPositionX += mg.left; + break; + case ccui.RelativeLayoutParameter.LOCATION_BELOW_RIGHTALIGN: + this._finalPositionY -= mg.top; + this._finalPositionX -= mg.right; + break; + case ccui.RelativeLayoutParameter.LOCATION_BELOW_CENTER: + this._finalPositionY -= mg.top; + break; + default: + break; + } + } +}; diff --git a/frameworks/cocos2d-html5/extensions/ccui/layouts/UILayoutParameter.js b/frameworks/cocos2d-html5/extensions/ccui/layouts/UILayoutParameter.js new file mode 100644 index 0000000..2ea2919 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/layouts/UILayoutParameter.js @@ -0,0 +1,576 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * Base class for ccui.Margin + * @class + * @extends ccui.Class + * + * @property {Number} left - Left of margin + * @property {Number} top - Top of margin + * @property {Number} right - right of margin + * @property {Number} bottom - bottom of margin + */ +ccui.Margin = ccui.Class.extend(/** @lends ccui.Margin# */{ + left: 0, + top: 0, + right: 0, + bottom: 0, + /** + * Constructor of ccui.Margin. + * @param {Number|ccui.Margin} margin a margin or left + * @param {Number} [top] + * @param {Number} [right] + * @param {Number} [bottom] + */ + ctor: function (margin, top, right, bottom) { + if (margin !== undefined && top === undefined) { + this.left = margin.left; + this.top = margin.top; + this.right = margin.right; + this.bottom = margin.bottom; + } + if (bottom !== undefined) { + this.left = margin; + this.top = top; + this.right = right; + this.bottom = bottom; + } + }, + /** + * Sets boundary of margin + * @param {Number} l left + * @param {Number} t top + * @param {Number} r right + * @param {Number} b bottom + */ + setMargin: function (l, t, r, b) { + this.left = l; + this.top = t; + this.right = r; + this.bottom = b; + }, + /** + * Checks target whether equals itself. + * @param {ccui.Margin} target + * @returns {boolean} + */ + equals: function (target) { + return (this.left === target.left && this.top === target.top && this.right === target.right && this.bottom === target.bottom); + } +}); + +/** + * Gets a zero margin object + * @function + * @returns {ccui.Margin} + */ +ccui.MarginZero = function(){ + return new ccui.Margin(0,0,0,0); +}; + +/** + * Layout parameter contains a margin and layout parameter type. It uses for ccui.LayoutManager. + * @class + * @extends ccui.Class + */ +ccui.LayoutParameter = ccui.Class.extend(/** @lends ccui.LayoutParameter# */{ + _margin: null, + _layoutParameterType: null, + + /** + * The constructor of ccui.LayoutParameter. + * @function + */ + ctor: function () { + this._margin = new ccui.Margin(); + this._layoutParameterType = ccui.LayoutParameter.NONE; + }, + + /** + * Sets Margin to LayoutParameter. + * @param {ccui.Margin} margin + */ + setMargin: function (margin) { + if(cc.isObject(margin)){ + this._margin.left = margin.left; + this._margin.top = margin.top; + this._margin.right = margin.right; + this._margin.bottom = margin.bottom; + }else{ + this._margin.left = arguments[0]; + this._margin.top = arguments[1]; + this._margin.right = arguments[2]; + this._margin.bottom = arguments[3]; + } + }, + + /** + * Gets Margin of LayoutParameter. + * @returns {ccui.Margin} + */ + getMargin: function () { + return this._margin; + }, + + /** + * Gets LayoutParameterType of LayoutParameter. + * @returns {Number} + */ + getLayoutType: function () { + return this._layoutParameterType; + }, + + /** + * Clones a ccui.LayoutParameter object from itself. + * @returns {ccui.LayoutParameter} + */ + clone:function(){ + var parameter = this._createCloneInstance(); + parameter._copyProperties(this); + return parameter; + }, + + /** + * create clone instance. + * @returns {ccui.LayoutParameter} + */ + _createCloneInstance:function(){ + return new ccui.LayoutParameter(); + }, + + /** + * copy properties from model. + * @param {ccui.LayoutParameter} model + */ + _copyProperties:function(model){ + this._margin.bottom = model._margin.bottom; + this._margin.left = model._margin.left; + this._margin.right = model._margin.right; + this._margin.top = model._margin.top; + } +}); + +/** + * allocates and initializes a LayoutParameter. + * @constructs + * @return {ccui.LayoutParameter} + */ +ccui.LayoutParameter.create = function () { + return new ccui.LayoutParameter(); +}; + +// Constants +//layout parameter type +/** + * The none of ccui.LayoutParameter's type. + * @constant + * @type {number} + */ +ccui.LayoutParameter.NONE = 0; +/** + * The linear of ccui.LayoutParameter's type. + * @constant + * @type {number} + */ +ccui.LayoutParameter.LINEAR = 1; +/** + * The relative of ccui.LayoutParameter's type. + * @constant + * @type {number} + */ +ccui.LayoutParameter.RELATIVE = 2; + +/** + * The linear of Layout parameter. its parameter type is ccui.LayoutParameter.LINEAR. + * @class + * @extends ccui.LayoutParameter + */ +ccui.LinearLayoutParameter = ccui.LayoutParameter.extend(/** @lends ccui.LinearLayoutParameter# */{ + _linearGravity: null, + /** + * The constructor of ccui.LinearLayoutParameter. + * @function + */ + ctor: function () { + ccui.LayoutParameter.prototype.ctor.call(this); + this._linearGravity = ccui.LinearLayoutParameter.NONE; + this._layoutParameterType = ccui.LayoutParameter.LINEAR; + }, + + /** + * Sets LinearGravity to LayoutParameter. + * @param {Number} gravity + */ + setGravity: function (gravity) { + this._linearGravity = gravity; + }, + + /** + * Gets LinearGravity of LayoutParameter. + * @returns {Number} + */ + getGravity: function () { + return this._linearGravity; + }, + + _createCloneInstance: function () { + return new ccui.LinearLayoutParameter(); + }, + + _copyProperties: function (model) { + ccui.LayoutParameter.prototype._copyProperties.call(this, model); + if (model instanceof ccui.LinearLayoutParameter) + this.setGravity(model._linearGravity); + } +}); + +/** + * allocates and initializes a LinearLayoutParameter. + * @constructs + * @return {ccui.LinearLayoutParameter} + * @deprecated since v3.0, please use new construction instead + */ +ccui.LinearLayoutParameter.create = function () { + return new ccui.LinearLayoutParameter(); +}; + +// Constants +//Linear layout parameter LinearGravity +/** + * The none of ccui.LinearLayoutParameter's linear gravity. + * @constant + * @type {number} + */ +ccui.LinearLayoutParameter.NONE = 0; + +/** + * The left of ccui.LinearLayoutParameter's linear gravity. + * @constant + * @type {number} + */ +ccui.LinearLayoutParameter.LEFT = 1; +/** + * The top of ccui.LinearLayoutParameter's linear gravity. + * @constant + * @type {number} + */ +ccui.LinearLayoutParameter.TOP = 2; +/** + * The right of ccui.LinearLayoutParameter's linear gravity. + * @constant + * @type {number} + */ +ccui.LinearLayoutParameter.RIGHT = 3; +/** + * The bottom of ccui.LinearLayoutParameter's linear gravity. + * @constant + * @type {number} + */ +ccui.LinearLayoutParameter.BOTTOM = 4; +/** + * The center vertical of ccui.LinearLayoutParameter's linear gravity. + * @constant + * @type {number} + */ +ccui.LinearLayoutParameter.CENTER_VERTICAL = 5; +/** + * The center horizontal of ccui.LinearLayoutParameter's linear gravity. + * @constant + * @type {number} + */ +ccui.LinearLayoutParameter.CENTER_HORIZONTAL = 6; + +/** + * The relative of layout parameter. Its layout parameter type is ccui.LayoutParameter.RELATIVE. + * @class + * @extends ccui.LayoutParameter + */ +ccui.RelativeLayoutParameter = ccui.LayoutParameter.extend(/** @lends ccui.RelativeLayoutParameter# */{ + _relativeAlign: null, + _relativeWidgetName: "", + _relativeLayoutName: "", + _put:false, + /** + * The constructor of ccui.RelativeLayoutParameter + * @function + */ + ctor: function () { + ccui.LayoutParameter.prototype.ctor.call(this); + this._relativeAlign = ccui.RelativeLayoutParameter.NONE; + this._relativeWidgetName = ""; + this._relativeLayoutName = ""; + this._put = false; + this._layoutParameterType = ccui.LayoutParameter.RELATIVE; + }, + + /** + * Sets RelativeAlign parameter for LayoutParameter. + * @param {Number} align + */ + setAlign: function (align) { + this._relativeAlign = align; + }, + + /** + * Gets RelativeAlign parameter for LayoutParameter. + * @returns {Number} + */ + getAlign: function () { + return this._relativeAlign; + }, + + /** + * Sets a key for LayoutParameter. Witch widget named this is relative to. + * @param {String} name + */ + setRelativeToWidgetName: function (name) { + this._relativeWidgetName = name; + }, + + /** + * Gets the key of LayoutParameter. Witch widget named this is relative to. + * @returns {string} + */ + getRelativeToWidgetName: function () { + return this._relativeWidgetName; + }, + + /** + * Sets a name in Relative Layout for LayoutParameter. + * @param {String} name + */ + setRelativeName: function (name) { + this._relativeLayoutName = name; + }, + + /** + * Gets a name in Relative Layout of LayoutParameter. + * @returns {string} + */ + getRelativeName: function () { + return this._relativeLayoutName; + }, + + _createCloneInstance:function(){ + return new ccui.RelativeLayoutParameter(); + }, + + _copyProperties:function(model){ + ccui.LayoutParameter.prototype._copyProperties.call(this, model); + if (model instanceof ccui.RelativeLayoutParameter) { + this.setAlign(model._relativeAlign); + this.setRelativeToWidgetName(model._relativeWidgetName); + this.setRelativeName(model._relativeLayoutName); + } + } +}); + +/** + * Allocates and initializes a RelativeLayoutParameter. + * @function + * @deprecated since v3.0, please use new ccui.RelativeLayoutParameter() instead. + * @return {ccui.RelativeLayoutParameter} + */ +ccui.RelativeLayoutParameter.create = function () { + return new ccui.RelativeLayoutParameter(); +}; + +// Constants +//Relative layout parameter RelativeAlign +/** + * The none of ccui.RelativeLayoutParameter's relative align. + * @constant + * @type {number} + */ +ccui.RelativeLayoutParameter.NONE = 0; +/** + * The parent's top left of ccui.RelativeLayoutParameter's relative align. + * @constant + * @type {number} + */ +ccui.RelativeLayoutParameter.PARENT_TOP_LEFT = 1; +/** + * The parent's top center horizontal of ccui.RelativeLayoutParameter's relative align. + * @constant + * @type {number} + */ +ccui.RelativeLayoutParameter.PARENT_TOP_CENTER_HORIZONTAL = 2; +/** + * The parent's top right of ccui.RelativeLayoutParameter's relative align. + * @constant + * @type {number} + */ +ccui.RelativeLayoutParameter.PARENT_TOP_RIGHT = 3; +/** + * The parent's left center vertical of ccui.RelativeLayoutParameter's relative align. + * @constant + * @type {number} + */ +ccui.RelativeLayoutParameter.PARENT_LEFT_CENTER_VERTICAL = 4; + +/** + * The center in parent of ccui.RelativeLayoutParameter's relative align. + * @constant + * @type {number} + */ +ccui.RelativeLayoutParameter.CENTER_IN_PARENT = 5; + +/** + * The parent's right center vertical of ccui.RelativeLayoutParameter's relative align. + * @constant + * @type {number} + */ +ccui.RelativeLayoutParameter.PARENT_RIGHT_CENTER_VERTICAL = 6; +/** + * The parent's left bottom of ccui.RelativeLayoutParameter's relative align. + * @constant + * @type {number} + */ +ccui.RelativeLayoutParameter.PARENT_LEFT_BOTTOM = 7; +/** + * The parent's bottom center horizontal of ccui.RelativeLayoutParameter's relative align. + * @constant + * @type {number} + */ +ccui.RelativeLayoutParameter.PARENT_BOTTOM_CENTER_HORIZONTAL = 8; +/** + * The parent's right bottom of ccui.RelativeLayoutParameter's relative align. + * @constant + * @type {number} + */ +ccui.RelativeLayoutParameter.PARENT_RIGHT_BOTTOM = 9; + +/** + * The location above left align of ccui.RelativeLayoutParameter's relative align. + * @constant + * @type {number} + */ +ccui.RelativeLayoutParameter.LOCATION_ABOVE_LEFTALIGN = 10; +/** + * The location above center of ccui.RelativeLayoutParameter's relative align. + * @constant + * @type {number} + */ +ccui.RelativeLayoutParameter.LOCATION_ABOVE_CENTER = 11; +/** + * The location above right align of ccui.RelativeLayoutParameter's relative align. + * @constant + * @type {number} + */ +ccui.RelativeLayoutParameter.LOCATION_ABOVE_RIGHTALIGN = 12; +/** + * The location left of top align of ccui.RelativeLayoutParameter's relative align. + * @constant + * @type {number} + */ +ccui.RelativeLayoutParameter.LOCATION_LEFT_OF_TOPALIGN = 13; +/** + * The location left of center of ccui.RelativeLayoutParameter's relative align. + * @constant + * @type {number} + */ +ccui.RelativeLayoutParameter.LOCATION_LEFT_OF_CENTER = 14; +/** + * The location left of bottom align of ccui.RelativeLayoutParameter's relative align. + * @constant + * @type {number} + */ +ccui.RelativeLayoutParameter.LOCATION_LEFT_OF_BOTTOMALIGN = 15; +/** + * The location right of top align of ccui.RelativeLayoutParameter's relative align. + * @constant + * @type {number} + */ +ccui.RelativeLayoutParameter.LOCATION_RIGHT_OF_TOPALIGN = 16; +/** + * The location right of center of ccui.RelativeLayoutParameter's relative align. + * @constant + * @type {number} + */ +ccui.RelativeLayoutParameter.LOCATION_RIGHT_OF_CENTER = 17; +/** + * The location right of bottom align of ccui.RelativeLayoutParameter's relative align. + * @constant + * @type {number} + */ +ccui.RelativeLayoutParameter.LOCATION_RIGHT_OF_BOTTOMALIGN = 18; +/** + * The location below left align of ccui.RelativeLayoutParameter's relative align. + * @constant + * @type {number} + */ +ccui.RelativeLayoutParameter.LOCATION_BELOW_LEFTALIGN = 19; +/** + * The location below center of ccui.RelativeLayoutParameter's relative align. + * @constant + * @type {number} + */ +ccui.RelativeLayoutParameter.LOCATION_BELOW_CENTER = 20; +/** + * The location below right align of ccui.RelativeLayoutParameter's relative align. + * @constant + * @type {number} + */ +ccui.RelativeLayoutParameter.LOCATION_BELOW_RIGHTALIGN = 21; + +/** + * @ignore + */ +ccui.LINEAR_GRAVITY_NONE = 0; +ccui.LINEAR_GRAVITY_LEFT = 1; +ccui.LINEAR_GRAVITY_TOP = 2; +ccui.LINEAR_GRAVITY_RIGHT = 3; +ccui.LINEAR_GRAVITY_BOTTOM = 4; +ccui.LINEAR_GRAVITY_CENTER_VERTICAL = 5; +ccui.LINEAR_GRAVITY_CENTER_HORIZONTAL = 6; + +//RelativeAlign +ccui.RELATIVE_ALIGN_NONE = 0; +ccui.RELATIVE_ALIGN_PARENT_TOP_LEFT = 1; +ccui.RELATIVE_ALIGN_PARENT_TOP_CENTER_HORIZONTAL = 2; +ccui.RELATIVE_ALIGN_PARENT_TOP_RIGHT = 3; +ccui.RELATIVE_ALIGN_PARENT_LEFT_CENTER_VERTICAL = 4; +ccui.RELATIVE_ALIGN_PARENT_CENTER = 5; +ccui.RELATIVE_ALIGN_PARENT_RIGHT_CENTER_VERTICAL = 6; +ccui.RELATIVE_ALIGN_PARENT_LEFT_BOTTOM = 7; +ccui.RELATIVE_ALIGN_PARENT_BOTTOM_CENTER_HORIZONTAL = 8; +ccui.RELATIVE_ALIGN_PARENT_RIGHT_BOTTOM = 9; + +ccui.RELATIVE_ALIGN_LOCATION_ABOVE_LEFT = 10; +ccui.RELATIVE_ALIGN_LOCATION_ABOVE_CENTER = 11; +ccui.RELATIVE_ALIGN_LOCATION_ABOVE_RIGHT = 12; + +ccui.RELATIVE_ALIGN_LOCATION_LEFT_TOP = 13; +ccui.RELATIVE_ALIGN_LOCATION_LEFT_CENTER = 14; +ccui.RELATIVE_ALIGN_LOCATION_LEFT_BOTTOM = 15; + +ccui.RELATIVE_ALIGN_LOCATION_RIGHT_TOP = 16; +ccui.RELATIVE_ALIGN_LOCATION_RIGHT_CENTER = 17; +ccui.RELATIVE_ALIGN_LOCATION_RIGHT_BOTTOM = 18; + +ccui.RELATIVE_ALIGN_LOCATION_BELOW_TOP = 19; +ccui.RELATIVE_ALIGN_LOCATION_BELOW_CENTER = 20; +ccui.RELATIVE_ALIGN_LOCATION_BELOW_BOTTOM = 21; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/ccui/layouts/UILayoutWebGLRenderCmd.js b/frameworks/cocos2d-html5/extensions/ccui/layouts/UILayoutWebGLRenderCmd.js new file mode 100644 index 0000000..e83dc0c --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/layouts/UILayoutWebGLRenderCmd.js @@ -0,0 +1,204 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + if (!ccui.ProtectedNode.WebGLRenderCmd) + return; + ccui.Layout.WebGLRenderCmd = function (renderable) { + this._pNodeCmdCtor(renderable); + this._needDraw = false; + + this._currentStencilEnabled = 0; + this._scissorOldState = false; + this._clippingOldRect = null; + + this._mask_layer_le = 0; + + this._beforeVisitCmdStencil = null; + this._afterDrawStencilCmd = null; + this._afterVisitCmdStencil = null; + this._beforeVisitCmdScissor = null; + this._afterVisitCmdScissor = null; + }; + + var proto = ccui.Layout.WebGLRenderCmd.prototype = Object.create(ccui.ProtectedNode.WebGLRenderCmd.prototype); + proto.constructor = ccui.Layout.WebGLRenderCmd; + proto._layoutCmdCtor = ccui.Layout.CanvasRenderCmd; + + proto._syncStatus = function (parentCmd) { + this._originSyncStatus(parentCmd); + + if (parentCmd && (parentCmd._dirtyFlag & cc.Node._dirtyFlags.transformDirty)) + this._node._clippingRectDirty = true; + }; + + proto._onBeforeVisitStencil = function (ctx) { + var gl = ctx || cc._renderContext; + + ccui.Layout.WebGLRenderCmd._layer++; + + var mask_layer = 0x1 << ccui.Layout.WebGLRenderCmd._layer; + var mask_layer_l = mask_layer - 1; + this._mask_layer_le = mask_layer | mask_layer_l; + + // manually save the stencil state + this._currentStencilEnabled = gl.isEnabled(gl.STENCIL_TEST); + + gl.clear(gl.DEPTH_BUFFER_BIT); + + gl.enable(gl.STENCIL_TEST); + + gl.depthMask(false); + + gl.stencilFunc(gl.NEVER, mask_layer, mask_layer); + gl.stencilOp(gl.REPLACE, gl.KEEP, gl.KEEP); + + gl.stencilMask(mask_layer); + gl.clear(gl.STENCIL_BUFFER_BIT); + }; + + proto._onAfterDrawStencil = function (ctx) { + var gl = ctx || cc._renderContext; + gl.depthMask(true); + gl.stencilFunc(gl.EQUAL, this._mask_layer_le, this._mask_layer_le); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); + }; + + proto._onAfterVisitStencil = function (ctx) { + var gl = ctx || cc._renderContext; + + ccui.Layout.WebGLRenderCmd._layer--; + + if (this._currentStencilEnabled) { + var mask_layer = 0x1 << ccui.Layout.WebGLRenderCmd._layer; + var mask_layer_l = mask_layer - 1; + var mask_layer_le = mask_layer | mask_layer_l; + + gl.stencilMask(mask_layer); + gl.stencilFunc(gl.EQUAL, mask_layer_le, mask_layer_le); + } + else { + gl.disable(gl.STENCIL_TEST); + } + }; + + proto._onBeforeVisitScissor = function (ctx) { + this._node._clippingRectDirty = true; + var clippingRect = this._node._getClippingRect(); + var gl = ctx || cc._renderContext; + + this._scissorOldState = gl.isEnabled(gl.SCISSOR_TEST); + + if (!this._scissorOldState) { + gl.enable(gl.SCISSOR_TEST); + cc.view.setScissorInPoints(clippingRect.x, clippingRect.y, clippingRect.width, clippingRect.height); + } + else { + this._clippingOldRect = cc.view.getScissorRect(); + if (!cc.rectEqualToRect(this._clippingOldRect, clippingRect)) + cc.view.setScissorInPoints(clippingRect.x, clippingRect.y, clippingRect.width, clippingRect.height); + } + }; + + proto._onAfterVisitScissor = function (ctx) { + var gl = ctx || cc._renderContext; + if (this._scissorOldState) { + if (!cc.rectEqualToRect(this._clippingOldRect, this._node._clippingRect)) { + cc.view.setScissorInPoints(this._clippingOldRect.x, + this._clippingOldRect.y, + this._clippingOldRect.width, + this._clippingOldRect.height); + } + } + else { + gl.disable(gl.SCISSOR_TEST); + } + }; + + proto.rebindStencilRendering = function (stencil) { + }; + + proto.transform = function (parentCmd, recursive) { + var node = this._node; + this.pNodeTransform(parentCmd, recursive); + if (node._clippingStencil) + node._clippingStencil._renderCmd.transform(this, recursive); + }; + + proto.stencilClippingVisit = function (parentCmd) { + var node = this._node; + if (!node._clippingStencil || !node._clippingStencil.isVisible()) + return; + + // all the _stencilBits are in use? + if (ccui.Layout.WebGLRenderCmd._layer + 1 === cc.stencilBits) { + // warn once + ccui.Layout.WebGLRenderCmd._visit_once = true; + if (ccui.Layout.WebGLRenderCmd._visit_once) { + cc.log("Nesting more than " + cc.stencilBits + "stencils is not supported. Everything will be drawn without stencil for this node and its childs."); + ccui.Layout.WebGLRenderCmd._visit_once = false; + } + // draw everything, as if there where no stencil + return; + } + + if (!this._beforeVisitCmdStencil) { + this._beforeVisitCmdStencil = new cc.CustomRenderCmd(this, this._onBeforeVisitStencil); + this._afterDrawStencilCmd = new cc.CustomRenderCmd(this, this._onAfterDrawStencil); + this._afterVisitCmdStencil = new cc.CustomRenderCmd(this, this._onAfterVisitStencil); + } + + cc.renderer.pushRenderCommand(this._beforeVisitCmdStencil); + + //optimize performance for javascript + var currentStack = cc.current_stack; + currentStack.stack.push(currentStack.top); + currentStack.top = this._stackMatrix; + + node._clippingStencil.visit(this); + + cc.renderer.pushRenderCommand(this._afterDrawStencilCmd); + }; + + proto.postStencilVisit = function () { + renderer.pushRenderCommand(cmd._afterVisitCmdStencil); + cc.current_stack.top = cc.current_stack.stack.pop(); + }; + + proto.scissorClippingVisit = function (parentCmd) { + if (!this._beforeVisitCmdScissor) { + this._beforeVisitCmdScissor = new cc.CustomRenderCmd(this, this._onBeforeVisitScissor); + this._afterVisitCmdScissor = new cc.CustomRenderCmd(this, this._onAfterVisitScissor); + } + cc.renderer.pushRenderCommand(this._beforeVisitCmdScissor); + }; + + proto.postScissorVisit = function () { + cc.renderer.pushRenderCommand(this._afterVisitCmdScissor); + }; + + ccui.Layout.WebGLRenderCmd._layer = -1; + ccui.Layout.WebGLRenderCmd._visit_once = null; +})(); diff --git a/frameworks/cocos2d-html5/extensions/ccui/layouts/UIRelativeBox.js b/frameworks/cocos2d-html5/extensions/ccui/layouts/UIRelativeBox.js new file mode 100644 index 0000000..064be6e --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/layouts/UIRelativeBox.js @@ -0,0 +1,55 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The Relative box for Cocos UI layout. Its layout type is ccui.Layout.RELATIVE. + * @class + * @extends ccui.Layout + */ +ccui.RelativeBox = ccui.Layout.extend(/** @lends ccui.RelativeBox# */{ + /** + * The constructor of ccui.RelativeBox + * @function + * @param {cc.Size} [size] + */ + ctor: function(size){ + ccui.Layout.prototype.ctor.call(this); + this.setLayoutType(ccui.Layout.RELATIVE); + + if(size) { + this.setContentSize(size); + } + } +}); + +/** + * Creates a relative box + * @deprecated since v3.0, please use new ccui.RelativeBox(size) instead. + * @param {cc.Size} size + * @returns {ccui.RelativeBox} + */ +ccui.RelativeBox.create = function(size){ + return new ccui.RelativeBox(size); +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/ccui/layouts/UIVBox.js b/frameworks/cocos2d-html5/extensions/ccui/layouts/UIVBox.js new file mode 100644 index 0000000..d6888be --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/layouts/UIVBox.js @@ -0,0 +1,67 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The vertical box of Cocos UI. Its layout type is ccui.Layout.LINEAR_VERTICAL. + * @class + * @extends ccui.Layout + */ +ccui.VBox = ccui.Layout.extend(/** @lends ccui.VBox# */{ + /** + * The constructor of ccui.VBox + * @function + * @param {cc.Size} size + */ + ctor: function(size){ + ccui.Layout.prototype.ctor.call(this); + this.setLayoutType(ccui.Layout.LINEAR_VERTICAL); + + if (size) { + this.setContentSize(size); + } + }, + + /** + * Initializes a VBox with size. + * @param {cc.Size} size + * @returns {boolean} + */ + initWithSize: function(size){ + if(this.init()){ + + return true; + } + return false; + } +}); + +/** + * Creates a VBox + * @param {cc.Size} size + * @returns {ccui.VBox} + */ +ccui.VBox.create = function(size){ + return new ccui.VBox(size); +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/ccui/system/CocosGUI.js b/frameworks/cocos2d-html5/extensions/ccui/system/CocosGUI.js new file mode 100644 index 0000000..30622d0 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/system/CocosGUI.js @@ -0,0 +1,62 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The namespace of Cocos UI + * @namespace + * @name ccui + */ +var ccui = ccui || {}; + +//These classes defines are use for jsDoc +/** + * The same as cc.Class + * @class + */ +ccui.Class = ccui.Class || cc.Class; +ccui.Class.extend = ccui.Class.extend || cc.Class.extend; + +/** + * that same as cc.Node + * @class + * @extends ccui.Class + */ +ccui.Node = ccui.Node || cc.Node; +ccui.Node.extend = ccui.Node.extend || cc.Node.extend; + + +/** + * that same as cc.Node + * @class + * @extends ccui.Node + */ +ccui.ProtectedNode = ccui.ProtectedNode || cc.ProtectedNode; +ccui.ProtectedNode.extend = ccui.ProtectedNode.extend || cc.ProtectedNode.extend; + +/** + * Cocos UI version + * @type {String} + */ +ccui.cocosGUIVersion = "CocosGUI v1.0.0.0"; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/ccui/system/UIHelper.js b/frameworks/cocos2d-html5/extensions/ccui/system/UIHelper.js new file mode 100644 index 0000000..327bf8d --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/system/UIHelper.js @@ -0,0 +1,179 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +//todo maybe need change here + + +/** + * ccui.helper is the singleton object which is the Helper object contains some functions for seek widget + * @class + * @name ccui.helper + */ +ccui.helper = { + /** + * Finds a widget whose tag equals to param tag from root widget. + * @param {ccui.Widget} root + * @param {number} tag + * @returns {ccui.Widget} + */ + seekWidgetByTag: function (root, tag) { + if (!root) + return null; + if (root.getTag() === tag) + return root; + + var arrayRootChildren = root.getChildren(); + var length = arrayRootChildren.length; + for (var i = 0; i < length; i++) { + var child = arrayRootChildren[i]; + var res = ccui.helper.seekWidgetByTag(child, tag); + if (res !== null) + return res; + } + return null; + }, + + /** + * Finds a widget whose name equals to param name from root widget. + * @param {ccui.Widget} root + * @param {String} name + * @returns {ccui.Widget} + */ + seekWidgetByName: function (root, name) { + if (!root) + return null; + if (root.getName() === name) + return root; + var arrayRootChildren = root.getChildren(); + var length = arrayRootChildren.length; + for (var i = 0; i < length; i++) { + var child = arrayRootChildren[i]; + var res = ccui.helper.seekWidgetByName(child, name); + if (res !== null) + return res; + } + return null; + }, + + /** + * Finds a widget whose name equals to param name from root widget. + * RelativeLayout will call this method to find the widget witch is needed. + * @param {ccui.Widget} root + * @param {String} name + * @returns {ccui.Widget} + */ + seekWidgetByRelativeName: function (root, name) { + if (!root) + return null; + var arrayRootChildren = root.getChildren(); + var length = arrayRootChildren.length; + for (var i = 0; i < length; i++) { + var child = arrayRootChildren[i]; + var layoutParameter = child.getLayoutParameter(ccui.LayoutParameter.RELATIVE); + if (layoutParameter && layoutParameter.getRelativeName() === name) + return child; + } + return null; + }, + + /** + * Finds a widget whose action tag equals to param name from root widget. + * @param {ccui.Widget} root + * @param {Number} tag + * @returns {ccui.Widget} + */ + seekActionWidgetByActionTag: function (root, tag) { + if (!root) + return null; + if (root.getActionTag() === tag) + return root; + var arrayRootChildren = root.getChildren(); + for (var i = 0; i < arrayRootChildren.length; i++) { + var child = arrayRootChildren[i]; + var res = ccui.helper.seekActionWidgetByActionTag(child, tag); + if (res !== null) + return res; + } + return null; + }, + + _activeLayout: true, + /** + * Refresh object and it's children layout state + * @param {cc.Node} rootNode + */ + doLayout: function (rootNode) { + if (!this._activeLayout) + return; + var children = rootNode.getChildren(), node; + for(var i = 0, len = children.length;i < len; i++) { + node = children[i]; + var com = node.getComponent(ccui.LayoutComponent.NAME); + var parent = node.getParent(); + if (null != com && null !== parent && com.refreshLayout) + com.refreshLayout(); + } + }, + + changeLayoutSystemActiveState: function (active) { + this._activeLayout = active; + }, + + /** + * restrict capInsetSize, when the capInsets' width is larger than the textureSize, it will restrict to 0,
+ * the height goes the same way as width. + * @param {cc.Rect} capInsets + * @param {cc.Size} textureSize + */ + restrictCapInsetRect: function (capInsets, textureSize) { + var x = capInsets.x, y = capInsets.y; + var width = capInsets.width, height = capInsets.height; + + if (textureSize.width < width) { + x = 0.0; + width = 0.0; + } + if (textureSize.height < height) { + y = 0.0; + height = 0.0; + } + return cc.rect(x, y, width, height); + }, + + _createSpriteFromBase64: function (base64String, key) { + var texture2D = cc.textureCache.getTextureForKey(key); + + if (!texture2D) { + var image = new Image(); + image.src = base64String; + cc.textureCache.cacheImage(key, image); + texture2D = cc.textureCache.getTextureForKey(key); + } + + var sprite = new cc.Sprite(texture2D); + + return sprite; + } +}; diff --git a/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UIButton.js b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UIButton.js new file mode 100644 index 0000000..4741e2e --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UIButton.js @@ -0,0 +1,857 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2015-2016 zilongshanren + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The button controls of Cocos UI. + * @class + * @extends ccui.Widget + * + * @property {String} titleText - The content string of the button title + * @property {String} titleFont - The content string font of the button title + * @property {Number} titleFontSize - The content string font size of the button title + * @property {String} titleFontName - The content string font name of the button title + * @property {cc.Color} titleColor - The content string font color of the button title + * @property {Boolean} pressedActionEnabled - Indicate whether button has zoom effect when clicked + */ +ccui.Button = ccui.Widget.extend(/** @lends ccui.Button# */{ + _buttonScale9Renderer: null, + _buttonNormalSpriteFrame: null, + _buttonClickedSpriteFrame: null, + _buttonDisableSpriteFrame: null, + _titleRenderer: null, + + _normalFileName: "", + _clickedFileName: "", + _disabledFileName: "", + + _prevIgnoreSize: true, + _scale9Enabled: false, + + _capInsetsNormal: null, + + _normalTexType: ccui.Widget.LOCAL_TEXTURE, + _pressedTexType: ccui.Widget.LOCAL_TEXTURE, + _disabledTexType: ccui.Widget.LOCAL_TEXTURE, + + _normalTextureSize: null, + + pressedActionEnabled: false, + _titleColor: null, + + _zoomScale: 0.1, + + _normalTextureLoaded: false, + _pressedTextureLoaded: false, + _disabledTextureLoaded: false, + + _className: "Button", + _normalTextureAdaptDirty: true, + + _fontName: "Thonburi", + _fontSize: 12, + _type: 0, + + /** + * Allocates and initializes a UIButton. + * Constructor of ccui.Button. override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {String} normalImage + * @param {String} [selectedImage=""] + * @param {String} [disableImage=""] + * @param {Number} [texType=ccui.Widget.LOCAL_TEXTURE] + * @example + * // example + * var uiButton = new ccui.Button(); + */ + ctor: function (normalImage, selectedImage, disableImage, texType) { + this._capInsetsNormal = cc.rect(0, 0, 0, 0); + this._normalTextureSize = cc.size(0, 0); + ccui.Widget.prototype.ctor.call(this); + this.setTouchEnabled(true); + + this._normalLoader = new cc.Sprite.LoadManager(); + this._clickedLoader = new cc.Sprite.LoadManager(); + this._disabledLoader = new cc.Sprite.LoadManager(); + + if (normalImage) { + this.loadTextures(normalImage, selectedImage,disableImage, texType); + } + }, + + _createTitleRendererIfNeeded: function ( ) { + if(!this._titleRenderer) { + this._titleRenderer = new cc.LabelTTF(""); + this._titleRenderer.setAnchorPoint(0.5, 0.5); + this._titleColor = cc.color.WHITE; + this._titleRenderer.setVerticalAlignment(cc.VERTICAL_TEXT_ALIGNMENT_CENTER); + this.addProtectedChild(this._titleRenderer, ccui.Button.TITLE_RENDERER_ZORDER, -1); + } + }, + + _initRenderer: function () { + this._buttonScale9Renderer = new ccui.Scale9Sprite(); + + this._buttonScale9Renderer.setRenderingType(ccui.Scale9Sprite.RenderingType.SIMPLE); + + this.addProtectedChild(this._buttonScale9Renderer, ccui.Button.DISABLED_RENDERER_ZORDER, -1); + }, + + /** + * Sets if button is using scale9 renderer. + * @param {Boolean} able true that using scale9 renderer, false otherwise. + */ + setScale9Enabled: function (able) { + if (this._scale9Enabled === able) + return; + + this._brightStyle = ccui.Widget.BRIGHT_STYLE_NONE; + this._scale9Enabled = able; + + if (this._scale9Enabled) { + this._buttonScale9Renderer.setRenderingType(ccui.Scale9Sprite.RenderingType.SLICED); + } else { + this._buttonScale9Renderer.setRenderingType(ccui.Scale9Sprite.RenderingType.SIMPLE); + } + + if (this._scale9Enabled) { + var ignoreBefore = this._ignoreSize; + this.ignoreContentAdaptWithSize(false); + this._prevIgnoreSize = ignoreBefore; + } else { + this.ignoreContentAdaptWithSize(this._prevIgnoreSize); + } + this.setCapInsets(this._capInsetsNormal); + + this.setBright(this._bright); + + this._normalTextureAdaptDirty = true; + }, + + /** + * Returns button is using scale9 renderer or not. + * @returns {Boolean} + */ + isScale9Enabled: function () { + return this._scale9Enabled; + }, + + /** + * Sets whether ignore the widget size + * @param {Boolean} ignore true that widget will ignore it's size, use texture size, false otherwise. Default value is true. + * @override + */ + ignoreContentAdaptWithSize: function (ignore) { + if(this._unifySize){ + this._updateContentSize(); + return; + } + if (!this._scale9Enabled || (this._scale9Enabled && !ignore)) { + ccui.Widget.prototype.ignoreContentAdaptWithSize.call(this, ignore); + this._prevIgnoreSize = ignore; + } + }, + + /** + * Returns the renderer size. + * @returns {cc.Size} + */ + getVirtualRendererSize: function(){ + if (this._unifySize) + return this._getNormalSize(); + + if (!this._normalTextureLoaded ) { + if(this._titleRenderer && this._titleRenderer.getString().length > 0) { + return this._titleRenderer.getContentSize(); + } + } + return cc.size(this._normalTextureSize); + }, + + /** + * Load textures for button. + * @param {String} normal normal state of texture's filename. + * @param {String} selected selected state of texture's filename. + * @param {String} disabled disabled state of texture's filename. + * @param {ccui.Widget.LOCAL_TEXTURE|ccui.Widget.PLIST_TEXTURE} texType + */ + loadTextures: function (normal, selected, disabled, texType) { + this.loadTextureNormal(normal, texType); + this.loadTexturePressed(selected, texType); + this.loadTextureDisabled(disabled, texType); + }, + + _createSpriteFrameWithFile: function (file) { + var texture = cc.textureCache.getTextureForKey(file); + if (!texture) { + texture = cc.textureCache.addImage(file); + } + if(!texture._textureLoaded) { + return texture; + } + + var textureSize = texture.getContentSize(); + var rect = cc.rect(0, 0, textureSize.width, textureSize.height); + return new cc.SpriteFrame(texture, rect); + }, + + _createSpriteFrameWithName: function (name) { + var frame = cc.spriteFrameCache.getSpriteFrame(name); + if (frame == null) { + cc.log("ccui.Scale9Sprite.initWithSpriteFrameName(): can't find the sprite frame by spriteFrameName"); + return null; + } + + return frame; + }, + + /** + * Load normal state texture for button. + * @param {String} normal normal state of texture's filename. + * @param {ccui.Widget.LOCAL_TEXTURE|ccui.Widget.PLIST_TEXTURE} texType + */ + loadTextureNormal: function (normal, texType) { + if (!normal) return; + + texType = texType || ccui.Widget.LOCAL_TEXTURE; + this._normalFileName = normal; + this._normalTexType = texType; + + var normalSpriteFrame; + switch (this._normalTexType){ + case ccui.Widget.LOCAL_TEXTURE: + normalSpriteFrame = this._createSpriteFrameWithFile(normal); + break; + case ccui.Widget.PLIST_TEXTURE: + if (normal[0] === "#") { + normal = normal.substr(1, normal.length - 1); + } + normalSpriteFrame = this._createSpriteFrameWithName(normal); + break; + default: + break; + } + + if(!normalSpriteFrame) { + return; + } + + if(!normalSpriteFrame._textureLoaded) { + this._normalLoader.clear(); + this._normalLoader.once(normalSpriteFrame, function () { + this.loadTextureNormal(this._normalFileName, this._normalTexType); + }, this); + return; + } + + this._normalTextureLoaded = normalSpriteFrame._textureLoaded; + this._buttonNormalSpriteFrame = normalSpriteFrame; + this._buttonScale9Renderer.setSpriteFrame(normalSpriteFrame); + if (this._scale9Enabled){ + this._buttonScale9Renderer.setCapInsets(this._capInsetsNormal); + } + + // FIXME: https://github.com/cocos2d/cocos2d-x/issues/12249 + if(!this._ignoreSize && cc.sizeEqualToSize(this._customSize, cc.size(0, 0))) { + this._customSize = this._buttonScale9Renderer.getContentSize(); + } + + this._normalTextureSize = this._buttonScale9Renderer.getContentSize(); + this._updateChildrenDisplayedRGBA(); + if (this._unifySize){ + if (this._scale9Enabled){ + this._buttonScale9Renderer.setCapInsets(this._capInsetsNormal); + this._updateContentSizeWithTextureSize(this._getNormalSize()); + } + }else { + this._updateContentSizeWithTextureSize(this._normalTextureSize); + } + + this._normalTextureAdaptDirty = true; + this._findLayout(); + }, + + /** + * Load selected state texture for button. + * @param {String} selected selected state of texture's filename. + * @param {ccui.Widget.LOCAL_TEXTURE|ccui.Widget.PLIST_TEXTURE} texType + */ + loadTexturePressed: function (selected, texType) { + if (!selected) + return; + texType = texType || ccui.Widget.LOCAL_TEXTURE; + this._clickedFileName = selected; + this._pressedTexType = texType; + + var clickedSpriteFrame; + switch (this._pressedTexType) { + case ccui.Widget.LOCAL_TEXTURE: + clickedSpriteFrame = this._createSpriteFrameWithFile(selected); + break; + case ccui.Widget.PLIST_TEXTURE: + if (selected[0] === "#") { + selected = selected.substr(1, selected.length - 1); + } + clickedSpriteFrame = this._createSpriteFrameWithName(selected); + break; + default: + break; + } + + if(!clickedSpriteFrame) return; + + if(!clickedSpriteFrame._textureLoaded) { + this._clickedLoader.clear(); + this._clickedLoader.once(clickedSpriteFrame, function () { + this.loadTexturePressed(this._clickedFileName, this._pressedTexType); + }, this); + return; + } + + this._buttonClickedSpriteFrame = clickedSpriteFrame; + this._updateChildrenDisplayedRGBA(); + + this._pressedTextureLoaded = true; + }, + + /** + * Load dark state texture for button. + * @param {String} disabled disabled state of texture's filename. + * @param {ccui.Widget.LOCAL_TEXTURE|ccui.Widget.PLIST_TEXTURE} texType + */ + loadTextureDisabled: function (disabled, texType) { + if (!disabled) + return; + + texType = texType || ccui.Widget.LOCAL_TEXTURE; + this._disabledFileName = disabled; + this._disabledTexType = texType; + + var disabledSpriteframe; + switch (this._disabledTexType) { + case ccui.Widget.LOCAL_TEXTURE: + disabledSpriteframe = this._createSpriteFrameWithFile(disabled); + break; + case ccui.Widget.PLIST_TEXTURE: + if (disabled[0] === "#") { + disabled = disabled.substr(1, disabled.length - 1); + } + disabledSpriteframe = this._createSpriteFrameWithName(disabled); + break; + default: + break; + } + + if(!disabledSpriteframe) return; + + if(!disabledSpriteframe._textureLoaded) { + this._disabledLoader.clear(); + this._disabledLoader.once(disabledSpriteframe, function () { + this.loadTextureDisabled(this._disabledFileName, this._disabledTexType); + }, this); + return; + } + + this._buttonDisableSpriteFrame = disabledSpriteframe; + this._updateChildrenDisplayedRGBA(); + + this._disabledTextureLoaded = true; + this._findLayout(); + }, + + /** + * Sets capinsets for button, if button is using scale9 renderer. + * @param {cc.Rect} capInsets + */ + setCapInsets: function (capInsets) { + this.setCapInsetsNormalRenderer(capInsets); + }, + + /** + * Sets capinsets for button, if button is using scale9 renderer. + * @param {cc.Rect} capInsets + */ + setCapInsetsNormalRenderer: function (capInsets) { + if(!capInsets || !this._scale9Enabled) + return; + + var x = capInsets.x, y = capInsets.y; + var width = capInsets.width, height = capInsets.height; + if (this._normalTextureSize.width < width){ + x = 0; + width = 0; + } + if (this._normalTextureSize.height < height){ + y = 0; + height = 0; + } + + var locInsets = this._capInsetsNormal; + locInsets.x = x; + locInsets.y = y; + locInsets.width = width; + locInsets.height = height; + + this._capInsetsNormal = locInsets; + this._buttonScale9Renderer.setCapInsets(locInsets); + }, + + /** + * Returns normal renderer cap insets. + * @returns {cc.Rect} + */ + getCapInsetsNormalRenderer:function(){ + return cc.rect(this._capInsetsNormal); + }, + + /** + * Sets capinsets for button, if button is using scale9 renderer. + * @param {cc.Rect} capInsets + */ + setCapInsetsPressedRenderer: function (capInsets) { + this.setCapInsetsNormalRenderer(capInsets); + }, + + /** + * Returns pressed renderer cap insets. + * @returns {cc.Rect} + */ + getCapInsetsPressedRenderer: function () { + return cc.rect(this._capInsetsNormal); + }, + + /** + * Sets capinsets for button, if button is using scale9 renderer. + * @param {cc.Rect} capInsets + */ + setCapInsetsDisabledRenderer: function (capInsets) { + this.setCapInsetsNormalRenderer(capInsets); + }, + + /** + * Returns disable renderer cap insets. + * @returns {cc.Rect} + */ + getCapInsetsDisabledRenderer: function () { + return cc.rect(this._capInsetsNormal); + }, + + _onPressStateChangedToNormal: function () { + this._buttonScale9Renderer.setSpriteFrame(this._buttonNormalSpriteFrame); + + this._buttonScale9Renderer.setState( ccui.Scale9Sprite.state.NORMAL); + + if (this._pressedTextureLoaded) { + if (this.pressedActionEnabled){ + this._buttonScale9Renderer.stopAllActions(); + this._buttonScale9Renderer.setScale(1.0); + + if(this._titleRenderer) { + this._titleRenderer.stopAllActions(); + + if (this._unifySize){ + var zoomTitleAction = cc.scaleTo(ccui.Button.ZOOM_ACTION_TIME_STEP, 1, 1); + this._titleRenderer.runAction(zoomTitleAction); + }else{ + this._titleRenderer.setScaleX(1); + this._titleRenderer.setScaleY(1); + } + } + + } + } else { + this._buttonScale9Renderer.stopAllActions(); + this._buttonScale9Renderer.setScale(1.0); + + if (this._scale9Enabled) { + this._buttonScale9Renderer.setColor(cc.color.WHITE); + } + + if(this._titleRenderer) { + this._titleRenderer.stopAllActions(); + + this._titleRenderer.setScaleX(1); + this._titleRenderer.setScaleY(1); + } + } + }, + + _onPressStateChangedToPressed: function () { + this._buttonScale9Renderer.setState(ccui.Scale9Sprite.state.NORMAL); + + if (this._pressedTextureLoaded) { + this._buttonScale9Renderer.setSpriteFrame(this._buttonClickedSpriteFrame); + + if (this.pressedActionEnabled) { + this._buttonScale9Renderer.stopAllActions(); + + var zoomAction = cc.scaleTo(ccui.Button.ZOOM_ACTION_TIME_STEP, + 1.0 + this._zoomScale, + 1.0 + this._zoomScale); + this._buttonScale9Renderer.runAction(zoomAction); + + if(this._titleRenderer) { + this._titleRenderer.stopAllActions(); + this._titleRenderer.runAction(cc.scaleTo(ccui.Button.ZOOM_ACTION_TIME_STEP, + 1 + this._zoomScale, + 1 + this._zoomScale)); + } + } + } else { + this._buttonScale9Renderer.setSpriteFrame(this._buttonClickedSpriteFrame); + + this._buttonScale9Renderer.stopAllActions(); + this._buttonScale9Renderer.setScale(1.0 + this._zoomScale, 1.0 + this._zoomScale); + + if (this._titleRenderer) { + this._titleRenderer.stopAllActions(); + this._titleRenderer.setScaleX(1 + this._zoomScale); + this._titleRenderer.setScaleY(1 + this._zoomScale); + } + } + }, + + _onPressStateChangedToDisabled: function () { + //if disable resource is null + if (!this._disabledTextureLoaded){ + if (this._normalTextureLoaded) { + this._buttonScale9Renderer.setState(ccui.Scale9Sprite.state.GRAY); + } + }else{ + this._buttonScale9Renderer.setSpriteFrame(this._buttonDisableSpriteFrame); + } + + this._buttonScale9Renderer.setScale(1.0); + }, + + _updateContentSize: function(){ + if (this._unifySize){ + if (this._scale9Enabled) + ccui.ProtectedNode.setContentSize(this._customSize); + else{ + var s = this._getNormalSize(); + ccui.ProtectedNode.setContentSize(s); + } + this._onSizeChanged(); + return; + } + + if (this._ignoreSize) + this.setContentSize(this.getVirtualRendererSize()); + }, + + _onSizeChanged: function () { + ccui.Widget.prototype._onSizeChanged.call(this); + if(this._titleRenderer) { + this._updateTitleLocation(); + } + this._normalTextureAdaptDirty = true; + }, + + /** + * Gets the Virtual Renderer of widget. + * @returns {cc.Node} + */ + getVirtualRenderer: function () { + return this._buttonScale9Renderer; + }, + + _normalTextureScaleChangedWithSize: function () { + this._buttonScale9Renderer.setContentSize(this._contentSize); + this._buttonScale9Renderer.setPosition(this._contentSize.width / 2, this._contentSize.height / 2); + }, + + _adaptRenderers: function(){ + if (this._normalTextureAdaptDirty) { + this._normalTextureScaleChangedWithSize(); + this._normalTextureAdaptDirty = false; + } + }, + + _updateTitleLocation: function(){ + this._titleRenderer.setPosition(this._contentSize.width * 0.5, this._contentSize.height * 0.5); + }, + + /** + * Changes if button can be clicked zoom effect. + * @param {Boolean} enabled + */ + setPressedActionEnabled: function (enabled) { + this.pressedActionEnabled = enabled; + }, + + /** + * Sets title text to ccui.Button + * @param {String} text + */ + setTitleText: function (text) { + if(text === this.getTitleText()) return; + + this._createTitleRendererIfNeeded(); + + this._titleRenderer.setString(text); + if (this._ignoreSize){ + var s = this.getVirtualRendererSize(); + this.setContentSize(s); + }else{ + this._titleRenderer._renderCmd._updateTTF(); + } + }, + + /** + * Returns title text of ccui.Button + * @returns {String} text + */ + getTitleText: function () { + if(this._titleRenderer) { + return this._titleRenderer.getString(); + } + return ""; + }, + + /** + * Sets title color to ccui.Button. + * @param {cc.Color} color + */ + setTitleColor: function (color) { + this._createTitleRendererIfNeeded(); + this._titleRenderer.setFontFillColor(color); + }, + + /** + * Returns title color of ccui.Button + * @returns {cc.Color} + */ + getTitleColor: function () { + if (this._titleRenderer) { + return this._titleRenderer._getFillStyle(); + } + return cc.color.WHITE; + }, + + /** + * Sets title fontSize to ccui.Button + * @param {cc.Size} size + */ + setTitleFontSize: function (size) { + this._createTitleRendererIfNeeded(); + + this._titleRenderer.setFontSize(size); + this._fontSize = size; + }, + + /** + * Returns title fontSize of ccui.Button. + * @returns {Number} + */ + getTitleFontSize: function () { + if (this._titleRenderer) { + return this._titleRenderer.getFontSize(); + } + return this._fontSize; + }, + + /** + * When user pressed the button, the button will zoom to a scale. + * The final scale of the button equals (button original scale + _zoomScale) + * @since v3.2 + * @param scale + */ + setZoomScale: function(scale){ + this._zoomScale = scale; + }, + + /** + * Returns a zoom scale + * @since v3.2 + * @returns {number} + */ + getZoomScale: function(){ + return this._zoomScale; + }, + + /** + * Returns the normalize of texture size + * @since v3.3 + * @returns {cc.Size} + */ + getNormalTextureSize: function(){ + return this._normalTextureSize; + }, + + /** + * Sets title fontName to ccui.Button. + * @param {String} fontName + */ + setTitleFontName: function (fontName) { + this._createTitleRendererIfNeeded(); + + this._titleRenderer.setFontName(fontName); + this._fontName = fontName; + }, + + /** + * Get the title renderer. + * title ttf object. + * @returns {cc.LabelTTF} + */ + getTitleRenderer: function(){ + return this._titleRenderer; + }, + + /** + * Gets title fontName of ccui.Button. + * @returns {String} + */ + getTitleFontName: function () { + if(this._titleRenderer) { + return this._titleRenderer.getFontName(); + } + return this._fontName; + }, + + _setTitleFont: function (font) { + this._titleRenderer.font = font; + }, + _getTitleFont: function () { + return this._titleRenderer.font; + }, + + /** + * Returns the "class name" of widget. + * @override + * @returns {string} + */ + getDescription: function () { + return "Button"; + }, + + _createCloneInstance: function () { + return new ccui.Button(); + }, + + _copySpecialProperties: function (uiButton) { + this._prevIgnoreSize = uiButton._prevIgnoreSize; + this._capInsetsNormal = uiButton._capInsetsNormal; + this.setScale9Enabled(uiButton._scale9Enabled); + + this.loadTextureNormal(uiButton._normalFileName, uiButton._normalTexType); + this.loadTexturePressed(uiButton._clickedFileName, uiButton._pressedTexType); + this.loadTextureDisabled(uiButton._disabledFileName, uiButton._disabledTexType); + + if(uiButton._titleRenderer && uiButton._titleRenderer._string) { + this.setTitleText(uiButton.getTitleText()); + this.setTitleFontName(uiButton.getTitleFontName()); + this.setTitleFontSize(uiButton.getTitleFontSize()); + this.setTitleColor(uiButton.getTitleColor()); + } + this.setPressedActionEnabled(uiButton.pressedActionEnabled); + this.setZoomScale(uiButton._zoomScale); + }, + + _getNormalSize: function(){ + var titleSize; + if (this._titleRenderer !== null) + titleSize = this._titleRenderer.getContentSize(); + + var imageSize = this._buttonScale9Renderer.getContentSize(); + var width = titleSize.width > imageSize.width ? titleSize.width : imageSize.width; + var height = titleSize.height > imageSize.height ? titleSize.height : imageSize.height; + + return cc.size(width,height); + } +}); + +var _p = ccui.Button.prototype; + +// Extended properties +/** @expose */ +_p.titleText; +cc.defineGetterSetter(_p, "titleText", _p.getTitleText, _p.setTitleText); +/** @expose */ +_p.titleFont; +cc.defineGetterSetter(_p, "titleFont", _p._getTitleFont, _p._setTitleFont); +/** @expose */ +_p.titleFontSize; +cc.defineGetterSetter(_p, "titleFontSize", _p.getTitleFontSize, _p.setTitleFontSize); +/** @expose */ +_p.titleFontName; +cc.defineGetterSetter(_p, "titleFontName", _p.getTitleFontName, _p.setTitleFontName); +/** @expose */ +_p.titleColor; +cc.defineGetterSetter(_p, "titleColor", _p.getTitleColor, _p.setTitleColor); + +_p = null; + +/** + * allocates and initializes a UIButton. + * @deprecated since v3.0, please use new ccui.Button() instead. + * @param {string} [normalImage] normal state texture name + * @param {string} [selectedImage] selected state texture name + * @param {string} [disableImage] disabled state texture name + * @param {string} [texType] + * @return {ccui.Button} + */ +ccui.Button.create = function (normalImage, selectedImage, disableImage, texType) { + return new ccui.Button(normalImage, selectedImage, disableImage, texType); +}; + +// Constants +/** + * The normal renderer's zOrder value of ccui.Button. + * @constant + * @type {number} + */ +ccui.Button.NORMAL_RENDERER_ZORDER = -2; +/** + * The pressed renderer's zOrder value ccui.Button. + * @constant + * @type {number} + */ +ccui.Button.PRESSED_RENDERER_ZORDER = -2; +/** + * The disabled renderer's zOrder value of ccui.Button. + * @constant + * @type {number} + */ +ccui.Button.DISABLED_RENDERER_ZORDER = -2; +/** + * The title renderer's zOrder value of ccui.Button. + * @constant + * @type {number} + */ +ccui.Button.TITLE_RENDERER_ZORDER = -1; + +/** + * the zoom action time step of ccui.Button + * @constant + * @type {number} + */ +ccui.Button.ZOOM_ACTION_TIME_STEP = 0.05; + +/** + * @ignore + */ +ccui.Button.SYSTEM = 0; +ccui.Button.TTF = 1; diff --git a/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UICheckBox.js b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UICheckBox.js new file mode 100644 index 0000000..d76b4d5 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UICheckBox.js @@ -0,0 +1,722 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The CheckBox control of Cocos UI. + * @class + * @extends ccui.Widget + * + * @property {Boolean} selected - Indicate whether the check box has been selected + */ +ccui.CheckBox = ccui.Widget.extend(/** @lends ccui.CheckBox# */{ + _backGroundBoxRenderer: null, + _backGroundSelectedBoxRenderer: null, + _frontCrossRenderer: null, + _backGroundBoxDisabledRenderer: null, + _frontCrossDisabledRenderer: null, + + _isSelected: true, + + _checkBoxEventListener: null, + _checkBoxEventSelector:null, + + _backGroundTexType: ccui.Widget.LOCAL_TEXTURE, + _backGroundSelectedTexType: ccui.Widget.LOCAL_TEXTURE, + _frontCrossTexType: ccui.Widget.LOCAL_TEXTURE, + _backGroundDisabledTexType: ccui.Widget.LOCAL_TEXTURE, + _frontCrossDisabledTexType: ccui.Widget.LOCAL_TEXTURE, + + _backGroundFileName: "", + _backGroundSelectedFileName: "", + _frontCrossFileName: "", + _backGroundDisabledFileName: "", + _frontCrossDisabledFileName: "", + _className: "CheckBox", + + _zoomScale: 0.1, + _backgroundTextureScaleX: 0.1, + _backgroundTextureScaleY: 0.1, + + _backGroundBoxRendererAdaptDirty:true, + _backGroundSelectedBoxRendererAdaptDirty:true, + _frontCrossRendererAdaptDirty: true, + _backGroundBoxDisabledRendererAdaptDirty: true, + _frontCrossDisabledRendererAdaptDirty: true, + + /** + * allocates and initializes a UICheckBox. + * Constructor of ccui.CheckBox, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {String} backGround + * @param {String} backGroundSelected + * @param {String} cross + * @param {String} backGroundDisabled + * @param {String} frontCrossDisabled + * @param {Number} [texType=ccui.Widget.LOCAL_TEXTURE] + * @example + * // example + * var uiCheckBox = new ccui.CheckBox(); + */ + ctor: function (backGround, backGroundSelected,cross,backGroundDisabled,frontCrossDisabled,texType) { + ccui.Widget.prototype.ctor.call(this); + this.setTouchEnabled(true); + var strNum = 0; + for(var i=0; i + * Constructor of ccui.LoadingBar, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {string} textureName + * @param {Number} percentage + * @example + * // example + * var uiLoadingBar = new ccui.LoadingBar; + */ + ctor: function (textureName, percentage) { + this._direction = ccui.LoadingBar.TYPE_LEFT; + this._barRendererTextureSize = cc.size(0, 0); + this._capInsets = cc.rect(0, 0, 0, 0); + ccui.Widget.prototype.ctor.call(this); + + if (textureName !== undefined) + this.loadTexture(textureName); + if (percentage !== undefined) + this.setPercent(percentage); + }, + + _initRenderer: function () { + //todo use Scale9Sprite + this._barRenderer = new cc.Sprite(); + this.addProtectedChild(this._barRenderer, ccui.LoadingBar.RENDERER_ZORDER, -1); + this._barRenderer.setAnchorPoint(0.0, 0.5); + }, + + /** + * Changes the progress direction of LoadingBar.
+ * LoadingBarTypeLeft means progress left to right, LoadingBarTypeRight otherwise. + * @param {ccui.LoadingBar.TYPE_LEFT | ccui.LoadingBar.TYPE_RIGHT} dir + */ + setDirection: function (dir) { + if (this._direction === dir) + return; + this._direction = dir; + switch (this._direction) { + case ccui.LoadingBar.TYPE_LEFT: + this._barRenderer.setAnchorPoint(0, 0.5); + this._barRenderer.setPosition(0, this._contentSize.height * 0.5); + if (!this._scale9Enabled) + this._barRenderer.setFlippedX(false); + + break; + case ccui.LoadingBar.TYPE_RIGHT: + this._barRenderer.setAnchorPoint(1, 0.5); + this._barRenderer.setPosition(this._totalLength, this._contentSize.height * 0.5); + if (!this._scale9Enabled) + this._barRenderer.setFlippedX(true); + + break; + } + }, + + /** + * Returns the progress direction of LoadingBar.
+ * LoadingBarTypeLeft means progress left to right, LoadingBarTypeRight otherwise. + * @returns {ccui.LoadingBar.TYPE_LEFT | ccui.LoadingBar.TYPE_RIGHT} + */ + getDirection: function () { + return this._direction; + }, + + /** + * Loads texture for LoadingBar. + * @param {String} texture + * @param {ccui.Widget.LOCAL_TEXTURE|ccui.Widget.PLIST_TEXTURE} texType + */ + loadTexture: function (texture, texType) { + if (!texture) + return; + texType = texType || ccui.Widget.LOCAL_TEXTURE; + this._renderBarTexType = texType; + this._textureFile = texture; + var barRenderer = this._barRenderer; + + var self = this; + if (!barRenderer._textureLoaded) { + barRenderer.addEventListener("load", function () { + self.loadTexture(self._textureFile, self._renderBarTexType); + self._setPercent(self._percent); + }); + } + + switch (this._renderBarTexType) { + case ccui.Widget.LOCAL_TEXTURE: + barRenderer.initWithFile(texture); + break; + case ccui.Widget.PLIST_TEXTURE: + barRenderer.initWithSpriteFrameName(texture); + break; + default: + break; + } + + var bz = barRenderer.getContentSize(); + this._barRendererTextureSize.width = bz.width; + this._barRendererTextureSize.height = bz.height; + + switch (this._direction) { + case ccui.LoadingBar.TYPE_LEFT: + barRenderer.setAnchorPoint(0, 0.5); + if (!this._scale9Enabled) + barRenderer.setFlippedX(false); + break; + case ccui.LoadingBar.TYPE_RIGHT: + barRenderer.setAnchorPoint(1, 0.5); + if (!this._scale9Enabled) + barRenderer.setFlippedX(true); + break; + } + if (this._scale9Enabled) + barRenderer.setCapInsets(this._capInsets); + + this._updateChildrenDisplayedRGBA(); + this._barRendererScaleChangedWithSize(); + this._updateContentSizeWithTextureSize(this._barRendererTextureSize); + this._barRendererAdaptDirty = true; + this._findLayout(); + }, + + /** + * Sets if LoadingBar is using scale9 renderer. + * @param {Boolean} enabled + */ + setScale9Enabled: function (enabled) { + //todo use setScale9Enabled + if (this._scale9Enabled === enabled) + return; + this._scale9Enabled = enabled; + this.removeProtectedChild(this._barRenderer); + + this._barRenderer = this._scale9Enabled ? new ccui.Scale9Sprite() : new cc.Sprite(); + + this.loadTexture(this._textureFile, this._renderBarTexType); + this.addProtectedChild(this._barRenderer, ccui.LoadingBar.RENDERER_ZORDER, -1); + if (this._scale9Enabled) { + var ignoreBefore = this._ignoreSize; + this.ignoreContentAdaptWithSize(false); + this._prevIgnoreSize = ignoreBefore; + } else + this.ignoreContentAdaptWithSize(this._prevIgnoreSize); + this.setCapInsets(this._capInsets); + this.setPercent(this._percent); + this._barRendererAdaptDirty = true; + }, + + /** + * Returns LoadingBar is using scale9 renderer or not.. + * @returns {Boolean} + */ + isScale9Enabled: function () { + return this._scale9Enabled; + }, + + /** + * Sets capinsets for LoadingBar, if LoadingBar is using scale9 renderer. + * @param {cc.Rect} capInsets + */ + setCapInsets: function (capInsets) { + if (!capInsets) + return; + var locInsets = this._capInsets; + locInsets.x = capInsets.x; + locInsets.y = capInsets.y; + locInsets.width = capInsets.width; + locInsets.height = capInsets.height; + + if (this._scale9Enabled) + this._barRenderer.setCapInsets(capInsets); + }, + + /** + * Returns cap insets for loadingBar. + * @returns {cc.Rect} + */ + getCapInsets: function () { + return cc.rect(this._capInsets); + }, + + /** + * The current progress of loadingBar + * @param {number} percent percent value from 1 to 100. + */ + setPercent: function (percent) { + if (percent > 100) + percent = 100; + if (percent < 0) + percent = 0; + if (percent === this._percent) + return; + this._percent = percent; + this._setPercent(percent); + }, + + _setPercent: function () { + var res, rect, spriteRenderer, spriteTextureRect; + + if (this._totalLength <= 0) + return; + res = this._percent / 100.0; + + if (this._scale9Enabled) + this._setScale9Scale(); + else { + spriteRenderer = this._barRenderer; + spriteTextureRect = this._barRendererTextureSize; + rect = spriteRenderer.getTextureRect(); + rect.width = spriteTextureRect.width * res; + spriteRenderer.setTextureRect( + cc.rect( + rect.x, + rect.y, + spriteTextureRect.width * res, + spriteTextureRect.height + ), + spriteRenderer._rectRotated + ); + } + }, + + /** + * Sets the contentSize of ccui.LoadingBar + * @override + * @param {Number|cc.Size} contentSize + * @param {Number} [height] + */ + setContentSize: function (contentSize, height) { + ccui.Widget.prototype.setContentSize.call(this, contentSize, height); + this._totalLength = (height === undefined) ? contentSize.width : contentSize; + }, + + /** + * Returns the progress direction of LoadingBar. + * @returns {number} percent value from 1 to 100. + */ + getPercent: function () { + return this._percent; + }, + + _onSizeChanged: function () { + ccui.Widget.prototype._onSizeChanged.call(this); + this._barRendererAdaptDirty = true; + }, + + _adaptRenderers: function () { + if (this._barRendererAdaptDirty) { + this._barRendererScaleChangedWithSize(); + this._barRendererAdaptDirty = false; + } + }, + + /** + * Ignore the LoadingBar's custom size, if ignore is true that LoadingBar will ignore it's custom size, use renderer's content size, false otherwise. + * @override + * @param {Boolean}ignore + */ + ignoreContentAdaptWithSize: function (ignore) { + if (!this._scale9Enabled || (this._scale9Enabled && !ignore)) { + ccui.Widget.prototype.ignoreContentAdaptWithSize.call(this, ignore); + this._prevIgnoreSize = ignore; + } + }, + + /** + * Returns the texture size of renderer. + * @returns {cc.Size|*} + */ + getVirtualRendererSize: function () { + return cc.size(this._barRendererTextureSize); + }, + + /** + * Returns the renderer of ccui.LoadingBar + * @override + * @returns {cc.Node} + */ + getVirtualRenderer: function () { + return this._barRenderer; + }, + + _barRendererScaleChangedWithSize: function () { + var locBarRender = this._barRenderer, locContentSize = this._contentSize; + if(this._unifySize){ + this._totalLength = this._contentSize.width; + this.setPercent(this._percent); + }else if (this._ignoreSize) { + if (!this._scale9Enabled) { + this._totalLength = this._barRendererTextureSize.width; + locBarRender.setScale(1.0); + } + } else { + this._totalLength = locContentSize.width; + if (this._scale9Enabled) { + this._setScale9Scale(); + locBarRender.setScale(1.0); + } else { + var textureSize = this._barRendererTextureSize; + if (textureSize.width <= 0.0 || textureSize.height <= 0.0) { + locBarRender.setScale(1.0); + return; + } + var scaleX = locContentSize.width / textureSize.width; + var scaleY = locContentSize.height / textureSize.height; + locBarRender.setScaleX(scaleX); + locBarRender.setScaleY(scaleY); + } + } + switch (this._direction) { + case ccui.LoadingBar.TYPE_LEFT: + locBarRender.setPosition(0, locContentSize.height * 0.5); + break; + case ccui.LoadingBar.TYPE_RIGHT: + locBarRender.setPosition(this._totalLength, locContentSize.height * 0.5); + break; + default: + break; + } + }, + + _setScale9Scale: function () { + var width = (this._percent) / 100 * this._totalLength; + this._barRenderer.setPreferredSize(cc.size(width, this._contentSize.height)); + }, + + /** + * Returns the "class name" of widget. + * @returns {string} + */ + getDescription: function () { + return "LoadingBar"; + }, + + _createCloneInstance: function () { + return new ccui.LoadingBar(); + }, + + _copySpecialProperties: function (loadingBar) { + if (loadingBar instanceof ccui.LoadingBar) { + this._prevIgnoreSize = loadingBar._prevIgnoreSize; + this.setScale9Enabled(loadingBar._scale9Enabled); + this.loadTexture(loadingBar._textureFile, loadingBar._renderBarTexType); + this.setCapInsets(loadingBar._capInsets); + this.setPercent(loadingBar._percent); + this.setDirection(loadingBar._direction); + } + } +}); + +var _p = ccui.LoadingBar.prototype; + +// Extended properties +/** @expose */ +_p.direction; +cc.defineGetterSetter(_p, "direction", _p.getDirection, _p.setDirection); +/** @expose */ +_p.percent; +cc.defineGetterSetter(_p, "percent", _p.getPercent, _p.setPercent); + +_p = null; + +/** + * Allocates and initializes a UILoadingBar. + * @deprecated since v3.0, please use new ccui.LoadingBar() instead. + * @param {string} textureName + * @param {Number} percentage + * @return {ccui.LoadingBar} + */ +ccui.LoadingBar.create = function (textureName, percentage) { + return new ccui.LoadingBar(textureName, percentage); +}; + +// Constants +//loadingBar Type + +/** + * The left direction of ccui.LoadingBar. + * @constant + * @type {number} + */ +ccui.LoadingBar.TYPE_LEFT = 0; +/** + * The right direction of ccui.LoadingBar. + * @constant + * @type {number} + */ +ccui.LoadingBar.TYPE_RIGHT = 1; + +/** + * The zOrder value of ccui.LoadingBar's renderer. + * @constant + * @type {number} + */ +ccui.LoadingBar.RENDERER_ZORDER = -1; diff --git a/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UIRichText.js b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UIRichText.js new file mode 100644 index 0000000..c057256 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UIRichText.js @@ -0,0 +1,681 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * ccui.RichElement is the base class of RichElementText, RichElementImage etc. It has type, tag, color and opacity attributes. + * @class + * @extends ccui.Class + */ +ccui.RichElement = ccui.Class.extend(/** @lends ccui.RichElement# */{ + _type: 0, + _tag: 0, + _color: null, + _opacity: 0, + /** + * Constructor of ccui.RichElement + */ + ctor: function (tag, color, opacity) { + this._type = 0; + this._tag = tag || 0; + this._color = cc.color(255, 255, 255, 255); + if (color) { + this._color.r = color.r; + this._color.g = color.g; + this._color.b = color.b; + } + this._opacity = opacity || 0; + if (opacity === undefined) { + this._color.a = color.a; + } + else { + this._color.a = opacity; + } + } +}); + +/** + * The text element for RichText, it has text, fontName, fontSize attributes. + * @class + * @extends ccui.RichElement + */ +ccui.RichElementText = ccui.RichElement.extend(/** @lends ccui.RichElementText# */{ + _text: "", + _fontName: "", + _fontSize: 0, + /** @type cc.FontDefinition */ + _fontDefinition: null, + /** + * Usage Example using FontDefinition: + * + * var rtEl = new ccui.RichElementText("tag", new cc.FontDefinition({ + * fillStyle: cc.color.BLACK, + * fontName: "Arial", + * fontSize: 12, + * fontWeight: "bold", + * fontStyle: "normal", + * lineHeight: 14 + * }), 255, "Some Text"); + * + * Constructor of ccui.RichElementText + * @param {Number} tag + * @param {cc.Color|cc.FontDefinition} colorOrFontDef + * @param {Number} opacity + * @param {String} text + * @param {String} fontName + * @param {Number} fontSize + */ + ctor: function (tag, colorOrFontDef, opacity, text, fontName, fontSize) { + var color = colorOrFontDef; + if (colorOrFontDef && colorOrFontDef instanceof cc.FontDefinition) { + color = colorOrFontDef.fillStyle; + fontName = colorOrFontDef.fontName; + fontSize = colorOrFontDef.fontSize; + this._fontDefinition = colorOrFontDef; + } + ccui.RichElement.prototype.ctor.call(this, tag, color, opacity); + this._type = ccui.RichElement.TEXT; + this._text = text; + this._fontName = fontName; + this._fontSize = fontSize; + } +}); + +/** + * Create a richElementText + * @deprecated since v3.0, please use new ccui.RichElementText() instead. + * @param {Number} tag + * @param {cc.Color} color + * @param {Number} opacity + * @param {String} text + * @param {String} fontName + * @param {Number} fontSize + * @returns {ccui.RichElementText} + */ +ccui.RichElementText.create = function (tag, color, opacity, text, fontName, fontSize) { + return new ccui.RichElementText(tag, color, opacity, text, fontName, fontSize); +}; + +/** + * The image element for RichText, it has filePath, textureRect, textureType attributes. + * @class + * @extends ccui.RichElement + */ +ccui.RichElementImage = ccui.RichElement.extend(/** @lends ccui.RichElementImage# */{ + _filePath: "", + _textureRect: null, + _textureType: 0, + + /** + * Constructor of ccui.RichElementImage + * @param {Number} tag + * @param {cc.Color} color + * @param {Number} opacity + * @param {String} filePath + */ + ctor: function (tag, color, opacity, filePath) { + ccui.RichElement.prototype.ctor.call(this, tag, color, opacity); + this._type = ccui.RichElement.IMAGE; + this._filePath = filePath || ""; + this._textureRect = cc.rect(0, 0, 0, 0); + this._textureType = 0; + } +}); + +/** + * Create a richElementImage + * @deprecated since v3.0, please use new ccui.RichElementImage() instead. + * @param {Number} tag + * @param {cc.Color} color + * @param {Number} opacity + * @param {String} filePath + * @returns {ccui.RichElementImage} + */ +ccui.RichElementImage.create = function (tag, color, opacity, filePath) { + return new ccui.RichElementImage(tag, color, opacity, filePath); +}; + +/** + * The custom node element for RichText. + * @class + * @extends ccui.RichElement + */ +ccui.RichElementCustomNode = ccui.RichElement.extend(/** @lends ccui.RichElementCustomNode# */{ + _customNode: null, + + /** + * Constructor of ccui.RichElementCustomNode + * @param {Number} tag + * @param {cc.Color} color + * @param {Number} opacity + * @param {cc.Node} customNode + */ + ctor: function (tag, color, opacity, customNode) { + ccui.RichElement.prototype.ctor.call(this, tag, color, opacity); + this._type = ccui.RichElement.CUSTOM; + this._customNode = customNode || null; + } +}); + +/** + * Create a richElementCustomNode + * @deprecated since v3.0, please use new ccui.RichElementCustomNode() instead. + * @param {Number} tag + * @param {Number} color + * @param {Number} opacity + * @param {cc.Node} customNode + * @returns {ccui.RichElementCustomNode} + */ +ccui.RichElementCustomNode.create = function (tag, color, opacity, customNode) { + return new ccui.RichElementCustomNode(tag, color, opacity, customNode); +}; + +/** + * The rich text control of Cocos UI. It receives text, image, and custom node as its children to display. + * @class + * @extends ccui.Widget + */ +ccui.RichText = ccui.Widget.extend(/** @lends ccui.RichText# */{ + _formatTextDirty: false, + _richElements: null, + _elementRenders: null, + _leftSpaceWidth: 0, + _verticalSpace: 0, + _elementRenderersContainer: null, + _lineBreakOnSpace: false, + _textHorizontalAlignment: null, + _textVerticalAlignment: null, + + /** + * create a rich text + * Constructor of ccui.RichText. override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @example + * var uiRichText = new ccui.RichTex(); + */ + ctor: function () { + ccui.Widget.prototype.ctor.call(this); + this._formatTextDirty = false; + this._richElements = []; + this._elementRenders = []; + this._leftSpaceWidth = 0; + this._verticalSpace = 0; + this._textHorizontalAlignment = cc.TEXT_ALIGNMENT_LEFT; + this._textVerticalAlignment = cc.VERTICAL_TEXT_ALIGNMENT_TOP; + }, + + _initRenderer: function () { + this._elementRenderersContainer = new cc.Node(); + this._elementRenderersContainer.setAnchorPoint(0.5, 0.5); + this.addProtectedChild(this._elementRenderersContainer, 0, -1); + }, + + /** + * Insert a element + * @param {ccui.RichElement} element + * @param {Number} index + */ + insertElement: function (element, index) { + this._richElements.splice(index, 0, element); + this._formatTextDirty = true; + }, + + /** + * Push a element + * @param {ccui.RichElement} element + */ + pushBackElement: function (element) { + this._richElements.push(element); + this._formatTextDirty = true; + }, + + /** + * Remove element + * @param {ccui.RichElement} element + */ + removeElement: function (element) { + if (cc.isNumber(element)) + this._richElements.splice(element, 1); + else + cc.arrayRemoveObject(this._richElements, element); + this._formatTextDirty = true; + }, + + /** + * Formats the richText's content. + */ + formatText: function () { + if (this._formatTextDirty) { + this._elementRenderersContainer.removeAllChildren(); + this._elementRenders.length = 0; + var i, element, locRichElements = this._richElements; + if (this._ignoreSize) { + this._addNewLine(); + for (i = 0; i < locRichElements.length; i++) { + element = locRichElements[i]; + var elementRenderer = null; + switch (element._type) { + case ccui.RichElement.TEXT: + if (element._fontDefinition) + elementRenderer = new cc.LabelTTF(element._text, element._fontDefinition); + else //todo: There may be ambiguous + elementRenderer = new cc.LabelTTF(element._text, element._fontName, element._fontSize); + break; + case ccui.RichElement.IMAGE: + elementRenderer = new cc.Sprite(element._filePath); + break; + case ccui.RichElement.CUSTOM: + elementRenderer = element._customNode; + break; + default: + break; + } + elementRenderer.setColor(element._color); + elementRenderer.setOpacity(element._color.a); + this._pushToContainer(elementRenderer); + } + } else { + this._addNewLine(); + for (i = 0; i < locRichElements.length; i++) { + element = locRichElements[i]; + switch (element._type) { + case ccui.RichElement.TEXT: + if (element._fontDefinition) + this._handleTextRenderer(element._text, element._fontDefinition, element._fontDefinition.fontSize, element._fontDefinition.fillStyle); + else + this._handleTextRenderer(element._text, element._fontName, element._fontSize, element._color); + break; + case ccui.RichElement.IMAGE: + this._handleImageRenderer(element._filePath, element._color, element._color.a); + break; + case ccui.RichElement.CUSTOM: + this._handleCustomRenderer(element._customNode); + break; + default: + break; + } + } + } + this.formatRenderers(); + this._formatTextDirty = false; + } + }, + /** + * Prepare the child LabelTTF based on line breaking + * @param {String} text + * @param {String|cc.FontDefinition} fontNameOrFontDef + * @param {Number} fontSize + * @param {cc.Color} color + * @private + */ + _handleTextRenderer: function (text, fontNameOrFontDef, fontSize, color) { + if (text === "") + return; + + if (text === "\n") { //Force Line Breaking + this._addNewLine(); + return; + } + + var textRenderer = fontNameOrFontDef instanceof cc.FontDefinition ? new cc.LabelTTF(text, fontNameOrFontDef) : new cc.LabelTTF(text, fontNameOrFontDef, fontSize); + var textRendererWidth = textRenderer.getContentSize().width; + this._leftSpaceWidth -= textRendererWidth; + if (this._leftSpaceWidth < 0) { + var overstepPercent = (-this._leftSpaceWidth) / textRendererWidth; + var curText = text; + var stringLength = curText.length; + var leftLength = stringLength * (1 - overstepPercent); + var leftWords = curText.substr(0, leftLength); + var cutWords = curText.substr(leftLength, curText.length - 1); + var validLeftLength = leftLength > 0; + + if (this._lineBreakOnSpace) { + var lastSpaceIndex = leftWords.lastIndexOf(' '); + leftLength = lastSpaceIndex === -1 ? leftLength : lastSpaceIndex + 1; + cutWords = curText.substr(leftLength, curText.length - 1); + validLeftLength = leftLength > 0 && cutWords !== " "; + } + + if (validLeftLength) { + var leftRenderer = null; + if (fontNameOrFontDef instanceof cc.FontDefinition) { + leftRenderer = new cc.LabelTTF(leftWords.substr(0, leftLength), fontNameOrFontDef); + leftRenderer.setOpacity(fontNameOrFontDef.fillStyle.a); //TODO: Verify that might not be needed... + } else { + leftRenderer = new cc.LabelTTF(leftWords.substr(0, leftLength), fontNameOrFontDef, fontSize); + leftRenderer.setColor(color); + leftRenderer.setOpacity(color.a); + } + this._pushToContainer(leftRenderer); + } + + this._addNewLine(); + this._handleTextRenderer(cutWords, fontNameOrFontDef, fontSize, color); + } else { + if (fontNameOrFontDef instanceof cc.FontDefinition) { + textRenderer.setOpacity(fontNameOrFontDef.fillStyle.a); //TODO: Verify that might not be needed... + } else { + textRenderer.setColor(color); + textRenderer.setOpacity(color.a); + } + this._pushToContainer(textRenderer); + } + }, + + _handleImageRenderer: function (filePath, color, opacity) { + var imageRenderer = new cc.Sprite(filePath); + this._handleCustomRenderer(imageRenderer); + }, + + _handleCustomRenderer: function (renderer) { + var imgSize = renderer.getContentSize(); + this._leftSpaceWidth -= imgSize.width; + if (this._leftSpaceWidth < 0) { + this._addNewLine(); + this._pushToContainer(renderer); + this._leftSpaceWidth -= imgSize.width; + } else + this._pushToContainer(renderer); + }, + + _addNewLine: function () { + this._leftSpaceWidth = this._customSize.width; + this._elementRenders.push([]); + }, + + /** + * Formats richText's renderer. + */ + formatRenderers: function () { + var newContentSizeHeight = 0, locRenderersContainer = this._elementRenderersContainer; + var locElementRenders = this._elementRenders; + var i, j, row, nextPosX, l; + var lineHeight, offsetX; + if (this._ignoreSize) { + var newContentSizeWidth = 0; + row = locElementRenders[0]; + nextPosX = 0; + + for (j = 0; j < row.length; j++) { + l = row[j]; + l.setAnchorPoint(cc.p(0, 0)); + l.setPosition(nextPosX, 0); + locRenderersContainer.addChild(l, 1, j); + + lineHeight = l.getLineHeight ? l.getLineHeight() : newContentSizeHeight; + + var iSize = l.getContentSize(); + newContentSizeWidth += iSize.width; + newContentSizeHeight = Math.max(Math.min(newContentSizeHeight, lineHeight), iSize.height); + nextPosX += iSize.width; + } + + //Text flow horizontal alignment: + if (this._textHorizontalAlignment !== cc.TEXT_ALIGNMENT_LEFT) { + offsetX = 0; + if (this._textHorizontalAlignment === cc.TEXT_ALIGNMENT_RIGHT) + offsetX = this._contentSize.width - nextPosX; + else if (this._textHorizontalAlignment === cc.TEXT_ALIGNMENT_CENTER) + offsetX = (this._contentSize.width - nextPosX) / 2; + + for (j = 0; j < row.length; j++) + row[j].x += offsetX; + } + + locRenderersContainer.setContentSize(newContentSizeWidth, newContentSizeHeight); + } else { + var maxHeights = []; + for (i = 0; i < locElementRenders.length; i++) { + row = locElementRenders[i]; + var maxHeight = 0; + for (j = 0; j < row.length; j++) { + l = row[j]; + lineHeight = l.getLineHeight ? l.getLineHeight() : l.getContentSize().height; + maxHeight = Math.max(Math.min(l.getContentSize().height, lineHeight), maxHeight); + } + maxHeights[i] = maxHeight; + newContentSizeHeight += maxHeights[i]; + } + + var nextPosY = this._customSize.height; + + for (i = 0; i < locElementRenders.length; i++) { + row = locElementRenders[i]; + nextPosX = 0; + nextPosY -= (maxHeights[i] + this._verticalSpace); + + for (j = 0; j < row.length; j++) { + l = row[j]; + l.setAnchorPoint(cc.p(0, 0)); + l.setPosition(cc.p(nextPosX, nextPosY)); + locRenderersContainer.addChild(l, 1); + nextPosX += l.getContentSize().width; + } + //Text flow alignment(s) + if (this._textHorizontalAlignment !== cc.TEXT_ALIGNMENT_LEFT || this._textVerticalAlignment !== cc.VERTICAL_TEXT_ALIGNMENT_TOP) { + offsetX = 0; + if (this._textHorizontalAlignment === cc.TEXT_ALIGNMENT_RIGHT) + offsetX = this._contentSize.width - nextPosX; + else if (this._textHorizontalAlignment === cc.TEXT_ALIGNMENT_CENTER) + offsetX = (this._contentSize.width - nextPosX) / 2; + + var offsetY = 0; + if (this._textVerticalAlignment === cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM) + offsetY = this._customSize.height - newContentSizeHeight; + else if (this._textVerticalAlignment === cc.VERTICAL_TEXT_ALIGNMENT_CENTER) + offsetY = (this._customSize.height - newContentSizeHeight) / 2; + + for (j = 0; j < row.length; j++) { + l = row[j]; + l.x += offsetX; + l.y -= offsetY; + } + } + } + + locRenderersContainer.setContentSize(this._contentSize); + } + + var length = locElementRenders.length; + for (i = 0; i < length; i++) { + locElementRenders[i].length = 0; + } + this._elementRenders.length = 0; + + this.setContentSize(this._ignoreSize ? this.getVirtualRendererSize() : this._customSize); + this._updateContentSizeWithTextureSize(this._contentSize); + + locRenderersContainer.setPosition(this._contentSize.width * 0.5, this._contentSize.height * 0.5); + }, + + _pushToContainer: function (renderer) { + if (this._elementRenders.length <= 0) + return; + this._elementRenders[this._elementRenders.length - 1].push(renderer); + }, + + _adaptRenderers: function () { + this.formatText(); + }, + + /** + * Sets vertical space + * @param {Number} space + */ + setVerticalSpace: function (space) { + this._verticalSpace = space; + }, + + /** + * Sets anchor point + * @override + * @param {cc.Point} pt + */ + setAnchorPoint: function (pt) { + ccui.Widget.prototype.setAnchorPoint.call(this, pt); + this._elementRenderersContainer.setAnchorPoint(pt); + }, + _setAnchorX: function (x) { + ccui.Widget.prototype._setAnchorX.call(this, x); + this._elementRenderersContainer._setAnchorX(x); + }, + _setAnchorY: function (y) { + ccui.Widget.prototype._setAnchorY.call(this, y); + this._elementRenderersContainer._setAnchorY(y); + }, + + /** + * Returns the renderer container's content size. + * @override + * @returns {cc.Size} + */ + getVirtualRendererSize: function () { + return this._elementRenderersContainer.getContentSize(); + }, + + /** + * Ignore the richText's custom size, If ignore is true that richText will ignore it's custom size, use renderer's content size, false otherwise. + * @param {Boolean} ignore + * @override + */ + ignoreContentAdaptWithSize: function (ignore) { + if (this._ignoreSize !== ignore) { + this._formatTextDirty = true; + ccui.Widget.prototype.ignoreContentAdaptWithSize.call(this, ignore); + } + }, + + /** + * Gets the content size of ccui.RichText + * @override + * @return {cc.Size} + */ + getContentSize: function () { + this.formatText(); + return cc.Node.prototype.getContentSize.call(this); + }, + _getWidth: function () { + this.formatText(); + return cc.Node.prototype._getWidth.call(this); + }, + _getHeight: function () { + this.formatText(); + return cc.Node.prototype._getHeight.call(this); + }, + + setContentSize: function (contentSize, height) { + var locWidth = (height === undefined) ? contentSize.width : contentSize; + var locHeight = (height === undefined) ? contentSize.height : height; + ccui.Widget.prototype.setContentSize.call(this, locWidth, locHeight); + this._formatTextDirty = true; + }, + + /** + * Returns the class name of ccui.RichText. + * @returns {string} + */ + getDescription: function () { + return "RichText"; + }, + /** + * Allow child renderer to be affected by ccui.RichText's opacity + * @param {boolean} value + */ + setCascadeOpacityEnabled: function (value) { + ccui.Widget.prototype.setCascadeOpacityEnabled.call(this, value); + this._elementRenderersContainer.setCascadeOpacityEnabled(value); + }, + /** + * This allow the RichText layout to break line on space only like in Latin text format + * by default the property is false, which break the line on characters + * @param value + */ + setLineBreakOnSpace: function (value) { + this._lineBreakOnSpace = value; + this._formatTextDirty = true; + this.formatText(); + }, + /** + * Set the renderer horizontal flow alignment for the Control + * although it is named TextHorizontalAlignment, it should work with all type of renderer too. + * NOTE: we should rename this to setHorizontalAlignment directly + * + * @example + * var richText = new ccui.RichText(); + * richText.setTextHorizontalAlignment(cc.Text_ALIGNMENT_RIGHT); + * + * @param {Number} value - example cc.TEXT_ALIGNMENT_RIGHT + */ + setTextHorizontalAlignment: function (value) { + if (value !== this._textHorizontalAlignment) { + this._textHorizontalAlignment = value; + this.formatText(); + } + }, + /** + * Set the renderer vertical flow alignment for the Control + * although it is named TextVerticalAlignment, it should work with all type of renderer too. + * + * @example + * var richText = new ccui.RichText(); + * richText.setTextVerticalAlignment(cc.VERTICAL_TEXT_ALIGNMENT_CENTER); + * + * @param {Number} value - example cc.VERTICAL_TEXT_ALIGNMENT_CENTER + */ + setTextVerticalAlignment: function (value) { + if (value !== this._textVerticalAlignment) { + this._textVerticalAlignment = value; + this.formatText(); + } + } +}); + +/** + * create a rich text + * @deprecated since v3.0, please use new ccui.RichText() instead. + * @returns {RichText} + */ +ccui.RichText.create = function () { + return new ccui.RichText(); +}; + +// Constants +//Rich element type +/** + * The text type of rich element. + * @constant + * @type {number} + */ +ccui.RichElement.TEXT = 0; +/** + * The image type of rich element. + * @constant + * @type {number} + */ +ccui.RichElement.IMAGE = 1; +/** + * The custom type of rich element. + * @constant + * @type {number} + */ +ccui.RichElement.CUSTOM = 2; diff --git a/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UISlider.js b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UISlider.js new file mode 100644 index 0000000..c8362ff --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UISlider.js @@ -0,0 +1,787 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The Slider control of Cocos UI. + * @class + * @extends ccui.Widget + * + * @property {Number} percent - The current progress of loadingbar + */ +ccui.Slider = ccui.Widget.extend(/** @lends ccui.Slider# */{ + _barRenderer: null, + _progressBarRenderer: null, + _barTextureSize: null, + _progressBarTextureSize: null, + _slidBallNormalRenderer: null, + _slidBallPressedRenderer: null, + _slidBallDisabledRenderer: null, + _slidBallRenderer: null, + _barLength: 0, + _percent: 0, + _scale9Enabled: false, + _prevIgnoreSize: true, + _textureFile: "", + _progressBarTextureFile: "", + _slidBallNormalTextureFile: "", + _slidBallPressedTextureFile: "", + _slidBallDisabledTextureFile: "", + _capInsetsBarRenderer: null, + _capInsetsProgressBarRenderer: null, + _sliderEventListener: null, + _sliderEventSelector: null, + _barTexType: ccui.Widget.LOCAL_TEXTURE, + _progressBarTexType: ccui.Widget.LOCAL_TEXTURE, + _ballNTexType: ccui.Widget.LOCAL_TEXTURE, + _ballPTexType: ccui.Widget.LOCAL_TEXTURE, + _ballDTexType: ccui.Widget.LOCAL_TEXTURE, + _isTextureLoaded: false, + _className: "Slider", + _barRendererAdaptDirty: true, + _progressBarRendererDirty: true, + _unifySize: false, + _zoomScale: 0.1, + + _sliderBallNormalTextureScaleX: 1, + _sliderBallNormalTextureScaleY: 1, + + /** + * allocates and initializes a UISlider. + * Constructor of ccui.Slider. override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @example + * // example + * var uiSlider = new ccui.Slider(); + */ + ctor: function (barTextureName, normalBallTextureName, resType) { + this._barTextureSize = cc.size(0, 0); + this._progressBarTextureSize = cc.size(0, 0); + this._capInsetsBarRenderer = cc.rect(0, 0, 0, 0); + this._capInsetsProgressBarRenderer = cc.rect(0, 0, 0, 0); + ccui.Widget.prototype.ctor.call(this); + + resType = resType || 0; + this.setTouchEnabled(true); + if (barTextureName) { + this.loadBarTexture(barTextureName, resType); + } + if (normalBallTextureName) { + this.loadSlidBallTextures(normalBallTextureName, resType); + } + }, + + _initRenderer: function () { + //todo use Scale9Sprite + this._barRenderer = new cc.Sprite(); + this._progressBarRenderer = new cc.Sprite(); + this._progressBarRenderer.setAnchorPoint(0.0, 0.5); + this.addProtectedChild(this._barRenderer, ccui.Slider.BASEBAR_RENDERER_ZORDER, -1); + this.addProtectedChild(this._progressBarRenderer, ccui.Slider.PROGRESSBAR_RENDERER_ZORDER, -1); + this._slidBallNormalRenderer = new cc.Sprite(); + this._slidBallPressedRenderer = new cc.Sprite(); + this._slidBallPressedRenderer.setVisible(false); + this._slidBallDisabledRenderer = new cc.Sprite(); + this._slidBallDisabledRenderer.setVisible(false); + this._slidBallRenderer = new cc.Node(); + this._slidBallRenderer.addChild(this._slidBallNormalRenderer); + this._slidBallRenderer.addChild(this._slidBallPressedRenderer); + this._slidBallRenderer.addChild(this._slidBallDisabledRenderer); + this._slidBallRenderer.setCascadeColorEnabled(true); + this._slidBallRenderer.setCascadeOpacityEnabled(true); + + this.addProtectedChild(this._slidBallRenderer, ccui.Slider.BALL_RENDERER_ZORDER, -1); + }, + + /** + * Loads texture for slider bar. + * @param {String} fileName + * @param {ccui.Widget.LOCAL_TEXTURE|ccui.Widget.PLIST_TEXTURE} texType + */ + loadBarTexture: function (fileName, texType) { + if (!fileName) { + return; + } + texType = texType || ccui.Widget.LOCAL_TEXTURE; + this._textureFile = fileName; + this._barTexType = texType; + var barRenderer = this._barRenderer; + + var self = this; + if (!barRenderer._textureLoaded) { + barRenderer.addEventListener("load", function () { + self.loadBarTexture(self._textureFile, self._barTexType); + }); + } + + switch (this._barTexType) { + case ccui.Widget.LOCAL_TEXTURE: + //SetTexture cannot load resource + barRenderer.initWithFile(fileName); + break; + case ccui.Widget.PLIST_TEXTURE: + //SetTexture cannot load resource + barRenderer.initWithSpriteFrameName(fileName); + break; + default: + break; + } + this._updateChildrenDisplayedRGBA(); + + this._barRendererAdaptDirty = true; + this._progressBarRendererDirty = true; + this._updateContentSizeWithTextureSize(this._barRenderer.getContentSize()); + this._findLayout(); + this._barTextureSize = this._barRenderer.getContentSize(); + }, + + /** + * Loads dark state texture for slider progress bar. + * @param {String} fileName + * @param {ccui.Widget.LOCAL_TEXTURE|ccui.Widget.PLIST_TEXTURE} texType + */ + loadProgressBarTexture: function (fileName, texType) { + if (!fileName) { + return; + } + texType = texType || ccui.Widget.LOCAL_TEXTURE; + this._progressBarTextureFile = fileName; + this._progressBarTexType = texType; + var progressBarRenderer = this._progressBarRenderer; + + var self = this; + if (!progressBarRenderer._textureLoaded) { + progressBarRenderer.addEventListener("load", function () { + self.loadProgressBarTexture(self._progressBarTextureFile, self._progressBarTexType); + }); + } + + switch (this._progressBarTexType) { + case ccui.Widget.LOCAL_TEXTURE: + //SetTexture cannot load resource + progressBarRenderer.initWithFile(fileName); + break; + case ccui.Widget.PLIST_TEXTURE: + //SetTexture cannot load resource + progressBarRenderer.initWithSpriteFrameName(fileName); + break; + default: + break; + } + this._updateChildrenDisplayedRGBA(); + + this._progressBarRenderer.setAnchorPoint(cc.p(0, 0.5)); + var tz = this._progressBarRenderer.getContentSize(); + this._progressBarTextureSize = {width: tz.width, height: tz.height}; + this._progressBarRendererDirty = true; + this._findLayout(); + }, + + /** + * Sets if slider is using scale9 renderer. + * @param {Boolean} able + */ + setScale9Enabled: function (able) { + //todo use setScale9Enabled + if (this._scale9Enabled === able) + return; + + this._scale9Enabled = able; + this.removeProtectedChild(this._barRenderer, true); + this.removeProtectedChild(this._progressBarRenderer, true); + this._barRenderer = null; + this._progressBarRenderer = null; + if (this._scale9Enabled) { + this._barRenderer = new ccui.Scale9Sprite(); + this._progressBarRenderer = new ccui.Scale9Sprite(); + } else { + this._barRenderer = new cc.Sprite(); + this._progressBarRenderer = new cc.Sprite(); + } + this.loadBarTexture(this._textureFile, this._barTexType); + this.loadProgressBarTexture(this._progressBarTextureFile, this._progressBarTexType); + this.addProtectedChild(this._barRenderer, ccui.Slider.BASEBAR_RENDERER_ZORDER, -1); + this.addProtectedChild(this._progressBarRenderer, ccui.Slider.PROGRESSBAR_RENDERER_ZORDER, -1); + if (this._scale9Enabled) { + var ignoreBefore = this._ignoreSize; + this.ignoreContentAdaptWithSize(false); + this._prevIgnoreSize = ignoreBefore; + } else { + this.ignoreContentAdaptWithSize(this._prevIgnoreSize); + } + this.setCapInsetsBarRenderer(this._capInsetsBarRenderer); + this.setCapInsetProgressBarRenderer(this._capInsetsProgressBarRenderer); + this._barRendererAdaptDirty = true; + this._progressBarRendererDirty = true; + }, + + /** + * Returns slider is using scale9 renderer or not. + * @returns {Boolean} + */ + isScale9Enabled: function () { + return this._scale9Enabled; + }, + + /** + * override "ignoreContentAdaptWithSize" method of widget. + * @param {Boolean} ignore + */ + ignoreContentAdaptWithSize: function (ignore) { + if (!this._scale9Enabled || (this._scale9Enabled && !ignore)) { + ccui.Widget.prototype.ignoreContentAdaptWithSize.call(this, ignore); + this._prevIgnoreSize = ignore; + } + }, + + /** + * Sets capinsets for slider, if slider is using scale9 renderer. + * @param {cc.Rect} capInsets + */ + setCapInsets: function (capInsets) { + this.setCapInsetsBarRenderer(capInsets); + this.setCapInsetProgressBarRenderer(capInsets); + }, + + /** + * Sets capinsets for slider's renderer, if slider is using scale9 renderer. + * @param {cc.Rect} capInsets + */ + setCapInsetsBarRenderer: function (capInsets) { + if (!capInsets) + return; + var locInsets = this._capInsetsBarRenderer; + locInsets.x = capInsets.x; + locInsets.y = capInsets.y; + locInsets.width = capInsets.width; + locInsets.height = capInsets.height; + if (!this._scale9Enabled) + return; + this._barRenderer.setCapInsets(capInsets); + }, + + /** + * Returns cap insets for slider. + * @returns {cc.Rect} + */ + getCapInsetsBarRenderer: function () { + return cc.rect(this._capInsetsBarRenderer); + }, + + /** + * Sets capinsets of ProgressBar for slider, if slider is using scale9 renderer. + * @param {cc.Rect} capInsets + */ + setCapInsetProgressBarRenderer: function (capInsets) { + if (!capInsets) + return; + var locInsets = this._capInsetsProgressBarRenderer; + locInsets.x = capInsets.x; + locInsets.y = capInsets.y; + locInsets.width = capInsets.width; + locInsets.height = capInsets.height; + if (!this._scale9Enabled) + return; + this._progressBarRenderer.setCapInsets(capInsets); + }, + + /** + * Returns cap insets of ProgressBar for slider. + * @returns {cc.Rect} + */ + getCapInsetsProgressBarRenderer: function () { + return cc.rect(this._capInsetsProgressBarRenderer); + }, + + /** + * Loads textures for slider ball. + * @param {String} normal + * @param {String} pressed + * @param {String} disabled + * @param {ccui.Widget.LOCAL_TEXTURE|ccui.Widget.PLIST_TEXTURE} texType + */ + loadSlidBallTextures: function (normal, pressed, disabled, texType) { + this.loadSlidBallTextureNormal(normal, texType); + this.loadSlidBallTexturePressed(pressed, texType); + this.loadSlidBallTextureDisabled(disabled, texType); + }, + + /** + * Loads normal state texture for slider ball. + * @param {String} normal + * @param {ccui.Widget.LOCAL_TEXTURE|ccui.Widget.PLIST_TEXTURE} texType + */ + loadSlidBallTextureNormal: function (normal, texType) { + if (!normal) { + return; + } + texType = texType || ccui.Widget.LOCAL_TEXTURE; + this._slidBallNormalTextureFile = normal; + this._ballNTexType = texType; + + var self = this; + if (!this._slidBallNormalRenderer._textureLoaded) { + this._slidBallNormalRenderer.addEventListener("load", function () { + self.loadSlidBallTextureNormal(self._slidBallNormalTextureFile, self._ballNTexType); + }); + } + + switch (this._ballNTexType) { + case ccui.Widget.LOCAL_TEXTURE: + //SetTexture cannot load resource + this._slidBallNormalRenderer.initWithFile(normal); + break; + case ccui.Widget.PLIST_TEXTURE: + //SetTexture cannot load resource + this._slidBallNormalRenderer.initWithSpriteFrameName(normal); + break; + default: + break; + } + this._updateChildrenDisplayedRGBA(); + this._findLayout(); + }, + + /** + * Loads selected state texture for slider ball. + * @param {String} pressed + * @param {ccui.Widget.LOCAL_TEXTURE|ccui.Widget.PLIST_TEXTURE} texType + */ + loadSlidBallTexturePressed: function (pressed, texType) { + if (!pressed) { + return; + } + texType = texType || ccui.Widget.LOCAL_TEXTURE; + this._slidBallPressedTextureFile = pressed; + this._ballPTexType = texType; + + var self = this; + if (!this._slidBallPressedRenderer._textureLoaded) { + this._slidBallPressedRenderer.addEventListener("load", function () { + self.loadSlidBallTexturePressed(self._slidBallPressedTextureFile, self._ballPTexType); + }); + } + + switch (this._ballPTexType) { + case ccui.Widget.LOCAL_TEXTURE: + //SetTexture cannot load resource + this._slidBallPressedRenderer.initWithFile(pressed); + break; + case ccui.Widget.PLIST_TEXTURE: + //SetTexture cannot load resource + this._slidBallPressedRenderer.initWithSpriteFrameName(pressed); + break; + default: + break; + } + this._updateChildrenDisplayedRGBA(); + this._findLayout(); + }, + + /** + * Load dark state texture for slider ball. + * @param {String} disabled + * @param {ccui.Widget.LOCAL_TEXTURE|ccui.Widget.PLIST_TEXTURE} texType + */ + loadSlidBallTextureDisabled: function (disabled, texType) { + if (!disabled) { + return; + } + texType = texType || ccui.Widget.LOCAL_TEXTURE; + this._slidBallDisabledTextureFile = disabled; + this._ballDTexType = texType; + + var self = this; + if (!this._slidBallDisabledRenderer._textureLoaded) { + this._slidBallDisabledRenderer.addEventListener("load", function () { + self.loadSlidBallTextureDisabled(self._slidBallDisabledTextureFile, self._ballDTexType); + }); + } + + switch (this._ballDTexType) { + case ccui.Widget.LOCAL_TEXTURE: + //SetTexture cannot load resource + this._slidBallDisabledRenderer.initWithFile(disabled); + break; + case ccui.Widget.PLIST_TEXTURE: + //SetTexture cannot load resource + this._slidBallDisabledRenderer.initWithSpriteFrameName(disabled); + break; + default: + break; + } + this._updateChildrenDisplayedRGBA(); + this._findLayout(); + }, + + /** + * Changes the progress direction of slider. + * @param {number} percent + */ + setPercent: function (percent) { + if (percent > 100) + percent = 100; + if (percent < 0) + percent = 0; + this._percent = percent; + var res = percent / 100.0; + var dis = this._barLength * res; + this._slidBallRenderer.setPosition(dis, this._contentSize.height / 2); + if (this._scale9Enabled) + this._progressBarRenderer.setPreferredSize(cc.size(dis, this._contentSize.height)); + else { + var spriteRenderer = this._progressBarRenderer; + var rect = spriteRenderer.getTextureRect(); + spriteRenderer.setTextureRect( + cc.rect(rect.x, rect.y, dis / spriteRenderer._scaleX, rect.height), + spriteRenderer.isTextureRectRotated() + ); + } + }, + + /** + * test the point whether location in loadingBar's bounding box. + * @override + * @param {cc.Point} pt + * @returns {boolean} + */ + hitTest: function (pt) { + var nsp = this._slidBallNormalRenderer.convertToNodeSpace(pt); + var ballSize = this._slidBallNormalRenderer.getContentSize(); + var ballRect = cc.rect(0, 0, ballSize.width, ballSize.height); + return (nsp.x >= ballRect.x && + nsp.x <= (ballRect.x + ballRect.width) && + nsp.y >= ballRect.y && + nsp.y <= (ballRect.y +ballRect.height)); + }, + + onTouchBegan: function (touch, event) { + var pass = ccui.Widget.prototype.onTouchBegan.call(this, touch, event); + if (this._hit) { + var nsp = this.convertToNodeSpace(this._touchBeganPosition); + this.setPercent(this._getPercentWithBallPos(nsp.x)); + this._percentChangedEvent(); + } + return pass; + }, + + onTouchMoved: function (touch, event) { + var touchPoint = touch.getLocation(); + var nsp = this.convertToNodeSpace(touchPoint); + this.setPercent(this._getPercentWithBallPos(nsp.x)); + this._percentChangedEvent(); + }, + + onTouchEnded: function (touch, event) { + ccui.Widget.prototype.onTouchEnded.call(this, touch, event); + }, + + onTouchCancelled: function (touch, event) { + ccui.Widget.prototype.onTouchCancelled.call(this, touch, event); + }, + + /** + * Returns percent with ball's position. + * @param {cc.Point} px + * @returns {number} + */ + _getPercentWithBallPos: function (px) { + return ((px / this._barLength) * 100); + }, + + /** + * add event listener + * @param {Function} selector + * @param {Object} [target=] + * @deprecated since v3.0, please use addEventListener instead. + */ + addEventListenerSlider: function (selector, target) { + this.addEventListener(selector, target); + }, + + /** + * Adds a callback + * @param {Function} selector + * @param {Object} [target=] + */ + addEventListener: function (selector, target) { + this._sliderEventSelector = selector; //when target is undefined, _sliderEventSelector = _eventCallback + this._sliderEventListener = target; + }, + + _percentChangedEvent: function () { + if (this._sliderEventSelector) { + if (this._sliderEventListener) + this._sliderEventSelector.call(this._sliderEventListener, this, ccui.Slider.EVENT_PERCENT_CHANGED); + else + this._sliderEventSelector(this, ccui.Slider.EVENT_PERCENT_CHANGED); // _eventCallback + } + if (this._ccEventCallback) + this._ccEventCallback(this, ccui.Slider.EVENT_PERCENT_CHANGED); + }, + + /** + * Gets the progress direction of slider. + * @returns {number} + */ + getPercent: function () { + return this._percent; + }, + + _onSizeChanged: function () { + ccui.Widget.prototype._onSizeChanged.call(this); + this._barRendererAdaptDirty = true; + this._progressBarRendererDirty = true; + }, + + _adaptRenderers: function () { + if (this._barRendererAdaptDirty) { + this._barRendererScaleChangedWithSize(); + this._barRendererAdaptDirty = false; + } + if (this._progressBarRendererDirty) { + this._progressBarRendererScaleChangedWithSize(); + this._progressBarRendererDirty = false; + } + }, + + /** + * Returns the content size of bar renderer. + * @returns {cc.Size} + */ + getVirtualRendererSize: function () { + return this._barRenderer.getContentSize(); + }, + + /** + * Returns the bar renderer. + * @returns {cc.Node} + */ + getVirtualRenderer: function () { + return this._barRenderer; + }, + + _barRendererScaleChangedWithSize: function () { + if (this._unifySize) { + this._barLength = this._contentSize.width; + this._barRenderer.setPreferredSize(this._contentSize); + } else if (this._ignoreSize) { + this._barRenderer.setScale(1.0); + this._barLength = this._contentSize.width; + } else { + this._barLength = this._contentSize.width; + if (this._scale9Enabled) { + this._barRenderer.setPreferredSize(this._contentSize); + this._barRenderer.setScale(1.0); + } else { + var btextureSize = this._barTextureSize; + if (btextureSize.width <= 0.0 || btextureSize.height <= 0.0) { + this._barRenderer.setScale(1.0); + } else { + var bscaleX = this._contentSize.width / btextureSize.width; + var bscaleY = this._contentSize.height / btextureSize.height; + this._barRenderer.setScaleX(bscaleX); + this._barRenderer.setScaleY(bscaleY); + } + } + } + this._barRenderer.setPosition(this._contentSize.width / 2.0, this._contentSize.height / 2.0); + this.setPercent(this._percent); + }, + + _progressBarRendererScaleChangedWithSize: function () { + if (this._unifySize) { + this._progressBarRenderer.setPreferredSize(this._contentSize); + } else if (this._ignoreSize) { + if (!this._scale9Enabled) { + var ptextureSize = this._progressBarTextureSize; + var pscaleX = this._contentSize.width / ptextureSize.width; + var pscaleY = this._contentSize.height / ptextureSize.height; + this._progressBarRenderer.setScaleX(pscaleX); + this._progressBarRenderer.setScaleY(pscaleY); + } + } + else { + if (this._scale9Enabled) { + this._progressBarRenderer.setPreferredSize(this._contentSize); + this._progressBarRenderer.setScale(1); + } + else { + var ptextureSize = this._progressBarTextureSize; + if (ptextureSize.width <= 0.0 || ptextureSize.height <= 0.0) { + this._progressBarRenderer.setScale(1.0); + return; + } + var pscaleX = this._contentSize.width / ptextureSize.width; + var pscaleY = this._contentSize.height / ptextureSize.height; + this._progressBarRenderer.setScaleX(pscaleX); + this._progressBarRenderer.setScaleY(pscaleY); + } + } + this._progressBarRenderer.setPosition(0.0, this._contentSize.height / 2.0); + this.setPercent(this._percent); + }, + + _onPressStateChangedToNormal: function () { + this._slidBallNormalRenderer.setVisible(true); + this._slidBallPressedRenderer.setVisible(false); + this._slidBallDisabledRenderer.setVisible(false); + + this._slidBallNormalRenderer.setScale(this._sliderBallNormalTextureScaleX, this._sliderBallNormalTextureScaleY); + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + this._slidBallNormalRenderer._renderCmd._shaderProgram = this._getNormalGLProgram(); + } else { + // TODO: add canvas support + } + }, + + _onPressStateChangedToPressed: function () { + if (!this._slidBallPressedTextureFile) { + this._slidBallNormalRenderer.setScale(this._sliderBallNormalTextureScaleX + this._zoomScale, this._sliderBallNormalTextureScaleY + this._zoomScale); + } else { + this._slidBallNormalRenderer.setVisible(false); + this._slidBallPressedRenderer.setVisible(true); + this._slidBallDisabledRenderer.setVisible(false); + } + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + this._slidBallNormalRenderer._renderCmd._shaderProgram = this._getNormalGLProgram(); + } else { + // TODO: add canvas support + } + }, + + _onPressStateChangedToDisabled: function () { + if (this._slidBallDisabledTextureFile) { + this._slidBallNormalRenderer.setVisible(false); + this._slidBallDisabledRenderer.setVisible(true); + } else { + this._slidBallNormalRenderer.setVisible(true); + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + this._slidBallNormalRenderer._renderCmd._shaderProgram = this._getGrayGLProgram(); + } else { + // TODO: add canvas support + } + } + this._slidBallNormalRenderer.setScale(this._sliderBallNormalTextureScaleX, this._sliderBallNormalTextureScaleY); + this._slidBallPressedRenderer.setVisible(false); + }, + + setZoomScale: function (scale) { + this._zoomScale = scale; + }, + + getZoomScale: function () { + return this._zoomScale; + }, + + getSlidBallNormalRenderer: function () { + return this._slidBallNormalRenderer; + }, + + getSlidBallPressedRenderer: function () { + return this._slidBallPressedRenderer; + }, + + getSlidBallDisabledRenderer: function () { + return this._slidBallDisabledRenderer; + }, + + getSlidBallRenderer: function () { + return this._slidBallRenderer; + }, + + /** + * Returns the "class name" of ccui.LoadingBar. + * @returns {string} + */ + getDescription: function () { + return "Slider"; + }, + + _createCloneInstance: function () { + return new ccui.Slider(); + }, + + _copySpecialProperties: function (slider) { + this._prevIgnoreSize = slider._prevIgnoreSize; + this.setScale9Enabled(slider._scale9Enabled); + this.loadBarTexture(slider._textureFile, slider._barTexType); + this.loadProgressBarTexture(slider._progressBarTextureFile, slider._progressBarTexType); + this.loadSlidBallTextureNormal(slider._slidBallNormalTextureFile, slider._ballNTexType); + this.loadSlidBallTexturePressed(slider._slidBallPressedTextureFile, slider._ballPTexType); + this.loadSlidBallTextureDisabled(slider._slidBallDisabledTextureFile, slider._ballDTexType); + this.setPercent(slider.getPercent()); + this._sliderEventListener = slider._sliderEventListener; + this._sliderEventSelector = slider._sliderEventSelector; + this._zoomScale = slider._zoomScale; + this._ccEventCallback = slider._ccEventCallback; + + } +}); + +var _p = ccui.Slider.prototype; + +// Extended properties +/** @expose */ +_p.percent; +cc.defineGetterSetter(_p, "percent", _p.getPercent, _p.setPercent); + +_p = null; + +/** + * allocates and initializes a UISlider. + * @deprecated since v3.0, please use new ccui.Slider() instead. + * @return {ccui.Slider} + */ +ccui.Slider.create = function (barTextureName, normalBallTextureName, resType) { + return new ccui.Slider(barTextureName, normalBallTextureName, resType); +}; + +// Constant +//Slider event type +/** + * The percent change event flag of ccui.Slider. + * @constant + * @type {number} + */ +ccui.Slider.EVENT_PERCENT_CHANGED = 0; + +//Render zorder +/** + * The zOrder value of ccui.Slider's base bar renderer. + * @constant + * @type {number} + */ +ccui.Slider.BASEBAR_RENDERER_ZORDER = -3; +/** + * The zOrder value of ccui.Slider's progress bar renderer. + * @constant + * @type {number} + */ +ccui.Slider.PROGRESSBAR_RENDERER_ZORDER = -2; +/** + * The zOrder value of ccui.Slider's ball renderer. + * @constant + * @type {number} + */ +ccui.Slider.BALL_RENDERER_ZORDER = -1; diff --git a/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UIText.js b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UIText.js new file mode 100644 index 0000000..5d72e06 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UIText.js @@ -0,0 +1,525 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The text control of Cocos UI. + * @class + * @extends ccui.Widget + * + * @property {Number} boundingWidth - Width of the bounding area of label, the real content width is limited by boundingWidth + * @property {Number} boundingHeight - Height of the bounding area of label, the real content height is limited by boundingHeight + * @property {String} string - The content string of the label + * @property {Number} stringLength - <@readonly> The content string length of the label + * @property {String} font - The label font with a style string: e.g. "18px Verdana" + * @property {String} fontName - The label font name + * @property {Number} fontSize - The label font size + * @property {Number} textAlign - Horizontal Alignment of label, cc.TEXT_ALIGNMENT_LEFT|cc.TEXT_ALIGNMENT_CENTER|cc.TEXT_ALIGNMENT_RIGHT + * @property {Number} verticalAlign - Vertical Alignment of label: cc.VERTICAL_TEXT_ALIGNMENT_TOP|cc.VERTICAL_TEXT_ALIGNMENT_CENTER|cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM + * @property {Boolean} touchScaleEnabled - Indicate whether the label will scale when touching + */ +ccui.Text = ccui.Widget.extend(/** @lends ccui.Text# */{ + _touchScaleChangeEnabled: false, + _normalScaleValueX: 1, + _normalScaleValueY: 1, + _fontName: "Arial", + _fontSize: 16, + _onSelectedScaleOffset: 0.5, + _labelRenderer: null, + _textAreaSize: null, + _textVerticalAlignment: 0, + _textHorizontalAlignment: 0, + _className: "Text", + _type: null, + _labelRendererAdaptDirty: true, + + /** + * allocates and initializes a UILabel. + * Constructor of ccui.Text. override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {String} textContent + * @param {String} fontName + * @param {Number} fontSize + * @example + * // example + * var uiLabel = new ccui.Text(); + */ + ctor: function (textContent, fontName, fontSize) { + this._type = ccui.Text.Type.SYSTEM; + this._textAreaSize = cc.size(0, 0); + ccui.Widget.prototype.ctor.call(this); + + if (fontSize !== undefined) { + this.setFontName(fontName); + this.setFontSize(fontSize); + this.setString(textContent); + } else { + this.setFontName(this._fontName); + } + }, + + _initRenderer: function () { + this._labelRenderer = new cc.LabelTTF(); + this.addProtectedChild(this._labelRenderer, ccui.Text.RENDERER_ZORDER, -1); + }, + + /** + * Changes the value of ccui.Text. + * @deprecated since v3.0, please use setString() instead. + * @param {String} text + */ + setText: function (text) { + cc.log("Please use the setString"); + this.setString(text); + }, + + /** + * Changes the value of ccui.Text. + * @param {String} text + */ + setString: function (text) { + if(text === this._labelRenderer.getString()) return; + this._setString(text); + + this._updateContentSizeWithTextureSize(this._labelRenderer.getContentSize()); + }, + + _setString: function (text) { + this._labelRenderer.setString(text); + this._labelRendererAdaptDirty = true; + }, + + /** + * Gets the string value of ccui.Text. + * @deprecated since v3.0, please use getString instead. + * @returns {String} + */ + getStringValue: function () { + cc.log("Please use the getString"); + return this._labelRenderer.getString(); + }, + + /** + * Gets the string value of ccui.Text. + * @returns {String} + */ + getString: function () { + return this._labelRenderer.getString(); + }, + + /** + * Gets the string length of ccui.Text. + * @returns {Number} + */ + getStringLength: function () { + return this._labelRenderer.getStringLength(); + }, + + /** + * Sets fontSize + * @param {Number} size + */ + setFontSize: function (size) { + this._setFontSize(size); + this._updateContentSizeWithTextureSize(this._labelRenderer.getContentSize()); + }, + + _setFontSize: function (size) { + this._labelRenderer.setFontSize(size); + this._fontSize = size; + this._labelRendererAdaptDirty = true; + }, + + /** + * Returns font Size of ccui.Text + * @returns {Number} + */ + getFontSize: function () { + return this._fontSize; + }, + + /** + * Sets font name + * @return {String} name + */ + setFontName: function (name) { + this._setFontName(name); + this._updateContentSizeWithTextureSize(this._labelRenderer.getContentSize()); + }, + + _setFontName: function (name) { + this._fontName = name; + this._labelRenderer.setFontName(name); + this._labelRendererAdaptDirty = true; + }, + + _updateUITextContentSize: function () { + this._updateContentSizeWithTextureSize(this._labelRenderer.getContentSize()); + }, + + /** + * Returns font name of ccui.Text. + * @returns {string} + */ + getFontName: function () { + return this._fontName; + }, + + _setFont: function (font) { + var res = cc.LabelTTF._fontStyleRE.exec(font); + if (res) { + this._fontSize = parseInt(res[1]); + this._fontName = res[2]; + this._labelRenderer._setFont(font); + this._labelScaleChangedWithSize(); + } + }, + _getFont: function () { + return this._labelRenderer._getFont(); + }, + + /** + * Returns the type of ccui.Text. + * @returns {null} + */ + getType: function () { + return this._type; + }, + + /** + * Sets text Area Size + * @param {cc.Size} size + */ + setTextAreaSize: function (size) { + this._setTextAreaSize(size); + this._updateContentSizeWithTextureSize(this._labelRenderer.getContentSize()); + }, + + _setTextAreaSize: function (size) { + this._labelRenderer.setDimensions(size); + if (!this._ignoreSize){ + this._customSize = size; + } + this._labelRendererAdaptDirty = true; + }, + + /** + * Returns renderer's dimension. + * @returns {cc.Size} + */ + getTextAreaSize: function () { + return this._labelRenderer.getDimensions(); + }, + + /** + * Sets Horizontal Alignment of cc.LabelTTF + * @param {cc.TEXT_ALIGNMENT_LEFT|cc.TEXT_ALIGNMENT_CENTER|cc.TEXT_ALIGNMENT_RIGHT} alignment Horizontal Alignment + */ + setTextHorizontalAlignment: function (alignment) { + this._setTextHorizontalAlignment(alignment); + this._updateContentSizeWithTextureSize(this._labelRenderer.getContentSize()); + }, + + + _setTextHorizontalAlignment: function (alignment) { + this._labelRenderer.setHorizontalAlignment(alignment); + this._labelRendererAdaptDirty = true; + }, + + /** + * Returns Horizontal Alignment of label + * @returns {TEXT_ALIGNMENT_LEFT|TEXT_ALIGNMENT_CENTER|TEXT_ALIGNMENT_RIGHT} + */ + getTextHorizontalAlignment: function () { + return this._labelRenderer.getHorizontalAlignment(); + }, + + /** + * Sets Vertical Alignment of label + * @param {cc.VERTICAL_TEXT_ALIGNMENT_TOP|cc.VERTICAL_TEXT_ALIGNMENT_CENTER|cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM} alignment + */ + setTextVerticalAlignment: function (alignment) { + this._setTextVerticalAlignment(alignment); + this._updateContentSizeWithTextureSize(this._labelRenderer.getContentSize()); + }, + + _setTextVerticalAlignment: function (alignment) { + this._labelRenderer.setVerticalAlignment(alignment); + this._labelRendererAdaptDirty = true; + }, + /** + * Gets text vertical alignment. + * @returns {VERTICAL_TEXT_ALIGNMENT_TOP|VERTICAL_TEXT_ALIGNMENT_CENTER|VERTICAL_TEXT_ALIGNMENT_BOTTOM} + */ + getTextVerticalAlignment: function () { + return this._labelRenderer.getVerticalAlignment(); + }, + + /** + * Sets the touch scale enabled of label. + * @param {Boolean} enable + */ + setTouchScaleChangeEnabled: function (enable) { + this._touchScaleChangeEnabled = enable; + }, + + /** + * Gets the touch scale enabled of label. + * @returns {Boolean} + */ + isTouchScaleChangeEnabled: function () { + return this._touchScaleChangeEnabled; + }, + + _onPressStateChangedToNormal: function () { + if (!this._touchScaleChangeEnabled) + return; + this._labelRenderer.setScaleX(this._normalScaleValueX); + this._labelRenderer.setScaleY(this._normalScaleValueY); + }, + + _onPressStateChangedToPressed: function () { + if (!this._touchScaleChangeEnabled) + return; + this._labelRenderer.setScaleX(this._normalScaleValueX + this._onSelectedScaleOffset); + this._labelRenderer.setScaleY(this._normalScaleValueY + this._onSelectedScaleOffset); + }, + + _onPressStateChangedToDisabled: function () { + }, + + _onSizeChanged: function () { + ccui.Widget.prototype._onSizeChanged.call(this); + this._labelRendererAdaptDirty = true; + }, + + _adaptRenderers: function () { + if (this._labelRendererAdaptDirty) { + this._labelScaleChangedWithSize(); + this._labelRendererAdaptDirty = false; + } + }, + + /** + * Returns the renderer's content size. + * @override + * @returns {cc.Size} + */ + getVirtualRendererSize: function () { + return this._labelRenderer.getContentSize(); + }, + + /** + * Returns the renderer of ccui.Text. + * @returns {cc.Node} + */ + getVirtualRenderer: function () { + return this._labelRenderer; + }, + + //@since v3.3 + getAutoRenderSize: function () { + var virtualSize = this._labelRenderer.getContentSize(); + if (!this._ignoreSize) { + this._labelRenderer.setDimensions(0, 0); + virtualSize = this._labelRenderer.getContentSize(); + this._labelRenderer.setDimensions(this._contentSize.width, this._contentSize.height); + } + return virtualSize; + }, + + _labelScaleChangedWithSize: function () { + var locContentSize = this._contentSize; + if (this._ignoreSize) { + this._labelRenderer.setScale(1.0); + this._normalScaleValueX = this._normalScaleValueY = 1; + } else { + this._labelRenderer.setDimensions(cc.size(locContentSize.width, locContentSize.height)); + var textureSize = this._labelRenderer.getContentSize(); + if (textureSize.width <= 0.0 || textureSize.height <= 0.0) { + this._labelRenderer.setScale(1.0); + return; + } + var scaleX = locContentSize.width / textureSize.width; + var scaleY = locContentSize.height / textureSize.height; + this._labelRenderer.setScaleX(scaleX); + this._labelRenderer.setScaleY(scaleY); + this._normalScaleValueX = scaleX; + this._normalScaleValueY = scaleY; + } + this._labelRenderer.setPosition(locContentSize.width / 2.0, locContentSize.height / 2.0); + }, + + /** + * Returns the "class name" of ccui.Text. + * @returns {string} + */ + getDescription: function () { + return "Label"; + }, + + /** + * Enables shadow style and sets color, offset and blur radius styles. + * @param {cc.Color} shadowColor + * @param {cc.Size} offset + * @param {Number} blurRadius + */ + enableShadow: function (shadowColor, offset, blurRadius) { + this._labelRenderer.enableShadow(shadowColor, offset, blurRadius); + }, + + /** + * Enables outline style and sets outline's color and size. + * @param {cc.Color} outlineColor + * @param {cc.Size} outlineSize + */ + enableOutline: function (outlineColor, outlineSize) { + this._labelRenderer.enableStroke(outlineColor, outlineSize); + }, + + /** + * Enables glow color + * @param glowColor + */ + enableGlow: function (glowColor) { + if (this._type === ccui.Text.Type.TTF) + this._labelRenderer.enableGlow(glowColor); + }, + + /** + * Disables renderer's effect. + */ + disableEffect: function () { + if (this._labelRenderer.disableEffect) + this._labelRenderer.disableEffect(); + }, + + _createCloneInstance: function () { + return new ccui.Text(); + }, + + _copySpecialProperties: function (uiLabel) { + if (uiLabel instanceof ccui.Text) { + this.setFontName(uiLabel._fontName); + this.setFontSize(uiLabel.getFontSize()); + this.setString(uiLabel.getString()); + this.setTouchScaleChangeEnabled(uiLabel.touchScaleEnabled); + this.setTextAreaSize(uiLabel._textAreaSize); + this.setTextHorizontalAlignment(uiLabel._labelRenderer.getHorizontalAlignment()); + this.setTextVerticalAlignment(uiLabel._labelRenderer.getVerticalAlignment()); + this.setContentSize(uiLabel.getContentSize()); + this.setTextColor(uiLabel.getTextColor()); + } + }, + + _setBoundingWidth: function (value) { + this._textAreaSize.width = value; + this._labelRenderer._setBoundingWidth(value); + this._labelScaleChangedWithSize(); + }, + _setBoundingHeight: function (value) { + this._textAreaSize.height = value; + this._labelRenderer._setBoundingHeight(value); + this._labelScaleChangedWithSize(); + }, + _getBoundingWidth: function () { + return this._textAreaSize.width; + }, + _getBoundingHeight: function () { + return this._textAreaSize.height; + }, + + _changePosition: function () { + this._adaptRenderers(); + }, + + setColor: function (color) { + cc.ProtectedNode.prototype.setColor.call(this, color); + this._labelRenderer.setColor(color); + }, + + setTextColor: function (color) { + this._labelRenderer.setFontFillColor(color); + }, + + getTextColor: function () { + return this._labelRenderer._getFillStyle(); + } +}); + +var _p = ccui.Text.prototype; + +// Extended properties +/** @expose */ +_p.boundingWidth; +cc.defineGetterSetter(_p, "boundingWidth", _p._getBoundingWidth, _p._setBoundingWidth); +/** @expose */ +_p.boundingHeight; +cc.defineGetterSetter(_p, "boundingHeight", _p._getBoundingHeight, _p._setBoundingHeight); +/** @expose */ +_p.string; +cc.defineGetterSetter(_p, "string", _p.getString, _p.setString); +/** @expose */ +_p.stringLength; +cc.defineGetterSetter(_p, "stringLength", _p.getStringLength); +/** @expose */ +_p.font; +cc.defineGetterSetter(_p, "font", _p._getFont, _p._setFont); +/** @expose */ +_p.fontSize; +cc.defineGetterSetter(_p, "fontSize", _p.getFontSize, _p.setFontSize); +/** @expose */ +_p.fontName; +cc.defineGetterSetter(_p, "fontName", _p.getFontName, _p.setFontName); +/** @expose */ +_p.textAlign; +cc.defineGetterSetter(_p, "textAlign", _p.getTextHorizontalAlignment, _p.setTextHorizontalAlignment); +/** @expose */ +_p.verticalAlign; +cc.defineGetterSetter(_p, "verticalAlign", _p.getTextVerticalAlignment, _p.setTextVerticalAlignment); + +_p = null; + +/** + * allocates and initializes a UILabel. + * @deprecated since v3.0, please use new ccui.Text() instead. + * @return {ccui.Text} + */ +ccui.Label = ccui.Text.create = function (textContent, fontName, fontSize) { + return new ccui.Text(textContent, fontName, fontSize); +}; + +/** + * The zOrder value of ccui.Text's renderer. + * @constant + * @type {number} + */ +ccui.Text.RENDERER_ZORDER = -1; + +/** + * @ignore + */ +ccui.Text.Type = { + SYSTEM: 0, + TTF: 1 +}; diff --git a/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UITextAtlas.js b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UITextAtlas.js new file mode 100644 index 0000000..98c4b89 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UITextAtlas.js @@ -0,0 +1,236 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The text atlas control of Cocos UI. + * @class + * @extends ccui.Widget + * + * @property {String} string - Content string of the label + */ +ccui.TextAtlas = ccui.Widget.extend(/** @lends ccui.TextAtlas# */{ + _labelAtlasRenderer: null, + _stringValue: "", + _charMapFileName: "", + _itemWidth: 0, + _itemHeight: 0, + _startCharMap: "", + _className: "TextAtlas", + _labelAtlasRendererAdaptDirty: null, + + /** + * Allocates and initializes a UILabelAtlas.
+ * Constructor of ccui.TextAtlas, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {String} stringValue + * @param {String} charMapFile + * @param {number} itemWidth + * @param {number} itemHeight + * @param {String} startCharMap + * @example + * // example + * var uiLabelAtlas = new ccui.TextAtlas(); + */ + ctor: function (stringValue, charMapFile, itemWidth, itemHeight, startCharMap) { + ccui.Widget.prototype.ctor.call(this); + if (startCharMap !== undefined) { + this.setProperty(stringValue, charMapFile, itemWidth, itemHeight, startCharMap); + } + }, + + _initRenderer: function () { + this._labelAtlasRenderer = new cc.LabelAtlas(); + this._labelAtlasRenderer.setAnchorPoint(cc.p(0.5, 0.5)); + this.addProtectedChild(this._labelAtlasRenderer, ccui.TextAtlas.RENDERER_ZORDER, -1); + + this._labelAtlasRenderer.addEventListener('load', function () { + this._updateContentSizeWithTextureSize(this._labelAtlasRenderer.getContentSize()); + this._findLayout(); + }, this); + }, + + /** + * initializes the UILabelAtlas with a string, a char map file(the atlas), the width and height of each element and the starting char of the atlas + * @param {String} stringValue + * @param {String} charMapFile + * @param {number} itemWidth + * @param {number} itemHeight + * @param {String} startCharMap + */ + setProperty: function (stringValue, charMapFile, itemWidth, itemHeight, startCharMap) { + this._stringValue = stringValue; + this._charMapFileName = charMapFile; + this._itemWidth = itemWidth; + this._itemHeight = itemHeight; + this._startCharMap = startCharMap; + + this._labelAtlasRenderer.initWithString( + stringValue, + this._charMapFileName, + this._itemWidth, + this._itemHeight, + this._startCharMap[0] + ); + + this._updateContentSizeWithTextureSize(this._labelAtlasRenderer.getContentSize()); + this._labelAtlasRendererAdaptDirty = true; + }, + + /** + * Sets string value for ui text atlas. + * @param {String} value + */ + setString: function (value) { + if (value === this._labelAtlasRenderer.getString()) + return; + this._stringValue = value; + this._labelAtlasRenderer.setString(value); + this._updateContentSizeWithTextureSize(this._labelAtlasRenderer.getContentSize()); + this._labelAtlasRendererAdaptDirty = true; + }, + + /** + * Sets string value for text atlas. + * @deprecated since v3.0, please use setString instead. + * @param {String} value + */ + setStringValue: function (value) { + cc.log("Please use the setString"); + this.setString(value); + }, + + /** + * get string value for text atlas. + * @deprecated since v3.0, please use getString instead. + * @returns {String} + */ + getStringValue: function () { + cc.log("Please use the getString"); + return this.getString(); + }, + + /** + * get string value for ui text atlas. + * @returns {String} + */ + getString: function () { + return this._labelAtlasRenderer.getString(); + }, + + /** + * Returns the length of string. + * @returns {*|Number|long|int} + */ + getStringLength: function () { + return this._labelAtlasRenderer.getStringLength(); + }, + + _onSizeChanged: function () { + ccui.Widget.prototype._onSizeChanged.call(this); + this._labelAtlasRendererAdaptDirty = true; + }, + + _adaptRenderers: function () { + if (this._labelAtlasRendererAdaptDirty) { + this._labelAtlasScaleChangedWithSize(); + this._labelAtlasRendererAdaptDirty = false; + } + }, + + /** + * Returns the renderer's content size + * @overrider + * @returns {cc.Size} + */ + getVirtualRendererSize: function () { + return this._labelAtlasRenderer.getContentSize(); + }, + + /** + * Returns the renderer of ccui.TextAtlas. + * @returns {cc.Node} + */ + getVirtualRenderer: function () { + return this._labelAtlasRenderer; + }, + + _labelAtlasScaleChangedWithSize: function () { + var locRenderer = this._labelAtlasRenderer; + if (this._ignoreSize) { + locRenderer.setScale(1.0); + } else { + var textureSize = locRenderer.getContentSize(); + if (textureSize.width <= 0.0 || textureSize.height <= 0.0) { + locRenderer.setScale(1.0); + return; + } + locRenderer.setScaleX(this._contentSize.width / textureSize.width); + locRenderer.setScaleY(this._contentSize.height / textureSize.height); + } + locRenderer.setPosition(this._contentSize.width / 2.0, this._contentSize.height / 2.0); + }, + + /** + * Returns the "class name" of ccui.TextAtlas. + * @returns {string} + */ + getDescription: function () { + return "LabelAtlas"; + }, + + _copySpecialProperties: function (labelAtlas) { + if (labelAtlas) { + this.setProperty(labelAtlas._stringValue, labelAtlas._charMapFileName, labelAtlas._itemWidth, labelAtlas._itemHeight, labelAtlas._startCharMap); + } + }, + + _createCloneInstance: function () { + return new ccui.TextAtlas(); + } +}); + +var _p = ccui.TextAtlas.prototype; + +// Extended properties +/** @expose */ +_p.string; +cc.defineGetterSetter(_p, "string", _p.getString, _p.setString); + +_p = null; + +/** + * allocates and initializes a UILabelAtlas. + * @deprecated since v3.0, please use new ccui.TextAtlas() instead. + * @return {ccui.TextAtlas} + */ +ccui.TextAtlas.create = function (stringValue, charMapFile, itemWidth, itemHeight, startCharMap) { + return new ccui.TextAtlas(stringValue, charMapFile, itemWidth, itemHeight, startCharMap); +}; + +// Constants +/** + * The zOrder value of ccui.TextAtlas's renderer. + * @type {number} + */ +ccui.TextAtlas.RENDERER_ZORDER = -1; diff --git a/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UITextBMFont.js b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UITextBMFont.js new file mode 100644 index 0000000..25c9231 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UITextBMFont.js @@ -0,0 +1,224 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The TextBMFont control of Cocos UI, it rendered by LabelBMFont. + * @class + * @extends ccui.Widget + * + * @property {String} string - Content string of the label + */ +ccui.LabelBMFont = ccui.TextBMFont = ccui.Widget.extend(/** @lends ccui.TextBMFont# */{ + _labelBMFontRenderer: null, + _fntFileHasInit: false, + _fntFileName: "", + _stringValue: "", + _className: "TextBMFont", + _labelBMFontRendererAdaptDirty: true, + + /** + * Allocates and initializes a TextBMFont.
+ * Constructor of ccui.TextBMFont. override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {String} text + * @param {String} filename + * @example + * // example + * var uiLabelBMFont = new ccui.TextBMFont(); + */ + ctor: function (text, filename) { + ccui.Widget.prototype.ctor.call(this); + this._loader = new cc.Sprite.LoadManager(); + + if (filename !== undefined) { + this.setFntFile(filename); + this.setString(text); + } + }, + + _initRenderer: function () { + this._labelBMFontRenderer = new cc.LabelBMFont(); + this.addProtectedChild(this._labelBMFontRenderer, ccui.TextBMFont.RENDERER_ZORDER, -1); + }, + + /** + * Initializes a bitmap font atlas with an initial string and the FNT file + * @param {String} fileName + */ + setFntFile: function (fileName) { + if (!fileName) + return; + this._fntFileName = fileName; + + this._fntFileHasInit = true; + this._labelBMFontRenderer.initWithString(this._stringValue, fileName); + this._updateContentSizeWithTextureSize(this._labelBMFontRenderer.getContentSize()); + this._labelBMFontRendererAdaptDirty = true; + + var _self = this; + var locRenderer = _self._labelBMFontRenderer; + if (!locRenderer._textureLoaded) { + locRenderer.addEventListener("load", function () { + _self.setFntFile(_self._fntFileName); + }); + } + }, + + /** + * Sets string value for TextBMFont + * @deprecated since v3.0, please use setString instead. + * @param {String} value + */ + setText: function (value) { + cc.log("Please use the setString"); + this.setString(value); + }, + + /** + * Sets string value for TextBMFont + * @param {String} value + */ + setString: function (value) { + this._loader.clear(); + if (!this._labelBMFontRenderer._textureLoaded) { + this._loader.add(this._labelBMFontRenderer, function () { + this.setString(value); + }, this); + return; + } + if (value === this._labelBMFontRenderer.getString()) + return; + this._stringValue = value; + this._labelBMFontRenderer.setString(value); + if (!this._fntFileHasInit) + return; + this._updateContentSizeWithTextureSize(this._labelBMFontRenderer.getContentSize()); + this._labelBMFontRendererAdaptDirty = true; + }, + + /** + * Returns string value for TextBMFont. + * @returns {String} + */ + getString: function () { + return this._stringValue; + }, + + /** + * Returns the length of TextBMFont's string. + * @returns {Number} + */ + getStringLength: function () { + return this._labelBMFontRenderer.getStringLength(); + }, + + _onSizeChanged: function () { + ccui.Widget.prototype._onSizeChanged.call(this); + this._labelBMFontRendererAdaptDirty = true; + }, + + _adaptRenderers: function () { + if (this._labelBMFontRendererAdaptDirty) { + this._labelBMFontScaleChangedWithSize(); + this._labelBMFontRendererAdaptDirty = false; + } + }, + + /** + * Returns TextBMFont's content size + * @override + * @returns {cc.Size} + */ + getVirtualRendererSize: function () { + return this._labelBMFontRenderer.getContentSize(); + }, + + /** + * Returns the renderer of TextBMFont + * @override + * @returns {cc.Node} + */ + getVirtualRenderer: function () { + return this._labelBMFontRenderer; + }, + + _labelBMFontScaleChangedWithSize: function () { + var locRenderer = this._labelBMFontRenderer; + if (this._ignoreSize) + locRenderer.setScale(1.0); + else { + var textureSize = locRenderer.getContentSize(); + if (textureSize.width <= 0.0 || textureSize.height <= 0.0) { + locRenderer.setScale(1.0); + return; + } + locRenderer.setScaleX(this._contentSize.width / textureSize.width); + locRenderer.setScaleY(this._contentSize.height / textureSize.height); + } + locRenderer.setPosition(this._contentSize.width / 2.0, this._contentSize.height / 2.0); + }, + + /** + * Returns the "class name" of ccui.TextBMFont. + * @returns {string} + */ + getDescription: function () { + return "TextBMFont"; + }, + + _createCloneInstance: function () { + return new ccui.TextBMFont(); + }, + + _copySpecialProperties: function (labelBMFont) { + this.setFntFile(labelBMFont._fntFileName); + this.setString(labelBMFont._stringValue); + } +}); + +var _p = ccui.TextBMFont.prototype; + +// Extended properties +/** @expose */ +_p.string; +cc.defineGetterSetter(_p, "string", _p.getString, _p.setString); + +_p = null; + +/** + * allocates and initializes a UILabelBMFont. + * @deprecated since v3.0, please use new ccui.TextBMFont() instead. + * @return {ccui.TextBMFont} + */ +ccui.TextBMFont.create = function (text, filename) { + return new ccui.TextBMFont(text, filename); +}; + +// Constants +/** + * The zOrder value of TextBMFont's renderer. + * @constant + * @type {number} + */ +ccui.TextBMFont.RENDERER_ZORDER = -1; diff --git a/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UITextField.js b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UITextField.js new file mode 100644 index 0000000..7d59983 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UITextField.js @@ -0,0 +1,903 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * @ignore + */ +//it's a private class, it's a renderer of ccui.TextField. +ccui._TextFieldRenderer = cc.TextFieldTTF.extend({ + _maxLengthEnabled: false, + _maxLength: 0, + _passwordEnabled: false, + _passwordStyleText: "", + _attachWithIME: false, + _detachWithIME: false, + _insertText: false, + _deleteBackward: false, + _className: "_TextFieldRenderer", + + ctor: function () { + cc.TextFieldTTF.prototype.ctor.call(this); + this._maxLengthEnabled = false; + this._maxLength = 0; + this._passwordEnabled = false; + this._passwordStyleText = "*"; + this._attachWithIME = false; + this._detachWithIME = false; + this._insertText = false; + this._deleteBackward = false; + }, + + onEnter: function () { + cc.TextFieldTTF.prototype.onEnter.call(this); + cc.TextFieldTTF.prototype.setDelegate.call(this, this); + }, + + onTextFieldAttachWithIME: function (sender) { + this.setAttachWithIME(true); + return false; + }, + + onTextFieldInsertText: function (sender, text, len) { + if (len === 1 && text === "\n") + return false; + + this.setInsertText(true); + return (this._maxLengthEnabled) && (cc.TextFieldTTF.prototype.getCharCount.call(this) >= this._maxLength); + }, + + onTextFieldDeleteBackward: function (sender, delText, nLen) { + this.setDeleteBackward(true); + return false; + }, + + onTextFieldDetachWithIME: function (sender) { + this.setDetachWithIME(true); + return false; + }, + + insertText: function (text, len) { + var input_text = text; + + if (text !== "\n"){ + if (this._maxLengthEnabled){ + var text_count = this.getString().length; + if (text_count >= this._maxLength){ + // password + if (this._passwordEnabled) + this.setPasswordText(this.getString()); + return; + } + } + } + cc.TextFieldTTF.prototype.insertText.call(this, input_text, len); + + // password + if (this._passwordEnabled && cc.TextFieldTTF.prototype.getCharCount.call(this) > 0) + this.setPasswordText(this.getString()); + }, + + deleteBackward: function () { + cc.TextFieldTTF.prototype.deleteBackward.call(this); + + if (cc.TextFieldTTF.prototype.getCharCount.call(this) > 0 && this._passwordEnabled) + this.setPasswordText(this._inputText); + }, + + openIME: function () { + cc.TextFieldTTF.prototype.attachWithIME.call(this); + }, + + closeIME: function () { + cc.TextFieldTTF.prototype.detachWithIME.call(this); + }, + + setMaxLengthEnabled: function (enable) { + this._maxLengthEnabled = enable; + }, + + isMaxLengthEnabled: function () { + return this._maxLengthEnabled; + }, + + setMaxLength: function (length) { + this._maxLength = length; + }, + + getMaxLength: function () { + return this._maxLength; + }, + + getCharCount: function () { + return cc.TextFieldTTF.prototype.getCharCount.call(this); + }, + + setPasswordEnabled: function (enable) { + this._passwordEnabled = enable; + }, + + isPasswordEnabled: function () { + return this._passwordEnabled; + }, + + setPasswordStyleText: function (styleText) { + if (styleText.length > 1) + return; + var header = styleText.charCodeAt(0); + if (header < 33 || header > 126) + return; + this._passwordStyleText = styleText; + }, + + setPasswordText: function (text) { + var tempStr = ""; + var text_count = text.length; + var max = text_count; + + if (this._maxLengthEnabled && text_count > this._maxLength) + max = this._maxLength; + + for (var i = 0; i < max; ++i) + tempStr += this._passwordStyleText; + + cc.LabelTTF.prototype.setString.call(this, tempStr); + }, + + setAttachWithIME: function (attach) { + this._attachWithIME = attach; + }, + + getAttachWithIME: function () { + return this._attachWithIME; + }, + + setDetachWithIME: function (detach) { + this._detachWithIME = detach; + }, + + getDetachWithIME: function () { + return this._detachWithIME; + }, + + setInsertText: function (insert) { + this._insertText = insert; + }, + + getInsertText: function () { + return this._insertText; + }, + + setDeleteBackward: function (deleteBackward) { + this._deleteBackward = deleteBackward; + }, + + getDeleteBackward: function () { + return this._deleteBackward; + }, + + onDraw: function (sender) { + return false; + } +}); + +ccui._TextFieldRenderer.create = function (placeholder, fontName, fontSize) { + var ret = new ccui._TextFieldRenderer(); + if (ret && ret.initWithString("", fontName, fontSize)) { + if (placeholder) + ret.setPlaceHolder(placeholder); + return ret; + } + return null; +}; + +/** + * + * @class + * @extends ccui.Widget + * + * @property {String} string - The content string of the label + * @property {String} placeHolder - The place holder of the text field + * @property {String} font - The text field font with a style string: e.g. "18px Verdana" + * @property {String} fontName - The text field font name + * @property {Number} fontSize - The text field font size + * @property {Boolean} maxLengthEnabled - Indicate whether max length limit is enabled + * @property {Number} maxLength - The max length of the text field + * @property {Boolean} passwordEnabled - Indicate whether the text field is for entering password + */ +ccui.TextField = ccui.Widget.extend(/** @lends ccui.TextField# */{ + _textFieldRenderer: null, + _touchWidth: 0, + _touchHeight: 0, + _useTouchArea: false, + _textFieldEventListener: null, + _textFieldEventSelector: null, + _passwordStyleText: "", + _textFieldRendererAdaptDirty: true, + _fontName: "", + _fontSize: 12, + + _ccEventCallback: null, + + /** + * allocates and initializes a UITextField. + * Constructor of ccui.TextField. override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {string} placeholder + * @param {string} fontName + * @param {Number} fontSize + * @example + * // example + * var uiTextField = new ccui.TextField(); + */ + ctor: function (placeholder, fontName, fontSize) { + ccui.Widget.prototype.ctor.call(this); + this.setTouchEnabled(true); + if (fontName) + this.setFontName(fontName); + if (fontSize) + this.setFontSize(fontSize); + if (placeholder) + this.setPlaceHolder(placeholder); + }, + + /** + * Calls parent class' onEnter and schedules update function. + * @override + */ + onEnter: function () { + ccui.Widget.prototype.onEnter.call(this); + this.scheduleUpdate(); + }, + + _initRenderer: function () { + this._textFieldRenderer = ccui._TextFieldRenderer.create("input words here", "Thonburi", 20); + this.addProtectedChild(this._textFieldRenderer, ccui.TextField.RENDERER_ZORDER, -1); + }, + + /** + * Sets touch size of ccui.TextField. + * @param {cc.Size} size + */ + setTouchSize: function (size) { + this._touchWidth = size.width; + this._touchHeight = size.height; + }, + + /** + * Sets whether use touch area. + * @param enable + */ + setTouchAreaEnabled: function(enable){ + this._useTouchArea = enable; + }, + + /** + * Checks a point if is in ccui.TextField's space + * @param {cc.Point} pt + * @returns {boolean} + */ + hitTest: function(pt){ + if (this._useTouchArea) { + var nsp = this.convertToNodeSpace(pt); + var bb = cc.rect( + -this._touchWidth * this._anchorPoint.x, + -this._touchHeight * this._anchorPoint.y, + this._touchWidth, this._touchHeight + ); + + return ( nsp.x >= bb.x && nsp.x <= bb.x + bb.width && + nsp.y >= bb.y && nsp.y <= bb.y + bb.height ); + } else + return ccui.Widget.prototype.hitTest.call(this, pt); + }, + + /** + * Returns touch size of ccui.TextField. + * @returns {cc.Size} + */ + getTouchSize: function () { + return cc.size(this._touchWidth, this._touchHeight); + }, + + /** + * Changes the string value of textField. + * @deprecated since v3.0, please use setString instead. + * @param {String} text + */ + setText: function (text) { + cc.log("Please use the setString"); + this.setString(text); + }, + + /** + * Changes the string value of textField. + * @param {String} text + */ + setString: function (text) { + if (text == null) + return; + + text = String(text); + if (this.isMaxLengthEnabled()) + text = text.substr(0, this.getMaxLength()); + if (this.isPasswordEnabled()) { + this._textFieldRenderer.setPasswordText(text); + this._textFieldRenderer.setString(""); + this._textFieldRenderer.insertText(text, text.length); + } else + this._textFieldRenderer.setString(text); + this._textFieldRendererAdaptDirty = true; + this._updateContentSizeWithTextureSize(this._textFieldRenderer.getContentSize()); + }, + + /** + * Sets the placeholder string.
+ * display this string if string equal "". + * @param {String} value + */ + setPlaceHolder: function (value) { + this._textFieldRenderer.setPlaceHolder(value); + this._textFieldRendererAdaptDirty = true; + this._updateContentSizeWithTextureSize(this._textFieldRenderer.getContentSize()); + }, + + /** + * Returns the placeholder string. + * @returns {String} + */ + getPlaceHolder: function () { + return this._textFieldRenderer.getPlaceHolder(); + }, + + /** + * Returns the color of ccui.TextField's place holder. + * @returns {cc.Color} + */ + getPlaceHolderColor: function(){ + return this._textFieldRenderer.getPlaceHolderColor(); + }, + + /** + * Sets the place holder color to ccui.TextField. + * @param color + */ + setPlaceHolderColor: function(color){ + this._textFieldRenderer.setColorSpaceHolder(color); + }, + + /** + * Sets the text color to ccui.TextField + * @param textColor + */ + setTextColor: function(textColor){ + this._textFieldRenderer.setTextColor(textColor); + }, + + /** + * Sets font size for ccui.TextField. + * @param {Number} size + */ + setFontSize: function (size) { + this._textFieldRenderer.setFontSize(size); + this._fontSize = size; + this._textFieldRendererAdaptDirty = true; + this._updateContentSizeWithTextureSize(this._textFieldRenderer.getContentSize()); + }, + + /** + * Gets font size of ccui.TextField. + * @return {Number} size + */ + getFontSize: function () { + return this._fontSize; + }, + + /** + * Sets font name for ccui.TextField + * @param {String} name + */ + setFontName: function (name) { + this._textFieldRenderer.setFontName(name); + this._fontName = name; + this._textFieldRendererAdaptDirty = true; + this._updateContentSizeWithTextureSize(this._textFieldRenderer.getContentSize()); + }, + + /** + * Returns font name of ccui.TextField. + * @return {String} font name + */ + getFontName: function () { + return this._fontName; + }, + + /** + * detach with IME + */ + didNotSelectSelf: function () { + this._textFieldRenderer.detachWithIME(); + }, + + /** + * Returns textField string value + * @deprecated since v3.0, please use getString instead. + * @returns {String} + */ + getStringValue: function () { + cc.log("Please use the getString"); + return this.getString(); + }, + + /** + * Returns string value of ccui.TextField. + * @returns {String} + */ + getString: function () { + return this._textFieldRenderer.getString(); + }, + + /** + * Returns the length of ccui.TextField. + * @returns {Number} + */ + getStringLength: function(){ + return this._textFieldRenderer.getStringLength(); + }, + + /** + * The touch began event callback handler. + * @param {cc.Point} touchPoint + */ + onTouchBegan: function (touchPoint, unusedEvent) { + var self = this; + var pass = ccui.Widget.prototype.onTouchBegan.call(self, touchPoint, unusedEvent); + if (self._hit) { + setTimeout(function(){ + self._textFieldRenderer.attachWithIME(); + }, 0); + }else{ + setTimeout(function(){ + self._textFieldRenderer.detachWithIME(); + }, 0); + } + return pass; + }, + + /** + * Sets Whether to open string length limit for ccui.TextField. + * @param {Boolean} enable + */ + setMaxLengthEnabled: function (enable) { + this._textFieldRenderer.setMaxLengthEnabled(enable); + }, + + /** + * Returns Whether to open string length limit. + * @returns {Boolean} + */ + isMaxLengthEnabled: function () { + return this._textFieldRenderer.isMaxLengthEnabled(); + }, + + /** + * Sets the max length of ccui.TextField. Only when you turn on the string length limit, it is valid. + * @param {number} length + */ + setMaxLength: function (length) { + this._textFieldRenderer.setMaxLength(length); + this.setString(this.getString()); + }, + + /** + * Returns the max length of ccui.TextField. + * @returns {number} length + */ + getMaxLength: function () { + return this._textFieldRenderer.getMaxLength(); + }, + + /** + * Sets whether to open setting string as password character. + * @param {Boolean} enable + */ + setPasswordEnabled: function (enable) { + this._textFieldRenderer.setPasswordEnabled(enable); + }, + + /** + * Returns whether to open setting string as password character. + * @returns {Boolean} + */ + isPasswordEnabled: function () { + return this._textFieldRenderer.isPasswordEnabled(); + }, + + /** + * Sets the password style character, Only when you turn on setting string as password character, it is valid. + * @param styleText + */ + setPasswordStyleText: function(styleText){ + this._textFieldRenderer.setPasswordStyleText(styleText); + this._passwordStyleText = styleText; + + this.setString(this.getString()); + }, + + /** + * Returns the password style character. + * @returns {String} + */ + getPasswordStyleText: function () { + return this._passwordStyleText; + }, + + update: function (dt) { + if (this.getDetachWithIME()) { + this._detachWithIMEEvent(); + this.setDetachWithIME(false); + } + if (this.getAttachWithIME()) { + this._attachWithIMEEvent(); + this.setAttachWithIME(false); + } + if (this.getInsertText()) { + this._textFieldRendererAdaptDirty = true; + this._updateContentSizeWithTextureSize(this._textFieldRenderer.getContentSize()); + + this._insertTextEvent(); + this.setInsertText(false); + } + if (this.getDeleteBackward()) { + this._textFieldRendererAdaptDirty = true; + this._updateContentSizeWithTextureSize(this._textFieldRenderer.getContentSize()); + + this._deleteBackwardEvent(); + this.setDeleteBackward(false); + } + }, + + /** + * Returns whether attach with IME. + * @returns {Boolean} + */ + getAttachWithIME: function () { + return this._textFieldRenderer.getAttachWithIME(); + }, + + /** + * Sets attach with IME. + * @param {Boolean} attach + */ + setAttachWithIME: function (attach) { + this._textFieldRenderer.setAttachWithIME(attach); + }, + + /** + * Returns whether detach with IME. + * @returns {Boolean} + */ + getDetachWithIME: function () { + return this._textFieldRenderer.getDetachWithIME(); + }, + + /** + * Sets detach with IME. + * @param {Boolean} detach + */ + setDetachWithIME: function (detach) { + this._textFieldRenderer.setDetachWithIME(detach); + }, + + /** + * Returns insertText string of ccui.TextField. + * @returns {String} + */ + getInsertText: function () { + return this._textFieldRenderer.getInsertText(); + }, + + /** + * Sets insertText string to ccui.TextField. + * @param {String} insertText + */ + setInsertText: function (insertText) { + this._textFieldRenderer.setInsertText(insertText); + }, + + /** + * Returns the delete backward of ccui.TextField. + * @returns {Boolean} + */ + getDeleteBackward: function () { + return this._textFieldRenderer.getDeleteBackward(); + }, + + /** + * Sets the delete backward of ccui.TextField. + * @param {Boolean} deleteBackward + */ + setDeleteBackward: function (deleteBackward) { + this._textFieldRenderer.setDeleteBackward(deleteBackward); + }, + + _attachWithIMEEvent: function () { + if(this._textFieldEventSelector){ + if (this._textFieldEventListener) + this._textFieldEventSelector.call(this._textFieldEventListener, this, ccui.TextField.EVENT_ATTACH_WITH_IME); + else + this._textFieldEventSelector(this, ccui.TextField.EVENT_ATTACH_WITH_IME); + } + if (this._ccEventCallback){ + this._ccEventCallback(this, ccui.TextField.EVENT_ATTACH_WITH_IME); + } + }, + + _detachWithIMEEvent: function () { + if(this._textFieldEventSelector){ + if (this._textFieldEventListener) + this._textFieldEventSelector.call(this._textFieldEventListener, this, ccui.TextField.EVENT_DETACH_WITH_IME); + else + this._textFieldEventSelector(this, ccui.TextField.EVENT_DETACH_WITH_IME); + } + if (this._ccEventCallback) + this._ccEventCallback(this, ccui.TextField.EVENT_DETACH_WITH_IME); + }, + + _insertTextEvent: function () { + if(this._textFieldEventSelector){ + if (this._textFieldEventListener) + this._textFieldEventSelector.call(this._textFieldEventListener, this, ccui.TextField.EVENT_INSERT_TEXT); + else + this._textFieldEventSelector(this, ccui.TextField.EVENT_INSERT_TEXT); //eventCallback + } + if (this._ccEventCallback) + this._ccEventCallback(this, ccui.TextField.EVENT_INSERT_TEXT); + }, + + _deleteBackwardEvent: function () { + if(this._textFieldEventSelector){ + if (this._textFieldEventListener) + this._textFieldEventSelector.call(this._textFieldEventListener, this, ccui.TextField.EVENT_DELETE_BACKWARD); + else + this._textFieldEventSelector(this, ccui.TextField.EVENT_DELETE_BACKWARD); //eventCallback + } + if (this._ccEventCallback) + this._ccEventCallback(this, ccui.TextField.EVENT_DELETE_BACKWARD); + }, + + /** + * Adds event listener to cuci.TextField. + * @param {Object} [target=] + * @param {Function} selector + * @deprecated since v3.0, please use addEventListener instead. + */ + addEventListenerTextField: function (selector, target) { + this.addEventListener(selector, target); + }, + + /** + * Adds event listener callback. + * @param {Object} [target=] + * @param {Function} selector + */ + addEventListener: function(selector, target){ + this._textFieldEventSelector = selector; //when target is undefined, _textFieldEventSelector is ccEventCallback. + this._textFieldEventListener = target; + }, + + _onSizeChanged: function () { + ccui.Widget.prototype._onSizeChanged.call(this); + this._textFieldRendererAdaptDirty = true; + }, + + _adaptRenderers: function(){ + if (this._textFieldRendererAdaptDirty) { + this._textfieldRendererScaleChangedWithSize(); + this._textFieldRendererAdaptDirty = false; + } + }, + + _textfieldRendererScaleChangedWithSize: function () { + if (!this._ignoreSize) + this._textFieldRenderer.setDimensions(this._contentSize); + this._textFieldRenderer.setPosition(this._contentSize.width / 2, this._contentSize.height / 2); + }, + + //@since v3.3 + getAutoRenderSize: function(){ + var virtualSize = this._textFieldRenderer.getContentSize(); + if (!this._ignoreSize) { + this._textFieldRenderer.setDimensions(0, 0); + virtualSize = this._textFieldRenderer.getContentSize(); + this._textFieldRenderer.setDimensions(this._contentSize.width, this._contentSize.height); + } + return virtualSize; + }, + + /** + * Returns the ccui.TextField's content size. + * @returns {cc.Size} + */ + getVirtualRendererSize: function(){ + return this._textFieldRenderer.getContentSize(); + }, + + /** + * Returns the renderer of ccui.TextField. + * @returns {cc.Node} + */ + getVirtualRenderer: function () { + return this._textFieldRenderer; + }, + + /** + * Returns the "class name" of ccui.TextField. + * @returns {string} + */ + getDescription: function () { + return "TextField"; + }, + + /** + * Open keyboard and receive input text. + * @return {Boolean} + */ + attachWithIME: function () { + this._textFieldRenderer.attachWithIME(); + }, + + _createCloneInstance: function () { + return new ccui.TextField(); + }, + + _copySpecialProperties: function (textField) { + this.setString(textField._textFieldRenderer.getString()); + this.setPlaceHolder(textField.getString()); + this.setFontSize(textField._textFieldRenderer.getFontSize()); + this.setFontName(textField._textFieldRenderer.getFontName()); + this.setMaxLengthEnabled(textField.isMaxLengthEnabled()); + this.setMaxLength(textField.getMaxLength()); + this.setPasswordEnabled(textField.isPasswordEnabled()); + this.setPasswordStyleText(textField._passwordStyleText); + this.setAttachWithIME(textField.getAttachWithIME()); + this.setDetachWithIME(textField.getDetachWithIME()); + this.setInsertText(textField.getInsertText()); + this.setDeleteBackward(textField.getDeleteBackward()); + this._ccEventCallback = textField._ccEventCallback; + this._textFieldEventListener = textField._textFieldEventListener; + this._textFieldEventSelector = textField._textFieldEventSelector; + }, + + /** + * Sets the text area size to ccui.TextField. + * @param {cc.Size} size + */ + setTextAreaSize: function(size){ + this.setContentSize(size); + }, + + /** + * Sets the text horizontal alignment of ccui.TextField. + * @param alignment + */ + setTextHorizontalAlignment: function(alignment){ + this._textFieldRenderer.setHorizontalAlignment(alignment); + }, + + /** + * Sets the text vertical alignment of ccui.TextField. + * @param alignment + */ + setTextVerticalAlignment: function(alignment){ + this._textFieldRenderer.setVerticalAlignment(alignment); + }, + _setFont: function (font) { + this._textFieldRenderer._setFont(font); + this._textFieldRendererAdaptDirty = true; + }, + + _getFont: function () { + return this._textFieldRenderer._getFont(); + }, + + _changePosition: function(){ + this._adaptRenderers(); + } +}); + +/** + * Creates a ccui.TextField. + * @deprecated since v3.0, please use new ccui.TextField() instead. + * @param {String} placeholder + * @param {String} fontName + * @param {Number} fontSize + * @returns {ccui.TextField} + */ +ccui.TextField.create = function(placeholder, fontName, fontSize){ + return new ccui.TextField(placeholder, fontName, fontSize); +}; + +var _p = ccui.TextField.prototype; + +// Extended properties +/** @expose */ +_p.string; +cc.defineGetterSetter(_p, "string", _p.getString, _p.setString); +/** @expose */ +_p.placeHolder; +cc.defineGetterSetter(_p, "placeHolder", _p.getPlaceHolder, _p.setPlaceHolder); +/** @expose */ +_p.font; +cc.defineGetterSetter(_p, "font", _p._getFont, _p._setFont); +/** @expose */ +_p.fontSize; +cc.defineGetterSetter(_p, "fontSize", _p.getFontSize, _p.setFontSize); +/** @expose */ +_p.fontName; +cc.defineGetterSetter(_p, "fontName", _p.getFontName, _p.setFontName); +/** @expose */ +_p.maxLengthEnabled; +cc.defineGetterSetter(_p, "maxLengthEnabled", _p.isMaxLengthEnabled, _p.setMaxLengthEnabled); +/** @expose */ +_p.maxLength; +cc.defineGetterSetter(_p, "maxLength", _p.getMaxLength, _p.setMaxLength); +/** @expose */ +_p.passwordEnabled; +cc.defineGetterSetter(_p, "passwordEnabled", _p.isPasswordEnabled, _p.setPasswordEnabled); + +_p = null; + +// Constants +//TextField event +/** + * The attach with IME event flag of ccui.TextField + * @constant + * @type {number} + */ +ccui.TextField.EVENT_ATTACH_WITH_IME = 0; +/** + * The detach with IME event flag of ccui.TextField + * @constant + * @type {number} + */ +ccui.TextField.EVENT_DETACH_WITH_IME = 1; +/** + * The insert text event flag of ccui.TextField + * @constant + * @type {number} + */ +ccui.TextField.EVENT_INSERT_TEXT = 2; +/** + * The delete backward event flag of ccui.TextField + * @constant + * @type {number} + */ +ccui.TextField.EVENT_DELETE_BACKWARD = 3; + +/** + * The zOrder value of ccui.TextField's renderer. + * @constant + * @type {number} + */ +ccui.TextField.RENDERER_ZORDER = -1; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UIVideoPlayer.js b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UIVideoPlayer.js new file mode 100644 index 0000000..e4e3351 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UIVideoPlayer.js @@ -0,0 +1,562 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * @class + * @extends ccui.Widget + * @brief Displays a video file. + * + * @note VideoPlayer displays a video file based on DOM element + * VideoPlayer will be rendered above all other graphical elements. + * + * @property {String} path - The video path + */ +ccui.VideoPlayer = ccui.Widget.extend(/** @lends ccui.VideoPlayer# */{ + + _played: false, + _playing: false, + _stopped: true, + + ctor: function (path) { + ccui.Widget.prototype.ctor.call(this); + this._EventList = {}; + if (path) + this.setURL(path); + }, + + _createRenderCmd: function () { + return new ccui.VideoPlayer.RenderCmd(this); + }, + + visit: function () { + var cmd = this._renderCmd, + div = cmd._div, + container = cc.container, + eventManager = cc.eventManager; + if (this._visible) { + container.appendChild(cmd._video); + if (this._listener === null) + this._listener = cc.eventManager.addCustomListener(cc.game.EVENT_RESIZE, function () { + cmd.resize(); + }); + } else { + var hasChild = false; + if ('contains' in container) { + hasChild = container.contains(cmd._video); + } else { + hasChild = container.compareDocumentPosition(cmd._video) % 16; + } + if (hasChild) + container.removeChild(cmd._video); + eventManager.removeListener(cmd._listener); + cmd._listener = null; + } + cmd.updateStatus(); + cmd.resize(); + }, + + /** + * Set the video address + * Automatically replace extname + * All supported video formats will be added to the video + * @param {String} address + */ + setURL: function (address) { + this._renderCmd.updateURL(address); + }, + + /** + * Get the video path + * @returns {String} + */ + getURL: function () { + return this._renderCmd._url; + }, + + /** + * Play the video + */ + play: function () { + var self = this, + video = this._renderCmd._video; + if (video) { + this._played = true; + video.pause(); + if (this._stopped !== false || this._playing !== false || this._played !== true) + video.currentTime = 0; + if (ccui.VideoPlayer._polyfill.autoplayAfterOperation) { + setTimeout(function () { + video.play(); + self._playing = true; + self._stopped = false; + }, 20); + } else { + video.play(); + this._playing = true; + this._stopped = false; + } + } + }, + + /** + * Pause the video + */ + pause: function () { + var video = this._renderCmd._video; + if (video && this._playing === true && this._stopped === false) { + video.pause(); + this._playing = false; + } + }, + + /** + * Resume the video + */ + resume: function () { + if (this._stopped === false && this._playing === false && this._played === true) { + this.play(); + } + }, + + /** + * Stop the video + */ + stop: function () { + var self = this, + video = this._renderCmd._video; + if (video) { + video.pause(); + video.currentTime = 0; + this._playing = false; + this._stopped = true; + } + + setTimeout(function () { + self._dispatchEvent(ccui.VideoPlayer.EventType.STOPPED); + }, 0); + }, + /** + * Jump to the specified point in time + * @param {Number} sec + */ + seekTo: function (sec) { + var video = this._renderCmd._video; + if (video) { + video.currentTime = sec; + if (ccui.VideoPlayer._polyfill.autoplayAfterOperation && this.isPlaying()) { + setTimeout(function () { + video.play(); + }, 20); + } + } + }, + + /** + * Whether the video is playing + * @returns {boolean} + */ + isPlaying: function () { + if (ccui.VideoPlayer._polyfill.autoplayAfterOperation && this._playing) { + setTimeout(function () { + video.play(); + }, 20); + } + return this._playing; + }, + + /** + * Whether to keep the aspect ratio + */ + setKeepAspectRatioEnabled: function (enable) { + cc.log("On the web is always keep the aspect ratio"); + }, + isKeepAspectRatioEnabled: function () { + return false; + }, + + /** + * Set whether the full screen + * May appear inconsistent in different browsers + * @param {boolean} enable + */ + setFullScreenEnabled: function (enable) { + var video = this._renderCmd._video; + if (video) { + if (enable) + cc.screen.requestFullScreen(video); + else + cc.screen.exitFullScreen(video); + } + }, + + /** + * Determine whether already full screen + */ + isFullScreenEnabled: function () { + cc.log("Can't know status"); + }, + + /** + * The binding event + * @param {ccui.VideoPlayer.EventType} event + * @param {Function} callback + */ + setEventListener: function (event, callback) { + this._EventList[event] = callback; + }, + + /** + * Delete events + * @param {ccui.VideoPlayer.EventType} event + */ + removeEventListener: function (event) { + this._EventList[event] = null; + }, + + _dispatchEvent: function (event) { + var callback = this._EventList[event]; + if (callback) + callback.call(this, this, this._renderCmd._video.src); + }, + + /** + * Trigger playing events + */ + onPlayEvent: function () { + var list = this._EventList[ccui.VideoPlayer.EventType.PLAYING]; + if (list) + for (var i = 0; i < list.length; i++) + list[i].call(this, this, this._renderCmd._video.src); + }, + + setContentSize: function (w, h) { + ccui.Widget.prototype.setContentSize.call(this, w, h); + if (h === undefined) { + h = w.height; + w = w.width; + } + this._renderCmd.changeSize(w, h); + }, + + cleanup: function () { + this._renderCmd.removeDom(); + this.stopAllActions(); + this.unscheduleAllCallbacks(); + }, + + onEnter: function () { + ccui.Widget.prototype.onEnter.call(this); + var list = ccui.VideoPlayer.elements; + if (list.indexOf(this) === -1) + list.push(this); + }, + + onExit: function () { + ccui.Widget.prototype.onExit.call(this); + var list = ccui.VideoPlayer.elements; + var index = list.indexOf(this); + if (index !== -1) + list.splice(index, 1); + } + +}); + +// VideoHTMLElement list +ccui.VideoPlayer.elements = []; +ccui.VideoPlayer.pauseElements = []; + +cc.eventManager.addCustomListener(cc.game.EVENT_HIDE, function () { + var list = ccui.VideoPlayer.elements; + for (var node, i = 0; i < list.length; i++) { + node = list[i]; + if (list[i]._playing) { + node.pause(); + ccui.VideoPlayer.pauseElements.push(node); + } + } +}); +cc.eventManager.addCustomListener(cc.game.EVENT_SHOW, function () { + var list = ccui.VideoPlayer.pauseElements; + var node = list.pop(); + while (node) { + node.play(); + node = list.pop(); + } +}); + +/** + * The VideoPlayer support list of events + * @type {{PLAYING: string, PAUSED: string, STOPPED: string, COMPLETED: string}} + */ +ccui.VideoPlayer.EventType = { + PLAYING: "play", + PAUSED: "pause", + STOPPED: "stop", + COMPLETED: "complete" +}; + +(function (video) { + /** + * Adapter various machines + * @devicePixelRatio Whether you need to consider devicePixelRatio calculated position + * @event To get the data using events + */ + video._polyfill = { + devicePixelRatio: false, + event: "canplay", + canPlayType: [] + }; + + (function () { + /** + * Some old browser only supports Theora encode video + * But native does not support this encode, + * so it is best to provide mp4 and webm or ogv file + */ + var dom = document.createElement("video"); + if (dom.canPlayType("video/ogg")) { + video._polyfill.canPlayType.push(".ogg"); + video._polyfill.canPlayType.push(".ogv"); + } + if (dom.canPlayType("video/mp4")) + video._polyfill.canPlayType.push(".mp4"); + if (dom.canPlayType("video/webm")) + video._polyfill.canPlayType.push(".webm"); + })(); + + if (cc.sys.OS_IOS === cc.sys.os) { + video._polyfill.devicePixelRatio = true; + video._polyfill.event = "progress"; + } + if (cc.sys.browserType === cc.sys.BROWSER_TYPE_FIREFOX) { + video._polyfill.autoplayAfterOperation = true; + } + + var style = document.createElement("style"); + style.innerHTML = ".cocosVideo:-moz-full-screen{transform:matrix(1,0,0,1,0,0) !important;}" + + ".cocosVideo:full-screen{transform:matrix(1,0,0,1,0,0) !important;}" + + ".cocosVideo:-webkit-full-screen{transform:matrix(1,0,0,1,0,0) !important;}"; + document.head.appendChild(style); + +})(ccui.VideoPlayer); + +(function (polyfill) { + + var RenderCmd = null; + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + RenderCmd = cc.Node.WebGLRenderCmd; + } else { + RenderCmd = cc.Node.CanvasRenderCmd; + } + + ccui.VideoPlayer.RenderCmd = function (node) { + this._rootCtor(node); + this._listener = null; + this._url = ""; + this.initStyle(); + }; + + var proto = ccui.VideoPlayer.RenderCmd.prototype = Object.create(RenderCmd.prototype); + proto.constructor = ccui.VideoPlayer.RenderCmd; + + proto.transform = function (parentCmd, recursive) { + this.originTransform(parentCmd, recursive); + this.updateMatrix(this._worldTransform, cc.view._scaleX, cc.view._scaleY); + }; + + proto.updateStatus = function () { + polyfill.devicePixelRatio = cc.view.isRetinaEnabled(); + var flags = cc.Node._dirtyFlags, locFlag = this._dirtyFlag; + if (locFlag & flags.transformDirty) { + //update the transform + this.transform(this.getParentRenderCmd(), true); + this.updateMatrix(this._worldTransform, cc.view._scaleX, cc.view._scaleY); + this._dirtyFlag = this._dirtyFlag & cc.Node._dirtyFlags.transformDirty ^ this._dirtyFlag; + } + + if (locFlag & flags.orderDirty) { + this._dirtyFlag = this._dirtyFlag & flags.orderDirty ^ this._dirtyFlag; + } + }; + + proto.resize = function (view) { + view = view || cc.view; + var node = this._node, + eventManager = cc.eventManager; + if (node._parent && node._visible) + this.updateMatrix(this._worldTransform, view._scaleX, view._scaleY); + else { + eventManager.removeListener(this._listener); + this._listener = null; + } + }; + + proto.updateMatrix = function (t, scaleX, scaleY) { + var node = this._node; + if (polyfill.devicePixelRatio) { + var dpr = cc.view.getDevicePixelRatio(); + scaleX = scaleX / dpr; + scaleY = scaleY / dpr; + } + if (this._loaded === false) return; + var containerStyle = cc.game.container.style, + offsetX = parseInt(containerStyle.paddingLeft), + offsetY = parseInt(containerStyle.paddingBottom), + cw = node._contentSize.width, + ch = node._contentSize.height; + var a = t.a * scaleX, + b = t.b, + c = t.c, + d = t.d * scaleY, + tx = offsetX + t.tx * scaleX - cw / 2 + cw * node._scaleX / 2 * scaleX, + ty = offsetY + t.ty * scaleY - ch / 2 + ch * node._scaleY / 2 * scaleY; + var matrix = "matrix(" + a + "," + b + "," + c + "," + d + "," + tx + "," + -ty + ")"; + this._video.style["transform"] = matrix; + this._video.style["-webkit-transform"] = matrix; + }; + + proto.updateURL = function (path) { + var source, video, hasChild, container, extname; + var node = this._node; + + if (this._url == path) + return; + + this._url = path; + + if (cc.loader.resPath && !/^http/.test(path)) + path = cc.path.join(cc.loader.resPath, path); + + hasChild = false; + container = cc.container; + if ('contains' in container) { + hasChild = container.contains(this._video); + } else { + hasChild = container.compareDocumentPosition(this._video) % 16; + } + if (hasChild) + container.removeChild(this._video); + + this._video = document.createElement("video"); + video = this._video; + this.bindEvent(); + var self = this; + + var cb = function () { + if (self._loaded == true) + return; + self._loaded = true; + self.changeSize(); + self.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + video.removeEventListener(polyfill.event, cb); + video.currentTime = 0; + video.style["visibility"] = "visible"; + //IOS does not display video images + video.play(); + if (!node._played) { + video.pause(); + video.currentTime = 0; + } + }; + video.addEventListener(polyfill.event, cb); + + //video.controls = "controls"; + video.preload = "metadata"; + video.style["visibility"] = "hidden"; + this._loaded = false; + node._played = false; + node._playing = false; + node._stopped = true; + this.initStyle(); + this._node.visit(); + + source = document.createElement("source"); + source.src = path; + video.appendChild(source); + + extname = cc.path.extname(path); + for (var i = 0; i < polyfill.canPlayType.length; i++) { + if (extname !== polyfill.canPlayType[i]) { + source = document.createElement("source"); + source.src = path.replace(extname, polyfill.canPlayType[i]); + video.appendChild(source); + } + } + }; + + proto.bindEvent = function () { + var self = this, + node = this._node, + video = this._video; + //binding event + video.addEventListener("ended", function () { + node._renderCmd.updateMatrix(self._worldTransform, cc.view._scaleX, cc.view._scaleY); + node._playing = false; + node._dispatchEvent(ccui.VideoPlayer.EventType.COMPLETED); + }); + video.addEventListener("play", function () { + node._dispatchEvent(ccui.VideoPlayer.EventType.PLAYING); + }); + video.addEventListener("pause", function () { + node._dispatchEvent(ccui.VideoPlayer.EventType.PAUSED); + }); + }; + + proto.initStyle = function () { + if (!this._video) return; + var video = this._video; + video.style.position = "absolute"; + video.style.bottom = "0px"; + video.style.left = "0px"; + video.className = "cocosVideo"; + }; + + proto.changeSize = function (w, h) { + var contentSize = this._node._contentSize; + w = w || contentSize.width; + h = h || contentSize.height; + var video = this._video; + if (video) { + if (w !== 0) + video.width = w; + if (h !== 0) + video.height = h; + } + }; + + proto.removeDom = function () { + var video = this._video; + if (video) { + var hasChild = false; + if ('contains' in cc.container) { + hasChild = cc.container.contains(video); + } else { + hasChild = cc.container.compareDocumentPosition(video) % 16; + } + if (hasChild) + cc.container.removeChild(video); + } + }; + +})(ccui.VideoPlayer._polyfill); diff --git a/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UIWebView.js b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UIWebView.js new file mode 100644 index 0000000..b301a95 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/UIWebView.js @@ -0,0 +1,423 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * @class + * @extends ccui.Widget + * @brief A View that displays web pages. + * + * @note WebView displays web pages based on DOM element + * WebView will be rendered above all other graphical elements. + * + * @property {String} path - The url to be shown in the web view + */ +ccui.WebView = ccui.Widget.extend(/** @lends ccui.WebView# */{ + + ctor: function (path) { + ccui.Widget.prototype.ctor.call(this); + this._EventList = {}; + if (path) + this.loadURL(path); + }, + + visit: function () { + var cmd = this._renderCmd, + div = cmd._div, + container = cc.container, + eventManager = cc.eventManager; + if (this._visible) { + container.appendChild(div); + if (this._listener === null) + this._listener = eventManager.addCustomListener(cc.game.EVENT_RESIZE, function () { + cmd.resize(); + }); + } else { + var hasChild = false; + if ('contains' in container) { + hasChild = container.contains(div); + } else { + hasChild = container.compareDocumentPosition(div) % 16; + } + if (hasChild) + container.removeChild(div); + var list = eventManager._listenersMap[cc.game.EVENT_RESIZE].getFixedPriorityListeners(); + eventManager._removeListenerInVector(list, cmd._listener); + cmd._listener = null; + } + cmd.updateStatus(); + cmd.resize(cc.view); + }, + + setJavascriptInterfaceScheme: function (scheme) { + }, + loadData: function (data, MIMEType, encoding, baseURL) { + }, + loadHTMLString: function (string, baseURL) { + }, + + + /** + * Load an URL + * @param {String} url + */ + loadURL: function (url) { + this._renderCmd.updateURL(url); + this._dispatchEvent(ccui.WebView.EventType.LOADING); + }, + + /** + * Stop loading + */ + stopLoading: function () { + cc.log("Web does not support loading"); + }, + + /** + * Reload the WebView + */ + reload: function () { + var iframe = this._renderCmd._iframe; + if (iframe) { + var win = iframe.contentWindow; + if (win && win.location) + win.location.reload(); + } + }, + + /** + * Determine whether to go back + */ + canGoBack: function () { + cc.log("Web does not support query history"); + return true; + }, + + /** + * Determine whether to go forward + */ + canGoForward: function () { + cc.log("Web does not support query history"); + return true; + }, + + /** + * go back + */ + goBack: function () { + try { + if (ccui.WebView._polyfill.closeHistory) + return cc.log("The current browser does not support the GoBack"); + var iframe = this._renderCmd._iframe; + if (iframe) { + var win = iframe.contentWindow; + if (win && win.location) + try { + win.history.back.call(win); + } catch (error) { + win.history.back(); + } + } + } catch (err) { + cc.log(err); + } + }, + + /** + * go forward + */ + goForward: function () { + try { + if (ccui.WebView._polyfill.closeHistory) + return cc.log("The current browser does not support the GoForward"); + var iframe = this._renderCmd._iframe; + if (iframe) { + var win = iframe.contentWindow; + if (win && win.location) + try { + win.history.forward.call(win); + } catch (error) { + win.history.forward(); + } + } + } catch (err) { + cc.log(err); + } + }, + + /** + * In the webview execution within a period of js string + * @param {String} str + */ + evaluateJS: function (str) { + var iframe = this._renderCmd._iframe; + if (iframe) { + var win = iframe.contentWindow; + try { + win.eval(str); + this._dispatchEvent(ccui.WebView.EventType.JS_EVALUATED); + } catch (err) { + console.error(err); + } + } + }, + + /** + * Limited scale + */ + setScalesPageToFit: function () { + cc.log("Web does not support zoom"); + }, + + /** + * The binding event + * @param {ccui.WebView.EventType} event + * @param {Function} callback + */ + setEventListener: function (event, callback) { + this._EventList[event] = callback; + }, + + /** + * Delete events + * @param {ccui.WebView.EventType} event + */ + removeEventListener: function (event) { + this._EventList[event] = null; + }, + + _dispatchEvent: function (event) { + var callback = this._EventList[event]; + if (callback) + callback.call(this, this, this._renderCmd._iframe.src); + }, + + _createRenderCmd: function () { + return new ccui.WebView.RenderCmd(this); + }, + + /** + * Set the contentSize + * @param {Number} w + * @param {Number} h + */ + setContentSize: function (w, h) { + ccui.Widget.prototype.setContentSize.call(this, w, h); + if (h === undefined) { + h = w.height; + w = w.width; + } + this._renderCmd.changeSize(w, h); + }, + + /** + * remove node + */ + cleanup: function () { + this._renderCmd.removeDom(); + this.stopAllActions(); + this.unscheduleAllCallbacks(); + } +}); + +/** + * The WebView support list of events + * @type {{LOADING: string, LOADED: string, ERROR: string}} + */ +ccui.WebView.EventType = { + LOADING: "loading", + LOADED: "load", + ERROR: "error", + JS_EVALUATED: "js" +}; + +(function () { + + var polyfill = ccui.WebView._polyfill = { + devicePixelRatio: false, + enableDiv: false + }; + + if (cc.sys.os === cc.sys.OS_IOS) + polyfill.enableDiv = true; + + if (cc.sys.isMobile) { + if (cc.sys.browserType === cc.sys.BROWSER_TYPE_FIREFOX) { + polyfill.enableBG = true; + } + } else { + if (cc.sys.browserType === cc.sys.BROWSER_TYPE_IE) { + polyfill.closeHistory = true; + } + } + + +})(); + +(function (polyfill) { + + var RenderCmd = null; + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + RenderCmd = cc.Node.WebGLRenderCmd; + } else { + RenderCmd = cc.Node.CanvasRenderCmd; + } + + ccui.WebView.RenderCmd = function (node) { + this._rootCtor(node); + + this._div = null; + this._iframe = null; + + if (polyfill.enableDiv) { + this._div = document.createElement("div"); + this._div.style["-webkit-overflow"] = "auto"; + this._div.style["-webkit-overflow-scrolling"] = "touch"; + this._iframe = document.createElement("iframe"); + this._iframe.style["width"] = "100%"; + this._iframe.style["height"] = "100%"; + this._div.appendChild(this._iframe); + } else { + this._div = this._iframe = document.createElement("iframe"); + } + + if (polyfill.enableBG) + this._div.style["background"] = "#FFF"; + + this._iframe.addEventListener("load", function () { + node._dispatchEvent(ccui.WebView.EventType.LOADED); + }); + this._iframe.addEventListener("error", function () { + node._dispatchEvent(ccui.WebView.EventType.ERROR); + }); + this._div.style["background"] = "#FFF"; + this._div.style.height = "200px"; + this._div.style.width = "300px"; + this._div.style.overflow = "scroll"; + this._div.style["border"] = "none"; + this._listener = null; + this.initStyle(); + }; + + var proto = ccui.WebView.RenderCmd.prototype = Object.create(RenderCmd.prototype); + proto.constructor = ccui.WebView.RenderCmd; + + proto.transform = function (parentCmd, recursive) { + this.originTransform(parentCmd, recursive); + this.updateMatrix(this._worldTransform, cc.view._scaleX, cc.view._scaleY); + }; + + proto.updateStatus = function () { + polyfill.devicePixelRatio = cc.view.isRetinaEnabled(); + var flags = cc.Node._dirtyFlags, locFlag = this._dirtyFlag; + if (locFlag & flags.transformDirty) { + //update the transform + this.transform(this.getParentRenderCmd(), true); + this.updateMatrix(this._worldTransform, cc.view._scaleX, cc.view._scaleY); + this._dirtyFlag = this._dirtyFlag & cc.Node._dirtyFlags.transformDirty ^ this._dirtyFlag; + } + + if (locFlag & flags.orderDirty) { + this._dirtyFlag = this._dirtyFlag & flags.orderDirty ^ this._dirtyFlag; + } + }; + + proto.resize = function (view) { + view = view || cc.view; + var node = this._node, + eventManager = cc.eventManager; + if (node._parent && node._visible) + this.updateMatrix(this._worldTransform, view._scaleX, view._scaleY); + else { + var list = eventManager._listenersMap[cc.game.EVENT_RESIZE].getFixedPriorityListeners(); + eventManager._removeListenerInVector(list, this._listener); + this._listener = null; + } + }; + + proto.updateMatrix = function (t, scaleX, scaleY) { + var node = this._node; + if (polyfill.devicePixelRatio) { + var dpr = cc.view.getDevicePixelRatio(); + scaleX = scaleX / dpr; + scaleY = scaleY / dpr; + } + if (this._loaded === false) return; + var containerStyle = cc.game.container.style, + offsetX = parseInt(containerStyle.paddingLeft), + offsetY = parseInt(containerStyle.paddingBottom), + cw = node._contentSize.width, + ch = node._contentSize.height; + var a = t.a * scaleX, + b = t.b, + c = t.c, + d = t.d * scaleY, + tx = offsetX + t.tx * scaleX - cw / 2 + cw * node._scaleX / 2 * scaleX, + ty = offsetY + t.ty * scaleY - ch / 2 + ch * node._scaleY / 2 * scaleY; + var matrix = "matrix(" + a + "," + b + "," + c + "," + d + "," + tx + "," + -ty + ")"; + this._div.style["transform"] = matrix; + this._div.style["-webkit-transform"] = matrix; + }; + + proto.initStyle = function () { + if (!this._div) return; + var div = this._div; + div.style.position = "absolute"; + div.style.bottom = "0px"; + div.style.left = "0px"; + }; + + proto.updateURL = function (url) { + var iframe = this._iframe; + iframe.src = url; + var self = this; + var cb = function () { + self._loaded = true; + iframe.removeEventListener("load", cb); + }; + iframe.addEventListener("load", cb); + }; + + proto.changeSize = function (w, h) { + var div = this._div; + if (div) { + div.style["width"] = w + "px"; + div.style["height"] = h + "px"; + } + }; + + proto.removeDom = function () { + var div = this._div; + if (div) { + var hasChild = false; + if ('contains' in cc.container) { + hasChild = cc.container.contains(div); + } else { + hasChild = cc.container.compareDocumentPosition(div) % 16; + } + if (hasChild) + cc.container.removeChild(div); + } + }; + +})(ccui.WebView._polyfill); diff --git a/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/scroll-widget/UIListView.js b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/scroll-widget/UIListView.js new file mode 100644 index 0000000..3d0f673 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/scroll-widget/UIListView.js @@ -0,0 +1,1085 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The list view control of Cocos UI. + * @class + * @extends ccui.ScrollView + * @example + * var listView = new ccui.ListView(); + * // set list view ex direction + * listView.setDirection(ccui.ScrollView.DIR_VERTICAL); + * listView.setTouchEnabled(true); + * listView.setBounceEnabled(true); + * listView.setBackGroundImage("res/cocosui/green_edit.png"); + * listView.setBackGroundImageScale9Enabled(true); + * listView.setContentSize(cc.size(240, 130)); + * this.addChild(listView); + */ +ccui.ListView = ccui.ScrollView.extend(/** @lends ccui.ListView# */{ + _model: null, + _items: null, + _gravity: null, + _itemsMargin: 0, + + _curSelectedIndex: 0, + _refreshViewDirty: true, + + _listViewEventListener: null, + _listViewEventSelector: null, + _ccListViewEventCallback: null, + _magneticAllowedOutOfBoundary: true, + _magneticType: 0, + _className: "ListView", + + /** + * allocates and initializes a UIListView. + * Constructor of ccui.ListView, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @example + * // example + * var aListView = new ccui.ListView(); + */ + ctor: function () { + this._items = []; + ccui.ScrollView.prototype.ctor.call(this); + this._gravity = ccui.ListView.GRAVITY_CENTER_VERTICAL; + this.setTouchEnabled(true); + this.setDirection(ccui.ScrollView.DIR_VERTICAL); + }, + + /** + * Sets a item model for ListView. A model will be cloned for adding default item. + * @param {ccui.Widget} model + */ + setItemModel: function (model) { + if (!model) { + cc.log("Can't set a null to item model!"); + return; + } + + this._model = model; + }, + + _handleReleaseLogic: function (touch) { + ccui.ScrollView.prototype._handleReleaseLogic.call(this, touch); + + if (!this._autoScrolling) { + this._startMagneticScroll(); + } + }, + + _onItemListChanged: function () { + this._outOfBoundaryAmountDirty = true; + }, + + _updateInnerContainerSize: function () { + var i, locItems = this._items, length; + switch (this.direction) { + case ccui.ScrollView.DIR_VERTICAL: + length = locItems.length; + var totalHeight = (length - 1) * this._itemsMargin; + for (i = 0; i < length; i++) { + totalHeight += locItems[i].getContentSize().height; + } + this.setInnerContainerSize(cc.size(this._contentSize.width, totalHeight)); + break; + case ccui.ScrollView.DIR_HORIZONTAL: + length = locItems.length; + var totalWidth = (length - 1) * this._itemsMargin; + for (i = 0; i < length; i++) { + totalWidth += locItems[i].getContentSize().width; + } + this.setInnerContainerSize(cc.size(totalWidth, this._contentSize.height)); + break; + default: + break; + } + }, + + _remedyLayoutParameter: function (item) { + cc.assert(null != item, "ListView Item can't be nil!"); + + var linearLayoutParameter = item.getLayoutParameter(ccui.LayoutParameter.LINEAR); + var isLayoutParameterExists = true; + if (!linearLayoutParameter) { + linearLayoutParameter = new ccui.LinearLayoutParameter(); + isLayoutParameterExists = false; + } + var itemIndex = this.getIndex(item); + switch (this.direction) { + case ccui.ScrollView.DIR_VERTICAL: + this._remedyVerticalLayoutParameter(linearLayoutParameter, itemIndex); + break; + case ccui.ScrollView.DIR_HORIZONTAL: + this._remedyHorizontalLayoutParameter(linearLayoutParameter, itemIndex); + break; + default: + break; + } + if (!isLayoutParameterExists) + item.setLayoutParameter(linearLayoutParameter); + }, + + //@since v3.3 + _remedyVerticalLayoutParameter: function (layoutParameter, itemIndex) { + cc.assert(null != layoutParameter, "Layout parameter can't be nil!"); + + switch (this._gravity) { + case ccui.ListView.GRAVITY_LEFT: + layoutParameter.setGravity(ccui.LinearLayoutParameter.LEFT); + break; + case ccui.ListView.GRAVITY_RIGHT: + layoutParameter.setGravity(ccui.LinearLayoutParameter.RIGHT); + break; + case ccui.ListView.GRAVITY_CENTER_HORIZONTAL: + layoutParameter.setGravity(ccui.LinearLayoutParameter.CENTER_HORIZONTAL); + break; + default: + break; + } + if (0 === itemIndex) + layoutParameter.setMargin(ccui.MarginZero()); + else + layoutParameter.setMargin(new ccui.Margin(0.0, this._itemsMargin, 0.0, 0.0)); + }, + + //@since v3.3 + _remedyHorizontalLayoutParameter: function (layoutParameter, itemIndex) { + cc.assert(null != layoutParameter, "Layout parameter can't be nil!"); + + switch (this._gravity) { + case ccui.ListView.GRAVITY_TOP: + layoutParameter.setGravity(ccui.LinearLayoutParameter.TOP); + break; + case ccui.ListView.GRAVITY_BOTTOM: + layoutParameter.setGravity(ccui.LinearLayoutParameter.BOTTOM); + break; + case ccui.ListView.GRAVITY_CENTER_VERTICAL: + layoutParameter.setGravity(ccui.LinearLayoutParameter.CENTER_VERTICAL); + break; + default: + break; + } + if (0 === itemIndex) + layoutParameter.setMargin(ccui.MarginZero()); + else + layoutParameter.setMargin(new ccui.Margin(this._itemsMargin, 0.0, 0.0, 0.0)); + }, + + /** + * Push back a default item(create by a cloned model) into ListView. + */ + pushBackDefaultItem: function () { + if (this._model == null) + return; + var newItem = this._model.clone(); + this._remedyLayoutParameter(newItem); + this.addChild(newItem); + this._refreshViewDirty = true; + }, + + /** + * Insert a default item(create by a cloned model) into ListView. + * @param {Number} index + */ + insertDefaultItem: function (index) { + if (this._model == null) + return; + var newItem = this._model.clone(); + this._items.splice(index, 0, newItem); + ccui.ScrollView.prototype.addChild.call(this, newItem); + this._remedyLayoutParameter(newItem); + + this._refreshViewDirty = true; + }, + + /** + * Push back custom item into ListView. + * @param {ccui.Widget} item + */ + pushBackCustomItem: function (item) { + this._remedyLayoutParameter(item); + this.addChild(item); + this._refreshViewDirty = true; + }, + + /** + * add child to ListView + * @override + * @param {cc.Node} widget + * @param {Number} [zOrder] + * @param {Number|String} [tag] tag or name + */ + addChild: function (widget, zOrder, tag) { + if (widget) { + zOrder = zOrder || widget.getLocalZOrder(); + tag = tag || widget.getName(); + ccui.ScrollView.prototype.addChild.call(this, widget, zOrder, tag); + if (widget instanceof ccui.Widget) { + this._items.push(widget); + this._onItemListChanged(); + } + } + }, + + /** + * remove child from ListView + * @override + * @param {cc.Node} widget + * @param {Boolean} [cleanup=true] + */ + removeChild: function (widget, cleanup) { + if (widget) { + var index = this._items.indexOf(widget); + if (index > -1) + this._items.splice(index, 1); + + this._onItemListChanged(); + + ccui.ScrollView.prototype.removeChild.call(this, widget, cleanup); + } + }, + + /** + * Removes all children from ccui.ListView. + */ + removeAllChildren: function () { + this.removeAllChildrenWithCleanup(true); + }, + + /** + * Removes all children from ccui.ListView and do a cleanup all running actions depending on the cleanup parameter. + * @param {Boolean} cleanup + */ + removeAllChildrenWithCleanup: function (cleanup) { + ccui.ScrollView.prototype.removeAllChildrenWithCleanup.call(this, cleanup); + this._items = []; + + this._onItemListChanged(); + }, + + /** + * Push back custom item into ccui.ListView. + * @param {ccui.Widget} item + * @param {Number} index + */ + insertCustomItem: function (item, index) { + this._items.splice(index, 0, item); + + this._onItemListChanged(); + ccui.ScrollView.prototype.addChild.call(this, item); + this._remedyLayoutParameter(item); + this._refreshViewDirty = true; + }, + + /** + * Removes a item whose index is same as the parameter. + * @param {Number} index + */ + removeItem: function (index) { + var item = this.getItem(index); + if (item == null) + return; + this.removeChild(item, true); + this._refreshViewDirty = true; + }, + + /** + * Removes the last item of ccui.ListView. + */ + removeLastItem: function () { + this.removeItem(this._items.length - 1); + }, + + /** + * Removes all items from ccui.ListView. + */ + removeAllItems: function () { + this.removeAllChildren(); + }, + + /** + * Returns a item whose index is same as the parameter. + * @param {Number} index + * @returns {ccui.Widget} + */ + getItem: function (index) { + if (index < 0 || index >= this._items.length) + return null; + return this._items[index]; + }, + + /** + * Returns the item container. + * @returns {Array} + */ + getItems: function () { + return this._items; + }, + + /** + * Returns the index of item. + * @param {ccui.Widget} item the item which need to be checked. + * @returns {Number} the index of item. + */ + getIndex: function (item) { + if (item == null) + return -1; + return this._items.indexOf(item); + }, + + /** + * Changes the gravity of ListView. + * @param {ccui.ListView.GRAVITY_LEFT|ccui.ListView.GRAVITY_RIGHT|ccui.ListView.GRAVITY_CENTER_HORIZONTAL|ccui.ListView.GRAVITY_BOTTOM|ccui.ListView.GRAVITY_CENTER_VERTICAL} gravity + */ + setGravity: function (gravity) { + if (this._gravity === gravity) + return; + this._gravity = gravity; + this._refreshViewDirty = true; + }, + + /** + * Set magnetic type of ListView. + * @param {ccui.ListView.MAGNETIC_NONE|ccui.ListView.MAGNETIC_CENTER,ccui.ListView.MAGNETIC_BOTH_END|ccui.ListView.MAGNETIC_LEFT|ccui.ListView.MAGNETIC_RIGHT|ccui.ListView.MAGNETIC_TOP|ccui.ListView.MAGNETIC_BOTTOM} magneticType + */ + setMagneticType: function (magneticType) { + this._magneticType = magneticType; + this._onItemListChanged(); + this._startMagneticScroll(); + }, + + /** + * Get magnetic type of ListView. + * @returns {number} + */ + getMagneticType: function () { + return this._magneticType; + }, + + /** + * Set magnetic allowed out of boundary. + * @param {boolean} magneticAllowedOutOfBoundary + */ + setMagneticAllowedOutOfBoundary: function (magneticAllowedOutOfBoundary) { + this._magneticAllowedOutOfBoundary = magneticAllowedOutOfBoundary; + }, + + /** + * Query whether the magnetic out of boundary is allowed. + * @returns {boolean} + */ + getMagneticAllowedOutOfBoundary: function () { + return this._magneticAllowedOutOfBoundary; + }, + + /** + * Changes the margin between each item. + * @param {Number} margin + */ + setItemsMargin: function (margin) { + if (this._itemsMargin === margin) + return; + this._itemsMargin = margin; + this._refreshViewDirty = true; + }, + + /** + * Returns the margin between each item. + * @returns {Number} + */ + getItemsMargin: function () { + return this._itemsMargin; + }, + + /** + * Changes scroll direction of ccui.ListView. + * @param {ccui.ScrollView.DIR_NONE | ccui.ScrollView.DIR_VERTICAL | ccui.ScrollView.DIR_HORIZONTAL | ccui.ScrollView.DIR_BOTH} dir + */ + setDirection: function (dir) { + switch (dir) { + case ccui.ScrollView.DIR_VERTICAL: + this.setLayoutType(ccui.Layout.LINEAR_VERTICAL); + break; + case ccui.ScrollView.DIR_HORIZONTAL: + this.setLayoutType(ccui.Layout.LINEAR_HORIZONTAL); + break; + case ccui.ScrollView.DIR_BOTH: + return; + default: + return; + break; + } + ccui.ScrollView.prototype.setDirection.call(this, dir); + }, + + _getHowMuchOutOfBoundary: function (addition) { + if (addition === undefined) + addition = cc.p(0, 0); + + if (!this._magneticAllowedOutOfBoundary || this._items.length === 0) { + return ccui.ScrollView.prototype._getHowMuchOutOfBoundary.call(this, addition); + } + else if (this._magneticType === ccui.ListView.MAGNETIC_NONE || this._magneticType === ccui.ListView.MAGNETIC_BOTH_END) { + return ccui.ScrollView.prototype._getHowMuchOutOfBoundary.call(this, addition); + } + else if (addition.x === 0 && addition.y === 0 && !this._outOfBoundaryAmountDirty) { + return this._outOfBoundaryAmount; + } + + // If it is allowed to be out of boundary by magnetic, adjust the boundaries according to the magnetic type. + var leftBoundary = this._leftBoundary; + var rightBoundary = this._rightBoundary; + var topBoundary = this._topBoundary; + var bottomBoundary = this._bottomBoundary; + + var lastItemIndex = this._items.length - 1; + var contentSize = this.getContentSize(); + var firstItemAdjustment = cc.p(0, 0); + var lastItemAdjustment = cc.p(0, 0); + + switch (this._magneticType) { + case ccui.ListView.MAGNETIC_CENTER: + firstItemAdjustment.x = (contentSize.width - this._items[0].width) / 2; + firstItemAdjustment.y = (contentSize.height - this._items[0].height) / 2; + + lastItemAdjustment.x = (contentSize.width - this._items[lastItemIndex].width) / 2; + lastItemAdjustment.y = (contentSize.height - this._items[lastItemIndex].height) / 2; + + break; + case ccui.ListView.MAGNETIC_LEFT: + case ccui.ListView.MAGNETIC_TOP: + lastItemAdjustment.x = contentSize.width - this._items[lastItemIndex].width; + lastItemAdjustment.y = contentSize.height - this._items[lastItemIndex].height; + break; + case ccui.ListView.MAGNETIC_RIGHT: + case ccui.ListView.MAGNETIC_BOTTOM: + firstItemAdjustment.x = contentSize.width - this._items[0].width; + firstItemAdjustment.y = contentSize.height - this._items[0].height; + break; + } + + leftBoundary += firstItemAdjustment.x; + rightBoundary -= lastItemAdjustment.x; + topBoundary -= firstItemAdjustment.y; + bottomBoundary += lastItemAdjustment.y; + + + // Calculate the actual amount + var outOfBoundaryAmount = cc.p(0, 0); + + if (this._innerContainer.getLeftBoundary() + addition.x > leftBoundary) { + outOfBoundaryAmount.x = leftBoundary - (this._innerContainer.getLeftBoundary() + addition.x); + } + else if (this._innerContainer.getRightBoundary() + addition.x < rightBoundary) { + outOfBoundaryAmount.x = rightBoundary - (this._innerContainer.getRightBoundary() + addition.x); + } + + if (this._innerContainer.getTopBoundary() + addition.y < topBoundary) { + outOfBoundaryAmount.y = topBoundary - (this._innerContainer.getTopBoundary() + addition.y); + } + else if (this._innerContainer.getBottomBoundary() + addition.y > bottomBoundary) { + outOfBoundaryAmount.y = bottomBoundary - (this._innerContainer.getBottomBoundary() + addition.y); + } + + if (addition.x === 0 && addition.y === 0) { + this._outOfBoundaryAmount = outOfBoundaryAmount; + this._outOfBoundaryAmountDirty = false; + } + return outOfBoundaryAmount; + }, + + _calculateItemPositionWithAnchor: function (item, itemAnchorPoint) { + var origin = cc.p(item.getLeftBoundary(), item.getBottomBoundary()); + var size = item.getContentSize(); + + return cc.p(origin.x + size.width * itemAnchorPoint.x, origin.y + size.height * itemAnchorPoint.y); + }, + + _findClosestItem: function (targetPosition, items, itemAnchorPoint, firstIndex, distanceFromFirst, lastIndex, distanceFromLast) { + cc.assert(firstIndex >= 0 && lastIndex < items.length && firstIndex <= lastIndex, ""); + if (firstIndex === lastIndex) { + return items[firstIndex]; + } + if (lastIndex - firstIndex === 1) { + if (distanceFromFirst <= distanceFromLast) { + return items[firstIndex]; + } + else { + return items[lastIndex]; + } + } + + // Binary search + var midIndex = Math.floor((firstIndex + lastIndex) / 2); + var itemPosition = this._calculateItemPositionWithAnchor(items[midIndex], itemAnchorPoint); + var distanceFromMid = cc.pLength(cc.pSub(targetPosition, itemPosition)); + + if (distanceFromFirst <= distanceFromLast) { + // Left half + return this._findClosestItem(targetPosition, items, itemAnchorPoint, firstIndex, distanceFromFirst, midIndex, distanceFromMid); + } + else { + // Right half + return this._findClosestItem(targetPosition, items, itemAnchorPoint, midIndex, distanceFromMid, lastIndex, distanceFromLast); + } + }, + + /** + * Query the closest item to a specific position in inner container. + * + * @param {cc.Point} targetPosition Specifies the target position in inner container's coordinates. + * @param {cc.Point} itemAnchorPoint Specifies an anchor point of each item for position to calculate distance. + * @returns {?ccui.Widget} A item instance if list view is not empty. Otherwise, returns null. + */ + getClosestItemToPosition: function (targetPosition, itemAnchorPoint) { + if (this._items.length === 0) { + return null; + } + + // Find the closest item through binary search + var firstIndex = 0; + var firstPosition = this._calculateItemPositionWithAnchor(this._items[firstIndex], itemAnchorPoint); + var distanceFromFirst = cc.pLength(cc.pSub(targetPosition, firstPosition)); + + var lastIndex = this._items.length - 1; + var lastPosition = this._calculateItemPositionWithAnchor(this._items[lastIndex], itemAnchorPoint); + var distanceFromLast = cc.pLength(cc.pSub(targetPosition, lastPosition)); + + return this._findClosestItem(targetPosition, this._items, itemAnchorPoint, firstIndex, distanceFromFirst, lastIndex, distanceFromLast); + }, + + /** + * Query the closest item to a specific position in current view.
+ * For instance, to find the item in the center of view, call 'getClosestItemToPositionInCurrentView(cc.p(0.5, 0.5), cc.p(0.5, 0.5))'. + * + * @param {cc.Point} positionRatioInView Specifies the target position with ratio in list view's content size. + * @param {cc.Point} itemAnchorPoint Specifies an anchor point of each item for position to calculate distance. + * @returns {?ccui.Widget} A item instance if list view is not empty. Otherwise, returns null. + */ + + getClosestItemToPositionInCurrentView: function (positionRatioInView, itemAnchorPoint) { + // Calculate the target position + var contentSize = this.getContentSize(); + var targetPosition = cc.pMult(this._innerContainer.getPosition(), -1); + targetPosition.x += contentSize.width * positionRatioInView.x; + targetPosition.y += contentSize.height * positionRatioInView.y; + + return this.getClosestItemToPosition(targetPosition, itemAnchorPoint); + }, + + /** + * Query the center item + * @returns {?ccui.Widget} A item instance. + */ + getCenterItemInCurrentView: function () { + return this.getClosestItemToPositionInCurrentView(cc.p(0.5, 0.5), cc.p(0.5, 0.5)); + }, + + /** + * Query the leftmost item in horizontal list + * @returns {?ccui.Widget} A item instance. + */ + getLeftmostItemInCurrentView: function () { + if (this._direction === ccui.ScrollView.DIR_HORIZONTAL) { + return this.getClosestItemToPositionInCurrentView(cc.p(0, 0.5), cc.p(0.5, 0.5)); + } + + return null; + }, + + /** + * Query the rightmost item in horizontal list + * @returns {?ccui.Widget} A item instance. + */ + getRightmostItemInCurrentView: function () { + if (this._direction === ccui.ScrollView.DIR_HORIZONTAL) { + return this.getClosestItemToPositionInCurrentView(cc.p(1, 0.5), cc.p(0.5, 0.5)); + } + + return null; + }, + + /** + * Query the topmost item in horizontal list + * @returns {?ccui.Widget} A item instance. + */ + getTopmostItemInCurrentView: function () { + if (this._direction === ccui.ScrollView.DIR_VERTICAL) { + return this.getClosestItemToPositionInCurrentView(cc.p(0.5, 1), cc.p(0.5, 0.5)); + } + + return null; + }, + + /** + * Query the topmost item in horizontal list + * @returns {?ccui.Widget} A item instance. + */ + getBottommostItemInCurrentView: function () { + if (this._direction === ccui.ScrollView.DIR_VERTICAL) { + return this.getClosestItemToPositionInCurrentView(cc.p(0.5, 0), cc.p(0.5, 0.5)); + } + + return null; + }, + + _calculateItemDestination: function (positionRatioInView, item, itemAnchorPoint) { + var contentSize = this.getContentSize(); + var positionInView = cc.p(0, 0); + positionInView.x += contentSize.width * positionRatioInView.x; + positionInView.y += contentSize.height * positionRatioInView.y; + + var itemPosition = this._calculateItemPositionWithAnchor(item, itemAnchorPoint); + return cc.pMult(cc.pSub(itemPosition, positionInView), -1); + }, + + jumpToBottom: function () { + this.doLayout(); + ccui.ScrollView.prototype.jumpToBottom.call(this); + }, + + jumpToTop: function () { + this.doLayout(); + ccui.ScrollView.prototype.jumpToTop.call(this); + }, + + jumpToLeft: function () { + this.doLayout(); + ccui.ScrollView.prototype.jumpToLeft.call(this); + }, + + jumpToRight: function () { + this.doLayout(); + ccui.ScrollView.prototype.jumpToRight.call(this); + }, + + jumpToTopLeft: function () { + this.doLayout(); + ccui.ScrollView.prototype.jumpToTopLeft.call(this); + }, + + jumpToTopRight: function () { + this.doLayout(); + ccui.ScrollView.prototype.jumpToTopRight.call(this); + }, + + jumpToBottomLeft: function () { + this.doLayout(); + ccui.ScrollView.prototype.jumpToBottomLeft.call(this); + }, + + jumpToBottomRight: function () { + this.doLayout(); + ccui.ScrollView.prototype.jumpToBottomRight.call(this); + }, + + jumpToPercentVertical: function (percent) { + this.doLayout(); + ccui.ScrollView.prototype.jumpToPercentVertical.call(this, percent); + }, + + jumpToPercentHorizontal: function (percent) { + this.doLayout(); + ccui.ScrollView.prototype.jumpToPercentHorizontal.call(this, percent); + }, + + jumpToPercentBothDirection: function (percent) { + this.doLayout(); + ccui.ScrollView.prototype.jumpToPercentBothDirection.call(this, percent); + }, + + /** + * Jump to specific item + * @param {number} itemIndex Specifies the item's index + * @param {cc.Point} positionRatioInView Specifies the position with ratio in list view's content size. + * @param {cc.Point} itemAnchorPoint Specifies an anchor point of each item for position to calculate distance. + */ + jumpToItem: function (itemIndex, positionRatioInView, itemAnchorPoint) { + var item = this.getItem(itemIndex); + + if (!item) + return; + + this.doLayout(); + + var destination = this._calculateItemDestination(positionRatioInView, item, itemAnchorPoint); + + if (!this.bounceEnabled) { + var delta = cc.pSub(destination, this._innerContainer.getPosition()); + var outOfBoundary = this._getHowMuchOutOfBoundary(delta); + destination.x += outOfBoundary.x; + destination.y += outOfBoundary.y; + } + + this._jumpToDestination(destination); + }, + + /** + * Scroll to specific item + * @param {number} itemIndex Specifies the item's index + * @param {cc.Point} positionRatioInView Specifies the position with ratio in list view's content size. + * @param {cc.Point} itemAnchorPoint Specifies an anchor point of each item for position to calculate distance. + * @param {number} [timeInSec = 1.0] Scroll time + */ + scrollToItem: function (itemIndex, positionRatioInView, itemAnchorPoint, timeInSec) { + if (timeInSec === undefined) + timeInSec = 1; + + var item = this.getItem(itemIndex); + + if (!item) + return; + + var destination = this._calculateItemDestination(positionRatioInView, item, itemAnchorPoint); + this._startAutoScrollToDestination(destination, timeInSec, true); + }, + + /** + * Requests refresh list view. + * @deprecated Use method requestDoLayout() instead + */ + requestRefreshView: function () { + this._refreshViewDirty = true; + }, + + /** + * Refreshes list view. + * @deprecated Use method forceDoLayout() instead + */ + refreshView: function () { + this.forceDoLayout() + }, + + /** + * provides a public _doLayout function for Editor. it calls _doLayout. + */ + doLayout: function () { + this._doLayout(); + }, + + requestDoLayout: function () { + this._refreshViewDirty = true; + }, + + _doLayout: function () { + //ccui.Layout.prototype._doLayout.call(this); + if (this._refreshViewDirty) { + var locItems = this._items; + for (var i = 0; i < locItems.length; i++) { + var item = locItems[i]; + item.setLocalZOrder(i); + this._remedyLayoutParameter(item); + } + this._updateInnerContainerSize(); + this._innerContainer.forceDoLayout(); + this._refreshViewDirty = false; + } + }, + + /** + * Adds event listener to ccui.ListView. + * @param {Function} selector + * @param {Object} [target=] + * @deprecated since v3.0, please use addEventListener instead. + */ + addEventListenerListView: function (selector, target) { + this._listViewEventListener = target; + this._listViewEventSelector = selector; + }, + + /** + * Adds callback function called ListView event triggered + * @param {Function} selector + */ + addEventListener: function (selector) { + this._ccListViewEventCallback = selector; + }, + + _selectedItemEvent: function (event) { + var eventEnum = (event === ccui.Widget.TOUCH_BEGAN) ? ccui.ListView.ON_SELECTED_ITEM_START : ccui.ListView.ON_SELECTED_ITEM_END; + if (this._listViewEventSelector) { + if (this._listViewEventListener) + this._listViewEventSelector.call(this._listViewEventListener, this, eventEnum); + else + this._listViewEventSelector(this, eventEnum); + } + if (this._ccListViewEventCallback) + this._ccListViewEventCallback(this, eventEnum); + }, + + /** + * Intercept touch event, handle its child's touch event. + * @param {Number} eventType + * @param {ccui.Widget} sender + * @param {cc.Touch} touch + */ + interceptTouchEvent: function (eventType, sender, touch) { + ccui.ScrollView.prototype.interceptTouchEvent.call(this, eventType, sender, touch); + if (!this._touchEnabled) { + return; + } + if (eventType !== ccui.Widget.TOUCH_MOVED) { + var parent = sender; + while (parent) { + if (parent && parent.getParent() === this._innerContainer) { + this._curSelectedIndex = this.getIndex(parent); + break; + } + parent = parent.getParent(); + } + if (sender.isHighlighted()) + this._selectedItemEvent(eventType); + } + }, + + /** + * Returns current selected index + * @returns {number} + */ + getCurSelectedIndex: function () { + return this._curSelectedIndex; + }, + + _onSizeChanged: function () { + ccui.ScrollView.prototype._onSizeChanged.call(this); + this._refreshViewDirty = true; + }, + + /** + * Returns the "class name" of ccui.ListView. + * @returns {string} + */ + getDescription: function () { + return "ListView"; + }, + + _createCloneInstance: function () { + return new ccui.ListView(); + }, + + _copyClonedWidgetChildren: function (model) { + var arrayItems = model.getItems(); + for (var i = 0; i < arrayItems.length; i++) { + var item = arrayItems[i]; + this.pushBackCustomItem(item.clone()); + } + }, + + _copySpecialProperties: function (listView) { + if (listView instanceof ccui.ListView) { + ccui.ScrollView.prototype._copySpecialProperties.call(this, listView); + this.setItemModel(listView._model); + this.setItemsMargin(listView._itemsMargin); + this.setGravity(listView._gravity); + + this._listViewEventListener = listView._listViewEventListener; + this._listViewEventSelector = listView._listViewEventSelector; + } + }, + + _startAttenuatingAutoScroll: function (deltaMove, initialVelocity) { + var adjustedDeltaMove = deltaMove; + + if (this._items.length !== 0 && this._magneticType !== ccui.ListView.MAGNETIC_NONE) { + adjustedDeltaMove = this._flattenVectorByDirection(adjustedDeltaMove); + + var howMuchOutOfBoundary = this._getHowMuchOutOfBoundary(adjustedDeltaMove); + // If the destination is out of boundary, do nothing here. Because it will be handled by bouncing back. + if (howMuchOutOfBoundary.x === 0 && howMuchOutOfBoundary.y === 0) { + var magType = this._magneticType; + if (magType === ccui.ListView.MAGNETIC_BOTH_END) { + if (this._direction === ccui.ScrollView.DIR_HORIZONTAL) { + magType = (adjustedDeltaMove.x > 0 ? ccui.ListView.MAGNETIC_LEFT : ccui.ListView.MAGNETIC_RIGHT); + } + else if (this._direction === ccui.ScrollView.DIR_VERTICAL) { + magType = (adjustedDeltaMove.y > 0 ? ccui.ListView.MAGNETIC_BOTTOM : ccui.ListView.MAGNETIC_TOP); + } + } + + // Adjust the delta move amount according to the magnetic type + var magneticAnchorPoint = this._getAnchorPointByMagneticType(magType); + var magneticPosition = cc.pMult(this._innerContainer.getPosition(), -1); + magneticPosition.x += this.width * magneticAnchorPoint.x; + magneticPosition.y += this.height * magneticAnchorPoint.y; + + var pTargetItem = this.getClosestItemToPosition(cc.pSub(magneticPosition, adjustedDeltaMove), magneticAnchorPoint); + var itemPosition = this._calculateItemPositionWithAnchor(pTargetItem, magneticAnchorPoint); + adjustedDeltaMove = cc.pSub(magneticPosition, itemPosition); + } + } + ccui.ScrollView.prototype._startAttenuatingAutoScroll.call(this, adjustedDeltaMove, initialVelocity); + }, + + _getAnchorPointByMagneticType: function (magneticType) { + switch (magneticType) { + case ccui.ListView.MAGNETIC_NONE: + return cc.p(0, 0); + case ccui.ListView.MAGNETIC_BOTH_END: + return cc.p(0, 1); + case ccui.ListView.MAGNETIC_CENTER: + return cc.p(0.5, 0.5); + case ccui.ListView.MAGNETIC_LEFT: + return cc.p(0, 0.5); + case ccui.ListView.MAGNETIC_RIGHT: + return cc.p(1, 0.5); + case ccui.ListView.MAGNETIC_TOP: + return cc.p(0.5, 1); + case ccui.ListView.MAGNETIC_BOTTOM: + return cc.p(0.5, 0); + } + + return cc.p(0, 0); + }, + + _startMagneticScroll: function () { + if (this._items.length === 0 || this._magneticType === ccui.ListView.MAGNETIC_NONE) { + return; + } + + // Find the closest item + var magneticAnchorPoint = this._getAnchorPointByMagneticType(this._magneticType); + var magneticPosition = cc.pMult(this._innerContainer.getPosition(), -1); + magneticPosition.x += this.width * magneticAnchorPoint.x; + magneticPosition.y += this.height * magneticAnchorPoint.y; + + var pTargetItem = this.getClosestItemToPosition(magneticPosition, magneticAnchorPoint); + this.scrollToItem(this.getIndex(pTargetItem), magneticAnchorPoint, magneticAnchorPoint); + } +}); + +/** + * allocates and initializes a UIListView. + * @deprecated since v3.0, please use new ccui.ListView() instead. + */ +ccui.ListView.create = function () { + return new ccui.ListView(); +}; + +// Constants +//listView event type +/** + * The flag selected item of ccui.ListView's event. + * @constant + * @type {number} + */ +ccui.ListView.EVENT_SELECTED_ITEM = 0; + +/** + * The flag selected item start of ccui.ListView's event. + * @constant + * @type {number} + */ +ccui.ListView.ON_SELECTED_ITEM_START = 0; +/** + * The flag selected item end of ccui.ListView's event. + * @constant + * @type {number} + */ +ccui.ListView.ON_SELECTED_ITEM_END = 1; + +//listView gravity +/** + * The left flag of ccui.ListView's gravity. + * @constant + * @type {number} + */ +ccui.ListView.GRAVITY_LEFT = 0; +/** + * The right flag of ccui.ListView's gravity. + * @constant + * @type {number} + */ +ccui.ListView.GRAVITY_RIGHT = 1; +/** + * The center horizontal flag of ccui.ListView's gravity. + * @constant + * @type {number} + */ +ccui.ListView.GRAVITY_CENTER_HORIZONTAL = 2; +/** + * The top flag of ccui.ListView's gravity. + * @constant + * @type {number} + */ +ccui.ListView.GRAVITY_TOP = 3; +/** + * The bottom flag of ccui.ListView's gravity. + * @constant + * @type {number} + */ +ccui.ListView.GRAVITY_BOTTOM = 4; +/** + * The center vertical flag of ccui.ListView's gravity. + * @constant + * @type {number} + */ +ccui.ListView.GRAVITY_CENTER_VERTICAL = 5; + +/** + * The flag of ccui.ListView's magnetic none type. + * @constant + * @type {number} + */ +ccui.ListView.MAGNETIC_NONE = 0; +/** + * The flag of ccui.ListView's magnetic center type.
+ * ListView tries to align its items in center of current view. + * @constant + * @type {number} + */ +ccui.ListView.MAGNETIC_CENTER = 1; +/** + * The flag of ccui.ListView's magnetic both end type.
+ * ListView tries to align its items in left or right end if it is horizontal, top or bottom in vertical.
+ * The aligning side (left or right, top or bottom) is determined by user's scroll direction. + * @constant + * @type {number} + */ +ccui.ListView.MAGNETIC_BOTH_END = 2; +/** + * The flag of ccui.ListView's magnetic left type. + * @constant + * @type {number} + */ +ccui.ListView.MAGNETIC_LEFT = 3; +/** + * The flag of ccui.ListView's magnetic right type. + * @constant + * @type {number} + */ +ccui.ListView.MAGNETIC_RIGHT = 4; +/** + * The flag of ccui.ListView's magnetic top type. + * @constant + * @type {number} + */ +ccui.ListView.MAGNETIC_TOP = 5; +/** + * The flag of ccui.ListView's magnetic bottom type. + * @constant + * @type {number} + */ +ccui.ListView.MAGNETIC_BOTTOM = 6; diff --git a/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/scroll-widget/UIPageView.js b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/scroll-widget/UIPageView.js new file mode 100644 index 0000000..e378537 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/scroll-widget/UIPageView.js @@ -0,0 +1,593 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The PageView control of Cocos UI. + * @class + * @extends ccui.ListView + * @example + * var pageView = new ccui.PageView(); + * pageView.setTouchEnabled(true); + * pageView.addPage(new ccui.Layout()); + * this.addChild(pageView); + */ +ccui.PageView = ccui.ListView.extend(/** @lends ccui.PageView# */{ + _curPageIdx: 0, + _childFocusCancelOffset: 0, + _pageViewEventListener: null, + _pageViewEventSelector: null, + _className: "PageView", + + _indicator: null, + _indicatorPositionAsAnchorPoint: null, + /** + * Allocates and initializes a UIPageView. + * Constructor of ccui.PageView. please do not call this function by yourself, you should pass the parameters to constructor to initialize it
. + * @example + * // example + * var uiPageView = new ccui.PageView(); + */ + ctor: function () { + ccui.ListView.prototype.ctor.call(this); + + this._childFocusCancelOffset = 5; + this._indicatorPositionAsAnchorPoint = cc.p(0.5, 0.1); + this._pageViewEventListener = null; + this._pageViewEventSelector = null; + + this.setDirection(ccui.ScrollView.DIR_HORIZONTAL); + this.setMagneticType(ccui.ListView.MAGNETIC_CENTER); + this.setScrollBarEnabled(false); + }, + + /** + * Add a widget to a page of PageView. + * @deprecated since v3.9, please use 'insertPage(Widget* page, int idx)' instead. + * @param {ccui.Widget} widget widget to be added to PageView. + * @param {number} pageIdx index of page. + * @param {Boolean} forceCreate if force create and there is no page exist, PageView would create a default page for adding widget. + */ + addWidgetToPage: function (widget, pageIdx, forceCreate) { + this.insertCustomItem(widget, pageIdx); + }, + + /** + * Insert a page into the end of PageView. + * @param {ccui.Widget} page Page to be inserted. + */ + addPage: function (page) { + this.pushBackCustomItem(page); + }, + + /** + * Insert a page into PageView at a given index. + * @param {ccui.Widget} page Page to be inserted. + * @param {number} idx A given index. + */ + insertPage: function (page, idx) { + this.insertCustomItem(page, idx); + }, + + /** + * Removes a page from PageView. + * @param {ccui.Widget} page Page to be removed. + */ + removePage: function (page) { + this.removeItem(this.getIndex(page)); + }, + + /** + * Removes a page at index of PageView. + * @param {number} index A given index. + */ + removePageAtIndex: function (index) { + this.removeItem(index); + }, + + /** + * Removes all pages from PageView + */ + removeAllPages: function () { + this.removeAllItems(); + }, + + /** + * scroll PageView to index. + * @param {number} idx A given index in the PageView. Index start from 0 to pageCount -1. + */ + scrollToItem: function (idx) { + ccui.ListView.prototype.scrollToItem.call(this, idx, cc.p(0.5, 0.5), cc.p(0.5, 0.5)); + }, + + /** + * scroll PageView to index. + * @param {number} idx A given index in the PageView. Index start from 0 to pageCount -1. + */ + scrollToPage: function (idx) { + this.scrollToItem(idx); + }, + + + _doLayout: function () { + if (!this._refreshViewDirty) + return; + + ccui.ListView.prototype._doLayout.call(this); + + if (this._indicator) { + var index = this.getIndex(this.getCenterItemInCurrentView()); + this._indicator.indicate(index); + } + + this._refreshViewDirty = false; + }, + + /** + * Changes scroll direction of ccui.PageView. + * @param {ccui.ScrollView.DIR_NONE | ccui.ScrollView.DIR_VERTICAL | ccui.ScrollView.DIR_HORIZONTAL | ccui.ScrollView.DIR_BOTH} direction + */ + setDirection: function (direction) { + ccui.ListView.prototype.setDirection.call(this, direction); + if (direction === ccui.ScrollView.DIR_HORIZONTAL) { + this._indicatorPositionAsAnchorPoint = cc.p(0.5, 0.1); + } + else if (direction === ccui.ScrollView.DIR_VERTICAL) { + this._indicatorPositionAsAnchorPoint = cc.p(0.1, 0.5); + } + + if (this._indicator) { + this._indicator.setDirection(direction); + this._refreshIndicatorPosition(); + } + }, + + /** + * Set custom scroll threshold to page view. If you don't specify the value, the pageView will scroll when half page view width reached. + * @since v3.2 + * @param threshold + * @deprecated Since v3.9, this method has no effect. + */ + setCustomScrollThreshold: function (threshold) { + + }, + + /** + * Returns user defined scroll page threshold. + * @since v3.2 + * @deprecated Since v3.9, this method always returns 0. + */ + getCustomScrollThreshold: function () { + return 0; + }, + + /** + * Set using user defined scroll page threshold or not. If you set it to false, then the default scroll threshold is pageView.width / 2. + * @since v3.2 + * @deprecated Since v3.9, this method has no effect. + */ + setUsingCustomScrollThreshold: function (flag) { + }, + + /** + * Queries whether we are using user defined scroll page threshold or not + * @deprecated Since v3.9, this method always returns false. + */ + isUsingCustomScrollThreshold: function () { + return false; + }, + + _moveInnerContainer: function (deltaMove, canStartBounceBack) { + ccui.ListView.prototype._moveInnerContainer.call(this, deltaMove, canStartBounceBack); + this._curPageIdx = this.getIndex(this.getCenterItemInCurrentView()); + if (this._indicator) { + this._indicator.indicate(this._curPageIdx); + } + }, + + _onItemListChanged: function () { + ccui.ListView.prototype._onItemListChanged.call(this); + if (this._indicator) { + this._indicator.reset(this._items.length); + } + }, + + _onSizeChanged: function () { + ccui.ListView.prototype._onSizeChanged.call(this); + this._refreshIndicatorPosition(); + }, + + _remedyLayoutParameter: function (item) { + item.setContentSize(this.getContentSize()); + ccui.ListView.prototype._remedyLayoutParameter.call(this, item); + }, + + _refreshIndicatorPosition: function () { + if (this._indicator) { + var contentSize = this.getContentSize(); + var posX = contentSize.width * this._indicatorPositionAsAnchorPoint.x; + var posY = contentSize.height * this._indicatorPositionAsAnchorPoint.y; + this._indicator.setPosition(cc.p(posX, posY)); + } + }, + + _handleReleaseLogic: function (touchPoint) { + + ccui.ScrollView.prototype._handleReleaseLogic.call(this, touchPoint); + + if (this._items.length <= 0) + return; + + var touchMoveVelocity = this._flattenVectorByDirection(this._calculateTouchMoveVelocity()); + + var INERTIA_THRESHOLD = 500; + if (cc.pLength(touchMoveVelocity) < INERTIA_THRESHOLD) { + this._startMagneticScroll(); + } + else { + // Handle paging by inertia force. + var currentPage = this.getItem(this._curPageIdx); + var destination = this._calculateItemDestination(cc.p(0.5, 0.5), currentPage, cc.p(0.5, 0.5)); + var deltaToCurrentPage = cc.pSub(destination, this.getInnerContainerPosition()); + deltaToCurrentPage = this._flattenVectorByDirection(deltaToCurrentPage); + + // If the direction of displacement to current page and the direction of touch are same, just start magnetic scroll to the current page. + // Otherwise, move to the next page of touch direction. + if (touchMoveVelocity.x * deltaToCurrentPage.x > 0 || touchMoveVelocity.y * deltaToCurrentPage.y > 0) { + this._startMagneticScroll(); + } + else { + if (touchMoveVelocity.x < 0 || touchMoveVelocity.y > 0) { + ++this._curPageIdx; + } + else { + --this._curPageIdx; + } + this._curPageIdx = Math.min(this._curPageIdx, this._items.length); + this._curPageIdx = Math.max(this._curPageIdx, 0); + this.scrollToItem(this._curPageIdx); + } + } + + }, + + _getAutoScrollStopEpsilon: function () { + return 0.001; + }, + + _pageTurningEvent: function () { + if (this._pageViewEventSelector) { + if (this._pageViewEventListener) + this._pageViewEventSelector.call(this._pageViewEventListener, this, ccui.PageView.EVENT_TURNING); + else + this._pageViewEventSelector(this, ccui.PageView.EVENT_TURNING); + } + if (this._ccEventCallback) + this._ccEventCallback(this, ccui.PageView.EVENT_TURNING); + }, + + /** + * Adds event listener to ccui.PageView. + * @param {Function} selector + * @param {Object} [target=] + * @deprecated since v3.0, please use addEventListener instead. + */ + addEventListenerPageView: function (selector, target) { + this._pageViewEventSelector = selector; + this._pageViewEventListener = target; + }, + + addEventListener: function (selector) { + this._ccEventCallback = function (ref, eventType) { + if (eventType == ccui.ScrollView.EVENT_AUTOSCROLL_ENDED) + selector(this, eventType) + }; + }, + + /** + * Jump to a page with a given index without scrolling. + * This is the different between scrollToPage. + * @param {number} index A given index in PageView. Index start from 0 to pageCount -1. + */ + setCurrentPageIndex: function (index) { + this.jumpToItem(index, cc.p(0.5, 0.5), cc.p(0.5, 0.5)); + }, + + /** + * Jump to a page with a given index without scrolling. + * This is the different between scrollToPage. + * @param {number} index A given index in PageView. Index start from 0 to pageCount -1. + * @deprecated since v3.9, this is deprecated. Use `setCurrentPageIndex()` instead. + */ + setCurPageIndex: function (index) { + this.setCurrentPageIndex(index); + }, + + /** + * Returns current page index + * @returns {number} + */ + getCurrentPageIndex: function () { + return this._curPageIdx; + }, + + /** + * Returns current page index + * @deprecated since v3.9, this is deprecated. Use `getCurrentPageIndex()` instead. + * @returns {number} + */ + getCurPageIndex: function () { + var widget = this.getCenterItemInCurrentView(); + return this.getIndex(widget); + }, + + /** + * Returns all pages of PageView + * @returns {Array} + */ + getPages: function () { + return this.getItems(); + }, + + /** + * Returns a page from PageView by index + * @param {Number} index + * @returns {ccui.Layout} + */ + getPage: function (index) { + return this.getItem(index); + }, + + /** + * Returns the "class name" of ccui.PageView. + * @returns {string} + */ + getDescription: function () { + return "PageView"; + }, + + _createCloneInstance: function () { + return new ccui.PageView(); + }, + + _copyClonedWidgetChildren: function (model) { + var arrayPages = model.getPages(); + for (var i = 0; i < arrayPages.length; i++) { + var page = arrayPages[i]; + this.addPage(page.clone()); + } + }, + + _copySpecialProperties: function (pageView) { + ccui.ListView.prototype._copySpecialProperties.call(this, pageView); + this._ccEventCallback = pageView._ccEventCallback; + this._pageViewEventListener = pageView._pageViewEventListener; + this._pageViewEventSelector = pageView._pageViewEventSelector; + this._customScrollThreshold = pageView._customScrollThreshold; + }, + + + /** + * Toggle page indicator enabled. + * @param {boolean} enabled True if enable page indicator, false otherwise. + */ + setIndicatorEnabled: function (enabled) { + if (enabled == (this._indicator !== null)) { + return; + } + + if (!enabled) { + this.removeProtectedChild(this._indicator); + this._indicator = null; + } + else { + this._indicator = new ccui.PageViewIndicator(); + this._indicator.setDirection(this.getDirection()); + this.addProtectedChild(this._indicator, 10000); + this.setIndicatorSelectedIndexColor(cc.color(100, 100, 255)); + this._refreshIndicatorPosition(); + } + }, + + /** + * Query page indicator state. + * @returns {boolean} True if page indicator is enabled, false otherwise. + */ + getIndicatorEnabled: function () { + return this._indicator !== null; + }, + + /** + * Set the page indicator's position using anchor point. + * @param {cc.Point} positionAsAnchorPoint The position as anchor point. + */ + setIndicatorPositionAsAnchorPoint: function (positionAsAnchorPoint) { + this._indicatorPositionAsAnchorPoint = positionAsAnchorPoint; + this._refreshIndicatorPosition(); + }, + + /** + * Get the page indicator's position as anchor point. + * @returns {cc.Point} + */ + getIndicatorPositionAsAnchorPoint: function () { + return this._indicatorPositionAsAnchorPoint; + }, + + /** + * Set the page indicator's position in page view. + * @param {cc.Point} position The position in page view + */ + setIndicatorPosition: function (position) { + if (this._indicator) { + var contentSize = this.getContentSize(); + this._indicatorPositionAsAnchorPoint.x = position.x / contentSize.width; + this._indicatorPositionAsAnchorPoint.y = position.y / contentSize.height; + this._indicator.setPosition(position); + } + }, + + /** + * Get the page indicator's position. + * @returns {cc.Point} + */ + getIndicatorPosition: function () { + cc.assert(this._indicator !== null, ""); + return this._indicator.getPosition(); + }, + + /** + * Set space between page indicator's index nodes. + * @param {number} spaceBetweenIndexNodes Space between nodes in pixel. + */ + setIndicatorSpaceBetweenIndexNodes: function (spaceBetweenIndexNodes) { + if (this._indicator) { + this._indicator.setSpaceBetweenIndexNodes(spaceBetweenIndexNodes); + } + }, + + /** + * Get the space between page indicator's index nodes. + * @returns {number} + */ + getIndicatorSpaceBetweenIndexNodes: function () { + cc.assert(this._indicator !== null, ""); + return this._indicator.getSpaceBetweenIndexNodes(); + }, + + /** + * Set color of page indicator's selected index. + * @param {cc.Color} color Color for indicator + */ + setIndicatorSelectedIndexColor: function (color) { + if (this._indicator) { + this._indicator.setSelectedIndexColor(color); + } + }, + + /** + * Get the color of page indicator's selected index. + * @returns {cc.Color} + */ + getIndicatorSelectedIndexColor: function () { + cc.assert(this._indicator !== null, ""); + return this._indicator.getSelectedIndexColor(); + }, + + /** + * Set color of page indicator's index nodes. + * @param {cc.Color} color Color for indicator + */ + setIndicatorIndexNodesColor: function (color) { + if (this._indicator) { + this._indicator.setIndexNodesColor(color); + } + }, + + /** + * Get the color of page indicator's index nodes. + * @returns {cc.Color} + */ + getIndicatorIndexNodesColor: function () { + cc.assert(this._indicator !== null, ""); + return this._indicator.getIndexNodesColor(); + }, + + /** + * Set scale of page indicator's index nodes. + * @param {Number} scale Scale for indicator + */ + setIndicatorIndexNodesScale: function (indexNodesScale) { + if (this._indicator) { + this._indicator.setIndexNodesScale(indexNodesScale); + this._indicator.indicate(this._curPageIdx); + } + }, + + /** + * Get the scale of page indicator's index nodes. + * @returns {Number} + */ + getIndicatorIndexNodesScale: function () { + cc.assert(this._indicator !== null, ""); + return this._indicator.getIndexNodesScale(); + }, + + /** + * Sets texture of indicator index nodes + * @param {String} texName + * @param {ccui.Widget.LOCAL_TEXTURE | ccui.Widget.PLIST_TEXTURE} [texType = ccui.Widget.LOCAL_TEXTURE] + */ + setIndicatorIndexNodesTexture: function (texName, texType) { + if (this._indicator) { + this._indicator.setIndexNodesTexture(texName, texType); + this._indicator.indicate(this._curPageIdx); + } + } +}); +/** + * allocates and initializes a UIPageView. + * @deprecated since v3.0, please use new ccui.PageView() instead. + * @return {ccui.PageView} + */ +ccui.PageView.create = function () { + return new ccui.PageView(); +}; + +// Constants +//PageView event +/** + * The turning flag of ccui.PageView's event. + * @constant + * @type {number} + */ +ccui.PageView.EVENT_TURNING = 0; + +//PageView touch direction +/** + * The left flag of ccui.PageView's touch direction. + * @constant + * @type {number} + */ +ccui.PageView.TOUCH_DIR_LEFT = 0; +/** + * The right flag of ccui.PageView's touch direction. + * @constant + * @type {number} + */ +ccui.PageView.TOUCH_DIR_RIGHT = 1; + +//PageView auto scroll direction +/** + * The right flag of ccui.PageView's auto scroll direction. + * @constant + * @type {number} + */ +ccui.PageView.DIRECTION_LEFT = 0; +/** + * The right flag of ccui.PageView's auto scroll direction. + * @constant + * @type {number} + */ +ccui.PageView.DIRECTION_RIGHT = 1; diff --git a/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/scroll-widget/UIPageViewIndicator.js b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/scroll-widget/UIPageViewIndicator.js new file mode 100644 index 0000000..8044ba2 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/scroll-widget/UIPageViewIndicator.js @@ -0,0 +1,305 @@ +/**************************************************************************** + Copyright (c) 2015 Neo Kim (neo.kim@neofect.com) + Copyright (c) 2015 Nikita Besshaposhnikov (nikita.besshaposhnikov@gmail.com) + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The PageViewIndicator control of Cocos UI
+ * Indicator being attached to page view. + * @class + * @extends ccui.ProtectedNode + * @property {Number} spaceBetweenIndexNodes - Space between index nodes in PageViewIndicator + */ +ccui.PageViewIndicator = ccui.ProtectedNode.extend(/** @lends ccui.PageViewIndicator# */{ + _direction: null, + _indexNodes: null, + _currentIndexNode: null, + _spaceBetweenIndexNodes: 0, + _indexNodesScale: 1.0, + _indexNodesColor: null, + _useDefaultTexture: true, + _indexNodesTextureFile: "", + _indexNodesTexType: ccui.Widget.LOCAL_TEXTURE, + + _className: "PageViewIndicator", + + /** + * Allocates and initializes a PageViewIndicator. + * Constructor of ccui.PageViewIndicator. override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + */ + ctor: function () { + cc.ProtectedNode.prototype.ctor.call(this); + + this._direction = ccui.ScrollView.DIR_HORIZONTAL; + this._indexNodes = []; + this._spaceBetweenIndexNodes = ccui.PageViewIndicator.SPACE_BETWEEN_INDEX_NODES_DEFAULT; + this._indexNodesColor = cc.color.WHITE; + + this._currentIndexNode = ccui.helper._createSpriteFromBase64(ccui.PageViewIndicator.CIRCLE_IMAGE, ccui.PageViewIndicator.CIRCLE_IMAGE_KEY); + this._currentIndexNode.setVisible(false); + this.addProtectedChild(this._currentIndexNode, 1); + + // this.setCascadeColorEnabled(true); + // this.setCascadeOpacityEnabled(true); + }, + + /** + * Sets direction of indicator + * @param {ccui.ScrollView.DIR_NONE | ccui.ScrollView.DIR_VERTICAL | ccui.ScrollView.DIR_HORIZONTAL | ccui.ScrollView.DIR_BOTH} direction + */ + setDirection: function (direction) { + this._direction = direction; + this._rearrange(); + }, + + /** + * resets indicator with new page count. + * @param {number} numberOfTotalPages + */ + reset: function (numberOfTotalPages) { + while (this._indexNodes.length < numberOfTotalPages) { + this._increaseNumberOfPages(); + } + while (this._indexNodes.length > numberOfTotalPages) { + this._decreaseNumberOfPages(); + } + this._rearrange(); + this._currentIndexNode.setVisible(this._indexNodes.length > 0); + }, + + /** + * Indicates node by index + * @param {number} index + */ + indicate: function (index) { + if (index < 0 || index >= this._indexNodes.length) { + return; + } + this._currentIndexNode.setPosition(this._indexNodes[index].getPosition()); + }, + + _rearrange: function () { + if (this._indexNodes.length === 0) { + return; + } + + var horizontal = (this._direction === ccui.ScrollView.DIR_HORIZONTAL); + + // Calculate total size + var indexNodeSize = this._indexNodes[0].getContentSize(); + var sizeValue = (horizontal ? indexNodeSize.width : indexNodeSize.height); + + var numberOfItems = this._indexNodes.length; + var totalSizeValue = sizeValue * numberOfItems + this._spaceBetweenIndexNodes * (numberOfItems - 1); + + var posValue = -(totalSizeValue / 2) + (sizeValue / 2); + for (var i = 0; i < this._indexNodes.length; ++i) { + var position; + if (horizontal) { + position = cc.p(posValue, indexNodeSize.height / 2.0); + } + else { + position = cc.p(indexNodeSize.width / 2.0, -posValue); + } + this._indexNodes[i].setPosition(position); + posValue += sizeValue + this._spaceBetweenIndexNodes; + } + }, + + /** + * Sets space between index nodes. + * @param {number} spaceBetweenIndexNodes + */ + setSpaceBetweenIndexNodes: function (spaceBetweenIndexNodes) { + if (this._spaceBetweenIndexNodes === spaceBetweenIndexNodes) { + return; + } + this._spaceBetweenIndexNodes = spaceBetweenIndexNodes; + this._rearrange(); + }, + + /** + * Gets space between index nodes. + * @returns {number} + */ + getSpaceBetweenIndexNodes: function () { + return this._spaceBetweenIndexNodes; + }, + + /** + * Sets color of selected index node + * @param {cc.Color} color + */ + setSelectedIndexColor: function (color) { + this._currentIndexNode.setColor(color); + }, + + /** + * Gets color of selected index node + * @returns {cc.Color} + */ + getSelectedIndexColor: function () { + return this._currentIndexNode.getColor(); + }, + + /** + * Sets color of index nodes + * @param {cc.Color} indexNodesColor + */ + setIndexNodesColor: function (indexNodesColor) { + this._indexNodesColor = indexNodesColor; + + for (var i = 0; i < this._indexNodes.length; ++i) { + this._indexNodes[i].setColor(indexNodesColor); + } + }, + + /** + * Gets color of index nodes + * @returns {cc.Color} + */ + getIndexNodesColor: function () { + var locRealColor = this._indexNodesColor; + return cc.color(locRealColor.r, locRealColor.g, locRealColor.b, locRealColor.a); + }, + + /** + * Sets scale of index nodes + * @param {Number} indexNodesScale + */ + setIndexNodesScale: function (indexNodesScale) { + if (this._indexNodesScale === indexNodesScale) { + return; + } + this._indexNodesScale = indexNodesScale; + + this._currentIndexNode.setScale(indexNodesScale); + + for (var i = 0; i < this._indexNodes.length; ++i) { + this._indexNodes[i].setScale(this, _indexNodesScale); + } + + this._rearrange(); + }, + + /** + * Gets scale of index nodes + * @returns {Number} + */ + getIndexNodesScale: function () { + return this._indexNodesScale; + }, + + /** + * Sets texture of index nodes + * @param {String} texName + * @param {ccui.Widget.LOCAL_TEXTURE | ccui.Widget.PLIST_TEXTURE} [texType = ccui.Widget.LOCAL_TEXTURE] + */ + setIndexNodesTexture: function (texName, texType) { + if (texType === undefined) + texType = ccui.Widget.LOCAL_TEXTURE; + + this._useDefaultTexture = false; + this._indexNodesTextureFile = texName; + this._indexNodesTexType = texType; + + switch (texType) { + case ccui.Widget.LOCAL_TEXTURE: + this._currentIndexNode.setTexture(texName); + for (var i = 0; i < this._indexNodes.length; ++i) { + this._indexNodes[i].setTexture(texName); + } + break; + case ccui.Widget.PLIST_TEXTURE: + this._currentIndexNode.setSpriteFrame(texName); + for (var i = 0; i < this._indexNodes.length; ++i) { + this._indexNodes[i].setSpriteFrame(texName); + } + break; + default: + break; + } + + this._rearrange(); + }, + + _increaseNumberOfPages: function () { + var indexNode; + + if (this._useDefaultTexture) { + indexNode = ccui.helper._createSpriteFromBase64(ccui.PageViewIndicator.CIRCLE_IMAGE, ccui.PageViewIndicator.CIRCLE_IMAGE_KEY); + } + else { + indexNode = new cc.Sprite(); + switch (this._indexNodesTexType) { + case ccui.Widget.LOCAL_TEXTURE: + indexNode.initWithFile(this._indexNodesTextureFile); + break; + case ccui.Widget.PLIST_TEXTURE: + indexNode.initWithSpriteFrameName(this._indexNodesTextureFile); + break; + default: + break; + } + } + + indexNode.setColor(this._indexNodesColor); + indexNode.setScale(this._indexNodesScale); + + this.addProtectedChild(indexNode); + this._indexNodes.push(indexNode); + }, + + _decreaseNumberOfPages: function () { + if (this._indexNodes.length === 0) { + return; + } + this.removeProtectedChild(this._indexNodes[0]); + this._indexNodes.splice(0, 1); + }, + + /** + * Removes all index nodes. + */ + clear: function () { + for (var i = 0; i < this._indexNodes.length; ++i) { + this.removeProtectedChild(this._indexNodes[i]); + } + this._indexNodes.length = 0; + this._currentIndexNode.setVisible(false); + } + +}); + +var _p = ccui.PageViewIndicator.prototype; + +// Extended properties +/** @expose */ +_p.spaceBetweenIndexNodes; +cc.defineGetterSetter(_p, "spaceBetweenIndexNodes", _p.getSpaceBetweenIndexNodes, _p.setSpaceBetweenIndexNodes); +/** + * @ignore + */ +ccui.PageViewIndicator.SPACE_BETWEEN_INDEX_NODES_DEFAULT = 23; +ccui.PageViewIndicator.CIRCLE_IMAGE_KEY = "/__circle_image"; +ccui.PageViewIndicator.CIRCLE_IMAGE = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAA8ElEQVRIx62VyRGCQBBF+6gWRCEmYDIQkhiBCgHhSclC8YqWzOV5oVzKAYZp3r1/9fpbxAIBMTsKrjx5cqVgR0wgLhCRUWOjJiPqD56xoaGPhpRZV/iSEy6crHmw5oIrF9b/lVeMofrJgjlnxlIy/wik+JB+mme8BExbBhm+5CJC2LE2LtSEQoyGWDioBA5CoRIohJtK4CYDxzNEM4GAugR1E9VjVC+SZpXvhCJCrjomESLvc17pDGX7bWmlh6UtpjPVCWy9zaJ0TD7qfm3pwERMz2trRVZk3K3BD/L34AY+dEDCniMVBkPFkT2J/b2/AIV+dRpFLOYoAAAAAElFTkSuQmCC"; diff --git a/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/scroll-widget/UIScrollView.js b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/scroll-widget/UIScrollView.js new file mode 100644 index 0000000..79741c9 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/scroll-widget/UIScrollView.js @@ -0,0 +1,1927 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The ScrollView control of Cocos UI + * @class + * @extends ccui.Layout + * + * @property {Number} innerWidth - Inner container width of the scroll view + * @property {Number} innerHeight - Inner container height of the scroll view + * @property {ccui.ScrollView.DIR_NONE | ccui.ScrollView.DIR_VERTICAL | ccui.ScrollView.DIR_HORIZONTAL | ccui.ScrollView.DIR_BOTH} direction - Scroll direction of the scroll view + * @property {Boolean} bounceEnabled - Indicate whether bounce is enabled + * @property {Boolean} inertiaScrollEnabled - Indicate whether inertiaScroll is enabled + * @property {Number} touchTotalTimeThreshold - Touch total time threshold + */ +ccui.ScrollView = ccui.Layout.extend(/** @lends ccui.ScrollView# */{ + _innerContainer: null, + _direction: null, + + _topBoundary: 0, + _bottomBoundary: 0, + _leftBoundary: 0, + _rightBoundary: 0, + + _touchMoveDisplacements: null, + _touchMoveTimeDeltas: null, + _touchMovePreviousTimestamp: 0, + _touchTotalTimeThreshold: 0.5, + + _autoScrolling: false, + _autoScrollTargetDelta: null, + _autoScrollAttenuate: true, + _autoScrollStartPosition: null, + _autoScrollTotalTime: 0, + _autoScrollAccumulatedTime: 0, + _autoScrollCurrentlyOutOfBoundary: false, + _autoScrollBraking: false, + _autoScrollBrakingStartPosition: null, + + _bePressed: false, + + _childFocusCancelOffset: 0, + + bounceEnabled: false, + + _outOfBoundaryAmount: null, + _outOfBoundaryAmountDirty: true, + + inertiaScrollEnabled: false, + + _scrollBarEnabled: true, + _verticalScrollBar: null, + _horizontalScrollBar: null, + + _scrollViewEventListener: null, + _scrollViewEventSelector: null, + _className: "ScrollView", + + /** + * Allocates and initializes a UIScrollView. + * Constructor of ccui.ScrollView. override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @example + * // example + * var uiScrollView = new ccui.ScrollView(); + */ + ctor: function () { + ccui.Layout.prototype.ctor.call(this); + this.setClippingEnabled(true); + this._innerContainer.setTouchEnabled(false); + + this._direction = ccui.ScrollView.DIR_NONE; + + this._childFocusCancelOffset = 5; + this.inertiaScrollEnabled = true; + + this._outOfBoundaryAmount = cc.p(0, 0); + this._autoScrollTargetDelta = cc.p(0, 0); + this._autoScrollStartPosition = cc.p(0, 0); + this._autoScrollBrakingStartPosition = cc.p(0, 0); + this._touchMoveDisplacements = []; + this._touchMoveTimeDeltas = []; + this._touchMovePreviousTimestamp = 0; + + this._scrollBarEnabled = true; + this._initScrollBar(); + + this.setTouchEnabled(true); + }, + + /** + * Calls the parent class' onEnter and schedules update function. + * @override + */ + onEnter: function () { + ccui.Layout.prototype.onEnter.call(this); + this.scheduleUpdate(); + }, + + onExit: function () { + cc.renderer._removeCache(this.__instanceId); + ccui.Layout.prototype.onExit.call(this); + }, + + visit: function (parent) { + var cmd = this._renderCmd, parentCmd = parent ? parent._renderCmd : null; + + // quick return if not visible + if (!this._visible) { + cmd._propagateFlagsDown(parentCmd); + return; + } + + this._adaptRenderers(); + this._doLayout(); + + var renderer = cc.renderer; + cmd.visit(parentCmd); + + renderer.pushRenderCommand(cmd); + if (cmd instanceof ccui.ScrollView.WebGLRenderCmd) { + var currentID = this.__instanceId; + renderer._turnToCacheMode(currentID); + } + + var stencilClipping = this._clippingEnabled && this._clippingType === ccui.Layout.CLIPPING_STENCIL; + var scissorClipping = this._clippingEnabled && this._clippingType === ccui.Layout.CLIPPING_SCISSOR; + + if (stencilClipping) { + cmd.stencilClippingVisit(parentCmd); + } + else if (scissorClipping) { + cmd.scissorClippingVisit(parentCmd); + } + + var i, children = this._children, len = children.length, child; + var j, pChildren = this._protectedChildren, pLen = pChildren.length, pChild; + + if (this._reorderChildDirty) this.sortAllChildren(); + if (this._reorderProtectedChildDirty) this.sortAllProtectedChildren(); + for (i = 0; i < len; i++) { + child = children[i]; + if (child && child._visible) { + child.visit(this); + } + } + for (j = 0; j < pLen; j++) { + pChild = pChildren[j]; + if (pChild && pChild._visible) { + cmd._changeProtectedChild(pChild); + pChild.visit(this); + } + } + + if (stencilClipping) { + cmd.postStencilVisit(); + } + else if (scissorClipping) { + cmd.postScissorVisit(); + } + + if (cmd instanceof ccui.ScrollView.WebGLRenderCmd) { + renderer._turnToNormalMode(); + } + + // Need to update children after do layout + this.updateChildren(); + + cmd._dirtyFlag = 0; + }, + + /** + * When a widget is in a layout, you could call this method to get the next focused widget within a specified _direction.
+ * If the widget is not in a layout, it will return itself + * + * @param {Number} _direction the _direction to look for the next focused widget in a layout + * @param {ccui.Widget} current the current focused widget + * @returns {ccui.Widget} + */ + findNextFocusedWidget: function (direction, current) { + if (this.getLayoutType() === ccui.Layout.LINEAR_VERTICAL + || this.getLayoutType() === ccui.Layout.LINEAR_HORIZONTAL) { + return this._innerContainer.findNextFocusedWidget(direction, current); + } else + return ccui.Widget.prototype.findNextFocusedWidget.call(this, direction, current); + }, + + _initRenderer: function () { + ccui.Layout.prototype._initRenderer.call(this); + + this._innerContainer = new ccui.Layout(); + this._innerContainer.setColor(cc.color(255, 255, 255)); + this._innerContainer.setOpacity(255); + this._innerContainer.setCascadeColorEnabled(true); + this._innerContainer.setCascadeOpacityEnabled(true); + + this.addProtectedChild(this._innerContainer, 1, 1); + }, + + _createRenderCmd: function () { + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) + return new ccui.ScrollView.WebGLRenderCmd(this); + else + return new ccui.ScrollView.CanvasRenderCmd(this); + }, + + _onSizeChanged: function () { + ccui.Layout.prototype._onSizeChanged.call(this); + var locSize = this._contentSize; + this._topBoundary = locSize.height; + this._rightBoundary = locSize.width; + var innerSize = this._innerContainer.getContentSize(); + this._innerContainer.setContentSize(cc.size(Math.max(innerSize.width, locSize.width), Math.max(innerSize.height, locSize.height))); + this._innerContainer.setPosition(0, locSize.height - this._innerContainer.getContentSize().height); + + if(this._verticalScrollBar) + this._verticalScrollBar.onScrolled(this._getHowMuchOutOfBoundary()); + + if(this._horizontalScrollBar) + this._horizontalScrollBar.onScrolled(this._getHowMuchOutOfBoundary()); + }, + + /** + * Changes inner container size of ScrollView.
+ * Inner container size must be larger than or equal the size of ScrollView. + * @param {cc.Size} size inner container size. + */ + setInnerContainerSize: function (size) { + var innerContainer = this._innerContainer, + locSize = this._contentSize, + innerSizeWidth = locSize.width, innerSizeHeight = locSize.height; + + if (size.width < locSize.width) + cc.log("Inner width <= ScrollView width, it will be force sized!"); + else + innerSizeWidth = size.width; + + if (size.height < locSize.height) + cc.log("Inner height <= ScrollView height, it will be force sized!"); + else + innerSizeHeight = size.height; + + innerContainer.setContentSize(cc.size(innerSizeWidth, innerSizeHeight)); + + var pos = this._innerContainer.getPosition(); + var contAP = this._innerContainer.getAnchorPoint(); + + if (this._innerContainer.getLeftBoundary() != 0.0) + { + pos.x = contAP.x * innerSizeWidth; + } + if (this._innerContainer.getTopBoundary() != this._contentSize.height) + { + pos.y = this._contentSize.height - (1.0 - contAP.y) * innerSizeHeight; + } + this.setInnerContainerPosition(pos); + + this._updateScrollBar(cc.p(0 ,0)); + }, + + _setInnerWidth: function (width) { + var locW = this._contentSize.width, + innerWidth = locW, + container = this._innerContainer, + oldInnerWidth = container.width; + if (width < locW) + cc.log("Inner width <= scrollview width, it will be force sized!"); + else + innerWidth = width; + container.width = innerWidth; + + switch (this._direction) { + case ccui.ScrollView.DIR_HORIZONTAL: + case ccui.ScrollView.DIR_BOTH: + if (container.getRightBoundary() <= locW) { + var newInnerWidth = container.width; + var offset = oldInnerWidth - newInnerWidth; + this._scrollChildren(offset, 0); + } + break; + } + var innerAX = container.anchorX; + if (container.getLeftBoundary() > 0.0) + container.x = innerAX * innerWidth; + if (container.getRightBoundary() < locW) + container.x = locW - ((1.0 - innerAX) * innerWidth); + }, + + _setInnerHeight: function (height) { + var locH = this._contentSize.height, + innerHeight = locH, + container = this._innerContainer, + oldInnerHeight = container.height; + if (height < locH) + cc.log("Inner height <= scrollview height, it will be force sized!"); + else + innerHeight = height; + container.height = innerHeight; + + switch (this._direction) { + case ccui.ScrollView.DIR_VERTICAL: + case ccui.ScrollView.DIR_BOTH: + var newInnerHeight = innerHeight; + var offset = oldInnerHeight - newInnerHeight; + this._scrollChildren(0, offset); + break; + } + var innerAY = container.anchorY; + if (container.getLeftBoundary() > 0.0) + container.y = innerAY * innerHeight; + if (container.getRightBoundary() < locH) + container.y = locH - ((1.0 - innerAY) * innerHeight); + }, + /** + * Set inner container position + * + * @param {cc.Point} position Inner container position. + */ + setInnerContainerPosition: function (position) { + if (position.x === this._innerContainer.getPositionX() && position.y === this._innerContainer.getPositionY()) { + return; + } + this._innerContainer.setPosition(position); + this._outOfBoundaryAmountDirty = true; + + // Process bouncing events + if (this.bounceEnabled) { + for (var _direction = ccui.ScrollView.MOVEDIR_TOP; _direction < ccui.ScrollView.MOVEDIR_RIGHT; ++_direction) { + if (this._isOutOfBoundary(_direction)) { + this._processScrollEvent(_direction, true); + } + } + } + + this._dispatchEvent(ccui.ScrollView.EVENT_CONTAINER_MOVED); + }, + + /** + * Get inner container position + * + * @return The inner container position. + */ + getInnerContainerPosition: function () { + return this._innerContainer.getPosition(); + }, + + /** + * Returns inner container size of ScrollView.
+ * Inner container size must be larger than or equal ScrollView's size. + * + * @return {cc.Size} inner container size. + */ + getInnerContainerSize: function () { + return this._innerContainer.getContentSize(); + }, + _getInnerWidth: function () { + return this._innerContainer.width; + }, + _getInnerHeight: function () { + return this._innerContainer.height; + }, + + _isInContainer: function (widget) { + if (!this._clippingEnabled) + return true; + var wPos = widget._position, + wSize = widget._contentSize, + wAnchor = widget._anchorPoint, + size = this._customSize, + pos = this._innerContainer._position, + bottom = 0, left = 0; + if ( + // Top + (bottom = wPos.y - wAnchor.y * wSize.height) >= size.height - pos.y || + // Bottom + bottom + wSize.height <= -pos.y || + // right + (left = wPos.x - wAnchor.x * wSize.width) >= size.width - pos.x || + // left + left + wSize.width <= -pos.x + ) + return false; + else return true; + }, + + updateChildren: function () { + var child, i, l; + var childrenArray = this._innerContainer._children; + for (i = 0, l = childrenArray.length; i < l; i++) { + child = childrenArray[i]; + if (child._inViewRect === true && this._isInContainer(child) === false) + child._inViewRect = false; + else if (child._inViewRect === false && this._isInContainer(child) === true) + child._inViewRect = true; + } + }, + /** + * Add child to ccui.ScrollView. + * @param {cc.Node} widget + * @param {Number} [zOrder] + * @param {Number|string} [tag] tag or name + * @returns {boolean} + */ + addChild: function (widget, zOrder, tag) { + if (!widget) + return false; + if (this._isInContainer(widget) === false) + widget._inViewRect = false; + zOrder = zOrder || widget.getLocalZOrder(); + tag = tag || widget.getTag(); + return this._innerContainer.addChild(widget, zOrder, tag); + }, + + /** + * Removes all children. + */ + removeAllChildren: function () { + this.removeAllChildrenWithCleanup(true); + }, + + /** + * Removes all children. + * @param {Boolean} cleanup + */ + removeAllChildrenWithCleanup: function (cleanup) { + this._innerContainer.removeAllChildrenWithCleanup(cleanup); + }, + + /** + * Removes widget child + * @override + * @param {ccui.Widget} child + * @param {Boolean} cleanup + * @returns {boolean} + */ + removeChild: function (child, cleanup) { + return this._innerContainer.removeChild(child, cleanup); + }, + + /** + * Returns inner container's children + * @returns {Array} + */ + getChildren: function () { + return this._innerContainer.getChildren(); + }, + + /** + * Gets the count of inner container's children + * @returns {Number} + */ + getChildrenCount: function () { + return this._innerContainer.getChildrenCount(); + }, + + /** + * Gets a child from the container given its tag + * @param {Number} tag + * @returns {ccui.Widget} + */ + getChildByTag: function (tag) { + return this._innerContainer.getChildByTag(tag); + }, + + /** + * Gets a child from the container given its name + * @param {String} name + * @returns {ccui.Widget} + */ + getChildByName: function (name) { + return this._innerContainer.getChildByName(name); + }, + + _flattenVectorByDirection: function (vector) { + var result = cc.p(0, 0); + result.x = (this._direction === ccui.ScrollView.DIR_VERTICAL ? 0 : vector.x); + result.y = (this._direction === ccui.ScrollView.DIR_HORIZONTAL ? 0 : vector.y); + return result; + }, + + _getHowMuchOutOfBoundary: function (addition) { + if (addition === undefined) + addition = cc.p(0, 0); + + if (addition.x === 0 && addition.y === 0 && !this._outOfBoundaryAmountDirty) { + return this._outOfBoundaryAmount; + } + + var outOfBoundaryAmount = cc.p(0, 0); + + if (this._innerContainer.getLeftBoundary() + addition.x > this._leftBoundary) { + outOfBoundaryAmount.x = this._leftBoundary - (this._innerContainer.getLeftBoundary() + addition.x); + } + else if (this._innerContainer.getRightBoundary() + addition.x < this._rightBoundary) { + outOfBoundaryAmount.x = this._rightBoundary - (this._innerContainer.getRightBoundary() + addition.x); + } + + if (this._innerContainer.getTopBoundary() + addition.y < this._topBoundary) { + outOfBoundaryAmount.y = this._topBoundary - (this._innerContainer.getTopBoundary() + addition.y); + } + else if (this._innerContainer.getBottomBoundary() + addition.y > this._bottomBoundary) { + outOfBoundaryAmount.y = this._bottomBoundary - (this._innerContainer.getBottomBoundary() + addition.y); + } + + if (addition.x === 0 && addition.y === 0) { + this._outOfBoundaryAmount = outOfBoundaryAmount; + this._outOfBoundaryAmountDirty = false; + } + return outOfBoundaryAmount; + }, + + _isOutOfBoundary: function (dir) { + var outOfBoundary = this._getHowMuchOutOfBoundary(); + if (dir !== undefined) { + switch (dir) { + case ccui.ScrollView.MOVEDIR_TOP: + return outOfBoundary.y > 0; + case ccui.ScrollView.MOVEDIR_BOTTOM: + return outOfBoundary.y < 0; + case ccui.ScrollView.MOVEDIR_LEFT: + return outOfBoundary.x < 0; + case ccui.ScrollView.MOVEDIR_RIGHT: + return outOfBoundary.x > 0; + } + } + else { + return !this._fltEqualZero(outOfBoundary); + } + + return false; + }, + + + _moveInnerContainer: function (deltaMove, canStartBounceBack) { + var adjustedMove = this._flattenVectorByDirection(deltaMove); + + this.setInnerContainerPosition(cc.pAdd(this.getInnerContainerPosition(), adjustedMove)); + + var outOfBoundary = this._getHowMuchOutOfBoundary(); + this._updateScrollBar(outOfBoundary); + + if (this.bounceEnabled && canStartBounceBack) { + this._startBounceBackIfNeeded(); + } + }, + + _updateScrollBar: function(outOfBoundary) + { + if(this._verticalScrollBar) + { + this._verticalScrollBar.onScrolled(outOfBoundary); + } + if(this._horizontalScrollBar) + { + this._horizontalScrollBar.onScrolled(outOfBoundary); + } + }, + + _calculateTouchMoveVelocity: function () { + var totalTime = 0; + for (var i = 0; i < this._touchMoveTimeDeltas.length; ++i) { + totalTime += this._touchMoveTimeDeltas[i]; + } + if (totalTime == 0 || totalTime >= this._touchTotalTimeThreshold) { + return cc.p(0, 0); + } + + var totalMovement = cc.p(0, 0); + + for (var i = 0; i < this._touchMoveDisplacements.length; ++i) { + totalMovement.x += this._touchMoveDisplacements[i].x; + totalMovement.y += this._touchMoveDisplacements[i].y; + } + + return cc.pMult(totalMovement, 1 / totalTime); + }, + + /** + * Set the touch total time threshold + * @param {Number} touchTotalTimeThreshold + */ + setTouchTotalTimeThreshold: function (touchTotalTimeThreshold) { + this._touchTotalTimeThreshold = touchTotalTimeThreshold; + }, + + + /** + * Get the touch total time threshold + * @returns {Number} + */ + getTouchTotalTimeThreshold: function () { + return this._touchTotalTimeThreshold; + }, + + _startInertiaScroll: function (touchMoveVelocity) { + var MOVEMENT_FACTOR = 0.7; + var inertiaTotalMovement = cc.pMult(touchMoveVelocity, MOVEMENT_FACTOR); + this._startAttenuatingAutoScroll(inertiaTotalMovement, touchMoveVelocity); + }, + + _startBounceBackIfNeeded: function () { + if (!this.bounceEnabled) { + return false; + } + var bounceBackAmount = this._getHowMuchOutOfBoundary(); + if (this._fltEqualZero(bounceBackAmount)) { + return false; + } + + var BOUNCE_BACK_DURATION = 1.0; + this._startAutoScroll(bounceBackAmount, BOUNCE_BACK_DURATION, true); + return true; + }, + + _startAutoScrollToDestination: function (destination, timeInSec, attenuated) { + this._startAutoScroll(cc.pSub(destination, this._innerContainer.getPosition()), timeInSec, attenuated); + }, + + _calculateAutoScrollTimeByInitialSpeed: function (initialSpeed) { + // Calculate the time from the initial speed according to quintic polynomial. + return Math.sqrt(Math.sqrt(initialSpeed / 5)); + }, + + _startAttenuatingAutoScroll: function (deltaMove, initialVelocity) { + var time = this._calculateAutoScrollTimeByInitialSpeed(cc.pLength(initialVelocity)); + this._startAutoScroll(deltaMove, time, true); + }, + + _startAutoScroll: function (deltaMove, timeInSec, attenuated) { + var adjustedDeltaMove = this._flattenVectorByDirection(deltaMove); + + this._autoScrolling = true; + this._autoScrollTargetDelta = adjustedDeltaMove; + this._autoScrollAttenuate = attenuated; + this._autoScrollStartPosition = this._innerContainer.getPosition(); + this._autoScrollTotalTime = timeInSec; + this._autoScrollAccumulatedTime = 0; + this._autoScrollBraking = false; + this._autoScrollBrakingStartPosition = cc.p(0, 0); + + // If the destination is also out of boundary of same side, start brake from beggining. + var currentOutOfBoundary = this._getHowMuchOutOfBoundary(); + if (!this._fltEqualZero(currentOutOfBoundary)) { + this._autoScrollCurrentlyOutOfBoundary = true; + var afterOutOfBoundary = this._getHowMuchOutOfBoundary(adjustedDeltaMove); + if (currentOutOfBoundary.x * afterOutOfBoundary.x > 0 || currentOutOfBoundary.y * afterOutOfBoundary.y > 0) { + this._autoScrollBraking = true; + } + } + }, + + /** + * Immediately stops inner container scroll initiated by any of the "scrollTo*" member functions + */ + stopAutoScroll: function () { + this._autoScrolling = false; + this._autoScrollAttenuate = true; + this._autoScrollTotalTime = 0; + this._autoScrollAccumulatedTime = 0; + }, + + _isNecessaryAutoScrollBrake: function () { + if (this._autoScrollBraking) { + return true; + } + + if (this._isOutOfBoundary()) { + // It just went out of boundary. + if (!this._autoScrollCurrentlyOutOfBoundary) { + this._autoScrollCurrentlyOutOfBoundary = true; + this._autoScrollBraking = true; + this._autoScrollBrakingStartPosition = this.getInnerContainerPosition(); + return true; + } + } + else { + this._autoScrollCurrentlyOutOfBoundary = false; + } + return false; + }, + + _getAutoScrollStopEpsilon: function () { + return 0.0001; + }, + + _fltEqualZero: function (point) { + return (Math.abs(point.x) <= 0.0001 && Math.abs(point.y) <= 0.0001); + }, + + _processAutoScrolling: function (deltaTime) { + var OUT_OF_BOUNDARY_BREAKING_FACTOR = 0.05; + // Make auto scroll shorter if it needs to deaccelerate. + var brakingFactor = (this._isNecessaryAutoScrollBrake() ? OUT_OF_BOUNDARY_BREAKING_FACTOR : 1); + + // Elapsed time + this._autoScrollAccumulatedTime += deltaTime * (1 / brakingFactor); + + // Calculate the progress percentage + var percentage = Math.min(1, this._autoScrollAccumulatedTime / this._autoScrollTotalTime); + if (this._autoScrollAttenuate) { + percentage -= 1; + percentage = percentage * percentage * percentage * percentage * percentage + 1; + } + + // Calculate the new position + var newPosition = cc.pAdd(this._autoScrollStartPosition, cc.pMult(this._autoScrollTargetDelta, percentage)); + var reachedEnd = Math.abs(percentage - 1) <= this._getAutoScrollStopEpsilon(); + + if (this.bounceEnabled) { + // The new position is adjusted if out of boundary + newPosition = cc.pAdd(this._autoScrollBrakingStartPosition, cc.pMult(cc.pSub(newPosition, this._autoScrollBrakingStartPosition), brakingFactor)); + } + else { + // Don't let go out of boundary + var moveDelta = cc.pSub(newPosition, this.getInnerContainerPosition()); + var outOfBoundary = this._getHowMuchOutOfBoundary(moveDelta); + if (!this._fltEqualZero(outOfBoundary)) { + newPosition.x += outOfBoundary.x; + newPosition.y += outOfBoundary.y; + + reachedEnd = true; + } + } + + // Finish auto scroll if it ended + if (reachedEnd) { + this._autoScrolling = false; + this._dispatchEvent(ccui.ScrollView.EVENT_AUTOSCROLL_ENDED); + } + + this._moveInnerContainer(cc.pSub(newPosition, this.getInnerContainerPosition()), reachedEnd); + }, + + _jumpToDestination: function (desOrX, y) { + if (desOrX.x === undefined) { + desOrX = cc.p(desOrX, y); + } + + this._autoScrolling = false; + this._moveInnerContainer(cc.pSub(desOrX, this.getInnerContainerPosition()), true); + }, + + _scrollChildren: function (deltaMove) { + var realMove = deltaMove; + if (this.bounceEnabled) { + // If the position of the inner container is out of the boundary, the offsets should be divided by two. + var outOfBoundary = this._getHowMuchOutOfBoundary(); + realMove.x *= (outOfBoundary.x == 0 ? 1 : 0.5); + realMove.y *= (outOfBoundary.y == 0 ? 1 : 0.5); + } + + if (!this.bounceEnabled) { + var outOfBoundary = this._getHowMuchOutOfBoundary(realMove); + realMove.x += outOfBoundary.x; + realMove.y += outOfBoundary.y; + } + + var scrolledToLeft = false; + var scrolledToRight = false; + var scrolledToTop = false; + var scrolledToBottom = false; + + if (realMove.y > 0.0) // up + { + var icBottomPos = this._innerContainer.getBottomBoundary(); + if (icBottomPos + realMove.y >= this._bottomBoundary) { + scrolledToBottom = true; + } + } + else if (realMove.y < 0.0) // down + { + var icTopPos = this._innerContainer.getTopBoundary(); + if (icTopPos + realMove.y <= this._topBoundary) { + scrolledToTop = true; + } + } + + if (realMove.x < 0.0) // left + { + var icRightPos = this._innerContainer.getRightBoundary(); + if (icRightPos + realMove.x <= this._rightBoundary) { + scrolledToRight = true; + } + } + else if (realMove.x > 0.0) // right + { + var icLeftPos = this._innerContainer.getLeftBoundary(); + if (icLeftPos + realMove.x >= this._leftBoundary) { + scrolledToLeft = true; + } + } + this._moveInnerContainer(realMove, false); + + if (realMove.x != 0 || realMove.y != 0) { + this._processScrollingEvent(); + } + if (scrolledToBottom) { + this._processScrollEvent(ccui.ScrollView.MOVEDIR_BOTTOM, false); + } + if (scrolledToTop) { + this._processScrollEvent(ccui.ScrollView.MOVEDIR_TOP, false); + } + if (scrolledToLeft) { + this._processScrollEvent(ccui.ScrollView.MOVEDIR_LEFT, false); + } + if (scrolledToRight) { + this._processScrollEvent(ccui.ScrollView.MOVEDIR_RIGHT, false); + } + }, + + /** + * Scroll inner container to bottom boundary of ScrollView. + * @param {Number} time + * @param {Boolean} attenuated + */ + scrollToBottom: function (time, attenuated) { + this._startAutoScrollToDestination(cc.p(this._innerContainer.getPositionX(), 0), time, attenuated); + }, + + /** + * Scroll inner container to top boundary of ScrollView. + * @param {Number} time + * @param {Boolean} attenuated + */ + scrollToTop: function (time, attenuated) { + this._startAutoScrollToDestination( + cc.p(this._innerContainer.getPositionX(), this._contentSize.height - this._innerContainer.getContentSize().height), time, attenuated); + }, + + /** + * Scroll inner container to left boundary of ScrollView. + * @param {Number} time + * @param {Boolean} attenuated + */ + scrollToLeft: function (time, attenuated) { + this._startAutoScrollToDestination(cc.p(0, this._innerContainer.getPositionY()), time, attenuated); + }, + + /** + * Scroll inner container to right boundary of ScrollView. + * @param {Number} time + * @param {Boolean} attenuated + */ + scrollToRight: function (time, attenuated) { + this._startAutoScrollToDestination( + cc.p(this._contentSize.width - this._innerContainer.getContentSize().width, this._innerContainer.getPositionY()), time, attenuated); + }, + + /** + * Scroll inner container to top and left boundary of ScrollView. + * @param {Number} time + * @param {Boolean} attenuated + */ + scrollToTopLeft: function (time, attenuated) { + if (this._direction !== ccui.ScrollView.DIR_BOTH) { + cc.log("Scroll direction is not both!"); + return; + } + this._startAutoScrollToDestination(cc.p(0, this._contentSize.height - this._innerContainer.getContentSize().height), time, attenuated); + }, + + /** + * Scroll inner container to top and right boundary of ScrollView. + * @param {Number} time + * @param {Boolean} attenuated + */ + scrollToTopRight: function (time, attenuated) { + if (this._direction !== ccui.ScrollView.DIR_BOTH) { + cc.log("Scroll direction is not both!"); + return; + } + var inSize = this._innerContainer.getContentSize(); + this._startAutoScrollToDestination(cc.p(this._contentSize.width - inSize.width, + this._contentSize.height - inSize.height), time, attenuated); + }, + + /** + * Scroll inner container to bottom and left boundary of ScrollView. + * @param {Number} time + * @param {Boolean} attenuated + */ + scrollToBottomLeft: function (time, attenuated) { + if (this._direction !== ccui.ScrollView.DIR_BOTH) { + cc.log("Scroll direction is not both!"); + return; + } + this._startAutoScrollToDestination(cc.p(0, 0), time, attenuated); + }, + + /** + * Scroll inner container to bottom and right boundary of ScrollView. + * @param {Number} time + * @param {Boolean} attenuated + */ + scrollToBottomRight: function (time, attenuated) { + if (this._direction !== ccui.ScrollView.DIR_BOTH) { + cc.log("Scroll direction is not both!"); + return; + } + this._startAutoScrollToDestination(cc.p(this._contentSize.width - this._innerContainer.getContentSize().width, 0), time, attenuated); + }, + + /** + * Scroll inner container to vertical percent position of ScrollView. + * @param {Number} percent + * @param {Number} time + * @param {Boolean} attenuated + */ + scrollToPercentVertical: function (percent, time, attenuated) { + var minY = this._contentSize.height - this._innerContainer.getContentSize().height; + var h = -minY; + this._startAutoScrollToDestination(cc.p(this._innerContainer.getPositionX(), minY + percent * h / 100), time, attenuated); + }, + + /** + * Scroll inner container to horizontal percent position of ScrollView. + * @param {Number} percent + * @param {Number} time + * @param {Boolean} attenuated + */ + scrollToPercentHorizontal: function (percent, time, attenuated) { + var w = this._innerContainer.getContentSize().width - this._contentSize.width; + this._startAutoScrollToDestination(cc.p(-(percent * w / 100), this._innerContainer.getPositionY()), time, attenuated); + }, + + /** + * Scroll inner container to both _direction percent position of ScrollView. + * @param {cc.Point} percent + * @param {Number} time + * @param {Boolean} attenuated + */ + scrollToPercentBothDirection: function (percent, time, attenuated) { + if (this._direction !== ccui.ScrollView.DIR_BOTH) + return; + var minY = this._contentSize.height - this._innerContainer.getContentSize().height; + var h = -minY; + var w = this._innerContainer.getContentSize().width - this._contentSize.width; + this._startAutoScrollToDestination(cc.p(-(percent.x * w / 100), minY + percent.y * h / 100), time, attenuated); + }, + + /** + * Move inner container to bottom boundary of ScrollView. + */ + jumpToBottom: function () { + this._jumpToDestination(this._innerContainer.getPositionX(), 0); + }, + + /** + * Move inner container to top boundary of ScrollView. + */ + jumpToTop: function () { + this._jumpToDestination(this._innerContainer.getPositionX(), this._contentSize.height - this._innerContainer.getContentSize().height); + }, + + /** + * Move inner container to left boundary of ScrollView. + */ + jumpToLeft: function () { + this._jumpToDestination(0, this._innerContainer.getPositionY()); + }, + + /** + * Move inner container to right boundary of ScrollView. + */ + jumpToRight: function () { + this._jumpToDestination(this._contentSize.width - this._innerContainer.getContentSize().width, this._innerContainer.getPositionY()); + }, + + /** + * Move inner container to top and left boundary of ScrollView. + */ + jumpToTopLeft: function () { + if (this._direction !== ccui.ScrollView.DIR_BOTH) { + cc.log("Scroll _direction is not both!"); + return; + } + this._jumpToDestination(0, this._contentSize.height - this._innerContainer.getContentSize().height); + }, + + /** + * Move inner container to top and right boundary of ScrollView. + */ + jumpToTopRight: function () { + if (this._direction !== ccui.ScrollView.DIR_BOTH) { + cc.log("Scroll _direction is not both!"); + return; + } + var inSize = this._innerContainer.getContentSize(); + this._jumpToDestination(this._contentSize.width - inSize.width, this._contentSize.height - inSize.height); + }, + + /** + * Move inner container to bottom and left boundary of ScrollView. + */ + jumpToBottomLeft: function () { + if (this._direction !== ccui.ScrollView.DIR_BOTH) { + cc.log("Scroll _direction is not both!"); + return; + } + this._jumpToDestination(0, 0); + }, + + /** + * Move inner container to bottom and right boundary of ScrollView. + */ + jumpToBottomRight: function () { + if (this._direction !== ccui.ScrollView.DIR_BOTH) { + cc.log("Scroll _direction is not both!"); + return; + } + this._jumpToDestination(this._contentSize.width - this._innerContainer.getContentSize().width, 0); + }, + + /** + * Move inner container to vertical percent position of ScrollView. + * @param {Number} percent The destination vertical percent, accept value between 0 - 100 + */ + jumpToPercentVertical: function (percent) { + var minY = this._contentSize.height - this._innerContainer.getContentSize().height; + var h = -minY; + this._jumpToDestination(this._innerContainer.getPositionX(), minY + percent * h / 100); + }, + + /** + * Move inner container to horizontal percent position of ScrollView. + * @param {Number} percent The destination vertical percent, accept value between 0 - 100 + */ + jumpToPercentHorizontal: function (percent) { + var w = this._innerContainer.getContentSize().width - this._contentSize.width; + this._jumpToDestination(-(percent * w / 100), this._innerContainer.getPositionY()); + }, + + /** + * Move inner container to both _direction percent position of ScrollView. + * @param {cc.Point} percent The destination vertical percent, accept value between 0 - 100 + */ + jumpToPercentBothDirection: function (percent) { + if (this._direction !== ccui.ScrollView.DIR_BOTH) + return; + var inSize = this._innerContainer.getContentSize(); + var minY = this._contentSize.height - inSize.height; + var h = -minY; + var w = inSize.width - this._contentSize.width; + this._jumpToDestination(-(percent.x * w / 100), minY + percent.y * h / 100); + }, + + _gatherTouchMove: function (delta) { + var NUMBER_OF_GATHERED_TOUCHES_FOR_MOVE_SPEED = 5; + while (this._touchMoveDisplacements.length >= NUMBER_OF_GATHERED_TOUCHES_FOR_MOVE_SPEED) { + this._touchMoveDisplacements.splice(0, 1); + this._touchMoveTimeDeltas.splice(0, 1) + } + this._touchMoveDisplacements.push(delta); + + var timestamp = (new Date()).getTime(); + this._touchMoveTimeDeltas.push((timestamp - this._touchMovePreviousTimestamp) / 1000); + this._touchMovePreviousTimestamp = timestamp; + }, + + _handlePressLogic: function (touch) { + this._bePressed = true; + this._autoScrolling = false; + + // Clear gathered touch move information + + this._touchMovePreviousTimestamp = (new Date()).getTime(); + this._touchMoveDisplacements.length = 0; + this._touchMoveTimeDeltas.length = 0; + + + if(this._verticalScrollBar) + { + this._verticalScrollBar.onTouchBegan(); + } + if(this._horizontalScrollBar) + { + this._horizontalScrollBar.onTouchBegan(); + } + }, + + _handleMoveLogic: function (touch) { + var touchPositionInNodeSpace = this.convertToNodeSpace(touch.getLocation()), + previousTouchPositionInNodeSpace = this.convertToNodeSpace(touch.getPreviousLocation()); + var delta = cc.pSub(touchPositionInNodeSpace, previousTouchPositionInNodeSpace); + + this._scrollChildren(delta); + this._gatherTouchMove(delta); + }, + + _handleReleaseLogic: function (touch) { + + var touchPositionInNodeSpace = this.convertToNodeSpace(touch.getLocation()), + previousTouchPositionInNodeSpace = this.convertToNodeSpace(touch.getPreviousLocation()); + var delta = cc.pSub(touchPositionInNodeSpace, previousTouchPositionInNodeSpace); + + this._gatherTouchMove(delta); + + this._bePressed = false; + + var bounceBackStarted = this._startBounceBackIfNeeded(); + if (!bounceBackStarted && this.inertiaScrollEnabled) { + var touchMoveVelocity = this._calculateTouchMoveVelocity(); + if (touchMoveVelocity.x !== 0 || touchMoveVelocity.y !== 0) { + this._startInertiaScroll(touchMoveVelocity); + } + } + + if(this._verticalScrollBar) + { + this._verticalScrollBar.onTouchEnded(); + } + if(this._horizontalScrollBar) + { + this._horizontalScrollBar.onTouchEnded(); + } + }, + + /** + * The touch began event callback handler of ccui.ScrollView. + * @param {cc.Touch} touch + * @param {cc.Event} event + * @returns {boolean} + */ + onTouchBegan: function (touch, event) { + var pass = ccui.Layout.prototype.onTouchBegan.call(this, touch, event); + if (!this._isInterceptTouch) { + if (this._hit) + this._handlePressLogic(touch); + } + return pass; + }, + + /** + * The touch moved event callback handler of ccui.ScrollView. + * @param {cc.Touch} touch + * @param {cc.Event} event + */ + onTouchMoved: function (touch, event) { + ccui.Layout.prototype.onTouchMoved.call(this, touch, event); + if (!this._isInterceptTouch) + this._handleMoveLogic(touch); + }, + + /** + * The touch ended event callback handler of ccui.ScrollView. + * @param {cc.Touch} touch + * @param {cc.Event} event + */ + onTouchEnded: function (touch, event) { + ccui.Layout.prototype.onTouchEnded.call(this, touch, event); + if (!this._isInterceptTouch) + this._handleReleaseLogic(touch); + this._isInterceptTouch = false; + }, + + /** + * The touch canceled event callback of ccui.ScrollView. + * @param {cc.Touch} touch + * @param {cc.Event} event + */ + onTouchCancelled: function (touch, event) { + ccui.Layout.prototype.onTouchCancelled.call(this, touch, event); + if (!this._isInterceptTouch) + this._handleReleaseLogic(touch); + this._isInterceptTouch = false; + }, + + /** + * The update callback handler. + * @param {Number} dt + */ + update: function (dt) { + if (this._autoScrolling) + this._processAutoScrolling(dt); + }, + + /** + * Intercept touch event, handle its child's touch event. + * @override + * @param {number} event event type + * @param {ccui.Widget} sender + * @param {cc.Touch} touch + */ + interceptTouchEvent: function (event, sender, touch) { + if (!this._touchEnabled) { + ccui.Layout.prototype.interceptTouchEvent.call(this, event, sender, touch); + return; + } + + if (this._direction === ccui.ScrollView.DIR_NONE) + return; + + var touchPoint = touch.getLocation(); + switch (event) { + case ccui.Widget.TOUCH_BEGAN: + this._isInterceptTouch = true; + this._touchBeganPosition.x = touchPoint.x; + this._touchBeganPosition.y = touchPoint.y; + this._handlePressLogic(touch); + break; + case ccui.Widget.TOUCH_MOVED: + var offset = cc.pLength(cc.pSub(sender.getTouchBeganPosition(), touchPoint)); + this._touchMovePosition.x = touchPoint.x; + this._touchMovePosition.y = touchPoint.y; + if (offset > this._childFocusCancelOffset) { + sender.setHighlighted(false); + this._handleMoveLogic(touch); + } + break; + case ccui.Widget.TOUCH_CANCELED: + case ccui.Widget.TOUCH_ENDED: + this._touchEndPosition.x = touchPoint.x; + this._touchEndPosition.y = touchPoint.y; + this._handleReleaseLogic(touch); + if (sender.isSwallowTouches()) + this._isInterceptTouch = false; + break; + } + }, + + _processScrollEvent: function (_directionEvent, bounce) { + var event = 0; + + switch (_directionEvent) { + case ccui.ScrollView.MOVEDIR_TOP: + event = (bounce ? ccui.ScrollView.EVENT_BOUNCE_TOP : ccui.ScrollView.EVENT_SCROLL_TO_TOP); + break; + case ccui.ScrollView.MOVEDIR_BOTTOM: + event = (bounce ? ccui.ScrollView.EVENT_BOUNCE_BOTTOM : ccui.ScrollView.EVENT_SCROLL_TO_BOTTOM); + break; + case ccui.ScrollView.MOVEDIR_LEFT: + event = (bounce ? ccui.ScrollView.EVENT_BOUNCE_LEFT : ccui.ScrollView.EVENT_SCROLL_TO_LEFT); + break; + case ccui.ScrollView.MOVEDIR_RIGHT: + event = (bounce ? ccui.ScrollView.EVENT_BOUNCE_RIGHT : ccui.ScrollView.EVENT_SCROLL_TO_RIGHT); + break; + } + + this._dispatchEvent(event); + }, + + _processScrollingEvent: function () { + this._dispatchEvent(ccui.ScrollView.EVENT_SCROLLING); + }, + + _dispatchEvent: function (event) { + if (this._scrollViewEventSelector) { + if (this._scrollViewEventListener) + this._scrollViewEventSelector.call(this._scrollViewEventListener, this, event); + else + this._scrollViewEventSelector(this, event); + } + if (this._ccEventCallback) + this._ccEventCallback(this, event); + }, + + /** + * Adds callback function called ScrollView event triggered + * @param {Function} selector + * @param {Object} [target=] + * @deprecated since v3.0, please use addEventListener instead. + */ + addEventListenerScrollView: function (selector, target) { + this._scrollViewEventSelector = selector; + this._scrollViewEventListener = target; + }, + + /** + * Adds callback function called ScrollView event triggered + * @param {Function} selector + */ + addEventListener: function (selector) { + this._ccEventCallback = selector; + }, + + /** + * Changes scroll _direction of ScrollView. + * @param {ccui.ScrollView.DIR_NONE | ccui.ScrollView.DIR_VERTICAL | ccui.ScrollView.DIR_HORIZONTAL | ccui.ScrollView.DIR_BOTH} dir + * Direction::VERTICAL means vertical scroll, Direction::HORIZONTAL means horizontal scroll + */ + setDirection: function (dir) { + this._direction = dir; + + if(this._scrollBarEnabled) + { + this._removeScrollBar(); + this._initScrollBar(); + } + }, + + /** + * Returns scroll direction of ScrollView. + * @returns {ccui.ScrollView.DIR_NONE | ccui.ScrollView.DIR_VERTICAL | ccui.ScrollView.DIR_HORIZONTAL | ccui.ScrollView.DIR_BOTH} + */ + getDirection: function () { + return this._direction; + }, + + /** + * Sets bounce enabled + * @param {Boolean} enabled + */ + setBounceEnabled: function (enabled) { + this.bounceEnabled = enabled; + }, + + /** + * Returns whether bounce is enabled + * @returns {boolean} + */ + isBounceEnabled: function () { + return this.bounceEnabled; + }, + + /** + * Sets inertiaScroll enabled + * @param {boolean} enabled + */ + setInertiaScrollEnabled: function (enabled) { + this.inertiaScrollEnabled = enabled; + }, + + /** + * Returns whether inertiaScroll is enabled + * @returns {boolean} + */ + isInertiaScrollEnabled: function () { + return this.inertiaScrollEnabled; + }, + + /** + * Toggle scroll bar enabled. + * @param {boolean} enabled True if enable scroll bar, false otherwise. + */ + setScrollBarEnabled: function(enabled) + { + if(this._scrollBarEnabled === enabled) + { + return; + } + + if(this._scrollBarEnabled) + { + this._removeScrollBar(); + } + this._scrollBarEnabled = enabled; + if(this._scrollBarEnabled) + { + this._initScrollBar(); + } + }, + /** + * Query scroll bar state. + * @returns {boolean} True if scroll bar is enabled, false otherwise. + */ + isScrollBarEnabled: function() + { + return this._scrollBarEnabled; + }, + + /** + * Set the scroll bar positions from the left-bottom corner (horizontal) and right-top corner (vertical). + * @param {cc.Point} positionFromCorner The position from the left-bottom corner (horizontal) and right-top corner (vertical). + */ + setScrollBarPositionFromCorner: function(positionFromCorner) + { + if(this._direction !== ccui.ScrollView.DIR_HORIZONTAL) + { + this.setScrollBarPositionFromCornerForVertical(positionFromCorner); + } + if(this._direction !== ccui.ScrollView.DIR_VERTICAL) + { + this.setScrollBarPositionFromCornerForHorizontal(positionFromCorner); + } + }, + + /** + * Set the vertical scroll bar position from right-top corner. + * @param {cc.Point} positionFromCorner The position from right-top corner + */ + setScrollBarPositionFromCornerForVertical: function(positionFromCorner) + { + cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); + cc.assert(this._direction !== ccui.ScrollView.DIR_HORIZONTAL, "Scroll view doesn't have a vertical scroll bar!"); + this._verticalScrollBar.setPositionFromCorner(positionFromCorner); + }, + + /** + * Get the vertical scroll bar's position from right-top corner. + * @returns {cc.Point} + */ + getScrollBarPositionFromCornerForVertical: function() + { + cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); + cc.assert(this._direction !== ccui.ScrollView.DIR_HORIZONTAL, "Scroll view doesn't have a vertical scroll bar!"); + return this._verticalScrollBar.getPositionFromCorner(); + }, + + /** + * Set the horizontal scroll bar position from left-bottom corner. + * @param {cc.Point} positionFromCorner The position from left-bottom corner + */ + setScrollBarPositionFromCornerForHorizontal: function(positionFromCorner) + { + cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); + cc.assert(this._direction !== ccui.ScrollView.DIR_VERTICAL, "Scroll view doesn't have a horizontal scroll bar!"); + this._horizontalScrollBar.setPositionFromCorner(positionFromCorner); + }, + + /** + * Get the horizontal scroll bar's position from right-top corner. + * @returns {cc.Point} + */ + getScrollBarPositionFromCornerForHorizontal: function() + { + cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); + cc.assert(this._direction !== ccui.ScrollView.DIR_VERTICAL, "Scroll view doesn't have a horizontal scroll bar!"); + return this._horizontalScrollBar.getPositionFromCorner(); + }, + + /** + * Set the scroll bar's width + * @param {number} width The scroll bar's width + */ + setScrollBarWidth: function(width) + { + cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); + if(this._verticalScrollBar) + { + this._verticalScrollBar.setWidth(width); + } + if(this._horizontalScrollBar) + { + this._horizontalScrollBar.setWidth(width); + } + }, + + /** + * Get the scroll bar's width + * @returns {number} the scroll bar's width + */ + getScrollBarWidth: function() + { + cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); + if(this._verticalScrollBar) + { + return this._verticalScrollBar.getWidth(); + } + if(this._horizontalScrollBar) + { + return this._horizontalScrollBar.getWidth(); + } + return 0; + }, + + /** + * Set the scroll bar's color + * @param {cc.Color} color the scroll bar's color + */ + setScrollBarColor: function(color) + { + cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); + if(this._verticalScrollBar) + { + this._verticalScrollBar.setColor(color); + } + if(this._horizontalScrollBar) + { + this._horizontalScrollBar.setColor(color); + } + }, + + /** + * Get the scroll bar's color + * @returns {cc.Color} the scroll bar's color + */ + getScrollBarColor: function() + { + cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); + if(this._verticalScrollBar) + { + this._verticalScrollBar.getColor(); + } + if(this._horizontalScrollBar) + { + this._horizontalScrollBar.getColor(); + } + return cc.color.WHITE; + }, + + /** + * Set the scroll bar's opacity + * @param {number} opacity the scroll bar's opacity + */ + setScrollBarOpacity: function(opacity) + { + cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); + if(this._verticalScrollBar) + { + this._verticalScrollBar.opacity = opacity; + } + if(this._horizontalScrollBar) + { + this._horizontalScrollBar.opacity = opacity; + } + }, + + /** + * Get the scroll bar's opacity + * @returns {number} + */ + getScrollBarOpacity: function() + { + cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); + if(this._verticalScrollBar) + { + return this._verticalScrollBar.opacity; + } + if(this._horizontalScrollBar) + { + return this._horizontalScrollBar.opacity; + } + return -1; + }, + + /** + * Set scroll bar auto hide state + * @param {boolean} autoHideEnabled scroll bar auto hide state + */ + setScrollBarAutoHideEnabled: function(autoHideEnabled) + { + cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); + if(this._verticalScrollBar) + { + this._verticalScrollBar.autoHideEnabled = autoHideEnabled; + } + if(this._horizontalScrollBar) + { + this._horizontalScrollBar.autoHideEnabled = autoHideEnabled; + } + }, + + /** + * Query scroll bar auto hide state + * @returns {boolean} + */ + isScrollBarAutoHideEnabled: function() + { + cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); + if(this._verticalScrollBar) + { + return this._verticalScrollBar.autoHideEnabled; + } + if(this._horizontalScrollBar) + { + return this._horizontalScrollBar.autoHideEnabled; + } + return false; + }, + + /** + * Set scroll bar auto hide time + * @param {number} autoHideTime scroll bar auto hide state + */ + setScrollBarAutoHideTime: function(autoHideTime) + { + cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); + if(this._verticalScrollBar) + { + this._verticalScrollBar.autoHideTime = autoHideTime; + } + if(this._horizontalScrollBar) + { + this._horizontalScrollBar.autoHideTime = autoHideTime; + } + }, + + /** + * Get the scroll bar's auto hide time + * @returns {number} + */ + getScrollBarAutoHideTime: function() + { + cc.assert(this._scrollBarEnabled, "Scroll bar should be enabled!"); + if(this._verticalScrollBar) + { + return this._verticalScrollBar.autoHideTime; + } + if(this._horizontalScrollBar) + { + return this._horizontalScrollBar.autoHideTime; + } + return 0; + }, + + /** + * Gets inner container of ScrollView. Inner container is the container of ScrollView's children. + * @returns {ccui.Layout} + */ + getInnerContainer: function () { + return this._innerContainer; + }, + + /** + * Sets LayoutType of ccui.ScrollView. + * @param {ccui.Layout.ABSOLUTE|ccui.Layout.LINEAR_VERTICAL|ccui.Layout.LINEAR_HORIZONTAL|ccui.Layout.RELATIVE} type + */ + setLayoutType: function (type) { + this._innerContainer.setLayoutType(type); + }, + + /** + * Returns the layout type of ccui.ScrollView. + * @returns {ccui.Layout.ABSOLUTE|ccui.Layout.LINEAR_VERTICAL|ccui.Layout.LINEAR_HORIZONTAL|ccui.Layout.RELATIVE} + */ + getLayoutType: function () { + return this._innerContainer.getLayoutType(); + }, + + _doLayout: function () { + if (!this._doLayoutDirty) + return; + this._doLayoutDirty = false; + }, + + /** + * Returns the "class name" of ccui.ScrollView. + * @returns {string} + */ + getDescription: function () { + return "ScrollView"; + }, + + _createCloneInstance: function () { + return new ccui.ScrollView(); + }, + + _copyClonedWidgetChildren: function (model) { + ccui.Layout.prototype._copyClonedWidgetChildren.call(this, model); + }, + + _copySpecialProperties: function (scrollView) { + if (scrollView instanceof ccui.ScrollView) { + ccui.Layout.prototype._copySpecialProperties.call(this, scrollView); + this.setInnerContainerSize(scrollView.getInnerContainerSize()); + this.setInnerContainerPosition(scrollView.getInnerContainerPosition()); + this.setDirection(scrollView._direction); + + this._topBoundary = scrollView._topBoundary; + this._bottomBoundary = scrollView._bottomBoundary; + this._leftBoundary = scrollView._leftBoundary; + this._rightBoundary = scrollView._rightBoundary; + this._bePressed = scrollView._bePressed; + this._childFocusCancelOffset = scrollView._childFocusCancelOffset; + this._touchMoveDisplacements = scrollView._touchMoveDisplacements; + this._touchMoveTimeDeltas = scrollView._touchMoveTimeDeltas; + this._touchMovePreviousTimestamp = scrollView._touchMovePreviousTimestamp; + this._autoScrolling = scrollView._autoScrolling; + this._autoScrollAttenuate = scrollView._autoScrollAttenuate; + this._autoScrollStartPosition = scrollView._autoScrollStartPosition; + this._autoScrollTargetDelta = scrollView._autoScrollTargetDelta; + this._autoScrollTotalTime = scrollView._autoScrollTotalTime; + this._autoScrollAccumulatedTime = scrollView._autoScrollAccumulatedTime; + this._autoScrollCurrentlyOutOfBoundary = scrollView._autoScrollCurrentlyOutOfBoundary; + this._autoScrollBraking = scrollView._autoScrollBraking; + this._autoScrollBrakingStartPosition = scrollView._autoScrollBrakingStartPosition; + + this.setBounceEnabled(scrollView.bounceEnabled); + this.setInertiaScrollEnabled(scrollView.inertiaScrollEnabled); + + this._scrollViewEventListener = scrollView._scrollViewEventListener; + this._scrollViewEventSelector = scrollView._scrollViewEventSelector; + this._ccEventCallback = scrollView._ccEventCallback; + + this.setScrollBarEnabled(scrollView.isScrollBarEnabled()); + if(this.isScrollBarEnabled()) + { + if(this._direction !== ccui.ScrollView.DIR_HORIZONTAL) + { + this.setScrollBarPositionFromCornerForVertical(scrollView.getScrollBarPositionFromCornerForVertical()); + } + if(this._direction !== ccui.ScrollView.DIR_VERTICAL) + { + this.setScrollBarPositionFromCornerForHorizontal(scrollView.getScrollBarPositionFromCornerForHorizontal()); + } + this.setScrollBarWidth(scrollView.getScrollBarWidth()); + this.setScrollBarColor(scrollView.getScrollBarColor()); + this.setScrollBarAutoHideEnabled(scrollView.isScrollBarAutoHideEnabled()); + this.setScrollBarAutoHideTime(scrollView.getScrollBarAutoHideTime()); + } + } + }, + + _initScrollBar: function() + { + if(this._direction !== ccui.ScrollView.DIR_HORIZONTAL && !this._verticalScrollBar) + { + this._verticalScrollBar = new ccui.ScrollViewBar(this, ccui.ScrollView.DIR_VERTICAL); + this.addProtectedChild(this._verticalScrollBar, 2); + } + if(this._direction !== ccui.ScrollView.DIR_VERTICAL && !this._horizontalScrollBar) + { + this._horizontalScrollBar = new ccui.ScrollViewBar(this, ccui.ScrollView.DIR_HORIZONTAL); + this.addProtectedChild(this._horizontalScrollBar, 2); + } + }, + + _removeScrollBar: function() + { + if(this._verticalScrollBar) + { + this.removeProtectedChild(this._verticalScrollBar); + this._verticalScrollBar = null; + } + if(this._horizontalScrollBar) + { + this.removeProtectedChild(this._horizontalScrollBar); + this._horizontalScrollBar = null; + } + }, + + /** + * Returns a node by tag + * @param {Number} tag + * @returns {cc.Node} + * @deprecated since v3.0, please use getChildByTag instead. + */ + getNodeByTag: function (tag) { + return this._innerContainer.getNodeByTag(tag); + }, + + /** + * Returns all nodes of inner container + * @returns {Array} + * @deprecated since v3.0, please use getChildren instead. + */ + getNodes: function () { + return this._innerContainer.getNodes(); + }, + + /** + * Removes a node from ccui.ScrollView. + * @param {cc.Node} node + * @deprecated since v3.0, please use removeChild instead. + */ + removeNode: function (node) { + this._innerContainer.removeNode(node); + }, + + /** + * Removes a node by tag + * @param {Number} tag + * @deprecated since v3.0, please use removeChildByTag instead. + */ + removeNodeByTag: function (tag) { + this._innerContainer.removeNodeByTag(tag); + }, + + /** + * Remove all node from ccui.ScrollView. + * @deprecated since v3.0, please use removeAllChildren instead. + */ + removeAllNodes: function () { + this._innerContainer.removeAllNodes(); + }, + + /** + * Add node for scrollView + * @param {cc.Node} node + * @param {Number} zOrder + * @param {Number} tag + * @deprecated since v3.0, please use addChild instead. + */ + addNode: function (node, zOrder, tag) { + this._innerContainer.addNode(node, zOrder, tag); + } +}); + +var _p = ccui.ScrollView.prototype; + +// Extended properties +/** @expose */ +_p.innerWidth; +cc.defineGetterSetter(_p, "innerWidth", _p._getInnerWidth, _p._setInnerWidth); +/** @expose */ +_p.innerHeight; +cc.defineGetterSetter(_p, "innerHeight", _p._getInnerHeight, _p._setInnerHeight); +/** @expose */ +_p.direction; +cc.defineGetterSetter(_p, "direction", _p.getDirection, _p.setDirection); +/** @expose */ +_p.touchTotalTimeThreshold; +cc.defineGetterSetter(_p, "touchTotalTimeThreshold", _p.getTouchTotalTimeThreshold, _p.setTouchTotalTimeThreshold); +_p = null; +/** + * allocates and initializes a UIScrollView. + * @deprecated since v3.0, please use new ccui.ScrollView() instead. + * @return {ccui.ScrollView} + */ +ccui.ScrollView.create = function () { + return new ccui.ScrollView(); +}; + +// Constants +//ScrollView direction +/** + * The none flag of ccui.ScrollView's direction. + * @constant + * @type {number} + */ +ccui.ScrollView.DIR_NONE = 0; +/** + * The vertical flag of ccui.ScrollView's direction. + * @constant + * @type {number} + */ +ccui.ScrollView.DIR_VERTICAL = 1; +/** + * The horizontal flag of ccui.ScrollView's direction. + * @constant + * @type {number} + */ +ccui.ScrollView.DIR_HORIZONTAL = 2; +/** + * The both flag of ccui.ScrollView's direction. + * @constant + * @type {number} + */ +ccui.ScrollView.DIR_BOTH = 3; + +//ScrollView event +/** + * The flag scroll to top of ccui.ScrollView's event. + * @constant + * @type {number} + */ +ccui.ScrollView.EVENT_SCROLL_TO_TOP = 0; +/** + * The flag scroll to bottom of ccui.ScrollView's event. + * @constant + * @type {number} + */ +ccui.ScrollView.EVENT_SCROLL_TO_BOTTOM = 1; +/** + * The flag scroll to left of ccui.ScrollView's event. + * @constant + * @type {number} + */ +ccui.ScrollView.EVENT_SCROLL_TO_LEFT = 2; +/** + * The flag scroll to right of ccui.ScrollView's event. + * @constant + * @type {number} + */ +ccui.ScrollView.EVENT_SCROLL_TO_RIGHT = 3; +/** + * The scrolling flag of ccui.ScrollView's event. + * @constant + * @type {number} + */ +ccui.ScrollView.EVENT_SCROLLING = 4; +/** + * The flag bounce top of ccui.ScrollView's event. + * @constant + * @type {number} + */ +ccui.ScrollView.EVENT_BOUNCE_TOP = 5; +/** + * The flag bounce bottom of ccui.ScrollView's event. + * @constant + * @type {number} + */ +ccui.ScrollView.EVENT_BOUNCE_BOTTOM = 6; +/** + * The flag bounce left of ccui.ScrollView's event. + * @constant + * @type {number} + */ +ccui.ScrollView.EVENT_BOUNCE_LEFT = 7; +/** + * The flag bounce right of ccui.ScrollView's event. + * @constant + * @type {number} + */ +ccui.ScrollView.EVENT_BOUNCE_RIGHT = 8; +/** + * The flag container moved of ccui.ScrollView's event. + * @constant + * @type {number} + */ +ccui.ScrollView.EVENT_CONTAINER_MOVED = 9; +/** + * The flag autoscroll ended of ccui.ScrollView's event. + * @constant + * @type {number} + */ +ccui.ScrollView.EVENT_AUTOSCROLL_ENDED = 10; + +/** + * @ignore + */ + +ccui.ScrollView.MOVEDIR_TOP = 0; +ccui.ScrollView.MOVEDIR_BOTTOM = 1; +ccui.ScrollView.MOVEDIR_LEFT = 2; +ccui.ScrollView.MOVEDIR_RIGHT = 3; diff --git a/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/scroll-widget/UIScrollViewBar.js b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/scroll-widget/UIScrollViewBar.js new file mode 100644 index 0000000..4e9c184 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/scroll-widget/UIScrollViewBar.js @@ -0,0 +1,343 @@ +/**************************************************************************** + Copyright (c) 2015 Neo Kim (neo.kim@neofect.com) + Copyright (c) 2015 Nikita Besshaposhnikov (nikita.besshaposhnikov@gmail.com) + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The ScrollViewBar control of Cocos UI
+ * Scroll bar being attached to ScrollView layout container. + * @class + * @extends ccui.ProtectedNode + * + * @property {Number} opacity - Opacity of the scroll view bar + * @property {Boolean} autoHideEnabled - Auto hide is enabled in the scroll view bar + * @property {Number} autoHideTime - Auto hide time of the scroll view bar + */ +ccui.ScrollViewBar = ccui.ProtectedNode.extend(/** @lends ccui.ScrollViewBar# */{ + _parentScroll: null, + _direction: null, + + _upperHalfCircle: null, + _lowerHalfCircle: null, + _body: null, + + _opacity: 255, + + _marginFromBoundary: 0, + _marginForLength: 0, + + _touching: false, + + _autoHideEnabled: true, + autoHideTime: 0, + _autoHideRemainingTime: 0, + _className: "ScrollViewBar", + + /** + * Allocates and initializes a UIScrollViewBar. + * Constructor of ccui.ScrollViewBar. override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + * @param {ccui.ScrollView} parent A parent of scroll bar. + * @param {ccui.ScrollView.DIR_NONE | ccui.ScrollView.DIR_HORIZONTAL | ccui.ScrollView.DIR_VERTICAL | ccui.ScrollView.DIR_BOTH} direction + */ + ctor: function (parent, direction) { + cc.ProtectedNode.prototype.ctor.call(this); + this._direction = direction; + this._parentScroll = parent; + + this._marginFromBoundary = ccui.ScrollViewBar.DEFAULT_MARGIN; + this._marginForLength = ccui.ScrollViewBar.DEFAULT_MARGIN; + this.opacity = 255 * ccui.ScrollViewBar.DEFAULT_SCROLLBAR_OPACITY; + this.autoHideTime = ccui.ScrollViewBar.DEFAULT_AUTO_HIDE_TIME; + this._autoHideEnabled = true; + + ccui.ScrollViewBar.prototype.init.call(this); + + this.setCascadeColorEnabled(true); + this.setCascadeOpacityEnabled(true); + }, + + /** + * Initializes a ccui.ScrollViewBar. Please do not call this function by yourself, you should pass the parameters to constructor to initialize it. + * @returns {boolean} + */ + init: function () { + this._upperHalfCircle = ccui.helper._createSpriteFromBase64(ccui.ScrollViewBar.HALF_CIRCLE_IMAGE, ccui.ScrollViewBar.HALF_CIRCLE_IMAGE_KEY); + this._upperHalfCircle.setAnchorPoint(cc.p(0.5, 0)); + + this._lowerHalfCircle = ccui.helper._createSpriteFromBase64(ccui.ScrollViewBar.HALF_CIRCLE_IMAGE, ccui.ScrollViewBar.HALF_CIRCLE_IMAGE_KEY); + this._lowerHalfCircle.setAnchorPoint(cc.p(0.5, 0)); + this._lowerHalfCircle.setScaleY(-1); + + this.addProtectedChild(this._upperHalfCircle); + this.addProtectedChild(this._lowerHalfCircle); + + this._body = ccui.helper._createSpriteFromBase64(ccui.ScrollViewBar.BODY_IMAGE_1_PIXEL_HEIGHT, ccui.ScrollViewBar.BODY_IMAGE_1_PIXEL_HEIGHT_KEY); + this._body.setAnchorPoint(cc.p(0.5, 0)); + this.addProtectedChild(this._body); + + this.setColor(ccui.ScrollViewBar.DEFAULT_COLOR); + this.onScrolled(cc.p(0, 0)); + cc.ProtectedNode.prototype.setOpacity.call(this, 0); + this._autoHideRemainingTime = 0; + + if (this._direction === ccui.ScrollView.DIR_HORIZONTAL) { + this.setRotation(90); + } + }, + + /** + * Set the scroll bar position from the left-bottom corner (horizontal) or right-top corner (vertical). + * @param {cc.Point} positionFromCorner The position from the left-bottom corner (horizontal) or right-top corner (vertical). + */ + setPositionFromCorner: function (positionFromCorner) { + if (this._direction === ccui.ScrollView.DIR_VERTICAL) { + this._marginForLength = positionFromCorner.y; + this._marginFromBoundary = positionFromCorner.x; + } + else { + this._marginForLength = positionFromCorner.x; + this._marginFromBoundary = positionFromCorner.y; + } + }, + + onEnter: function () { + cc.ProtectedNode.prototype.onEnter.call(this); + this.scheduleUpdate(); + }, + + /** + * Get the scroll bar position from the left-bottom corner (horizontal) or right-top corner (vertical). + * @returns {cc.Point} + */ + getPositionFromCorner: function () { + if (this._direction === ccui.ScrollView.DIR_VERTICAL) { + return cc.p(this._marginFromBoundary, this._marginForLength); + } + else { + return cc.p(this._marginForLength, this._marginFromBoundary); + } + }, + /** + * Set the scroll bar's width + * @param {number} width The scroll bar's width + */ + setWidth: function (width) { + var scale = width / this._body.width; + this._body.setScaleX(scale); + this._upperHalfCircle.setScale(scale); + this._lowerHalfCircle.setScale(-scale); + }, + + /** + * Get the scroll bar's width + * @returns {number} the scroll bar's width + */ + getWidth: function () { + return this._body.getBoundingBox().width; + }, + + /** + * Set scroll bar auto hide state + * @param {boolean} autoHideEnabled scroll bar auto hide state + */ + setAutoHideEnabled: function (autoHideEnabled) { + this._autoHideEnabled = autoHideEnabled; + + if (!this._autoHideEnabled && !this._touching && this._autoHideRemainingTime <= 0) + cc.ProtectedNode.prototype.setOpacity.call(this, this.opacity); + else + cc.ProtectedNode.prototype.setOpacity.call(this, 0); + }, + /** + * Query scroll bar auto hide state + * @returns {boolean} True if scroll bar auto hide is enabled, false otherwise. + */ + isAutoHideEnabled: function () { + return this._autoHideEnabled; + }, + + /** + * Set scroll bar opacity + * @param {number} opacity scroll bar opacity + */ + setOpacity: function (opacity) { + this._opacity = opacity; + }, + + /** + * Get scroll bar opacity + * @returns {number} + */ + getOpacity: function () { + return this._opacity; + }, + + _updateLength: function (length) { + var ratio = length / this._body.getTextureRect().height; + this._body.setScaleY(ratio); + this._upperHalfCircle.setPositionY(this._body.getPositionY() + length); + }, + + _processAutoHide: function (dt) { + if (!this._autoHideEnabled || this._autoHideRemainingTime <= 0) { + return; + } + else if (this._touching) { + // If it is touching, don't auto hide. + return; + } + + this._autoHideRemainingTime -= dt; + if (this._autoHideRemainingTime <= this.autoHideTime) { + this._autoHideRemainingTime = Math.max(0, this._autoHideRemainingTime); + cc.ProtectedNode.prototype.setOpacity.call(this, this._opacity * (this._autoHideRemainingTime / this.autoHideTime)); + } + }, + + + update: function (dt) { + this._processAutoHide(dt); + }, + + /** + * This is called by parent ScrollView when a touch is began. Don't call this directly. + */ + onTouchBegan: function () { + if (!this._autoHideEnabled) { + return; + } + this._touching = true; + }, + + /** + * This is called by parent ScrollView when a touch is ended. Don't call this directly. + */ + onTouchEnded: function () { + if (!this._autoHideEnabled) { + return; + } + this._touching = false; + + if (this._autoHideRemainingTime <= 0) { + // If the remaining time is 0, it means that it didn't moved after touch started so scroll bar is not showing. + return; + } + this._autoHideRemainingTime = this.autoHideTime; + }, + + /** + * @brief This is called by parent ScrollView when the parent is scrolled. Don't call this directly. + * + * @param {cc.Point} outOfBoundary amount how much the inner container of ScrollView is out of boundary + */ + onScrolled: function (outOfBoundary) { + if (this._autoHideEnabled) { + this._autoHideRemainingTime = this.autoHideTime; + cc.ProtectedNode.prototype.setOpacity.call(this, this.opacity); + } + + var innerContainer = this._parentScroll.getInnerContainer(); + + var innerContainerMeasure = 0; + var scrollViewMeasure = 0; + var outOfBoundaryValue = 0; + var innerContainerPosition = 0; + + if (this._direction === ccui.ScrollView.DIR_VERTICAL) { + innerContainerMeasure = innerContainer.height; + scrollViewMeasure = this._parentScroll.height; + outOfBoundaryValue = outOfBoundary.y; + innerContainerPosition = -innerContainer.getPositionY(); + } + else if (this._direction === ccui.ScrollView.DIR_HORIZONTAL) { + innerContainerMeasure = innerContainer.width; + scrollViewMeasure = this._parentScroll.width; + outOfBoundaryValue = outOfBoundary.x; + innerContainerPosition = -innerContainer.getPositionX(); + } + + var length = this._calculateLength(innerContainerMeasure, scrollViewMeasure, outOfBoundaryValue); + var position = this._calculatePosition(innerContainerMeasure, scrollViewMeasure, innerContainerPosition, outOfBoundaryValue, length); + this._updateLength(length); + this.setPosition(position); + }, + + _calculateLength: function (innerContainerMeasure, scrollViewMeasure, outOfBoundaryValue) { + var denominatorValue = innerContainerMeasure; + if (outOfBoundaryValue !== 0) { + // If it is out of boundary, the length of scroll bar gets shorter quickly. + var GETTING_SHORTER_FACTOR = 20; + denominatorValue += (outOfBoundaryValue > 0 ? outOfBoundaryValue : -outOfBoundaryValue) * GETTING_SHORTER_FACTOR; + } + + var lengthRatio = scrollViewMeasure / denominatorValue; + return Math.abs(scrollViewMeasure - 2 * this._marginForLength) * lengthRatio; + }, + + _calculatePosition: function (innerContainerMeasure, scrollViewMeasure, innerContainerPosition, outOfBoundaryValue, length) { + var denominatorValue = innerContainerMeasure - scrollViewMeasure; + if (outOfBoundaryValue !== 0) { + denominatorValue += Math.abs(outOfBoundaryValue); + } + + var positionRatio = 0; + + if (denominatorValue !== 0) { + positionRatio = innerContainerPosition / denominatorValue; + positionRatio = Math.max(positionRatio, 0); + positionRatio = Math.min(positionRatio, 1); + } + + var position = (scrollViewMeasure - length - 2 * this._marginForLength) * positionRatio + this._marginForLength; + + if (this._direction === ccui.ScrollView.DIR_VERTICAL) { + return cc.p(this._parentScroll.width - this._marginFromBoundary, position); + } + else { + return cc.p(position, this._marginFromBoundary); + } + } + +}); + +var _p = ccui.ScrollViewBar.prototype; + +// Extended properties +/** @expose */ +_p.opacity; +cc.defineGetterSetter(_p, "opacity", _p.getOpacity, _p.setOpacity); +/** @expose */ +_p.autoHideEnabled; +cc.defineGetterSetter(_p, "autoHideEnabled", _p.isAutoHideEnabled, _p.setAutoHideEnabled); + +/** + * @ignore + */ +ccui.ScrollViewBar.DEFAULT_COLOR = cc.color(52, 65, 87); +ccui.ScrollViewBar.DEFAULT_MARGIN = 20; +ccui.ScrollViewBar.DEFAULT_AUTO_HIDE_TIME = 0.2; +ccui.ScrollViewBar.DEFAULT_SCROLLBAR_OPACITY = 0.4; +ccui.ScrollViewBar.HALF_CIRCLE_IMAGE_KEY = "/__half_circle_image"; +ccui.ScrollViewBar.HALF_CIRCLE_IMAGE = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAGCAMAAADAMI+zAAAAJ1BMVEX///////////////////////////////////////////////////9Ruv0SAAAADHRSTlMABgcbbW7Hz9Dz+PmlcJP5AAAAMElEQVR4AUXHwQ2AQAhFwYcLH1H6r1djzDK3ASxUpTBeK/uTCyz7dx54b44m4p5cD1MwAooEJyk3AAAAAElFTkSuQmCC"; +ccui.ScrollViewBar.BODY_IMAGE_1_PIXEL_HEIGHT_KEY = "/__body_image_height"; +ccui.ScrollViewBar.BODY_IMAGE_1_PIXEL_HEIGHT = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAABCAMAAADdNb8LAAAAA1BMVEX///+nxBvIAAAACklEQVR4AWNABgAADQABYc2cpAAAAABJRU5ErkJggg=="; diff --git a/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/scroll-widget/UIScrollViewCanvasRenderCmd.js b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/scroll-widget/UIScrollViewCanvasRenderCmd.js new file mode 100644 index 0000000..4607932 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/scroll-widget/UIScrollViewCanvasRenderCmd.js @@ -0,0 +1,32 @@ +(function () { + if (!ccui.ProtectedNode.CanvasRenderCmd) + return; + ccui.ScrollView.CanvasRenderCmd = function (renderable) { + this._layoutCmdCtor(renderable); + //this._needDraw = true; + this._dirty = false; + }; + + var proto = ccui.ScrollView.CanvasRenderCmd.prototype = Object.create(ccui.Layout.CanvasRenderCmd.prototype); + proto.constructor = ccui.ScrollView.CanvasRenderCmd; + + proto.rendering = function (ctx) { + var currentID = this._node.__instanceId; + var i, locCmds = cc.renderer._cacheToCanvasCmds[currentID], len, + scaleX = cc.view.getScaleX(), + scaleY = cc.view.getScaleY(); + var context = ctx || cc._renderContext; + context.computeRealOffsetY(); + + this._node.updateChildren(); + + for (i = 0, len = locCmds.length; i < len; i++) { + var checkNode = locCmds[i]._node; + if (checkNode instanceof ccui.ScrollView) + continue; + if (checkNode && checkNode._parent && checkNode._parent._inViewRect === false) + continue; + locCmds[i].rendering(context, scaleX, scaleY); + } + }; +})(); diff --git a/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/scroll-widget/UIScrollViewWebGLRenderCmd.js b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/scroll-widget/UIScrollViewWebGLRenderCmd.js new file mode 100644 index 0000000..97ba7ff --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/ccui/uiwidgets/scroll-widget/UIScrollViewWebGLRenderCmd.js @@ -0,0 +1,45 @@ +(function () { + if (!ccui.ProtectedNode.WebGLRenderCmd) + return; + ccui.ScrollView.WebGLRenderCmd = function (renderable) { + this._layoutCmdCtor(renderable); + this._needDraw = true; + this._dirty = false; + }; + + var proto = ccui.ScrollView.WebGLRenderCmd.prototype = Object.create(ccui.Layout.WebGLRenderCmd.prototype); + proto.constructor = ccui.ScrollView.WebGLRenderCmd; + + proto.rendering = function (ctx) { + var currentID = this._node.__instanceId, + locCmds = cc.renderer._cacheToBufferCmds[currentID], + i, len, checkNode, cmd, + context = ctx || cc._renderContext; + if (!locCmds) { + return; + } + + this._node.updateChildren(); + + // Reset buffer for rendering + context.bindBuffer(gl.ARRAY_BUFFER, null); + + for (i = 0, len = locCmds.length; i < len; i++) { + cmd = locCmds[i]; + checkNode = cmd._node; + if (checkNode && checkNode._parent && checkNode._parent._inViewRect === false) + continue; + + if (cmd.uploadData) { + cc.renderer._uploadBufferData(cmd); + } + else { + if (cmd._batchingSize > 0) { + cc.renderer._batchRendering(); + } + cmd.rendering(context); + } + cc.renderer._batchRendering(); + } + }; +})(); diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/CocoStudio.js b/frameworks/cocos2d-html5/extensions/cocostudio/CocoStudio.js new file mode 100644 index 0000000..fdb2ddc --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/CocoStudio.js @@ -0,0 +1,68 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ +/** + * The main namespace of Cocostudio, all classes, functions, properties and constants of Spine are defined in this namespace + * @namespace + * @name ccs + */ +var ccs = ccs || {}; + +/** + * The same as cc.Class + * @class + */ +ccs.Class = ccs.Class || cc.Class; +ccs.Class.extend = ccs.Class.extend || cc.Class.extend; + +/** + * The same as cc.Node + * @class + * @extends ccs.Class + */ +ccs.Node = ccs.Node || cc.Node; +ccs.Node.extend = ccs.Node.extend || cc.Node.extend; + +/** + * The same as cc.Sprite + * @class + * @extends ccs.Class + */ +ccs.Sprite = ccs.Sprite || cc.Sprite; +ccs.Sprite.extend = ccs.Sprite.extend || cc.Sprite.extend; + +/** + * The same as cc.Component + * @class + * @extends ccs.Class + */ +ccs.Component = ccs.Component || cc.Component; +ccs.Component.extend = ccs.Component.extend || cc.Component.extend; + +/** + * CocoStudio version + * @constant + * @type {string} + */ +ccs.cocostudioVersion = "v1.3.0.0"; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/action/CCActionFrame.js b/frameworks/cocos2d-html5/extensions/cocostudio/action/CCActionFrame.js new file mode 100644 index 0000000..2571031 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/action/CCActionFrame.js @@ -0,0 +1,530 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +//Action frame type +/** + * The flag move action type of Cocostudio frame. + * @constant + * @type {number} + */ +ccs.FRAME_TYPE_MOVE = 0; +/** + * The flag scale action type of Cocostudio frame. + * @constant + * @type {number} + */ +ccs.FRAME_TYPE_SCALE = 1; +/** + * The flag rotate action type of Cocostudio frame. + * @constant + * @type {number} + */ +ccs.FRAME_TYPE_ROTATE = 2; +/** + * The flag tint action type of Cocostudio frame. + * @constant + * @type {number} + */ +ccs.FRAME_TYPE_TINT = 3; +/** + * The flag fade action type of Cocostudio frame. + * @constant + * @type {number} + */ +ccs.FRAME_TYPE_FADE = 4; +/** + * The max flag of Cocostudio frame. + * @constant + * @type {number} + */ +ccs.FRAME_TYPE_MAX = 5; + +/** + * The ease type of Cocostudio frame. + * @constant + * @type {Object} + */ +ccs.FrameEaseType = { + CUSTOM : -1, + + LINEAR : 0, + + SINE_EASEIN : 1, + SINE_EASEOUT : 2, + SINE_EASEINOUT : 3, + + QUAD_EASEIN : 4, + QUAD_EASEOUT : 5, + QUAD_EASEINOUT : 6, + + CUBIC_EASEIN : 7, + CUBIC_EASEOUT : 8, + CUBIC_EASEINOUT : 9, + + QUART_EASEIN : 10, + QUART_EASEOUT : 11, + QUART_EASEINOUT : 12, + + QUINT_EASEIN : 13, + QUINT_EASEOUT : 14, + QUINT_EASEINOUT : 15, + + EXPO_EASEIN : 16, + EXPO_EASEOUT : 17, + EXPO_EASEINOUT : 18, + + CIRC_EASEIN : 19, + CIRC_EASEOUT : 20, + CIRC_EASEINOUT : 21, + + ELASTIC_EASEIN : 22, + ELASTIC_EASEOUT : 23, + ELASTIC_EASEINOUT : 24, + + BACK_EASEIN : 25, + BACK_EASEOUT : 26, + BACK_EASEINOUT : 27, + + BOUNCE_EASEIN : 28, + BOUNCE_EASEOUT : 29, + BOUNCE_EASEINOUT : 30, + + TWEEN_EASING_MAX: 1000 +}; + + +/** + * The action frame of Cocostudio. It's the base class of ccs.ActionMoveFrame, ccs.ActionScaleFrame etc. + * @class + * @extends ccs.Class + * + * @property {Number} frameType - frame type of ccs.ActionFrame + * @property {Number} easingType - easing type of ccs.ActionFrame + * @property {Number} frameIndex - frame index of ccs.ActionFrame + * @property {Number} time - time of ccs.ActionFrame + */ +ccs.ActionFrame = ccs.Class.extend(/** @lends ccs.ActionFrame# */{ + frameType: 0, + easingType: 0, + frameIndex: 0, + _Parameter: null, + time: 0, + + /** + * The constructor of cc.ActionFrame. + */ + ctor: function () { + this.frameType = 0; + this.easingType = ccs.FrameEaseType.LINEAR; + this.frameIndex = 0; + this.time = 0; + }, + + /** + * Returns the action of ActionFrame. its subClass need override it. + * @param {number} duration the duration time of ActionFrame + * @param {ccs.ActionFrame} srcFrame source frame. + * @returns {null} + */ + getAction: function (duration, srcFrame) { + cc.log("Need a definition of for ActionFrame"); + return null; + }, + + _getEasingAction : function (action) { + if (action === null) { + console.error("Action cannot be null!"); + return null; + } + + var resultAction; + switch (this.easingType) { + case ccs.FrameEaseType.CUSTOM: + break; + case ccs.FrameEaseType.LINEAR: + resultAction = action; + break; + case ccs.FrameEaseType.SINE_EASEIN: + resultAction = action.easing(cc.easeSineIn()); + break; + case ccs.FrameEaseType.SINE_EASEOUT: + resultAction = action.easing(cc.easeSineOut()); + break; + case ccs.FrameEaseType.SINE_EASEINOUT: + resultAction = action.easing(cc.easeSineInOut()); + break; + case ccs.FrameEaseType.QUAD_EASEIN: + resultAction = action.easing(cc.easeQuadraticActionIn()); + break; + case ccs.FrameEaseType.QUAD_EASEOUT: + resultAction = action.easing(cc.easeQuadraticActionOut()); + break; + case ccs.FrameEaseType.QUAD_EASEINOUT: + resultAction = action.easing(cc.easeQuadraticActionInOut()); + break; + case ccs.FrameEaseType.CUBIC_EASEIN: + resultAction = action.easing(cc.easeCubicActionIn()); + break; + case ccs.FrameEaseType.CUBIC_EASEOUT: + resultAction = action.easing(cc.easeCubicActionOut()); + break; + case ccs.FrameEaseType.CUBIC_EASEINOUT: + resultAction = action.easing(cc.easeCubicActionInOut()); + break; + case ccs.FrameEaseType.QUART_EASEIN: + resultAction = action.easing(cc.easeQuarticActionIn()); + break; + case ccs.FrameEaseType.QUART_EASEOUT: + resultAction = action.easing(cc.easeQuarticActionOut()); + break; + case ccs.FrameEaseType.QUART_EASEINOUT: + resultAction = action.easing(cc.easeQuarticActionInOut()); + break; + case ccs.FrameEaseType.QUINT_EASEIN: + resultAction = action.easing(cc.easeQuinticActionIn()); + break; + case ccs.FrameEaseType.QUINT_EASEOUT: + resultAction = action.easing(cc.easeQuinticActionOut()); + break; + case ccs.FrameEaseType.QUINT_EASEINOUT: + resultAction = action.easing(cc.easeQuinticActionInOut()); + break; + case ccs.FrameEaseType.EXPO_EASEIN: + resultAction = action.easing(cc.easeExponentialIn()); + break; + case ccs.FrameEaseType.EXPO_EASEOUT: + resultAction = action.easing(cc.easeExponentialOut()); + break; + case ccs.FrameEaseType.EXPO_EASEINOUT: + resultAction = action.easing(cc.easeExponentialInOut()); + break; + case ccs.FrameEaseType.CIRC_EASEIN: + resultAction = action.easing(cc.easeCircleActionIn()); + break; + case ccs.FrameEaseType.CIRC_EASEOUT: + resultAction = action.easing(cc.easeCircleActionOut()); + break; + case ccs.FrameEaseType.CIRC_EASEINOUT: + resultAction = action.easing(cc.easeCircleActionInOut()); + break; + case ccs.FrameEaseType.ELASTIC_EASEIN: + resultAction = action.easing(cc.easeElasticIn()); + break; + case ccs.FrameEaseType.ELASTIC_EASEOUT: + resultAction = action.easing(cc.easeElasticOut()); + break; + case ccs.FrameEaseType.ELASTIC_EASEINOUT: + resultAction = action.easing(cc.easeElasticInOut()); + break; + case ccs.FrameEaseType.BACK_EASEIN: + resultAction = action.easing(cc.easeBackIn()); + break; + case ccs.FrameEaseType.BACK_EASEOUT: + resultAction = action.easing(cc.easeBackOut()); + break; + case ccs.FrameEaseType.BACK_EASEINOUT: + resultAction = action.easing(cc.easeBackInOut()); + break; + case ccs.FrameEaseType.BOUNCE_EASEIN: + resultAction = action.easing(cc.easeBounceIn()); + break; + case ccs.FrameEaseType.BOUNCE_EASEOUT: + resultAction = action.easing(cc.easeBounceOut()); + break; + case ccs.FrameEaseType.BOUNCE_EASEINOUT: + resultAction = action.easing(cc.easeBounceInOut()); + break; + } + + return resultAction; + }, + + /** + * Sets the easing parameter to action frame. + * @param {Array} parameter + */ + setEasingParameter: function(parameter){ + this._Parameter = []; + for(var i=0;i locFrameIndex ? locFrameIndex : locFrameindex; + } + if (!bFindFrame) + locFrameindex = 0; + return locFrameindex; + }, + + /** + * Returns the index of last ccs.ActionFrame. + * @returns {number} + */ + getLastFrameIndex: function () { + var locFrameindex = -1; + var locIsFindFrame = false, locFrameArray = this._frameArray; + for (var i = 0, len = this._frameArrayNum; i < len; i++) { + var locArray = locFrameArray[i]; + if (locArray.length <= 0) + continue; + locIsFindFrame = true; + var locFrame = locArray[locArray.length - 1]; + var locFrameIndex = locFrame.frameIndex; + locFrameindex = locFrameindex < locFrameIndex ? locFrameIndex : locFrameindex; + } + if (!locIsFindFrame) + locFrameindex = 0; + return locFrameindex; + }, + + /** + * Updates action states to some time. + * @param {Number} time + * @returns {boolean} + */ + updateActionToTimeLine: function (time) { + var locIsFindFrame = false; + var locUnitTime = this.getUnitTime(); + for (var i = 0; i < this._frameArrayNum; i++) { + var locArray = this._frameArray[i]; + if (!locArray) + continue; + + for (var j = 0; j < locArray.length; j++) { + var locFrame = locArray[j]; + if (locFrame.frameIndex * locUnitTime === time) { + this._easingToFrame(1.0, 1.0, locFrame); + locIsFindFrame = true; + break; + } else if (locFrame.frameIndex * locUnitTime > time) { + if (j === 0) { + this._easingToFrame(1.0, 1.0, locFrame); + locIsFindFrame = false; + } else { + var locSrcFrame = locArray[j - 1]; + var locDuration = (locFrame.frameIndex - locSrcFrame.frameIndex) * locUnitTime; + var locDelaytime = time - locSrcFrame.frameIndex * locUnitTime; + this._easingToFrame(locDuration, 1.0, locSrcFrame); + this._easingToFrame(locDuration, locDelaytime / locDuration, locFrame); + locIsFindFrame = true; + } + break; + } + } + } + return locIsFindFrame; + }, + + _easingToFrame: function (duration, delayTime, destFrame) { + var action = destFrame.getAction(duration); + var node = this.getActionNode(); + if (action == null || node == null) + return; + action.startWithTarget(node); + action.update(delayTime); + }, + + /** + * Returns if the action is done once time. + * @returns {Boolean} that if the action is done once time + */ + isActionDoneOnce: function () { + if (!this._action) + return true; + return this._action.isDone(); + } +}); diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/action/CCActionObject.js b/frameworks/cocos2d-html5/extensions/cocostudio/action/CCActionObject.js new file mode 100644 index 0000000..88597f3 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/action/CCActionObject.js @@ -0,0 +1,263 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The Cocostudio's action object. + * @class + * @extends ccs.Class + */ +ccs.ActionObject = ccs.Class.extend(/** @lends ccs.ActionObject# */{ + _actionNodeList: null, + _name: "", + _loop: false, + _pause: false, + _playing: false, + _unitTime: 0, + _currentTime: 0, + _scheduler:null, + _callback: null, + _fTotalTime: 0, + + /** + * Construction of ccs.ActionObject. + */ + ctor: function () { + this._actionNodeList = []; + this._name = ""; + this._loop = false; + this._pause = false; + this._playing = false; + this._unitTime = 0.1; + this._currentTime = 0; + this._fTotalTime = 0; + this._scheduler = cc.director.getScheduler(); + }, + + /** + * Sets name to ccs.ActionObject + * @param {string} name + */ + setName: function (name) { + this._name = name; + }, + + /** + * Returns name fo ccs.ActionObject + * @returns {string} + */ + getName: function () { + return this._name; + }, + + /** + * Sets if the action will loop play. + * @param {boolean} loop + */ + setLoop: function (loop) { + this._loop = loop; + }, + + /** + * Returns if the action will loop play. + * @returns {boolean} + */ + getLoop: function () { + return this._loop; + }, + + /** + * Sets the time interval of frame. + * @param {number} time + */ + setUnitTime: function (time) { + this._unitTime = time; + var frameNum = this._actionNodeList.length; + for (var i = 0; i < frameNum; i++) { + var locActionNode = this._actionNodeList[i]; + locActionNode.setUnitTime(this._unitTime); + } + }, + + /** + * Returns the time interval of frame. + * @returns {number} the time interval of frame + */ + getUnitTime: function () { + return this._unitTime; + }, + + /** + * Returns the current time of frame. + * @returns {number} + */ + getCurrentTime: function () { + return this._currentTime; + }, + + /** + * Sets the current time of frame. + * @param {Number} time the current time of frame + */ + setCurrentTime: function (time) { + this._currentTime = time; + }, + + /** + * Returns the total time of frame. + * @returns {number} the total time of frame + */ + getTotalTime: function(){ + return this._fTotalTime; + }, + + /** + * Returns if the action is playing. + * @returns {boolean} true if the action is playing, false the otherwise + */ + isPlaying: function () { + return this._playing; + }, + + /** + * Init properties with a json dictionary + * @param {Object} dic + * @param {Object} root + */ + initWithDictionary: function (dic, root) { + this.setName(dic["name"]); + this.setLoop(dic["loop"]); + this.setUnitTime(dic["unittime"]); + var actionNodeList = dic["actionnodelist"]; + var maxLength = 0; + for (var i = 0; i < actionNodeList.length; i++) { + var actionNode = new ccs.ActionNode(); + + var actionNodeDic = actionNodeList[i]; + actionNode.initWithDictionary(actionNodeDic, root); + actionNode.setUnitTime(this.getUnitTime()); + this._actionNodeList.push(actionNode); + var length = actionNode.getLastFrameIndex() - actionNode.getFirstFrameIndex(); + if(length > maxLength){ + maxLength = length; + } + } + this._fTotalTime = maxLength * this._unitTime; + }, + + /** + * Adds a ActionNode to play the action. + * @param {ccs.ActionNode} node + */ + addActionNode: function (node) { + if (!node) + return; + this._actionNodeList.push(node); + node.setUnitTime(this._unitTime); + }, + + /** + * Removes a ActionNode which play the action. + * @param {ccs.ActionNode} node + */ + removeActionNode: function (node) { + if (node == null) + return; + cc.arrayRemoveObject(this._actionNodeList, node); + }, + + /** + * Plays the action. + * @param {cc.CallFunc} [fun] Action Call Back + */ + play: function (fun) { + this.stop(); + this.updateToFrameByTime(0); + var locActionNodeList = this._actionNodeList; + var frameNum = locActionNodeList.length; + for (var i = 0; i < frameNum; i++) { + locActionNodeList[i].playAction(fun); + } + if (this._loop) + this._scheduler.schedule(this.simulationActionUpdate, this, 0, cc.REPEAT_FOREVER, 0, false, this.__instanceId + ""); + if(fun !== undefined) + this._callback = fun; + }, + + /** + * Pauses the action. + */ + pause: function () { + this._pause = true; + this._playing = false; + }, + + /** + * Stop the action. + */ + stop: function () { + var locActionNodeList = this._actionNodeList; + for (var i = 0; i < locActionNodeList.length; i++) + locActionNodeList[i].stopAction(); + this._scheduler.unschedule(this.simulationActionUpdate, this); + this._pause = false; + this._playing = false; + }, + + /** + * Updates frame by time. + */ + updateToFrameByTime: function (time) { + this._currentTime = time; + for (var i = 0; i < this._actionNodeList.length; i++) { + var locActionNode = this._actionNodeList[i]; + locActionNode.updateActionToTimeLine(time); + } + }, + + /** + * scheduler update function + * @param {Number} dt delta time + */ + simulationActionUpdate: function (dt) { + var isEnd = true, locNodeList = this._actionNodeList; + for(var i = 0, len = locNodeList.length; i < len; i++) { + if (!locNodeList[i].isActionDoneOnce()){ + isEnd = false; + break; + } + } + + if (isEnd){ + if (this._callback !== null) + this._callback.execute(); + if (this._loop) + this.play(); + else{ + this._playing = false; + this._scheduler.unschedule(this.simulationActionUpdate, this); + } + } + } +}); \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/armature/CCArmature.js b/frameworks/cocos2d-html5/extensions/cocostudio/armature/CCArmature.js new file mode 100644 index 0000000..3836c34 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/armature/CCArmature.js @@ -0,0 +1,591 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The main class of Armature, it plays armature animation, manages and updates bones' state. + * @class + * @extends ccs.Node + * + * @property {ccs.Bone} parentBone - The parent bone of the armature node + * @property {ccs.ArmatureAnimation} animation - The animation + * @property {ccs.ArmatureData} armatureData - The armature data + * @property {String} name - The name of the armature + * @property {cc.SpriteBatchNode} batchNode - The batch node of the armature + * @property {Number} version - The version + * @property {Object} body - The body of the armature + * @property {ccs.ColliderFilter} colliderFilter - <@writeonly> The collider filter of the armature + */ +ccs.Armature = ccs.Node.extend(/** @lends ccs.Armature# */{ + animation: null, + armatureData: null, + batchNode: null, + _parentBone: null, + _boneDic: null, + _topBoneList: null, + _armatureIndexDic: null, + _offsetPoint: null, + version: 0, + _armatureTransformDirty: true, + _body: null, + _blendFunc: null, + _className: "Armature", + + /** + * Create a armature node. + * Constructor of ccs.Armature + * @param {String} name + * @param {ccs.Bone} parentBone + * @example + * var armature = new ccs.Armature(); + */ + ctor: function (name, parentBone) { + cc.Node.prototype.ctor.call(this); + this._name = ""; + this._topBoneList = []; + this._armatureIndexDic = {}; + this._offsetPoint = cc.p(0, 0); + this._armatureTransformDirty = true; + this._blendFunc = {src: cc.BLEND_SRC, dst: cc.BLEND_DST}; + name && ccs.Armature.prototype.init.call(this, name, parentBone); + // Hack way to avoid RendererWebGL from skipping Armature + this._texture = {}; + }, + + /** + * Initializes a CCArmature with the specified name and CCBone + * @param {String} [name] + * @param {ccs.Bone} [parentBone] + * @return {Boolean} + */ + init: function (name, parentBone) { + if (parentBone) + this._parentBone = parentBone; + this.removeAllChildren(); + this.animation = new ccs.ArmatureAnimation(); + this.animation.init(this); + + this._boneDic = {}; + this._topBoneList.length = 0; + + //this._name = name || ""; + var armatureDataManager = ccs.armatureDataManager; + + var animationData; + if (name !== "") { + //animationData + animationData = armatureDataManager.getAnimationData(name); + cc.assert(animationData, "AnimationData not exist!"); + + this.animation.setAnimationData(animationData); + + //armatureData + var armatureData = armatureDataManager.getArmatureData(name); + cc.assert(armatureData, "ArmatureData not exist!"); + + this.armatureData = armatureData; + + //boneDataDic + var boneDataDic = armatureData.getBoneDataDic(); + for (var key in boneDataDic) { + var bone = this.createBone(String(key)); + + //! init bone's Tween to 1st movement's 1st frame + do { + var movData = animationData.getMovement(animationData.movementNames[0]); + if (!movData) break; + + var _movBoneData = movData.getMovementBoneData(bone.getName()); + if (!_movBoneData || _movBoneData.frameList.length <= 0) break; + + var frameData = _movBoneData.getFrameData(0); + if (!frameData) break; + + bone.getTweenData().copy(frameData); + bone.changeDisplayWithIndex(frameData.displayIndex, false); + } while (0); + } + + this.update(0); + this.updateOffsetPoint(); + } else { + name = "new_armature"; + this.armatureData = new ccs.ArmatureData(); + this.armatureData.name = name; + + animationData = new ccs.AnimationData(); + animationData.name = name; + + armatureDataManager.addArmatureData(name, this.armatureData); + armatureDataManager.addAnimationData(name, animationData); + + this.animation.setAnimationData(animationData); + } + + this._renderCmd.initShaderCache(); + + this.setCascadeOpacityEnabled(true); + this.setCascadeColorEnabled(true); + return true; + }, + + visit: function (parent) { + var cmd = this._renderCmd, parentCmd = parent ? parent._renderCmd : null; + + // quick return if not visible + if (!this._visible) { + cmd._propagateFlagsDown(parentCmd); + return; + } + + cmd.visit(parentCmd); + cmd._dirtyFlag = 0; + }, + + addChild: function (child, localZOrder, tag) { + if (child instanceof ccui.Widget) { + cc.log("Armature doesn't support to add Widget as its child, it will be fix soon."); + return; + } + cc.Node.prototype.addChild.call(this, child, localZOrder, tag); + }, + + /** + * create a bone with name + * @param {String} boneName + * @return {ccs.Bone} + */ + createBone: function (boneName) { + var existedBone = this.getBone(boneName); + if (existedBone) + return existedBone; + + var boneData = this.armatureData.getBoneData(boneName); + var parentName = boneData.parentName; + + var bone = null; + if (parentName) { + this.createBone(parentName); + bone = new ccs.Bone(boneName); + this.addBone(bone, parentName); + } else { + bone = new ccs.Bone(boneName); + this.addBone(bone, ""); + } + + bone.setBoneData(boneData); + bone.getDisplayManager().changeDisplayWithIndex(-1, false); + return bone; + }, + + /** + * Add a Bone to this Armature + * @param {ccs.Bone} bone The Bone you want to add to Armature + * @param {String} parentName The parent Bone's name you want to add to. If it's null, then set Armature to its parent + */ + addBone: function (bone, parentName) { + cc.assert(bone, "Argument must be non-nil"); + var locBoneDic = this._boneDic; + if (bone.getName()) + cc.assert(!locBoneDic[bone.getName()], "bone already added. It can't be added again"); + + if (parentName) { + var boneParent = locBoneDic[parentName]; + if (boneParent) + boneParent.addChildBone(bone); + else + this._topBoneList.push(bone); + } else + this._topBoneList.push(bone); + bone.setArmature(this); + + locBoneDic[bone.getName()] = bone; + this.addChild(bone); + }, + + /** + * Remove a bone with the specified name. If recursion it will also remove child Bone recursively. + * @param {ccs.Bone} bone The bone you want to remove + * @param {Boolean} recursion Determine whether remove the bone's child recursion. + */ + removeBone: function (bone, recursion) { + cc.assert(bone, "bone must be added to the bone dictionary!"); + + bone.setArmature(null); + bone.removeFromParent(recursion); + cc.arrayRemoveObject(this._topBoneList, bone); + + delete this._boneDic[bone.getName()]; + this.removeChild(bone, true); + }, + + /** + * Gets a bone with the specified name + * @param {String} name The bone's name you want to get + * @return {ccs.Bone} + */ + getBone: function (name) { + return this._boneDic[name]; + }, + + /** + * Change a bone's parent with the specified parent name. + * @param {ccs.Bone} bone The bone you want to change parent + * @param {String} parentName The new parent's name + */ + changeBoneParent: function (bone, parentName) { + cc.assert(bone, "bone must be added to the bone dictionary!"); + + var parentBone = bone.getParentBone(); + if (parentBone) { + cc.arrayRemoveObject(parentBone.getChildren(), bone); + bone.setParentBone(null); + } + + if (parentName) { + var boneParent = this._boneDic[parentName]; + if (boneParent) { + boneParent.addChildBone(bone); + cc.arrayRemoveObject(this._topBoneList, bone); + } else + this._topBoneList.push(bone); + } + }, + + /** + * Get CCArmature's bone dictionary + * @return {Object} Armature's bone dictionary + */ + getBoneDic: function () { + return this._boneDic; + }, + + /** + * Set contentSize and Calculate anchor point. + */ + updateOffsetPoint: function () { + // Set contentsize and Calculate anchor point. + var rect = this.getBoundingBox(); + this.setContentSize(rect); + var locOffsetPoint = this._offsetPoint; + locOffsetPoint.x = -rect.x; + locOffsetPoint.y = -rect.y; + if (rect.width !== 0 && rect.height !== 0) + this.setAnchorPoint(locOffsetPoint.x / rect.width, locOffsetPoint.y / rect.height); + }, + + getOffsetPoints: function () { + return {x: this._offsetPoint.x, y: this._offsetPoint.y}; + }, + + /** + * Sets animation to this Armature + * @param {ccs.ArmatureAnimation} animation + */ + setAnimation: function (animation) { + this.animation = animation; + }, + + /** + * Gets the animation of this Armature. + * @return {ccs.ArmatureAnimation} + */ + getAnimation: function () { + return this.animation; + }, + + /** + * armatureTransformDirty getter + * @returns {Boolean} + */ + getArmatureTransformDirty: function () { + return this._armatureTransformDirty; + }, + + /** + * The update callback of ccs.Armature, it updates animation's state and updates bone's state. + * @override + * @param {Number} dt + */ + update: function (dt) { + this.animation.update(dt); + var locTopBoneList = this._topBoneList; + for (var i = 0; i < locTopBoneList.length; i++) + locTopBoneList[i].update(dt); + this._armatureTransformDirty = false; + }, + + /** + * The callback when ccs.Armature enter stage. + * @override + */ + onEnter: function () { + cc.Node.prototype.onEnter.call(this); + this.scheduleUpdate(); + }, + + /** + * The callback when ccs.Armature exit stage. + * @override + */ + onExit: function () { + cc.Node.prototype.onExit.call(this); + this.unscheduleUpdate(); + }, + + /** + * This boundingBox will calculate all bones' boundingBox every time + * @returns {cc.Rect} + */ + getBoundingBox: function () { + var minX, minY, maxX, maxY = 0; + var first = true; + + var boundingBox = cc.rect(0, 0, 0, 0), locChildren = this._children; + + var len = locChildren.length; + for (var i = 0; i < len; i++) { + var bone = locChildren[i]; + if (bone) { + var r = bone.getDisplayManager().getBoundingBox(); + if (r.x === 0 && r.y === 0 && r.width === 0 && r.height === 0) + continue; + + if (first) { + minX = r.x; + minY = r.y; + maxX = r.x + r.width; + maxY = r.y + r.height; + first = false; + } else { + minX = r.x < boundingBox.x ? r.x : boundingBox.x; + minY = r.y < boundingBox.y ? r.y : boundingBox.y; + maxX = r.x + r.width > boundingBox.x + boundingBox.width ? + r.x + r.width : boundingBox.x + boundingBox.width; + maxY = r.y + r.height > boundingBox.y + boundingBox.height ? + r.y + r.height : boundingBox.y + boundingBox.height; + } + + boundingBox.x = minX; + boundingBox.y = minY; + boundingBox.width = maxX - minX; + boundingBox.height = maxY - minY; + } + } + return cc.rectApplyAffineTransform(boundingBox, this.getNodeToParentTransform()); + }, + + /** + * when bone contain the point ,then return it. + * @param {Number} x + * @param {Number} y + * @returns {ccs.Bone} + */ + getBoneAtPoint: function (x, y) { + var locChildren = this._children; + for (var i = locChildren.length - 1; i >= 0; i--) { + var child = locChildren[i]; + if (child instanceof ccs.Bone && child.getDisplayManager().containPoint(x, y)) + return child; + } + return null; + }, + + /** + * Sets parent bone of this Armature + * @param {ccs.Bone} parentBone + */ + setParentBone: function (parentBone) { + this._parentBone = parentBone; + var locBoneDic = this._boneDic; + for (var key in locBoneDic) { + locBoneDic[key].setArmature(this); + } + }, + + /** + * Return parent bone of ccs.Armature. + * @returns {ccs.Bone} + */ + getParentBone: function () { + return this._parentBone; + }, + + /** + * draw contour + */ + drawContour: function () { + cc._drawingUtil.setDrawColor(255, 255, 255, 255); + cc._drawingUtil.setLineWidth(1); + var locBoneDic = this._boneDic; + for (var key in locBoneDic) { + var bone = locBoneDic[key]; + var detector = bone.getColliderDetector(); + if (!detector) + continue; + var bodyList = detector.getColliderBodyList(); + for (var i = 0; i < bodyList.length; i++) { + var body = bodyList[i]; + var vertexList = body.getCalculatedVertexList(); + cc._drawingUtil.drawPoly(vertexList, vertexList.length, true); + } + } + }, + + setBody: function (body) { + if (this._body === body) + return; + + this._body = body; + this._body.data = this; + var child, displayObject, locChildren = this._children; + for (var i = 0; i < locChildren.length; i++) { + child = locChildren[i]; + if (child instanceof ccs.Bone) { + var displayList = child.getDisplayManager().getDecorativeDisplayList(); + for (var j = 0; j < displayList.length; j++) { + displayObject = displayList[j]; + var detector = displayObject.getColliderDetector(); + if (detector) + detector.setBody(this._body); + } + } + } + }, + + getShapeList: function () { + if (this._body) + return this._body.shapeList; + return null; + }, + + getBody: function () { + return this._body; + }, + + /** + * Sets the blendFunc to ccs.Armature + * @param {cc.BlendFunc|Number} blendFunc + * @param {Number} [dst] + */ + setBlendFunc: function (blendFunc, dst) { + if (dst === undefined) { + this._blendFunc.src = blendFunc.src; + this._blendFunc.dst = blendFunc.dst; + } else { + this._blendFunc.src = blendFunc; + this._blendFunc.dst = dst; + } + }, + + /** + * Returns the blendFunc of ccs.Armature + * @returns {cc.BlendFunc} + */ + getBlendFunc: function () { + return new cc.BlendFunc(this._blendFunc.src, this._blendFunc.dst); + }, + + /** + * set collider filter + * @param {ccs.ColliderFilter} filter + */ + setColliderFilter: function (filter) { + var locBoneDic = this._boneDic; + for (var key in locBoneDic) + locBoneDic[key].setColliderFilter(filter); + }, + + /** + * Returns the armatureData of ccs.Armature + * @return {ccs.ArmatureData} + */ + getArmatureData: function () { + return this.armatureData; + }, + + /** + * Sets armatureData to this Armature + * @param {ccs.ArmatureData} armatureData + */ + setArmatureData: function (armatureData) { + this.armatureData = armatureData; + }, + + getBatchNode: function () { + return this.batchNode; + }, + + setBatchNode: function (batchNode) { + this.batchNode = batchNode; + }, + + /** + * version getter + * @returns {Number} + */ + getVersion: function () { + return this.version; + }, + + /** + * version setter + * @param {Number} version + */ + setVersion: function (version) { + this.version = version; + }, + + _createRenderCmd: function () { + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return new ccs.Armature.CanvasRenderCmd(this); + else + return new ccs.Armature.WebGLRenderCmd(this); + } +}); + +var _p = ccs.Armature.prototype; + +/** @expose */ +_p.parentBone; +cc.defineGetterSetter(_p, "parentBone", _p.getParentBone, _p.setParentBone); +/** @expose */ +_p.body; +cc.defineGetterSetter(_p, "body", _p.getBody, _p.setBody); +/** @expose */ +_p.colliderFilter; +cc.defineGetterSetter(_p, "colliderFilter", null, _p.setColliderFilter); + +_p = null; + +/** + * Allocates an armature, and use the ArmatureData named name in ArmatureDataManager to initializes the armature. + * @param {String} [name] Bone name + * @param {ccs.Bone} [parentBone] the parent bone + * @return {ccs.Armature} + * @deprecated since v3.1, please use new construction instead + */ +ccs.Armature.create = function (name, parentBone) { + return new ccs.Armature(name, parentBone); +}; diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/armature/CCArmatureCanvasRenderCmd.js b/frameworks/cocos2d-html5/extensions/cocostudio/armature/CCArmatureCanvasRenderCmd.js new file mode 100644 index 0000000..bdb35f9 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/armature/CCArmatureCanvasRenderCmd.js @@ -0,0 +1,213 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + ccs.Armature.RenderCmd = { + _updateAnchorPointInPoint: function () { + var node = this._node; + var contentSize = node._contentSize, anchorPoint = node._anchorPoint, offsetPoint = node._offsetPoint; + this._anchorPointInPoints.x = contentSize.width * anchorPoint.x - offsetPoint.x; + this._anchorPointInPoints.y = contentSize.height * anchorPoint.y - offsetPoint.y; + + this._realAnchorPointInPoints.x = contentSize.width * anchorPoint.x; + this._realAnchorPointInPoints.y = contentSize.height * anchorPoint.y; + this.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }, + + getAnchorPointInPoints: function () { + return cc.p(this._realAnchorPointInPoints); + } + }; +})(); + +(function () { + ccs.Armature.CanvasRenderCmd = function (renderableObject) { + this._rootCtor(renderableObject); + this._needDraw = true; + + this._realAnchorPointInPoints = new cc.Point(0, 0); + this._canUseDirtyRegion = true; + this._startRenderCmd = new cc.CustomRenderCmd(this, this._startCmdCallback); + this._RestoreRenderCmd = new cc.CustomRenderCmd(this, this._RestoreCmdCallback); + this._startRenderCmd._canUseDirtyRegion = true; + this._RestoreRenderCmd._canUseDirtyRegion = true; + + this._transform = {a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0}; + this._worldTransform = {a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0}; + }; + + var proto = ccs.Armature.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + cc.inject(ccs.Armature.RenderCmd, proto); + proto.constructor = ccs.Armature.CanvasRenderCmd; + + proto._startCmdCallback = function (ctx, scaleX, scaleY) { + var node = this._node, parent = node._parent; + this.transform(parent ? parent._renderCmd : null); + + var wrapper = ctx || cc._renderContext; + wrapper.save(); + //set to armature mode + wrapper._switchToArmatureMode(true, this._worldTransform, scaleX, scaleY); + }; + + proto.transform = function (parentCmd, recursive) { + this.originTransform(parentCmd, recursive); + + var locChildren = this._node._children; + for (var i = 0, len = locChildren.length; i < len; i++) { + var selBone = locChildren[i]; + var boneCmd = selBone._renderCmd; + if (selBone && selBone.getDisplayRenderNode) { + var boneType = selBone.getDisplayRenderNodeType(); + var selNode = selBone.getDisplayRenderNode(); + if (selNode && selNode._renderCmd) { + var cmd = selNode._renderCmd; + cmd.transform(null); //must be null, use transform in armature mode + if (boneType !== ccs.DISPLAY_TYPE_ARMATURE && boneType !== ccs.DISPLAY_TYPE_SPRITE) { + cc.affineTransformConcatIn(cmd._worldTransform, selBone._worldTransform); + } + + //update displayNode's color and opacity, because skin didn't call visit() + var flags = cc.Node._dirtyFlags, locFlag = cmd._dirtyFlag, boneFlag = boneCmd._dirtyFlag; + var colorDirty = boneFlag & flags.colorDirty, + opacityDirty = boneFlag & flags.opacityDirty; + if (colorDirty) + boneCmd._updateDisplayColor(this._displayedColor); + if (opacityDirty) + boneCmd._updateDisplayOpacity(this._displayedOpacity); + if (colorDirty || opacityDirty) + boneCmd._updateColor(); + + var parentColor = selBone._renderCmd._displayedColor, parentOpacity = selBone._renderCmd._displayedOpacity; + colorDirty = locFlag & flags.colorDirty; + opacityDirty = locFlag & flags.opacityDirty; + if (colorDirty) + cmd._updateDisplayColor(parentColor); + if (opacityDirty) + cmd._updateDisplayOpacity(parentOpacity); + if (colorDirty || opacityDirty) { + cmd._updateColor(); + } + } + } + } + }; + + proto._RestoreCmdCallback = function (wrapper) { + this._cacheDirty = false; + wrapper._switchToArmatureMode(false); + wrapper.restore(); + }; + + proto.initShaderCache = function () { + }; + proto.setShaderProgram = function () { + }; + proto.updateChildPosition = function (dis, bone) { + dis.visit(); + // cc.renderer.pushRenderCommand(dis._renderCmd); + }; + + proto.rendering = function (ctx, scaleX, scaleY) { + var node = this._node; + var locChildren = node._children; + var alphaPremultiplied = cc.BlendFunc.ALPHA_PREMULTIPLIED, alphaNonPremultipled = cc.BlendFunc.ALPHA_NON_PREMULTIPLIED; + for (var i = 0, len = locChildren.length; i < len; i++) { + var selBone = locChildren[i]; + if (selBone && selBone.getDisplayRenderNode) { + var selNode = selBone.getDisplayRenderNode(); + if (null === selNode) + continue; + + selBone._renderCmd._syncStatus(this); + switch (selBone.getDisplayRenderNodeType()) { + case ccs.DISPLAY_TYPE_SPRITE: + selNode.visit(selBone); + break; + case ccs.DISPLAY_TYPE_ARMATURE: + selNode._renderCmd.rendering(ctx, scaleX, scaleY); + break; + default: + selNode.visit(selBone); + break; + } + } else if (selBone instanceof cc.Node) { + this._visitNormalChild(selBone); + // selBone.visit(this); + } + } + }; + + proto._visitNormalChild = function (childNode) { + if (!childNode) + return; + + var cmd = childNode._renderCmd; + // quick return if not visible + if (!childNode._visible) + return; + cmd._curLevel = this._curLevel + 1; + + //visit for canvas + var i, children = childNode._children, child; + cmd._syncStatus(this); + //because armature use transform, not setTransform + cmd.transform(null); + + var len = children.length; + if (len > 0) { + childNode.sortAllChildren(); + // draw children zOrder < 0 + for (i = 0; i < len; i++) { + child = children[i]; + if (child._localZOrder < 0) + child.visit(childNode); + else + break; + } + cc.renderer.pushRenderCommand(cmd); + for (; i < len; i++) + children[i].visit(childNode); + } else { + cc.renderer.pushRenderCommand(cmd); + } + this._dirtyFlag = 0; + }; + + proto.visit = function (parentCmd) { + var node = this._node; + // quick return if not visible. children won't be drawn. + if (!node._visible) + return; + + this._syncStatus(parentCmd); + node.sortAllChildren(); + + cc.renderer.pushRenderCommand(this._startRenderCmd); + this.rendering(); + cc.renderer.pushRenderCommand(this._RestoreRenderCmd); + + this._cacheDirty = false; + }; +})(); diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/armature/CCArmatureWebGLRenderCmd.js b/frameworks/cocos2d-html5/extensions/cocostudio/armature/CCArmatureWebGLRenderCmd.js new file mode 100644 index 0000000..6f24c7b --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/armature/CCArmatureWebGLRenderCmd.js @@ -0,0 +1,185 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + + ccs.Armature.WebGLRenderCmd = function (renderableObject) { + this._rootCtor(renderableObject); + this._needDraw = true; + + this._parentCmd = null; + this._realAnchorPointInPoints = new cc.Point(0, 0); + + this._transform = {a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0}; + this._worldTransform = {a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0}; + }; + + var proto = ccs.Armature.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + cc.inject(ccs.Armature.RenderCmd, proto); + proto.constructor = ccs.Armature.WebGLRenderCmd; + + proto.uploadData = function (f32buffer, ui32buffer, vertexDataOffset) { + var node = this._node, cmd; + var parentCmd = this._parentCmd || this; + + var locChildren = node._children; + var alphaPremultiplied = cc.BlendFunc.ALPHA_PREMULTIPLIED, alphaNonPremultipled = cc.BlendFunc.ALPHA_NON_PREMULTIPLIED; + for (var i = 0, len = locChildren.length; i < len; i++) { + var selBone = locChildren[i]; + var boneCmd = selBone._renderCmd; + if (selBone && selBone.getDisplayRenderNode) { + var selNode = selBone.getDisplayRenderNode(); + if (null === selNode) + continue; + cmd = selNode._renderCmd; + switch (selBone.getDisplayRenderNodeType()) { + case ccs.DISPLAY_TYPE_SPRITE: + if (selNode instanceof ccs.Skin) { + selNode.setShaderProgram(this._shaderProgram); + this._updateColorAndOpacity(cmd, selBone); //because skin didn't call visit() + cmd.transform(parentCmd); + + var func = selBone.getBlendFunc(); + if (func.src !== alphaPremultiplied.src || func.dst !== alphaPremultiplied.dst) + selNode.setBlendFunc(selBone.getBlendFunc()); + else { + var tex = selNode.getTexture(); + if (node._blendFunc.src === alphaPremultiplied.src && + node._blendFunc.dst === alphaPremultiplied.dst && + tex && !tex.hasPremultipliedAlpha()) { + selNode.setBlendFunc(alphaNonPremultipled); + } + else { + selNode.setBlendFunc(node._blendFunc); + } + } + // Support batch for Armature skin + cc.renderer._uploadBufferData(cmd); + } + break; + case ccs.DISPLAY_TYPE_ARMATURE: + selNode.setShaderProgram(this._shaderProgram); + this._updateColorAndOpacity(cmd, selBone); + cmd._parentCmd = this; + // Continue rendering in default + default: + boneCmd._syncStatus(parentCmd); + cmd._syncStatus(boneCmd); + if (cmd.uploadData) { + cc.renderer._uploadBufferData(cmd); + } + else if (cmd.rendering) { + // Finish previous batch + cc.renderer._batchRendering(); + cmd.rendering(cc._renderContext); + } + break; + } + } else if (selBone instanceof cc.Node) { + selBone.setShaderProgram(this._shaderProgram); + boneCmd._syncStatus(parentCmd); + if (boneCmd.uploadData) { + cc.renderer._uploadBufferData(boneCmd); + } + else if (boneCmd.rendering) { + // Finish previous batch + cc.renderer._batchRendering(); + boneCmd.rendering(cc._renderContext); + } + } + } + this._parentCmd = null; + return 0; + }; + + proto.initShaderCache = function () { + this._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_SPRITE_POSITION_TEXTURECOLOR); + }; + + proto.setShaderProgram = function (shaderProgram) { + this._glProgramState = cc.GLProgramState.getOrCreateWithGLProgram(shaderProgram); + }; + + proto._updateColorAndOpacity = function (skinRenderCmd, bone) { + //update displayNode's color and opacity + var parentColor = bone._renderCmd._displayedColor, parentOpacity = bone._renderCmd._displayedOpacity; + + var flags = cc.Node._dirtyFlags, locFlag = skinRenderCmd._dirtyFlag; + var colorDirty = locFlag & flags.colorDirty, + opacityDirty = locFlag & flags.opacityDirty; + if (colorDirty) + skinRenderCmd._updateDisplayColor(parentColor); + if (opacityDirty) + skinRenderCmd._updateDisplayOpacity(parentOpacity); + if (colorDirty || opacityDirty) + skinRenderCmd._updateColor(); + }; + + proto.visit = function (parentCmd) { + var node = this._node; + // quick return if not visible. children won't be drawn. + if (!node._visible) + return; + + parentCmd = parentCmd || this.getParentRenderCmd(); + if (parentCmd) + this._curLevel = parentCmd._curLevel + 1; + + this._syncStatus(parentCmd); + + node.sortAllChildren(); + var renderer = cc.renderer, + children = node._children, child, + i, len = children.length; + + if (isNaN(node._customZ)) { + node._vertexZ = renderer.assignedZ; + renderer.assignedZ += renderer.assignedZStep; + } + + for (i = 0; i < len; i++) { + child = children[i]; + if (child._localZOrder < 0) { + if (isNaN(child._customZ)) { + child._vertexZ = renderer.assignedZ; + renderer.assignedZ += renderer.assignedZStep; + } + } + else { + break; + } + } + + renderer.pushRenderCommand(this); + for (; i < len; i++) { + child = children[i]; + if (isNaN(child._customZ)) { + child._vertexZ = renderer.assignedZ; + renderer.assignedZ += renderer.assignedZStep; + } + } + + this._dirtyFlag = 0; + }; +})(); diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/armature/CCBone.js b/frameworks/cocos2d-html5/extensions/cocostudio/armature/CCBone.js new file mode 100644 index 0000000..953e786 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/armature/CCBone.js @@ -0,0 +1,747 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The Bone of Armature, it has bone data, display manager and transform data for armature. + * @class + * @extends ccs.Node + * + * @param {String} [name] The name of the bone + * @example + * + * var bone = new ccs.Bone("head"); + * + * @property {ccs.BoneData} boneData - The bone data + * @property {ccs.Armature} armature - The armature + * @property {ccs.Bone} parentBone - The parent bone + * @property {ccs.Armature} childArmature - The child armature + * @property {Array} childrenBone - <@readonly> All children bones + * @property {ccs.Tween} tween - <@readonly> Tween + * @property {ccs.FrameData} tweenData - <@readonly> The tween data + * @property {ccs.ColliderFilter} colliderFilter - The collider filter + * @property {ccs.DisplayManager} displayManager - The displayManager + * @property {Boolean} ignoreMovementBoneData - Indicate whether force the bone to show When CCArmature play a animation and there isn't a CCMovementBoneData of this bone in this CCMovementData. + * @property {String} name - The name of the bone + * @property {Boolean} blendDirty - Indicate whether the blend is dirty + * + */ +ccs.Bone = ccs.Node.extend(/** @lends ccs.Bone# */{ + _boneData: null, + _armature: null, + _childArmature: null, + _displayManager: null, + ignoreMovementBoneData: false, + _tween: null, + _tweenData: null, + _parentBone: null, + _boneTransformDirty: false, + _worldTransform: null, + _blendFunc: null, + blendDirty: false, + _worldInfo: null, + _armatureParentBone: null, + _dataVersion: 0, + _className: "Bone", + + ctor: function (name) { + cc.Node.prototype.ctor.call(this); + this._tweenData = null; + this._parentBone = null; + this._armature = null; + this._childArmature = null; + this._boneData = null; + this._tween = null; + this._displayManager = null; + this.ignoreMovementBoneData = false; + + this._worldTransform = cc.affineTransformMake(1, 0, 0, 1, 0, 0); + this._boneTransformDirty = true; + this._blendFunc = new cc.BlendFunc(cc.BLEND_SRC, cc.BLEND_DST); + this.blendDirty = false; + this._worldInfo = null; + + this._armatureParentBone = null; + this._dataVersion = 0; + + ccs.Bone.prototype.init.call(this, name); + }, + + /** + * Initializes a ccs.Bone with the specified name + * @param {String} name bone name + * @return {Boolean} + */ + init: function (name) { +// cc.Node.prototype.init.call(this); + if (name) + this._name = name; + this._tweenData = new ccs.FrameData(); + + this._tween = new ccs.Tween(this); + + this._displayManager = new ccs.DisplayManager(this); + + this._worldInfo = new ccs.BaseData(); + this._boneData = new ccs.BaseData(); + + return true; + }, + + /** + * Sets the boneData to ccs.Bone. + * @param {ccs.BoneData} boneData + */ + setBoneData: function (boneData) { + cc.assert(boneData, "_boneData must not be null"); + + if (this._boneData !== boneData) + this._boneData = boneData; + + this.setName(this._boneData.name); + this._localZOrder = this._boneData.zOrder; + this._displayManager.initDisplayList(boneData); + }, + + /** + * Returns boneData of ccs.Bone. + * @return {ccs.BoneData} + */ + getBoneData: function () { + return this._boneData; + }, + + /** + * Sets the armature reference to ccs.Bone. + * @param {ccs.Armature} armature + */ + setArmature: function (armature) { + this._armature = armature; + if (armature) { + this._tween.setAnimation(this._armature.getAnimation()); + this._dataVersion = this._armature.getArmatureData().dataVersion; + this._armatureParentBone = this._armature.getParentBone(); + } else + this._armatureParentBone = null; + }, + + /** + * Returns the armature reference of ccs.Bone. + * @return {ccs.Armature} + */ + getArmature: function () { + return this._armature; + }, + + /** + * Updates worldTransform by tween data and updates display state + * @param {Number} delta + */ + update: function (delta) { + if (this._parentBone) + this._boneTransformDirty = this._boneTransformDirty || this._parentBone.isTransformDirty(); + + if (this._armatureParentBone && !this._boneTransformDirty) + this._boneTransformDirty = this._armatureParentBone.isTransformDirty(); + + if (this._boneTransformDirty) { + var locTweenData = this._tweenData; + if (this._dataVersion >= ccs.CONST_VERSION_COMBINED) { + ccs.TransformHelp.nodeConcat(locTweenData, this._boneData); + locTweenData.scaleX -= 1; + locTweenData.scaleY -= 1; + } + + var locWorldInfo = this._worldInfo; + locWorldInfo.copy(locTweenData); + locWorldInfo.x = locTweenData.x + this._position.x; + locWorldInfo.y = locTweenData.y + this._position.y; + locWorldInfo.scaleX = locTweenData.scaleX * this._scaleX; + locWorldInfo.scaleY = locTweenData.scaleY * this._scaleY; + locWorldInfo.skewX = locTweenData.skewX + this._skewX + cc.degreesToRadians(this._rotationX); + locWorldInfo.skewY = locTweenData.skewY + this._skewY - cc.degreesToRadians(this._rotationY); + + if (this._parentBone) + this._applyParentTransform(this._parentBone); + else { + if (this._armatureParentBone) + this._applyParentTransform(this._armatureParentBone); + } + + ccs.TransformHelp.nodeToMatrix(locWorldInfo, this._worldTransform); + if (this._armatureParentBone) + cc.affineTransformConcatIn(this._worldTransform, this._armature.getNodeToParentTransform()); //TODO TransformConcat + } + + ccs.displayFactory.updateDisplay(this, delta, this._boneTransformDirty || this._armature.getArmatureTransformDirty()); + for (var i = 0; i < this._children.length; i++) { + var childBone = this._children[i]; + childBone.update(delta); + } + this._boneTransformDirty = false; + }, + + _applyParentTransform: function (parent) { + var locWorldInfo = this._worldInfo; + var locParentWorldTransform = parent._worldTransform; + var locParentWorldInfo = parent._worldInfo; + var x = locWorldInfo.x; + var y = locWorldInfo.y; + locWorldInfo.x = x * locParentWorldTransform.a + y * locParentWorldTransform.c + locParentWorldInfo.x; + locWorldInfo.y = x * locParentWorldTransform.b + y * locParentWorldTransform.d + locParentWorldInfo.y; + locWorldInfo.scaleX = locWorldInfo.scaleX * locParentWorldInfo.scaleX; + locWorldInfo.scaleY = locWorldInfo.scaleY * locParentWorldInfo.scaleY; + locWorldInfo.skewX = locWorldInfo.skewX + locParentWorldInfo.skewX; + locWorldInfo.skewY = locWorldInfo.skewY + locParentWorldInfo.skewY; + }, + + /** + * Sets BlendFunc to ccs.Bone. + * @param {cc.BlendFunc|Number} blendFunc blendFunc or src of blendFunc + * @param {Number} [dst] dst of blendFunc + */ + setBlendFunc: function (blendFunc, dst) { + var locBlendFunc = this._blendFunc, srcValue, dstValue; + if (dst === undefined) { + srcValue = blendFunc.src; + dstValue = blendFunc.dst; + } else { + srcValue = blendFunc; + dstValue = dst; + } + if (locBlendFunc.src !== srcValue || locBlendFunc.dst !== dstValue) { + locBlendFunc.src = srcValue; + locBlendFunc.dst = dstValue; + this.blendDirty = true; + } + }, + + /** + * Updates display color + */ + updateColor: function () { + var display = this._displayManager.getDisplayRenderNode(); + if (display !== null) { + var cmd = this._renderCmd; + display.setColor( + cc.color( + cmd._displayedColor.r * this._tweenData.r / 255, + cmd._displayedColor.g * this._tweenData.g / 255, + cmd._displayedColor.b * this._tweenData.b / 255)); + display.setOpacity(cmd._displayedOpacity * this._tweenData.a / 255); + } + }, + + /** + * Updates display zOrder + */ + updateZOrder: function () { + if (this._armature.getArmatureData().dataVersion >= ccs.CONST_VERSION_COMBINED) { + this.setLocalZOrder(this._tweenData.zOrder + this._boneData.zOrder); + } else { + this.setLocalZOrder(this._tweenData.zOrder); + } + }, + + /** + * Adds a child to this bone, and it will let this child call setParent(ccs.Bone) function to set self to it's parent + * @param {ccs.Bone} child + */ + addChildBone: function (child) { + cc.assert(child, "Argument must be non-nil"); + cc.assert(!child.parentBone, "child already added. It can't be added again"); + + if (this._children.indexOf(child) < 0) { + this._children.push(child); + child.setParentBone(this); + } + }, + + /** + * Removes a child bone + * @param {ccs.Bone} bone + * @param {Boolean} recursion + */ + removeChildBone: function (bone, recursion) { + if (this._children.length > 0 && this._children.getIndex(bone) !== -1) { + if (recursion) { + var ccbones = bone._children; + for (var i = 0; i < ccbones.length; i++) { + var ccBone = ccbones[i]; + bone.removeChildBone(ccBone, recursion); + } + } + + bone.setParentBone(null); + bone.getDisplayManager().setCurrentDecorativeDisplay(null); + cc.arrayRemoveObject(this._children, bone); + } + }, + + /** + * Removes itself from its parent ccs.Bone. + * @param {Boolean} recursion + */ + removeFromParent: function (recursion) { + if (this._parentBone) + this._parentBone.removeChildBone(this, recursion); + }, + + /** + * Sets parent bone to ccs.Bone. + * If _parent is NUll, then also remove this bone from armature. + * It will not set the ccs.Armature, if you want to add the bone to a ccs.Armature, you should use ccs.Armature.addBone(bone, parentName). + * @param {ccs.Bone} parent the parent bone. + */ + setParentBone: function (parent) { + this._parentBone = parent; + }, + + /** + * Returns the parent bone of ccs.Bone. + * @returns {ccs.Bone} + */ + getParentBone: function () { + return this._parentBone; + }, + + /** + * Sets ccs.Bone's child armature + * @param {ccs.Armature} armature + */ + setChildArmature: function (armature) { + if (this._childArmature !== armature) { + if (armature == null && this._childArmature) + this._childArmature.setParentBone(null); + this._childArmature = armature; + } + }, + + /** + * Returns ccs.Bone's child armature. + * @return {ccs.Armature} + */ + getChildArmature: function () { + return this._childArmature; + }, + + /** + * Return the tween of ccs.Bone + * @return {ccs.Tween} + */ + getTween: function () { + return this._tween; + }, + + /** + * Sets the local zOrder to ccs.Bone. + * @param {Number} zOrder + */ + setLocalZOrder: function (zOrder) { + if (this._localZOrder !== zOrder) + cc.Node.prototype.setLocalZOrder.call(this, zOrder); + }, + + /** + * Return the worldTransform of ccs.Bone. + * @returns {cc.AffineTransform} + */ + getNodeToArmatureTransform: function () { + return this._worldTransform; + }, + + /** + * Returns the world transform of ccs.Bone. + * @override + * @returns {cc.AffineTransform} + */ + getNodeToWorldTransform: function () { + return cc.affineTransformConcat(this._worldTransform, this._armature.getNodeToWorldTransform()); + }, + + /** + * Returns the display render node. + * @returns {cc.Node} + */ + getDisplayRenderNode: function () { + return this._displayManager.getDisplayRenderNode(); + }, + + /** + * Returns the type of display render node + * @returns {Number} + */ + getDisplayRenderNodeType: function () { + return this._displayManager.getDisplayRenderNodeType(); + }, + + /** + * Add display and use _displayData init the display. + * If index already have a display, then replace it. + * If index is current display index, then also change display to _index + * @param {ccs.DisplayData} displayData it include the display information, like DisplayType. + * If you want to create a sprite display, then create a CCSpriteDisplayData param + *@param {Number} index the index of the display you want to replace or add to + * -1 : append display from back + */ + addDisplay: function (displayData, index) { + index = index || 0; + return this._displayManager.addDisplay(displayData, index); + }, + + /** + * Removes display by index. + * @param {Number} index display renderer's index + */ + removeDisplay: function (index) { + this._displayManager.removeDisplay(index); + }, + + /** + * Changes display by index + * @deprecated since v3.0, please use changeDisplayWithIndex instead. + * @param {Number} index + * @param {Boolean} force + */ + changeDisplayByIndex: function (index, force) { + cc.log("changeDisplayByIndex is deprecated. Use changeDisplayWithIndex instead."); + this.changeDisplayWithIndex(index, force); + }, + + /** + * Changes display by name + * @deprecated since v3.0, please use changeDisplayWithName instead. + * @param {String} name + * @param {Boolean} force + */ + changeDisplayByName: function (name, force) { + cc.log("changeDisplayByName is deprecated. Use changeDisplayWithName instead."); + this.changeDisplayWithName(name, force); + }, + + /** + * Changes display with index + * @param {Number} index + * @param {Boolean} force + */ + changeDisplayWithIndex: function (index, force) { + this._displayManager.changeDisplayWithIndex(index, force); + }, + + /** + * Changes display with name + * @param {String} name + * @param {Boolean} force + */ + changeDisplayWithName: function (name, force) { + this._displayManager.changeDisplayWithName(name, force); + }, + + /** + * Returns the collide detector of ccs.Bone. + * @returns {*} + */ + getColliderDetector: function () { + var decoDisplay = this._displayManager.getCurrentDecorativeDisplay(); + if (decoDisplay) { + var detector = decoDisplay.getColliderDetector(); + if (detector) + return detector; + } + return null; + }, + + /** + * Sets collider filter to ccs.Bone. + * @param {ccs.ColliderFilter} filter + */ + setColliderFilter: function (filter) { + var displayList = this._displayManager.getDecorativeDisplayList(); + for (var i = 0; i < displayList.length; i++) { + var locDecoDisplay = displayList[i]; + var locDetector = locDecoDisplay.getColliderDetector(); + if (locDetector) + locDetector.setColliderFilter(filter); + } + }, + + /** + * Returns collider filter of ccs.Bone. + * @returns {cc.ColliderFilter} + */ + getColliderFilter: function () { + var decoDisplay = this.displayManager.getCurrentDecorativeDisplay(); + if (decoDisplay) { + var detector = decoDisplay.getColliderDetector(); + if (detector) + return detector.getColliderFilter(); + } + return null; + }, + + /** + * Sets ccs.Bone's transform dirty flag. + * @param {Boolean} dirty + */ + setTransformDirty: function (dirty) { + this._boneTransformDirty = dirty; + }, + + /** + * Returns ccs.Bone's transform dirty flag whether is dirty. + * @return {Boolean} + */ + isTransformDirty: function () { + return this._boneTransformDirty; + }, + + /** + * displayManager dirty getter + * @return {ccs.DisplayManager} + */ + getDisplayManager: function () { + return this._displayManager; + }, + + /** + * When CCArmature play a animation, if there is not a CCMovementBoneData of this bone in this CCMovementData, this bone will hide.
+ * Set IgnoreMovementBoneData to true, then this bone will also show. + * @param {Boolean} bool + */ + setIgnoreMovementBoneData: function (bool) { + this._ignoreMovementBoneData = bool; + }, + + /** + * Returns whether is ignore movement bone data. + * @returns {Boolean} + */ + isIgnoreMovementBoneData: function () { + return this._ignoreMovementBoneData; + }, + + /** + * Returns the blendFunc of ccs.Bone. + * @return {cc.BlendFunc} + */ + getBlendFunc: function () { + return new cc.BlendFunc(this._blendFunc.src, this._blendFunc.dst); + }, + + /** + * Sets blend dirty flag + * @param {Boolean} dirty + */ + setBlendDirty: function (dirty) { + this._blendDirty = dirty; + }, + + /** + * Returns the blend dirty flag whether is dirty. + * @returns {Boolean|*|ccs.Bone._blendDirty} + */ + isBlendDirty: function () { + return this._blendDirty; + }, + + /** + * Returns the tweenData of ccs.Bone. + * @return {ccs.FrameData} + */ + getTweenData: function () { + return this._tweenData; + }, + + /** + * Returns the world information of ccs.Bone. + * @returns {ccs.BaseData} + */ + getWorldInfo: function () { + return this._worldInfo; + }, + + /** + * Returns the children of ccs.Bone + * @return {Array} + * @deprecated since v3.0, please use getChildren instead. + */ + getChildrenBone: function () { + return this._children; + }, + + /** + * Returns the worldTransform of ccs.Bone. + * @return {cc.AffineTransform} + * @deprecated since v3.0, please use getNodeToArmatureTransform instead. + */ + nodeToArmatureTransform: function () { + return this.getNodeToArmatureTransform(); + }, + + /** + * @deprecated + * Returns the world affine transform matrix. The matrix is in Pixels. + * @returns {cc.AffineTransform} + */ + nodeToWorldTransform: function () { + return this.getNodeToWorldTransform(); + }, + + /** + * Returns the collider body list in this bone. + * @returns {Array|null} + * @deprecated since v3.0, please use getColliderDetector to get a delector, and calls its getColliderBodyList instead. + */ + getColliderBodyList: function () { + var detector = this.getColliderDetector(); + if (detector) + return detector.getColliderBodyList(); + return null; + }, + + /** + * Returns whether is ignore movement bone data. + * @return {Boolean} + * @deprecated since v3.0, please isIgnoreMovementBoneData instead. + */ + getIgnoreMovementBoneData: function () { + return this.isIgnoreMovementBoneData(); + }, + + _createRenderCmd: function () { + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return new ccs.Bone.CanvasRenderCmd(this); + else + return new ccs.Bone.WebGLRenderCmd(this); + } +}); + +var _p = ccs.Bone.prototype; + +// Extended properties +/** @expose */ +_p.boneData; +cc.defineGetterSetter(_p, "boneData", _p.getBoneData, _p.setBoneData); +/** @expose */ +_p.armature; +cc.defineGetterSetter(_p, "armature", _p.getArmature, _p.setArmature); +/** @expose */ +_p.childArmature; +cc.defineGetterSetter(_p, "childArmature", _p.getChildArmature, _p.setChildArmature); +/** @expose */ +_p.childrenBone; +cc.defineGetterSetter(_p, "childrenBone", _p.getChildrenBone); +/** @expose */ +_p.tween; +cc.defineGetterSetter(_p, "tween", _p.getTween); +/** @expose */ +_p.tweenData; +cc.defineGetterSetter(_p, "tweenData", _p.getTweenData); +/** @expose */ +_p.colliderFilter; +cc.defineGetterSetter(_p, "colliderFilter", _p.getColliderFilter, _p.setColliderFilter); + +_p = null; + +/** + * Allocates and initializes a bone. + * @return {ccs.Bone} + * @deprecated since v3.1, please use new construction instead + */ +ccs.Bone.create = function (name) { + return new ccs.Bone(name); +}; + +ccs.Bone.RenderCmd = { + _updateColor: function () { + var node = this._node; + var display = node._displayManager.getDisplayRenderNode(); + if (display !== null) { + var displayCmd = display._renderCmd; + display.setColor(this._displayedColor); + display.setOpacity(this._displayedOpacity); + displayCmd._syncDisplayColor(node._tweenData); + displayCmd._syncDisplayOpacity(node._tweenData.a); + displayCmd._updateColor(); + } + }, + + transform: function (parentCmd, recursive) { + if (!this._transform) { + this._transform = {a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0}; + this._worldTransform = {a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0}; + } + + var node = this._node, + t = this._transform, + wt = this._worldTransform, + pt = parentCmd ? parentCmd._worldTransform : null; + + if (pt) { + this.originTransform(); + cc.affineTransformConcatIn(t, node._worldTransform); + } + + if (pt) { + wt.a = t.a * pt.a + t.b * pt.c; + wt.b = t.a * pt.b + t.b * pt.d; + wt.c = t.c * pt.a + t.d * pt.c; + wt.d = t.c * pt.b + t.d * pt.d; + wt.tx = t.tx * pt.a + t.ty * pt.c + pt.tx; + wt.ty = t.tx * pt.b + t.ty * pt.d + pt.ty; + } + else { + wt.a = t.a; + wt.b = t.b; + wt.c = t.c; + wt.d = t.d; + wt.tx = t.tx; + wt.ty = t.ty; + } + } +}; + +(function () { + ccs.Bone.CanvasRenderCmd = function (renderable) { + this._rootCtor(renderable); + this._needDraw = false; + }; + + var proto = ccs.Bone.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + cc.inject(ccs.Bone.RenderCmd, proto); + proto.constructor = ccs.Bone.CanvasRenderCmd; +})(); + +(function () { + if (!cc.Node.WebGLRenderCmd) + return; + ccs.Bone.WebGLRenderCmd = function (renderable) { + this._rootCtor(renderable); + this._needDraw = false; + }; + + var proto = ccs.Bone.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + cc.inject(ccs.Bone.RenderCmd, proto); + proto.constructor = ccs.Bone.WebGLRenderCmd; +})(); diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/armature/animation/CCArmatureAnimation.js b/frameworks/cocos2d-html5/extensions/cocostudio/armature/animation/CCArmatureAnimation.js new file mode 100644 index 0000000..09ab2fe --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/armature/animation/CCArmatureAnimation.js @@ -0,0 +1,672 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * movement event type enum + * @constant + * @type {Object} + */ +ccs.MovementEventType = { + start: 0, + complete: 1, + loopComplete: 2 +}; + +/** + * The animation event class, it has the callback, target and arguments. + * @deprecated since v3.0. + * @class + * @extends ccs.Class + */ +ccs.AnimationEvent = ccs.Class.extend(/** @lends ccs.AnimationEvent# */{ + _arguments: null, + _callFunc: null, + _selectorTarget: null, + + /** + * Constructor of ccs.AnimationEvent + * @param {function} callFunc + * @param {object} target + * @param {object} [data] + */ + ctor: function (callFunc,target, data) { + this._data = data; + this._callFunc = callFunc; + this._selectorTarget = target; + }, + call: function () { + if (this._callFunc) + this._callFunc.apply(this._selectorTarget, this._arguments); + }, + setArguments: function (args) { + this._arguments = args; + } +}); + +/** + * The movement event class for Armature. + * @constructor + * + * @property {ccs.Armature} armature - The armature reference of movement event. + * @property {Number} movementType - The type of movement. + * @property {String} movementID - The ID of movement. + */ +ccs.MovementEvent = function () { + this.armature = null; + this.movementType = ccs.MovementEventType.start; + this.movementID = ""; +}; + +/** + * The frame event class for Armature. + * @constructor + * + * @property {ccs.Bone} bone - The bone reference of frame event. + * @property {String} frameEventName - The name of frame event. + * @property {Number} originFrameIndex - The index of origin frame. + * @property {Number} currentFrameIndex - The index of current frame. + */ +ccs.FrameEvent = function () { + this.bone = null; + this.frameEventName = ""; + this.originFrameIndex = 0; + this.currentFrameIndex = 0; +}; + +/** + * The Animation class for Armature, it plays armature animation, and controls speed scale and manages animation frame. + * @class + * @extends ccs.ProcessBase + * + * @param {ccs.Armature} [armature] The armature + * + * @property {ccs.AnimationData} animationData - Animation data + * @property {Object} userObject - User custom object + * @property {Boolean} ignoreFrameEvent - Indicate whether the frame event is ignored + * @property {Number} speedScale - Animation play speed scale + * @property {Number} animationScale - Animation play speed scale + */ +ccs.ArmatureAnimation = ccs.ProcessBase.extend(/** @lends ccs.ArmatureAnimation# */{ + _animationData: null, + _movementData: null, + _armature: null, + _movementID: "", + _toIndex: 0, + _tweenList: null, + _speedScale: 1, + _ignoreFrameEvent: false, + _frameEventQueue: null, + _movementEventQueue: null, + _movementList: null, + _onMovementList: false, + _movementListLoop: false, + _movementIndex: 0, + _movementListDurationTo: -1, + + _movementEventCallFunc: null, + _frameEventCallFunc: null, + _movementEventTarget: null, + _frameEventTarget:null, + _movementEventListener: null, + _frameEventListener: null, + + ctor: function (armature) { + ccs.ProcessBase.prototype.ctor.call(this); + + this._tweenList = []; + this._movementList = []; + this._frameEventQueue = []; + this._movementEventQueue = []; + this._armature = null; + + armature && ccs.ArmatureAnimation.prototype.init.call(this, armature); + }, + + /** + * Initializes with an armature object + * @param {ccs.Armature} armature + * @return {Boolean} + */ + init: function (armature) { + this._armature = armature; + this._tweenList.length = 0; + return true; + }, + + /** + * Pauses armature animation. + */ + pause: function () { + var locTweenList = this._tweenList; + for (var i = 0; i < locTweenList.length; i++) + locTweenList[i].pause(); + ccs.ProcessBase.prototype.pause.call(this); + }, + + /** + * Resumes armature animation. + */ + resume: function () { + var locTweenList = this._tweenList; + for (var i = 0; i < locTweenList.length; i++) + locTweenList[i].resume(); + ccs.ProcessBase.prototype.resume.call(this); + }, + + /** + * Stops armature animation. + */ + stop: function () { + var locTweenList = this._tweenList; + for (var i = 0; i < locTweenList.length; i++) + locTweenList[i].stop(); + locTweenList.length = 0; + ccs.ProcessBase.prototype.stop.call(this); + }, + + /** + * Sets animation play speed scale. + * @deprecated since v3.0, please use setSpeedScale instead. + * @param {Number} animationScale + */ + setAnimationScale: function (animationScale) { + this.setSpeedScale(animationScale); + }, + + /** + * Returns animation play speed scale. + * @deprecated since v3.0, please use getSpeedScale instead. + * @returns {Number} + */ + getAnimationScale: function () { + return this.getSpeedScale(); + }, + + /** + * Sets animation play speed scale. + * @param {Number} speedScale + */ + setSpeedScale: function (speedScale) { + if (speedScale === this._speedScale) + return; + this._speedScale = speedScale; + this._processScale = !this._movementData ? this._speedScale : this._speedScale * this._movementData.scale; + var dict = this._armature.getBoneDic(); + for (var key in dict) { + var bone = dict[key]; + bone.getTween().setProcessScale(this._processScale); + if (bone.getChildArmature()) + bone.getChildArmature().getAnimation().setSpeedScale(this._processScale); + } + }, + + /** + * Returns animation play speed scale. + * @returns {Number} + */ + getSpeedScale: function () { + return this._speedScale; + }, + + /** + * play animation by animation name. + * @param {String} animationName The animation name you want to play + * @param {Number} [durationTo=-1] + * the frames between two animation changing-over.It's meaning is changing to this animation need how many frames + * -1 : use the value from CCMovementData get from flash design panel + * @param {Number} [loop=-1] + * Whether the animation is loop. + * loop < 0 : use the value from CCMovementData get from flash design panel + * loop = 0 : this animation is not loop + * loop > 0 : this animation is loop + * @example + * // example + * armature.getAnimation().play("run",-1,1);//loop play + * armature.getAnimation().play("run",-1,0);//not loop play + */ + play: function (animationName, durationTo, loop) { + cc.assert(this._animationData, "this.animationData can not be null"); + + this._movementData = this._animationData.getMovement(animationName); + cc.assert(this._movementData, "this._movementData can not be null"); + + durationTo = (durationTo === undefined) ? -1 : durationTo; + loop = (loop === undefined) ? -1 : loop; + + //! Get key frame count + this._rawDuration = this._movementData.duration; + this._movementID = animationName; + this._processScale = this._speedScale * this._movementData.scale; + + //! Further processing parameters + durationTo = (durationTo === -1) ? this._movementData.durationTo : durationTo; + var durationTween = this._movementData.durationTween === 0 ? this._rawDuration : this._movementData.durationTween; + + var tweenEasing = this._movementData.tweenEasing; + //loop = (!loop || loop < 0) ? this._movementData.loop : loop; + loop = (loop < 0) ? this._movementData.loop : loop; + this._onMovementList = false; + + ccs.ProcessBase.prototype.play.call(this, durationTo, durationTween, loop, tweenEasing); + + if (this._rawDuration === 0) + this._loopType = ccs.ANIMATION_TYPE_SINGLE_FRAME; + else { + this._loopType = loop ? ccs.ANIMATION_TYPE_TO_LOOP_FRONT : ccs.ANIMATION_TYPE_NO_LOOP; + this._durationTween = durationTween; + } + + this._tweenList.length = 0; + + var movementBoneData, map = this._armature.getBoneDic(); + for(var element in map) { + var bone = map[element]; + movementBoneData = this._movementData.movBoneDataDic[bone.getName()]; + + var tween = bone.getTween(); + if(movementBoneData && movementBoneData.frameList.length > 0) { + this._tweenList.push(tween); + movementBoneData.duration = this._movementData.duration; + tween.play(movementBoneData, durationTo, durationTween, loop, tweenEasing); + tween.setProcessScale(this._processScale); + + if (bone.getChildArmature()) { + bone.getChildArmature().getAnimation().setSpeedScale(this._processScale); + if (!bone.getChildArmature().getAnimation().isPlaying()) + bone.getChildArmature().getAnimation().playWithIndex(0); + } + } else { + if(!bone.isIgnoreMovementBoneData()){ + //! this bone is not include in this movement, so hide it + bone.getDisplayManager().changeDisplayWithIndex(-1, false); + tween.stop(); + } + } + } + this._armature.update(0); + }, + + /** + * Plays animation with index, the other param is the same to play. + * @param {Number} animationIndex + * @param {Number} durationTo + * @param {Number} durationTween + * @param {Number} loop + * @param {Number} [tweenEasing] + * @deprecated since v3.0, please use playWithIndex instead. + */ + playByIndex: function (animationIndex, durationTo, durationTween, loop, tweenEasing) { + cc.log("playByIndex is deprecated. Use playWithIndex instead."); + this.playWithIndex(animationIndex, durationTo, loop); + }, + + /** + * Plays animation with index, the other param is the same to play. + * @param {Number|Array} animationIndex + * @param {Number} durationTo + * @param {Number} loop + */ + playWithIndex: function (animationIndex, durationTo, loop) { + var movName = this._animationData.movementNames; + cc.assert((animationIndex > -1) && (animationIndex < movName.length)); + + var animationName = movName[animationIndex]; + this.play(animationName, durationTo, loop); + }, + + /** + * Plays animation with names + * @param {Array} movementNames + * @param {Number} durationTo + * @param {Boolean} loop + */ + playWithNames: function (movementNames, durationTo, loop) { + durationTo = (durationTo === undefined) ? -1 : durationTo; + loop = (loop === undefined) ? true : loop; + + this._movementListLoop = loop; + this._movementListDurationTo = durationTo; + this._onMovementList = true; + this._movementIndex = 0; + if(movementNames instanceof Array) + this._movementList = movementNames; + else + this._movementList.length = 0; + this.updateMovementList(); + }, + + /** + * Plays animation by indexes + * @param {Array} movementIndexes + * @param {Number} durationTo + * @param {Boolean} loop + */ + playWithIndexes: function (movementIndexes, durationTo, loop) { + durationTo = (durationTo === undefined) ? -1 : durationTo; + loop = (loop === undefined) ? true : loop; + + this._movementList.length = 0; + this._movementListLoop = loop; + this._movementListDurationTo = durationTo; + this._onMovementList = true; + this._movementIndex = 0; + + var movName = this._animationData.movementNames; + + for (var i = 0; i < movementIndexes.length; i++) { + var name = movName[movementIndexes[i]]; + this._movementList.push(name); + } + + this.updateMovementList(); + }, + + /** + *

+ * Goes to specified frame and plays current movement.
+ * You need first switch to the movement you want to play, then call this function.
+ *
+ * example : playByIndex(0);
+ * gotoAndPlay(0);
+ * playByIndex(1);
+ * gotoAndPlay(0);
+ * gotoAndPlay(15);
+ *

+ * @param {Number} frameIndex + */ + gotoAndPlay: function (frameIndex) { + if (!this._movementData || frameIndex < 0 || frameIndex >= this._movementData.duration) { + cc.log("Please ensure you have played a movement, and the frameIndex is in the range."); + return; + } + + var ignoreFrameEvent = this._ignoreFrameEvent; + this._ignoreFrameEvent = true; + this._isPlaying = true; + this._isComplete = this._isPause = false; + + ccs.ProcessBase.prototype.gotoFrame.call(this, frameIndex); + this._currentPercent = this._curFrameIndex / (this._movementData.duration - 1); + this._currentFrame = this._nextFrameIndex * this._currentPercent; + + var locTweenList = this._tweenList; + for (var i = 0; i < locTweenList.length; i++) + locTweenList[i].gotoAndPlay(frameIndex); + this._armature.update(0); + this._ignoreFrameEvent = ignoreFrameEvent; + }, + + /** + * Goes to specified frame and pauses current movement. + * @param {Number} frameIndex + */ + gotoAndPause: function (frameIndex) { + this.gotoAndPlay(frameIndex); + this.pause(); + }, + + /** + * Returns the length of armature's movements + * @return {Number} + */ + getMovementCount: function () { + return this._animationData.getMovementCount(); + }, + + /** + * Updates the state of ccs.Tween list, calls frame event's callback and calls movement event's callback. + * @param {Number} dt + */ + update: function (dt) { + ccs.ProcessBase.prototype.update.call(this, dt); + + var locTweenList = this._tweenList; + for (var i = 0; i < locTweenList.length; i++) + locTweenList[i].update(dt); + + var frameEvents = this._frameEventQueue, event; + while (frameEvents.length > 0) { + event = frameEvents.shift(); + this._ignoreFrameEvent = true; + if(this._frameEventCallFunc) + this._frameEventCallFunc.call(this._frameEventTarget, event.bone, event.frameEventName, event.originFrameIndex, event.currentFrameIndex); + if(this._frameEventListener) + this._frameEventListener(event.bone, event.frameEventName, event.originFrameIndex, event.currentFrameIndex); + this._ignoreFrameEvent = false; + } + + var movementEvents = this._movementEventQueue; + while (movementEvents.length > 0) { + event = movementEvents.shift(); + if(this._movementEventCallFunc) + this._movementEventCallFunc.call(this._movementEventTarget, event.armature, event.movementType, event.movementID); + if (this._movementEventListener) + this._movementEventListener(event.armature, event.movementType, event.movementID); + } + }, + + /** + * Updates will call this handler, you can handle your logic here + */ + updateHandler: function () { //TODO set it to protected in v3.1 + var locCurrentPercent = this._currentPercent; + if (locCurrentPercent >= 1) { + switch (this._loopType) { + case ccs.ANIMATION_TYPE_NO_LOOP: + this._loopType = ccs.ANIMATION_TYPE_MAX; + this._currentFrame = (locCurrentPercent - 1) * this._nextFrameIndex; + locCurrentPercent = this._currentFrame / this._durationTween; + if (locCurrentPercent < 1.0) { + this._nextFrameIndex = this._durationTween; + this.movementEvent(this._armature, ccs.MovementEventType.start, this._movementID); + break; + } + break; + case ccs.ANIMATION_TYPE_MAX: + case ccs.ANIMATION_TYPE_SINGLE_FRAME: + locCurrentPercent = 1; + this._isComplete = true; + this._isPlaying = false; + + this.movementEvent(this._armature, ccs.MovementEventType.complete, this._movementID); + + this.updateMovementList(); + break; + case ccs.ANIMATION_TYPE_TO_LOOP_FRONT: + this._loopType = ccs.ANIMATION_TYPE_LOOP_FRONT; + locCurrentPercent = ccs.fmodf(locCurrentPercent, 1); + this._currentFrame = this._nextFrameIndex === 0 ? 0 : ccs.fmodf(this._currentFrame, this._nextFrameIndex); + this._nextFrameIndex = this._durationTween > 0 ? this._durationTween : 1; + this.movementEvent(this, ccs.MovementEventType.start, this._movementID); + break; + default: + //locCurrentPercent = ccs.fmodf(locCurrentPercent, 1); + this._currentFrame = ccs.fmodf(this._currentFrame, this._nextFrameIndex); + this._toIndex = 0; + this.movementEvent(this._armature, ccs.MovementEventType.loopComplete, this._movementID); + break; + } + this._currentPercent = locCurrentPercent; + } + }, + + /** + * Returns the Id of current movement + * @returns {String} + */ + getCurrentMovementID: function () { + if (this._isComplete) + return ""; + return this._movementID; + }, + + /** + * Sets movement event callback to animation. + * @param {function} callFunc + * @param {Object} target + */ + setMovementEventCallFunc: function (callFunc, target) { + if(arguments.length === 1){ + this._movementEventListener = callFunc; + }else if(arguments.length === 2){ + this._movementEventTarget = target; + this._movementEventCallFunc = callFunc; + } + }, + + /** + * Sets frame event callback to animation. + * @param {function} callFunc + * @param {Object} target + */ + setFrameEventCallFunc: function (callFunc, target) { + if(arguments.length === 1){ + this._frameEventListener = callFunc; + }else if(arguments.length === 2){ + this._frameEventTarget = target; + this._frameEventCallFunc = callFunc; + } + }, + + /** + * Sets user object to animation. + * @param {Object} userObject + */ + setUserObject: function (userObject) { + this._userObject = userObject; + }, + + /** + * Emits a frame event + * @param {ccs.Bone} bone + * @param {String} frameEventName + * @param {Number} originFrameIndex + * @param {Number} currentFrameIndex + */ + frameEvent: function (bone, frameEventName, originFrameIndex, currentFrameIndex) { + if ((this._frameEventTarget && this._frameEventCallFunc) || this._frameEventListener) { + var frameEvent = new ccs.FrameEvent(); + frameEvent.bone = bone; + frameEvent.frameEventName = frameEventName; + frameEvent.originFrameIndex = originFrameIndex; + frameEvent.currentFrameIndex = currentFrameIndex; + this._frameEventQueue.push(frameEvent); + } + }, + + /** + * Emits a movement event + * @param {ccs.Armature} armature + * @param {Number} movementType + * @param {String} movementID + */ + movementEvent: function (armature, movementType, movementID) { + if ((this._movementEventTarget && this._movementEventCallFunc) || this._movementEventListener) { + var event = new ccs.MovementEvent(); + event.armature = armature; + event.movementType = movementType; + event.movementID = movementID; + this._movementEventQueue.push(event); + } + }, + + /** + * Updates movement list. + */ + updateMovementList: function () { + if (this._onMovementList) { + var movementObj, locMovementList = this._movementList; + if (this._movementListLoop) { + movementObj = locMovementList[this._movementIndex]; + this.play(movementObj, movementObj.durationTo, 0); + this._movementIndex++; + if (this._movementIndex >= locMovementList.length) + this._movementIndex = 0; + } else { + if (this._movementIndex < locMovementList.length) { + movementObj = locMovementList[this._movementIndex]; + this.play(movementObj, movementObj.durationTo, 0); + this._movementIndex++; + } else + this._onMovementList = false; + } + this._onMovementList = true; + } + }, + + /** + * Sets animation data to animation. + * @param {ccs.AnimationData} data + */ + setAnimationData: function (data) { + if(this._animationData !== data) + this._animationData = data; + }, + + /** + * Returns animation data of animation. + * @return {ccs.AnimationData} + */ + getAnimationData: function () { + return this._animationData; + }, + + /** + * Returns the user object of animation. + * @return {Object} + */ + getUserObject: function () { + return this._userObject; + }, + + /** + * Determines if the frame event is ignored + * @returns {boolean} + */ + isIgnoreFrameEvent: function () { + return this._ignoreFrameEvent; + } +}); + +var _p = ccs.ArmatureAnimation.prototype; + +// Extended properties +/** @expose */ +_p.speedScale; +cc.defineGetterSetter(_p, "speedScale", _p.getSpeedScale, _p.setSpeedScale); +/** @expose */ +_p.animationScale; +cc.defineGetterSetter(_p, "animationScale", _p.getAnimationScale, _p.setAnimationScale); + +_p = null; + +/** + * Allocates and initializes a ArmatureAnimation. + * @return {ccs.ArmatureAnimation} + * @deprecated since v3.1, please use new construction instead + */ +ccs.ArmatureAnimation.create = function (armature) { + return new ccs.ArmatureAnimation(armature); +}; diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/armature/animation/CCProcessBase.js b/frameworks/cocos2d-html5/extensions/cocostudio/armature/animation/CCProcessBase.js new file mode 100644 index 0000000..a4707d3 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/armature/animation/CCProcessBase.js @@ -0,0 +1,365 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +//animation type +/** + * The animation just have one frame + * @constant + * @type {number} + */ +ccs.ANIMATION_TYPE_SINGLE_FRAME = -4; +/** + * The animation isn't loop + * @constant + * @type {number} + */ +ccs.ANIMATION_TYPE_NO_LOOP = -3; +/** + * The animation to loop from front + * @constant + * @type {number} + */ +ccs.ANIMATION_TYPE_TO_LOOP_FRONT = -2; +/** + * The animation to loop from back + * @constant + * @type {number} + */ +ccs.ANIMATION_TYPE_TO_LOOP_BACK = -1; +/** + * The animation loop from front + * @constant + * @type {number} + */ +ccs.ANIMATION_TYPE_LOOP_FRONT = 0; +/** + * The animation loop from back + * @constant + * @type {number} + */ +ccs.ANIMATION_TYPE_LOOP_BACK = 1; +/** + * The animation max + * @constant + * @type {number} + */ +ccs.ANIMATION_TYPE_MAX = 2; + +/** + * The Base Process class for Cocostudio. + * @class + * @extends ccs.Class + * + * @property {Number} currentFrameIndex - <@readonly> The current frame's index + * @property {Boolean} paused - <@readonly> Indicate whether the process is paused + * @property {Boolean} completed - <@readonly> Indicate whether the process is done + * @property {Number} currentPercent - <@readonly> The current percentage of the process + * @property {Number} rawDuration - <@readonly> The duration + * @property {Number} loop - <@readonly> The number of loop + * @property {Number} tweenEasing - <@readonly> The tween easing + * @property {Number} animationInterval - The animation internal + * @property {Number} processScale - The process scale + * @property {Boolean} playing - <@readonly> Indicate whether the process is playing + */ +ccs.ProcessBase = ccs.Class.extend(/** @lends ccs.ProcessBase# */{ + _processScale: 1, + _isComplete: true, + _isPause: true, + _isPlaying: false, + _currentPercent: 0.0, + _rawDuration: 0, + _loopType: 0, + _tweenEasing: 0, + animationInternal: null, + _currentFrame: 0, + _durationTween: 0, + _nextFrameIndex: 0, + _curFrameIndex: null, + _isLoopBack: false, + + /** + * Constructor of ccs.ProcessBase + */ + ctor: function () { + this._processScale = 1; + this._isComplete = true; + this._isPause = true; + this._isPlaying = false; + this._currentFrame = 0; + this._currentPercent = 0.0; + this._durationTween = 0; + this._rawDuration = 0; + this._loopType = ccs.ANIMATION_TYPE_LOOP_BACK; + this._tweenEasing = ccs.TweenType.LINEAR; + this.animationInternal = 1 / 60; + this._curFrameIndex = 0; + this._durationTween = 0; + this._isLoopBack = false; + }, + + /** + * Pauses the Process + */ + pause: function () { + this._isPause = true; + this._isPlaying = false; + }, + + /** + * Resumes the Process + */ + resume: function () { + this._isPause = false; + this._isPlaying = true; + }, + + /** + * Stops the Process + */ + stop: function () { + this._isComplete = true; + this._isPlaying = false; + }, + + /** + * Plays animation by animation name. + * @param {Number} durationTo The frames between two animation changing-over. + * It's meaning is changing to this animation need how many frames + * -1 : use the value from MovementData get from flash design panel + * @param {Number} durationTween The frame count you want to play in the game. + * if _durationTween is 80, then the animation will played 80 frames in a loop + * -1 : use the value from MovementData get from flash design panel + * @param {Number} loop Whether the animation is loop + * loop < 0 : use the value from MovementData get from flash design panel + * loop = 0 : this animation is not loop + * loop > 0 : this animation is loop + * @param {Number} tweenEasing Tween easing is used for calculate easing effect + * TWEEN_EASING_MAX : use the value from MovementData get from flash design panel + * -1 : fade out + * 0 : line + * 1 : fade in + * 2 : fade in and out + */ + play: function (durationTo, durationTween, loop, tweenEasing) { + this._isComplete = false; + this._isPause = false; + this._isPlaying = true; + this._currentFrame = 0; + /* + * Set m_iTotalFrames to durationTo, it is used for change tween between two animation. + * When changing end, m_iTotalFrames will be set to _durationTween + */ + this._nextFrameIndex = durationTo; + this._tweenEasing = tweenEasing; + }, + + /** + * Update process' state. + * @param {Number} dt + */ + update: function (dt) { + if (this._isComplete || this._isPause) + return; + + /* + * Fileter the m_iDuration <=0 and dt >1 + * If dt>1, generally speaking the reason is the device is stuck. + */ + if (this._rawDuration <= 0 || dt > 1) + return; + + var locNextFrameIndex = this._nextFrameIndex === undefined ? 0 : this._nextFrameIndex; + var locCurrentFrame = this._currentFrame; + if (locNextFrameIndex <= 0) { + this._currentPercent = 1; + locCurrentFrame = 0; + } else { + /* + * update currentFrame, every update add the frame passed. + * dt/this.animationInternal determine it is not a frame animation. If frame speed changed, it will not make our + * animation speed slower or quicker. + */ + locCurrentFrame += this._processScale * (dt / this.animationInternal); + this._currentPercent = locCurrentFrame / locNextFrameIndex; + + /* + * if currentFrame is bigger or equal than this._nextFrameIndex, then reduce it util currentFrame is + * smaller than this._nextFrameIndex + */ + locCurrentFrame = ccs.fmodf(locCurrentFrame, locNextFrameIndex); + } + this._currentFrame = locCurrentFrame; + this.updateHandler(); + }, + + /** + * Goes to specified frame by frameIndex. + * @param {Number} frameIndex + */ + gotoFrame: function (frameIndex) { + var locLoopType = this._loopType; + if (locLoopType === ccs.ANIMATION_TYPE_NO_LOOP) + locLoopType = ccs.ANIMATION_TYPE_MAX; + else if (locLoopType === ccs.ANIMATION_TYPE_TO_LOOP_FRONT) + locLoopType = ccs.ANIMATION_TYPE_LOOP_FRONT; + this._loopType = locLoopType; + this._curFrameIndex = frameIndex; + this._nextFrameIndex = this._durationTween; + }, + + /** + * Returns the index of current frame. + * @return {Number} + */ + getCurrentFrameIndex: function () { + this._curFrameIndex = (this._rawDuration - 1) * this._currentPercent; + return this._curFrameIndex; + }, + + /** + * Updates will call this handler, you can handle your logic here + */ + updateHandler: function () { + //override + }, + + /** + * Returns whether the animation is pause + * @returns {boolean} + */ + isPause: function () { + return this._isPause; + }, + + /** + * Returns whether the animation is complete + * @returns {boolean} + */ + isComplete: function () { + return this._isComplete; + }, + + /** + * Returns current percent of ccs.ProcessBase + * @returns {number} + */ + getCurrentPercent: function () { + return this._currentPercent; + }, + + /** + * Returns the raw duration of ccs.ProcessBase + * @returns {number} + */ + getRawDuration: function () { + return this._rawDuration; + }, + + /** + * Returns loop type of ccs.ProcessBase + * @returns {number} + */ + getLoop: function () { + return this._loopType; + }, + + /** + * Returns tween easing of ccs.ProcessBase + * @returns {number} + */ + getTweenEasing: function () { + return this._tweenEasing; + }, + + /** + * Returns animation interval of ccs.ProcessBase + * @returns {number} + */ + getAnimationInternal: function () { //TODO rename getAnimationInternal to getAnimationInterval in v3.1 + return this.animationInternal; + }, + + /** + * Sets animation interval to ccs.ProcessBase. + * @param animationInternal + */ + setAnimationInternal: function (animationInternal) { + this.animationInternal = animationInternal; + }, + + /** + * Returns process scale + * @returns {number} + */ + getProcessScale: function () { + return this._processScale; + }, + + /** + * Sets process scale + * @param processScale + */ + setProcessScale: function (processScale) { + this._processScale = processScale; + }, + + /** + * Returns whether the animation is playing + * @returns {boolean} + */ + isPlaying: function () { + return this._isPlaying; + } +}); + +var _p = ccs.ProcessBase.prototype; + +// Extended properties +/** @expose */ +_p.currentFrameIndex; +cc.defineGetterSetter(_p, "currentFrameIndex", _p.getCurrentFrameIndex); +/** @expose */ +_p.paused; +cc.defineGetterSetter(_p, "paused", _p.isPause); +/** @expose */ +_p.completed; +cc.defineGetterSetter(_p, "completed", _p.isComplete); +/** @expose */ +_p.currentPercent; +cc.defineGetterSetter(_p, "currentPercent", _p.getCurrentPercent); +/** @expose */ +_p.rawDuration; +cc.defineGetterSetter(_p, "rawDuration", _p.getRawDuration); +/** @expose */ +_p.loop; +cc.defineGetterSetter(_p, "loop", _p.getLoop); +/** @expose */ +_p.tweenEasing; +cc.defineGetterSetter(_p, "tweenEasing", _p.getTweenEasing); +/** @expose */ +_p.playing; +cc.defineGetterSetter(_p, "playing", _p.isPlaying); + +_p = null; diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/armature/animation/CCTween.js b/frameworks/cocos2d-html5/extensions/cocostudio/armature/animation/CCTween.js new file mode 100644 index 0000000..5706d1d --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/armature/animation/CCTween.js @@ -0,0 +1,448 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The tween class for Armature. + * @class + * @extends ccs.ProcessBase + * + * @param {ccs.Bone} The bone to be animated + * + * @property {ccs.ArmatureAnimation} animation - The animation + */ +ccs.Tween = ccs.ProcessBase.extend(/** @lends ccs.Tween# */{ + _tweenData:null, + _to:null, + _from:null, + _between:null, + _movementBoneData:null, + _bone:null, + _frameTweenEasing:0, + _betweenDuration:0, + _totalDuration:0, + _toIndex:0, + _fromIndex:0, + _animation:null, + _passLastFrame:false, + + ctor:function (bone) { + ccs.ProcessBase.prototype.ctor.call(this); + this._frameTweenEasing = ccs.TweenType.LINEAR; + + ccs.Tween.prototype.init.call(this, bone); + }, + + /** + * initializes a ccs.Tween with a CCBone + * @param {ccs.Bone} bone + * @return {Boolean} + */ + init:function (bone) { + this._from = new ccs.FrameData(); + this._between = new ccs.FrameData(); + + this._bone = bone; + this._tweenData = this._bone.getTweenData(); + this._tweenData.displayIndex = -1; + + this._animation = (this._bone !== null && this._bone.getArmature() !== null) ? + this._bone.getArmature().getAnimation() : + null; + return true; + }, + + /** + * Plays the tween. + * @param {ccs.MovementBoneData} movementBoneData + * @param {Number} durationTo + * @param {Number} durationTween + * @param {Boolean} loop + * @param {ccs.TweenType} tweenEasing + */ + play:function (movementBoneData, durationTo, durationTween, loop, tweenEasing) { + ccs.ProcessBase.prototype.play.call(this, durationTo, durationTween, loop, tweenEasing); + this._loopType = (loop)?ccs.ANIMATION_TYPE_TO_LOOP_FRONT:ccs.ANIMATION_TYPE_NO_LOOP; + + this._totalDuration = 0; + this._betweenDuration = 0; + this._fromIndex = this._toIndex = 0; + + var difMovement = movementBoneData !== this._movementBoneData; + + this.setMovementBoneData(movementBoneData); + this._rawDuration = this._movementBoneData.duration; + + var nextKeyFrame = this._movementBoneData.getFrameData(0); + this._tweenData.displayIndex = nextKeyFrame.displayIndex; + + if (this._bone.getArmature().getArmatureData().dataVersion >= ccs.CONST_VERSION_COMBINED) { + ccs.TransformHelp.nodeSub(this._tweenData, this._bone.getBoneData()); + this._tweenData.scaleX += 1; + this._tweenData.scaleY += 1; + } + + if (this._rawDuration === 0) { + this._loopType = ccs.ANIMATION_TYPE_SINGLE_FRAME; + if (durationTo === 0) + this.setBetween(nextKeyFrame, nextKeyFrame); + else + this.setBetween(this._tweenData, nextKeyFrame); + this._frameTweenEasing = ccs.TweenType.LINEAR; + } + else if (this._movementBoneData.frameList.length > 1) { + this._durationTween = durationTween * this._movementBoneData.scale; + if (loop && this._movementBoneData.delay !== 0) + this.setBetween(this._tweenData, this.tweenNodeTo(this.updateFrameData(1 - this._movementBoneData.delay), this._between)); + else { + if (!difMovement || durationTo === 0) + this.setBetween(nextKeyFrame, nextKeyFrame); + else + this.setBetween(this._tweenData, nextKeyFrame); + } + } + this.tweenNodeTo(0); + }, + + /** + * Goes to specified frame and plays frame. + * @param {Number} frameIndex + */ + gotoAndPlay: function (frameIndex) { + ccs.ProcessBase.prototype.gotoFrame.call(this, frameIndex); + + this._totalDuration = 0; + this._betweenDuration = 0; + this._fromIndex = this._toIndex = 0; + + this._isPlaying = true; + this._isComplete = this._isPause = false; + + this._currentPercent = this._curFrameIndex / (this._rawDuration-1); + this._currentFrame = this._nextFrameIndex * this._currentPercent; + }, + + /** + * Goes to specified frame and pauses frame. + * @param {Number} frameIndex + */ + gotoAndPause: function (frameIndex) { + this.gotoAndPlay(frameIndex); + this.pause(); + }, + + /** + * update will call this handler, you can handle your logic here + */ + updateHandler:function () { + var locCurrentPercent = this._currentPercent == null ? 1 : this._currentPercent; + var locLoopType = this._loopType; + if (locCurrentPercent >= 1) { + switch (locLoopType) { + case ccs.ANIMATION_TYPE_SINGLE_FRAME: + locCurrentPercent = 1; + this._isComplete = true; + this._isPlaying = false; + break; + case ccs.ANIMATION_TYPE_NO_LOOP: + locLoopType = ccs.ANIMATION_TYPE_MAX; + if (this._durationTween <= 0) + locCurrentPercent = 1; + else + locCurrentPercent = (locCurrentPercent - 1) * this._nextFrameIndex / this._durationTween; + if (locCurrentPercent >= 1) { + locCurrentPercent = 1; + this._isComplete = true; + this._isPlaying = false; + break; + } else { + this._nextFrameIndex = this._durationTween; + this._currentFrame = locCurrentPercent * this._nextFrameIndex; + this._totalDuration = 0; + this._betweenDuration = 0; + this._fromIndex = this._toIndex = 0; + break; + } + case ccs.ANIMATION_TYPE_TO_LOOP_FRONT: + locLoopType = ccs.ANIMATION_TYPE_LOOP_FRONT; + this._nextFrameIndex = this._durationTween > 0 ? this._durationTween : 1; + + if (this._movementBoneData.delay !== 0) { + this._currentFrame = (1 - this._movementBoneData.delay) * this._nextFrameIndex; + locCurrentPercent = this._currentFrame / this._nextFrameIndex; + } else { + locCurrentPercent = 0; + this._currentFrame = 0; + } + + this._totalDuration = 0; + this._betweenDuration = 0; + this._fromIndex = this._toIndex = 0; + break; + case ccs.ANIMATION_TYPE_MAX: + locCurrentPercent = 1; + this._isComplete = true; + this._isPlaying = false; + break; + default: + this._currentFrame = ccs.fmodf(this._currentFrame, this._nextFrameIndex); + break; + } + } + + if (locCurrentPercent < 1 && locLoopType < ccs.ANIMATION_TYPE_TO_LOOP_BACK) + locCurrentPercent = Math.sin(locCurrentPercent * cc.PI / 2); + + this._currentPercent = locCurrentPercent; + this._loopType = locLoopType; + + if (locLoopType > ccs.ANIMATION_TYPE_TO_LOOP_BACK) + locCurrentPercent = this.updateFrameData(locCurrentPercent); + if (this._frameTweenEasing !== ccs.TweenType.TWEEN_EASING_MAX) + this.tweenNodeTo(locCurrentPercent); + }, + + /** + * Calculate the between value of _from and _to, and give it to between frame data + * @param {ccs.FrameData} from + * @param {ccs.FrameData} to + * @param {Boolean} [limit=true] + */ + setBetween:function (from, to, limit) { //TODO set tweenColorTo to protected in v3.1 + if(limit === undefined) + limit = true; + do { + if (from.displayIndex < 0 && to.displayIndex >= 0) { + this._from.copy(to); + this._between.subtract(to, to, limit); + break; + } + if (to.displayIndex < 0 && from.displayIndex >= 0) { + this._from.copy(from); + this._between.subtract(to, to, limit); + break; + } + this._from.copy(from); + this._between.subtract(from, to, limit); + } while (0); + if (!from.isTween){ + this._tweenData.copy(from); + this._tweenData.isTween = true; + } + this.arriveKeyFrame(from); + }, + + /** + * Update display index and process the key frame event when arrived a key frame + * @param {ccs.FrameData} keyFrameData + */ + arriveKeyFrame:function (keyFrameData) { //TODO set tweenColorTo to protected in v3.1 + if (keyFrameData) { + var locBone = this._bone; + var displayManager = locBone.getDisplayManager(); + + //! Change bone's display + var displayIndex = keyFrameData.displayIndex; + + if (!displayManager.getForceChangeDisplay()) + displayManager.changeDisplayWithIndex(displayIndex, false); + + //! Update bone zorder, bone's zorder is determined by frame zorder and bone zorder + this._tweenData.zOrder = keyFrameData.zOrder; + locBone.updateZOrder(); + + //! Update blend type + this._bone.setBlendFunc(keyFrameData.blendFunc); + + var childAramture = locBone.getChildArmature(); + if (childAramture) { + if (keyFrameData.movement !== "") + childAramture.getAnimation().play(keyFrameData.movement); + } + } + }, + + /** + * According to the percent to calculate current CCFrameData with tween effect + * @param {Number} percent + * @param {ccs.FrameData} [node] + * @return {ccs.FrameData} + */ + tweenNodeTo:function (percent, node) { //TODO set tweenColorTo to protected in v3.1 + if (!node) + node = this._tweenData; + + var locFrom = this._from; + var locBetween = this._between; + if (!locFrom.isTween) + percent = 0; + node.x = locFrom.x + percent * locBetween.x; + node.y = locFrom.y + percent * locBetween.y; + node.scaleX = locFrom.scaleX + percent * locBetween.scaleX; + node.scaleY = locFrom.scaleY + percent * locBetween.scaleY; + node.skewX = locFrom.skewX + percent * locBetween.skewX; + node.skewY = locFrom.skewY + percent * locBetween.skewY; + + this._bone.setTransformDirty(true); + if (node && locBetween.isUseColorInfo) + this.tweenColorTo(percent, node); + + return node; + }, + + /** + * According to the percent to calculate current color with tween effect + * @param {Number} percent + * @param {ccs.FrameData} node + */ + tweenColorTo:function(percent,node){ //TODO set tweenColorTo to protected in v3.1 + var locFrom = this._from; + var locBetween = this._between; + node.a = locFrom.a + percent * locBetween.a; + node.r = locFrom.r + percent * locBetween.r; + node.g = locFrom.g + percent * locBetween.g; + node.b = locFrom.b + percent * locBetween.b; + this._bone.updateColor(); + }, + + /** + * Calculate which frame arrived, and if current frame have event, then call the event listener + * @param {Number} currentPercent + * @return {Number} + */ + updateFrameData:function (currentPercent) { //TODO set tweenColorTo to protected in v3.1 + if (currentPercent > 1 && this._movementBoneData.delay !== 0) + currentPercent = ccs.fmodf(currentPercent,1); + + var playedTime = (this._rawDuration-1) * currentPercent; + + var from, to; + var locTotalDuration = this._totalDuration,locBetweenDuration = this._betweenDuration, locToIndex = this._toIndex; + // if play to current frame's front or back, then find current frame again + if (playedTime < locTotalDuration || playedTime >= locTotalDuration + locBetweenDuration) { + /* + * get frame length, if this._toIndex >= _length, then set this._toIndex to 0, start anew. + * this._toIndex is next index will play + */ + var frames = this._movementBoneData.frameList; + var length = frames.length; + + if (playedTime < frames[0].frameID){ + from = to = frames[0]; + this.setBetween(from, to); + return this._currentPercent; + } + + if (playedTime >= frames[length - 1].frameID) { + // If _passLastFrame is true and playedTime >= frames[length - 1]->frameID, then do not need to go on. + if (this._passLastFrame) { + from = to = frames[length - 1]; + this.setBetween(from, to); + return this._currentPercent; + } + this._passLastFrame = true; + } else + this._passLastFrame = false; + + do { + this._fromIndex = locToIndex; + from = frames[this._fromIndex]; + locTotalDuration = from.frameID; + + locToIndex = this._fromIndex + 1; + if (locToIndex >= length) + locToIndex = 0; + to = frames[locToIndex]; + + //! Guaranteed to trigger frame event + if(from.strEvent && !this._animation.isIgnoreFrameEvent()) + this._animation.frameEvent(this._bone, from.strEvent,from.frameID, playedTime); + + if (playedTime === from.frameID|| (this._passLastFrame && this._fromIndex === length-1)) + break; + } while (playedTime < from.frameID || playedTime >= to.frameID); + + locBetweenDuration = to.frameID - from.frameID; + this._frameTweenEasing = from.tweenEasing; + this.setBetween(from, to, false); + + this._totalDuration = locTotalDuration; + this._betweenDuration = locBetweenDuration; + this._toIndex = locToIndex; + } + currentPercent = locBetweenDuration === 0 ? 0 : (playedTime - this._totalDuration) / this._betweenDuration; + + /* + * if frame tween easing equal to TWEEN_EASING_MAX, then it will not do tween. + */ + var tweenType = (this._frameTweenEasing !== ccs.TweenType.LINEAR) ? this._frameTweenEasing : this._tweenEasing; + if (tweenType !== ccs.TweenType.TWEEN_EASING_MAX && tweenType !== ccs.TweenType.LINEAR && !this._passLastFrame) { + currentPercent = ccs.TweenFunction.tweenTo(currentPercent, tweenType, this._from.easingParams); + } + return currentPercent; + }, + + /** + * Sets Armature animation to ccs.Tween. + * @param {ccs.ArmatureAnimation} animation + */ + setAnimation:function (animation) { + this._animation = animation; + }, + + /** + * Returns Armature animation of ccs.Tween. + * @return {ccs.ArmatureAnimation} + */ + getAnimation:function () { + return this._animation; + }, + + /** + * Sets movement bone data to ccs.Tween. + * @param data + */ + setMovementBoneData: function(data){ + this._movementBoneData = data; + } +}); + +var _p = ccs.Tween.prototype; + +// Extended properties +/** @expose */ +_p.animation; +cc.defineGetterSetter(_p, "animation", _p.getAnimation, _p.setAnimation); + +_p = null; + +/** + * Allocates and initializes a ArmatureAnimation. + * @param {ccs.Bone} bone + * @return {ccs.Tween} + * @deprecated since v3.1, please use new construction instead + */ +ccs.Tween.create = function (bone) { + return new ccs.Tween(bone); +}; diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/armature/datas/CCDatas.js b/frameworks/cocos2d-html5/extensions/cocostudio/armature/datas/CCDatas.js new file mode 100644 index 0000000..6aa958b --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/armature/datas/CCDatas.js @@ -0,0 +1,807 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +//BlendType +/** + * The value of the blend type of normal + * @constant + * @type Number + */ +ccs.BLEND_TYPE_NORMAL = 0; + +/** + * The value of the blend type of layer + * @constant + * @type Number + */ +ccs.BLEND_TYPE_LAYER = 1; + +/** + * The value of the blend type of darken + * @constant + * @type Number + */ +ccs.BLEND_TYPE_DARKEN = 2; + +/** + * The value of the blend type of multiply + * @constant + * @type Number + */ +ccs.BLEND_TYPE_MULTIPLY = 3; + +/** + * The value of the blend type of lighten + * @constant + * @type Number + */ +ccs.BLEND_TYPE_LIGHTEN = 4; + +/** + * The value of the blend type of screen + * @constant + * @type Number + */ +ccs.BLEND_TYPE_SCREEN = 5; + +/** + * The value of the blend type of overlay + * @constant + * @type Number + */ +ccs.BLEND_TYPE_OVERLAY = 6; + +/** + * The value of the blend type of highlight + * @constant + * @type Number + */ +ccs.BLEND_TYPE_HIGHLIGHT = 7; + +/** + * The value of the blend type of add + * @constant + * @type Number + */ +ccs.BLEND_TYPE_ADD = 8; + +/** + * The value of the blend type of subtract + * @constant + * @type Number + */ +ccs.BLEND_TYPE_SUBTRACT = 9; + +/** + * The value of the blend type of difference + * @constant + * @type Number + */ +ccs.BLEND_TYPE_DIFFERENCE = 10; + +/** + * The value of the blend type of invert + * @constant + * @type Number + */ +ccs.BLEND_TYPE_INVERT = 11; + +/** + * The value of the blend type of alpha + * @constant + * @type Number + */ +ccs.BLEND_TYPE_ALPHA = 12; + +/** + * The value of the blend type of erase + * @constant + * @type Number + */ +ccs.BLEND_TYPE_ERASE = 13; + +//DisplayType +/** + * The Sprite flag of display render type. + * @constant + * @type Number + */ +ccs.DISPLAY_TYPE_SPRITE = 0; +/** + * The Armature flag of display render type. + * @constant + * @type Number + */ +ccs.DISPLAY_TYPE_ARMATURE = 1; +/** + * The Particle flag of display render type. + * @constant + * @type Number + */ +ccs.DISPLAY_TYPE_PARTICLE = 2; +ccs.DISPLAY_TYPE_MAX = 3; + +/** + *

+ * The base data class for Armature. it contains position, zOrder, skew, scale, color datas.
+ * x y skewX skewY scaleX scaleY used to calculate transform matrix
+ * skewX, skewY can have rotation effect
+ * To get more matrix information, you can have a look at this pape : http://www.senocular.com/flash/tutorials/transformmatrix/
+ *

+ * @class + * @extends ccs.Class + * + * @property {Number} x - x + * @property {Number} y - y + * @property {Number} zOrder - zOrder + * @property {Number} skewX - skewX + * @property {Number} skewY - skewY + * @property {Number} scaleX - scaleX + * @property {Number} scaleY - scaleY + * @property {Number} tweenRotate - tween Rotate + * @property {Number} isUseColorInfo - is Use Color Info + * @property {Number} r - r of color + * @property {Number} g - g of color + * @property {Number} b - b of color + * @property {Number} a - a of color + */ +ccs.BaseData = ccs.Class.extend(/** @lends ccs.BaseData# */{ + x:0, + y:0, + zOrder:0, + skewX:0, + skewY:0, + scaleX:1, + scaleY:1, + tweenRotate:0, //! SkewX, SkewY, and TweenRotate effect the rotation + isUseColorInfo:false, //! Whether or not this frame have the color changed Info + r:255, + g:255, + b:255, + a:255, + + /** + * Construction of ccs.BaseData + */ + ctor:function () { + this.x = 0; + this.y = 0; + this.zOrder = 0; + this.skewX = 0; + this.skewY = 0; + this.scaleX = 1; + this.scaleY = 1; + this.tweenRotate = 0; + this.isUseColorInfo = false; + this.r = 255; + this.g = 255; + this.b = 255; + this.a = 255; + }, + + /** + * Copy data from node + * @function + * @param {ccs.BaseData} node + */ + copy:function (node) { + this.x = node.x; + this.y = node.y; + this.zOrder = node.zOrder; + + this.scaleX = node.scaleX; + this.scaleY = node.scaleY; + this.skewX = node.skewX; + this.skewY = node.skewY; + + this.tweenRotate = node.tweenRotate; + + this.isUseColorInfo = node.isUseColorInfo; + this.r = node.r; + this.g = node.g; + this.b = node.b; + this.a = node.a; + }, + + /** + * Sets color to base data. + * @function + * @param {cc.Color} color + */ + setColor:function(color){ + this.r = color.r; + this.g = color.g; + this.b = color.b; + this.a = color.a; + }, + + /** + * Returns the color of ccs.BaseData + * @function + * @returns {cc.Color} + */ + getColor:function(){ + return cc.color(this.r, this.g, this.b, this.a); + }, + + /** + * Calculate two baseData's between value(to - from) and set to self + * @function + * @param {ccs.BaseData} from + * @param {ccs.BaseData} to + * @param {Boolean} limit + */ + subtract:function (from, to, limit) { + this.x = to.x - from.x; + this.y = to.y - from.y; + this.scaleX = to.scaleX - from.scaleX; + this.scaleY = to.scaleY - from.scaleY; + this.skewX = to.skewX - from.skewX; + this.skewY = to.skewY - from.skewY; + + if (this.isUseColorInfo || from.isUseColorInfo || to.isUseColorInfo) { + this.a = to.a - from.a; + this.r = to.r - from.r; + this.g = to.g - from.g; + this.b = to.b - from.b; + this.isUseColorInfo = true; + } else { + this.a = this.r = this.g = this.b = 0; + this.isUseColorInfo = false; + } + + if (limit) { + if (this.skewX > ccs.M_PI) + this.skewX -= ccs.DOUBLE_PI; + if (this.skewX < -ccs.M_PI) + this.skewX += ccs.DOUBLE_PI; + if (this.skewY > ccs.M_PI) + this.skewY -= ccs.DOUBLE_PI; + if (this.skewY < -ccs.M_PI) + this.skewY += ccs.DOUBLE_PI; + } + + if (to.tweenRotate) { + this.skewX += to.tweenRotate * ccs.PI * 2; + this.skewY -= to.tweenRotate * ccs.PI * 2; + } + } +}); + +/** + * The class use for save display data. + * @class + * @extends ccs.Class + * + * @property {Number} displayType - the display type + * @property {String} displayName - the display name + */ +ccs.DisplayData = ccs.Class.extend(/** @lends ccs.DisplayData# */{ + displayType: ccs.DISPLAY_TYPE_MAX, + displayName: "", + + /** + * Construction of ccs.DisplayData + */ + ctor: function () { + this.displayType = ccs.DISPLAY_TYPE_MAX; + }, + /** + * Changes display name to texture type + * @function + * @param {String} displayName + * @returns {String} + */ + changeDisplayToTexture:function (displayName) { + // remove .xxx + var textureName = displayName; + var startPos = textureName.lastIndexOf("."); + + if (startPos !== -1) + textureName = textureName.substring(0, startPos); + return textureName; + }, + + /** + * copy data + * @function + * @param {ccs.DisplayData} displayData + */ + copy:function (displayData) { + this.displayName = displayData.displayName; + this.displayType = displayData.displayType; + } +}); + +/** + * The sprite display data class. + * @class + * @extends ccs.DisplayData + * + * @property {ccs.BaseData} skinData - the skin data + */ +ccs.SpriteDisplayData = ccs.DisplayData.extend(/** @lends ccs.SpriteDisplayData# */{ + skinData:null, + + /** + * Construction of ccs.SpriteDisplayData + */ + ctor:function () { + this.skinData = new ccs.BaseData(); + this.displayType = ccs.DISPLAY_TYPE_SPRITE; + }, + /** + * copy data + * @function + * @param {ccs.SpriteDisplayData} displayData + */ + copy:function (displayData) { + ccs.DisplayData.prototype.copy.call(this,displayData); + this.skinData = displayData.skinData; + } +}); + +/** + * The armature display data class + * @class ccs.ArmatureDisplayData + * @extends ccs.DisplayData + */ +ccs.ArmatureDisplayData = ccs.DisplayData.extend(/** @lends ccs.ArmatureDisplayData# */{ + /** + * Construction of ccs.ArmatureDisplayData + */ + ctor:function () { + this.displayName = ""; + this.displayType = ccs.DISPLAY_TYPE_ARMATURE; + } +}); + +/** + * The particle display data class. + * @class ccs.ParticleDisplayData + * @extends ccs.DisplayData + */ +ccs.ParticleDisplayData = ccs.DisplayData.extend(/** @lends ccs.ParticleDisplayData# */{ + /** + * Construction of ccs.ParticleDisplayData + */ + ctor:function () { + this.displayType = ccs.DISPLAY_TYPE_PARTICLE; + } +}); + +/** + *

+ * BoneData used to init a Bone.
+ * BoneData keeps a DisplayData list, a Bone can have many display to change.
+ * The display information saved in the DisplayData
+ *

+ * @class ccs.BoneData + * @extends ccs.BaseData + * + * @property {Array} displayDataList - the display data list + * @property {String} name - the name of Bone + * @property {String} parentName - the parent name of bone + * @property {cc.AffineTransform} boneDataTransform - the bone transform data + */ +ccs.BoneData = ccs.BaseData.extend(/** @lends ccs.BoneData# */{ + displayDataList: null, + name: "", + parentName: "", + boneDataTransform: null, + + /** + * Construction of ccs.BoneData + */ + ctor: function () { + this.displayDataList = []; + this.name = ""; + this.parentName = ""; + this.boneDataTransform = null; + }, + + /** + * Initializes a ccs.BoneData + * @returns {boolean} + */ + init: function () { + this.displayDataList.length = 0; + return true; + }, + /** + * Adds display data to list + * @function + * @param {ccs.DisplayData} displayData + */ + addDisplayData:function (displayData) { + this.displayDataList.push(displayData); + }, + + /** + * Returns display data with index. + * @function + * @param {Number} index + * @returns {ccs.DisplayData} + */ + getDisplayData:function (index) { + return this.displayDataList[index]; + } +}); + +/** + *

+ * ArmatureData saved the Armature name and BoneData needed for the CCBones in this Armature
+ * When we create a Armature, we need to get each Bone's BoneData as it's init information.
+ * So we can get a BoneData from the Dictionary saved in the ArmatureData.
+ *

+ * @class ccs.ArmatureData + * @extends ccs.Class + * + * @property {Object} boneDataDic - the bone data dictionary + * @property {String} name - the name of armature data + * @property {Number} dataVersion - the data version of armature data + */ +ccs.ArmatureData = ccs.Class.extend(/** @lends ccs.ArmatureData# */{ + boneDataDic:null, + name:"", + dataVersion:0.1, + + /** + * Construction of ccs.ArmatureData + */ + ctor:function () { + this.boneDataDic = {}; + this.name = ""; + this.dataVersion = 0.1; + }, + + /** + * Initializes a ccs.ArmatureData + * @returns {boolean} + */ + init:function () { + return true; + }, + + /** + * Adds bone data to dictionary + * @param {ccs.BoneData} boneData + */ + addBoneData:function (boneData) { + this.boneDataDic[boneData.name] = boneData; + }, + + /** + * Gets bone data dictionary + * @returns {Object} + */ + getBoneDataDic:function () { + return this.boneDataDic; + }, + /** + * Gets bone data by bone name + * @function + * @param {String} boneName + * @returns {ccs.BoneData} + */ + getBoneData:function (boneName) { + return this.boneDataDic[boneName]; + } +}); + +/** + * FrameData saved the frame data needed for armature animation in this Armature. + * @class ccs.FrameData + * @extends ccs.BaseData + * + * @property {Number} duration - the duration of frame + * @property {Number} tweenEasing - the easing type of frame + * @property {Number} easingParamNumber - the count of easing parameters. + * @property {Object} easingParams - the dictionary of easing parameters. + * @property {Number} displayIndex - the display renderer index. + * @property {String} movement - the movement name. + * @property {String} event - the event name + * @property {String} sound - the sound path. + * @property {String} soundEffect - the sound effect path. + * @property {Object} blendFunc - the blendFunc of frame. + * @property {Number} frameID - the frame ID of frame + * @property {Boolean} isTween - the flag which frame whether is tween. + */ +ccs.FrameData = ccs.BaseData.extend(/** @lends ccs.FrameData# */{ + duration:0, + tweenEasing:0, + easingParamNumber: 0, + easingParams: null, + displayIndex:-1, + movement:"", + event:"", + sound:"", + soundEffect:"", + blendFunc:null, + frameID:0, + isTween:true, + + /** + * Construction of ccs.FrameData. + */ + ctor:function () { + ccs.BaseData.prototype.ctor.call(this); + this.duration = 1; + this.tweenEasing = ccs.TweenType.LINEAR; + this.easingParamNumber = 0; + this.easingParams = []; + this.displayIndex = 0; + this.movement = ""; + this.event = ""; + this.sound = ""; + this.soundEffect = ""; + this.blendFunc = new cc.BlendFunc(cc.BLEND_SRC, cc.BLEND_DST); + this.frameID = 0; + this.isTween = true; + }, + + /** + * copy data + * @function + * @param frameData + */ + copy:function (frameData) { + ccs.BaseData.prototype.copy.call(this, frameData); + this.duration = frameData.duration; + this.displayIndex = frameData.displayIndex; + + this.tweenEasing = frameData.tweenEasing; + this.easingParamNumber = frameData.easingParamNumber; + +// this.movement = frameData.movement; +// this.event = frameData.event; +// this.sound = frameData.sound; +// this.soundEffect = frameData.soundEffect; +// this.easingParams.length = 0; + if (this.easingParamNumber !== 0){ + this.easingParams.length = 0; + for (var i = 0; i + * The animation data information of Cocos Armature. It include all movement information for the Armature.
+ * The struct is AnimationData -> MovementData -> MovementBoneData -> FrameData
+ * -> MovementFrameData
+ *

+ * @class ccs.AnimationData + * @extends ccs.Class + */ +ccs.AnimationData = function(){ + this.movementDataDic = {}; + this.movementNames = []; + this.name = ""; +}; + +/** + * adds movement data to the movement data dictionary + * @param {ccs.MovementData} moveData + */ +ccs.AnimationData.prototype.addMovement = function(moveData){ + this.movementDataDic[moveData.name] = moveData; + this.movementNames.push(moveData.name); +}; + +/** + * gets movement data from movement data dictionary + * @param {String} moveName + * @returns {ccs.MovementData} + */ +ccs.AnimationData.prototype.getMovement = function(moveName){ + return this.movementDataDic[moveName]; +}; + +/** + * gets the count of movement data dictionary + * @returns {Number} + */ +ccs.AnimationData.prototype.getMovementCount = function(){ + return Object.keys(this.movementDataDic).length; +}; + +/** + * contour vertex + * @class ccs.ContourVertex2 + * @param {Number} x + * @param {Number} y + * @constructor + */ +ccs.ContourVertex2 = function (x, y) { + this.x = x || 0; + this.y = y || 0; +}; + +/** + * The Contour data information of Cocos Armature. + * @class ccs.ContourData + * @constructor + */ +ccs.ContourData = function(){ + this.vertexList = []; +}; + +ccs.ContourData.prototype.init = function(){ + this.vertexList.length = 0; + return true; +}; + +/** + * add a vertex object to vertex list + * @param {cc.Point} p + */ +ccs.ContourData.prototype.addVertex = function(p){ + //var v = new ccs.ContourVertex2(p.x, p.y); //ccs.ContourVertex2 is same as cc.Point, so we needn't create a ccs.ContourVertex2 object + this.vertexList.push(p); +}; + +/** + * The texture data information of Cocos Armature + * @class ccs.TextureData + */ +ccs.TextureData = function(){ + this.height = 0; + this.width = 0; + this.pivotX = 0.5; + this.pivotY = 0.5; + this.name = ""; + this.contourDataList = []; +}; + +ccs.TextureData.prototype.init = function(){ + this.contourDataList.length = 0; +}; + +/** + * Adds a contourData to contourDataList + * @param {ccs.ContourData} contourData + */ +ccs.TextureData.prototype.addContourData = function(contourData){ + this.contourDataList.push(contourData); +}; + +/** + * gets a contourData from contourDataList by index + * @param {Number} index + * @returns {ccs.ContourData} + */ +ccs.TextureData.prototype.getContourData = function(index){ + return this.contourDataList[index]; +}; diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/armature/display/CCBatchNode.js b/frameworks/cocos2d-html5/extensions/cocostudio/armature/display/CCBatchNode.js new file mode 100644 index 0000000..f20c238 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/armature/display/CCBatchNode.js @@ -0,0 +1,114 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * A batchNode to Armature + * @class ccs.BatchNode + * @extends cc.Node + */ +ccs.BatchNode = cc.Node.extend(/** @lends ccs.BatchNode# */{ + _atlas:null, + _className:"BatchNode", + + ctor:function () { + this._atlas = null; + + ccs.BatchNode.prototype.init.call(this); + }, + + init:function () { + var ret = cc.Node.prototype.init.call(this); + this.setShaderProgram(cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURE_UCOLOR)); + return ret; + }, + + addChild:function (child, zOrder, tag) { + cc.Node.prototype.addChild.call(this, child, zOrder, tag); + if (child instanceof cc.Armature){ + child.setBatchNode(this); + } + }, + + removeChild: function(child, cleanup){ + if (child instanceof cc.Armature) + child.setBatchNode(null); + cc.Node.prototype.removeChild.call(this, child, cleanup); + }, + + visit:function (renderer, parentTransform, parentTransformUpdated) { + // quick return if not visible. children won't be drawn. + if (!this._visible) + return; + + var dirty = parentTransformUpdated || this._transformUpdated; + if(dirty) + this._modelViewTransform = this.transform(parentTransform); + this._transformUpdated = false; + + // IMPORTANT: + // To ease the migration to v3.0, we still support the kmGL stack, + // but it is deprecated and your code should not rely on it + cc.kmGLPushMatrixWitMat4(this._stackMatrix); + + if (this.grid && this.grid.isActive()) + this.grid.beforeDraw(); + + this.sortAllChildren(); + this.draw(renderer, this._modelViewTransform, dirty); + + if (this.grid && this.grid.isActive()) + this.grid.afterDraw(this); + + cc.kmGLPopMatrix(); + }, + + draw:function (renderer, transform, transformUpdated) { + var locChildren = this._children; + if(locChildren.length === 0) + return; + + var child = null; + for (var i = 0, len = locChildren.length; i < len; i++) { + child = locChildren[i]; + child.visit(); + if (child instanceof cc.Armature) { + this._atlas = child.getTextureAtlas(); + } + } + if (this._atlas) { + this._atlas.drawQuads(); + this._atlas.removeAllQuads(); + } + } +}); + +/** + * + * @returns {ccs.BatchNode} + * @deprecated since v3.1, please use new construction instead + */ +ccs.BatchNode.create = function () { + return new ccs.BatchNode(); +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/armature/display/CCDecorativeDisplay.js b/frameworks/cocos2d-html5/extensions/cocostudio/armature/display/CCDecorativeDisplay.js new file mode 100644 index 0000000..f7a7c3d --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/armature/display/CCDecorativeDisplay.js @@ -0,0 +1,118 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * Decorative a display node for Cocos Armature + * @class + * @extends ccs.Class + */ +ccs.DecorativeDisplay = ccs.Class.extend(/** @lends ccs.DecorativeDisplay# */{ + _display: null, + _colliderDetector: null, + _displayData: null, + + ctor:function () { + this._display = null; + this._colliderDetector = null; + this._displayData = null; + + //ccs.DecorativeDisplay.prototype.init.call(this); + }, + + /** + * Initializes a ccs.DecorativeDisplay + * @returns {boolean} + */ + init:function () { + return true; + }, + + /** + * Sets display node to decorative + * @param {cc.Node} display + */ + setDisplay:function (display) { + if(display._parent){ + display._parent.removeChild(display); + delete display._parent; + } + this._display = display; + }, + + /** + * Returns the display node + * @returns {cc.Node} + */ + getDisplay:function () { + return this._display; + }, + + /** + * Sets collide detector + * @param {ccs.ColliderDetector} colliderDetector + */ + setColliderDetector:function (colliderDetector) { + this._colliderDetector = colliderDetector; + }, + + /** + * Returns collide detector + * @returns {ccs.ColliderDetector} + */ + getColliderDetector:function () { + return this._colliderDetector; + }, + + /** + * Sets display data + * @param {ccs.DisplayData} displayData + */ + setDisplayData:function (displayData) { + this._displayData = displayData; + }, + + /** + * Returns display data + * @returns {ccs.DisplayData} + */ + getDisplayData:function () { + return this._displayData; + }, + + release:function () { + this._display = null; + this._displayData = null; + this._colliderDetector = null; + } +}); + +/** + * Allocates and initializes a decorative display. + * @return {ccs.DecorativeDisplay} + * @deprecated since v3.1, please use new construction instead + */ +ccs.DecorativeDisplay.create = function () { + return new ccs.DecorativeDisplay(); +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/armature/display/CCDisplayFactory.js b/frameworks/cocos2d-html5/extensions/cocostudio/armature/display/CCDisplayFactory.js new file mode 100644 index 0000000..772bade --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/armature/display/CCDisplayFactory.js @@ -0,0 +1,219 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * @ignore + */ +ccs.displayFactory = { + addDisplay: function (bone, decoDisplay, displayData) { + switch (displayData.displayType) { + case ccs.DISPLAY_TYPE_SPRITE: + this.addSpriteDisplay(bone, decoDisplay, displayData); + break; + case ccs.DISPLAY_TYPE_PARTICLE: + this.addParticleDisplay(bone, decoDisplay, displayData); + break; + case ccs.DISPLAY_TYPE_ARMATURE: + this.addArmatureDisplay(bone, decoDisplay, displayData); + break; + default: + break; + } + }, + + createDisplay: function (bone, decoDisplay) { + switch (decoDisplay.getDisplayData().displayType) { + case ccs.DISPLAY_TYPE_SPRITE: + this.createSpriteDisplay(bone, decoDisplay); + break; + case ccs.DISPLAY_TYPE_PARTICLE: + this.createParticleDisplay(bone, decoDisplay); + break; + case ccs.DISPLAY_TYPE_ARMATURE: + this.createArmatureDisplay(bone, decoDisplay); + break; + default: + break; + } + }, + + _helpTransform: {a:1, b:0, c:0, d:1, tx:0, ty:0}, + updateDisplay: function (bone, dt, dirty) { + var display = bone.getDisplayRenderNode(); + if(!display) + return; + + switch (bone.getDisplayRenderNodeType()) { + case ccs.DISPLAY_TYPE_SPRITE: + if (dirty) { + display._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + display.updateArmatureTransform(); + } + break; + case ccs.DISPLAY_TYPE_PARTICLE: + this.updateParticleDisplay(bone, display, dt); + break; + case ccs.DISPLAY_TYPE_ARMATURE: + this.updateArmatureDisplay(bone, display, dt); + break; + default: + var transform = bone.getNodeToArmatureTransform(); + display.setAdditionalTransform(transform); + break; + } + if (ccs.ENABLE_PHYSICS_CHIPMUNK_DETECT || ccs.ENABLE_PHYSICS_SAVE_CALCULATED_VERTEX) { + if (dirty) { + var decoDisplay = bone.getDisplayManager().getCurrentDecorativeDisplay(); + var detector = decoDisplay.getColliderDetector(); + if (detector) { + var node = decoDisplay.getDisplay(); + var displayTransform = node.getNodeToParentTransform(); + var helpTransform = this._helpTransform; + helpTransform.a = displayTransform.a; + helpTransform.b = displayTransform.b; + helpTransform.c = displayTransform.c; + helpTransform.d = displayTransform.d; + helpTransform.tx = displayTransform.tx; + helpTransform.ty = displayTransform.ty; + var anchorPoint = cc.pointApplyAffineTransform(node.getAnchorPointInPoints(), helpTransform); + helpTransform.tx = anchorPoint.x; + helpTransform.ty = anchorPoint.y; + var t = cc.affineTransformConcat(helpTransform, bone.getArmature().getNodeToParentTransform()); + detector.updateTransform(t); + } + } + } + }, + + addSpriteDisplay: function (bone, decoDisplay, displayData) { + var sdp = new ccs.SpriteDisplayData(); + sdp.copy(displayData); + decoDisplay.setDisplayData(sdp); + this.createSpriteDisplay(bone, decoDisplay); + }, + + createSpriteDisplay: function (bone, decoDisplay) { + var skin = null; + var displayData = decoDisplay.getDisplayData(); + //! remove .xxx + var textureName = displayData.displayName; + var startPos = textureName.lastIndexOf("."); + if (startPos !== -1) + textureName = textureName.substring(0, startPos); + //! create display + if (textureName === "") + skin = new ccs.Skin(); + else + skin = new ccs.Skin("#" + textureName + ".png"); + + decoDisplay.setDisplay(skin); + + skin.setBone(bone); + this.initSpriteDisplay(bone, decoDisplay, displayData.displayName, skin); + + var armature = bone.getArmature(); + if (armature) { + if (armature.getArmatureData().dataVersion >= ccs.CONST_VERSION_COMBINED) + skin.setSkinData(displayData.skinData); + else + skin.setSkinData(bone.boneData); + } + }, + + initSpriteDisplay: function (bone, decoDisplay, displayName, skin) { + //! remove .xxx + var textureName = displayName; + var startPos = textureName.lastIndexOf("."); + + if (startPos !== -1) + textureName = textureName.substring(0, startPos); + + var textureData = ccs.armatureDataManager.getTextureData(textureName); + if (textureData) { + //! Init display anchorPoint, every Texture have a anchor point + skin.setAnchorPoint(cc.p(textureData.pivotX, textureData.pivotY)); + } + + if (ccs.ENABLE_PHYSICS_CHIPMUNK_DETECT || ccs.ENABLE_PHYSICS_SAVE_CALCULATED_VERTEX) { + if (textureData && textureData.contourDataList.length > 0) { + //! create ContourSprite + var colliderDetector = new ccs.ColliderDetector(bone); + colliderDetector.addContourDataList(textureData.contourDataList); + decoDisplay.setColliderDetector(colliderDetector); + } + } + }, + + addArmatureDisplay: function (bone, decoDisplay, displayData) { + var adp = new ccs.ArmatureDisplayData(); + adp.copy(displayData); + decoDisplay.setDisplayData(adp); + + this.createArmatureDisplay(bone, decoDisplay); + }, + + createArmatureDisplay: function (bone, decoDisplay) { + var displayData = decoDisplay.getDisplayData(); + var armature = new ccs.Armature(displayData.displayName, bone); + decoDisplay.setDisplay(armature); + }, + + updateArmatureDisplay: function (bone, armature, dt) { + if (armature) { + armature.sortAllChildren(); + armature.update(dt); + } + }, + + addParticleDisplay: function (bone, decoDisplay, displayData) { + var adp = new ccs.ParticleDisplayData(); + adp.copy(displayData); + decoDisplay.setDisplayData(adp); + this.createParticleDisplay(bone, decoDisplay); + }, + + createParticleDisplay: function (bone, decoDisplay) { + var displayData = decoDisplay.getDisplayData(); + var system = new cc.ParticleSystem(displayData.displayName); + + system.removeFromParent(); + system.cleanup(); + + var armature = bone.getArmature(); + if (armature) + system.setParent(bone.getArmature()); + + decoDisplay.setDisplay(system); + }, + + updateParticleDisplay: function (bone, particleSystem, dt) { + var node = new ccs.BaseData(); + ccs.TransformHelp.matrixToNode(bone.nodeToArmatureTransform(), node); + particleSystem.setPosition(node.x, node.y); + particleSystem.setScaleX(node.scaleX); + particleSystem.setScaleY(node.scaleY); + particleSystem.update(dt); + } +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/armature/display/CCDisplayManager.js b/frameworks/cocos2d-html5/extensions/cocostudio/armature/display/CCDisplayManager.js new file mode 100644 index 0000000..584f864 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/armature/display/CCDisplayManager.js @@ -0,0 +1,465 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The display manager for CocoStudio Armature bone. + * @Class ccs.DisplayManager + * @extend cc.Class + * + * @param {ccs.Bone} bone The bone for the display manager + */ +ccs.DisplayManager = ccs.Class.extend(/** @lends ccs.DisplayManager */{ + _decoDisplayList: null, + _currentDecoDisplay: null, + _displayRenderNode: null, + _displayIndex: null, + _forceChangeDisplay: false, + _bone: null, + _visible: true, + _displayType: null, + + ctor: function (bone) { + this._decoDisplayList = []; + this._currentDecoDisplay = null; + this._displayRenderNode = null; + this._displayIndex = null; + this._forceChangeDisplay = false; + this._bone = null; + this._visible = true; + this._displayType = ccs.DISPLAY_TYPE_MAX; + + bone && ccs.DisplayManager.prototype.init.call(this, bone); + }, + + /** + * Initializes a ccs.DisplayManager. + * @param bone + * @returns {boolean} + */ + init: function (bone) { + this._bone = bone; + this.initDisplayList(bone.getBoneData()); + return true; + }, + + /** + *

+ * Add display and use _DisplayData init the display.
+ * If index already have a display, then replace it.
+ * If index is current display index, then also change display to _index
+ *

+ * @param {ccs.DisplayData|cc.Node} display it include the display information, like DisplayType. If you want to create a sprite display, then create a SpriteDisplayData param + * @param {Number} index the index of the display you want to replace or add to. -1 : append display from back + */ + addDisplay: function (display, index) { + var decoDisplay, locDisplayList = this._decoDisplayList; + if ((index >= 0) && (index < locDisplayList.length)) + decoDisplay = locDisplayList[index]; + else { + decoDisplay = new ccs.DecorativeDisplay(); + locDisplayList.push(decoDisplay); + } + + if (display instanceof ccs.DisplayData) { + ccs.displayFactory.addDisplay(this._bone, decoDisplay, display); + //! if changed display index is current display index, then change current display to the new display + if (index === this._displayIndex) { + this._displayIndex = -1; + this.changeDisplayWithIndex(index, false); + } + return; + } + + var displayData = null; + if (display instanceof ccs.Skin) { + display.setBone(this._bone); + displayData = new ccs.SpriteDisplayData(); + ccs.displayFactory.initSpriteDisplay(this._bone, decoDisplay, display.getDisplayName(), display); + + var spriteDisplayData = decoDisplay.getDisplayData(); + if (spriteDisplayData instanceof ccs.SpriteDisplayData) { + display.setSkinData(spriteDisplayData.skinData); + displayData.skinData = spriteDisplayData.skinData; + } else { + var find = false; + for (var i = locDisplayList.length - 2; i >= 0; i--) { + var dd = locDisplayList[i]; + var sdd = dd.getDisplayData(); + if (sdd instanceof ccs.SpriteDisplayData) { + find = true; + display.setSkinData(sdd.skinData); + displayData.skinData = sdd.skinData; + break; + } + } + if (!find) + display.setSkinData(new ccs.BaseData()); + } + } else if (display instanceof cc.ParticleSystem) { + displayData = new ccs.ParticleDisplayData(); + display.removeFromParent(); + display._performRecursive(cc.Node._stateCallbackType.cleanup); + var armature = this._bone.getArmature(); + if (armature) + display.setParent(armature); + } else if (display instanceof ccs.Armature) { + displayData = new ccs.ArmatureDisplayData(); + displayData.displayName = display.getName(); + display.setParentBone(this._bone); + } else + displayData = new ccs.DisplayData(); + decoDisplay.setDisplay(display); + decoDisplay.setDisplayData(displayData); + + //! if changed display index is current display index, then change current display to the new display + if (index === this._displayIndex) { + this._displayIndex = -1; + this.changeDisplayWithIndex(index, false); + } + }, + + _addDisplayOther: function (decoDisplay, display) { + var displayData = null; + if (display instanceof ccs.Skin) { + var skin = display; + skin.setBone(this._bone); + displayData = new ccs.SpriteDisplayData(); + displayData.displayName = skin.getDisplayName(); + ccs.displayFactory.initSpriteDisplay(this._bone, decoDisplay, skin.getDisplayName(), skin); + var spriteDisplayData = decoDisplay.getDisplayData(); + if (spriteDisplayData instanceof ccs.SpriteDisplayData) + skin.setSkinData(spriteDisplayData.skinData); + else { + var find = false; + for (var i = this._decoDisplayList.length - 2; i >= 0; i--) { + var dd = this._decoDisplayList[i]; + var sdd = dd.getDisplayData(); + if (sdd) { + find = true; + skin.setSkinData(sdd.skinData); + displayData.skinData = sdd.skinData; + break; + } + } + if (!find) { + skin.setSkinData(new ccs.BaseData()); + } + skin.setSkinData(new ccs.BaseData()); + } + + } + else if (display instanceof cc.ParticleSystem) { + displayData = new ccs.ParticleDisplayData(); + displayData.displayName = display._plistFile; + } + else if (display instanceof ccs.Armature) { + displayData = new ccs.ArmatureDisplayData(); + displayData.displayName = display.getName(); + display.setParentBone(this._bone); + } + else { + displayData = new ccs.DisplayData(); + } + decoDisplay.setDisplay(display); + decoDisplay.setDisplayData(displayData); + }, + + /** + * Removes display node from list. + * @param {Number} index + */ + removeDisplay: function (index) { + this._decoDisplayList.splice(index, 1); + if (index === this._displayIndex) { + this.setCurrentDecorativeDisplay(null); + this._displayIndex = -1; + } + }, + + /** + * Returns the display node list. + * @returns {Array} + */ + getDecorativeDisplayList: function () { + return this._decoDisplayList; + }, + + /** + *

+ * Change display by index. You can just use this method to change display in the display list.
+ * The display list is just used for this bone, and it is the displays you may use in every frame.
+ * Note : if index is the same with prev index, the method will not effect
+ *

+ * @param {Number} index The index of the display you want to change + * @param {Boolean} force If true, then force change display to specified display, or current display will set to display index edit in the flash every key frame. + */ + changeDisplayWithIndex: function (index, force) { + if (index >= this._decoDisplayList.length) { + cc.log("the index value is out of range"); + return; + } + this._forceChangeDisplay = force; + + //if index is equal to current display index,then do nothing + if (this._displayIndex === index) + return; + + this._displayIndex = index; + + //! If displayIndex < 0, it means you want to hide you display + if (index < 0) { + if (this._displayRenderNode) { + this._displayRenderNode.removeFromParent(true); + this.setCurrentDecorativeDisplay(null); + } + return; + } + this.setCurrentDecorativeDisplay(this._decoDisplayList[index]); + }, + + /** + * Change display by name. @see changeDisplayWithIndex. + * @param {String} name + * @param {Boolean} force + */ + changeDisplayWithName: function (name, force) { + var locDisplayList = this._decoDisplayList; + for (var i = 0; i < locDisplayList.length; i++) { + if (locDisplayList[i].getDisplayData().displayName === name) { + this.changeDisplayWithIndex(i, force); + break; + } + } + }, + + /** + * Sets current decorative display. + * @param {ccs.DecorativeDisplay} decoDisplay + */ + setCurrentDecorativeDisplay: function (decoDisplay) { + var locCurrentDecoDisplay = this._currentDecoDisplay; + if (ccs.ENABLE_PHYSICS_CHIPMUNK_DETECT || ccs.ENABLE_PHYSICS_SAVE_CALCULATED_VERTEX) { + if (locCurrentDecoDisplay && locCurrentDecoDisplay.getColliderDetector()) + locCurrentDecoDisplay.getColliderDetector().setActive(false); + } + + this._currentDecoDisplay = decoDisplay; + locCurrentDecoDisplay = this._currentDecoDisplay; + if (ccs.ENABLE_PHYSICS_CHIPMUNK_DETECT || ccs.ENABLE_PHYSICS_SAVE_CALCULATED_VERTEX) { + if (locCurrentDecoDisplay && locCurrentDecoDisplay.getColliderDetector()) + locCurrentDecoDisplay.getColliderDetector().setActive(true); + } + + var displayRenderNode = (!locCurrentDecoDisplay) ? null : locCurrentDecoDisplay.getDisplay(); + + var locRenderNode = this._displayRenderNode, locBone = this._bone; + if (locRenderNode) { + if (locRenderNode instanceof ccs.Armature) + locBone.setChildArmature(null); + locRenderNode.removeFromParent(true); + } + this._displayRenderNode = displayRenderNode; + + if (displayRenderNode) { + if (displayRenderNode instanceof ccs.Armature) { + this._bone.setChildArmature(displayRenderNode); + displayRenderNode.setParentBone(this._bone); + } else if (displayRenderNode instanceof cc.ParticleSystem) { + if (displayRenderNode instanceof ccs.Armature) { + locBone.setChildArmature(displayRenderNode); + displayRenderNode.setParentBone(locBone); + } else if (displayRenderNode instanceof cc.ParticleSystem) + displayRenderNode.resetSystem(); + } + + displayRenderNode.setColor(locBone.getDisplayedColor()); + displayRenderNode.setOpacity(locBone.getDisplayedOpacity()); + + this._displayRenderNode.setVisible(this._visible); + this._displayType = this._currentDecoDisplay.getDisplayData().displayType; + } else + this._displayType = ccs.DISPLAY_TYPE_MAX; + + + cc.renderer.childrenOrderDirty = true; + }, + + /** + * Returns the current display render node. + * @returns {cc.Node} + */ + getDisplayRenderNode: function () { + return this._displayRenderNode; + }, + + /** + * Returns the type of display render node. + * @returns {Number} + */ + getDisplayRenderNodeType: function () { + return this._displayType; + }, + + /** + * Returns the index of display render node. + * @returns {Number} + */ + getCurrentDisplayIndex: function () { + return this._displayIndex; + }, + + /** + * Returns the current decorative display + * @returns {ccs.DecorativeDisplay} + */ + getCurrentDecorativeDisplay: function () { + return this._currentDecoDisplay; + }, + + /** + * Gets a decorative display by index. + * @param index + * @returns {ccs.DecorativeDisplay} + */ + getDecorativeDisplayByIndex: function (index) { + return this._decoDisplayList[index]; + }, + + /** + *

+ * Use BoneData to init the display list. + * If display is a sprite, and it have texture info in the TextureData, then use TextureData to init the display node's anchor point + * If the display is a Armature, then create a new Armature + *

+ * @param {ccs.BoneData} boneData + */ + initDisplayList: function (boneData) { + this._decoDisplayList.length = 0; + if (!boneData) + return; + var displayList = boneData.displayDataList, decoList = this._decoDisplayList, locBone = this._bone; + for (var i = 0; i < displayList.length; i++) { + var displayData = displayList[i]; + var decoDisplay = new ccs.DecorativeDisplay(); + decoDisplay.setDisplayData(displayData); + ccs.displayFactory.createDisplay(locBone, decoDisplay); + decoList.push(decoDisplay); + } + }, + + /** + * Check if the position is inside the bone. + * @param {cc.Point|Number} point + * @param {Number} [y] + * @returns {boolean} + */ + containPoint: function (point, y) { + if (!this._visible || this._displayIndex < 0) + return false; + + if (y !== undefined) + point = cc.p(point, y); + + if (this._currentDecoDisplay.getDisplayData().displayType === ccs.DISPLAY_TYPE_SPRITE) { + /* + * First we first check if the point is in the sprite content rect. If false, then we continue to check + * the contour point. If this step is also false, then we can say the bone not contain this point. + * + */ + var sprite = this._currentDecoDisplay.getDisplay(); + sprite = sprite.getChildByTag(0); + return ccs.SPRITE_CONTAIN_POINT_WITH_RETURN(sprite, point); + } + return false; + }, + + /** + *

+ * Sets whether the display is visible
+ * The default value is true, a node is default to visible + *

+ * @param {boolean} visible + */ + setVisible: function (visible) { + if (!this._displayRenderNode) + return; + this._visible = visible; + this._displayRenderNode.setVisible(visible); + }, + + /** + * Determines if the display is visible + * @returns {boolean} true if the node is visible, false if the node is hidden. + */ + isVisible: function () { + return this._visible; + }, + + getContentSize: function () { + if (!this._displayRenderNode) + return cc.size(0, 0); + return this._displayRenderNode.getContentSize(); + }, + + getBoundingBox: function () { + if (!this._displayRenderNode) + return cc.rect(0, 0, 0, 0); + return this._displayRenderNode.getBoundingBox(); + }, + + getAnchorPoint: function () { + if (!this._displayRenderNode) + return cc.p(0, 0); + return this._displayRenderNode.getAnchorPoint(); + }, + + getAnchorPointInPoints: function () { + if (!this._displayRenderNode) + return cc.p(0, 0); + return this._displayRenderNode.getAnchorPointInPoints(); + }, + + getForceChangeDisplay: function () { + return this._forceChangeDisplay; + }, + + release: function () { + this._decoDisplayList = null; + if (this._displayRenderNode) { + this._displayRenderNode.removeFromParent(true); + this._displayRenderNode = null; + } + } +}); + +/** + * Allocates and initializes a display manager with ccs.Bone. + * @param {ccs.Bone} bone + * @returns {ccs.DisplayManager} + * @deprecated since v3.1, please use new construction instead + */ +ccs.DisplayManager.create = function (bone) { + return new ccs.DisplayManager(bone); +}; diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/armature/display/CCSkin.js b/frameworks/cocos2d-html5/extensions/cocostudio/armature/display/CCSkin.js new file mode 100644 index 0000000..9f75ceb --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/armature/display/CCSkin.js @@ -0,0 +1,207 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * ccs.Bone uses ccs.Skin to displays on screen. + * @class + * @extends ccs.Sprite + * + * @param {String} [fileName] + * @param {cc.Rect} [rect] + * + * @property {Object} skinData - The data of the skin + * @property {ccs.Bone} bone - The bone of the skin + * @property {String} displayName - <@readonly> The displayed name of skin + * + */ +ccs.Skin = ccs.Sprite.extend(/** @lends ccs.Skin# */{ + _skinData: null, + bone: null, + _skinTransform: null, + _displayName: "", + _armature: null, + _className: "Skin", + + ctor: function (fileName, rect) { + cc.Sprite.prototype.ctor.call(this); + this._skinData = null; + this.bone = null; + this._displayName = ""; + this._skinTransform = cc.affineTransformIdentity(); + this._armature = null; + + if (fileName == null || fileName === "") { + ccs.Skin.prototype.init.call(this); + } else { + if(fileName[0] === "#"){ + ccs.Skin.prototype.initWithSpriteFrameName.call(this, fileName.substr(1)); + } else { + ccs.Skin.prototype.initWithFile.call(this, fileName, rect); + } + } + }, + + /** + * Initializes with sprite frame name + * @param {String} spriteFrameName + * @returns {Boolean} + */ + initWithSpriteFrameName: function (spriteFrameName) { + if(spriteFrameName === "") + return false; + var pFrame = cc.spriteFrameCache.getSpriteFrame(spriteFrameName); + var ret = true; + if(pFrame) + this.initWithSpriteFrame(pFrame); + else{ + cc.log("Can't find CCSpriteFrame with %s. Please check your .plist file", spriteFrameName); + ret = false; + } + this._displayName = spriteFrameName; + return ret; + }, + + /** + * Initializes with texture file name. + * @param {String} fileName + * @param {cc.Rect} rect + * @returns {Boolean} + */ + initWithFile: function (fileName, rect) { + var ret = rect ? cc.Sprite.prototype.initWithFile.call(this, fileName, rect) + : cc.Sprite.prototype.initWithFile.call(this, fileName); + this._displayName = fileName; + return ret; + }, + + /** + * Sets skin data to ccs.Skin. + * @param {ccs.BaseData} skinData + */ + setSkinData: function (skinData) { + this._skinData = skinData; + this.setScaleX(skinData.scaleX); + this.setScaleY(skinData.scaleY); + this.setRotationX(cc.radiansToDegrees(skinData.skewX)); + this.setRotationY(cc.radiansToDegrees(-skinData.skewY)); + this.setPosition(skinData.x, skinData.y); + + this._renderCmd.transform(); + }, + + /** + * Returns skin date of ccs.Skin. + * @returns {ccs.BaseData} + */ + getSkinData: function () { + return this._skinData; + }, + + /** + * Updates armature skin's transform with skin transform and bone's transform. + */ + updateArmatureTransform: function () { + this._renderCmd.transform(); + }, + + /** + * Returns skin's world transform. + * @returns {cc.AffineTransform} + */ + getNodeToWorldTransform: function(){ + return this._renderCmd.getNodeToWorldTransform(); + }, + + getNodeToWorldTransformAR: function(){ + return this._renderCmd.getNodeToWorldTransformAR(); + }, + + /** + * Sets the bone reference to ccs.Skin. + * @param bone + */ + setBone: function (bone) { + this.bone = bone; + var armature = this.bone.getArmature(); + if(armature) + this._armature = armature; + }, + + /** + * Returns the bone reference of ccs.Skin. + * @returns {null} + */ + getBone: function () { + return this.bone; + }, + + /** + * display name getter + * @returns {String} + */ + getDisplayName: function () { + return this._displayName; + }, + + _createRenderCmd: function(){ + if(cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return new ccs.Skin.CanvasRenderCmd(this); + else + return new ccs.Skin.WebGLRenderCmd(this); + } +}); + +var _p = ccs.Skin.prototype; + +// Extended properties +/** @expose */ +_p.skinData; +cc.defineGetterSetter(_p, "skinData", _p.getSkinData, _p.setSkinData); +/** @expose */ +_p.displayName; +cc.defineGetterSetter(_p, "displayName", _p.getDisplayName); + +_p = null; + +/** + * allocates and initializes a skin. + * @param {String} [fileName] fileName or sprite frame name + * @param {cc.Rect} [rect] + * @returns {ccs.Skin} + * @deprecated since v3.1, please use new construction instead + */ +ccs.Skin.create = function (fileName, rect) { + return new ccs.Skin(fileName, rect); +}; + +/** + * allocates and initializes a skin. + * @param {String} spriteFrameName + * @returns {ccs.Skin} + * @deprecated since v3.1, please use new construction instead + */ +ccs.Skin.createWithSpriteFrameName = function (spriteFrameName) { + return new ccs.Skin("#" + spriteFrameName); +}; diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/armature/display/CCSkinRenderCmd.js b/frameworks/cocos2d-html5/extensions/cocostudio/armature/display/CCSkinRenderCmd.js new file mode 100644 index 0000000..eabcd02 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/armature/display/CCSkinRenderCmd.js @@ -0,0 +1,126 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + ccs.Skin.RenderCmd = { + _realWorldTM: null, + transform: function (parentCmd, recursive) { + if (!this._transform) { + this._transform = {a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0}; + this._worldTransform = {a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0}; + } + + var node = this._node, + pt = parentCmd ? parentCmd._worldTransform : null, + t = this._transform, + wt = this._worldTransform, + dirty = this._dirtyFlag & cc.Node._dirtyFlags.transformDirty; + + if (dirty || pt) { + this.originTransform(); + cc.affineTransformConcatIn(this._transform, node.bone.getNodeToArmatureTransform()); + this._dirtyFlag &= ~cc.Node._dirtyFlags.transformDirty; + } + + if (pt) { + wt.a = t.a * pt.a + t.b * pt.c; + wt.b = t.a * pt.b + t.b * pt.d; + wt.c = t.c * pt.a + t.d * pt.c; + wt.d = t.c * pt.b + t.d * pt.d; + wt.tx = t.tx * pt.a + t.ty * pt.c + pt.tx; + wt.ty = t.tx * pt.b + t.ty * pt.d + pt.ty; + + var vertices = this._vertices; + if (vertices) { + var lx = node._offsetPosition.x, rx = lx + node._rect.width, + by = node._offsetPosition.y, ty = by + node._rect.height; + + vertices[0].x = lx * wt.a + ty * wt.c + wt.tx; // tl + vertices[0].y = lx * wt.b + ty * wt.d + wt.ty; + vertices[1].x = lx * wt.a + by * wt.c + wt.tx; // bl + vertices[1].y = lx * wt.b + by * wt.d + wt.ty; + vertices[2].x = rx * wt.a + ty * wt.c + wt.tx; // tr + vertices[2].y = rx * wt.b + ty * wt.d + wt.ty; + vertices[3].x = rx * wt.a + by * wt.c + wt.tx; // br + vertices[3].y = rx * wt.b + by * wt.d + wt.ty; + } + } + else { + wt.a = t.a; + wt.b = t.b; + wt.c = t.c; + wt.d = t.d; + wt.tx = t.tx; + wt.ty = t.ty; + } + var rwtm = this._realWorldTM; + if (rwtm) { + rwtm.a = t.a; rwtm.b = t.b; rwtm.c = t.c; rwtm.d = t.d; rwtm.tx = t.tx; rwtm.ty = t.ty; + cc.affineTransformConcatIn(rwtm, this._node.bone.getArmature()._renderCmd._worldTransform); + } + }, + + getNodeToWorldTransform: function () { + return cc.affineTransformConcat(this._transform, this._node.bone.getArmature().getNodeToWorldTransform()); + }, + + getNodeToWorldTransformAR: function () { + var displayTransform = this._transform, node = this._node; + this._anchorPointInPoints = cc.pointApplyAffineTransform(this._anchorPointInPoints, displayTransform); + displayTransform.tx = this._anchorPointInPoints.x; + displayTransform.ty = this._anchorPointInPoints.y; + return cc.affineTransformConcat(displayTransform, node.bone.getArmature().getNodeToWorldTransform()); + } + }; + + ccs.Skin.CanvasRenderCmd = function (renderable) { + this._spriteCmdCtor(renderable); + this._realWorldTM = {a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0}; + }; + + var proto = ccs.Skin.CanvasRenderCmd.prototype = Object.create(cc.Sprite.CanvasRenderCmd.prototype); + cc.inject(ccs.Skin.RenderCmd, proto); + + proto.constructor = ccs.Skin.CanvasRenderCmd; + + proto._updateCurrentRegions = function () { + var temp = this._currentRegion; + this._currentRegion = this._oldRegion; + this._oldRegion = temp; + //hittest will call the transform, and set region flag to DirtyDouble, and the changes need to be considered for rendering + if (cc.Node.CanvasRenderCmd.RegionStatus.DirtyDouble === this._regionFlag && (!this._currentRegion.isEmpty())) { + this._oldRegion.union(this._currentRegion); + } + this._currentRegion.updateRegion(this.getLocalBB(), this._realWorldTM); + }; + + ccs.Skin.WebGLRenderCmd = function (renderable) { + this._spriteCmdCtor(renderable); + }; + + proto = ccs.Skin.WebGLRenderCmd.prototype = Object.create(cc.Sprite.WebGLRenderCmd.prototype); + cc.inject(ccs.Skin.RenderCmd, proto); + proto.constructor = ccs.Skin.WebGLRenderCmd; +})(); diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/armature/physics/CCColliderDetector.js b/frameworks/cocos2d-html5/extensions/cocostudio/armature/physics/CCColliderDetector.js new file mode 100644 index 0000000..dccbe58 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/armature/physics/CCColliderDetector.js @@ -0,0 +1,397 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * @ignore + */ +ccs.PT_RATIO = 32; + +/** + * Base class for ccs.ColliderFilter + * @class + * @extends ccs.Class + */ +ccs.ColliderFilter = ccs.Class.extend(/** @lends ccs.ColliderFilter# */{ + _collisionType: 0, + _group: 0, + _categoryBits: 0, + _groupIndex: 0, + _maskBits: 0, + + ctor: function (collisionType, group) { + this._collisionType = collisionType || 0; + this._group = group || 0; + }, + + updateShape: function (shape) { + if(shape instanceof cp.Shape){ + shape.collision_type = this._collisionType; + shape.group = this._group; + }else if(shape instanceof Box2D.b2FilterData){ + var filter = new Box2D.b2FilterData(); + filter.categoryBits = this._categoryBits; + filter.groupIndex = this._groupIndex; + filter.maskBits = this._maskBits; + + shape.SetFilterData(filter); + } + } +}); + +/** + * Base class for ccs.ColliderBody + * @class + * @extends ccs.Class + * + * @property {ccs.ContourData} contourData - The contour data of collider body + * @property {ccs.Shape} shape - The shape of collider body + * @property {ccs.ColliderFilter} colliderFilter - The collider filter of collider body + * + */ +ccs.ColliderBody = ccs.Class.extend(/** @lends ccs.ColliderBody# */{ + shape: null, + coutourData: null, + colliderFilter: null, + _calculatedVertexList: null, + ctor: function (contourData) { + this.shape = null; + this.coutourData = contourData; + this.colliderFilter = new ccs.ColliderFilter(); + if (ccs.ENABLE_PHYSICS_SAVE_CALCULATED_VERTEX) { + this._calculatedVertexList = []; + } + }, + + /** + * contourData getter + * @returns {ccs.ContourData} + */ + getContourData: function () { + return this.coutourData; + }, + + /** + * colliderFilter setter + * @param {ccs.ColliderFilter} colliderFilter + */ + setColliderFilter: function (colliderFilter) { + this.colliderFilter = colliderFilter; + }, + + /** + * get calculated vertex list + * @returns {Array} + */ + getCalculatedVertexList: function () { + return this._calculatedVertexList; + }, + + setB2Fixture: function(fixture){ + this._fixture = fixture; + }, + + getB2Fixture: function(){ + return this._fixture; + }, + + /** + * shape getter + * @param {ccs.Shape} shape + */ + setShape: function (shape) { + this.shape = shape; + }, + + /** + * shape setter + * @return {ccs.Shape} + */ + getShape: function () { + return this.shape; + }, + + /** + * contourData setter + * @param {ccs.ContourData} contourData + */ + setContourData: function (contourData) { + this.coutourData = contourData; + }, + + /** + * colliderFilter getter + * @returns {ccs.ColliderFilter} + */ + getColliderFilter: function () { + return this.colliderFilter; + } +}); + +/** + * Base class for ccs.ColliderDetector + * @class + * @extends ccs.Class + * + * @param {ccs.Bone} [bone] + * + * @property {ccs.ColliderFilter} colliderFilter - The collider filter of the collider detector + * @property {Boolean} active - Indicate whether the collider detector is active + * @property {Object} body - The collider body + */ +ccs.ColliderDetector = ccs.Class.extend(/** @lends ccs.ColliderDetector# */{ + _colliderBodyList: null, + _bone: null, + _body: null, + _active: false, + _filter: null, + helpPoint: cc.p(0, 0), + + ctor: function (bone) { + this._colliderBodyList = []; + this._bone = null; + this._body = null; + this._active = false; + this._filter = null; + + ccs.ColliderDetector.prototype.init.call(this, bone); + }, + init: function (bone) { + this._colliderBodyList.length = 0; + if (bone) + this._bone = bone; + this._filter = new ccs.ColliderFilter(); + return true; + }, + + /** + * add contourData + * @param {ccs.ContourData} contourData + */ + addContourData: function (contourData) { + var colliderBody = new ccs.ColliderBody(contourData); + this._colliderBodyList.push(colliderBody); + + if (ccs.ENABLE_PHYSICS_SAVE_CALCULATED_VERTEX) { + var calculatedVertexList = colliderBody.getCalculatedVertexList(); + var vertexList = contourData.vertexList; + for (var i = 0; i < vertexList.length; i++) { + var newVertex = new ccs.ContourVertex2(0, 0); + calculatedVertexList.push(newVertex); + } + } + }, + + /** + * add contourData + * @param {Array} contourDataList + */ + addContourDataList: function (contourDataList) { + for (var i = 0; i < contourDataList.length; i++) { + this.addContourData(contourDataList[i]); + } + }, + + /** + * remove contourData + * @param contourData + */ + removeContourData: function (contourData) { + var eraseList = [], i, locBodyList = this._colliderBodyList; + for (i = 0; i < locBodyList.length; i++) { + var body = locBodyList[i]; + if (body && body.getContourData() === contourData) + eraseList.push(body); + } + + for (i=0; igetB2Fixture()->GetShape(); + shape = colliderBody.getShape(); + } + + var vs = contourData.vertexList; + var cvs = colliderBody.getCalculatedVertexList(); + + for (var j = 0; j < vs.length; j++) { + locHelpPoint.x = vs[j].x; + locHelpPoint.y = vs[j].y; + locHelpPoint = cc.pointApplyAffineTransform(locHelpPoint, t); + + if (ccs.ENABLE_PHYSICS_SAVE_CALCULATED_VERTEX) { + var v = cc.p(0, 0); + v.x = locHelpPoint.x; + v.y = locHelpPoint.y; + cvs[j] = v; + } + + if (shape) { + shape.verts[j * 2] = locHelpPoint.x; + shape.verts[j * 2 + 1] = locHelpPoint.y; + } + } + if (shape) { + for (var j = 0; j < vs.length; j++) { + var b = shape.verts[(j + 1) % shape.verts.length]; + var n = cp.v.normalize(cp.v.perp(cp.v.sub(b, shape.verts[j]))); + + if(shape.planes){ + shape.planes[j].n = n; + shape.planes[j].d = cp.v.dot(n, shape.verts[j]); + } +// var b = shape.verts[(i + 1) % shape.numVerts]; +// var n = cp.v.normalize(cp.v.perp(cp.v.sub(b, shape.verts[i]))); +// +// shape.planes[i].n = n; +// shape.planes[i].d = cp.v.dot(n, shape.verts[i]); + } + } + } + }, + + setBody: function (body) { + this._body = body; + var colliderBody, locBodyList = this._colliderBodyList; + for (var i = 0; i < locBodyList.length; i++) { + colliderBody = locBodyList[i]; + var contourData = colliderBody.getContourData(), verts = []; + var vs = contourData.vertexList; + for (var j = 0; j < vs.length; j++) { + var v = vs[j]; + verts.push(v.x); + verts.push(v.y); + } + var shape = new cp.PolyShape(this._body, verts, cp.vzero); + shape.sensor = true; + shape.data = this._bone; + if (this._active) + this._body.space.addShape(shape); + colliderBody.setShape(shape); + colliderBody.getColliderFilter().updateShape(shape); + } + }, + + getBody: function () { + return this._body; + } +}); + +var _p = ccs.ColliderDetector.prototype; + +// Extended properties +/** @expose */ +_p.colliderFilter; +cc.defineGetterSetter(_p, "colliderFilter", _p.getColliderFilter, _p.setColliderFilter); +/** @expose */ +_p.active; +cc.defineGetterSetter(_p, "active", _p.getActive, _p.setActive); +/** @expose */ +_p.body; +cc.defineGetterSetter(_p, "body", _p.getBody, _p.setBody); + +_p = null; + +ccs.ColliderDetector.create = function (bone) { + return new ccs.ColliderDetector(bone); +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/armature/utils/CCArmatureDataManager.js b/frameworks/cocos2d-html5/extensions/cocostudio/armature/utils/CCArmatureDataManager.js new file mode 100644 index 0000000..ca901bb --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/armature/utils/CCArmatureDataManager.js @@ -0,0 +1,329 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * RelativeData uses to save plist files, armature files, animations and textures for armature data manager. + * @constructor + */ +ccs.RelativeData = function () { + this.plistFiles = []; + this.armatures = []; + this.animations = []; + this.textures = []; +}; + +/** + * ccs.armatureDataManager is a singleton object which format and manage armature configuration and armature animation + * @class + * @name ccs.armatureDataManager + */ +ccs.armatureDataManager = /** @lends ccs.armatureDataManager# */ { + _animationDatas: {}, + _armatureDatas: {}, + _textureDatas: {}, + _autoLoadSpriteFile: false, + _relativeDatas: {}, + + s_sharedArmatureDataManager: null, + + /** + * Removes armature cache data by configFilePath + * @param {String} configFilePath + */ + removeArmatureFileInfo: function (configFilePath) { + var data = this.getRelativeData(configFilePath); + if (data) { + var i, obj; + for (i = 0; i < data.armatures.length; i++) { + obj = data.armatures[i]; + this.removeArmatureData(obj); + } + for (i = 0; i < data.animations.length; i++) { + obj = data.animations[i]; + this.removeAnimationData(obj); + } + for (i = 0; i < data.textures.length; i++) { + obj = data.textures[i]; + this.removeTextureData(obj); + } + for (i = 0; i < data.plistFiles.length; i++) { + obj = data.plistFiles[i]; + cc.spriteFrameCache.removeSpriteFramesFromFile(obj); + } + delete this._relativeDatas[configFilePath]; + ccs.dataReaderHelper.removeConfigFile(configFilePath); + } + }, + + /** + * Adds armature data + * @param {string} id The id of the armature data + * @param {ccs.ArmatureData} armatureData + */ + addArmatureData: function (id, armatureData, configFilePath) { + var data = this.getRelativeData(configFilePath); + if (data) { + data.armatures.push(id); + } + this._armatureDatas[id] = armatureData; + }, + + /** + * Gets armatureData by id + * @param {String} id + * @return {ccs.ArmatureData} + */ + getArmatureData: function (id) { + var armatureData = null; + if (this._armatureDatas) { + armatureData = this._armatureDatas[id]; + } + return armatureData; + }, + + /** + * Removes armature data from armature data manager. + * @param {string} id + */ + removeArmatureData: function (id) { + if (this._armatureDatas[id]) + delete this._armatureDatas[id]; + }, + + /** + * Adds animation data to armature data manager. + * @param {String} id + * @param {ccs.AnimationData} animationData + */ + addAnimationData: function (id, animationData, configFilePath) { + var data = this.getRelativeData(configFilePath); + if (data) + data.animations.push(id); + this._animationDatas[id] = animationData; + }, + + /** + * Gets animationData by id + * @param {String} id + * @return {ccs.AnimationData} + */ + getAnimationData: function (id) { + var animationData = null; + if (this._animationDatas[id]) { + animationData = this._animationDatas[id]; + } + return animationData; + }, + + /** + * Removes animation data + * @param {string} id + */ + removeAnimationData: function (id) { + if (this._animationDatas[id]) + delete this._animationDatas[id]; + }, + + /** + * Adds texture data to Armature data manager. + * @param {String} id + * @param {ccs.TextureData} textureData + */ + addTextureData: function (id, textureData, configFilePath) { + var data = this.getRelativeData(configFilePath); + if (data) { + data.textures.push(id); + } + this._textureDatas[id] = textureData; + }, + + /** + * Gets textureData by id + * @param {String} id + * @return {ccs.TextureData} + */ + getTextureData: function (id) { + var textureData = null; + if (this._textureDatas) { + textureData = this._textureDatas[id]; + } + return textureData; + }, + + /** + * Removes texture data by id + * @param {string} id + */ + removeTextureData: function (id) { + if (this._textureDatas[id]) + delete this._textureDatas[id]; + }, + + /** + * Adds ArmatureFileInfo, it is managed by CCArmatureDataManager. + * @param {String} imagePath + * @param {String} plistPath + * @param {String} configFilePath + * @example + * //example1 + * ccs.armatureDataManager.addArmatureFileInfo("res/test.json"); + * //example2 + * ccs.armatureDataManager.addArmatureFileInfo("res/test.png","res/test.plist","res/test.json"); + */ + addArmatureFileInfo: function ( /*imagePath, plistPath, configFilePath*/ ) { + var imagePath, plistPath, configFilePath; + switch (arguments.length) { + case 1: + configFilePath = arguments[0]; + + this.addRelativeData(configFilePath); + + this._autoLoadSpriteFile = true; + ccs.dataReaderHelper.addDataFromFile(configFilePath); + break; + case 3: + imagePath = arguments[0]; + plistPath = arguments[1]; + configFilePath = arguments[2]; + + this.addRelativeData(configFilePath); + + this._autoLoadSpriteFile = false; + ccs.dataReaderHelper.addDataFromFile(configFilePath); + this.addSpriteFrameFromFile(plistPath, imagePath); + } + }, + + /** + * Adds ArmatureFileInfo, it is managed by CCArmatureDataManager. + * @param {String} imagePath + * @param {String} plistPath + * @param {String} configFilePath + * @param {Function} selector + * @param {Object} target + */ + addArmatureFileInfoAsync: function ( /*imagePath, plistPath, configFilePath, selector, target*/ ) { + var imagePath, plistPath, configFilePath, target, selector; + switch (arguments.length) { + case 3: + configFilePath = arguments[0]; + target = arguments[2]; + selector = arguments[1]; + this.addRelativeData(configFilePath); + this._autoLoadSpriteFile = true; + ccs.dataReaderHelper.addDataFromFileAsync("", "", configFilePath, selector, target); + break; + case 5: + imagePath = arguments[0]; + plistPath = arguments[1]; + configFilePath = arguments[2]; + target = arguments[4]; + selector = arguments[3]; + this.addRelativeData(configFilePath); + + this._autoLoadSpriteFile = false; + ccs.dataReaderHelper.addDataFromFileAsync(imagePath, plistPath, configFilePath, selector, target); + this.addSpriteFrameFromFile(plistPath, imagePath); + } + }, + + /** + * Add sprite frame to CCSpriteFrameCache, it will save display name and it's relative image name + * @param {String} plistPath + * @param {String} imagePath + * @param {String} configFilePath + */ + addSpriteFrameFromFile: function (plistPath, imagePath, configFilePath) { + var data = this.getRelativeData(configFilePath); + if (data) + data.plistFiles.push(plistPath); + ccs.spriteFrameCacheHelper.addSpriteFrameFromFile(plistPath, imagePath); + }, + + /** + * Returns whether or not need auto load sprite file + * @returns {boolean} + */ + isAutoLoadSpriteFile: function () { + return this._autoLoadSpriteFile; + }, + + /** + * Returns armature Data of Armature data manager. + * @return {Object} + */ + getArmatureDatas: function () { + return this._armatureDatas; + }, + + /** + * Returns animation data of Armature data manager. + * @return {Object} + */ + getAnimationDatas: function () { + return this._animationDatas; + }, + + /** + * Returns texture data of Armature data manager. + * @return {Object} + */ + getTextureDatas: function () { + return this._textureDatas; + }, + + /** + * Adds RelativeData of Armature data manager. + * @param {String} configFilePath + */ + addRelativeData: function (configFilePath) { + if (!this._relativeDatas[configFilePath]) + this._relativeDatas[configFilePath] = new ccs.RelativeData(); + }, + + /** + * Gets RelativeData of Armature data manager. + * @param {String} configFilePath + * @returns {ccs.RelativeData} + */ + getRelativeData: function (configFilePath) { + return this._relativeDatas[configFilePath]; + }, + + /** + * Clear data + */ + clear: function () { + for (var key in this._relativeDatas) { + this.removeArmatureFileInfo(key); + } + this._animationDatas = {}; + this._armatureDatas = {}; + this._textureDatas = {}; + this._relativeDatas = {}; + ccs.spriteFrameCacheHelper.clear(); + ccs.dataReaderHelper.clear(); + } +}; diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/armature/utils/CCArmatureDefine.js b/frameworks/cocos2d-html5/extensions/cocostudio/armature/utils/CCArmatureDefine.js new file mode 100644 index 0000000..a7db1b9 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/armature/utils/CCArmatureDefine.js @@ -0,0 +1,45 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ +/** + * @ignore + */ +ccs.VERSION_COMBINED = 0.30; +ccs.VERSION_CHANGE_ROTATION_RANGE = 1.0; +ccs.VERSION_COLOR_READING = 1.1; +ccs.MAX_VERTEXZ_VALUE = 5000000.0; +ccs.ARMATURE_MAX_CHILD = 50.0; +ccs.ARMATURE_MAX_ZORDER = 100; +ccs.ARMATURE_MAX_COUNT = ((ccs.MAX_VERTEXZ_VALUE) / (ccs.ARMATURE_MAX_CHILD) / ccs.ARMATURE_MAX_ZORDER); +ccs.AUTO_ADD_SPRITE_FRAME_NAME_PREFIX = false; +ccs.ENABLE_PHYSICS_CHIPMUNK_DETECT = false; +ccs.ENABLE_PHYSICS_SAVE_CALCULATED_VERTEX = false; + +/** + * Returns the version of Armature. + * @returns {string} + */ +ccs.armatureVersion = function(){ + return "v1.1.0.0"; +}; diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/armature/utils/CCDataReaderHelper.js b/frameworks/cocos2d-html5/extensions/cocostudio/armature/utils/CCDataReaderHelper.js new file mode 100644 index 0000000..b029fb2 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/armature/utils/CCDataReaderHelper.js @@ -0,0 +1,1223 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * @ignore + */ +ccs.CONST_VERSION = "version"; +ccs.CONST_VERSION_2_0 = 2.0; +ccs.CONST_VERSION_COMBINED = 0.3; + +ccs.CONST_ARMATURES = "armatures"; +ccs.CONST_ARMATURE = "armature"; +ccs.CONST_BONE = "b"; +ccs.CONST_DISPLAY = "d"; + +ccs.CONST_ANIMATIONS = "animations"; +ccs.CONST_ANIMATION = "animation"; +ccs.CONST_MOVEMENT = "mov"; +ccs.CONST_FRAME = "f"; + +ccs.CONST_TEXTURE_ATLAS = "TextureAtlas"; +ccs.CONST_SUB_TEXTURE = "SubTexture"; + +ccs.CONST_SKELETON = "skeleton"; + +ccs.CONST_A_NAME = "name"; +ccs.CONST_A_DURATION = "dr"; +ccs.CONST_A_FRAME_INDEX = "fi"; +ccs.CONST_A_DURATION_TO = "to"; +ccs.CONST_A_DURATION_TWEEN = "drTW"; +ccs.CONST_A_LOOP = "lp"; +ccs.CONST_A_MOVEMENT_SCALE = "sc"; +ccs.CONST_A_MOVEMENT_DELAY = "dl"; +ccs.CONST_A_DISPLAY_INDEX = "dI"; + +ccs.CONST_A_PLIST = "plist"; + +ccs.CONST_A_PARENT = "parent"; +ccs.CONST_A_SKEW_X = "kX"; +ccs.CONST_A_SKEW_Y = "kY"; +ccs.CONST_A_SCALE_X = "cX"; +ccs.CONST_A_SCALE_Y = "cY"; +ccs.CONST_A_Z = "z"; +ccs.CONST_A_EVENT = "evt"; +ccs.CONST_A_SOUND = "sd"; +ccs.CONST_A_SOUND_EFFECT = "sdE"; +ccs.CONST_A_TWEEN_EASING = "twE"; +ccs.CONST_A_EASING_PARAM = "twEP"; +ccs.CONST_A_TWEEN_ROTATE = "twR"; +ccs.CONST_A_IS_ARMATURE = "isArmature"; +ccs.CONST_A_DISPLAY_TYPE = "displayType"; +ccs.CONST_A_MOVEMENT = "mov"; + +ccs.CONST_A_X = "x"; +ccs.CONST_A_Y = "y"; + +ccs.CONST_A_COCOS2DX_X = "cocos2d_x"; +ccs.CONST_A_COCOS2DX_Y = "cocos2d_y"; + +ccs.CONST_A_WIDTH = "width"; +ccs.CONST_A_HEIGHT = "height"; +ccs.CONST_A_PIVOT_X = "pX"; +ccs.CONST_A_PIVOT_Y = "pY"; + +ccs.CONST_A_COCOS2D_PIVOT_X = "cocos2d_pX"; +ccs.CONST_A_COCOS2D_PIVOT_Y = "cocos2d_pY"; + +ccs.CONST_A_BLEND_TYPE = "bd"; +ccs.CONST_A_BLEND_SRC = "bd_src"; +ccs.CONST_A_BLEND_DST = "bd_dst"; + +ccs.CONST_A_ALPHA = "a"; +ccs.CONST_A_RED = "r"; +ccs.CONST_A_GREEN = "g"; +ccs.CONST_A_BLUE = "b"; +ccs.CONST_A_ALPHA_OFFSET = "aM"; +ccs.CONST_A_RED_OFFSET = "rM"; +ccs.CONST_A_GREEN_OFFSET = "gM"; +ccs.CONST_A_BLUE_OFFSET = "bM"; +ccs.CONST_A_COLOR_TRANSFORM = "colorTransform"; +ccs.CONST_A_TWEEN_FRAME = "tweenFrame"; + +ccs.CONST_CONTOUR = "con"; +ccs.CONST_CONTOUR_VERTEX = "con_vt"; + +ccs.CONST_FL_NAN = "NaN"; + +ccs.CONST_FRAME_DATA = "frame_data"; +ccs.CONST_MOVEMENT_BONE_DATA = "mov_bone_data"; +ccs.CONST_MOVEMENT_DATA = "mov_data"; +ccs.CONST_ANIMATION_DATA = "animation_data"; +ccs.CONST_DISPLAY_DATA = "display_data"; +ccs.CONST_SKIN_DATA = "skin_data"; +ccs.CONST_BONE_DATA = "bone_data"; +ccs.CONST_ARMATURE_DATA = "armature_data"; +ccs.CONST_CONTOUR_DATA = "contour_data"; +ccs.CONST_TEXTURE_DATA = "texture_data"; +ccs.CONST_VERTEX_POINT = "vertex"; +ccs.CONST_COLOR_INFO = "color"; + +ccs.CONST_CONFIG_FILE_PATH = "config_file_path"; +ccs.CONST_CONTENT_SCALE = "content_scale"; + +/** + * @ignore + * @constructor + */ +ccs.DataInfo = function () { + this.asyncStruct = null; + this.configFileQueue = []; + this.contentScale = 1; + this.filename = ""; + this.baseFilePath = ""; + this.flashToolVersion = 0; + this.cocoStudioVersion = 0 +}; + +/** + * ccs.dataReaderHelper is a singleton object for reading CocoStudio data + * @class + * @name ccs.dataReaderHelper + */ +ccs.dataReaderHelper = /** @lends ccs.dataReaderHelper# */{ + ConfigType: { + DragonBone_XML: 0, + CocoStudio_JSON: 1, + CocoStudio_Binary: 2 + }, + + _configFileList: [], + _flashToolVersion: ccs.CONST_VERSION_2_0, +// _cocoStudioVersion: ccs.CONST_VERSION_COMBINED, + _positionReadScale: 1, + _asyncRefCount: 0, + _asyncRefTotalCount: 0, + + _dataQueue: null, + + //LoadData don't need + + setPositionReadScale: function (scale) { + this._positionReadScale = scale; + }, + + getPositionReadScale: function () { + return this._positionReadScale; + }, + + /** + * Add armature data from file. + * @param {String} filePath + */ + addDataFromFile: function (filePath) { + /* + * Check if file is already added to ArmatureDataManager, if then return. + */ + if (this._configFileList.indexOf(filePath) !== -1) + return; + this._configFileList.push(filePath); + + //! find the base file path + var basefilePath = this._initBaseFilePath(filePath); + + // Read content from file + // Here the reader into the next process + + var str = cc.path.extname(filePath).toLowerCase(); + + var dataInfo = new ccs.DataInfo(); + dataInfo.filename = filePath; + dataInfo.basefilePath = basefilePath; + if (str === ".xml") + ccs.dataReaderHelper.addDataFromXML(filePath, dataInfo); + else if (str === ".json" || str === ".exportjson") + ccs.dataReaderHelper.addDataFromJson(filePath, dataInfo); + else if(str === ".csb") + ccs.dataReaderHelper.addDataFromBinaryCache(filePath, dataInfo); + }, + + /** + * Adds data from file with Async. + * @param {String} imagePath + * @param {String} plistPath + * @param {String} filePath + * @param {function} selector + * @param {Object} [target] + */ + addDataFromFileAsync: function (imagePath, plistPath, filePath, selector, target) { + /* + * Check if file is already added to ArmatureDataManager, if then return. + */ + if (this._configFileList.indexOf(filePath) !== -1) { + if (target && selector) { + if (this._asyncRefTotalCount === 0 && this._asyncRefCount === 0) + this._asyncCallBack(selector,target, 1); + else + this._asyncCallBack(selector, target, (this._asyncRefTotalCount - this._asyncRefCount) / this._asyncRefTotalCount); + } + return; + } +// this._configFileList.push(filePath); + + //! find the base file path +// var basefilePath = this._initBaseFilePath(filePath); + + this._asyncRefTotalCount++; + this._asyncRefCount++; + var self = this; + var fun = function () { + self.addDataFromFile(filePath); + self._asyncRefCount--; + self._asyncCallBack(selector,target, (self._asyncRefTotalCount - self._asyncRefCount) / self._asyncRefTotalCount); + }; + cc.director.getScheduler().schedule(fun, this, 0.1, false, 0, false, "armatrueDataHelper"); + }, + + /** + * Removes config file from config file list. + * @param {String} configFile + */ + removeConfigFile: function (configFile) { +// cc.arrayRemoveObject(this._configFileList, configFile); + var locFileList = this._configFileList; + var len = locFileList.length; + var it = locFileList[len]; + for (var i = 0;i " + ccs.CONST_ARMATURES + " > " + ccs.CONST_ARMATURE + ""); + var armatureDataManager = ccs.armatureDataManager, i; + for (i = 0; i < armaturesXML.length; i++) { + var armatureData = this.decodeArmature(armaturesXML[i], dataInfo); + armatureDataManager.addArmatureData(armatureData.name, armatureData, dataInfo.filename); + } + + /* + * Begin decode animation data from xml + */ + var animationsXML = skeleton.querySelectorAll(ccs.CONST_SKELETON + " > " + ccs.CONST_ANIMATIONS + " > " + ccs.CONST_ANIMATION + ""); + for (i = 0; i < animationsXML.length; i++) { + var animationData = this.decodeAnimation(animationsXML[i], dataInfo); + armatureDataManager.addAnimationData(animationData.name, animationData, dataInfo.filename); + } + + var texturesXML = skeleton.querySelectorAll(ccs.CONST_SKELETON + " > " + ccs.CONST_TEXTURE_ATLAS + " > " + ccs.CONST_SUB_TEXTURE + ""); + for (i = 0; i < texturesXML.length; i++) { + var textureData = this.decodeTexture(texturesXML[i], dataInfo); + armatureDataManager.addTextureData(textureData.name, textureData, dataInfo.filename); + } + }, + + /** + * decode xml armature data. + * @param {XMLDocument} armatureXML + * @param {ccs.DataInfo} dataInfo + * @returns {ccs.ArmatureData} + */ + decodeArmature: function (armatureXML, dataInfo) { + var armatureData = new ccs.ArmatureData(); + armatureData.init(); + armatureData.name = armatureXML.getAttribute(ccs.CONST_A_NAME); + + var bonesXML = armatureXML.querySelectorAll(ccs.CONST_ARMATURE + " > " + ccs.CONST_BONE); + + for (var i = 0; i < bonesXML.length; i++) { + /* + * If this bone have parent, then get the parent bone xml + */ + var boneXML = bonesXML[i]; + var parentName = boneXML.getAttribute(ccs.CONST_A_PARENT); + var parentXML = null; + if (parentName) { + //parentXML = armatureXML.querySelectorAll(ccs.CONST_ARMATURE+" > "+ccs.CONST_BONE); + for (var j = 0; j < bonesXML.length; j++) { + parentXML = bonesXML[j]; + if (parentName == bonesXML[j].getAttribute(ccs.CONST_A_NAME)) { + //todo + break; + } + } + } + var boneData = this.decodeBone(boneXML, parentXML, dataInfo); + armatureData.addBoneData(boneData); + } + return armatureData; + }, + + /** + * decode json armature data. + * @param {Object} json + * @param {ccs.DataInfo} dataInfo + * @returns {ccs.ArmatureData} + */ + decodeArmatureFromJSON: function (json, dataInfo) { + var armatureData = new ccs.ArmatureData(); + armatureData.init(); + + var name = json[ccs.CONST_A_NAME]; + if (name) { + armatureData.name = name; + } + + dataInfo.cocoStudioVersion = armatureData.dataVersion = json[ccs.CONST_VERSION] || 0.1; + + var boneDataList = json[ccs.CONST_BONE_DATA]; + for (var i = 0; i < boneDataList.length; i++) { + var boneData = this.decodeBoneFromJson(boneDataList[i], dataInfo); + armatureData.addBoneData(boneData); + } + return armatureData; + }, + + /** + * decode xml bone data. + * @param {XMLDocument} boneXML + * @param {XMLDocument} parentXML + * @param {ccs.DataInfo} dataInfo + * @returns {ccs.BoneData} + */ + decodeBone: function (boneXML, parentXML, dataInfo) { + var boneData = new ccs.BoneData(); + boneData.init(); + + boneData.name = boneXML.getAttribute(ccs.CONST_A_NAME); + boneData.parentName = boneXML.getAttribute(ccs.CONST_A_PARENT) || ""; + + boneData.zOrder = parseInt(boneXML.getAttribute(ccs.CONST_A_Z)) || 0; + + var displaysXML = boneXML.querySelectorAll(ccs.CONST_BONE + " > " + ccs.CONST_DISPLAY); + for (var i = 0; i < displaysXML.length; i++) { + var displayXML = displaysXML[i]; + var displayData = this.decodeBoneDisplay(displayXML, dataInfo); + boneData.addDisplayData(displayData); + } + return boneData; + }, + + /** + * decode json bone data. + * @param {Object} json json bone data. + * @param {ccs.DataInfo} dataInfo + * @returns {ccs.BoneData} + */ + decodeBoneFromJson: function (json, dataInfo) { + var boneData = new ccs.BoneData(); + boneData.init(); + + this.decodeNodeFromJson(boneData, json, dataInfo); + + boneData.name = json[ccs.CONST_A_NAME] || ""; + + boneData.parentName = json[ccs.CONST_A_PARENT] || ""; + var displayDataList = json[ccs.CONST_DISPLAY_DATA] || []; + for (var i = 0; i < displayDataList.length; i++) { + var locDisplayData = this.decodeBoneDisplayFromJson(displayDataList[i], dataInfo); + boneData.addDisplayData(locDisplayData); + } + return boneData; + }, + + /** + * decode xml display data of bone + * @param {XMLDocument} displayXML + * @param {ccs.DataInfo} dataInfo + * @returns {ccs.DisplayData} + */ + decodeBoneDisplay: function (displayXML, dataInfo) { + var isArmature = parseFloat(displayXML.getAttribute(ccs.CONST_A_IS_ARMATURE)) || 0; + var displayData = null; + + if (isArmature === 1) { + displayData = new ccs.ArmatureDisplayData(); + displayData.displayType = ccs.DISPLAY_TYPE_ARMATURE; + } else { + displayData = new ccs.SpriteDisplayData(); + displayData.displayType = ccs.DISPLAY_TYPE_SPRITE; + } + + var displayName = displayXML.getAttribute(ccs.CONST_A_NAME) || ""; + if (displayName) { + displayData.displayName = displayName; + } + return displayData; + }, + + /** + * Decodes json display data of bone. + * @param {Object} json + * @param {ccs.DataInfo} dataInfo + * @returns {ccs.DisplayData} + */ + decodeBoneDisplayFromJson: function (json, dataInfo) { + var displayType = json[ccs.CONST_A_DISPLAY_TYPE] || ccs.DISPLAY_TYPE_SPRITE; + var displayData = null; + + switch (displayType) { + case ccs.DISPLAY_TYPE_SPRITE: + displayData = new ccs.SpriteDisplayData(); + + var name = json[ccs.CONST_A_NAME]; + if(name != null){ + displayData.displayName = name; + } + + var dicArray = json[ccs.CONST_SKIN_DATA] || []; + var dic = dicArray[0]; + if (dic) { + var skinData = displayData.skinData; + skinData.x = dic[ccs.CONST_A_X] * this._positionReadScale; + skinData.y = dic[ccs.CONST_A_Y] * this._positionReadScale; + skinData.scaleX = dic[ccs.CONST_A_SCALE_X] == null ? 1 : dic[ccs.CONST_A_SCALE_X]; + skinData.scaleY = dic[ccs.CONST_A_SCALE_Y] == null ? 1 : dic[ccs.CONST_A_SCALE_Y]; + skinData.skewX = dic[ccs.CONST_A_SKEW_X] == null ? 1 : dic[ccs.CONST_A_SKEW_X]; + skinData.skewY = dic[ccs.CONST_A_SKEW_Y] == null ? 1 : dic[ccs.CONST_A_SKEW_Y]; + + skinData.x *= dataInfo.contentScale; + skinData.y *= dataInfo.contentScale; + } + break; + case ccs.DISPLAY_TYPE_ARMATURE: + displayData = new ccs.ArmatureDisplayData(); + var name = json[ccs.CONST_A_NAME]; + if(name != null){ + displayData.displayName = json[ccs.CONST_A_NAME]; + } + break; + case ccs.DISPLAY_TYPE_PARTICLE: + displayData = new ccs.ParticleDisplayData(); + var plist = json[ccs.CONST_A_PLIST]; + if(plist != null){ + if(dataInfo.asyncStruct){ + displayData.displayName = dataInfo.asyncStruct.basefilePath + plist; + }else{ + displayData.displayName = dataInfo.basefilePath + plist; + } + } + break; + default: + displayData = new ccs.SpriteDisplayData(); + break; + } + displayData.displayType = displayType; + return displayData; + }, + + /** + * Decodes xml animation data. + * @param {XMLDocument} animationXML + * @param {ccs.DataInfo} dataInfo + * @returns {ccs.AnimationData} + */ + decodeAnimation: function (animationXML, dataInfo) { + var aniData = new ccs.AnimationData(); + var name = animationXML.getAttribute(ccs.CONST_A_NAME); + var armatureData = ccs.armatureDataManager.getArmatureData(name); + aniData.name = name; + + var movementsXML = animationXML.querySelectorAll(ccs.CONST_ANIMATION + " > " + ccs.CONST_MOVEMENT); + var movementXML = null; + + for (var i = 0; i < movementsXML.length; i++) { + movementXML = movementsXML[i]; + var movementData = this.decodeMovement(movementXML, armatureData, dataInfo); + aniData.addMovement(movementData); + } + return aniData; + }, + + /** + * Decodes animation json data. + * @param {Object} json + * @param {ccs.DataInfo} dataInfo + * @returns {ccs.AnimationData} + */ + decodeAnimationFromJson: function (json, dataInfo) { + var aniData = new ccs.AnimationData(); + var name = json[ccs.CONST_A_NAME]; + if(name){ + aniData.name = json[ccs.CONST_A_NAME]; + } + + var movementDataList = json[ccs.CONST_MOVEMENT_DATA] || []; + for (var i = 0; i < movementDataList.length; i++) { + var locMovementData = this.decodeMovementFromJson(movementDataList[i], dataInfo); + aniData.addMovement(locMovementData); + } + return aniData; + }, + + /** + * Decodes xml movement data. + * @param {XMLDocument} movementXML + * @param {ccs.ArmatureData} armatureData + * @param {ccs.DataInfo} dataInfo + * @returns {ccs.MovementData} + */ + decodeMovement: function (movementXML, armatureData, dataInfo) { + var movementData = new ccs.MovementData(); + movementData.name = movementXML.getAttribute(ccs.CONST_A_NAME); + + var duration, durationTo, durationTween, loop, tweenEasing = 0; + + duration = movementXML.getAttribute(ccs.CONST_A_DURATION); + movementData.duration = duration == null ? 0 : parseFloat(duration); + + durationTo = movementXML.getAttribute(ccs.CONST_A_DURATION_TO); + movementData.durationTo = durationTo == null ? 0 : parseFloat(durationTo); + + durationTween = movementXML.getAttribute(ccs.CONST_A_DURATION_TWEEN); + movementData.durationTween = durationTween == null ? 0 : parseFloat(durationTween); + + loop = movementXML.getAttribute(ccs.CONST_A_LOOP); + movementData.loop = loop ? Boolean(parseFloat(loop)) : true; + + var easing = movementXML.getAttribute(ccs.CONST_A_TWEEN_EASING); + if (easing) { + if (easing != ccs.CONST_FL_NAN) { + tweenEasing = easing == null ? 0 : parseFloat(easing); + movementData.tweenEasing = tweenEasing === 2 ? ccs.TweenType.SINE_EASEINOUT : tweenEasing; + } else + movementData.tweenEasing = ccs.TweenType.LINEAR; + } + + var movBonesXml = movementXML.querySelectorAll(ccs.CONST_MOVEMENT + " > " + ccs.CONST_BONE); + var movBoneXml = null; + for (var i = 0; i < movBonesXml.length; i++) { + movBoneXml = movBonesXml[i]; + var boneName = movBoneXml.getAttribute(ccs.CONST_A_NAME); + + if (movementData.getMovementBoneData(boneName)) + continue; + + var boneData = armatureData.getBoneData(boneName); + var parentName = boneData.parentName; + + var parentXML = null; + if (parentName !== "") { + for (var j = 0; j < movBonesXml.length; j++) { + parentXML = movBonesXml[j]; + if (parentName === parentXML.getAttribute(ccs.CONST_A_NAME)) + break; + } + } + var moveBoneData = this.decodeMovementBone(movBoneXml, parentXML, boneData, dataInfo); + movementData.addMovementBoneData(moveBoneData); + } + return movementData; + }, + + /** + * Decodes json movement data. + * @param {Object} json + * @param {ccs.DataInfo} dataInfo + * @returns {ccs.MovementData} + */ + decodeMovementFromJson: function (json, dataInfo) { + var movementData = new ccs.MovementData(); + + movementData.loop = json[ccs.CONST_A_LOOP] == null ? false : json[ccs.CONST_A_LOOP]; + movementData.durationTween = json[ccs.CONST_A_DURATION_TWEEN] || 0; + movementData.durationTo = json[ccs.CONST_A_DURATION_TO] || 0; + movementData.duration = json[ccs.CONST_A_DURATION] || 0; + + if(json[ccs.CONST_A_DURATION] == null){ + movementData.scale = 1; + }else{ + movementData.scale = json[ccs.CONST_A_MOVEMENT_SCALE] == null ? 1 : json[ccs.CONST_A_MOVEMENT_SCALE]; + } + + movementData.tweenEasing = json[ccs.CONST_A_TWEEN_EASING] == null ? ccs.TweenType.LINEAR : json[ccs.CONST_A_TWEEN_EASING]; + var name = json[ccs.CONST_A_NAME]; + if(name) + movementData.name = name; + + var movementBoneList = json[ccs.CONST_MOVEMENT_BONE_DATA] || []; + for (var i = 0; i < movementBoneList.length; i++) { + var locMovementBoneData = this.decodeMovementBoneFromJson(movementBoneList[i], dataInfo); + movementData.addMovementBoneData(locMovementBoneData); + } + return movementData; + }, + + /** + * Decodes xml data of bone's movement. + * @param {XMLDocument} movBoneXml + * @param {XMLDocument} parentXml + * @param {ccs.BoneData} boneData + * @param {ccs.DataInfo} dataInfo + * @returns {ccs.MovementBoneData} + */ + decodeMovementBone: function (movBoneXml, parentXml, boneData, dataInfo) { + var movBoneData = new ccs.MovementBoneData(); + movBoneData.init(); + + var scale, delay; + if (movBoneXml) { + scale = parseFloat(movBoneXml.getAttribute(ccs.CONST_A_MOVEMENT_SCALE)) || 0; + movBoneData.scale = scale; + + delay = parseFloat(movBoneXml.getAttribute(ccs.CONST_A_MOVEMENT_DELAY)) || 0; + if (delay > 0) + delay -= 1; + movBoneData.delay = delay; + } + + var length = 0, parentTotalDuration = 0,currentDuration = 0; + var parentFrameXML = null,parentXMLList = []; + + /* + * get the parent frame xml list, we need get the origin data + */ + if (parentXml != null) { + var parentFramesXML = parentXml.querySelectorAll(ccs.CONST_BONE + " > " + ccs.CONST_FRAME); + for (var i = 0; i < parentFramesXML.length; i++) + parentXMLList.push(parentFramesXML[i]); + length = parentXMLList.length; + } + + movBoneData.name = movBoneXml.getAttribute(ccs.CONST_A_NAME); + + var framesXML = movBoneXml.querySelectorAll(ccs.CONST_BONE + " > " + ccs.CONST_FRAME); + + var j = 0, totalDuration = 0; + for (var ii = 0; ii < framesXML.length; ii++) { + var frameXML = framesXML[ii]; + if (parentXml) { + /* + * in this loop we get the corresponding parent frame xml + */ + while (j < length && (parentFrameXML ? (totalDuration < parentTotalDuration || totalDuration >= parentTotalDuration + currentDuration) : true)) { + parentFrameXML = parentXMLList[j]; + parentTotalDuration += currentDuration; + currentDuration = parseFloat(parentFrameXML.getAttribute(ccs.CONST_A_DURATION)); + j++; + } + } + var boneFrameData = this.decodeFrame(frameXML, parentFrameXML, boneData, dataInfo); + movBoneData.addFrameData(boneFrameData); + boneFrameData.frameID = totalDuration; + totalDuration += boneFrameData.duration; + movBoneData.duration = totalDuration; + } + + //Change rotation range from (-180 -- 180) to (-infinity -- infinity) + var frames = movBoneData.frameList, pi = Math.PI; + for (var i = frames.length - 1; i >= 0; i--) { + if (i > 0) { + var difSkewX = frames[i].skewX - frames[i - 1].skewX; + var difSkewY = frames[i].skewY - frames[i - 1].skewY; + + if (difSkewX < -pi || difSkewX > pi) { + frames[i - 1].skewX = difSkewX < 0 ? frames[i - 1].skewX - 2 * pi : frames[i - 1].skewX + 2 * pi; + } + + if (difSkewY < -pi || difSkewY > pi) { + frames[i - 1].skewY = difSkewY < 0 ? frames[i - 1].skewY - 2 * pi : frames[i - 1].skewY + 2 * pi; + } + } + } + + var frameData = new ccs.FrameData(); + frameData.copy(movBoneData.frameList[movBoneData.frameList.length - 1]); + frameData.frameID = movBoneData.duration; + movBoneData.addFrameData(frameData); + return movBoneData; + }, + + /** + * Decodes json data of bone's movement. + * @param {Object} json + * @param {ccs.DataInfo} dataInfo + * @returns {ccs.MovementBoneData} + */ + decodeMovementBoneFromJson: function (json, dataInfo) { + var movementBoneData = new ccs.MovementBoneData(); + movementBoneData.init(); + movementBoneData.delay = json[ccs.CONST_A_MOVEMENT_DELAY] || 0; + + var name = json[ccs.CONST_A_NAME]; + if(name) + movementBoneData.name = name; + + var framesData = json[ccs.CONST_FRAME_DATA] || []; + var length = framesData.length; + for (var i = 0; i < length; i++) { + var dic = json[ccs.CONST_FRAME_DATA][i]; + var frameData = this.decodeFrameFromJson(dic, dataInfo); + movementBoneData.addFrameData(frameData); + + if (dataInfo.cocoStudioVersion < ccs.CONST_VERSION_COMBINED){ + frameData.frameID = movementBoneData.duration; + movementBoneData.duration += frameData.duration; + } + } + + if (dataInfo.cocoStudioVersion < ccs.VERSION_CHANGE_ROTATION_RANGE) { + //! Change rotation range from (-180 -- 180) to (-infinity -- infinity) + var frames = movementBoneData.frameList; + var pi = Math.PI; + for (var i = frames.length - 1; i >= 0; i--) { + if (i > 0) { + var difSkewX = frames[i].skewX - frames[i - 1].skewX; + var difSkewY = frames[i].skewY - frames[i - 1].skewY; + + if (difSkewX < -pi || difSkewX > pi) { + frames[i - 1].skewX = difSkewX < 0 ? frames[i - 1].skewX - 2 * pi : frames[i - 1].skewX + 2 * pi; + } + + if (difSkewY < -pi || difSkewY > pi) { + frames[i - 1].skewY = difSkewY < 0 ? frames[i - 1].skewY - 2 * pi : frames[i - 1].skewY + 2 * pi; + } + } + } + } + + if (dataInfo.cocoStudioVersion < ccs.CONST_VERSION_COMBINED) { + if (movementBoneData.frameList.length > 0) { + var frameData = new ccs.FrameData(); + frameData.copy(movementBoneData.frameList[movementBoneData.frameList.length - 1]); + movementBoneData.addFrameData(frameData); + frameData.frameID = movementBoneData.duration; + } + } + return movementBoneData; + }, + + /** + * Decodes xml data of frame. + * @param {XMLDocument} frameXML + * @param {XMLDocument} parentFrameXml + * @param {ccs.BoneData} boneData + * @param {ccs.DataInfo} dataInfo + * @returns {ccs.FrameData} + */ + decodeFrame: function (frameXML, parentFrameXml, boneData, dataInfo) { + var x = 0, y = 0, scale_x = 0, scale_y = 0, skew_x = 0, skew_y = 0, tweenRotate = 0; + var duration = 0, displayIndex = 0, zOrder = 0, tweenEasing = 0, blendType = 0; + + var frameData = new ccs.FrameData(); + frameData.strMovement = frameXML.getAttribute(ccs.CONST_A_MOVEMENT) || ""; + frameData.movement = frameData.strMovement; + frameData.strEvent = frameXML.getAttribute(ccs.CONST_A_EVENT) || ""; + frameData.event = frameData.strEvent; + frameData.strSound = frameXML.getAttribute(ccs.CONST_A_SOUND) || ""; + frameData.sound = frameData.strSound; + frameData.strSoundEffect = frameXML.getAttribute(ccs.CONST_A_SOUND_EFFECT) || ""; + frameData.soundEffect = frameData.strSoundEffect; + + var isTween = frameXML.getAttribute(ccs.CONST_A_TWEEN_FRAME); + frameData.isTween = !(isTween !== undefined && (isTween === "false" || isTween === "0")); + + if (dataInfo.flashToolVersion >= ccs.CONST_VERSION_2_0) { + x = frameXML.getAttribute(ccs.CONST_A_COCOS2DX_X); + if(x){ + frameData.x = parseFloat(x); + frameData.x *= this._positionReadScale; + } + y = frameXML.getAttribute(ccs.CONST_A_COCOS2DX_Y); + if(y){ + frameData.y = -parseFloat(y); + frameData.y *= this._positionReadScale; + } + } else { + x = frameXML.getAttribute(ccs.CONST_A_X); + if(x) { + frameData.x = parseFloat(x); + frameData.x *= this._positionReadScale; + } + y = frameXML.getAttribute(ccs.CONST_A_Y); + if(y) { + frameData.y = -parseFloat(y); + frameData.y *= this._positionReadScale; + } + } + + scale_x = frameXML.getAttribute(ccs.CONST_A_SCALE_X); + if( scale_x != null ) + frameData.scaleX = parseFloat(scale_x); + scale_y = frameXML.getAttribute(ccs.CONST_A_SCALE_Y); + if( scale_y != null ) + frameData.scaleY = parseFloat(scale_y); + skew_x = frameXML.getAttribute(ccs.CONST_A_SKEW_X); + if( skew_x != null ) + frameData.skewX = cc.degreesToRadians(parseFloat(skew_x)); + skew_y = frameXML.getAttribute(ccs.CONST_A_SKEW_Y); + if( skew_y != null ) + frameData.skewY = cc.degreesToRadians(-parseFloat(skew_y)); + + duration = frameXML.getAttribute(ccs.CONST_A_DURATION); + if( duration != null ) + frameData.duration = parseFloat(duration); + displayIndex = frameXML.getAttribute(ccs.CONST_A_DISPLAY_INDEX); + if( displayIndex != null ) + frameData.displayIndex = parseFloat(displayIndex); + zOrder = frameXML.getAttribute(ccs.CONST_A_Z); + if( zOrder != null ) + frameData.zOrder = parseInt(zOrder); + tweenRotate = frameXML.getAttribute(ccs.CONST_A_TWEEN_ROTATE); + if( tweenRotate != null ) + frameData.tweenRotate = parseFloat(tweenRotate); + + blendType = frameXML.getAttribute(ccs.CONST_A_BLEND_TYPE); + if ( blendType != null ) { + var blendFunc = frameData.blendFunc; + switch (blendType) { + case ccs.BLEND_TYPE_NORMAL: + blendFunc.src = cc.BLEND_SRC; + blendFunc.dst = cc.BLEND_DST; + break; + case ccs.BLEND_TYPE_ADD: + blendFunc.src = cc.SRC_ALPHA; + blendFunc.dst = cc.ONE; + break; + case ccs.BLEND_TYPE_MULTIPLY: + blendFunc.src = cc.DST_COLOR; + blendFunc.dst = cc.ONE_MINUS_SRC_ALPHA; + break; + case ccs.BLEND_TYPE_SCREEN: + blendFunc.src = cc.ONE; + blendFunc.dst = cc.ONE_MINUS_DST_COLOR; + break; + default: + frameData.blendFunc.src = cc.BLEND_SRC; + frameData.blendFunc.dst = cc.BLEND_DST; + break; + } + } + + var colorTransformXML = frameXML.querySelectorAll(ccs.CONST_FRAME + " > " + ccs.CONST_A_COLOR_TRANSFORM); + if (colorTransformXML && colorTransformXML.length > 0) { + colorTransformXML = colorTransformXML[0]; + var alpha, red, green, blue; + var alphaOffset, redOffset, greenOffset, blueOffset; + + alpha = parseFloat(colorTransformXML.getAttribute(ccs.CONST_A_ALPHA)) || 0; + red = parseFloat(colorTransformXML.getAttribute(ccs.CONST_A_RED)) || 0; + green = parseFloat(colorTransformXML.getAttribute(ccs.CONST_A_GREEN)) || 0; + blue = parseFloat(colorTransformXML.getAttribute(ccs.CONST_A_BLUE)) || 0; + + alphaOffset = parseFloat(colorTransformXML.getAttribute(ccs.CONST_A_ALPHA_OFFSET)) || 0; + redOffset = parseFloat(colorTransformXML.getAttribute(ccs.CONST_A_RED_OFFSET)) || 0; + greenOffset = parseFloat(colorTransformXML.getAttribute(ccs.CONST_A_GREEN_OFFSET)) || 0; + blueOffset = parseFloat(colorTransformXML.getAttribute(ccs.CONST_A_BLUE_OFFSET)) || 0; + + frameData.a = 2.55 * alphaOffset + alpha; + frameData.r = 2.55 * redOffset + red; + frameData.g = 2.55 * greenOffset + green; + frameData.b = 2.55 * blueOffset + blue; + + frameData.isUseColorInfo = true; + } + + var _easing = frameXML.getAttribute(ccs.CONST_A_TWEEN_EASING); + if(_easing != null) { + if(_easing != ccs.CONST_FL_NAN){ + tweenEasing = frameXML.getAttribute(ccs.CONST_A_TWEEN_EASING); + if( tweenEasing ) + frameData.tweenEasing = (tweenEasing === 2) ? ccs.TweenType.SINE_EASEINOUT : tweenEasing; + } else + frameData.tweenEasing = ccs.TweenType.LINEAR; + } + + if (parentFrameXml) { + //* recalculate frame data from parent frame data, use for translate matrix + var helpNode = new ccs.BaseData(); + if (dataInfo.flashToolVersion >= ccs.CONST_VERSION_2_0) { + helpNode.x = parseFloat(parentFrameXml.getAttribute(ccs.CONST_A_COCOS2DX_X)); + helpNode.y = parseFloat(parentFrameXml.getAttribute(ccs.CONST_A_COCOS2DX_Y)); + } else { + helpNode.x = parseFloat(parentFrameXml.getAttribute(ccs.CONST_A_X)); + helpNode.y = parseFloat(parentFrameXml.getAttribute(ccs.CONST_A_Y)); + } + helpNode.skewX = parseFloat(parentFrameXml.getAttribute(ccs.CONST_A_SKEW_X)); + helpNode.skewY = parseFloat(parentFrameXml.getAttribute(ccs.CONST_A_SKEW_Y)); + + helpNode.y = -helpNode.y; + helpNode.skewX = cc.degreesToRadians(helpNode.skewX); + helpNode.skewY = cc.degreesToRadians(-helpNode.skewY); + ccs.TransformHelp.transformFromParent(frameData, helpNode); + } + return frameData; + }, + + /** + * Decodes json data of frame. + * @param {Object} json + * @param {ccs.DataInfo} dataInfo + * @returns {ccs.FrameData} + */ + decodeFrameFromJson: function (json, dataInfo) { + var frameData = new ccs.FrameData(); + + this.decodeNodeFromJson(frameData, json, dataInfo); + + frameData.tweenEasing = json[ccs.CONST_A_TWEEN_EASING] || ccs.TweenType.LINEAR; + frameData.displayIndex = json[ccs.CONST_A_DISPLAY_INDEX]; + var bd_src = json[ccs.CONST_A_BLEND_SRC] == null ? cc.BLEND_SRC : json[ccs.CONST_A_BLEND_SRC]; + var bd_dst = json[ccs.CONST_A_BLEND_DST] == null ? cc.BLEND_DST : json[ccs.CONST_A_BLEND_DST]; + frameData.blendFunc.src = bd_src; + frameData.blendFunc.dst = bd_dst; + frameData.isTween = json[ccs.CONST_A_TWEEN_FRAME] == null ? true : json[ccs.CONST_A_TWEEN_FRAME]; + + var event = json[ccs.CONST_A_EVENT]; + if(event != null){ + frameData.strEvent = event; + frameData.event = event; + } + + if (dataInfo.cocoStudioVersion < ccs.CONST_VERSION_COMBINED) + frameData.duration = json[ccs.CONST_A_DURATION] == null ? 1 : json[ccs.CONST_A_DURATION]; + else + frameData.frameID = json[ccs.CONST_A_FRAME_INDEX]; + + var twEPs = json[ccs.CONST_A_EASING_PARAM] || []; + for (var i = 0; i < twEPs.length; i++) { + frameData.easingParams[i] = twEPs[i]; + } + + return frameData; + }, + + /** + * Decodes xml data of texture + * @param {XMLDocument} textureXML + * @param {ccs.DataInfo} dataInfo + * @returns {ccs.TextureData} + */ + decodeTexture: function (textureXML, dataInfo) { + var textureData = new ccs.TextureData(); + textureData.init(); + + if (textureXML.getAttribute(ccs.CONST_A_NAME)) { + textureData.name = textureXML.getAttribute(ccs.CONST_A_NAME); + } + + var px, py; + + if (dataInfo.flashToolVersion >= ccs.CONST_VERSION_2_0) { + px = parseFloat(textureXML.getAttribute(ccs.CONST_A_COCOS2D_PIVOT_X)) || 0; + py = parseFloat(textureXML.getAttribute(ccs.CONST_A_COCOS2D_PIVOT_Y)) || 0; + } else { + px = parseFloat(textureXML.getAttribute(ccs.CONST_A_PIVOT_X)) || 0; + py = parseFloat(textureXML.getAttribute(ccs.CONST_A_PIVOT_Y)) || 0; + } + + var width = parseFloat(textureXML.getAttribute(ccs.CONST_A_WIDTH)) || 0; + var height = parseFloat(textureXML.getAttribute(ccs.CONST_A_HEIGHT)) || 0; + + var anchorPointX = px / width; + var anchorPointY = (height - py) / height; + + textureData.pivotX = anchorPointX; + textureData.pivotY = anchorPointY; + + var contoursXML = textureXML.querySelectorAll(ccs.CONST_SUB_TEXTURE + " > " + ccs.CONST_CONTOUR); + for (var i = 0; i < contoursXML.length; i++) { + textureData.addContourData(this.decodeContour(contoursXML[i], dataInfo)); + } + return textureData; + }, + + /** + * Decodes json data of Texture. + * @param json + * @returns {ccs.TextureData} + */ + decodeTextureFromJson: function (json) { + var textureData = new ccs.TextureData(); + textureData.init(); + + var name = json[ccs.CONST_A_NAME]; + if(name != null) + textureData.name = name; + + textureData.width = json[ccs.CONST_A_WIDTH] || 0; + textureData.height = json[ccs.CONST_A_HEIGHT] || 0; + textureData.pivotX = json[ccs.CONST_A_PIVOT_X] || 0; + textureData.pivotY = json[ccs.CONST_A_PIVOT_Y] || 0; + + var contourDataList = json[ccs.CONST_CONTOUR_DATA] || []; + for (var i = 0; i < contourDataList.length; i++) { + textureData.contourDataList.push(this.decodeContourFromJson(contourDataList[i])); + } + return textureData; + }, + + /** + * Decodes xml data of contour. + * @param {XMLDocument} contourXML + * @param {ccs.DataInfo} dataInfo + * @returns {ccs.ContourData} + */ + decodeContour: function (contourXML, dataInfo) { + var contourData = new ccs.ContourData(); + contourData.init(); + + var vertexDatasXML = contourXML.querySelectorAll(ccs.CONST_CONTOUR + " > " + ccs.CONST_CONTOUR_VERTEX); + var vertexDataXML; + for (var i = 0; i < vertexDatasXML.length; i++) { + vertexDataXML = vertexDatasXML[i]; + var vertex = cc.p(0, 0); + vertex.x = parseFloat(vertexDataXML.getAttribute(ccs.CONST_A_X)) || 0; + vertex.y = parseFloat(vertexDataXML.getAttribute(ccs.CONST_A_Y)) || 0; + + vertex.y = - vertex.y; + contourData.vertexList.push(vertex); + } + return contourData; + }, + + /** + * Decodes json data of contour. + * @param {Object} json + * @returns {ccs.ContourData} + */ + decodeContourFromJson: function (json) { + var contourData = new ccs.ContourData(); + contourData.init(); + + var vertexPointList = json[ccs.CONST_VERTEX_POINT] || []; + var len = vertexPointList.length; + for (var i = 0; i < len; i++) { + var dic = vertexPointList[i]; + var vertex = cc.p(0, 0); + vertex.x = dic[ccs.CONST_A_X] || 0; + vertex.y = dic[ccs.CONST_A_Y] || 0; + contourData.vertexList.push(vertex); + } + return contourData; + }, + + /** + * Adds json armature data to armature data manager. + * @param {Object} dic json armature data + * @param {ccs.DataInfo} dataInfo + */ + addDataFromJsonCache: function (dic, dataInfo) { + dataInfo.contentScale = dic[ccs.CONST_CONTENT_SCALE] == null ? 1 : dic[ccs.CONST_CONTENT_SCALE]; + + // Decode armatures + var armatureDataArr = dic[ccs.CONST_ARMATURE_DATA] || [], i; + var armatureData; + for (i = 0; i < armatureDataArr.length; i++) { + armatureData = this.decodeArmatureFromJSON(armatureDataArr[i], dataInfo); + ccs.armatureDataManager.addArmatureData(armatureData.name, armatureData, dataInfo.filename); + } + + // Decode animations + var animationDataArr = dic[ccs.CONST_ANIMATION_DATA] || []; + var animationData; + for (i = 0; i < animationDataArr.length; i++) { + animationData = this.decodeAnimationFromJson(animationDataArr[i], dataInfo); + ccs.armatureDataManager.addAnimationData(animationData.name, animationData, dataInfo.filename); + } + + // Decode textures + var textureDataArr = dic[ccs.CONST_TEXTURE_DATA] || []; + var textureData; + for (i = 0; i < textureDataArr.length; i++) { + textureData = this.decodeTextureFromJson(textureDataArr[i], dataInfo); + ccs.armatureDataManager.addTextureData(textureData.name, textureData, dataInfo.filename); + } + + // Auto load sprite file + var autoLoad = dataInfo.asyncStruct == null ? ccs.armatureDataManager.isAutoLoadSpriteFile() : dataInfo.asyncStruct.autoLoadSpriteFile; +// if (isLoadSpriteFrame) { + if (autoLoad) { + var configFiles = dic[ccs.CONST_CONFIG_FILE_PATH] || []; + var locFilePath, locPos, locPlistPath, locImagePath; + for (i = 0; i < configFiles.length; i++) { + locFilePath = configFiles[i]; + locPos = locFilePath.lastIndexOf("."); + locFilePath = locFilePath.substring(0, locPos); + locPlistPath = dataInfo.basefilePath + locFilePath + ".plist"; + locImagePath = dataInfo.basefilePath + locFilePath + ".png"; + ccs.armatureDataManager.addSpriteFrameFromFile(locPlistPath, locImagePath, dataInfo.filename); + } + } + + armatureData = null; + animationData = null; + }, + + /** + * Decodes json data of node. + * @param node + * @param json + * @param dataInfo + */ + decodeNodeFromJson: function (node, json, dataInfo) { + node.x = json[ccs.CONST_A_X] * this._positionReadScale; + node.y = json[ccs.CONST_A_Y] * this._positionReadScale; + + node.x *= dataInfo.contentScale; + node.y *= dataInfo.contentScale; + + node.zOrder = json[ccs.CONST_A_Z]; + + node.skewX = json[ccs.CONST_A_SKEW_X] || 0; + node.skewY = json[ccs.CONST_A_SKEW_Y] || 0; + node.scaleX = json[ccs.CONST_A_SCALE_X] == null ? 1 : json[ccs.CONST_A_SCALE_X]; + node.scaleY = json[ccs.CONST_A_SCALE_Y] == null ? 1 : json[ccs.CONST_A_SCALE_Y]; + + var colorDic; + if (dataInfo.cocoStudioVersion < ccs.VERSION_COLOR_READING) { + colorDic = json[0]; + if (colorDic){ + node.a = colorDic[ccs.CONST_A_ALPHA] == null ? 255 : colorDic[ccs.CONST_A_ALPHA]; + node.r = colorDic[ccs.CONST_A_RED] == null ? 255 : colorDic[ccs.CONST_A_RED]; + node.g = colorDic[ccs.CONST_A_GREEN] == null ? 255 : colorDic[ccs.CONST_A_GREEN]; + node.b = colorDic[ccs.CONST_A_BLUE] == null ? 255 : colorDic[ccs.CONST_A_BLUE]; + node.isUseColorInfo = true; + } + } else { + colorDic = json[ccs.CONST_COLOR_INFO] || null; + if (colorDic){ + node.a = colorDic[ccs.CONST_A_ALPHA] == null ? 255 : colorDic[ccs.CONST_A_ALPHA]; + node.r = colorDic[ccs.CONST_A_RED] == null ? 255 : colorDic[ccs.CONST_A_RED]; + node.g = colorDic[ccs.CONST_A_GREEN] == null ? 255 : colorDic[ccs.CONST_A_GREEN]; + node.b = colorDic[ccs.CONST_A_BLUE] == null ? 255 : colorDic[ccs.CONST_A_BLUE]; + node.isUseColorInfo = true; + } + } + }, + + clear: function () { + this._configFileList = []; + this._asyncRefCount = 0; + this._asyncRefTotalCount = 0; + }, + + _asyncCallBack: function (selector, target, percent) { + if(selector && cc.isFunction(selector)) + selector.call(target, percent); + if(target && selector && typeof selector === 'string') + target[selector](percent); + }, + /** + * find the base file path + * @param filePath + * @returns {String} + * @private + */ + _initBaseFilePath: function (filePath) { + var path = filePath; + var pos = path.lastIndexOf("/"); + if (pos > -1) + path = path.substr(0, pos + 1); + else + path = ""; + return path; + }, + + /** + * Adds xml armature data to armature data manager. + * @param {XMLDocument} xml + * @param {ccs.DataInfo} dataInfo + */ + addDataFromXML: function (xml, dataInfo) { + /* + * Need to get the full path of the xml file, or the Tiny XML can't find the xml at IOS + */ + var xmlStr = cc.loader.getRes(xml); + if (!xmlStr) throw new Error("Please load the resource first : " + xml); + var skeletonXML = cc.saxParser.parse(xmlStr); + var skeleton = skeletonXML.documentElement; + if (skeleton) + this.addDataFromCache(skeleton, dataInfo); + }, + + /** + * Adds json armature data to armature data manager. + * @param {String} filePath + * @param {ccs.DataInfo} dataInfo + */ + addDataFromJson: function (filePath, dataInfo) { + var fileContent = cc.loader.getRes(filePath); + this.addDataFromJsonCache(fileContent, dataInfo); + } +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/armature/utils/CCSpriteFrameCacheHelper.js b/frameworks/cocos2d-html5/extensions/cocostudio/armature/utils/CCSpriteFrameCacheHelper.js new file mode 100644 index 0000000..a0496e9 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/armature/utils/CCSpriteFrameCacheHelper.js @@ -0,0 +1,68 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * ccs.spriteFrameCacheHelper is a singleton object, it's a sprite frame cache helper + * @class + * @name ccs.spriteFrameCacheHelper + */ +ccs.spriteFrameCacheHelper = /** @lends ccs.spriteFrameCacheHelper# */ { + _textureAtlasDic:{}, + _imagePaths:[], + + /** + * Adds sprite frame from file + * @param plistPath + * @param imagePath + */ + addSpriteFrameFromFile:function (plistPath, imagePath) { + cc.spriteFrameCache.addSpriteFrames(plistPath, imagePath); + }, + + /** + * Returns texture atlas with texture. + * @param texture + * @returns {*} + */ + getTextureAtlasWithTexture:function (texture) { + //todo + return null; + var textureName = texture.getName(); + var atlas = this._textureAtlasDic[textureName]; + if (atlas == null) { + atlas = new cc.TextureAtlas(texture, 20); + this._textureAtlasDic[textureName] = atlas; + } + return atlas; + }, + + /** + * Clear the sprite frame cache's data. + */ + clear: function () { + this._textureAtlasDic = {}; + this._imagePaths = []; + } +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/armature/utils/CCTransformHelp.js b/frameworks/cocos2d-html5/extensions/cocostudio/armature/utils/CCTransformHelp.js new file mode 100644 index 0000000..0d6228d --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/armature/utils/CCTransformHelp.js @@ -0,0 +1,183 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * use to calculate the matrix of node from parent node + * @class ccs.TransformHelp + * @extend ccs.Class + */ +ccs.TransformHelp = ccs.TransformHelp || ccs.Class.extend({}); + +ccs.TransformHelp.helpMatrix1 = cc.affineTransformMake(1, 0, 0, 1, 0, 0); +ccs.TransformHelp.helpMatrix2 = cc.affineTransformMake(1, 0, 0, 1, 0, 0); +ccs.TransformHelp.helpPoint1 = cc.p(0, 0); +ccs.TransformHelp.helpPoint2 = cc.p(0, 0); +ccs.TransformHelp.helpParentNode = {}; + +/** + * Calculate a BaseData's transform matrix from parent node. + * @function + * @static + * @param {ccs.BaseData} bone + * Constructor + */ +ccs.TransformHelp.transformFromParent = function (bone, parentNode) { + this.nodeToMatrix(bone, this.helpMatrix1); + this.nodeToMatrix(parentNode, this.helpMatrix2); + + this.helpMatrix2 = cc.affineTransformInvert(this.helpMatrix2); + this.helpMatrix1 = cc.affineTransformConcat(this.helpMatrix1, this.helpMatrix2); + + this.matrixToNode(this.helpMatrix1, bone); +}; + +ccs.TransformHelp.transformToParent = function(node, parentNode){ + this.nodeToMatrix(node, this.helpMatrix1); + this.nodeToMatrix(parentNode, this.helpMatrix2); + + this.helpMatrix1 = cc.affineTransformConcat(this.helpMatrix1, this.helpMatrix2); + + this.matrixToNode(this.helpMatrix1, node); +}; + +ccs.TransformHelp.transformFromParentWithoutScale = function(node, parentNode){ +// this.helpParentNode.copy(&parentNode); + + for(var p in parentNode){ + this.helpParentNode[p] = parentNode[p]; + } + this.helpParentNode.scaleX = 1; + this.helpParentNode.scaleY = 1; + + this.nodeToMatrix(node, this.helpMatrix1); + this.nodeToMatrix(this.helpParentNode, this.helpMatrix2); + + this.helpMatrix2 = cc.affineTransformInvert(this.helpMatrix2); + this.helpMatrix1 = cc.affineTransformConcat(this.helpMatrix1, this.helpMatrix2); + + this.matrixToNode(this.helpMatrix1, node); +}; + +ccs.TransformHelp.transformToParentWithoutScale = function(node, parentNode){ + for(var p in parentNode){ + this.helpParentNode[p] = parentNode[p]; + } + this.helpParentNode.scaleX = 1; + this.helpParentNode.scaleY = 1; + + this.nodeToMatrix(node, this.helpMatrix1); + this.nodeToMatrix(this.helpParentNode, this.helpMatrix2); + + this.helpMatrix1 = cc.affineTransformConcat(this.helpMatrix1, this.helpMatrix2); + + this.matrixToNode(this.helpMatrix1, node); + +}; + +/** + * @function + * @static + * @param {ccs.BaseData} node + * @param {cc.AffineTransform} matrix + */ +ccs.TransformHelp.nodeToMatrix = function (node, matrix) { + if (node.skewX === -node.skewY) { + var sine = Math.sin(node.skewX); + var cosine = Math.cos(node.skewX); + matrix.a = node.scaleX * cosine; + matrix.b = node.scaleX * -sine; + matrix.c = node.scaleY * sine; + matrix.d = node.scaleY * cosine; + } else { + matrix.a = node.scaleX * Math.cos(node.skewY); + matrix.b = node.scaleX * Math.sin(node.skewY); + matrix.c = node.scaleY * Math.sin(node.skewX); + matrix.d = node.scaleY * Math.cos(node.skewX); + } + matrix.tx = node.x; + matrix.ty = node.y; +}; + +/** + * @function + * @static + * @param {cc.AffineTransform} matrix + * @param {ccs.BaseData} node + */ +ccs.TransformHelp.matrixToNode = function (matrix, node) { + /* + * In as3 language, there is a function called "deltaTransformPoint", it calculate a point used give Transform + * but not used the tx, ty value. we simulate the function here + */ + this.helpPoint1.x = 0; + this.helpPoint1.y = 1; + this.helpPoint1 = cc.pointApplyAffineTransform(this.helpPoint1, matrix); + this.helpPoint1.x -= matrix.tx; + this.helpPoint1.y -= matrix.ty; + + this.helpPoint2.x = 1; + this.helpPoint2.y = 0; + this.helpPoint2 = cc.pointApplyAffineTransform(this.helpPoint2, matrix); + this.helpPoint2.x -= matrix.tx; + this.helpPoint2.y -= matrix.ty; + + node.skewX = -(Math.atan2(this.helpPoint1.y, this.helpPoint1.x) - 1.5707964); //todo + //node.skewX = -Math.atan2(this.helpPoint2.y, this.helpPoint2.x); + node.skewY = Math.atan2(this.helpPoint2.y, this.helpPoint2.x); + node.scaleX = Math.sqrt(matrix.a * matrix.a + matrix.b * matrix.b); + node.scaleY = Math.sqrt(matrix.c * matrix.c + matrix.d * matrix.d); + node.x = matrix.tx; + node.y = matrix.ty; +}; + +/** + * @function + * @static + * @param {ccs.BaseData} target + * @param {ccs.BaseData} source + */ +ccs.TransformHelp.nodeConcat = function (target, source) { + target.x += source.x; + target.y += source.y; + target.skewX += source.skewX; + target.skewY += source.skewY; + target.scaleX += source.scaleX; + target.scaleY += source.scaleY; +}; + +/** + * @function + * @static + * @param {ccs.BaseData} target + * @param {ccs.BaseData} source + */ +ccs.TransformHelp.nodeSub = function (target, source) { + target.x -= source.x; + target.y -= source.y; + target.skewX -= source.skewX; + target.skewY -= source.skewY; + target.scaleX -= source.scaleX; + target.scaleY -= source.scaleY; +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/armature/utils/CCTweenFunction.js b/frameworks/cocos2d-html5/extensions/cocostudio/armature/utils/CCTweenFunction.js new file mode 100644 index 0000000..7ced7fa --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/armature/utils/CCTweenFunction.js @@ -0,0 +1,501 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * TweenType + * @type Object + */ +ccs.TweenType = { + CUSTOM_EASING: -1, + LINEAR: 0, + + SINE_EASEIN: 1, + SINE_EASEOUT: 2, + SINE_EASEINOUT: 3, + + QUAD_EASEIN: 4, + QUAD_EASEOUT: 5, + QUAD_EASEINOUT: 6, + + CUBIC_EASEIN: 7, + CUBIC_EASEOUT: 8, + CUBIC_EASEINOUT: 9, + + QUART_EASEIN: 10, + QUART_EASEOUT: 11, + QUART_EASEINOUT: 12, + + QUINT_EASEIN: 13, + QUINT_EASEOUT: 14, + QUINT_EASEINOUT: 15, + + EXPO_EASEIN: 16, + EXPO_EASEOUT: 17, + EXPO_EASEINOUT: 18, + + CIRC_EASEIN: 19, + CIRC_EASEOUT: 20, + CIRC_EASEINOUT: 21, + + ELASTIC_EASEIN: 22, + ELASTIC_EASEOUT: 23, + ELASTIC_EASEINOUT: 24, + + BACK_EASEIN: 25, + BACK_EASEOUT: 26, + BACK_EASEINOUT: 27, + + BOUNCE_EASEIN: 28, + BOUNCE_EASEOUT: 29, + BOUNCE_EASEINOUT: 30, + + TWEEN_EASING_MAX: 10000 +}; + +ccs.TweenFunction = ccs.TweenFunction || ccs.Class.extend({}); + +ccs.DOUBLE_PI = ccs.M_PI_X_2 = Math.PI * 2; +ccs.HALF_PI = ccs.M_PI_2 = Math.PI / 2; +ccs.M_PI = Math.PI; + +ccs.TweenFunction.tweenTo = function (time, type, easingParam) { + var delta = 0; + + switch (type) { + case ccs.TweenType.CUSTOM_EASING: + delta = this.customEase(time, easingParam); + break; + case ccs.TweenType.LINEAR: + delta = this.linear(time); + break; + case ccs.TweenType.SINE_EASEIN: + delta = this.sineEaseIn(time); + break; + case ccs.TweenType.SINE_EASEOUT: + delta = this.sineEaseOut(time); + break; + case ccs.TweenType.SINE_EASEINOUT: + delta = this.sineEaseInOut(time); + break; + + case ccs.TweenType.QUAD_EASEIN: + delta = this.quadEaseIn(time); + break; + case ccs.TweenType.QUAD_EASEOUT: + delta = this.quadEaseOut(time); + break; + case ccs.TweenType.QUAD_EASEINOUT: + delta = this.quadEaseInOut(time); + break; + + case ccs.TweenType.CUBIC_EASEIN: + delta = this.cubicEaseIn(time); + break; + case ccs.TweenType.CUBIC_EASEOUT: + delta = this.cubicEaseOut(time); + break; + case ccs.TweenType.CUBIC_EASEINOUT: + delta = this.cubicEaseInOut(time); + break; + + case ccs.TweenType.QUART_EASEIN: + delta = this.quartEaseIn(time); + break; + case ccs.TweenType.QUART_EASEOUT: + delta = this.quartEaseOut(time); + break; + case ccs.TweenType.QUART_EASEINOUT: + delta = this.quartEaseInOut(time); + break; + + case ccs.TweenType.QUINT_EASEIN: + delta = this.quintEaseIn(time); + break; + case ccs.TweenType.QUINT_EASEOUT: + delta = this.quintEaseOut(time); + break; + case ccs.TweenType.QUINT_EASEINOUT: + delta = this.quintEaseInOut(time); + break; + + case ccs.TweenType.EXPO_EASEIN: + delta = this.expoEaseIn(time); + break; + case ccs.TweenType.EXPO_EASEOUT: + delta = this.expoEaseOut(time); + break; + case ccs.TweenType.EXPO_EASEINOUT: + delta = this.expoEaseInOut(time); + break; + + case ccs.TweenType.CIRC_EASEIN: + delta = this.circEaseIn(time); + break; + case ccs.TweenType.CIRC_EASEOUT: + delta = this.circEaseOut(time); + break; + case ccs.TweenType.CIRC_EASEINOUT: + delta = this.circEaseInOut(time); + break; + + case ccs.TweenType.ELASTIC_EASEIN: + var period = 0.3; + if(null != easingParam && easingParam.length > 0){ + period = easingParam[0]; + } + delta = this.elasticEaseIn(time, period); + break; + case ccs.TweenType.ELASTIC_EASEOUT: + var period = 0.3; + if(null != easingParam && easingParam.length > 0){ + period = easingParam[0]; + } + delta = this.elasticEaseOut(time, period); + break; + case ccs.TweenType.ELASTIC_EASEINOUT: + var period = 0.3; + if(null != easingParam && easingParam.length > 0){ + period = easingParam[0]; + } + delta = this.elasticEaseInOut(time, period); + break; + + case ccs.TweenType.BACK_EASEIN: + delta = this.backEaseIn(time); + break; + case ccs.TweenType.BACK_EASEOUT: + delta = this.backEaseOut(time); + break; + case ccs.TweenType.BACK_EASEINOUT: + delta = this.backEaseInOut(time); + break; + + case ccs.TweenType.BOUNCE_EASEIN: + delta = this.bounceEaseIn(time); + break; + case ccs.TweenType.BOUNCE_EASEOUT: + delta = this.bounceEaseOut(time); + break; + case ccs.TweenType.BOUNCE_EASEINOUT: + delta = this.bounceEaseInOut(time); + break; + + default: + delta = this.sineEaseInOut(time); + break; + } + + return delta; +}; + + +// Linear +ccs.TweenFunction.linear = function (time) { + return time; +}; + + +// Sine Ease +ccs.TweenFunction.sineEaseIn = function (time) { + return -1 * Math.cos(time * ccs.HALF_PI) + 1; +}; +ccs.TweenFunction.sineEaseOut = function (time) { + return Math.sin(time * ccs.HALF_PI); +}; +ccs.TweenFunction.sineEaseInOut = function (time) { + return -0.5 * (Math.cos(ccs.M_PI * time) - 1); +}; + + +// Quad Ease +ccs.TweenFunction.quadEaseIn = function (time) { + return time * time; +}; +ccs.TweenFunction.quadEaseOut = function (time) { + return -1 * time * (time - 2); +}; +ccs.TweenFunction.quadEaseInOut = function (time) { + time = time * 2; + if (time < 1) + return 0.5 * time * time; + --time; + return -0.5 * (time * (time - 2) - 1); +}; + + +// Cubic Ease +ccs.TweenFunction.cubicEaseIn = function (time) { + return time * time * time; +}; +ccs.TweenFunction.cubicEaseOut = function (time) { + time -= 1; + return (time * time * time + 1); +}; +ccs.TweenFunction.cubicEaseInOut = function (time) { + time = time * 2; + if (time < 1) + return 0.5 * time * time * time; + time -= 2; + return 0.5 * (time * time * time + 2); +}; + + +// Quart Ease +ccs.TweenFunction.quartEaseIn = function (time) { + return time * time * time * time; +}; +ccs.TweenFunction.quartEaseOut = function (time) { + time -= 1; + return -(time * time * time * time - 1); +}; +ccs.TweenFunction.quartEaseInOut = function (time) { + time = time * 2; + if (time < 1) + return 0.5 * time * time * time * time; + time -= 2; + return -0.5 * (time * time * time * time - 2); +}; + + +// Quint Ease +ccs.TweenFunction.quintEaseIn = function (time) { + return time * time * time * time * time; +}; +ccs.TweenFunction.quintEaseOut = function (time) { + time -= 1; + return (time * time * time * time * time + 1); +}; +ccs.TweenFunction.quintEaseInOut = function (time) { + time = time * 2; + if (time < 1) + return 0.5 * time * time * time * time * time; + time -= 2; + return 0.5 * (time * time * time * time * time + 2); +}; + + +// Expo Ease +ccs.TweenFunction.expoEaseIn = function (time) { + return time === 0 ? 0 : Math.pow(2, 10 * (time - 1)) - 0.001; +}; +ccs.TweenFunction.expoEaseOut = function (time) { + return time === 1 ? 1 : (-Math.pow(2, -10 * time) + 1); +}; +ccs.TweenFunction.expoEaseInOut = function (time) { + time /= 0.5; + if (time < 1) { + time = 0.5 * Math.pow(2, 10 * (time - 1)); + } + else { + time = 0.5 * (-Math.pow(2, -10 * (time - 1)) + 2); + } + + return time; +}; + + +// Circ Ease +ccs.TweenFunction.circEaseIn = function (time) { + return -1 * (Math.sqrt(1 - time * time) - 1); +}; +ccs.TweenFunction.circEaseOut = function (time) { + time = time - 1; + return Math.sqrt(1 - time * time); +}; +ccs.TweenFunction.circEaseInOut = function (time) { + time = time * 2; + if (time < 1) + return -0.5 * (Math.sqrt(1 - time * time) - 1); + time -= 2; + return 0.5 * (Math.sqrt(1 - time * time) + 1); +}; + + +// Elastic Ease +ccs.TweenFunction.elasticEaseIn = function (time, easingParam) { + var period = 0.3; + + if (easingParam.length > 0) { + period = easingParam[0]; + } + + var newT = 0; + if (time === 0 || time === 1) { + newT = time; + } + else { + var s = period / 4; + time = time - 1; + newT = -Math.pow(2, 10 * time) * Math.sin((time - s) * ccs.DOUBLE_PI / period); + } + + return newT; +}; +ccs.TweenFunction.elasticEaseOut = function (time, easingParam) { + var period = 0.3; + + if (easingParam.length > 0) { + period = easingParam[0]; + } + + var newT = 0; + if (time === 0 || time === 1) { + newT = time; + } + else { + var s = period / 4; + newT = Math.pow(2, -10 * time) * Math.sin((time - s) * ccs.DOUBLE_PI / period) + 1; + } + + return newT; +}; +ccs.TweenFunction.elasticEaseInOut = function (time, easingParam) { + var period = 0.3; + + if (easingParam.length > 0) { + period = easingParam[0]; + } + + var newT = 0; + if (time === 0 || time === 1) { + newT = time; + } + else { + time = time * 2; + if (!period) { + period = 0.3 * 1.5; + } + + var s = period / 4; + + time = time - 1; + if (time < 0) { + newT = -0.5 * Math.pow(2, 10 * time) * Math.sin((time - s) * ccs.DOUBLE_PI / period); + } else { + newT = Math.pow(2, -10 * time) * Math.sin((time - s) * ccs.DOUBLE_PI / period) * 0.5 + 1; + } + } + return newT; +}; + + +// Back Ease +ccs.TweenFunction.backEaseIn = function (time) { + var overshoot = 1.70158; + return time * time * ((overshoot + 1) * time - overshoot); +}; +ccs.TweenFunction.backEaseOut = function (time) { + var overshoot = 1.70158; + + time = time - 1; + return time * time * ((overshoot + 1) * time + overshoot) + 1; +}; +ccs.TweenFunction.backEaseInOut = function (time) { + var overshoot = 1.70158 * 1.525; + + time = time * 2; + if (time < 1) { + return (time * time * ((overshoot + 1) * time - overshoot)) / 2; + } + else { + time = time - 2; + return (time * time * ((overshoot + 1) * time + overshoot)) / 2 + 1; + } +}; + + +// Bounce Ease +ccs.bounceTime = function (time) { + if (time < 1 / 2.75) { + return 7.5625 * time * time; + } else if (time < 2 / 2.75) { + time -= 1.5 / 2.75; + return 7.5625 * time * time + 0.75; + } else if (time < 2.5 / 2.75) { + time -= 2.25 / 2.75; + return 7.5625 * time * time + 0.9375; + } + + time -= 2.625 / 2.75; + return 7.5625 * time * time + 0.984375; +}; +ccs.TweenFunction.bounceEaseIn = function (time) { + return 1 - ccs.bounceTime(1 - time); +}; + +ccs.TweenFunction.bounceEaseOut = function (time) { + return ccs.bounceTime(time); +}; + +ccs.TweenFunction.bounceEaseInOut = function (time) { + var newT = 0; + if (time < 0.5) { + time = time * 2; + newT = (1 - ccs.bounceTime(1 - time)) * 0.5; + } else { + newT = ccs.bounceTime(time * 2 - 1) * 0.5 + 0.5; + } + + return newT; +}; + + +// Custom Ease +ccs.TweenFunction.customEase = function (time, easingParam) { + if (easingParam.length > 0) { + var tt = 1 - time; + return easingParam[1] * tt * tt * tt + 3 * easingParam[3] * time * tt * tt + 3 * easingParam[5] * time * time * tt + easingParam[7] * time * time * time; + } + return time; +}; + +ccs.TweenFunction.easeIn = function(time, rate){ + return Math.pow(time, rate); +}; + +ccs.TweenFunction.easeOut = function(time, rate){ + return Math.pow(time, 1 / rate); +}; + +ccs.TweenFunction.easeInOut = function(time, rate){ + time *= 2; + if(time < 1){ + return 0.5 * Math.pow(time, rate); + }else{ + return 1 - 0.5 * Math.pow(2 - time, rate); + } +}; + +ccs.TweenFunction.quadraticIn = function(time){ + return Math.pow(time, 2); +}; + +ccs.TweenFunction.quadraticOut = function(time){ + return -time * (time - 2); +}; + +ccs.TweenFunction.bezieratFunction = function(a, b, c, d, t){ + return (Math.pow(1-t,3) * a + 3*t*(Math.pow(1-t,2))*b + 3*Math.pow(t,2)*(1-t)*c + Math.pow(t,3)*d ); +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/armature/utils/CCUtilMath.js b/frameworks/cocos2d-html5/extensions/cocostudio/armature/utils/CCUtilMath.js new file mode 100644 index 0000000..082ec35 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/armature/utils/CCUtilMath.js @@ -0,0 +1,69 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +var ENABLE_PHYSICS_DETECT = false; +ccs.fmodf = function (x, y) { + while (x > y) { + x -= y; + } + return x; +}; +var CC_SAFE_RELEASE = function (obj) { + if (obj && obj.release) { + obj.release(); + } +}; + +ccs.isSpriteContainPoint = function (sprite, point, outPoint) { + var p = sprite.convertToNodeSpace(point); + if (outPoint) { + outPoint.x = p.x; + outPoint.y = p.y; + } + var s = sprite.getContentSize(); + return cc.rectContainsPoint(cc.rect(0, 0, s.width, s.height), p); +}; +ccs.SPRITE_CONTAIN_POINT = ccs.isSpriteContainPoint; +ccs.SPRITE_CONTAIN_POINT_WITH_RETURN = ccs.isSpriteContainPoint; + +ccs.extBezierTo = function (t, point1, point2, point3, point4) { + var p = cc.p(0, 0); + if (point3 && !point4) { + p.x = Math.pow((1 - t), 2) * point1.x + 2 * t * (1 - t) * point2.x + Math.pow(t, 2) * point3.x; + p.y = Math.pow((1 - t), 2) * point1.y + 2 * t * (1 - t) * point2.y + Math.pow(t, 2) * point3.y; + } + if (point4) { + p.x = point1.x * Math.pow((1 - t), 3) + 3 * t * point2.x * Math.pow((1 - t), 2) + 3 * point3.x * Math.pow(t, 2) * (1 - t) + point4.x * Math.pow(t, 3); + p.y = point1.y * Math.pow((1 - t), 3) + 3 * t * point2.y * Math.pow((1 - t), 2) + 3 * point3.y * Math.pow(t, 2) * (1 - t) + point4.y * Math.pow(t, 3); + } + return p; +}; + +ccs.extCircleTo = function (t, center, radius, fromRadian, radianDif) { + var p = cc.p(0, 0); + p.x = center.x + radius * Math.cos(fromRadian + radianDif * t); + p.y = center.y + radius * Math.sin(fromRadian + radianDif * t); + return p; +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/components/CCComAttribute.js b/frameworks/cocos2d-html5/extensions/cocostudio/components/CCComAttribute.js new file mode 100644 index 0000000..0575b93 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/components/CCComAttribute.js @@ -0,0 +1,210 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The attribute component for Cocostudio. + * @class + * @extends ccs.Component + */ +ccs.ComAttribute = ccs.Component.extend(/** @lends ccs.ComAttribute# */{ + _jsonDict: null, + _filePath: "", + + /** + * Construction of ccs.ComAttribute + */ + ctor: function () { + cc.Component.prototype.ctor.call(this); + this._jsonDict = {}; + this._filePath = ""; + this._name = "CCComAttribute"; + ccs.ComAttribute.prototype.init.call(this); + }, + + /** + * Initializes a ccs.ComAttribute + * @returns {boolean} + */ + init: function () { + this._jsonDict = {}; + return true; + }, + + /** + * Sets int attribute + * @param {String} key + * @param {number} value + */ + setInt: function (key, value) { + if (!key) { + cc.log("Argument must be non-nil"); + return; + } + this._jsonDict[key] = value; + }, + + /** + * Sets double attribute + * @param {String} key + * @param {number} value + */ + setDouble: function (key, value) { + if (!key) { + cc.log("Argument must be non-nil"); + return; + } + this._jsonDict[key] = value; + }, + + /** + * Sets float attribute + * @param {String} key + * @param {number} value + */ + setFloat: function (key, value) { + if (!key) { + cc.log("Argument must be non-nil"); + return; + } + this._jsonDict[key] = value; + }, + + /** + * Sets boolean attribute + * @param {String} key + * @param {Boolean} value + */ + setBool: function (key, value) { + if (!key) { + cc.log("Argument must be non-nil"); + return; + } + this._jsonDict[key] = value; + }, + + /** + * Sets string attribute + * @param {String} key + * @param {Boolean} value + */ + setString: function (key, value) { + if (!key) { + cc.log("Argument must be non-nil"); + return; + } + this._jsonDict[key] = value; + }, + + /** + * Sets object attribute + * @param {String} key + * @param {Object} value + */ + setObject: function (key, value) { + if (!key) { + cc.log("Argument must be non-nil"); + return; + } + this._jsonDict[key] = value; + }, + + /** + * Returns int value from attribute + * @param {String} key + * @returns {Number} + */ + getInt: function (key) { + var ret = this._jsonDict[key]; + return parseInt(ret || 0); + }, + + /** + * Returns double value from attribute + * @param {String} key + * @returns {Number} + */ + getDouble: function (key) { + var ret = this._jsonDict[key]; + return parseFloat(ret || 0.0); + }, + + /** + * Returns float value from attribute + * @param {String} key + * @returns {Number} + */ + getFloat: function (key) { + var ret = this._jsonDict[key]; + return parseFloat(ret || 0.0); + }, + + /** + * Returns boolean value from attribute + * @param {String} key + * @returns {Boolean} + */ + getBool: function (key) { + var ret = this._jsonDict[key]; + return Boolean(ret || false); + }, + + /** + * Returns string value from attribute + * @param {String} key + * @returns {String} + */ + getString: function (key) { + var ret = this._jsonDict[key]; + return ret || ""; + }, + + /** + * Returns object value from attribute + * @param {String} key + * @returns {Object} + */ + getObject: function (key) { + return this._jsonDict[key]; + }, + + /** + * Parses json file. + * @param filename + */ + parse:function(filename){ + this._jsonDict = cc.loader.getRes(filename); + } +}); +/** + * allocates and initializes a ComAttribute. + * @deprecated since v3.0, please use new construction instead. + * @return {ccs.ComAttribute} + * @example + * // example + * var com = ccs.ComAttribute.create(); + */ +ccs.ComAttribute.create = function () { + return new ccs.ComAttribute(); +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/components/CCComAudio.js b/frameworks/cocos2d-html5/extensions/cocostudio/components/CCComAudio.js new file mode 100644 index 0000000..0b15af0 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/components/CCComAudio.js @@ -0,0 +1,285 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The audio component for Cocostudio. + * @class + * @extends ccs.Component + */ +ccs.ComAudio = ccs.Component.extend(/** @lends ccs.ComAudio# */{ + _filePath: "", + _loop: false, + + /** + * Construction of ccs.ComAudio + */ + ctor: function () { + cc.Component.prototype.ctor.call(this); + this._name = "Audio"; + ccs.ComAudio.prototype.init.call(this); + }, + + /** + * Initializes a ccs.ComAudio. + * @returns {boolean} + */ + init: function () { + return true; + }, + + /** + * The callback calls when a audio component enter stage. + * @override + */ + onExit: function () { + this.stopBackgroundMusic(true); + this.stopAllEffects(); + }, + + /** + * Stops all audios. + */ + end: function () { + cc.audioEngine.end(); + }, + + /** + * Preload background music resource + * @param {String} pszFilePath + */ + preloadBackgroundMusic: function (pszFilePath) { + cc.loader.load(pszFilePath); + }, + + /** + * Play background music + * @param {String} [pszFilePath] + * @param {Boolean} [loop] + */ + playBackgroundMusic: function (pszFilePath, loop) { + if(pszFilePath){ + cc.audioEngine.playMusic(pszFilePath, loop); + }else{ + cc.audioEngine.playMusic(this._filePath, this._loop); + } + }, + + /** + * Stop background music + * @param {String} releaseData + */ + stopBackgroundMusic: function (releaseData) { + cc.audioEngine.stopMusic(releaseData); + }, + + /** + * Pause background music + */ + pauseBackgroundMusic: function () { + cc.audioEngine.pauseMusic(); + }, + + /** + * Resume background music + */ + resumeBackgroundMusic: function () { + cc.audioEngine.resumeMusic(); + }, + + /** + * Rewind background music + */ + rewindBackgroundMusic: function () { + cc.audioEngine.rewindMusic(); + }, + + /** + * Indicates whether any background music can be played or not. + * @returns {boolean} + */ + willPlayBackgroundMusic: function () { + return cc.audioEngine.willPlayMusic(); + }, + + /** + * Whether the music is playing. + * @returns {Boolean} + */ + isBackgroundMusicPlaying: function () { + return cc.audioEngine.isMusicPlaying(); + }, + + /** + * The volume of the music max value is 1.0,the min value is 0.0 . + * @returns {Number} + */ + getBackgroundMusicVolume: function () { + return cc.audioEngine.getMusicVolume(); + }, + + /** + * Set the volume of music. + * @param {Number} volume must be in 0.0~1.0 . + */ + setBackgroundMusicVolume: function (volume) { + cc.audioEngine.setMusicVolume(volume); + }, + + /** + * The volume of the effects max value is 1.0,the min value is 0.0 . + * @returns {Number} + */ + getEffectsVolume: function () { + return cc.audioEngine.getEffectsVolume(); + }, + + /** + * Set the volume of sound effects. + * @param {Number} volume + */ + setEffectsVolume: function (volume) { + cc.audioEngine.setEffectsVolume(volume); + }, + + /** + * Play sound effect. + * @param {String} [pszFilePath] + * @param {Boolean} [loop] + * @returns {Boolean} + */ + playEffect: function (pszFilePath, loop) { + if (pszFilePath) + return cc.audioEngine.playEffect(pszFilePath, loop); + else + return cc.audioEngine.playEffect(this._filePath, this._loop); + }, + + /** + * Pause playing sound effect. + * @param {Number} soundId + */ + pauseEffect: function (soundId) { + cc.audioEngine.pauseEffect(soundId); + }, + + /** + * Pause all effects + */ + pauseAllEffects: function () { + cc.audioEngine.pauseAllEffects(); + }, + + /** + * Resume effect + * @param {Number} soundId + */ + resumeEffect: function (soundId) { + cc.audioEngine.resumeEffect(soundId); + }, + + /** + * Resume all effects + */ + resumeAllEffects: function () { + cc.audioEngine.resumeAllEffects(); + }, + + /** + * Stop effect + * @param {Number} soundId + */ + stopEffect: function (soundId) { + cc.audioEngine.stopEffect(soundId); + }, + + /** + * stop all effects + */ + stopAllEffects: function () { + cc.audioEngine.stopAllEffects(); + }, + + /** + * Preload effect + * @param {String} pszFilePath + */ + preloadEffect: function (pszFilePath) { + cc.loader.getRes(pszFilePath); + this.setFile(pszFilePath); + this.setLoop(false); + }, + + /** + * Unload effect + * @param {String} pszFilePath + */ + unloadEffect: function (pszFilePath) { + cc.audioEngine.unloadEffect(pszFilePath); + }, + + /** + * File path setter + * @param {String} pszFilePath + */ + setFile: function (pszFilePath) { + this._filePath = pszFilePath; + }, + + /** + * Sets audio component whether plays loop + * @param {Boolean} loop + */ + setLoop: function (loop) { + this._loop = loop; + }, + + /** + * Returns the file path of audio component. + * @returns {string} + */ + getFile: function () { + return this._filePath; + }, + + /** + * Returns audio component whether plays loop + * @returns {boolean} + */ + isLoop: function () { + return this._loop; + } +}); + +/** + * allocates and initializes a ComAudio. + * @deprecated since v3.0, please use new construction instead. + * @return {ccs.ComAudio} + * @example + * // example + * var com = ccs.ComAudio.create(); + */ +ccs.ComAudio.create = function () { + return new ccs.ComAudio(); +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/components/CCComController.js b/frameworks/cocos2d-html5/extensions/cocostudio/components/CCComController.js new file mode 100644 index 0000000..cdd85ee --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/components/CCComController.js @@ -0,0 +1,76 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The controller component for Cocostudio. + * @class + * @extends ccs.Component + */ +ccs.ComController = ccs.Component.extend(/** @lends ccs.ComController# */{ + /** + * Construction of ccs.ComController. + */ + ctor: function () { + cc.Component.prototype.ctor.call(this); + this._name = "ComController"; + ccs.ComController.prototype.init.call(this); + }, + + /** + * The callback calls when controller component enter stage. + * @override + */ + onEnter: function () { + if (this._owner !== null) + this._owner.scheduleUpdate(); + }, + + /** + * Returns controller component whether is enabled + * @returns {Boolean} + */ + isEnabled: function () { + return this._enabled; + }, + + /** + * Sets controller component whether is enabled + * @param {Boolean} bool + */ + setEnabled: function (bool) { + this._enabled = bool; + } +}); +/** + * Allocates and initializes a ComController. + * @deprecated since v3.0, please use new construction instead. + * @return {ccs.ComController} + * @example + * // example + * var com = ccs.ComController.create(); + */ +ccs.ComController.create = function () { + return new ccs.ComController(); +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/components/CCComRender.js b/frameworks/cocos2d-html5/extensions/cocostudio/components/CCComRender.js new file mode 100644 index 0000000..960e096 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/components/CCComRender.js @@ -0,0 +1,91 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The render component for Cocostudio. + * @class + * @extends ccs.Component + */ +ccs.ComRender = ccs.Component.extend(/** @lends ccs.ComRender# */{ + _render: null, + /** + * Construction of ccs.ComRender + * @param {cc.Node} node + * @param {String} comName + */ + ctor: function (node, comName) { + cc.Component.prototype.ctor.call(this); + this._render = node; + this._name = comName; + this.isRenderer = true; + ccs.ComRender.prototype.init.call(this); + }, + + /** + * The callback calls when a render component enter stage. + */ + onEnter: function () { + if (this._owner) + this._owner.addChild(this._render); + }, + + /** + * The callback calls when a render component exit stage. + */ + onExit: function () { + if (this._owner) { + this._owner.removeChild(this._render, true); + this._render = null; + } + }, + + /** + * Returns a render node + * @returns {cc.Node} + */ + getNode: function () { + return this._render; + }, + + /** + * Sets a render node to component. + * @param {cc.Node} node + */ + setNode: function (node) { + this._render = node; + } +}); + +/** + * allocates and initializes a ComRender. + * @deprecated since v3.0, please use new construction instead. + * @return {ccs.ComRender} + * @example + * // example + * var com = ccs.ComRender.create(); + */ +ccs.ComRender.create = function (node, comName) { + return new ccs.ComRender(node, comName); +}; diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/components/CCComponent.js b/frameworks/cocos2d-html5/extensions/cocostudio/components/CCComponent.js new file mode 100644 index 0000000..5084a97 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/components/CCComponent.js @@ -0,0 +1,136 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The base class of component in CocoStudio + * @class + * @extends cc.Class + */ +cc.Component = cc.Class.extend(/** @lends cc.Component# */{ + _owner: null, + _name: "", + _enabled: true, + + /** + * Construction of cc.Component + */ + ctor:function(){ + this._owner = null; + this._name = ""; + this._enabled = true; + }, + + /** + * Initializes a cc.Component. + * @returns {boolean} + */ + init:function(){ + return true; + }, + + /** + * The callback when a component enter stage. + */ + onEnter:function(){ + }, + + /** + * The callback when a component exit stage. + */ + onExit:function(){ + }, + + /** + * The callback per every frame if it schedules update. + * @param delta + */ + update:function(delta){ + }, + + /** + * Serialize a component object. + * @param reader + */ + serialize:function( reader){ + }, + + /** + * Returns component whether is enabled. + * @returns {boolean} + */ + isEnabled:function(){ + return this._enabled; + }, + + /** + * Sets component whether is enabled. + * @param enable + */ + setEnabled:function(enable){ + this._enabled = enable; + }, + + /** + * Returns the name of cc.Component. + * @returns {string} + */ + getName:function(){ + return this._name; + } , + + /** + * Sets the name to cc.Component. + * @param {String} name + */ + setName:function(name){ + this._name = name; + } , + + /** + * Sets the owner to cc.Component. + * @param owner + */ + setOwner:function(owner){ + this._owner = owner; + }, + + /** + * Returns the owner of cc.Component. + * @returns {*} + */ + getOwner:function(){ + return this._owner; + } +}); + +/** + * Allocates and initializes a component. + * @deprecated since v3.0, please use new construction instead. + * @return {cc.Component} + */ +cc.Component.create = function(){ + return new cc.Component(); +}; + diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/components/CCComponentContainer.js b/frameworks/cocos2d-html5/extensions/cocostudio/components/CCComponentContainer.js new file mode 100644 index 0000000..0b9b461 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/components/CCComponentContainer.js @@ -0,0 +1,159 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The component container for Cocostudio, it has some components. + * @class + * @extends cc.Class + */ +cc.ComponentContainer = cc.Class.extend(/** @lends cc.ComponentContainer# */{ + _components:null, + _owner:null, + + /** + * Construction of cc.ComponentContainer + * @param node + */ + ctor:function(node){ + this._components = null; + this._owner = node; + }, + + /** + * Gets component by name. + * @param name + * @returns {*} + */ + getComponent:function(name){ + if(!name) + throw new Error("cc.ComponentContainer.getComponent(): name should be non-null"); + name = name.trim(); + if(!this._components){ + this._components = {}; + } + return this._components[name]; + }, + + /** + * Adds a component to container + * @param {cc.Component} component + * @returns {boolean} + */ + add:function(component){ + if(!component) + throw new Error("cc.ComponentContainer.add(): component should be non-null"); + if(component.getOwner()){ + cc.log("cc.ComponentContainer.add(): Component already added. It can't be added again"); + return false; + } + + if(this._components == null){ + this._components = {}; + this._owner.scheduleUpdate(); + } + var oldComponent = this._components[component.getName()]; + if(oldComponent){ + cc.log("cc.ComponentContainer.add(): Component already added. It can't be added again"); + return false; + } + component.setOwner(this._owner); + this._components[component.getName()] = component; + component.onEnter(); + return true; + }, + + /** + * Removes component from container by name or component object. + * @param {String|cc.Component} name component name or component object. + * @returns {boolean} + */ + remove:function(name){ + if(!name) + throw new Error("cc.ComponentContainer.remove(): name should be non-null"); + if(!this._components) + return false; + if(name instanceof cc.Component) + return this._removeByComponent(name); + else { + name = name.trim(); + return this._removeByComponent(this._components[name]); + } + }, + + _removeByComponent:function(component){ + if(!component) + return false; + component.onExit(); + component.setOwner(null); + delete this._components[component.getName()]; + return true; + }, + + /** + * Removes all components of container. + */ + removeAll:function(){ + if(!this._components) + return; + var locComponents = this._components; + for(var selKey in locComponents){ + var selComponent = locComponents[selKey]; + selComponent.onExit(); + selComponent.setOwner(null); + delete locComponents[selKey]; + } + this._owner.unscheduleUpdate(); + this._components = null; + }, + + _alloc:function(){ + this._components = {}; + }, + + /** + * Visit callback by director. it calls every frame. + * @param {Number} delta + */ + visit:function(delta){ + if(!this._components) + return; + + var locComponents = this._components; + for(var selKey in locComponents) + locComponents[selKey].update(delta); + }, + + /** + * Returns the container whether is empty. + * @returns {boolean} + */ + isEmpty: function () { + if (!this._components) + return true; + return this._components.length === 0; + } +}); + + diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/loader/load.js b/frameworks/cocos2d-html5/extensions/cocostudio/loader/load.js new file mode 100644 index 0000000..bf75cce --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/loader/load.js @@ -0,0 +1,280 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +ccs._load = (function () { + + /** + * load file + * @param {String} file + * @param {String} [type=] - ccui|node|action + * @param {String} [path=] - Resource search path + * @returns {*} + */ + var load = function (file, type, path) { + + var json = cc.loader.getRes(file); + + if (!json) + return cc.log("%s does not exist", file); + var ext = extname(file).toLocaleLowerCase(); + if (ext !== "json" && ext !== "exportjson") + return cc.log("%s load error, must be json file", file); + + var parse; + if (!type) { + if (json["widgetTree"]) + parse = parser["ccui"]; + else if (json["nodeTree"]) + parse = parser["timeline"]; + else if (json["Content"] && json["Content"]["Content"]) + parse = parser["timeline"]; + else if (json["gameobjects"]) + parse = parser["scene"]; + } else { + parse = parser[type]; + } + + if (!parse) { + cc.log("Can't find the parser : %s", file); + return new cc.Node(); + } + var version = json["version"] || json["Version"]; + if (!version && json["armature_data"]) { + cc.warn("%s is armature. please use:", file); + cc.warn(" ccs.armatureDataManager.addArmatureFileInfoAsync(%s);", file); + cc.warn(" var armature = new ccs.Armature('name');"); + return new cc.Node(); + } + var currentParser = getParser(parse, version); + if (!currentParser) { + cc.log("Can't find the parser : %s", file); + return new cc.Node(); + } + + return currentParser.parse(file, json, path) || null; + }; + + var parser = { + "ccui": {}, + "timeline": {}, + "action": {}, + "scene": {} + }; + + load.registerParser = function (name, version, target) { + if (!name || !version || !target) + return cc.log("register parser error"); + if (!parser[name]) + parser[name] = {}; + parser[name][version] = target; + }; + + load.getParser = function (name, version) { + if (name && version) + return parser[name] ? parser[name][version] : undefined; + if (name) + return parser[name]; + return parser; + }; + + //Gets the file extension + var extname = function (fileName) { + var arr = fileName.match(extnameReg); + return ( arr && arr[1] ) ? arr[1] : null; + }; + var extnameReg = /\.([^\.]+)$/; + + + var parserReg = /([^\.](\.\*)?)*$/; + var getParser = function (parser, version) { + if (parser[version]) + return parser[version]; + else if (version === "*") + return null; + else + return getParser(parser, version.replace(parserReg, "*")); + }; + + return load; + +})(); + +ccs._parser = cc.Class.extend({ + + ctor: function () { + this.parsers = {}; + }, + + _dirnameReg: /\S*\//, + _dirname: function (path) { + var arr = path.match(this._dirnameReg); + return (arr && arr[0]) ? arr[0] : ""; + }, + + getClass: function (json) { + return json["classname"]; + }, + + getNodeJson: function (json) { + return json["widgetTree"]; + }, + + parse: function (file, json, resourcePath) { + resourcePath = resourcePath || this._dirname(file); + this.pretreatment(json, resourcePath); + var node = this.parseNode(this.getNodeJson(json), resourcePath, file); + node && this.deferred(json, resourcePath, node, file); + return node; + }, + + pretreatment: function (json, resourcePath, file) { + }, + + deferred: function (json, resourcePath, node, file) { + }, + + parseNode: function (json, resourcePath) { + var parser = this.parsers[this.getClass(json)]; + var widget = null; + if (parser) + widget = parser.call(this, json, resourcePath); + else + cc.log("Can't find the parser : %s", this.getClass(json)); + + return widget; + }, + + registerParser: function (widget, parse) { + this.parsers[widget] = parse; + } +}); + +/** + * Analysis of studio JSON file + * The incoming file name, parse out the corresponding object + * Temporary support file list: + * ui 1.* + * node 1.* - 2.* + * action 1.* - 2.* + * scene 0.* - 1.* + * @param {String} file + * @param {String} [path=] Resource path + * @returns {{node: cc.Node, action: cc.Action}} + */ +ccs.load = function (file, path) { + var object = { + node: null, + action: null + }; + + object.node = ccs._load(file, null, path); + object.action = ccs._load(file, "action", path); + if (object.action && object.action.tag === -1 && object.node) + object.action.tag = object.node.tag; + return object; +}; +ccs.load.validate = {}; + +ccs.load.preload = true; + +/** + * Analysis of studio JSON file and layout ui widgets by visible size. + * The incoming file name, parse out the corresponding object + * Temporary support file list: + * ui 1.* + * node 1.* - 2.* + * action 1.* - 2.* + * scene 0.* - 1.* + * @param {String} file + * @param {String} [path=] Resource path + * @returns {{node: cc.Node, action: cc.Action}} + */ +ccs.loadWithVisibleSize = function (file, path) { + var object = ccs.load(file, path); + var size = cc.director.getVisibleSize(); + if (object.node && size) { + object.node.setContentSize(size.width, size.height); + ccui.helper.doLayout(object.node); + } + return object; +}; + +//Forward compatible interface + +ccs.actionTimelineCache = { + + + //@deprecated This function will be deprecated sooner or later please use ccs.load + /** + * Create Timeline Action + * @param file + * @returns {*} + */ + createAction: function (file) { + return ccs._load(file, "action"); + } +}; + +ccs.csLoader = { + + //@deprecated This function will be deprecated sooner or later please use ccs.load + /** + * Create Timeline Node + * @param file + * @returns {*} + */ + createNode: function (file) { + return ccs._load(file); + } +}; + +cc.loader.register(["json"], { + load: function (realUrl, url, res, cb) { + cc.loader.loadJson(realUrl, function (error, data) { + var path = cc.path; + if (data && data["Content"] && data["Content"]["Content"]["UsedResources"]) { + var UsedResources = data["Content"]["Content"]["UsedResources"], + dirname = path.dirname(url), + list = [], + tmpUrl, normalUrl; + for (var i = 0; i < UsedResources.length; i++) { + if (!ccs.load.preload && /\.(png|jpg$)/.test(UsedResources[i])) + continue; + tmpUrl = path.join(dirname, UsedResources[i]); + normalUrl = path._normalize(tmpUrl); + if (!ccs.load.validate[normalUrl]) { + ccs.load.validate[normalUrl] = true; + list.push(normalUrl); + } + } + cc.loader.load(list, function () { + cb(error, data); + }); + } else { + cb(error, data); + } + + }); + } +}); diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/loader/parsers/action-1.x.js b/frameworks/cocos2d-html5/extensions/cocostudio/loader/parsers/action-1.x.js new file mode 100644 index 0000000..f3b6ff1 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/loader/parsers/action-1.x.js @@ -0,0 +1,233 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function(load, baseParser){ + + var Parser = baseParser.extend({ + + getNodeJson: function(json){ + return json["action"]; + }, + + parseNode: function(json, resourcePath, file){ + if(!json) + return null; + + var self = this, + action = new ccs.ActionTimeline(); + + action.setDuration(json["duration"]); + action.setTimeSpeed(json["speed"] || 1); + //The process of analysis + var timelines = json["timelines"]; + timelines.forEach(function(timeline){ + var parser = self.parsers[timeline["frameType"]]; + var frame; + if(parser) + frame = parser.call(self, timeline, resourcePath); + else + cc.log("parser does not exist : %s", timeline["frameType"]); + if(frame) + action.addTimeline(frame); + + if(timeline["frameType"] === "ColorFrame"){ + action.addTimeline( + self.parsers["AlphaFrame"].call(self, timeline, resourcePath) + ); + } + }); + + return action; + } + + }); + + var parser = new Parser(); + + var frameList = [ + { + name: "PositionFrame", + handle: function(options){ + var frame = new ccs.PositionFrame(); + var x = options["x"]; + var y = options["y"]; + frame.setPosition(cc.p(x,y)); + return frame; + } + }, + { + name: "VisibleFrame", + handle: function(options){ + var frame = new ccs.VisibleFrame(); + var visible = options["value"]; + frame.setVisible(visible); + return frame; + } + }, + { + name: "ScaleFrame", + handle: function(options){ + var frame = new ccs.ScaleFrame(); + var scalex = options["x"]; + var scaley = options["y"]; + frame.setScaleX(scalex); + frame.setScaleY(scaley); + return frame; + } + }, + { + name: "RotationFrame", + handle: function(options){ + var frame = new ccs.RotationFrame(); + var rotation = options["rotation"]; + frame.setRotation(rotation); + return frame; + } + }, + { + name: "SkewFrame", + handle: function(options){ + var frame = new ccs.SkewFrame(); + var skewx = options["x"]; + var skewy = options["y"]; + frame.setSkewX(skewx); + frame.setSkewY(skewy); + return frame; + } + }, + { + name: "RotationSkewFrame", + handle: function(options){ + var frame = new ccs.RotationSkewFrame(); + var skewx = options["x"]; + var skewy = options["y"]; + frame.setSkewX(skewx); + frame.setSkewY(skewy); + return frame; + } + }, + { + name: "AnchorFrame", + handle: function(options){ + var frame = new ccs.AnchorPointFrame(); + var anchorx = options["x"]; + var anchory = options["y"]; + frame.setAnchorPoint(cc.p(anchorx, anchory)); + return frame; + } + }, + { + name: "InnerActionFrame", + handle: function(options){ + var frame = new ccs.InnerActionFrame(); + var type = options["innerActionType"]; + var startFrame = options["startFrame"]; + frame.setInnerActionType(type); + frame.setStartFrameIndex(startFrame); + return frame; + } + }, + { + name: "ColorFrame", + handle: function(options){ + var frame = new ccs.ColorFrame(); + var red = options["red"]; + var green = options["green"]; + var blue = options["blue"]; + frame.setColor(cc.color(red, green, blue)); + var alphaFrame = new ccs.AlphaFrame(); + var alpha = options["alpha"]; + alphaFrame.setAlpha(alpha); + return frame; + } + }, + { + name: "AlphaFrame", + handle: function(options){ + var frame = new ccs.AlphaFrame(); + var alpha = options["alpha"]; + frame.setAlpha(alpha); + return frame; + } + }, + { + name: "TextureFrame", + handle: function(options){ + var frame = new ccs.TextureFrame(); + var texture = options["value"]; + if(texture != null) { + var path = texture; + var spriteFrame = cc.spriteFrameCache.getSpriteFrame(path); + if(spriteFrame == null){ + var jsonPath = ccs.csLoader.getJsonPath(); + path = jsonPath + texture; + } + frame.setTextureName(path); + } + return frame; + } + }, + { + name: "EventFrame", + handle: function(options){ + var frame = new ccs.EventFrame(); + var evnt = options["value"]; + if(evnt != null) + frame.setEvent(evnt); + return frame; + } + }, + { + name: "ZOrderFrame", + handle: function(options){ + var frame = new ccs.ZOrderFrame(); + var zorder = options["value"]; + frame.setZOrder(zorder); + return frame; + } + } + ]; + + frameList.forEach(function(item){ + parser.registerParser(item.name, function(options, resourcePath){ + var timeline = new ccs.Timeline(); + timeline.setActionTag(options["actionTag"]); + + var frames = options["frames"]; + if(frames && frames.length){ + frames.forEach(function(frameData){ + var frame = item.handle(frameData); + frame.setFrameIndex(frameData["frameIndex"]); + frame.setTween(frameData["tween"]); + timeline.addFrame(frame); + }); + } + return timeline; + }); + }); + + load.registerParser("action", "0.*", parser); + load.registerParser("action", "1.*", parser); + +})(ccs._load, ccs._parser); diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/loader/parsers/action-2.x.js b/frameworks/cocos2d-html5/extensions/cocostudio/loader/parsers/action-2.x.js new file mode 100644 index 0000000..febdad1 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/loader/parsers/action-2.x.js @@ -0,0 +1,310 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function(load, baseParser){ + + var Parser = baseParser.extend({ + + getNodeJson: function(json){ + return json["Content"]["Content"]["Animation"]; + }, + + parseNode: function(json, resourcePath, file){ + if(!json) + return null; + + var self = this, + action = new ccs.ActionTimeline(); + + action.setDuration(json["Duration"]); + action.setTimeSpeed(json["Speed"] || 1); + + //The process of analysis + var timelines = json["Timelines"]; + timelines.forEach(function(timeline){ + var parser = self.parsers[timeline["Property"]]; + var frame; + if(parser) + frame = parser.call(self, timeline, resourcePath); + else + cc.log("parser does not exist : %s", timeline["Property"]); + if(frame) + action.addTimeline(frame); + }); + + return action; + }, + + deferred: function(json, resourcePath, action, file){ + var animationlist = json["Content"]["Content"]["AnimationList"]; + var length = animationlist ? animationlist.length : 0; + for (var i = 0; i < length; i++){ + var animationdata = animationlist[i]; + var info = { name: null, startIndex: null, endIndex: null }; + info.name = animationdata["Name"]; + info.startIndex = animationdata["StartIndex"]; + info.endIndex = animationdata["EndIndex"]; + action.addAnimationInfo(info); + } + } + + }); + var parser = new Parser(); + + var frameList = [ + { + name: "Position", + handle: function(options){ + var frame = new ccs.PositionFrame(); + var x = options["X"]; + var y = options["Y"]; + frame.setPosition(cc.p(x,y)); + return frame; + } + }, + { + name: "VisibleForFrame", + handle: function(options){ + var frame = new ccs.VisibleFrame(); + var visible = options["Value"]; + frame.setVisible(visible); + return frame; + } + }, + { + name: "Scale", + handle: function(options){ + var frame = new ccs.ScaleFrame(); + var scalex = options["X"]; + var scaley = options["Y"]; + frame.setScaleX(scalex); + frame.setScaleY(scaley); + return frame; + } + }, + { + name: "Rotation", + handle: function(options){ + var frame = new ccs.RotationFrame(); + var rotation = options["Rotation"] || options["Value"] || 0; + frame.setRotation(rotation); + return frame; + } + }, + { + name: "Skew", + handle: function(options){ + var frame = new ccs.SkewFrame(); + var skewx = options["X"]; + var skewy = options["Y"]; + frame.setSkewX(skewx); + frame.setSkewY(skewy); + return frame; + } + }, + { + name: "RotationSkew", + handle: function(options){ + var frame = new ccs.RotationSkewFrame(); + var skewx = options["X"]; + var skewy = options["Y"]; + frame.setSkewX(skewx); + frame.setSkewY(skewy); + return frame; + } + }, + { + name: "Anchor", + handle: function(options){ + var frame = new ccs.AnchorPointFrame(); + var anchorx = options["X"]; + var anchory = options["Y"]; + frame.setAnchorPoint(cc.p(anchorx, anchory)); + return frame; + } + },{ + name: "AnchorPoint", + handle: function(options){ + var frame = new ccs.AnchorPointFrame(); + var anchorx = options["X"]; + var anchory = options["Y"]; + frame.setAnchorPoint(cc.p(anchorx, anchory)); + return frame; + } + },{ + name: "InnerAction", + handle: function(options){ + var frame = new ccs.InnerActionFrame(); + var type = options["InnerActionType"]; + var startFrame = options["StartFrame"]; + frame.setInnerActionType(type); + frame.setStartFrameIndex(startFrame); + return frame; + } + }, + { + name: "CColor", + handle: function(options){ + var frame = new ccs.ColorFrame(); + var color = options["Color"]; + if(!color) color = {}; + color["R"] = color["R"] === undefined ? 255 : color["R"]; + color["G"] = color["G"] === undefined ? 255 : color["G"]; + color["B"] = color["B"] === undefined ? 255 : color["B"]; + frame.setColor(cc.color(color["R"], color["G"], color["B"])); + return frame; + } + }, + { + name: "Alpha", + handle: function(options){ + var frame = new ccs.AlphaFrame(); + var alpha = options["Value"]; + frame.setAlpha(alpha); + return frame; + } + }, + { + name: "FileData", + handle: function(options, resourcePath){ + var frame, texture, plist, path, spriteFrame; + frame = new ccs.TextureFrame(); + texture = options["TextureFile"]; + if(texture != null) { + plist = texture["Plist"]; + path = texture["Path"]; + spriteFrame = cc.spriteFrameCache.getSpriteFrame(path); + if(!spriteFrame && plist){ + if(cc.loader.getRes(resourcePath + plist)){ + cc.spriteFrameCache.addSpriteFrames(resourcePath + plist); + spriteFrame = cc.spriteFrameCache.getSpriteFrame(path); + }else{ + cc.log("%s need to be preloaded", resourcePath + plist); + } + } + if(spriteFrame == null){ + path = resourcePath + path; + } + frame.setTextureName(path); + } + return frame; + } + }, + { + name: "FrameEvent", + handle: function(options){ + var frame = new ccs.EventFrame(); + var evnt = options["Value"]; + if(evnt != null) + frame.setEvent(evnt); + return frame; + } + }, + { + name: "ZOrder", + handle: function(options){ + var frame = new ccs.ZOrderFrame(); + var zorder = options["Value"]; + frame.setZOrder(zorder); + return frame; + } + }, + { + name: "ActionValue", + handle: function (options) { + + var frame = new ccs.InnerActionFrame(); + var innerActionType = options["InnerActionType"]; + + var currentAnimationFrame = options["CurrentAniamtionName"]; + + var singleFrameIndex = options["SingleFrameIndex"]; + + var frameIndex = options["FrameIndex"]; + if(frameIndex !== undefined) + frame.setFrameIndex(frameIndex); + + frame.setInnerActionType(ccs.InnerActionType[innerActionType]); + frame.setSingleFrameIndex(singleFrameIndex); + + frame.setEnterWithName(true); + if (currentAnimationFrame) + frame.setAnimationName(currentAnimationFrame); + + return frame; + } + }, + { + name: "BlendFunc", + handle: function(options){ + var frame = new ccs.BlendFuncFrame(); + var blendFunc = options["BlendFunc"]; + if(blendFunc && blendFunc["Src"] !== undefined && blendFunc["Dst"] !== undefined) + frame.setBlendFunc(new cc.BlendFunc(blendFunc["Src"], blendFunc["Dst"])); + return frame; + } + } + ]; + + var loadEasingDataWithFlatBuffers = function(frame, options){ + var type = options["Type"]; + frame.setTweenType(type); + var points = options["Points"]; + var result = []; + if(points){ + points.forEach(function(p){ + result.push(p["X"]); + result.push(p["Y"]); + }); + frame.setEasingParams(result); + } + }; + + frameList.forEach(function(item){ + parser.registerParser(item.name, function(options, resourcePath){ + var timeline = new ccs.Timeline(); + timeline.setActionTag(options["ActionTag"]); + + var frames = options["Frames"]; + if(frames && frames.length){ + frames.forEach(function(frameData){ + var frame = item.handle(frameData, resourcePath); + frame.setFrameIndex(frameData["FrameIndex"]); + var tween = frameData["Tween"] != null ? frameData["Tween"] : true; + frame.setTween(tween); + //https://github.com/cocos2d/cocos2d-x/pull/11388/files + var easingData = frameData["EasingData"]; + if(easingData) + loadEasingDataWithFlatBuffers(frame, easingData); + timeline.addFrame(frame); + }); + } + return timeline; + }); + }); + + load.registerParser("action", "2.*", parser); + load.registerParser("action", "*", parser); + +})(ccs._load, ccs._parser); diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/loader/parsers/compatible.js b/frameworks/cocos2d-html5/extensions/cocostudio/loader/parsers/compatible.js new file mode 100644 index 0000000..9a73bdb --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/loader/parsers/compatible.js @@ -0,0 +1,253 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + + +/* + This file is for compatibility compatibility with older versions of GUIReader and SceneReader + todo: deprecated all + */ + +(function () { + + ccs.uiReader = { + + _fileDesignSizes: {}, + + //@deprecated This function will be deprecated sooner or later please use ccs.load + /** + * Create CCUI Node + * @param file + * @returns {*} + */ + widgetFromJsonFile: function (file) { + var json = cc.loader.getRes(file); + if (json) + this._fileDesignSizes[file] = cc.size(json["designWidth"] || 0, json["designHeight"] || 0); + + var version = json["Version"] || json["version"]; + var versionNum = ccs.uiReader.getVersionInteger(version); + if (!version || versionNum >= 1700) { + cc.warn("Not supported file types, Please try use the ccs.load"); + return null; + } + return ccs._load(file, "ccui"); + }, + + //@deprecated This function will be deprecated sooner or later please use parser.registerParser + /** + * Register a custom Widget reader + * @param classType + * @param ins + * @param object + * @param callback + * @deprecated This function will be deprecated sooner or later please use parser.registerParser + */ + registerTypeAndCallBack: function (classType, ins, object, callback) { + var parser = ccs._load.getParser("ccui")["*"]; + var func = callback.bind(object); + parser.registerParser(classType, function (options, resourcePath) { + var widget = new ins(); + var uiOptions = options["options"]; + object.setPropsFromJsonDictionary && object.setPropsFromJsonDictionary(widget, uiOptions); + this.generalAttributes(widget, uiOptions); + var customProperty = uiOptions["customProperty"]; + if (customProperty) + customProperty = JSON.parse(customProperty); + else + customProperty = {}; + func(classType, widget, customProperty); + this.colorAttributes(widget, uiOptions); + this.anchorPointAttributes(widget, uiOptions); + this.parseChild.call(this, widget, options, resourcePath); + return widget; + }); + }, + + //@deprecated This function will be deprecated sooner or later + /** + * Gets the version number by version string. + * @param {String} version version string. + * @returns {Number} + */ + getVersionInteger: function (version) { + if (!version || typeof version !== "string") return 0; + var arr = version.split("."); + if (arr.length !== 4) + return 0; + var num = 0; + arr.forEach(function (n, i) { + num += n * Math.pow(10, 3 - i); + }); + return num; + }, + + //@deprecated This function will be deprecated sooner or later + /** + * stores the designSize of UI file. + * @param {String} fileName + * @param {cc.Size} size + */ + storeFileDesignSize: function (fileName, size) { + this._fileDesignSizes[fileName] = size; + }, + + //@deprecated This function will be deprecated sooner or later + /** + * Gets the design size by filename. + * @param {String} fileName + * @returns {cc.Size} + */ + getFileDesignSize: function (fileName) { + return this._fileDesignSizes[fileName]; + }, + + //@deprecated This function will be deprecated sooner or later + /** + * Returns the file path + * @returns {string} + */ + getFilePath: function () { + return this._filePath; + }, + + //@deprecated This function will be deprecated sooner or later + setFilePath: function (path) { + this._filePath = path; + }, + + //@deprecated This function will be deprecated sooner or later + /** + * Returns the parsed object map. (analytic function) + * @returns {Object} + */ + getParseObjectMap: function () { + return ccs._load.getParser("ccui")["*"]["parsers"]; + }, + + //@deprecated This function will be deprecated sooner or later + /** + * Returns the parsed callback map. (analytic function) + * @returns {*} + */ + getParseCallBackMap: function () { + return ccs._load.getParser("ccui")["*"]["parsers"]; + }, + + //@deprecated This function will be deprecated sooner or later + clear: function () { + } + }; + + var parser = ccs._load.getParser("ccui")["*"]; + ccs.imageViewReader = {setPropsFromJsonDictionary: parser.ImageViewAttributes}; + ccs.buttonReader = {setPropsFromJsonDictionary: parser.ButtonAttributes}; + ccs.checkBoxReader = {setPropsFromJsonDictionary: parser.CheckBoxAttributes}; + ccs.labelAtlasReader = {setPropsFromJsonDictionary: parser.TextAtlasAttributes}; + ccs.labelBMFontReader = {setPropsFromJsonDictionary: parser.TextBMFontAttributes}; + ccs.labelReader = {setPropsFromJsonDictionary: parser.TextAttributes}; + ccs.layoutReader = {setPropsFromJsonDictionary: parser.LayoutAttributes}; + ccs.listViewReader = {setPropsFromJsonDictionary: parser.ListViewAttributes}; + ccs.loadingBarReader = {setPropsFromJsonDictionary: parser.LoadingBarAttributes}; + ccs.pageViewReader = {setPropsFromJsonDictionary: parser.PageViewAttributes}; + ccs.scrollViewReader = {setPropsFromJsonDictionary: parser.ScrollViewAttributes}; + ccs.sliderReader = {setPropsFromJsonDictionary: parser.SliderAttributes}; + ccs.textFieldReader = {setPropsFromJsonDictionary: parser.TextFieldAttributes}; +})(); + +(function () { + ccs.sceneReader = { + + _node: null, + + //@deprecated This function will be deprecated sooner or later please use ccs.load + /** + * Create Scene Node + * @param file + * @returns {*} + */ + createNodeWithSceneFile: function (file) { + var node = ccs._load(file, "scene"); + this._node = node; + return node; + }, + + /** + * Get a node by tag. + * @param {Number} tag + * @returns {cc.Node|null} + */ + getNodeByTag: function (tag) { + if (this._node == null) + return null; + if (this._node.getTag() === tag) + return this._node; + return this._nodeByTag(this._node, tag); + }, + + _nodeByTag: function (parent, tag) { + if (parent == null) + return null; + var retNode = null; + var children = parent.getChildren(); + for (var i = 0; i < children.length; i++) { + var child = children[i]; + if (child && child.getTag() === tag) { + retNode = child; + break; + } else { + retNode = this._nodeByTag(child, tag); + if (retNode) + break; + } + } + return retNode; + }, + + //@deprecated This function will be deprecated sooner or later + /** + * Returns the version of ccs.SceneReader. + * @returns {string} + */ + version: function () { + return "*"; + }, + + //@deprecated This function will be deprecated sooner or later + /** + * Sets the listener to reader. + * Cannot use + */ + setTarget: function () { + }, + + //@deprecated This function will be deprecated sooner or later + /** + * Clear all triggers and stops all sounds. + */ + clear: function () { + ccs.triggerManager.removeAll(); + cc.audioEngine.end(); + } + }; +})(); diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/loader/parsers/scene-1.x.js b/frameworks/cocos2d-html5/extensions/cocostudio/loader/parsers/scene-1.x.js new file mode 100644 index 0000000..8982f26 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/loader/parsers/scene-1.x.js @@ -0,0 +1,264 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function (load, baseParser) { + + var Parser = baseParser.extend({ + + getNodeJson: function (json) { + return json; + }, + + parseNode: function (json, resourcePath) { + var parser = this.parsers[this.getClass(json)]; + var node = null; + if (parser) + node = parser.call(this, json, resourcePath); + else + cc.log("Can't find the parser : %s", this.getClass(json)); + + return node; + }, + + deferred: function (json, resourcePath, node, file) { + ccs.triggerManager.parse(json["Triggers"] || []); + if (ccs.sceneReader) + ccs.sceneReader._node = node; + }, + + setPropertyFromJsonDict: function (node, json) { + var x = (cc.isUndefined(json["x"])) ? 0 : json["x"]; + var y = (cc.isUndefined(json["y"])) ? 0 : json["y"]; + node.setPosition(x, y); + + var bVisible = Boolean((cc.isUndefined(json["visible"])) ? 1 : json["visible"]); + node.setVisible(bVisible); + + var nTag = (cc.isUndefined(json["objecttag"])) ? -1 : json["objecttag"]; + node.setTag(nTag); + + var nZorder = (cc.isUndefined(json["zorder"])) ? 0 : json["zorder"]; + node.setLocalZOrder(nZorder); + + var fScaleX = (cc.isUndefined(json["scalex"])) ? 1 : json["scalex"]; + var fScaleY = (cc.isUndefined(json["scaley"])) ? 1 : json["scaley"]; + node.setScaleX(fScaleX); + node.setScaleY(fScaleY); + + var fRotationZ = (cc.isUndefined(json["rotation"])) ? 0 : json["rotation"]; + node.setRotation(fRotationZ); + + var sName = json["name"] || ""; + node.setName(sName); + } + + }); + + var parser = new Parser(); + + parser.parseChild = function (node, objects, resourcePath) { + for (var i = 0; i < objects.length; i++) { + var child, + options = objects[i]; + if (options) + child = this.parseNode(options, resourcePath); + if (child) + node.addChild(child); + } + }; + + var componentsParser = { + "CCSprite": function (node, component, resourcePath) { + var child = new cc.Sprite(); + loadTexture(component["fileData"], resourcePath, function (path, type) { + if (type === 0) + child.setTexture(path); + else if (type === 1) { + var spriteFrame = cc.spriteFrameCache.getSpriteFrame(path); + child.setSpriteFrame(spriteFrame); + } + }); + var render = new ccs.ComRender(child, "CCSprite"); + node.addComponent(render); + return render; + }, + "CCTMXTiledMap": function (node, component, resourcePath) { + var child = null; + loadTexture(component["fileData"], resourcePath, function (path, type) { + if (type === 0) + child = new cc.TMXTiledMap(path); + }); + var render = new ccs.ComRender(child, "CCTMXTiledMap"); + node.addComponent(render); + return render; + }, + "CCParticleSystemQuad": function (node, component, resourcePath) { + var child = null; + loadTexture(component["fileData"], resourcePath, function (path, type) { + if (type === 0) + child = new cc.ParticleSystem(path); + else + cc.log("unknown resourcetype on CCParticleSystemQuad!"); + child.setPosition(0, 0); + }); + var render = new ccs.ComRender(child, "CCParticleSystemQuad"); + node.addComponent(render); + return render; + }, + "CCArmature": function (node, component, resourcePath) { + var child = null; + loadTexture(component["fileData"], resourcePath, function (path, type) { + if (type === 0) { + var jsonDict = cc.loader.getRes(path); + if (!jsonDict) cc.log("Please load the resource [%s] first!", path); + var armature_data = jsonDict["armature_data"]; + var subData = armature_data[0]; + var name = subData["name"]; + ccs.armatureDataManager.addArmatureFileInfo(path); + child = new ccs.Armature(name); + } + }); + if (child) { + var render = new ccs.ComRender(child, "CCArmature"); + node.addComponent(render); + var actionName = component["selectedactionname"]; + if (actionName && child.getAnimation()) + child.getAnimation().play(actionName); + + return render; + } + + }, + "CCComAudio": function (node, component, resourcePath) { + var audio = null; + loadTexture(component["fileData"], resourcePath, function (path, type) { + if (type === 0) { + audio = new ccs.ComAudio(); + audio.preloadEffect(path); + var name = component["name"]; + if (name) + audio.setName(name); + node.addComponent(audio); + } + }); + }, + "CCComAttribute": function (node, component, resourcePath) { + var attribute = null; + loadTexture(component["fileData"], resourcePath, function (path, type) { + if (type === 0) { + attribute = new ccs.ComAttribute(); + if (path !== "") + attribute.parse(path); + node.addComponent(attribute); + } else + cc.log("unknown resourcetype on CCComAttribute!"); + }); + return attribute; + }, + "CCBackgroundAudio": function (node, component, resourcePath) { + var audio = null; + loadTexture(component["fileData"], resourcePath, function (path, type) { + if (type === 0) { + audio = new ccs.ComAudio(); + audio.preloadBackgroundMusic(path); + audio.setFile(path); + var bLoop = Boolean(component["loop"] || 0); + audio.setLoop(bLoop); + var name = component["name"]; + if (name) + audio.setName(name); + node.addComponent(audio); + audio.playBackgroundMusic(path, bLoop); + } + }); + }, + "GUIComponent": function (node, component, resourcePath) { + var widget = null; + loadTexture(component["fileData"], resourcePath, function (path, type) { + widget = ccs._load(path, "ccui"); + }); + var render = new ccs.ComRender(widget, "GUIComponent"); + node.addComponent(render); + return render; + }, + "CCScene": function () { + } + }; + var loadedPlist = {}; + var loadTexture = function (json, resourcePath, cb) { + if (json != null) { + var path = json["path"]; + var type = json["resourceType"]; + var plist = json["plist"]; + if (!path) + return; + if (plist) { + if (cc.loader.getRes(resourcePath + plist)) { + loadedPlist[resourcePath + plist] = true; + cc.spriteFrameCache.addSpriteFrames(resourcePath + plist); + } else { + if (!loadedPlist[resourcePath + plist]) + cc.log("%s need to be preloaded", resourcePath + plist); + } + } + if (type !== 0) + cb(path, type); + else + cb(resourcePath + path, type); + } + }; + + parser.parseComponents = function (node, json, resourcePath) { + if (!node || !json) + return; + json.forEach(function (component) { + var parser = componentsParser[component["classname"]]; + var render = null; + if (parser) + render = parser(node, component, resourcePath); + else + cc.log("Can't find the component parser : %s", component["classname"]); + var name = component["name"]; + if (render && name) { + render.setName(name); + } + }); + }; + + parser.registerParser("CCNode", function (options, resourcePath) { + var node = new cc.Node(); + this.setPropertyFromJsonDict(node, options); + this.parseChild.call(this, node, options["gameobjects"], resourcePath); + this.parseComponents(node, options["components"], resourcePath); + var size = options["CanvasSize"]; + if (size) + node.setContentSize(cc.size(size["_width"], size["_height"])); + + return node; + }); + + load.registerParser("scene", "*", parser); + + +})(ccs._load, ccs._parser); diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/loader/parsers/timelineParser-1.x.js b/frameworks/cocos2d-html5/extensions/cocostudio/loader/parsers/timelineParser-1.x.js new file mode 100644 index 0000000..1e9d907 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/loader/parsers/timelineParser-1.x.js @@ -0,0 +1,291 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function (load, baseParser) { + + var loadedPlist = {}; + + var Parser = baseParser.extend({ + + getNodeJson: function (json) { + return json["nodeTree"]; + }, + + addSpriteFrame: function (plists, pngs, resourcePath) { + if (!plists || !pngs || plists.length !== pngs.length) + return; + for (var i = 0; i < plists.length; i++) { + var plist = resourcePath + plists[i]; + if (!cc.loader.getRes(plist) && !loadedPlist[plist]) + cc.log("%s need to be preloaded", plist); + else + loadedPlist[plist] = true; + cc.spriteFrameCache.addSpriteFrames( + plist, + resourcePath + pngs[i] + ); + } + }, + + pretreatment: function (json, resourcePath, file) { + this.addSpriteFrame(json["textures"], json["texturesPng"], resourcePath); + } + + }); + var parser = new Parser(); + + parser.generalAttributes = function (node, options) { + var width = options["width"] != null ? options["width"] : 0; + var height = options["height"] != null ? options["height"] : 0; + var x = options["x"] != null ? options["x"] : 0; + var y = options["y"] != null ? options["y"] : 0; + var scalex = options["scaleX"] != null ? options["scaleX"] : 1; + var scaley = options["scaleY"] != null ? options["scaleY"] : 1; + var rotation = options["rotation"] != null ? options["rotation"] : 0; + var rotationSkewX = options["rotationSkewX"] != null ? options["rotationSkewX"] : 0; + var rotationSkewY = options["rotationSkewY"] != null ? options["rotationSkewY"] : 0; + var skewx = options["skewX"] != null ? options["skewX"] : 0; + var skewy = options["skewY"] != null ? options["skewY"] : 0; + var anchorx = options["anchorPointX"] != null ? options["anchorPointX"] : 0.5; + var anchory = options["anchorPointY"] != null ? options["anchorPointY"] : 0.5; + var alpha = options["opacity"] != null ? options["opacity"] : 255; + var red = options["colorR"] != null ? options["colorR"] : 255; + var green = options["colorG"] != null ? options["colorG"] : 255; + var blue = options["colorB"] != null ? options["colorB"] : 255; + var zorder = options["colorR"] != null ? options["colorR"] : 0; + var tag = options["tag"] != null ? options["tag"] : 0; + var actionTag = options["actionTag"] != null ? options["actionTag"] : 0; + var visible = options["visible"] != null ? options["visible"] : true; + + if (x != 0 || y != 0) + node.setPosition(cc.p(x, y)); + if (scalex != 1) + node.setScaleX(scalex); + if (scaley != 1) + node.setScaleY(scaley); + if (rotation != 0) + node.setRotation(rotation); + if (rotationSkewX != 0) + node.setRotationX(rotationSkewX); + if (rotationSkewY != 0) + node.setRotationY(rotationSkewY); + if (skewx != 0) + node.setSkewX(skewx); + if (skewy != 0) + node.setSkewY(skewy); + if (anchorx != 0.5 || anchory != 0.5) + node.setAnchorPoint(cc.p(anchorx, anchory)); + if (width != 0 || height != 0) + node.setContentSize(cc.size(width, height)); + if (zorder != 0) + node.setLocalZOrder(zorder); + if (visible != true) + node.setVisible(visible); + + if (alpha != 255) { + node.setOpacity(alpha); + } + if (red != 255 || green != 255 || blue != 255) { + node.setColor(cc.color(red, green, blue)); + } + + + node.setTag(tag); + node.setUserObject(new ccs.ActionTimelineData(actionTag)); + }; + + parser.parseComponent = function (node, options) { + if (!options) return; + for (var i = 0; i < options.length; ++i) { + var dic = options[i]; + var component = this.loadComponent(dic); + if (component) { + node.addComponent(component); + } + } + }; + + parser.parseChild = function (parse, widget, options, resourcePath) { + var children = options["children"]; + for (var i = 0; i < children.length; i++) { + var child = this.parseNode(children[i], resourcePath); + if (child) { + if (widget instanceof ccui.PageView) { + if (child instanceof ccui.Layout) + widget.addPage(child); + } else { + if (widget instanceof ccui.ListView) { + if (child instanceof ccui.Widget) + widget.pushBackCustomItem(child); + } else { + if (!(widget instanceof ccui.Layout) && child instanceof ccui.Widget) { + if (child.getPositionType() === ccui.Widget.POSITION_PERCENT) { + var position = child.getPositionPercent(); + var anchor = widget.getAnchorPoint(); + child.setPositionPercent(cc.p(position.x + anchor.x, position.y + anchor.y)); + } + //To make up for the studio positioning error problem + var AnchorPointIn = widget.getAnchorPointInPoints(); + child.setPosition(cc.p(child.getPositionX() + AnchorPointIn.x, child.getPositionY() + AnchorPointIn.y)); + } + widget.addChild(child); + } + } + } + } + }; + + parser.initNode = function (options) { + var node = new cc.Node(); + this.generalAttributes(node, options); + return node; + }; + parser.initSubGraph = function (options) { + var filePath = options["fileName"]; + + var node; + if (filePath && "" !== filePath) { + node = this.createNode(filePath); + } else { + node = new ccs.Node(); + } + this.generalAttributes(node, options); + return node; + }; + parser.initSprite = function (options, resourcePath) { + var path = options["fileName"]; + var sprite; + if (path != null) { + var spriteFrame = cc.spriteFrameCache.getSpriteFrame(path); + if (!spriteFrame) { + path = resourcePath + path; + sprite = new ccs.Sprite(path); + } else { + sprite = ccs.Sprite.createWithSpriteFrame(spriteFrame); + } + + if (!sprite) { + sprite = new cc.Sprite(); + cc.log("filePath is empty. Create a sprite with no texture"); + } + } else { + sprite = new ccs.Sprite(); + } + this.generalAttributes(sprite, options); + var flipX = options["flipX"]; + var flipY = options["flipY"]; + + if (flipX != false) + sprite.setFlippedX(flipX); + if (flipY != false) + sprite.setFlippedY(flipY); + return sprite; + }; + parser.initParticle = function (options, resourcePath) { + var filePath = options["plistFile"]; + var num = options["tmxFile"]; + var particle = new cc.ParticleSystemQuad(filePath); + particle.setTotalParticles(num); + this.generalAttributes(particle, options); + return particle; + }; + parser.initTMXTiledMap = function (options, resourcePath) { + var tmxFile = options["tmxFile"]; + var tmxString = options["tmxString"]; + //todo check path and resourcePath + var path = options["resourcePath"]; + + var tmx = null; + if (tmxFile && "" !== tmxFile) { + tmx = new cc.TMXTiledMap(tmxFile); + } else if (tmxString && "" !== tmxString && path && "" !== path) { + tmx = new cc.TMXTiledMap(tmxString, path); + } + return tmx; + }; + var uiParser = load.getParser("ccui")["*"]; + parser.initWidget = function (options, resourcePath) { + var type = options["classname"]; + + var parser = uiParser.parsers[type]; + if (!parser) + return cc.log("%s parser is not found", type); + + var node = parser.call(uiParser, options, resourcePath); + if (node) { + var rotationSkewX = options["rotationSkewX"]; + var rotationSkewY = options["rotationSkewY"]; + var skewx = options["skewX"]; + var skewy = options["skewY"]; + if (rotationSkewX != 0) + node.setRotationX(rotationSkewX); + if (rotationSkewY != 0) + node.setRotationY(rotationSkewY); + if (skewx != 0) + node.setSkewX(skewx); + if (skewy != 0) + node.setSkewY(skewy); + + var actionTag = options["actionTag"]; + node.setUserObject(new ccs.ActionTimelineData(actionTag)); + } + return node; + }; + + var register = [ + {name: "Node", handle: parser.initNode}, + {name: "SubGraph", handle: parser.initSubGraph}, + {name: "Sprite", handle: parser.initSprite}, + {name: "Particle", handle: parser.initParticle}, + {name: "TMXTiledMap", handle: parser.initTMXTiledMap}, + + {name: "Widget", handle: parser.initWidget}, + {name: "Panel", handle: parser.initWidget}, + {name: "Button", handle: parser.initWidget}, + {name: "CheckBox", handle: parser.initWidget}, + {name: "ImageView", handle: parser.initWidget}, + {name: "LabelAtlas", handle: parser.initWidget}, + {name: "LabelBMFont", handle: parser.initWidget}, + {name: "Label", handle: parser.initWidget}, + {name: "ListView", handle: parser.initWidget}, + {name: "LoadingBar", handle: parser.initWidget}, + {name: "PageView", handle: parser.initWidget}, + {name: "ScrollView", handle: parser.initWidget}, + {name: "Slider", handle: parser.initWidget}, + {name: "TextField", handle: parser.initWidget} + ]; + + register.forEach(function (item) { + parser.registerParser(item.name, function (options, parse, resourcePath) { + var node = item.handle.call(this, options["options"]); + this.parseComponent(node, options["components"]); + this.parseChild(parse, node, options, resourcePath); + return node; + }); + }); + + load.registerParser("timeline", "0.*", parser); + load.registerParser("timeline", "1.*", parser); + +})(ccs._load, ccs._parser); diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/loader/parsers/timelineParser-2.x.js b/frameworks/cocos2d-html5/extensions/cocostudio/loader/parsers/timelineParser-2.x.js new file mode 100644 index 0000000..c823ce8 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/loader/parsers/timelineParser-2.x.js @@ -0,0 +1,1409 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function (load, baseParser) { + + var DEBUG = false; + + var Parser = baseParser.extend({ + + parse: function (file, json, path) { + var resourcePath; + if (path !== undefined) + resourcePath = path; + else + resourcePath = this._dirname(file); + this.pretreatment(json, resourcePath, file); + var node = this.parseNode(this.getNodeJson(json), resourcePath); + this.deferred(json, resourcePath, node, file); + return node; + }, + + getNodeJson: function (json) { + var content = json["Content"]; + if (content["ObjectData"]) + return content["ObjectData"]; + + return content["Content"]["ObjectData"]; + }, + + getClass: function (json) { + return json["ctype"]; + } + + }); + var parser = new Parser(); + + + var getParam = function (value, dValue) { + if (value === undefined) + return dValue; + else + return value; + }; + + ////////// + // NODE // + ////////// + + parser.generalAttributes = function (node, json) { + if (json["Name"] != null) + node.setName(json["Name"]); + + var position = json["Position"]; + if (position != null && (position["X"] != null || position["Y"] != null)) + node.setPosition(cc.p(position["X"] || 0, position["Y"] || 0)); + + var scale = json["Scale"]; + if (scale != null) { + if (scale["ScaleX"] != null) + node.setScaleX(scale["ScaleX"]); + if (scale["ScaleY"] != null) + node.setScaleY(scale["ScaleY"]); + } + + var rotationSkewX = json["RotationSkewX"]; + if (rotationSkewX != null) + node.setRotationX(rotationSkewX); + + var rotationSkewY = json["RotationSkewY"]; + if (json["RotationSkewY"] != null) + node.setRotationY(rotationSkewY); + + + var anchor = json["AnchorPoint"]; + if (anchor != null) { + if (anchor["ScaleX"] == null) + anchor["ScaleX"] = 0; + if (anchor["ScaleY"] == null) + anchor["ScaleY"] = 0; + if (anchor["ScaleX"] != 0.5 || anchor["ScaleY"] != 0.5) + node.setAnchorPoint(cc.p(anchor["ScaleX"], anchor["ScaleY"])); + } + + if (json["ZOrder"] != null) + node.setLocalZOrder(json["ZOrder"]); + + var visible = getParam(json["VisibleForFrame"], true); + node.setVisible(visible); + + var size = json["Size"]; + if (size) + setContentSize(node, size); + + if (json["Alpha"] != null) + node.setOpacity(json["Alpha"]); + + node.setTag(json["Tag"] || 0); + + var actionTag = json["ActionTag"] || 0; + var extensionData = new ccs.ComExtensionData(); + var customProperty = json["UserData"]; + if (customProperty !== undefined) + extensionData.setCustomProperty(customProperty); + extensionData.setActionTag(actionTag); + if (node.getComponent("ComExtensionData")) + node.removeComponent("ComExtensionData"); + node.addComponent(extensionData); + + node.setCascadeColorEnabled(true); + node.setCascadeOpacityEnabled(true); + + setLayoutComponent(node, json); + }; + + parser.parseChild = function (node, children, resourcePath) { + if (!node || !children) return; + for (var i = 0; i < children.length; i++) { + var child = this.parseNode(children[i], resourcePath); + if (child) { + if (node instanceof ccui.PageView) { + if (child instanceof ccui.Layout) + node.addPage(child); + } else { + if (node instanceof ccui.ListView) { + if (child instanceof ccui.Widget) + node.pushBackCustomItem(child); + } else { + if (!(node instanceof ccui.Layout) && child instanceof ccui.Widget) { + if (child.getPositionType() === ccui.Widget.POSITION_PERCENT) { + var position = child.getPositionPercent(); + var anchor = node.getAnchorPoint(); + child.setPositionPercent(cc.p(position.x + anchor.x, position.y + anchor.y)); + } + } + node.addChild(child); + } + } + } + } + }; + + /** + * SingleNode + * @param json + * @returns {cc.Node} + */ + parser.initSingleNode = function (json) { + var node = new cc.Node(); + + this.generalAttributes(node, json); + var color = json["CColor"]; + if (color != null) + node.setColor(getColor(color)); + + return node; + }; + + /** + * Sprite + * @param json + * @param resourcePath + * @returns {cc.Sprite} + */ + parser.initSprite = function (json, resourcePath) { + var node = new cc.Sprite(); + + loadTexture(json["FileData"], resourcePath, function (path, type) { + if (type === 0) + node.setTexture(path); + else if (type === 1) { + var spriteFrame = cc.spriteFrameCache.getSpriteFrame(path); + if (spriteFrame) + node.setSpriteFrame(spriteFrame); + } + }); + + var blendData = json["BlendFunc"]; + if (json["BlendFunc"]) { + var blendFunc = cc.BlendFunc.ALPHA_PREMULTIPLIED; + if (blendData["Src"] !== undefined) + blendFunc.src = blendData["Src"]; + if (blendData["Dst"] !== undefined) + blendFunc.dst = blendData["Dst"]; + node.setBlendFunc(blendFunc); + } + + if (json["FlipX"]) + node.setFlippedX(true); + if (json["FlipY"]) + node.setFlippedY(true); + + this.generalAttributes(node, json); + var color = json["CColor"]; + if (color != null) + node.setColor(getColor(color)); + + return node; + }; + + /** + * Particle + * @param json + * @param resourcePath + * @returns {*} + */ + parser.initParticle = function (json, resourcePath) { + var node, + self = this; + loadTexture(json["FileData"], resourcePath, function (path, type) { + if (!cc.loader.getRes(path)) + cc.log("%s need to be preloaded", path); + node = new cc.ParticleSystem(path); + self.generalAttributes(node, json); + node.setPositionType(cc.ParticleSystem.TYPE_GROUPED); + !cc.sys.isNative && node.setDrawMode(cc.ParticleSystem.TEXTURE_MODE); + + var blendData = json["BlendFunc"]; + if (json["BlendFunc"]) { + var blendFunc = cc.BlendFunc.ALPHA_PREMULTIPLIED; + if (blendData["Src"] !== undefined) + blendFunc.src = blendData["Src"]; + if (blendData["Dst"] !== undefined) + blendFunc.dst = blendData["Dst"]; + node.setBlendFunc(blendFunc); + } + }); + return node; + }; + + + //////////// + // WIDGET // + //////////// + + parser.widgetAttributes = function (widget, json, enableContent) { + widget.setCascadeColorEnabled(true); + widget.setCascadeOpacityEnabled(true); + + widget.setUnifySizeEnabled(false); + //widget.setLayoutComponentEnabled(true); + widget.ignoreContentAdaptWithSize(false); + !enableContent && setContentSize(widget, json["Size"]); + + var name = json["Name"]; + if (name) + widget.setName(name); + + var actionTag = json["ActionTag"] || 0; + widget.setActionTag(actionTag); + var extensionData = new ccs.ComExtensionData(); + var customProperty = json["UserData"]; + if (customProperty !== undefined) + extensionData.setCustomProperty(customProperty); + extensionData.setActionTag(actionTag); + if (widget.getComponent("ComExtensionData")) + widget.removeComponent("ComExtensionData"); + widget.addComponent(extensionData); + + var rotationSkewX = json["RotationSkewX"]; + if (rotationSkewX) + widget.setRotationX(rotationSkewX); + + var rotationSkewY = json["RotationSkewY"]; + if (rotationSkewY) + widget.setRotationY(rotationSkewY); + + //var rotation = json["Rotation"]; + + var flipX = json["FlipX"]; + if (flipX) + widget.setFlippedX(true); + + var flipY = json["FlipY"]; + if (flipY) + widget.setFlippedY(true); + + var zOrder = json["zOrder"]; + if (zOrder != null) + widget.setLocalZOrder(zOrder); + + //var visible = json["Visible"]; + + var visible = getParam(json["VisibleForFrame"], true); + widget.setVisible(visible); + + var alpha = json["Alpha"]; + if (alpha != null) + widget.setOpacity(alpha); + + widget.setTag(json["Tag"] || 0); + + var touchEnabled = json["TouchEnable"] || false; + widget.setTouchEnabled(touchEnabled); + + // -- var frameEvent = json["FrameEvent"]; + + var callBackType = json["CallBackType"]; + if (callBackType != null) + widget.setCallbackType(callBackType); + + var callBackName = json["CallBackName"]; + if (callBackName) + widget.setCallbackName(callBackName); + + var position = json["Position"]; + if (position != null) + widget.setPosition(position["X"] || 0, position["Y"] || 0); + + var scale = json["Scale"]; + if (scale != null) { + var scaleX = getParam(scale["ScaleX"], 1); + var scaleY = getParam(scale["ScaleY"], 1); + widget.setScaleX(scaleX); + widget.setScaleY(scaleY); + } + + var anchorPoint = json["AnchorPoint"]; + if (anchorPoint != null) + widget.setAnchorPoint(anchorPoint["ScaleX"] || 0, anchorPoint["ScaleY"] || 0); + + var color = json["CColor"]; + if (color != null) + widget.setColor(getColor(color)); + + setLayoutComponent(widget, json); + bindCallback(widget, json); + }; + + var bindCallback = function (widget, json) { + var callBackType = json["CallBackType"]; + var callBackName = json["CallBackName"]; + var callBack = function (e) { + if (typeof widget[callBackName] === "function") + widget[callBackName](e); + }; + if (callBackType === "Click") { + widget.addClickEventListener(callBack); + } else if (callBackType === "Touch") { + widget.addTouchEventListener(callBack); + } else if (callBackType === "Event") { + widget.addCCSEventListener(callBack); + } + }; + + var setLayoutComponent = function (widget, json) { + + var layoutComponent = ccui.LayoutComponent.bindLayoutComponent(widget); + if (!layoutComponent) + return; + + var positionXPercentEnabled = json["PositionPercentXEnable"] || json["PositionPercentXEnabled"] || false; + var positionYPercentEnabled = json["PositionPercentYEnable"] || json["PositionPercentYEnabled"] || false; + var positionXPercent = 0, + positionYPercent = 0, + PrePosition = json["PrePosition"]; + if (PrePosition != null) { + positionXPercent = PrePosition["X"] || 0; + positionYPercent = PrePosition["Y"] || 0; + } + var sizeXPercentEnable = json["PercentWidthEnable"] || json["PercentWidthEnabled"] || false; + var sizeYPercentEnable = json["PercentHeightEnable"] || json["PercentHeightEnabled"] || false; + var sizeXPercent = 0, + sizeYPercent = 0, + PreSize = json["PreSize"]; + if (PrePosition != null) { + sizeXPercent = PreSize["X"] || 0; + sizeYPercent = PreSize["Y"] || 0; + } + var stretchHorizontalEnabled = json["StretchWidthEnable"] || false; + var stretchVerticalEnabled = json["StretchHeightEnable"] || false; + var horizontalEdge = json["HorizontalEdge"];// = ccui.LayoutComponent.horizontalEdge.LEFT; + var verticalEdge = json["VerticalEdge"]; // = ccui.LayoutComponent.verticalEdge.TOP; + var leftMargin = json["LeftMargin"] || 0; + var rightMargin = json["RightMargin"] || 0; + var topMargin = json["TopMargin"] || 0; + var bottomMargin = json["BottomMargin"] || 0; + + layoutComponent.setPositionPercentXEnabled(positionXPercentEnabled); + layoutComponent.setPositionPercentYEnabled(positionYPercentEnabled); + layoutComponent.setPositionPercentX(positionXPercent); + layoutComponent.setPositionPercentY(positionYPercent); + layoutComponent.setPercentWidthEnabled(sizeXPercentEnable); + layoutComponent.setPercentHeightEnabled(sizeYPercentEnable); + layoutComponent.setPercentWidth(sizeXPercent); + layoutComponent.setPercentHeight(sizeYPercent); + layoutComponent.setPercentWidthEnabled(sizeXPercentEnable || sizeYPercentEnable); + layoutComponent.setStretchWidthEnabled(stretchHorizontalEnabled); + layoutComponent.setStretchHeightEnabled(stretchVerticalEnabled); + + var horizontalEdgeType = ccui.LayoutComponent.horizontalEdge.NONE; + if (horizontalEdge === "LeftEdge") { + horizontalEdgeType = ccui.LayoutComponent.horizontalEdge.LEFT; + } else if (horizontalEdge === "RightEdge") { + horizontalEdgeType = ccui.LayoutComponent.horizontalEdge.RIGHT; + } else if (horizontalEdge === "BothEdge") { + horizontalEdgeType = ccui.LayoutComponent.horizontalEdge.CENTER; + } + layoutComponent.setHorizontalEdge(horizontalEdgeType); + + var verticalEdgeType = ccui.LayoutComponent.verticalEdge.NONE; + if (verticalEdge === "TopEdge") { + verticalEdgeType = ccui.LayoutComponent.verticalEdge.TOP; + } else if (verticalEdge === "BottomEdge") { + verticalEdgeType = ccui.LayoutComponent.verticalEdge.BOTTOM; + } else if (verticalEdge === "BothEdge") { + verticalEdgeType = ccui.LayoutComponent.verticalEdge.CENTER; + } + layoutComponent.setVerticalEdge(verticalEdgeType); + + layoutComponent.setTopMargin(topMargin); + layoutComponent.setBottomMargin(bottomMargin); + layoutComponent.setLeftMargin(leftMargin); + layoutComponent.setRightMargin(rightMargin); + + layoutComponent.setVerticalEdge(verticalEdgeType); + + layoutComponent.setTopMargin(topMargin); + layoutComponent.setBottomMargin(bottomMargin); + layoutComponent.setLeftMargin(leftMargin); + layoutComponent.setRightMargin(rightMargin); + }; + + var setLayoutBackground = function (layout, single, first, end) { + if (layout.getBackGroundColorType() === 2) { + first = first || {}; + end = end || {}; + layout.setBackGroundColor(getColor(first), getColor(end)); + } else { + single = single || {}; + layout.setBackGroundColor(getColor(single)); + } + }; + + var setLayoutBackgroundVector = function (widget, vector) { + var x = vector["ScaleX"] || 0; + var y = vector["ScaleY"] || 0; + widget.setBackGroundColorVector(cc.p(x, y)); + }; + + /** + * Layout + * @param json + * @param resourcePath + * @returns {ccui.Layout} + */ + parser.initPanel = function (json, resourcePath) { + var widget = new ccui.Layout(); + + this.widgetAttributes(widget, json); + + var clipEnabled = json["ClipAble"] || false; + if (clipEnabled != null) + widget.setClippingEnabled(clipEnabled); + + var colorType = getParam(json["ComboBoxIndex"], 0); + widget.setBackGroundColorType(colorType); + + var bgColorOpacity = getParam(json["BackColorAlpha"], 255); + if (bgColorOpacity != null) + widget.setBackGroundColorOpacity(bgColorOpacity); + + var backGroundScale9Enabled = json["Scale9Enable"]; + if (backGroundScale9Enabled != null) + widget.setBackGroundImageScale9Enabled(backGroundScale9Enabled); + + var opacity = getParam(json["Alpha"], 255); + widget.setOpacity(opacity); + + loadTexture(json["FileData"], resourcePath, function (path, type) { + widget.setBackGroundImage(path, type); + }); + + if (backGroundScale9Enabled) { + var scale9OriginX = json["Scale9OriginX"] || 0; + var scale9OriginY = json["Scale9OriginY"] || 0; + + var scale9Width = json["Scale9Width"] || 0; + var scale9Height = json["Scale9Height"] || 0; + + widget.setBackGroundImageCapInsets(cc.rect( + scale9OriginX, scale9OriginY, scale9Width, scale9Height + )); + + setContentSize(widget, json["Size"]); + } else { + if (!widget.isIgnoreContentAdaptWithSize()) { + setContentSize(widget, json["Size"]); + } + + } + + setLayoutBackground(widget, json["SingleColor"], json["FirstColor"], json["EndColor"]); + setLayoutBackgroundVector(widget, json["ColorVector"]); + + return widget; + }; + + /** + * Text + * @param json + * @param resourcePath + */ + parser.initText = function (json, resourcePath) { + + var widget = new ccui.Text(); + + var touchScaleEnabled = json["TouchScaleChangeAble"]; + if (touchScaleEnabled != null) + widget.setTouchScaleChangeEnabled(touchScaleEnabled); + + var text = json["LabelText"]; + if (text != null) + widget.setString(text); + + var fontSize = json["FontSize"]; + if (fontSize != null) + widget.setFontSize(fontSize); + + var fontName = json["FontName"]; + if (fontName != null) + widget.setFontName(fontName); + + var areaWidth = json["AreaWidth"]; + var areaHeight = json["areaHeight"]; + if (areaWidth && areaHeight) + widget.setTextAreaSize(cc.size(areaWidth, areaHeight)); + + var h_alignment = json["HorizontalAlignmentType"] || "HT_Left"; + switch (h_alignment) { + case "HT_Right": + h_alignment = 2; + break; + case "HT_Center": + h_alignment = 1; + break; + case "HT_Left": + default: + h_alignment = 0; + } + widget.setTextHorizontalAlignment(h_alignment); + + var v_alignment = json["VerticalAlignmentType"] || "VT_Top"; + switch (v_alignment) { + case "VT_Bottom": + v_alignment = 2; + break; + case "VT_Center": + v_alignment = 1; + break; + case "VT_Top": + default: + v_alignment = 0; + } + widget.setTextVerticalAlignment(v_alignment); + + var fontResource = json["FontResource"]; + if (fontResource != null) { + var path = fontResource["Path"]; + //resoutceType = fontResource["Type"]; + if (path != null) { + if (cc.sys.isNative) { + fontName = cc.path.join(cc.loader.resPath, resourcePath, path); + } else { + fontName = path.match(/([^\/]+)\.(\S+)/); + fontName = fontName ? fontName[1] : ""; + } + widget.setFontName(fontName); + } + } + + if (json["OutlineEnabled"] && json["OutlineColor"] && widget.enableOutline) + widget.enableOutline(getColor(json["OutlineColor"]), getParam(json["OutlineSize"], 1)); + + if (json["ShadowEnabled"] && json["ShadowColor"] && widget.enableShadow) + widget.enableShadow( + getColor(json["ShadowColor"]), + cc.size(getParam(json["ShadowOffsetX"], 2), getParam(json["ShadowOffsetY"], -2)), + json["ShadowBlurRadius"] || 0 + ); + + var isCustomSize = json["IsCustomSize"]; + if (isCustomSize != null) + widget.ignoreContentAdaptWithSize(!isCustomSize); + + widget.setUnifySizeEnabled(false); + + var color = json["CColor"]; + json["CColor"] = null; + widget.setTextColor(getColor(color)); + this.widgetAttributes(widget, json, widget.isIgnoreContentAdaptWithSize()); + json["CColor"] = color; + return widget; + + }; + + /** + * Button + * @param json + * @param resourcePath + */ + parser.initButton = function (json, resourcePath) { + + var widget = new ccui.Button(); + + loadTexture(json["NormalFileData"], resourcePath, function (path, type) { + widget.loadTextureNormal(path, type); + }); + loadTexture(json["PressedFileData"], resourcePath, function (path, type) { + widget.loadTexturePressed(path, type); + }); + loadTexture(json["DisabledFileData"], resourcePath, function (path, type) { + widget.loadTextureDisabled(path, type); + }); + + var scale9Enabled = getParam(json["Scale9Enable"], false); + if (scale9Enabled) { + widget.setScale9Enabled(scale9Enabled); + } + + var text = json["ButtonText"]; + if (text != null) + widget.setTitleText(text); + + var fontSize = json["FontSize"]; + if (fontSize != null) + widget.setTitleFontSize(fontSize); + + var fontName = json["FontName"]; + if (fontName != null) + widget.setTitleFontName(fontName); + + var textColor = json["TextColor"]; + if (textColor != null) + widget.setTitleColor(getColor(textColor)); + + var displaystate = getParam(json["DisplayState"], true); + widget.setBright(displaystate); + widget.setEnabled(displaystate); + + var fontResource = json["FontResource"]; + if (fontResource != null) { + var path = fontResource["Path"]; + //resoutceType = fontResource["Type"]; + if (path != null) { + if (cc.sys.isNative) { + fontName = cc.path.join(cc.loader.resPath, resourcePath, path); + } else { + fontName = path.match(/([^\/]+)\.(\S+)/); + fontName = fontName ? fontName[1] : ""; + } + widget.setTitleFontName(fontName); + } + } + + var label = widget.getTitleRenderer(); + if (label && json["ShadowEnabled"] && json["ShadowColor"] && label.enableShadow) { + label.enableShadow( + getColor(json["ShadowColor"]), + cc.size(getParam(json["ShadowOffsetX"], 2), getParam(json["ShadowOffsetY"], -2)), + json["ShadowBlurRadius"] || 0 + ); + } + if (label && json["OutlineEnabled"] && json["OutlineColor"] && label.enableStroke) + label.enableStroke(getColor(json["OutlineColor"]), getParam(json["OutlineSize"], 1)); + + this.widgetAttributes(widget, json); + + if (scale9Enabled) { + widget.setUnifySizeEnabled(false); + widget.ignoreContentAdaptWithSize(false); + var capInsets = cc.rect( + json["Scale9OriginX"] || 0, + json["Scale9OriginY"] || 0, + json["Scale9Width"] || 0, + json["Scale9Height"] || 0 + ); + widget.setCapInsets(capInsets); + + } + + setContentSize(widget, json["Size"]); + + return widget; + + }; + + /** + * CheckBox + * @param json + * @param resourcePath + */ + parser.initCheckBox = function (json, resourcePath) { + + var widget = new ccui.CheckBox(); + + this.widgetAttributes(widget, json); + + var dataList = [ + {name: "NormalBackFileData", handle: widget.loadTextureBackGround}, + {name: "PressedBackFileData", handle: widget.loadTextureBackGroundSelected}, + {name: "NodeNormalFileData", handle: widget.loadTextureFrontCross}, + {name: "DisableBackFileData", handle: widget.loadTextureBackGroundDisabled}, + {name: "NodeDisableFileData", handle: widget.loadTextureFrontCrossDisabled} + ]; + + dataList.forEach(function (item) { + loadTexture(json[item.name], resourcePath, function (path, type) { + item.handle.call(widget, path, type); + }); + }); + + var selectedState = getParam(json["CheckedState"], false); + widget.setSelected(selectedState); + + var displaystate = getParam(json["DisplayState"], true); + widget.setBright(displaystate); + widget.setEnabled(displaystate); + + return widget; + }; + + /** + * ScrollView + * @param json + * @param resourcePath + */ + parser.initScrollView = function (json, resourcePath) { + var widget = new ccui.ScrollView(); + + this.widgetAttributes(widget, json); + + loadTexture(json["FileData"], resourcePath, function (path, type) { + widget.setBackGroundImage(path, type); + }); + + var clipEnabled = json["ClipAble"] || false; + widget.setClippingEnabled(clipEnabled); + + var colorType = getParam(json["ComboBoxIndex"], 0); + widget.setBackGroundColorType(colorType); + + var bgColorOpacity = json["BackColorAlpha"]; + if (bgColorOpacity != null) + widget.setBackGroundColorOpacity(bgColorOpacity); + + var backGroundScale9Enabled = json["Scale9Enable"]; + if (backGroundScale9Enabled) { + widget.setBackGroundImageScale9Enabled(true); + + + var scale9OriginX = json["Scale9OriginX"] || 0; + var scale9OriginY = json["Scale9OriginY"] || 0; + var scale9Width = json["Scale9Width"] || 0; + var scale9Height = json["Scale9Height"] || 0; + widget.setBackGroundImageCapInsets(cc.rect( + scale9OriginX, scale9OriginY, scale9Width, scale9Height + )); + setContentSize(widget, json["Size"]); + } else if (!widget.isIgnoreContentAdaptWithSize()) { + setContentSize(widget, json["Size"]); + } + + setLayoutBackground(widget, json["SingleColor"], json["FirstColor"], json["EndColor"]); + setLayoutBackgroundVector(widget, json["ColorVector"]); + + var innerNodeSize = json["InnerNodeSize"]; + var innerSize = cc.size( + innerNodeSize["Width"] || 0, + innerNodeSize["Height"] || 0 + ); + widget.setInnerContainerSize(innerSize); + + var direction = 0; + if (json["ScrollDirectionType"] === "Vertical") direction = 1; + if (json["ScrollDirectionType"] === "Horizontal") direction = 2; + if (json["ScrollDirectionType"] === "Vertical_Horizontal") direction = 3; + widget.setDirection(direction); + + var bounceEnabled = getParam(json["IsBounceEnabled"], false); + widget.setBounceEnabled(bounceEnabled); + + return widget; + }; + + /** + * ImageView + * @param json + * @param resourcePath + */ + parser.initImageView = function (json, resourcePath) { + + var widget = new ccui.ImageView(); + + loadTexture(json["FileData"], resourcePath, function (path, type) { + widget.loadTexture(path, type); + }); + loadTexture(json["ImageFileData"], resourcePath, function (path, type) { + widget.loadTexture(path, type); + }); + + var scale9Enabled = json["Scale9Enable"]; + if (scale9Enabled) { + widget.setScale9Enabled(true); + widget.setUnifySizeEnabled(false); + widget.ignoreContentAdaptWithSize(false); + + var scale9OriginX = json["Scale9OriginX"] || 0; + var scale9OriginY = json["Scale9OriginY"] || 0; + var scale9Width = json["Scale9Width"] || 0; + var scale9Height = json["Scale9Height"] || 0; + widget.setCapInsets(cc.rect( + scale9OriginX, + scale9OriginY, + scale9Width, + scale9Height + )); + } else + setContentSize(widget, json["Size"]); + + this.widgetAttributes(widget, json); + + return widget; + }; + + /** + * LoadingBar + * @param json + * @param resourcePath + * @returns {ccui.LoadingBar} + */ + parser.initLoadingBar = function (json, resourcePath) { + + var widget = new ccui.LoadingBar(); + + this.widgetAttributes(widget, json); + + loadTexture(json["ImageFileData"], resourcePath, function (path, type) { + widget.loadTexture(path, type); + }); + + var direction = json["ProgressType"] === "Right_To_Left" ? 1 : 0; + widget.setDirection(direction); + + var percent = getParam(json["ProgressInfo"], 80); + if (percent != null) + widget.setPercent(percent); + + return widget; + + }; + + /** + * Slider + * @param json + * @param resourcePath + */ + parser.initSlider = function (json, resourcePath) { + + var widget = new ccui.Slider(); + var loader = cc.loader; + + this.widgetAttributes(widget, json); + + var textureList = [ + {name: "BackGroundData", handle: widget.loadBarTexture}, + {name: "BallNormalData", handle: widget.loadSlidBallTextureNormal}, + {name: "BallPressedData", handle: widget.loadSlidBallTexturePressed}, + {name: "BallDisabledData", handle: widget.loadSlidBallTextureDisabled}, + {name: "ProgressBarData", handle: widget.loadProgressBarTexture} + ]; + textureList.forEach(function (item) { + loadTexture(json[item.name], resourcePath, function (path, type) { + if (type === 0 && !loader.getRes(path)) + cc.log("%s need to be preloaded", path); + item.handle.call(widget, path, type); + }); + }); + + var percent = json["PercentInfo"] || 0; + widget.setPercent(percent); + + var displaystate = getParam(json["DisplayState"], true); + widget.setBright(displaystate); + widget.setEnabled(displaystate); + + return widget; + }; + + /** + * PageView + * @param json + * @param resourcePath + */ + parser.initPageView = function (json, resourcePath) { + + var widget = new ccui.PageView(); + + this.widgetAttributes(widget, json); + + loadTexture(json["FileData"], resourcePath, function (path, type) { + widget.setBackGroundImage(path, type); + }); + + var clipEnabled = json["ClipAble"] || false; + widget.setClippingEnabled(clipEnabled); + + var backGroundScale9Enabled = json["Scale9Enable"]; + if (backGroundScale9Enabled) { + widget.setBackGroundImageScale9Enabled(true); + + var scale9OriginX = json["Scale9OriginX"] || 0; + var scale9OriginY = json["Scale9OriginY"] || 0; + var scale9Width = json["Scale9Width"] || 0; + var scale9Height = json["Scale9Height"] || 0; + widget.setBackGroundImageCapInsets(cc.rect( + scale9OriginX, + scale9OriginY, + scale9Width, + scale9Height + )); + } + + var colorType = getParam(json["ComboBoxIndex"], 0); + widget.setBackGroundColorType(colorType); + + setLayoutBackground(widget, json["SingleColor"], json["FirstColor"], json["EndColor"]); + setLayoutBackgroundVector(widget, json["ColorVector"]); + + var bgColorOpacity = json["BackColorAlpha"]; + if (bgColorOpacity != null) + widget.setBackGroundColorOpacity(bgColorOpacity); + + setContentSize(widget, json["Size"]); + + return widget; + + }; + + /** + * ListView + * @param json + * @param resourcePath + * @returns {ccui.ListView} + */ + parser.initListView = function (json, resourcePath) { + + var widget = new ccui.ListView(); + + this.widgetAttributes(widget, json); + + loadTexture(json["FileData"], resourcePath, function (path, type) { + widget.setBackGroundImage(path, type); + }); + + var clipEnabled = json["ClipAble"] || false; + widget.setClippingEnabled(clipEnabled); + + var colorType = getParam(json["ComboBoxIndex"], 0); + widget.setBackGroundColorType(colorType); + + var bgColorOpacity = getParam(json["BackColorAlpha"], 255); + var backGroundScale9Enabled = json["Scale9Enable"]; + if (backGroundScale9Enabled) { + widget.setBackGroundImageScale9Enabled(true); + + var scale9OriginX = json["Scale9OriginX"] || 0; + var scale9OriginY = json["Scale9OriginY"] || 0; + var scale9Width = json["Scale9Width"] || 0; + var scale9Height = json["Scale9Height"] || 0; + widget.setBackGroundImageCapInsets(cc.rect( + scale9OriginX, + scale9OriginY, + scale9Width, + scale9Height + )); + } + + var directionType = getParam(json["DirectionType"], ccui.ListView.DIR_HORIZONTAL); + var verticalType = getParam(json["VerticalType"], "Align_Left"); + var horizontalType = getParam(json["HorizontalType"], "Align_Top"); + if (!directionType) { + widget.setDirection(ccui.ScrollView.DIR_HORIZONTAL); + if (verticalType === "Align_Bottom") + widget.setGravity(ccui.ListView.GRAVITY_BOTTOM); + else if (verticalType === "Align_VerticalCenter") + widget.setGravity(ccui.ListView.GRAVITY_CENTER_VERTICAL); + else + widget.setGravity(ccui.ListView.GRAVITY_TOP); + } else if (directionType === "Vertical") { + widget.setDirection(ccui.ScrollView.DIR_VERTICAL); + if (horizontalType === "") + widget.setGravity(ccui.ListView.GRAVITY_LEFT); + else if (horizontalType === "Align_Right") + widget.setGravity(ccui.ListView.GRAVITY_RIGHT); + else if (horizontalType === "Align_HorizontalCenter") + widget.setGravity(ccui.ListView.GRAVITY_CENTER_HORIZONTAL); + } + + + var bounceEnabled = getParam(json["IsBounceEnabled"], false); + widget.setBounceEnabled(bounceEnabled); + + var itemMargin = json["ItemMargin"] || 0; + widget.setItemsMargin(itemMargin); + + var innerSize = json["InnerNodeSize"]; + //Width + if (innerSize != null) + widget.setInnerContainerSize(cc.size(innerSize["Widget"] || 0, innerSize["Height"] || 0)); + + setLayoutBackground(widget, json["SingleColor"], json["FirstColor"], json["EndColor"]); + setLayoutBackgroundVector(widget, json["ColorVector"]); + + if (bgColorOpacity != null) + widget.setBackGroundColorOpacity(bgColorOpacity); + + setContentSize(widget, json["Size"]); + + return widget; + }; + + /** + * TextAtlas + * @param json + * @param resourcePath + * @returns {ccui.TextAtlas} + */ + parser.initTextAtlas = function (json, resourcePath) { + + var widget = new ccui.TextAtlas(); + + var stringValue = json["LabelText"]; + var itemWidth = json["CharWidth"]; + var itemHeight = json["CharHeight"]; + + var startCharMap = json["StartChar"]; + + loadTexture(json["LabelAtlasFileImage_CNB"], resourcePath, function (path, type) { + if (!cc.loader.getRes(path)) + cc.log("%s need to be preloaded", path); + if (type === 0) { + widget.setProperty(stringValue, path, itemWidth, itemHeight, startCharMap); + } + }); + this.widgetAttributes(widget, json); + + // the TextAtlas must be ignore ContentSize[Size] in the ccs file. + widget.ignoreContentAdaptWithSize(true); + + return widget; + }; + + /** + * TextBMFont + * @param json + * @param resourcePath + * @returns {ccui.TextBMFont} + */ + parser.initTextBMFont = function (json, resourcePath) { + var widget = new ccui.TextBMFont(); + this.widgetAttributes(widget, json); + + loadTexture(json["LabelBMFontFile_CNB"], resourcePath, function (path, type) { + if (!cc.loader.getRes(path)) + cc.log("%s need to be pre loaded", path); + widget.setFntFile(path); + }); + + var text = json["LabelText"]; + widget.setString(text); + + widget.ignoreContentAdaptWithSize(true); + return widget; + }; + + /** + * TextField + * @param json + * @param resourcePath + * @returns {ccui.TextField} + */ + parser.initTextField = function (json, resourcePath) { + var widget = new ccui.TextField(); + + var passwordEnabled = json["PasswordEnable"]; + if (passwordEnabled) { + widget.setPasswordEnabled(true); + var passwordStyleText = json["PasswordStyleText"] || "*"; + widget.setPasswordStyleText(passwordStyleText); + } + + var placeHolder = json["PlaceHolderText"]; + if (placeHolder != null) + widget.setPlaceHolder(placeHolder); + + var fontSize = json["FontSize"]; + if (fontSize != null) + widget.setFontSize(fontSize); + + var fontName = json["FontName"]; + if (fontName != null) + widget.setFontName(fontName); + + var maxLengthEnabled = json["MaxLengthEnable"]; + if (maxLengthEnabled) { + widget.setMaxLengthEnabled(true); + var maxLength = json["MaxLengthText"] || 0; + widget.setMaxLength(maxLength); + } + + //var isCustomSize = json["IsCustomSize"]; + this.widgetAttributes(widget, json); + + var text = json["LabelText"]; + if (text != null) + widget.setString(text); + + var fontResource = json["FontResource"]; + if (fontResource != null) { + var path = fontResource["Path"]; + //resoutceType = fontResource["Type"]; + if (path != null) { + if (cc.sys.isNative) { + fontName = cc.path.join(cc.loader.resPath, resourcePath, path); + } else { + fontName = path.match(/([^\/]+)\.(\S+)/); + fontName = fontName ? fontName[1] : ""; + } + widget.setFontName(fontName); + } + } + + widget.setUnifySizeEnabled(false); + widget.ignoreContentAdaptWithSize(false); + + var color = json["CColor"]; + if (color != null) + widget.setTextColor(getColor(color)); + + if (!widget.isIgnoreContentAdaptWithSize()) { + setContentSize(widget, json["Size"]); + if (cc.sys.isNative) + widget.getVirtualRenderer().setLineBreakWithoutSpace(true); + } + + + return widget; + + }; + + /** + * SimpleAudio + * @param json + * @param resourcePath + */ + parser.initSimpleAudio = function (json, resourcePath) { + + var node = new ccs.ComAudio(); + var loop = json["Loop"] || false; + //var volume = json["Volume"] || 0; + //cc.audioEngine.setMusicVolume(volume); + node.setLoop(loop); + loadTexture(json["FileData"], resourcePath, function (path, type) { + node.setFile(path); + }); + + }; + + /** + * GameMap + * @param json + * @param resourcePath + * @returns {*} + */ + parser.initGameMap = function (json, resourcePath) { + + var node = null; + + loadTexture(json["FileData"], resourcePath, function (path, type) { + if (type === 0) + node = new cc.TMXTiledMap(path); + + parser.generalAttributes(node, json); + }); + + return node; + }; + + /** + * ProjectNode + * @param json + * @param resourcePath + * @returns {*} + */ + parser.initProjectNode = function (json, resourcePath) { + var projectFile = json["FileData"]; + if (projectFile != null && projectFile["Path"]) { + var file = resourcePath + projectFile["Path"]; + if (cc.loader.getRes(file)) { + var obj = ccs.load(file, resourcePath); + parser.generalAttributes(obj.node, json); + if (obj.action && obj.node) { + obj.action.tag = obj.node.tag; + var InnerActionSpeed = json["InnerActionSpeed"]; + if (InnerActionSpeed !== undefined) + obj.action.setTimeSpeed(InnerActionSpeed); + obj.node.runAction(obj.action); + obj.action.gotoFrameAndPause(0); + } + return obj.node; + } else + cc.log("%s need to be preloaded", file); + } + }; + + var getFileName = function (name) { + if (!name) return ""; + var arr = name.match(/([^\/]+)\.[^\/]+$/); + if (arr && arr[1]) + return arr[1]; + else + return ""; + }; + + /** + * Armature + * @param json + * @param resourcePath + */ + parser.initArmature = function (json, resourcePath) { + + var node = new ccs.Armature(); + + var isLoop = json["IsLoop"]; + + var isAutoPlay = json["IsAutoPlay"]; + + var currentAnimationName = json["CurrentAnimationName"]; + + loadTexture(json["FileData"], resourcePath, function (path, type) { + var plists, pngs; + var armJson = cc.loader.getRes(path); + if (!armJson) + cc.log("%s need to be preloaded", path); + else { + plists = armJson["config_file_path"]; + pngs = armJson["config_png_path"]; + plists.forEach(function (plist, index) { + if (pngs[index]) + cc.spriteFrameCache.addSpriteFrames(plist, pngs[index]); + }); + } + ccs.armatureDataManager.addArmatureFileInfo(path); + node.init(getFileName(path)); + if (isAutoPlay) + node.getAnimation().play(currentAnimationName, -1, isLoop); + else { + node.getAnimation().play(currentAnimationName); + node.getAnimation().gotoAndPause(0); + } + + }); + + delete json["AnchorPoint"]; + delete json["Size"]; + parser.generalAttributes(node, json); + + node.setColor(getColor(json["CColor"])); + return node; + }; + + parser.initBoneNode = function (json, resourcePath) { + + var node = new ccs.BoneNode(); + + var length = json["Length"]; + if (length !== undefined) + node.setDebugDrawLength(length); + + var blendFunc = json["BlendFunc"]; + if (blendFunc && blendFunc["Src"] !== undefined && blendFunc["Dst"] !== undefined) + node.setBlendFunc(new cc.BlendFunc(blendFunc["Src"], blendFunc["Dst"])); + + parser.generalAttributes(node, json); + var color = json["CColor"]; + if (color && (color["R"] !== undefined || color["G"] !== undefined || color["B"] !== undefined)) + node.setColor(getColor(color)); + return node; + }; + + parser.initSkeletonNode = function (json) { + var node = new ccs.SkeletonNode(); + parser.generalAttributes(node, json); + var color = json["CColor"]; + if (color && (color["R"] !== undefined || color["G"] !== undefined || color["B"] !== undefined)) + node.setColor(getColor(color)); + return node; + }; + + var loadedPlist = {}; + var loadTexture = function (json, resourcePath, cb) { + if (json != null) { + var path = json["Path"]; + var type; + if (json["Type"] === "Default" || json["Type"] === "Normal") + type = 0; + else + type = 1; + var plist = json["Plist"]; + if (plist) { + if (cc.loader.getRes(resourcePath + plist)) { + loadedPlist[resourcePath + plist] = true; + cc.spriteFrameCache.addSpriteFrames(resourcePath + plist); + } else { + if (!loadedPlist[resourcePath + plist] && !cc.spriteFrameCache.getSpriteFrame(path)) + cc.log("%s need to be preloaded", resourcePath + plist); + } + } + if (type !== 0) { + if (cc.spriteFrameCache.getSpriteFrame(path)) + cb(path, type); + else + cc.log("failed to get spriteFrame: %s", path); + } else + cb(resourcePath + path, type); + } + }; + + var getColor = function (json) { + if (!json) return; + var r = json["R"] != null ? json["R"] : 255; + var g = json["G"] != null ? json["G"] : 255; + var b = json["B"] != null ? json["B"] : 255; + var a = json["A"] != null ? json["A"] : 255; + return cc.color(r, g, b, a); + }; + + var setContentSize = function (node, size) { + var x = size["X"] || 0; + var y = size["Y"] || 0; + if (size) + node.setContentSize(cc.size(x, y)); + }; + + var register = [ + {name: "SingleNodeObjectData", handle: parser.initSingleNode}, + {name: "NodeObjectData", handle: parser.initSingleNode}, + {name: "LayerObjectData", handle: parser.initSingleNode}, + {name: "GameNodeObjectData", handle: parser.initSingleNode}, + {name: "GameLayerObjectData", handle: parser.initSingleNode}, + {name: "SpriteObjectData", handle: parser.initSprite}, + {name: "ParticleObjectData", handle: parser.initParticle}, + {name: "PanelObjectData", handle: parser.initPanel}, + {name: "TextObjectData", handle: parser.initText}, + {name: "ButtonObjectData", handle: parser.initButton}, + {name: "CheckBoxObjectData", handle: parser.initCheckBox}, + {name: "ScrollViewObjectData", handle: parser.initScrollView}, + {name: "ImageViewObjectData", handle: parser.initImageView}, + {name: "LoadingBarObjectData", handle: parser.initLoadingBar}, + {name: "SliderObjectData", handle: parser.initSlider}, + {name: "PageViewObjectData", handle: parser.initPageView}, + {name: "ListViewObjectData", handle: parser.initListView}, + {name: "TextAtlasObjectData", handle: parser.initTextAtlas}, + {name: "TextBMFontObjectData", handle: parser.initTextBMFont}, + {name: "TextFieldObjectData", handle: parser.initTextField}, + {name: "SimpleAudioObjectData", handle: parser.initSimpleAudio}, + {name: "GameMapObjectData", handle: parser.initGameMap}, + {name: "ProjectNodeObjectData", handle: parser.initProjectNode}, + {name: "ArmatureNodeObjectData", handle: parser.initArmature}, + {name: "BoneNodeObjectData", handle: parser.initBoneNode}, + {name: "SkeletonNodeObjectData", handle: parser.initSkeletonNode} + ]; + + register.forEach(function (item) { + parser.registerParser(item.name, function (options, resourcePath) { + var node = item.handle.call(this, options, resourcePath); + this.parseChild(node, options["Children"], resourcePath); + DEBUG && node && (node.__parserName = item.name); + return node; + }); + }); + + + load.registerParser("timeline", "2.*", parser); + load.registerParser("timeline", "*", parser); + + +})(ccs._load, ccs._parser); diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/loader/parsers/uiParser-1.x.js b/frameworks/cocos2d-html5/extensions/cocostudio/loader/parsers/uiParser-1.x.js new file mode 100644 index 0000000..e6904df --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/loader/parsers/uiParser-1.x.js @@ -0,0 +1,710 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function (load, baseParser) { + + var _stack = new Array(50); + + var Parser = baseParser.extend({ + + addSpriteFrame: function (textures, resourcePath) { + if (!textures) return; + for (var i = 0; i < textures.length; i++) { + cc.spriteFrameCache.addSpriteFrames(resourcePath + textures[i]); + } + }, + + pretreatment: function (json, resourcePath) { + this.addSpriteFrame(json["textures"], resourcePath); + }, + + parseRecursive: function (json, resourcePath) { + var index = 1; + var rootNode = null; + var parser, curr, className, options, position, anchor, anchorPP, + node, parent, child, children; + _stack[0] = json; + while (index > 0) { + index--; + curr = _stack[index]; + // Avoid memory leak + _stack[index] = null; + if (!curr) continue; + + // Parse node + className = curr["classname"]; + parser = this.parsers[className]; + if (!parser) { + cc.log("Can't find the parser : %s", className); + continue; + } + node = new parser.object(); + if (!node) continue; + if (!rootNode) { + rootNode = node; + } + + // Parse attributes + options = curr["options"]; + this.generalAttributes(node, options); + parser.handle(node, options, resourcePath); + this.colorAttributes(node, options); + + parent = curr.parent; + curr.parent = null; + if (parent instanceof ccui.PageView) { + parent.addPage(node); + } + else if (parent instanceof ccui.ListView) { + parent.pushBackCustomItem(node); + } + else if (parent) { + if (!(parent instanceof ccui.Layout)) { + if (node.getPositionType() === ccui.Widget.POSITION_PERCENT) { + position = node._positionPercent; + anchor = parent._anchorPoint; + node._positionPercent.x = position.x + anchor.x; + node._positionPercent.y = position.y + anchor.y; + } + anchorPP = parent._renderCmd._anchorPointInPoints; + node._position.x += anchorPP.x; + node._position.y += anchorPP.y; + node.setNodeDirty(); + } + parent.addChild(node); + } + + children = curr["children"]; + if (children && children.length > 0) { + for (var i = children.length - 1; i >= 0; i--) { + _stack[index] = children[i]; + _stack[index].parent = node; + index++; + } + } + } + return rootNode; + }, + + parse: function (file, json, resourcePath) { + resourcePath = resourcePath || this._dirname(file); + this.pretreatment(json, resourcePath); + + var node = this.parseRecursive(json["widgetTree"], resourcePath); + + node && this.deferred(json, resourcePath, node, file); + return node; + }, + + deferred: function (json, resourcePath, node, file) { + if (node) { + ccs.actionManager.initWithDictionary(file, json["animation"], node); + node.setContentSize(json["designWidth"], json["designHeight"]); + } + } + + }); + var parser = new Parser(); + + parser.generalAttributes = function (widget, options) { + widget._ignoreSize = options["ignoreSize"] || true; + widget._sizeType = options["sizeType"] || 0; + widget._positionType = options["positionType"] || 0; + + widget._sizePercent.x = options["sizePercentX"] || 0; + widget._sizePercent.y = options["sizePercentY"] || 0; + widget._positionPercent.x = options["positionPercentX"] || 0; + widget._positionPercent.y = options["positionPercentY"] || 0; + + /* adapt screen */ + var w = 0, h = 0; + var adaptScreen = options["adaptScreen"]; + if (adaptScreen) { + var screenSize = cc.director.getWinSize(); + w = screenSize.width; + h = screenSize.height; + } else { + w = options["width"] || 0; + h = options["height"] || 0; + } + + var anchorPointX = options["anchorPointX"]; + var anchorPointY = options["anchorPointY"]; + + widget._anchorPoint.x = isNaN(anchorPointX) ? 0.5 : anchorPointX; + widget._anchorPoint.y = isNaN(anchorPointY) ? 0.5 : anchorPointY; + + widget.setContentSize(w, h); + + widget.setTag(options["tag"]); + widget.setActionTag(options["actiontag"]); + widget.setTouchEnabled(options["touchAble"]); + + widget._name = options["name"] || "default"; + + widget._position.x = options["x"] || 0; + widget._position.y = options["y"] || 0; + widget._scaleX = options["scaleX"] || 1; + widget._scaleY = options["scaleY"] || 1; + widget._rotationX = widget._rotationY = options["rotation"] || 0; + + widget._visible = options["visible"] || false; + widget._localZOrder = options["ZOrder"] || 0; + + var layout = options["layoutParameter"]; + if (layout != null) { + var layoutParameterDic = options["layoutParameter"]; + var paramType = isNaN(layoutParameterDic["type"]) ? 2 : layoutParameterDic["type"]; + var parameter = null; + + switch (paramType) { + case 0: + break; + case 1: + parameter = new ccui.LinearLayoutParameter(); + parameter._linearGravity = layoutParameterDic["gravity"] || 0; + break; + case 2: + parameter = new ccui.RelativeLayoutParameter(); + parameter._relativeLayoutName = layoutParameterDic["relativeName"]; + parameter._relativeWidgetName = layoutParameterDic["relativeToName"]; + parameter._relativeAlign = layoutParameterDic["align"] || 0; + break; + default: + break; + } + if (parameter != null) { + var margin = parameter._margin; + margin.left = layoutParameterDic["marginLeft"] || 0; + margin.top = layoutParameterDic["marginTop"] || 0; + margin.right = layoutParameterDic["marginRight"] || 0; + margin.bottom = layoutParameterDic["marginDown"] || 0; + widget.setLayoutParameter(parameter); + } + } + }; + + parser.colorAttributes = function (widget, options) { + var op = options["opacity"] !== null ? options["opacity"] : 255; + if (op != null) + widget.setOpacity(op); + var colorR = options["colorR"]; + var colorG = options["colorG"]; + var colorB = options["colorB"]; + widget.setColor(cc.color((colorR == null) ? 255 : colorR, (colorG == null) ? 255 : colorG, (colorB == null) ? 255 : colorB)); + + widget.setFlippedX(options["flipX"]); + widget.setFlippedY(options["flipY"]); + }; + + var getPath = function (res, type, path, cb) { + if (path) { + if (type === 0) + cb(res + path, type); + else + cb(path, type); + } + }; + + /** + * Panel parser (UILayout) + */ + parser.LayoutAttributes = function (widget, options, resourcePath) { + var w = 0, h = 0; + var adaptScreen = options["adaptScreen"]; + if (adaptScreen) { + var screenSize = cc.director.getWinSize(); + w = screenSize.width; + h = screenSize.height; + } else { + w = options["width"] || 0; + h = options["height"] || 0; + } + widget.setSize(cc.size(w, h)); + + widget.setClippingEnabled(options["clipAble"]); + + var backGroundScale9Enable = options["backGroundScale9Enable"]; + widget.setBackGroundImageScale9Enabled(backGroundScale9Enable); + var cr = options["bgColorR"] || 0; + var cg = options["bgColorG"] || 0; + var cb = options["bgColorB"] || 0; + + var scr = isNaN(options["bgStartColorR"]) ? 255 : options["bgStartColorR"]; + var scg = isNaN(options["bgStartColorG"]) ? 255 : options["bgStartColorG"]; + var scb = isNaN(options["bgStartColorB"]) ? 255 : options["bgStartColorB"]; + + var ecr = isNaN(options["bgEndColorR"]) ? 255 : options["bgEndColorR"]; + var ecg = isNaN(options["bgEndColorG"]) ? 255 : options["bgEndColorG"]; + var ecb = isNaN(options["bgEndColorB"]) ? 255 : options["bgEndColorB"]; + + var bgcv1 = options["vectorX"] || 0; + var bgcv2 = options["vectorY"] || 0; + widget.setBackGroundColorVector(cc.p(bgcv1, bgcv2)); + + var co = options["bgColorOpacity"] || 0; + + var colorType = options["colorType"] || 0; + widget.setBackGroundColorType(colorType/*ui.LayoutBackGroundColorType(colorType)*/); + widget.setBackGroundColor(cc.color(scr, scg, scb), cc.color(ecr, ecg, ecb)); + widget.setBackGroundColor(cc.color(cr, cg, cb)); + widget.setBackGroundColorOpacity(co); + + + var imageFileNameDic = options["backGroundImageData"]; + if (imageFileNameDic) { + getPath(resourcePath, imageFileNameDic["resourceType"] || 0, imageFileNameDic["path"], function (path, type) { + widget.setBackGroundImage(path, type); + }); + } + + if (backGroundScale9Enable) { + var cx = options["capInsetsX"] || 0; + var cy = options["capInsetsY"] || 0; + var cw = isNaN(options["capInsetsWidth"]) ? 1 : options["capInsetsWidth"]; + var ch = isNaN(options["capInsetsHeight"]) ? 1 : options["capInsetsHeight"]; + widget.setBackGroundImageCapInsets(cc.rect(cx, cy, cw, ch)); + } + if (options["layoutType"]) { + widget.setLayoutType(options["layoutType"]); + } + }; + /** + * Button parser (UIButton) + */ + parser.ButtonAttributes = function (widget, options, resourcePath) { + var button = widget; + var scale9Enable = options["scale9Enable"]; + button.setScale9Enabled(scale9Enable); + + var normalDic = options["normalData"]; + getPath(resourcePath, normalDic["resourceType"] || 0, normalDic["path"], function (path, type) { + button.loadTextureNormal(path, type); + }); + var pressedDic = options["pressedData"]; + getPath(resourcePath, pressedDic["resourceType"] || 0, pressedDic["path"], function (path, type) { + button.loadTexturePressed(path, type); + }); + var disabledDic = options["disabledData"]; + getPath(resourcePath, disabledDic["resourceType"] || 0, disabledDic["path"], function (path, type) { + button.loadTextureDisabled(path, type); + }); + if (scale9Enable) { + var cx = options["capInsetsX"] || 0; + var cy = options["capInsetsY"] || 0; + var cw = isNaN(options["capInsetsWidth"]) ? 1 : options["capInsetsWidth"]; + var ch = isNaN(options["capInsetsHeight"]) ? 1 : options["capInsetsHeight"]; + + button.setCapInsets(cc.rect(cx, cy, cw, ch)); + var sw = options["scale9Width"] || 0; + var sh = options["scale9Height"] || 0; + if (sw != null && sh != null) + button.setSize(cc.size(sw, sh)); + } + var text = options["text"] || ""; + if (text) { + button.setTitleText(text); + + var cr = options["textColorR"]; + var cg = options["textColorG"]; + var cb = options["textColorB"]; + var cri = (cr !== null) ? options["textColorR"] : 255; + var cgi = (cg !== null) ? options["textColorG"] : 255; + var cbi = (cb !== null) ? options["textColorB"] : 255; + + button.setTitleColor(cc.color(cri, cgi, cbi)); + var fs = options["fontSize"]; + if (fs != null) + button.setTitleFontSize(options["fontSize"]); + var fn = options["fontName"]; + if (fn) + button.setTitleFontName(options["fontName"]); + } + }; + /** + * CheckBox parser (UICheckBox) + */ + parser.CheckBoxAttributes = function (widget, options, resourcePath) { + //load background image + var backGroundDic = options["backGroundBoxData"]; + getPath(resourcePath, backGroundDic["resourceType"] || 0, backGroundDic["path"], function (path, type) { + widget.loadTextureBackGround(path, type); + }); + + //load background selected image + var backGroundSelectedDic = options["backGroundBoxSelectedData"]; + getPath( + resourcePath, + backGroundSelectedDic["resourceType"] || backGroundDic["resourceType"], + backGroundSelectedDic["path"] || backGroundDic["path"], + function (path, type) { + widget.loadTextureBackGroundSelected(path, type); + }); + + //load frontCross image + var frontCrossDic = options["frontCrossData"]; + getPath(resourcePath, frontCrossDic["resourceType"] || 0, frontCrossDic["path"], function (path, type) { + widget.loadTextureFrontCross(path, type); + }); + + //load backGroundBoxDisabledData + var backGroundDisabledDic = options["backGroundBoxDisabledData"]; + getPath( + resourcePath, + backGroundDisabledDic["resourceType"] || frontCrossDic["resourceType"], + backGroundDisabledDic["path"] || frontCrossDic["path"], + function (path, type) { + widget.loadTextureBackGroundDisabled(path, type); + }); + + ///load frontCrossDisabledData + var frontCrossDisabledDic = options["frontCrossDisabledData"]; + getPath(resourcePath, frontCrossDisabledDic["resourceType"] || 0, frontCrossDisabledDic["path"], function (path, type) { + widget.loadTextureFrontCrossDisabled(path, type); + }); + + if (options["selectedState"]) + widget.setSelected(options["selectedState"]); + }; + /** + * ImageView parser (UIImageView) + */ + parser.ImageViewAttributes = function (widget, options, resourcePath) { + var imageFileNameDic = options["fileNameData"] + getPath(resourcePath, imageFileNameDic["resourceType"] || 0, imageFileNameDic["path"], function (path, type) { + widget.loadTexture(path, type); + }); + + var scale9EnableExist = options["scale9Enable"]; + var scale9Enable = false; + if (scale9EnableExist) { + scale9Enable = options["scale9Enable"]; + } + widget.setScale9Enabled(scale9Enable); + + if (scale9Enable) { + var sw = options["scale9Width"] || 0; + var sh = options["scale9Height"] || 0; + if (sw && sh) { + var swf = options["scale9Width"] || 0; + var shf = options["scale9Height"] || 0; + widget.setSize(cc.size(swf, shf)); + } + + var cx = options["capInsetsX"] || 0; + var cy = options["capInsetsY"] || 0; + var cw = isNaN(options["capInsetsWidth"]) ? 1 : options["capInsetsWidth"]; + var ch = isNaN(options["capInsetsHeight"]) ? 1 : options["capInsetsHeight"]; + + widget.setCapInsets(cc.rect(cx, cy, cw, ch)); + + } + }; + /** + * TextAtlas parser (UITextAtlas) + */ + parser.TextAtlasAttributes = function (widget, options, resourcePath) { + var sv = options["stringValue"]; + var cmf = options["charMapFileData"]; // || options["charMapFile"]; + var iw = options["itemWidth"]; + var ih = options["itemHeight"]; + var scm = options["startCharMap"]; + if (sv != null && cmf && iw != null && ih != null && scm != null) { + var cmftDic = options["charMapFileData"]; + var cmfType = cmftDic["resourceType"] || 0; + switch (cmfType) { + case 0: + var tp_c = resourcePath; + var cmfPath = cmftDic["path"]; + var cmf_tp = tp_c + cmfPath; + widget.setProperty(sv, cmf_tp, iw, ih, scm); + break; + case 1: + cc.log("Wrong res type of LabelAtlas!"); + break; + default: + break; + } + } + }; + /** + * TextBMFont parser (UITextBMFont) + */ + parser.TextBMFontAttributes = function (widget, options, resourcePath) { + var cmftDic = options["fileNameData"]; + var cmfType = cmftDic["resourceType"] || 0; + switch (cmfType) { + case 0: + var tp_c = resourcePath; + var cmfPath = cmftDic["path"]; + var cmf_tp = tp_c + cmfPath; + widget.setFntFile(cmf_tp); + break; + case 1: + cc.log("Wrong res type of LabelAtlas!"); + break; + default: + break; + } + + var text = options["text"] || ""; + widget.setString(text); + }; + /** + * Text parser (UIText) + */ + var regTTF = /\.ttf$/; + parser.TextAttributes = function (widget, options, resourcePath) { + var touchScaleChangeAble = options["touchScaleEnable"]; + widget.setTouchScaleChangeEnabled(touchScaleChangeAble); + var text = options["text"] || ""; + if(text) { + widget._setString(text); + } + + var fs = options["fontSize"]; + if (fs != null) { + widget._setFontSize(options["fontSize"]); + } + var fn = options["fontName"]; + if (fn != null) { + if (cc.sys.isNative) { + if (regTTF.test(fn)) { + widget.setFontName(cc.path.join(cc.loader.resPath, resourcePath, fn)); + } else { + widget.setFontName(fn); + } + } else { + widget._setFontName(fn.replace(regTTF, '')); + } + } + var aw = options["areaWidth"] || 0; + var ah = options["areaHeight"] || 0; + if (aw && ah) { + var size = cc.size(options["areaWidth"], options["areaHeight"]); + widget._setTextAreaSize(size); + } + var ha = options["hAlignment"] || 0; + if (ha != null) { + widget._setTextHorizontalAlignment(ha); + } + var va = options["vAlignment"] || 0; + if (va != null) { + widget._setTextVerticalAlignment(va); + } + widget._updateUITextContentSize(); + }; + /** + * ListView parser (UIListView) + */ + parser.ListViewAttributes = function (widget, options, resoutcePath) { + parser.ScrollViewAttributes(widget, options, resoutcePath); + var direction = options["direction"] || 1; + widget.setDirection(direction); + var gravity = options["gravity"] || 0; + widget.setGravity(gravity); + var itemMargin = options["itemMargin"] || 0; + widget.setItemsMargin(itemMargin); + }; + /** + * LoadingBar parser (UILoadingBar) + */ + parser.LoadingBarAttributes = function (widget, options, resourcePath) { + var imageFileNameDic = options["textureData"]; + getPath(resourcePath, imageFileNameDic["resourceType"] || 0, imageFileNameDic["path"], function (path, type) { + widget.loadTexture(path, type); + }); + + var scale9Enable = options["scale9Enable"]; + widget.setScale9Enabled(scale9Enable); + + if (scale9Enable) { + var cx = options["capInsetsX"] || 0; + var cy = options["capInsetsY"] || 0; + var cw = isNaN(options["capInsetsWidth"]) ? 1 : options["capInsetsWidth"]; + var ch = isNaN(options["capInsetsHeight"]) ? 1 : options["capInsetsHeight"]; + + widget.setCapInsets(cc.rect(cx, cy, cw, ch)); + + var width = options["width"] || 0; + var height = options["height"] || 0; + widget.setSize(cc.size(width, height)); + } + + widget.setDirection(options["direction"] || 0); + widget.setPercent(options["percent"] || 0); + }; + /** + * PageView parser (UIPageView) + */ + parser.PageViewAttributes = parser.LayoutAttributes; + /** + * ScrollView parser (UIScrollView) + */ + parser.ScrollViewAttributes = function (widget, options, resoutcePath) { + parser.LayoutAttributes(widget, options, resoutcePath); + var innerWidth = options["innerWidth"] != null ? options["innerWidth"] : 200; + var innerHeight = options["innerHeight"] != null ? options["innerHeight"] : 200; + widget.setInnerContainerSize(cc.size(innerWidth, innerHeight)); + + var direction = options["direction"] != null ? options["direction"] : 1; + widget.setDirection(direction); + widget.setBounceEnabled(options["bounceEnable"]); + }; + /** + * Slider parser (UISlider) + */ + parser.SliderAttributes = function (widget, options, resourcePath) { + + var slider = widget; + + var barTextureScale9Enable = options["scale9Enable"]; + slider.setScale9Enabled(barTextureScale9Enable); + var bt = options["barFileName"]; + var barLength = options["length"]; + + var imageFileNameDic = options["barFileNameData"]; + var imageFileType = imageFileNameDic["resourceType"] || 0; + var imageFileName = imageFileNameDic["path"]; + + if (bt != null) { + if (barTextureScale9Enable) { + getPath(resourcePath, imageFileType, imageFileName, function (path, type) { + slider.loadBarTexture(path, type); + }); + slider.setSize(cc.size(barLength, slider.getContentSize().height)); + } + } else { + getPath(resourcePath, imageFileType, imageFileName, function (path, type) { + slider.loadBarTexture(path, type); + }); + } + + var normalDic = options["ballNormalData"]; + getPath(resourcePath, normalDic["resourceType"] || 0, normalDic["path"], function (path, type) { + slider.loadSlidBallTextureNormal(path, type); + }); + + var pressedDic = options["ballPressedData"]; + getPath( + resourcePath, + pressedDic["resourceType"] || normalDic["resourceType"], + pressedDic["path"] || normalDic["path"], + function (path, type) { + slider.loadSlidBallTexturePressed(path, type); + }); + + var disabledDic = options["ballDisabledData"]; + getPath(resourcePath, disabledDic["resourceType"] || 0, disabledDic["path"], function (path, type) { + slider.loadSlidBallTextureDisabled(path, type); + }); + + var progressBarDic = options["progressBarData"]; + getPath(resourcePath, progressBarDic["resourceType"] || 0, progressBarDic["path"], function (path, type) { + slider.loadProgressBarTexture(path, type); + }); + }; + /** + * TextField parser (UITextField) + */ + parser.TextFieldAttributes = function (widget, options, resourcePath) { + var ph = options["placeHolder"] || ""; + if (ph) + widget.setPlaceHolder(ph); + widget.setString(options["text"] || ""); + var fs = options["fontSize"]; + if (fs) + widget.setFontSize(fs); + var fn = options["fontName"]; + if (fn != null) { + if (cc.sys.isNative) { + if (regTTF.test(fn)) { + widget.setFontName(cc.path.join(cc.loader.resPath, resourcePath, fn)); + } else { + widget.setFontName(fn); + } + } else { + widget.setFontName(fn.replace(regTTF, '')); + } + } + var tsw = options["touchSizeWidth"] || 0; + var tsh = options["touchSizeHeight"] || 0; + if (tsw != null && tsh != null) + widget.setTouchSize(tsw, tsh); + + var dw = options["width"] || 0; + var dh = options["height"] || 0; + if (dw > 0 || dh > 0) { + //textField.setSize(cc.size(dw, dh)); + } + var maxLengthEnable = options["maxLengthEnable"]; + widget.setMaxLengthEnabled(maxLengthEnable); + + if (maxLengthEnable) { + var maxLength = options["maxLength"]; + widget.setMaxLength(maxLength); + } + var passwordEnable = options["passwordEnable"]; + widget.setPasswordEnabled(passwordEnable); + if (passwordEnable) + widget.setPasswordStyleText(options["passwordStyleText"]); + + var aw = options["areaWidth"] || 0; + var ah = options["areaHeight"] || 0; + if (aw && ah) { + var size = cc.size(aw, ah); + widget.setTextAreaSize(size); + } + var ha = options["hAlignment"] || 0; + if (ha) + widget.setTextHorizontalAlignment(ha); + var va = options["vAlignment"] || 0; + if (va) + widget.setTextVerticalAlignment(va); + + var r = isNaN(options["colorR"]) ? 255 : options["colorR"]; + var g = isNaN(options["colorG"]) ? 255 : options["colorG"]; + var b = isNaN(options["colorB"]) ? 255 : options["colorB"]; + widget.setTextColor(cc.color(r, g, b)); + }; + + parser.parsers = { + "Panel": {object: ccui.Layout, handle: parser.LayoutAttributes}, + "Button": {object: ccui.Button, handle: parser.ButtonAttributes}, + "CheckBox": {object: ccui.CheckBox, handle: parser.CheckBoxAttributes}, + "ImageView": {object: ccui.ImageView, handle: parser.ImageViewAttributes}, + "LabelAtlas": {object: ccui.TextAtlas, handle: parser.TextAtlasAttributes}, + "LabelBMFont": {object: ccui.TextBMFont, handle: parser.TextBMFontAttributes}, + "Label": {object: ccui.Text, handle: parser.TextAttributes}, + "ListView": {object: ccui.ListView, handle: parser.ListViewAttributes}, + "LoadingBar": {object: ccui.LoadingBar, handle: parser.LoadingBarAttributes}, + "PageView": {object: ccui.PageView, handle: parser.PageViewAttributes}, + "ScrollView": {object: ccui.ScrollView, handle: parser.ScrollViewAttributes}, + "Slider": {object: ccui.Slider, handle: parser.SliderAttributes}, + "TextField": {object: ccui.TextField, handle: parser.TextFieldAttributes} + }; + + load.registerParser("ccui", "*", parser); + +})(ccs._load, ccs._parser); diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/timeline/ActionTimeline.js b/frameworks/cocos2d-html5/extensions/cocostudio/timeline/ActionTimeline.js new file mode 100644 index 0000000..fd899ae --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/timeline/ActionTimeline.js @@ -0,0 +1,537 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + + +/** + * ActionTimelineData + * @name ccs.ActionTimelineData + * @extend ccs.Class + * @class + * + */ +ccs.ActionTimelineData = ccs.Class.extend({ + + _actionTag: 0, + + ctor: function (actionTag) { + this._init(actionTag); + }, + + _init: function (actionTag) { + this._actionTag = actionTag; + return true; + }, + + /** + * Set the action tag. + * @param {number} actionTag + */ + setActionTag: function (actionTag) { + this._actionTag = actionTag; + }, + + /** + * Gets the action tag. + */ + getActionTag: function () { + return this._actionTag; + } + +}); + +ccs.AnimationInfo = function (name, start, end) { + this.name = name; + this.startIndex = start; + this.endIndex = end; +}; + +ccs.ComExtensionData = ccs.Component.extend({ + + _customProperty: null, + _timelineData: null, + _name: "ComExtensionData", + + ctor: function(){ + this._customProperty = ""; + this._timelineData = new ccs.ActionTimelineData(0); + return true; + }, + + setActionTag: function(actionTag){ + this._timelineData.setActionTag(actionTag); + }, + + getActionTag: function(){ + return this._timelineData.getActionTag(); + }, + + setCustomProperty: function(customProperty){ + this._customProperty = customProperty; + }, + + getCustomProperty: function(){ + return this._customProperty; + } + +}); + +ccs.ComExtensionData.create = function(){ + return new ccs.ComExtensionData(); +}; + +/** + * Create new ActionTimelineData. + * + * @deprecated v3.0, please use new ccs.ActionTimelineData() instead. + * + * @name ccs.ActionTimelineData.create + * @function + * @param actionTag + * @returns {ccs.ActionTimelineData} + */ +ccs.ActionTimelineData.create = function (actionTag) { + return new ccs.ActionTimelineData(actionTag); +}; + + +/** + * ActionTimeline + * @class + * @extend cc.Action + * + * @property gotoFrameAndPlay + * @property gotoFrameAndPause + */ +ccs.ActionTimeline = cc.Action.extend({ + + _timelineMap: null, + _timelineList: null, + _duration: 0, + _time: null, + _timeSpeed: 1, + _frameInternal: 1 / 60, + _playing: false, + _currentFrame: 0, + _startFrame: 0, + _endFrame: 0, + _loop: null, + _frameEventListener: null, + _animationInfos: null, + _lastFrameListener: null, + + ctor: function () { + cc.Action.prototype.ctor.call(this); + this._timelineMap = {}; + this._timelineList = []; + this._animationInfos = {}; + this.init(); + }, + + _gotoFrame: function (frameIndex) { + var size = this._timelineList.length; + for (var i = 0; i < size; i++) { + this._timelineList[i]._gotoFrame(frameIndex); + } + }, + + _stepToFrame: function (frameIndex) { + var size = this._timelineList.length; + for (var i = 0; i < size; i++) { + this._timelineList[i]._stepToFrame(frameIndex); + } + }, + + //emit frame event, call it when enter a frame + _emitFrameEvent: function (frame) { + if (this._frameEventListener) { + this._frameEventListener(frame); + } + }, + + init: function () { + return true; + }, + + /** + * Goto the specified frame index, and start playing from this index. + * @param startIndex The animation will play from this index. + * @param [endIndex=] The animation will end at this index. + * @param [currentFrameIndex=] set current frame index. + * @param [loop=] Whether or not the animation need loop. + */ + gotoFrameAndPlay: function (startIndex, endIndex, currentFrameIndex, loop) { + //Consolidation parameters + var i = 0, + argLen = arguments.length; + var num = [], + bool; + for (i; i < argLen; i++) { + if (typeof arguments[i] === "boolean") { + bool = arguments[i]; + } else { + num.push(arguments[i]); + } + } + startIndex = num[0]; + endIndex = num[1] !== undefined ? num[1] : this._duration; + currentFrameIndex = num[2] || startIndex; + loop = bool != null ? bool : true; + + this._startFrame = startIndex; + this._endFrame = endIndex; + this._currentFrame = currentFrameIndex; + this._loop = loop; + this._time = this._currentFrame * this._frameInternal; + + this.resume(); + this._gotoFrame(this._currentFrame); + }, + + /** + * Goto the specified frame index, and pause at this index. + * @param startIndex The animation will pause at this index. + */ + gotoFrameAndPause: function (startIndex) { + this._startFrame = this._currentFrame = startIndex; + this._time = this._currentFrame * this._frameInternal; + + this.pause(); + this._gotoFrame(this._currentFrame); + }, + + /** + * Pause the animation. + */ + pause: function () { + this._playing = false; + }, + + /** + * Resume the animation. + */ + resume: function () { + this._playing = true; + }, + + /** + * Whether or not Action is playing. + */ + isPlaying: function () { + return this._playing; + }, + + /** + * Set the animation speed, this will speed up or slow down the speed. + * @param {number} speed + */ + setTimeSpeed: function (speed) { + this._timeSpeed = speed; + }, + + /** + * Get current animation speed. + * @returns {number} + */ + getTimeSpeed: function () { + return this._timeSpeed; + }, + + /** + * duration of the whole action + * @param {number} duration + */ + setDuration: function (duration) { + this._duration = duration; + }, + + /** + * Get current animation duration. + * @returns {number} + */ + getDuration: function () { + return this._duration; + }, + + /** + * Start frame index of this action + * @returns {number} + */ + getStartFrame: function () { + return this._startFrame; + }, + + /** + * End frame of this action. + * When action play to this frame, if action is not loop, then it will stop, + * or it will play from start frame again. + * @returns {number} + */ + getEndFrame: function () { + return this._endFrame; + }, + + /** + * Set current frame index, this will cause action plays to this frame. + */ + setCurrentFrame: function (frameIndex) { + if (frameIndex >= this._startFrame && frameIndex <= this._endFrame) { + this._currentFrame = frameIndex; + this._time = this._currentFrame * this._frameInternal; + } else { + cc.log("frame index is not between start frame and end frame"); + } + + }, + + /** + * Get current frame. + * @returns {number} + */ + getCurrentFrame: function () { + return this._currentFrame; + }, + + /** + * add Timeline to ActionTimeline + * @param {ccs.Timeline} timeline + */ + addTimeline: function (timeline) { + var tag = timeline.getActionTag(); + if (!this._timelineMap[tag]) { + this._timelineMap[tag] = []; + } + + if (this._timelineMap[tag].indexOf(timeline) === -1) { + this._timelineList.push(timeline); + this._timelineMap[tag].push(timeline); + timeline.setActionTimeline(this); + } + + }, + + /** + * remove Timeline to ActionTimeline + * @param {ccs.Timeline} timeline + */ + removeTimeline: function (timeline) { + var tag = timeline.getActionTag(); + if (this._timelineMap[tag]) { + if (this._timelineMap[tag].some(function (item) { + if (item === timeline) + return true; + })) { + cc.arrayRemoveObject(this._timelineMap[tag], timeline); + cc.arrayRemoveObject(this._timelineList, timeline); + timeline.setActionTimeline(null); + } + } + }, + + /** + * Gets the timeline list + * @returns {array | null} + */ + getTimelines: function () { + return this._timelineList; + }, + + /** + * Set the Frame event + * @param {function} listener + */ + setFrameEventCallFunc: function (listener) { + this._frameEventListener = listener; + }, + + /** + * remove event + */ + clearFrameEventCallFunc: function () { + this._frameEventListener = null; + }, + + /** + * Clone this timeline + * @returns {ccs.ActionTimeline} + */ + clone: function () { + var newAction = new ccs.ActionTimeline(); + newAction.setDuration(this._duration); + newAction.setTimeSpeed(this._timeSpeed); + + for (var a in this._timelineMap) { + var timelines = this._timelineMap[a]; + for (var b in timelines) { + var timeline = timelines[b]; + var newTimeline = timeline.clone(); + newAction.addTimeline(newTimeline); + } + } + + return newAction; + + }, + + /** + * Reverse is not defined; + * @returns {null} + */ + reverse: function () { + return null; + }, + + /** + * Stepping of this time line. + * @param {number} delta + */ + step: function (delta) { + if (!this._playing || this._timelineMap.length === 0 || this._duration === 0) { + return; + } + + this._time += delta * this._timeSpeed; + var endoffset = this._time - this._endFrame * this._frameInternal; + + if (endoffset < this._frameInternal) { + this._currentFrame = Math.floor(this._time / this._frameInternal); + this._stepToFrame(this._currentFrame); + if (endoffset >= 0 && this._lastFrameListener) + this._lastFrameListener(); + } else { + this._playing = this._loop; + if (!this._playing) { + this._time = this._endFrame * this._frameInternal; + if (this._currentFrame != this._endFrame) { + this._currentFrame = this._endFrame; + this._stepToFrame(this._currentFrame); + if (this._lastFrameListener) + this._lastFrameListener(); + } + } else + this.gotoFrameAndPlay(this._startFrame, this._endFrame, this._loop); + } + + }, + + _foreachNodeDescendant: function (parent, callback) { + callback(parent); + + var children = parent.getChildren(); + for (var i = 0; i < children.length; i++) { + var child = children[i]; + this._foreachNodeDescendant(child, callback); + } + }, + + /** + * start with node. + * @param {cc.Node} target + */ + startWithTarget: function(target){ + cc.Action.prototype.startWithTarget.call(this, target); + + var self = this; + var callback = function(child){ + var data = child.getComponent("ComExtensionData"); + + if(data) { + var actionTag = data.getActionTag(); + if(self._timelineMap[actionTag]) { + var timelines = self._timelineMap[actionTag]; + for (var i=0; i _renderCmd._debug + if (this._squareVertices === null) + this._squareVertices = [ + {x: 0, y: 0}, {x: 0, y: 0}, {x: 0, y: 0}, {x: 0, y: 0} + ]; + + this._rackColor = cc.color.WHITE; + this._blendFunc = BlendFunc.ALPHA_NON_PREMULTIPLIED; + + this._childBones = []; + this._boneSkins = []; + + this._rackLength = length === undefined ? 50 : length; + this._rackWidth = 20; + this._updateVertices(); + //this._updateColor(); + }, + + visit: function (parent) { + this._visit && this._visit(parent && parent._renderCmd); + }, + + addSkin: function (skin, display, hideOthers/*false*/) { + // skin, display + // skin, display, hideOthers + var boneSkins = this._boneSkins; + debug.assert(skin != null, "Argument must be non-nil"); + if (hideOthers) { + for (var i = 0; i < boneSkins.length; i++) { + boneSkins[i].setVisible(false); + } + } + Node.prototype.addChild.call(this, skin); + this._boneSkins.push(skin); + skin.setVisible(display); + }, + + getChildBones: function () { + return this._childBones; + }, + + getSkins: function () { + return this._boneSkins; + }, + + displaySkin: function (skin, hideOthers) { + var boneSkins = this._boneSkins; + var boneSkin, i; + if (typeof skin === "string") { + for (i = 0; i < boneSkins.length; i++) { + boneSkin = boneSkins[i]; + if (skin == boneSkin.getName()) { + boneSkin.setVisible(true); + } else if (hideOthers) { + boneSkin.setVisible(false); + } + } + } else { + for (i = 0; i < boneSkins.length; i++) { + boneSkin = boneSkins[i]; + if (boneSkin == skin) { + boneSkin.setVisible(true); + } else if (hideOthers) { + boneSkin.setVisible(false); + } + } + } + }, + + getVisibleSkins: function () { + var displayingSkins = []; + var boneSkins = this._boneSkins; + for (var boneSkin, i = 0; i < boneSkins.length; i++) { + boneSkin = boneSkins[i]; + if (boneSkin.isVisible()) { + displayingSkins.push(boneSkin); + } + } + return displayingSkins; + }, + + getRootSkeletonNode: function () { + return this._rootSkeleton; + }, + + getAllSubBones: function () { + var allBones = []; + var boneStack = []; // for avoid recursive + var childBones = this._childBones; + for (var i = 0; i < childBones.length; i++) { + boneStack.push(childBones[i]); + } + + while (boneStack.length > 0) { + var top = boneStack.pop(); + allBones.push(top); + var topChildren = top.getChildBones(); + for (var j = 0; j < topChildren; j++) { + boneStack.push(topChildren[j]); + } + } + return allBones; + }, + + getAllSubSkins: function () { + var allBones = this.getAllSubBones(); + var allSkins = []; + for (var i = 0; i < allBones.length; i++) { + var skins = allBones[i].getSkins(); + for (var j = 0; j < skins.length; j++) { + allSkins.push(skins[i]); + } + } + return allSkins; + }, + + addChild: function (child, localZOrder, tag) { + //child, localZOrder, tag + //child, localZOrder, name + Node.prototype.addChild.call(this, child, localZOrder, tag); + this._addToChildrenListHelper(child); + }, + + removeChild: function (child, cleanup) { + if (this._children.indexOf(child) !== -1) { + Node.prototype.removeChild.call(this, child, cleanup); + this._removeFromChildrenListHelper(child); + } + }, + + setBlendFunc: function (blendFunc) { + var ob = this._blendFunc; + if (blendFunc && ob.src !== blendFunc.src && ob.dst !== blendFunc.dst) { + this._blendFunc = blendFunc; + var boneSkins = this._boneSkins; + for (var boneSkin, i = 0; i < boneSkins.length; i++) { + boneSkin = boneSkins[i]; + boneSkin.setBlendFunc(blendFunc); + } + } + }, + + getBlendFunc: function () { + return this._blendFunc; + }, + + setDebugDrawLength: function (length) { + this._rackLength = length; + this._updateVertices(); + }, + + getDebugDrawLength: function () { + return this._rackLength; + }, + + setDebugDrawWidth: function (width) { + this._rackWidth = width; + this._updateVertices(); + }, + + getDebugDrawWidth: function () { + return this._rackWidth; + }, + + setDebugDrawEnabled: function (isDebugDraw) { + var renderCmd = this._renderCmd; + if (renderCmd._debug === isDebugDraw) + return; + + renderCmd._debug = isDebugDraw; + cc.renderer.childrenOrderDirty = true; + + if (this._visible && null != this._rootSkeleton) { + this._rootSkeleton._subBonesDirty = true; + this._rootSkeleton._subBonesOrderDirty = true; + } + }, + + isDebugDrawEnabled: function () { + return this._renderCmd._debug; + }, + + setDebugDrawColor: function (color) { + this._rackColor = color; + }, + + getDebugDrawColor: function () { + return this._rackColor; + }, + + getVisibleSkinsRect: function () { + var minx, miny, maxx, maxy = 0; + minx = miny = maxx = maxy; + var first = true; + + var displayRect = type.rect(0, 0, 0, 0); + if (this._renderCmd._debug && this._rootSkeleton != null && this._rootSkeleton._renderCmd._debug) { + maxx = this._rackWidth; + maxy = this._rackLength; + first = false; + } + + var boneSkins = this._boneSkins; + for (var skin, i = 0; i < boneSkins.length; i++) { + skin = boneSkins[i]; + var r = skin.getBoundingBox(); + if (!skin.isVisible() || (r.x === 0 && r.y === 0 && r.width === 0 && r.height === 0)) + continue; + + if (first) { + minx = cc.rectGetMinX(r); + miny = cc.rectGetMinY(r); + maxx = cc.rectGetMaxX(r); + maxy = cc.rectGetMaxY(r); + + first = false; + } else { + minx = Math.min(cc.rectGetMinX(r), minx); + miny = Math.min(cc.rectGetMinY(r), miny); + maxx = Math.max(cc.rectGetMaxX(r), maxx); + maxy = Math.max(cc.rectGetMaxY(r), maxy); + } + displayRect.setRect(minx, miny, maxx - minx, maxy - miny); + } + return displayRect; + }, + + getBoundingBox: function () { + var boundingBox = this.getVisibleSkinsRect(); + return cc.rectApplyAffineTransform(boundingBox, this.getNodeToParentAffineTransform()); + }, + + batchBoneDrawToSkeleton: function (bone) { + }, + + setLocalZOrder: function (localZOrder) { + Node.prototype.setLocalZOrder.call(this, localZOrder); + if (this._rootSkeleton != null) + this._rootSkeleton._subBonesOrderDirty = true; + }, + + setName: function (name) { + var rootSkeleton = this._rootSkeleton; + var oldName = this.getName(); + Node.prototype.setName.call(this, name); + if (rootSkeleton != null) { + var oIter = rootSkeleton._subBonesMap[oldName]; + var nIter = rootSkeleton._subBonesMap[name]; + if (oIter && !nIter) { + delete rootSkeleton._subBonesMap[oIter]; + rootSkeleton._subBonesMap[name] = oIter; + } + } + }, + + setContentSize: function (contentSize) { + Node.prototype.setContentSize.call(this, contentSize); + this._updateVertices(); + }, + + setAnchorPoint: function (anchorPoint) { + Node.prototype.setAnchorPoint.call(this, anchorPoint); + this._updateVertices(); + }, + + setVisible: function (visible) { + if (this._visible == visible) + return; + Node.prototype.setVisible.call(this, visible); + if (this._rootSkeleton != null) { + this._rootSkeleton._subBonesDirty = true; + this._rootSkeleton._subBonesOrderDirty = true; + } + }, + + _addToChildrenListHelper: function (child) { + if (child instanceof BoneNode) { + this._addToBoneList(child); + } else { + //if (child instanceof SkinNode) { + this._addToSkinList(child); + //} + } + }, + + _removeFromChildrenListHelper: function (child) { + if (child instanceof BoneNode) { + this._removeFromBoneList(child); + } else { + if (child instanceof SkinNode) + this._removeFromSkinList(skin); + } + }, + + _removeFromBoneList: function (bone) { + if ( + this._rootSkeleton != null && + bone instanceof ccs.SkeletonNode && + bone._rootSkeleton === this._rootSkeleton + ) { + bone._rootSkeleton = null; + var subBones = bone.getAllSubBones(); + subBones.push(bone); + for (var subBone, i = 0; i < subBones.length; i++) { + subBone = subBones[i]; + subBone._rootSkeleton = null; + delete this._rootSkeleton._subBonesMap[subBone.getName()]; + this._rootSkeleton._subBonesDirty = true; + this._rootSkeleton._subBonesOrderDirty = true; + } + } else { + this._rootSkeleton._subBonesDirty = true; + this._rootSkeleton._subBonesOrderDirty = true; + } + cc.arrayRemoveObject(this._childBones, bone); + }, + + _setRootSkeleton: function (rootSkeleton) { + this._rootSkeleton = rootSkeleton; + var subBones = this.getAllSubBones(); + for (var i = 0; i < subBones.length; i++) { + this._addToBoneList(subBones[i]); + } + }, + + _addToBoneList: function (bone) { + if (this._childBones.indexOf(bone) === -1) + this._childBones.push(bone); + if (this._rootSkeleton != null) { + var skeletonNode = bone; + if (!(skeletonNode instanceof SkinNode) && !bone._rootSkeleton) {// not nest skeleton + var subBones = bone.getAllSubBones(); + subBones.push(bone); + for (var subBone, i = 0; i < subBones.length; i++) { + subBone = subBones[i]; + subBone._setRootSkeleton(this._rootSkeleton); + var bonename = subBone.getName(); + if (!this._rootSkeleton._subBonesMap[bonename]) { + this._rootSkeleton._subBonesMap[subBone.getName()] = subBone; + this._rootSkeleton._subBonesDirty = true; + this._rootSkeleton._subBonesOrderDirty = true; + } else { + cc.log("already has a bone named %s in skeleton %s", bonename, this._rootSkeleton.getName()); + this._rootSkeleton._subBonesDirty = true; + this._rootSkeleton._subBonesOrderDirty = true; + } + } + } + } + }, + + _visitSkins: function () { + var cmd = this._renderCmd; + // quick return if not visible + if (!this._visible) + return; + + var parentCmd = cmd.getParentRenderCmd(); + if (parentCmd) + cmd._curLevel = parentCmd._curLevel + 1; + + //visit for canvas + var i, children = this._boneSkins, child; + //var i, children = this._children, child; + cmd._syncStatus(parentCmd); + var len = children.length; + if (len > 0) { + this.sortAllChildren(); + // draw children zOrder < 0 + for (i = 0; i < len; i++) { + child = children[i]; + if (child._localZOrder < 0) + child.visit(this); + else + break; + } + for (; i < len; i++) + children[i].visit(this); + } + cmd._dirtyFlag = 0; + }, + + _addToSkinList: function (skin) { + this._boneSkins.push(skin); + if (skin.getBlendFunc) { + var blendFunc = skin.getBlendFunc(); + if (this._blendFunc.src !== blendFunc.src && this._blendFunc.dst !== blendFunc.dst) + skin.setBlendFunc(this._blendFunc); + } + }, + + _removeFromSkinList: function (skin) { + cc.arrayRemoveObject(this._boneSkins, skin); + }, + + sortAllChildren: function () { + this._sortArray(this._childBones); + this._sortArray(this._boneSkins); + Node.prototype.sortAllChildren.call(this); + }, + + _sortArray: function (array) { + if (!array) + return; + var len = array.length, i, j, tmp; + for (i = 1; i < len; i++) { + tmp = array[i]; + j = i - 1; + while (j >= 0) { + if (tmp._localZOrder < array[j]._localZOrder) { + array[j + 1] = array[j]; + } else if (tmp._localZOrder === array[j]._localZOrder && tmp.arrivalOrder < array[j].arrivalOrder) { + array[j + 1] = array[j]; + } else { + break; + } + j--; + } + array[j + 1] = tmp; + } + }, + + _updateVertices: function () { + var squareVertices = this._squareVertices, + anchorPointInPoints = this._renderCmd._anchorPointInPoints; + if (this._rackLength != squareVertices[2].x - anchorPointInPoints.x || + squareVertices[3].y != this._rackWidth / 2 - anchorPointInPoints.y) { + + squareVertices[1].x = squareVertices[1].y = squareVertices[3].y = 0; + squareVertices[0].x = squareVertices[2].x = this._rackLength * .1; + squareVertices[2].y = this._rackWidth * .5; + squareVertices[0].y = -squareVertices[2].y; + squareVertices[3].x = this._rackLength; + + for (var i = 0; i < squareVertices.length; i++) { + squareVertices[i].x += anchorPointInPoints.x; + squareVertices[i].y += anchorPointInPoints.y; + } + + this._renderCmd.updateDebugPoint(squareVertices); + } + }, + + _createRenderCmd: function () { + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return new BoneNodeCanvasCmd(this); + else + return new BoneNodeWebGLCmd(this); + } + }); + + BoneNode.create = function (length, color) { + // null + // length + // length, color + return new ccui.BoneNode(length, color); + }; + + var BoneNodeCanvasCmd = (function () { + + var BoneNodeCanvasCmd = function (node) { + this._rootCtor(node); + this._debug = false; + this._color = cc.color.WHITE; + this._drawNode = new cc.DrawNode(); + }; + + var proto = BoneNodeCanvasCmd.prototype = Object.create(Node.CanvasRenderCmd.prototype); + proto.constructor = BoneNodeCanvasCmd; + + proto.updateDebugPoint = function (points) { + this._drawNode.clear(); + this._drawNode.drawPoly(points, this._color, 0, this._color); + }; + + proto.transform = function (parentCmd, recursive) { + var rootSkeleton = this._node._rootSkeleton; + this.originTransform(parentCmd, recursive); + if (rootSkeleton && rootSkeleton._renderCmd._debug) { + this._drawNode._renderCmd.transform(this); + } + }; + + return BoneNodeCanvasCmd; + + })(); + + var BoneNodeWebGLCmd = (function () { + + var BoneNodeWebGLCmd = function (node) { + this._rootCtor(node); + this._debug = false; + this._color = cc.color.WHITE; + this._drawNode = new cc.DrawNode(); + }; + + var proto = BoneNodeWebGLCmd.prototype = Object.create(Node.WebGLRenderCmd.prototype); + proto.constructor = BoneNodeWebGLCmd; + + proto.updateDebugPoint = function (points) { + this._drawNode.clear(); + this._drawNode.drawPoly(points, this._color, 0, this._color); + }; + + proto.transform = function (parentCmd, recursive) { + var rootSkeleton = this._node._rootSkeleton; + this.originTransform(parentCmd, recursive); + if (rootSkeleton && rootSkeleton._renderCmd._debug) { + this._drawNode._renderCmd.transform(this); + } + }; + + return BoneNodeWebGLCmd; + + })(); + + return BoneNode; + +})(); diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/timeline/CCSkeletonNode.js b/frameworks/cocos2d-html5/extensions/cocostudio/timeline/CCSkeletonNode.js new file mode 100644 index 0000000..2bd075f --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/timeline/CCSkeletonNode.js @@ -0,0 +1,266 @@ +/**************************************************************************** + Copyright (c) 2015-2016 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +//9980397 + +/** + * SkeletonNode + * base class + * @class + */ +ccs.SkeletonNode = (function () { + + var BoneNode = ccs.BoneNode; + + var type = { + p: cc.p, + size: cc.size, + rect: cc.rect + }; + + var SkeletonNode = BoneNode.extend(/** @lends ccs.SkeletonNode# */{ + _subBonesMap: null, + + _squareVertices: null, + _squareColors: null, + _noMVPVertices: null, + _skinGroupMap: null, + + _sortedAllBonesDirty: false, + _sortedAllBones: null, + _batchedBoneVetices: null, + _batchedBoneColors: null, + _batchedVeticesCount: null, + _batchBoneCommand: null, + _subOrderedAllBones: null, + + ctor: function () { + this._squareVertices = [ + {x: 0, y: 0}, {x: 0, y: 0}, {x: 0, y: 0}, {x: 0, y: 0}, + {x: 0, y: 0}, {x: 0, y: 0}, {x: 0, y: 0}, {x: 0, y: 0} + ]; + this._rootSkeleton = this; + BoneNode.prototype.ctor.call(this); + this._subBonesMap = {}; + this._subOrderedAllBones = []; + + this._skinGroupMap = {}; + + this._rackLength = this._rackWidth = 20; + this._updateVertices(); + }, + + getBoneNode: function (boneName) { + var item = this._subBonesMap[boneName]; + if (item) + return item; + return null; + }, + + getAllSubBonesMap: function () { + return this._subBonesMap; + }, + + changeSkins: function (boneSkinNameMap) { + //boneSkinNameMap + //suitName + if (typeof boneSkinNameMap === "object") { + var boneSkin; + for (var name in boneSkinNameMap) { + boneSkin = boneSkinNameMap[name]; + var bone = this.getBoneNode(name); + if (null !== bone) + bone.displaySkin(boneSkin, true); + } + } else { + var suit = this._suitMap[boneSkinNameMap/*suitName*/]; + if (suit) + this.changeSkins(suit, true); + } + }, + + addSkinGroup: function (groupName, boneSkinNameMap) { + this._skinGroupMap[groupName] = boneSkinNameMap; + }, + + getBoundingBox: function () { + var minx, miny, maxx, maxy = 0; + minx = miny = maxx = maxy; + var boundingBox = this.getVisibleSkinsRect(); + var first = true; + if (boundingBox.x !== 0 || boundingBox.y !== 0 || boundingBox.width !== 0 || boundingBox.height !== 0) { + minx = cc.rectGetMinX(boundingBox); + miny = cc.rectGetMinY(boundingBox); + maxx = cc.rectGetMaxX(boundingBox); + maxy = cc.rectGetMaxY(boundingBox); + first = false; + } + var allBones = this.getAllSubBones(); + for (var bone, i = 0; i < allBones.length; i++) { + bone = allBones[i]; + var r = cc.rectApplyAffineTransform(bone.getVisibleSkinsRect(), bone.getNodeToParentTransform(bone.getRootSkeletonNode())); + if (r.x === 0 && r.y === 0 && r.width === 0 && r.height === 0) + continue; + + if (first) { + minx = cc.rectGetMinX(r); + miny = cc.rectGetMinY(r); + maxx = cc.rectGetMaxX(r); + maxy = cc.rectGetMaxY(r); + + first = false; + } else { + minx = Math.min(cc.rectGetMinX(r), minx); + miny = Math.min(cc.rectGetMinY(r), miny); + maxx = Math.max(cc.rectGetMaxX(r), maxx); + maxy = Math.max(cc.rectGetMaxY(r), maxy); + } + } + boundingBox.x = minx; + boundingBox.y = miny; + boundingBox.width = maxx - minx; + boundingBox.height = maxy - miny; + return cc.rectApplyAffineTransform(boundingBox, this.getNodeToParentTransform()); + }, + + _visit: function (parentCmd) { + var cmd = this._renderCmd; + parentCmd = parentCmd || cmd.getParentRenderCmd(); + + // quick return if not visible + if (!this._visible) { + cmd._propagateFlagsDown(parentCmd); + return; + } + cmd._syncStatus(parentCmd); + + var i, node; + if (this._children.length !== 0) { + for (i = 0; i < this._children.length; i++) { + node = this._children[i]; + node.visit(this); + } + } + + this._checkSubBonesDirty(); + var subOrderedAllBones = this._subOrderedAllBones, + subOrderedBone, subOrderedBoneCmd; + for (i = 0; i < subOrderedAllBones.length; i++) { + subOrderedBone = subOrderedAllBones[i]; + subOrderedBone._visitSkins(); + } + + if (cmd._debug) + for (i = 0; i < subOrderedAllBones.length; i++) { + subOrderedBoneCmd = subOrderedAllBones[i]._renderCmd; + cc.renderer.pushRenderCommand(subOrderedBoneCmd._drawNode._renderCmd); + } + cmd._dirtyFlag = 0; + }, + + _checkSubBonesDirty: function () { + if (this._subBonesDirty) { + this._updateOrderedAllbones(); + this._subBonesDirty = false; + } + if (this._subBonesOrderDirty) { + this._sortOrderedAllBones(); + this._subBonesOrderDirty = false; + } + }, + + _updateOrderedAllbones: function () { + this._subOrderedAllBones.length = 0; + // update sub bones, get All Visible SubBones + // get all sub bones as visit with visible + var boneStack = []; + var childBones = this._childBones; + for (var bone, i = 0; i < childBones.length; i++) { + bone = childBones[i]; + if (bone.isVisible()) + boneStack.push(bone); + } + while (boneStack.length > 0) { + var top = boneStack.pop(); + var topCmd = top._renderCmd; + topCmd._syncStatus(topCmd.getParentRenderCmd()); + this._subOrderedAllBones.push(top); + + var topChildren = top.getChildBones(); + + for (var childbone, i = 0; i < topChildren.length; i++) { + childbone = topChildren[i]; + if (childbone.isVisible()) + boneStack.push(childbone); + } + } + }, + + _sortOrderedAllBones: function () { + this._sortArray(this._subOrderedAllBones); + }, + + // protected + _updateVertices: function () { + var squareVertices = this._squareVertices, + anchorPointInPoints = this._renderCmd._anchorPointInPoints; + if (this._rackLength != squareVertices[6].x - anchorPointInPoints.x || + this._rackWidth != squareVertices[3].y - anchorPointInPoints.y) { + var radiusl = this._rackLength * .5; + var radiusw = this._rackWidth * .5; + var radiusl_2 = radiusl * .25; + var radiusw_2 = radiusw * .25; + squareVertices[5].y = squareVertices[2].y = squareVertices[1].y = squareVertices[6].y + = squareVertices[0].x = squareVertices[4].x = squareVertices[7].x = squareVertices[3].x = .0; + squareVertices[5].x = -radiusl; squareVertices[0].y = -radiusw; + squareVertices[6].x = radiusl; squareVertices[3].y = radiusw; + squareVertices[1].x = radiusl_2; squareVertices[7].y = radiusw_2; + squareVertices[2].x = -radiusl_2; squareVertices[4].y = -radiusw_2; + for (var i = 0; i < squareVertices.length; i++) { + squareVertices[i].x += anchorPointInPoints.x; + squareVertices[i].y += anchorPointInPoints.y; + } + } + }, + + _updateAllDrawBones: function () { + this._subDrawBones = {}; //.clear() + for (var name in this._subBonesMap) { + var bone = this._subBonesMap[name]; + if (bone.isVisible() && bone.isDebugDrawEnabled()) + this._subDrawBones.push(bone); + } + this._sortArray(this._sortedAllBones); + this._subDrawBones = false; + } + + }); + + SkeletonNode.create = function () { + return new SkeletonNode; + }; + + return SkeletonNode; + +})(); diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/timeline/CCSkinNode.js b/frameworks/cocos2d-html5/extensions/cocostudio/timeline/CCSkinNode.js new file mode 100644 index 0000000..310af38 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/timeline/CCSkinNode.js @@ -0,0 +1,14 @@ +ccs.SkinNode = (function () { + + var Node = cc.Node; + + var proto = {}; + + var SkinNode = Node.extend(proto); + + SkinNode.create = function () { + }; + + return SkinNode; + +})(); diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/timeline/Frame.js b/frameworks/cocos2d-html5/extensions/cocostudio/timeline/Frame.js new file mode 100644 index 0000000..b935fce --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/timeline/Frame.js @@ -0,0 +1,1430 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * Timeline Frame. + * base class + * @class + */ +ccs.Frame = ccs.Class.extend({ + + _frameIndex: null, + _tween: null, + _timeline: null, + _node: null, + _tweenType: null, + _easingParam: null, + _enterWhenPassed: null, + + ctor: function () { + this._frameIndex = 0; + this._tween = true; + this._timeline = null; + this._node = null; + this._enterWhenPassed = false; + this._easingParam = []; + }, + + _emitEvent: function () { + if (this._timeline) { + this._timeline.getActionTimeline()._emitFrameEvent(this); + } + }, + + _cloneProperty: function (frame) { + this._frameIndex = frame.getFrameIndex(); + this._tween = frame.isTween(); + this._tweenType = frame.getTweenType(); + this.setEasingParams(frame.getEasingParams()); + }, + + /** + * Set the frame index + * @param {number} frameIndex + */ + setFrameIndex: function (frameIndex) { + this._frameIndex = frameIndex; + }, + + /** + * Get the frame index + * @returns {null} + */ + getFrameIndex: function () { + return this._frameIndex; + }, + + /** + * Set timeline + * @param timeline + */ + setTimeline: function (timeline) { + this._timeline = timeline; + }, + + /** + * Get timeline + * @param timeline + * @returns {ccs.timeline} + */ + getTimeline: function (timeline) { + return this._timeline; + }, + + /** + * Set Node + * @param {cc.Node} node + */ + setNode: function (node) { + this._node = node; + }, + + /** + * gets the Node + * @return node + */ + getNode: function () { + return this._node; + }, + + /** + * set tween + * @param tween + */ + setTween: function (tween) { + this._tween = tween; + }, + + /** + * Gets the tween + * @returns {boolean | null} + */ + isTween: function () { + return this._tween; + }, + + /** + * the execution of the callback + * @override + * @param {ccs.Frame} nextFrame + */ + onEnter: function (nextFrame) { // = 0 + }, + + /** + * Each frame logic + * @override + * @param {number} percent + */ + apply: function (percent) { + if (!this._tween) + return; + if (this._tweenType !== ccs.FrameEaseType.TWEEN_EASING_MAX && this._tweenType !== ccs.FrameEaseType.LINEAR) + percent = this.tweenPercent(percent); + this._onApply(percent); + }, + + _onApply: function (percent) { + + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * @override + * @return {ccs.Frame} + */ + clone: function () { // = 0 + }, + + tweenPercent: function (percent) { + var func = ccs.Frame.tweenToMap[this._tweenType]; + if (func) + return func(percent, this._easingParam); + else + return percent; + }, + + setEasingParams: function (easingParams) { + if (easingParams) { + this._easingParam.length = 0; + for (var i = 0; i < easingParams.length; i++) + this._easingParam[i] = easingParams[i]; + } + }, + + getEasingParams: function () { + return this._easingParam; + }, + + setTweenType: function (tweenType) { + this._tweenType = tweenType; + }, + + getTweenType: function () { + return this._tweenType; + }, + + isEnterWhenPassed: function () { + return this._enterWhenPassed; + } +}); + +ccs.Frame.tweenToMap = { + "-1": function (time, easingParam) { + if (easingParam) { + var tt = 1 - time; + return easingParam[1] * tt * tt * tt + 3 * easingParam[3] * time * tt * tt + 3 * easingParam[5] * time * time * tt + easingParam[7] * time * time * time; + } + return time; + }, + 1: cc._easeSineInObj.easing,//Sine_EaseIn + 2: cc._easeSineOutObj.easing,//Sine_EaseOut + 3: cc._easeSineInOutObj.easing,//Sine_EaseInOut + + 4: cc._easeQuadraticActionIn.easing,//Quad_EaseIn + 5: cc._easeQuadraticActionOut.easing,//Quad_EaseOut + 6: cc._easeQuadraticActionInOut.easing,//Quad_EaseInOut + + 7: cc._easeCubicActionIn.easing, //Cubic_EaseIn + 8: cc._easeCubicActionOut.easing,//Cubic_EaseOut + 9: cc._easeCubicActionInOut.easing,//Cubic_EaseInOut + + 10: cc._easeCubicActionIn.easing,//Cubic_EaseIn + 11: cc._easeCubicActionOut.easing,//Cubic_EaseOut + 12: cc._easeCubicActionInOut.easing,//Cubic_EaseInOut + + 13: cc._easeQuinticActionIn.easing,//Quint_EaseIn + 14: cc._easeQuinticActionOut.easing,//Quint_EaseOut + 15: cc._easeQuinticActionInOut.easing,//Quint_EaseInOut + + 16: cc._easeExponentialInObj.easing,//Expo_EaseIn + 17: cc._easeExponentialOutObj.easing,//Expo_EaseOut + 18: cc._easeExponentialInOutObj.easing,//Expo_EaseInOut + + 19: cc._easeCircleActionIn.easing,//Circ_EaseIn + 20: cc._easeCircleActionOut.easing,//Circ_EaseOut + 21: cc._easeCircleActionInOut.easing,//Circ_EaseInOut + + 22: function (time, easingParam) { + var period = 0.3; + easingParam != null && ( period = easingParam[0] ); + return cc.easeElasticIn(period).easing(time); + },//Elastic_EaesIn + 23: function (time, easingParam) { + var period = 0.3; + easingParam != null && ( period = easingParam[0] ); + return cc.easeElasticOut(period).easing(time); + },//Elastic_EaesOut + 24: function (time, easingParam) { + var period = 0.3; + easingParam != null && ( period = easingParam[0] ); + return cc.easeElasticInOut(period).easing(time); + },//Elastic_EaesInOut + + 25: cc._easeBackInObj.easing, //Back_EaseIn + 26: cc._easeBackOutObj.easing, //Back_EaseOut + 27: cc._easeBackInOutObj.easing, //Back_EaseInOut + + 28: cc._easeBounceInObj.easing, //Bounce_EaseIn + 29: cc._easeBounceOutObj.easing, //Bounce_EaseOut + 30: cc._easeBounceInOutObj.easing //Bounce_EaseInOut +}; + +/** + * Visible frame + * To control the display state + * @class + * @extend ccs.Frame + */ +ccs.VisibleFrame = ccs.Frame.extend({ + + _visible: true, + + ctor: function () { + ccs.Frame.prototype.ctor.call(this); + this._visible = true; + }, + + /** + * the execution of the callback + * @param {ccs.Frame} nextFrame + */ + onEnter: function (nextFrame) { + if (this._node) + this._node.setVisible(this._visible); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * @return {ccs.VisibleFrame} + */ + clone: function () { + var frame = new ccs.VisibleFrame(); + frame.setVisible(this._visible); + + frame._cloneProperty(this); + + return frame; + }, + + /** + * Set display state + * @param {Boolean} visible + */ + setVisible: function (visible) { + this._visible = visible; + }, + + /** + * Get the display state + * @returns {Boolean} + */ + isVisible: function () { + return this._visible; + } + +}); + +/** + * Create the visible frame + * + * @deprecated v3.0, please use new ccs.VisibleFrame() instead. + * @returns {ccs.VisibleFrame} + */ +ccs.VisibleFrame.create = function () { + return new ccs.VisibleFrame(); +}; + +/** + * Texture frame + * @class + * @extend ccs.Frame + */ +ccs.TextureFrame = ccs.Frame.extend({ + + _sprite: null, + _textureName: null, + + ctor: function () { + ccs.Frame.prototype.ctor.call(this); + + this._textureName = ""; + }, + + /** + * Set the node element to draw texture + * @param {cc.Node} node + */ + setNode: function (node) { + ccs.Frame.prototype.setNode.call(this, node); + this._sprite = node; + }, + + /** + * the execution of the callback + * @param {ccs.Frame} nextFrame + */ + onEnter: function (nextFrame) { + if (this._sprite) { + var spriteBlendFunc = this._sprite.getBlendFunc(); + var spriteFrame = cc.spriteFrameCache._spriteFrames[this._textureName]; + if (spriteFrame != null) + this._sprite.setSpriteFrame(spriteFrame); + else + this._sprite.setTexture(this._textureName); + + if (this._sprite.getBlendFunc() !== spriteBlendFunc) + this._sprite.setBlendFunc(spriteBlendFunc); + } + + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * @return {ccs.TextureFrame} + */ + clone: function () { + var frame = new ccs.TextureFrame(); + frame.setTextureName(this._textureName); + frame._cloneProperty(this); + return frame; + }, + + /** + * Set the texture name + * @param {string} textureName + */ + setTextureName: function (textureName) { + this._textureName = textureName; + }, + + /** + * Gets the Texture name + * @returns {null} + */ + getTextureName: function () { + return this._textureName; + } + +}); + +/** + * Create the Texture frame + * + * @deprecated v3.0, please use new ccs.TextureFrame() instead. + * @returns {ccs.TextureFrame} + */ +ccs.TextureFrame.create = function () { + return new ccs.TextureFrame(); +}; + +/** + * Rotation Frame + * @class + * @extend ccs.Frame + */ +ccs.RotationFrame = ccs.Frame.extend({ + + _rotation: null, + _betwennRotation: null, + + ctor: function () { + ccs.Frame.prototype.ctor.call(this); + this._rotation = 0; + }, + + /** + * the execution of the callback + * @param {ccs.Frame} nextFrame + */ + onEnter: function (nextFrame) { + if (!this._node) + return; + this._node.setRotation(this._rotation); + + if (this._tween) { + this._betwennRotation = nextFrame._rotation - this._rotation; + } + }, + + /** + * Each frame logic + * @param {number} percent + */ + _onApply: function (percent) { + if (this._betwennRotation !== 0) { + var rotation = this._rotation + percent * this._betwennRotation; + this._node.setRotation(rotation); + } + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * @return {ccs.RotationFrame} + */ + clone: function () { + var frame = new ccs.RotationFrame(); + frame.setRotation(this._rotation); + + frame._cloneProperty(this); + + return frame; + }, + + /** + * Set the rotation + * @param {Number} rotation + */ + setRotation: function (rotation) { + this._rotation = rotation; + }, + + /** + * Gets the rotation + * @returns {Number} + */ + getRotation: function () { + return this._rotation; + } + +}); + +/** + * Create the Rotation frame + * + * @deprecated v3.0, please use new ccs.RotationFrame() instead. + * @returns {ccs.RotationFrame} + */ +ccs.RotationFrame.create = function () { + return new ccs.RotationFrame(); +}; + +/** + * Skew frame + * @class + * @extend ccs.Frame + */ +ccs.SkewFrame = ccs.Frame.extend({ + + _skewX: null, + _skewY: null, + _betweenSkewX: null, + _betweenSkewY: null, + + ctor: function () { + ccs.Frame.prototype.ctor.call(this); + this._skewX = 0; + this._skewY = 0; + }, + + /** + * the execution of the callback + * @param {ccs.Frame} nextFrame + */ + onEnter: function (nextFrame) { + if (!this._node) + return; + this._node.setSkewX(this._skewX); + this._node.setSkewY(this._skewY); + + if (this._tween) { + this._betweenSkewX = nextFrame._skewX - this._skewX; + this._betweenSkewY = nextFrame._skewY - this._skewY; + } + + }, + + /** + * Each frame logic + * @param {number} percent + */ + _onApply: function (percent) { + if (this._betweenSkewX !== 0 || this._betweenSkewY !== 0) { + var skewx = this._skewX + percent * this._betweenSkewX; + var skewy = this._skewY + percent * this._betweenSkewY; + + this._node.setSkewX(skewx); + this._node.setSkewY(skewy); + } + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * @return {ccs.SkewFrame} + */ + clone: function () { + var frame = new ccs.SkewFrame(); + frame.setSkewX(this._skewX); + frame.setSkewY(this._skewY); + + frame._cloneProperty(this); + + return frame; + }, + + /** + * Set the skew x + * @param {Number} skewx + */ + setSkewX: function (skewx) { + this._skewX = skewx; + }, + + /** + * Gets the skew x + * @returns {Number} + */ + getSkewX: function () { + return this._skewX; + }, + + /** + * Set the skew y + * @param {Number} skewy + */ + setSkewY: function (skewy) { + this._skewY = skewy; + }, + + /** + * Gets the skew y + * @returns {Number} + */ + getSkewY: function () { + return this._skewY; + } + +}); + +/** + * Create the Skew frame + * + * @deprecated v3.0, please use new ccs.SkewFrame() instead. + * @returns {ccs.SkewFrame} + */ +ccs.SkewFrame.create = function () { + return new ccs.SkewFrame(); +}; + +/** + * Rotation skew frame + * @class + * @extend ccs.SkewFrame + */ +ccs.RotationSkewFrame = ccs.SkewFrame.extend({ + + /** + * the execution of the callback + * @param {ccs.Frame} nextFrame + */ + onEnter: function (nextFrame) { + if (!this._node) + return; + this._node.setRotationX(this._skewX); + this._node.setRotationY(this._skewY); + + if (this._tween) { + this._betweenSkewX = nextFrame._skewX - this._skewX; + this._betweenSkewY = nextFrame._skewY - this._skewY; + } + + }, + + /** + * Each frame logic + * @param {number} percent + */ + _onApply: function (percent) { + if (this._node && (this._betweenSkewX !== 0 || this._betweenSkewY !== 0)) { + var skewx = this._skewX + percent * this._betweenSkewX; + var skewy = this._skewY + percent * this._betweenSkewY; + + this._node.setRotationX(skewx); + this._node.setRotationY(skewy); + } + + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * @return {ccs.RotationSkewFrame} + */ + clone: function () { + var frame = new ccs.RotationSkewFrame(); + frame.setSkewX(this._skewX); + frame.setSkewY(this._skewY); + + frame._cloneProperty(this); + + return frame; + + } + +}); + +/** + * Create the RotationSkew frame + * + * @deprecated v3.0, please use new ccs.RotationSkewFrame() instead. + * @returns {ccs.RotationSkewFrame} + */ +ccs.RotationSkewFrame.create = function () { + return new ccs.RotationSkewFrame(); +}; + +/** + * Position frame + * @class + * @extend ccs.Frame + */ +ccs.PositionFrame = ccs.Frame.extend({ + + _position: null, + _betweenX: null, + _betweenY: null, + + ctor: function () { + ccs.Frame.prototype.ctor.call(this); + this._position = cc.p(0, 0); + }, + + /** + * the execution of the callback + * @param {ccs.Frame} nextFrame + */ + onEnter: function (nextFrame) { + if (!this._node) + return; + + this._node.setPosition(this._position); + + if (this._tween) { + this._betweenX = nextFrame._position.x - this._position.x; + this._betweenY = nextFrame._position.y - this._position.y; + } + }, + + /** + * Each frame logic + * @param {number} percent + */ + _onApply: function (percent) { + if (this._node && (this._betweenX !== 0 || this._betweenY !== 0)) { + var p = cc.p(0, 0); + p.x = this._position.x + this._betweenX * percent; + p.y = this._position.y + this._betweenY * percent; + + this._node.setPosition(p); + } + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * @return {ccs.PositionFrame} + */ + clone: function () { + var frame = new ccs.PositionFrame(); + frame.setPosition(this._position); + + frame._cloneProperty(this); + + return frame; + }, + + /** + * Set the position + * @param {cc.p} position + */ + setPosition: function (position) { + this._position = position; + }, + + /** + * gets the position + * @returns {cc.p} + */ + getPosition: function () { + return this._position; + }, + + /** + * Set the position x + * @param {Number} x + */ + setX: function (x) { + this._position.x = x; + }, + + /** + * Gets the position x + * @returns {Number} + */ + getX: function () { + return this._position.x; + }, + + /** + * Set the position y + * @param {Number} y + */ + setY: function (y) { + this._position.y = y; + }, + + /** + * Gets the position y + * @returns {Number} + */ + getY: function () { + return this._position.y; + } + +}); + +/** + * Create the Position frame + * + * @deprecated v3.0, please use new ccs.PositionFrame() instead. + * @returns {ccs.PositionFrame} + */ +ccs.PositionFrame.create = function () { + return new ccs.PositionFrame(); +}; + +/** + * Scale frame + * @class + * @xtend ccs.Frame + */ +ccs.ScaleFrame = ccs.Frame.extend({ + + _scaleX: null, + _scaleY: null, + _betweenScaleX: null, + _betweenScaleY: null, + + ctor: function () { + ccs.Frame.prototype.ctor.call(this); + this._scaleX = 1; + this._scaleY = 1; + }, + + /** + * the execution of the callback + * @param {ccs.Frame} nextFrame + */ + onEnter: function (nextFrame) { + if (!this._node) + return; + this._node.setScaleX(this._scaleX); + this._node.setScaleY(this._scaleY); + + if (this._tween) { + this._betweenScaleX = nextFrame._scaleX - this._scaleX; + this._betweenScaleY = nextFrame._scaleY - this._scaleY; + } + + }, + + /** + * Each frame logic + * @param {number} percent + */ + _onApply: function (percent) { + if (this._node && (this._betweenScaleX !== 0 || this._betweenScaleY !== 0)) { + var scaleX = this._scaleX + this._betweenScaleX * percent; + var scaleY = this._scaleY + this._betweenScaleY * percent; + + this._node.setScaleX(scaleX); + this._node.setScaleY(scaleY); + } + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * @return {ccs.ScaleFrame} + */ + clone: function () { + var frame = new ccs.ScaleFrame(); + frame.setScaleX(this._scaleX); + frame.setScaleY(this._scaleY); + + frame._cloneProperty(this); + + return frame; + + }, + + /** + * Set the scale + * @param {Number} scale + */ + setScale: function (scale) { + this._scaleX = scale; + this._scaleY = scale; + }, + + /** + * Set the scale x + * @param {Number} scaleX + */ + setScaleX: function (scaleX) { + this._scaleX = scaleX; + }, + + /** + * Gets the scale x + * @returns {Number} + */ + getScaleX: function () { + return this._scaleX; + }, + + /** + * Set the scale y + * @param {Number} scaleY + */ + setScaleY: function (scaleY) { + this._scaleY = scaleY; + }, + + /** + * Gets the scale y + * @returns {Number} + */ + getScaleY: function () { + return this._scaleY; + } + +}); + +/** + * Create the Scale frame + * + * @deprecated v3.0, please use new ccs.ScaleFrame() instead. + * @returns {ccs.ScaleFrame} + */ +ccs.ScaleFrame.create = function () { + return new ccs.ScaleFrame(); +}; + +/** + * AnchorPoint frame + * @class + * @extend ccs.Frame + */ +ccs.AnchorPointFrame = ccs.Frame.extend({ + + _anchorPoint: null, + + ctor: function () { + ccs.Frame.prototype.ctor.call(this); + this._anchorPoint = cc.p(0, 0); + }, + + /** + * the execution of the callback + * @param {ccs.Frame} nextFrame + */ + onEnter: function (nextFrame) { + if (this._node) + this._node.setAnchorPoint(this._anchorPoint); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * @return {ccs.AnchorPointFrame} + */ + clone: function () { + var frame = new ccs.AnchorPointFrame(); + frame.setAnchorPoint(this._anchorPoint); + + frame._cloneProperty(this); + + return frame; + }, + + /** + * Set the anchor point + * @param {cc.p} point + */ + setAnchorPoint: function (point) { + this._anchorPoint = point; + }, + + /** + * Gets the anchor point + * @returns {cc.p} + */ + getAnchorPoint: function () { + return this._anchorPoint; + } + +}); + +/** + * Create the AnchorPoint frame + * + * @deprecated v3.0, please use new ccs.AnchorPointFrame() instead. + * @returns {ccs.AnchorPointFrame} + */ +ccs.AnchorPointFrame.create = function () { + return new ccs.AnchorPointFrame(); +}; + +/** + * Static param + * @namespace + */ +ccs.InnerActionType = { + LoopAction: 0, + NoLoopAction: 1, + SingleFrame: 2 +}; + +/** + * Inner action frame + * @class + * @extend ccs.Frame + */ +ccs.InnerActionFrame = ccs.Frame.extend({ + + _innerActionType: null, + _startFrameIndex: null, + + _endFrameIndex: 0, + _singleFrameIndex: 0, + _enterWithName: null, + _animationName: "", + + ctor: function () { + ccs.Frame.prototype.ctor.call(this); + + this._enterWithName = false; + this._innerActionType = ccs.InnerActionType.LoopAction; + this._startFrameIndex = 0; + }, + + /** + * the execution of the callback + * @param {ccs.Frame} nextFrame + */ + onEnter: function (nextFrame) { + if (!this._node) return; + var innerActiontimeline = this._node.getActionByTag(this._node.getTag()); + if (!innerActiontimeline) return; + if (ccs.InnerActionType.SingleFrame === this._innerActionType) { + innerActiontimeline.gotoFrameAndPause(this._singleFrameIndex); + return; + } + + var innerStart = this._startFrameIndex; + var innerEnd = this._endFrameIndex; + if (this._enterWithName) { + if (this._animationName === "-- ALL --") { + innerStart = 0; + innerEnd = innerActiontimeline.getDuration(); + } else if (innerActiontimeline.isAnimationInfoExists(this._animationName)) { + var info = innerActiontimeline.getAnimationInfo(this._animationName); + innerStart = info.startIndex; + innerEnd = info.endIndex; + } else { + cc.log("Animation %s not exists!", this._animationName); + } + } + + var duration = this._timeline.getActionTimeline().getDuration(); + var odddiff = duration - this._frameIndex - innerEnd + innerStart; + if (odddiff < 0) { + innerEnd += odddiff; + } + + if (ccs.InnerActionType.NoLoopAction === this._innerActionType) { + innerActiontimeline.gotoFrameAndPlay(innerStart, innerEnd, false); + } else if (ccs.InnerActionType.LoopAction === this._innerActionType) { + innerActiontimeline.gotoFrameAndPlay(innerStart, innerEnd, true); + } + }, + + setAnimationName: function (animationName) { + this._animationName = animationName; + }, + + setSingleFrameIndex: function (frameIndex) { + this._singleFrameIndex = frameIndex; + }, + + getSingleFrameIndex: function () { + return this._startFrameIndex; + }, + + setEnterWithName: function (isEnterWithName) { + this._enterWithName = isEnterWithName; + }, + + getEnterWithName: function () { + return this._enterWithName; + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * @return {ccs.InnerActionFrame} + */ + clone: function () { + var frame = new ccs.InnerActionFrame(); + frame.setInnerActionType(this._innerActionType); + frame.setStartFrameIndex(this._startFrameIndex); + frame.setEnterWithName(this._enterWithName); + frame.setAnimationName(this._animationName); + frame.setSingleFrameIndex(this._singleFrameIndex); + + frame._cloneProperty(this); + + return frame; + + }, + + /** + * Set the inner action type + * @param {ccs.InnerActionType} type + */ + setInnerActionType: function (type) { + this._innerActionType = type; + }, + + /** + * Gets the inner action type + * @returns {ccs.InnerActionType} + */ + getInnerActionType: function () { + return this._innerActionType; + }, + + /** + * Set the start frame index + * @param {Number} frameIndex + */ + setStartFrameIndex: function (frameIndex) { + this._startFrameIndex = frameIndex; + }, + + /** + * Get the start frame index + * @returns {Number} + */ + getStartFrameIndex: function () { + return this._startFrameIndex; + } + +}); + +/** + * Create the InnerAction frame + * + * @deprecated v3.0, please use new ccs.InnerActionFrame() instead. + * @returns {ccs.InnerActionFrame} + */ +ccs.InnerActionFrame.create = function () { + return new ccs.InnerActionFrame(); +}; + +/** + * Color frame + * @class + * @extend ccs.Frame + */ +ccs.ColorFrame = ccs.Frame.extend({ + + _alpha: null, + _color: null, + + _betweenAlpha: null, + _betweenRed: null, + _betweenGreen: null, + _betweenBlue: null, + + ctor: function () { + ccs.Frame.prototype.ctor.call(this); + this._color = cc.color(255, 255, 255); + }, + + /** + * the execution of the callback + * @param {ccs.ColorFrame} nextFrame + */ + onEnter: function (nextFrame) { + if (!this._node) + return; + this._node.setColor(this._color); + if (this._tween) { + var color = nextFrame._color; + this._betweenRed = color.r - this._color.r; + this._betweenGreen = color.g - this._color.g; + this._betweenBlue = color.b - this._color.b; + } + + }, + + /** + * Each frame logic + * @param {number} percent + */ + _onApply: function (percent) { + if (this._node && this._tween && (this._betweenAlpha !== 0 || this._betweenRed !== 0 || this._betweenGreen !== 0 || this._betweenBlue !== 0)) { + + var color = cc.color(255, 255, 255); + color.r = this._color.r + this._betweenRed * percent; + color.g = this._color.g + this._betweenGreen * percent; + color.b = this._color.b + this._betweenBlue * percent; + + this._node.setColor(color); + if (this._alpha !== null) { + var alpha = this._alpha + this._betweenAlpha * percent; + this._node.setOpacity(alpha); + } + + } + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * @return {ccs.ColorFrame} + */ + clone: function () { + var frame = new ccs.ColorFrame(); + frame.setColor(this._color); + frame._cloneProperty(this); + return frame; + }, + + /** + * Set the color + * @param {cc.color} color + */ + setColor: function (color) { + this._color = color; + }, + + /** + * Gets the color + * @returns {cc.color} + */ + getColor: function () { + return this._color; + } + +}); + +/** + * Create the Color frame + * + * @deprecated v3.0, please use new ccs.ColorFrame() instead. + * @returns {ccs.ColorFrame} + */ +ccs.ColorFrame.create = function () { + return new ccs.ColorFrame(); +}; + +/** + * Alpha frame + * @class + * @extend ccs.Frame + */ +ccs.AlphaFrame = ccs.Frame.extend({ + + _alpha: null, + _betweenAlpha: null, + + ctor: function () { + ccs.Frame.prototype.ctor.call(this); + this._alpha = 255; + }, + + onEnter: function (nextFrame) { + if (!this._node) + return; + this._node.setOpacity(this._alpha); + if (this._tween) { + this._betweenAlpha = nextFrame._alpha - this._alpha; + } + }, + + _onApply: function (percent) { + if (!this._node) + return; + var alpha = this._alpha + this._betweenAlpha * percent; + this._node.setOpacity(alpha); + }, + + /** + * Set the alpha + * @param {Number} alpha + */ + setAlpha: function (alpha) { + this._alpha = alpha; + }, + + /** + * Gets the alpha + * @returns {Number} + */ + getAlpha: function () { + return this._alpha; + }, + + clone: function () { + var frame = new ccs.AlphaFrame(); + frame.setAlpha(this._alpha); + frame._cloneProperty(this); + return frame; + } +}); + +/** + * Event frame + * @class + * @extend ccs.Frame + */ +ccs.EventFrame = ccs.Frame.extend({ + + _event: null, + + ctor: function () { + ccs.Frame.prototype.ctor.call(this); + this._event = ""; + this._enterWhenPassed = true; + }, + + /** + * the execution of the callback + * @param {ccs.Frame} nextFrame + */ + onEnter: function (nextFrame) { + this._emitEvent(); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * @return {ccs.EventFrame} + */ + clone: function () { + var frame = new ccs.EventFrame(); + frame.setEvent(this._event); + + frame._cloneProperty(this); + + return frame; + }, + + /** + * Set the event + * @param event + */ + setEvent: function (event) { + this._event = event; + }, + + /** + * Gets the event + * @returns {null} + */ + getEvent: function () { + return this._event; + } + +}); + +/** + * Create the Event frame + * + * @deprecated v3.0, please use new ccs.EventFrame() instead. + * @returns {ccs.EventFrame} + */ +ccs.EventFrame.create = function () { + return new ccs.EventFrame(); +}; + +/** + * zOrder frame + * @class + * @extend ccs.Frame + */ +ccs.ZOrderFrame = ccs.Frame.extend({ + + _zorder: 0, + + /** + * the execution of the callback + * @param {ccs.Frame} nextFrame + */ + onEnter: function (nextFrame) { + if (this._node) + this._node.setLocalZOrder(this._zorder); + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * @return {ccs.ZOrderFrame} + */ + clone: function () { + var frame = new ccs.ZOrderFrame(); + frame.setZOrder(this._zorder); + + frame._cloneProperty(this); + + return frame; + }, + + /** + * Set the zOrder + * @param {Number} zorder + */ + setZOrder: function (zorder) { + this._zorder = zorder; + }, + + /** + * Gets the zOrder + * @returns {Number} + */ + getZOrder: function () { + return this._zorder; + } + +}); + +/** + * Create the ZOrder frame + * + * @deprecated v3.0, please use new ccs.ZOrderFrame() instead. + * @returns {ccs.ZOrderFrame} + */ +ccs.ZOrderFrame.create = function () { + return new ccs.ZOrderFrame(); +}; + +ccs.BlendFuncFrame = ccs.Frame.extend({ + ctor: function () { + this._super(); + this._blendFunc = null; + }, + + onEnter: function (nextFrame, currentFrameIndex) { + if (this._node && this._blendFunc) + this._node.setBlendFunc(this._blendFunc); + }, + + clone: function () { + var frame = new ccs.BlendFuncFrame(); + frame.setBlendFunc(this._blendFunc); + frame._cloneProperty(this); + return frame; + }, + + setBlendFunc: function (blendFunc) { + if (blendFunc && blendFunc.src && blendFunc.dst) + this._blendFunc = blendFunc; + }, + + getBlendFunc: function () { + return this._blendFunc; + } +}); + +ccs.BlendFuncFrame.create = function () { + return new ccs.BlendFuncFrame(); +}; diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/timeline/Timeline.js b/frameworks/cocos2d-html5/extensions/cocostudio/timeline/Timeline.js new file mode 100644 index 0000000..fe2818c --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/timeline/Timeline.js @@ -0,0 +1,329 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * timeline object + * @class + * @extend ccs.Class + */ +ccs.Timeline = ccs.Class.extend({ + + //{ccs.Frame} + _frames: null, + //{ccs.Frame} + _currentKeyFrame: null, + //{Number} + _currentKeyFrameIndex: null, + //{Number} + _fromIndex: null, + //{Number} + _toIndex: null, + //{Number} + _betweenDuration: null, + //{Number} + _actionTag: null, + //{ccs.ActionTimeline} + _ActionTimeline: null, + //{cc.Node} + _node: null, + + ctor: function () { + this._frames = []; + this._currentKeyFrame = null; + this._currentKeyFrameIndex = 0; + this._fromIndex = 0; + this._toIndex = 0; + this._betweenDuration = 0; + this._actionTag = 0; + this._ActionTimeline = null; + this._node = null; + }, + + _gotoFrame: function (frameIndex) { + if (this._frames.length === 0) + return; + + this._binarySearchKeyFrame(frameIndex); + this._apply(frameIndex); + }, + + _stepToFrame: function (frameIndex) { + if (this._frames.length === 0) + return; + + this._updateCurrentKeyFrame(frameIndex); + this._apply(frameIndex); + }, + + /** + * Get the frame list + * @returns {ccs.Frame} + */ + getFrames: function () { + return this._frames; + }, + + /** + * push frame to frame list + * @param {ccs.Frame} frame + */ + addFrame: function (frame) { + this._frames.push(frame); + frame.setTimeline(this) + }, + + /** + * insert the frame to frame list + * @param {ccs.Frame} frame + * @param {Number} index + */ + insertFrame: function (frame, index) { + this._frames.splice(index, 0, frame); + frame.setTimeline(this); + + }, + + /** + * remove frame + * @param {ccs.Frame} frame + */ + removeFrame: function (frame) { + cc.arrayRemoveObject(this._frames, frame); + frame.setTimeline(null); + }, + + /** + * Set the action tag + * @param {Number} tag + */ + setActionTag: function (tag) { + this._actionTag = tag; + }, + + /** + * Gets the action tag + * return {Number} + */ + getActionTag: function () { + return this._actionTag; + }, + + /** + * Set the node + * @param {cc.Node} node + */ + setNode: function (node) { + for (var i = 0; i < this._frames.length; i++) { + var frame = this._frames[i]; + frame.setNode(node); + } + }, + + /** + * Gets the node + * return {cc.Node} + */ + getNode: function () { + return this._node; + }, + + /** + * Set the action timeline + * @param {ccs.ActionTimeline} action + */ + setActionTimeline: function (action) { + this._ActionTimeline = action; + }, + + /** + * get the action timeline + * return {cc.Action} + */ + getActionTimeline: function () { + return this._ActionTimeline; + }, + + /** + * to copy object with deep copy. + * returns a clone of action. + * @return {ccs.Timeline} + */ + clone: function () { + var timeline = new ccs.Timeline(); + timeline._actionTag = this._actionTag; + + for (var i = 0; i < this._frames.length; i++) { + var frame = this._frames[i]; + var newFrame = frame.clone(); + timeline.addFrame(newFrame); + } + + return timeline; + + }, + + _apply: function (frameIndex) { + if (this._currentKeyFrame) { + var currentPercent = this._betweenDuration <= 0 ? 0 : (frameIndex - this._currentKeyFrameIndex) / this._betweenDuration; + this._currentKeyFrame.apply(currentPercent); + } + }, + + _binarySearchKeyFrame: function (frameIndex) { + var from = null; + var to = null; + + var length = this._frames.length; + var needEnterFrame = false; + + do { + if (frameIndex < this._frames[0].getFrameIndex()) { + if (this._currentKeyFrameIndex >= this._frames[0].getFrameIndex()) + needEnterFrame = true; + + this._fromIndex = 0; + this._toIndex = 0; + + from = to = this._frames[0]; + this._currentKeyFrameIndex = 0; + this._betweenDuration = this._frames[0].getFrameIndex(); + break; + } else if (frameIndex >= this._frames[length - 1].getFrameIndex()) { + this._fromIndex = length - 1; + this._toIndex = 0; + + from = to = this._frames[length - 1]; + this._currentKeyFrameIndex = this._frames[length - 1].getFrameIndex(); + this._betweenDuration = 0; + break; + } + + var target = -1; + var low = 0, + high = length - 1, + mid = 0; + while (low <= high) { + mid = Math.ceil(( low + high ) / 2); + if (frameIndex >= this._frames[mid].getFrameIndex() && frameIndex < this._frames[mid + 1].getFrameIndex()) { + target = mid; + break; + } + if (this._frames[mid].getFrameIndex() > frameIndex) + high = mid - 1; + else + low = mid + 1; + } + + this._fromIndex = target; + + if (length > 1) + this._toIndex = (target + 1) | 0; + else + this._toIndex = (target) | 0; + + from = this._frames[this._fromIndex]; + to = this._frames[this._toIndex]; + + from = this._frames[target]; + to = this._frames[target + 1]; + + if (target === 0 && this._currentKeyFrameIndex < from.getFrameIndex()) + needEnterFrame = true; + + this._currentKeyFrameIndex = from.getFrameIndex(); + this._betweenDuration = to.getFrameIndex() - from.getFrameIndex(); + } while (0); + + if (needEnterFrame || this._currentKeyFrame != from) { + this._currentKeyFrame = from; + this._currentKeyFrame.onEnter(to); + } + + }, + + _updateCurrentKeyFrame: function (frameIndex) { + if (frameIndex > 60) + var a = 0; + //! If play to current frame's front or back, then find current frame again + if (frameIndex < this._currentKeyFrameIndex || frameIndex >= this._currentKeyFrameIndex + this._betweenDuration) { + var from = null; + var to = null; + + do + { + var length = this._frames.length; + + if (frameIndex < this._frames[0].getFrameIndex()) { + from = to = this._frames[0]; + this._currentKeyFrameIndex = 0; + this._betweenDuration = this._frames[0].getFrameIndex(); + break; + } + else if (frameIndex >= this._frames[length - 1].getFrameIndex()) { + var lastFrameIndex = this._frames[length - 1].getFrameIndex(); + if (this._currentKeyFrameIndex >= lastFrameIndex) + return; + frameIndex = lastFrameIndex; + } + + do { + this._fromIndex = this._toIndex; + from = this._frames[this._fromIndex]; + this._currentKeyFrameIndex = from.getFrameIndex(); + + this._toIndex = this._fromIndex + 1; + if (this._toIndex >= length) { + this._toIndex = 0; + } + + to = this._frames[this._toIndex]; + + if (frameIndex === from.getFrameIndex()) + break; + if (frameIndex > from.getFrameIndex() && frameIndex < to.getFrameIndex()) + break; + if (from.isEnterWhenPassed()) + from.onEnter(to); + } while (true); + + this._betweenDuration = to.getFrameIndex() - from.getFrameIndex(); + + } while (0); + + this._currentKeyFrame = from; + this._currentKeyFrame.onEnter(to); + } + } + +}); + +/** + * Create the Timeline + * + * @deprecated v3.0, please use new ccs.Timeline() instead. + * @returns {ccs.Timeline} + */ +ccs.Timeline.create = function () { + return new ccs.Timeline(); +}; diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/trigger/ObjectFactory.js b/frameworks/cocos2d-html5/extensions/cocostudio/trigger/ObjectFactory.js new file mode 100644 index 0000000..c2bc197 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/trigger/ObjectFactory.js @@ -0,0 +1,99 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The singleton object that creating object factory, it creates object with class name, and manager the type mapping. + * @class + * @name ccs.objectFactory + */ +ccs.objectFactory = /** @lends ccs.objectFactory# */{ + _typeMap: {}, + + /** + * Creates object with class name. if the the class name without register in type map, it returns null. + * @param {String} className + * @returns {*} + */ + createObject: function (className) { + var o = null; + var t = this._typeMap[className]; + if (t) { + if(cc.isFunction(t._fun)) + o = new t._fun(); + else + o = t._fun; + } + return o; + }, + + /** + * Registers class type in type map. + * @param {ccs.TInfo} t + */ + registerType: function (t) { + this._typeMap[t._className] = t; + }, + + /** + * Creates ccui widget object. + * @param {String} name widget name + * @returns {ccui.Widget|null} + */ + createGUI: function(name){ + var object = null; + if(name === "Panel") + name = "Layout"; + else if(name === "TextArea") + name = "Label"; + else if(name === "TextButton") + name = "Button"; + + var t = this._typeMap[name]; + if(t && t._fun) + object = t._fun; + + return object; + }, + + removeAll: function(){ + this._typeMap = {}; + } +}; + +ccs.TInfo = ccs.Class.extend({ + _className: "", + _fun: null, + + ctor: function (c, f) { + if (f) { + this._className = c; + this._fun = f; + } else { + this._className = c._className; + this._fun = c._fun; + } + ccs.objectFactory.registerType(this); + } +}); diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/trigger/TriggerBase.js b/frameworks/cocos2d-html5/extensions/cocostudio/trigger/TriggerBase.js new file mode 100644 index 0000000..4c773e4 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/trigger/TriggerBase.js @@ -0,0 +1,49 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * Sends event by trigger manager. + * @function + * @param {Number} event + */ +ccs.sendEvent = function (event) { + var triggerObjArr = ccs.triggerManager.get(event); + if (triggerObjArr == null) + return; + for (var i = 0; i < triggerObjArr.length; i++) { + var triObj = triggerObjArr[i]; + if (triObj != null && triObj.detect()) + triObj.done(); + } +}; + +/** + * Registers a trigger class to objectFactory type map. + * @param {String} className + * @param {function} func + */ +ccs.registerTriggerClass = function (className, func) { + new ccs.TInfo(className, func); +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/trigger/TriggerMng.js b/frameworks/cocos2d-html5/extensions/cocostudio/trigger/TriggerMng.js new file mode 100644 index 0000000..e0167a1 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/trigger/TriggerMng.js @@ -0,0 +1,301 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The trigger manager of Cocostudio + * @class + * @name ccs.triggerManager + */ +ccs.triggerManager = /** @lends ccs.triggerManager# */{ + _eventTriggers: {}, + _triggerObjs: {}, + _movementDispatches: [], + + /** + * Parses the triggers. + * @param {Array} triggers + */ + parse: function (triggers) { + for (var i = 0; i < triggers.length; ++i) { + var subDict = triggers[i]; + var triggerObj = new ccs.TriggerObj(); + triggerObj.serialize(subDict); + var events = triggerObj.getEvents(); + for (var j = 0; j < events.length; j++) { + var event = events[j]; + this.add(event, triggerObj); + } + this._triggerObjs[triggerObj.getId()] = triggerObj; + } + }, + + /** + * Returns the event triggers by event id. + * @param {Number} event + * @returns {Array} + */ + get: function (event) { + return this._eventTriggers[event]; + }, + + /** + * Returns the trigger object by id + * @param {Number} id + * @returns {ccs.TriggerObj} + */ + getTriggerObj: function (id) { + return this._triggerObjs[id]; + }, + + /** + * Adds event and trigger object to trigger manager. + * @param event + * @param triggerObj + */ + add: function (event, triggerObj) { + var eventTriggers = this._eventTriggers[event]; + if (!eventTriggers) + eventTriggers = []; + if (eventTriggers.indexOf(triggerObj) === -1) { + eventTriggers.push(triggerObj); + this._eventTriggers[event] = eventTriggers; + } + }, + + /** + * Removes all event triggers from manager. + */ + removeAll: function () { + for (var key in this._eventTriggers) { + var triObjArr = this._eventTriggers[key]; + for (var j = 0; j < triObjArr.length; j++) { + var obj = triObjArr[j]; + obj.removeAll(); + } + } + this._eventTriggers = {}; + }, + + /** + * Removes event object from trigger manager. + * @param {*} event + * @param {*} Obj + * @returns {Boolean} + */ + remove: function (event, Obj) { + if (Obj) + return this._removeObj(event, Obj); + + var bRet = false; + do { + var triObjects = this._eventTriggers[event]; + if (!triObjects) + break; + for (var i = 0; i < triObjects.length; i++) { + var triObject = triObjects[i]; + if (triObject) + triObject.removeAll(); + } + delete this._eventTriggers[event]; + bRet = true; + } while (0); + return bRet; + }, + + _removeObj: function (event, Obj) { + var bRet = false; + do + { + var triObjects = this._eventTriggers[event]; + if (!triObjects) break; + for (var i = 0; i < triObjects.length; i++) { + var triObject = triObjects[i]; + if (triObject && triObject == Obj) { + triObject.removeAll(); + triObjects.splice(i, 1); + break; + } + } + bRet = true; + } while (0); + return bRet; + }, + + /** + * Removes trigger object from manager + * @param {Number} id + * @returns {boolean} + */ + removeTriggerObj: function (id) { + var obj = this.getTriggerObj(id); + if (!obj) + return false; + var events = obj.getEvents(); + for (var i = 0; i < events.length; i++) { + var event = events[i]; + this.remove(event, obj); + } + return true; + }, + + /** + * Returns the event triggers whether is empty. + * @returns {boolean} + */ + isEmpty: function () { + return !this._eventTriggers || this._eventTriggers.length <= 0; + }, + + /** + * Adds an armature movement callback to manager. + * @param {ccs.Armature} armature + * @param {function} callFunc + * @param {Object} target + */ + addArmatureMovementCallBack: function (armature, callFunc, target) { + if (armature == null || target == null || callFunc == null) + return; + var locAmd, hasADD = false; + for (var i = 0; i < this._movementDispatches.length; i++) { + locAmd = this._movementDispatches[i]; + if (locAmd && locAmd[0] === armature) { + locAmd.addAnimationEventCallBack(callFunc, target); + hasADD = true; + } + } + if (!hasADD) { + var newAmd = new ccs.ArmatureMovementDispatcher(); + armature.getAnimation().setMovementEventCallFunc(newAmd.animationEvent, newAmd); + newAmd.addAnimationEventCallBack(callFunc, target); + this._movementDispatches.push([armature, newAmd]); + } + }, + + /** + * Removes armature movement callback from manager. + * @param {ccs.Armature} armature + * @param {Object} target + * @param {function} callFunc + */ + removeArmatureMovementCallBack: function (armature, target, callFunc) { + if (armature == null || target == null || callFunc == null) + return; + var locAmd; + for (var i = 0; i < this._movementDispatches.length; i++) { + locAmd = this._movementDispatches[i]; + if (locAmd && locAmd[0] === armature) + locAmd.removeAnimationEventCallBack(callFunc, target); + } + }, + + /** + * Removes an armature's all movement callbacks. + * @param {ccs.Armature} armature + */ + removeArmatureAllMovementCallBack: function (armature) { + if (armature == null) + return; + var locAmd; + for (var i = 0; i < this._movementDispatches.length; i++) { + locAmd = this._movementDispatches[i]; + if (locAmd && locAmd[0] === armature) { + this._movementDispatches.splice(i, 1); + break; + } + } + }, + + /** + * Removes all armature movement callbacks from ccs.triggerManager. + */ + removeAllArmatureMovementCallBack: function () { + this._movementDispatches.length = 0; + }, + + /** + * Returns the version of ccs.triggerManager + * @returns {string} + */ + version: function () { + return "1.2.0.0"; + } +}; + +/** + * The armature movement dispatcher for trigger manager. + * @class + * @extends ccs.Class + */ +ccs.ArmatureMovementDispatcher = ccs.Class.extend(/** @lends ccs.ArmatureMovementDispatcher# */{ + _mapEventAnimation: null, + + /** + * Constructor of ArmatureMovementDispatcher. + */ + ctor: function () { + this._mapEventAnimation = []; + }, + + /** + * Calls armature movement events. + * @param {ccs.Armature} armature + * @param {Number} movementType + * @param {String} movementID + */ + animationEvent: function (armature, movementType, movementID) { + var locEventAni, locTarget, locFunc; + for (var i = 0; i < this._mapEventAnimation.length; i++) { + locEventAni = this._mapEventAnimation[i]; + locTarget = locEventAni[0]; + locFunc = locEventAni[1]; + if (locFunc) + locFunc.call(locTarget, armature, movementType, movementID); + } + }, + + /** + * Adds animation event callback to event animation list + * @param {function} callFunc + * @param {Object|null} [target] + */ + addAnimationEventCallBack: function (callFunc, target) { + this._mapEventAnimation.push([target, callFunc]); + }, + + /** + * Removes animation event callback from trigger manager. + * @param {function} callFunc + * @param {Object|null} [target] + */ + removeAnimationEventCallBack: function (callFunc, target) { + var locEventAni; + for (var i = 0; i < this._mapEventAnimation.length; i++) { + locEventAni = this._mapEventAnimation[i]; + if (locEventAni[0] === target) { + this._mapEventAnimation.splice(i, 1); + } + } + } +}); \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/cocostudio/trigger/TriggerObj.js b/frameworks/cocos2d-html5/extensions/cocostudio/trigger/TriggerObj.js new file mode 100644 index 0000000..6a8e3c1 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/cocostudio/trigger/TriggerObj.js @@ -0,0 +1,263 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The base class of trigger condition. + * @class + * @extends ccs.Class + */ +ccs.BaseTriggerCondition = ccs.Class.extend(/** @lends ccs.BaseTriggerCondition# */{ + /** + * Construction of ccs.BaseTriggerCondition + */ + ctor:function(){ + }, + + /** + * initializes a BaseTriggerCondition class. + * @returns {boolean} + */ + init: function () { + return true; + }, + + /** + * Detects trigger condition + * @returns {boolean} + */ + detect: function () { + return true; + }, + + /** + * Serialize a BaseTriggerCondition object. + * @param jsonVal + */ + serialize: function (jsonVal) { + }, + + /** + * Removes all condition + */ + removeAll: function () { + } +}); + +/** + * The base class of trigger action + * @class + * @extends ccs.Class + */ +ccs.BaseTriggerAction = ccs.Class.extend(/** @lends ccs.BaseTriggerAction# */{ + /** + * Construction of ccs.BaseTriggerAction + */ + ctor:function(){ + }, + + /** + * Initializes a BaseTriggerAction object. + * @returns {boolean} + */ + init: function () { + return true; + }, + + /** + * Sets the action to done. + */ + done: function () { + }, + + /** + * Serializes a ccs.BaseTriggerAction object. + * @param jsonVal + */ + serialize: function (jsonVal) { + }, + + /** + * Removes all actions. + */ + removeAll: function () { + } +}); + +/** + * The trigger object of Cocostudio. + * @class + * @extends ccs.Class + */ +ccs.TriggerObj = ccs.Class.extend(/** @lends ccs.TriggerObj# */{ + _cons: null, + _acts: null, + _id: 0, + _enable: true, + _vInt: null, + + ctor: function () { + this._id = 0; + this._enable = true; + + ccs.TriggerObj.prototype.init.call(this); + }, + + /** + * Initializes a ccs.TriggerObj + * @returns {boolean} + */ + init: function () { + this._cons = []; + this._acts = []; + this._vInt = []; + return true; + }, + + /** + * Detects trigger's conditions. + * @returns {boolean} + */ + detect: function () { + if (!this._enable || this._cons.length === 0) { + return true; + } + var ret = true; + var obj = null; + for (var i = 0; i < this._cons.length; i++) { + obj = this._cons[i]; + if (obj && obj.detect) + ret = ret && obj.detect(); + } + return ret; + }, + + /** + * Sets trigger's actions to done. + */ + done: function () { + if (!this._enable || this._acts.length === 0) + return; + var obj; + for (var i = 0; i < this._acts.length; i++) { + obj = this._acts[i]; + if (obj && obj.done) + obj.done(); + } + }, + + /** + * Removes all condition and actions from ccs.TriggerObj. + */ + removeAll: function () { + var obj = null; + for (var i = 0; i < this._cons.length; i++) { + obj = this._cons[i]; + if (obj) + obj.removeAll(); + } + this._cons = []; + for (var i = 0; i < this._acts.length; i++) { + obj = this._acts[i]; + if (obj) + obj.removeAll(); + } + this._acts = []; + }, + + /** + * Serializes ccs.TriggerObj + * @param jsonVal + */ + serialize: function (jsonVal) { + this._id = jsonVal["id"] || 0; + var conditions = jsonVal["conditions"] || []; + for (var i = 0; i < conditions.length; i++) { + var subDict = conditions[i]; + var classname = subDict["classname"]; + var con = ccs.objectFactory.createObject(classname); + if (!con) { + cc.log("class named classname(" + classname + ") can not implement!"); + continue; + } + + con.serialize(subDict); + con.init(); + this._cons.push(con); + } + + var actions = jsonVal["actions"] || []; + for (var i = 0; i < actions.length; i++) { + var subDict = actions[i]; + var classname = subDict["classname"]; + var act = ccs.objectFactory.createObject(classname); + if (!act) { + cc.log("class named classname(" + classname + ") can not implement!"); + continue; + } + + act.serialize(subDict); + act.init(); + this._acts.push(act); + } + + var events = jsonVal["events"] || []; + for (var i = 0; i < events.length; i++) { + var subDict = events[i]; + var event = subDict["id"]; + if (event < 0) { + continue; + } + this._vInt.push(event); + } + }, + + /** + * Returns the id of ccs.TriggerObj. + * @returns {number} + */ + getId: function () { + return this._id; + }, + + /** + * Sets enable value. + * @param {Boolean} enable + */ + setEnable: function (enable) { + this._enable = enable; + }, + + /** + * Returns the events of ccs.TriggerObj. + * @returns {null|Array} + */ + getEvents: function () { + return this._vInt; + } +}); + +ccs.TriggerObj.create = function () { + return new ccs.TriggerObj(); +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/editbox/CCEditBox.js b/frameworks/cocos2d-html5/extensions/editbox/CCEditBox.js new file mode 100644 index 0000000..3a53543 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/editbox/CCEditBox.js @@ -0,0 +1,1420 @@ +/**************************************************************************** + Copyright (c) 2013-2016 Chukong Technologies Inc. + Copyright (c) 2012 James Chen + Copyright (c) 2011-2012 cocos2d-x.org + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * @constant + * @type Number + */ +cc.KEYBOARD_RETURNTYPE_DEFAULT = 0; + +/** + * @constant + * @type Number + */ +cc.KEYBOARD_RETURNTYPE_DONE = 1; + +/** + * @constant + * @type Number + */ +cc.KEYBOARD_RETURNTYPE_SEND = 2; + +/** + * @constant + * @type Number + */ +cc.KEYBOARD_RETURNTYPE_SEARCH = 3; + +/** + * @constant + * @type Number + */ +cc.KEYBOARD_RETURNTYPE_GO = 4; + +/** + * The EditBoxInputMode defines the type of text that the user is allowed * to enter. + * @constant + * @type Number + */ +cc.EDITBOX_INPUT_MODE_ANY = 0; + +/** + * The user is allowed to enter an e-mail address. + * @constant + * @type Number + */ +cc.EDITBOX_INPUT_MODE_EMAILADDR = 1; + +/** + * The user is allowed to enter an integer value. + * @constant + * @type Number + */ +cc.EDITBOX_INPUT_MODE_NUMERIC = 2; + +/** + * The user is allowed to enter a phone number. + * @constant + * @type Number + */ +cc.EDITBOX_INPUT_MODE_PHONENUMBER = 3; + +/** + * The user is allowed to enter a URL. + * @constant + * @type Number + */ +cc.EDITBOX_INPUT_MODE_URL = 4; + +/** + * The user is allowed to enter a real number value. + * This extends kEditBoxInputModeNumeric by allowing a decimal point. + * @constant + * @type Number + */ +cc.EDITBOX_INPUT_MODE_DECIMAL = 5; + +/** + * The user is allowed to enter any text, except for line breaks. + * @constant + * @type Number + */ +cc.EDITBOX_INPUT_MODE_SINGLELINE = 6; + +/** + * Indicates that the text entered is confidential data that should be + * obscured whenever possible. This implies EDIT_BOX_INPUT_FLAG_SENSITIVE. + * @constant + * @type Number + */ +cc.EDITBOX_INPUT_FLAG_PASSWORD = 0; + +/** + * Indicates that the text entered is sensitive data that the + * implementation must never store into a dictionary or table for use + * in predictive, auto-completing, or other accelerated input schemes. + * A credit card number is an example of sensitive data. + * @constant + * @type Number + */ +cc.EDITBOX_INPUT_FLAG_SENSITIVE = 1; + +/** + * This flag is a hint to the implementation that during text editing, + * the initial letter of each word should be capitalized. + * @constant + * @type Number + */ +cc.EDITBOX_INPUT_FLAG_INITIAL_CAPS_WORD = 2; + +/** + * This flag is a hint to the implementation that during text editing, + * the initial letter of each sentence should be capitalized. + * @constant + * @type Number + */ +cc.EDITBOX_INPUT_FLAG_INITIAL_CAPS_SENTENCE = 3; + +/** + * Capitalize all characters automatically. + * @constant + * @type Number + */ +cc.EDITBOX_INPUT_FLAG_INITIAL_CAPS_ALL_CHARACTERS = 4; + +/** + * @class + * @extends cc.Class + */ +cc.EditBoxDelegate = cc.Class.extend({ + /** + * This method is called when an edit box gains focus after keyboard is shown. + * @param {cc.EditBox} sender + */ + editBoxEditingDidBegin: function (sender) { + }, + + /** + * This method is called when an edit box loses focus after keyboard is hidden. + * @param {cc.EditBox} sender + */ + editBoxEditingDidEnd: function (sender) { + }, + + /** + * This method is called when the edit box text was changed. + * @param {cc.EditBox} sender + * @param {String} text + */ + editBoxTextChanged: function (sender, text) { + }, + + /** + * This method is called when the return button was pressed. + * @param {cc.EditBox} sender + */ + editBoxReturn: function (sender) { + } +}); + + +/** + *

cc.EditBox is a brief Class for edit box.
+ * You can use this widget to gather small amounts of text from the user.

+ * + * @class + * @extends cc.Node + * + * @property {String} string - Content string of edit box + * @property {String} maxLength - Max length of the content string + * @property {String} font - <@writeonly> Config font of edit box + * @property {String} fontName - <@writeonly> Config font name of edit box + * @property {Number} fontSize - <@writeonly> Config font size of edit box + * @property {cc.Color} fontColor - <@writeonly> Config font color of edit box + * @property {String} placeHolder - Place holder of edit box + * @property {String} placeHolderFont - <@writeonly> Config font of place holder + * @property {String} placeHolderFontName - <@writeonly> Config font name of place holder + * @property {Number} placeHolderFontSize - <@writeonly> Config font size of place holder + * @property {cc.Color} placeHolderFontColor - <@writeonly> Config font color of place holder + * @property {Number} inputFlag - <@writeonly> Input flag of edit box, one of the EditBoxInputFlag constants. e.g.cc.EDITBOX_INPUT_FLAG_PASSWORD + * @property {Object} delegate - <@writeonly> Delegate of edit box + * @property {Number} inputMode - <@writeonly> Input mode of the edit box. Value should be one of the EditBoxInputMode constants. + * @property {Number} returnType - <@writeonly> Return type of edit box, value should be one of the KeyboardReturnType constants. + * + */ +cc.EditBox = cc.Node.extend({ + _backgroundSprite: null, + _delegate: null, + _editBoxInputMode: cc.EDITBOX_INPUT_MODE_ANY, + _editBoxInputFlag: cc.EDITBOX_INPUT_FLAG_SENSITIVE, + _keyboardReturnType: cc.KEYBOARD_RETURNTYPE_DEFAULT, + _maxLength: 50, + _text: '', + _textColor: null, + _placeholderText: '', + _placeholderFontName: '', + _placeholderFontSize: 14, + _placeholderColor: null, + _className: 'EditBox', + _touchListener: null, + _touchEnabled: true, + + /** + * constructor of cc.EditBox + * @param {cc.Size} size + * @param {cc.Scale9Sprite} normal9SpriteBg + * @param {cc.Scale9Sprite} press9SpriteBg + * @param {cc.Scale9Sprite} disabled9SpriteBg + */ + ctor: function (size, normal9SpriteBg) { + cc.Node.prototype.ctor.call(this); + + this._anchorPoint = cc.p(0.5, 0.5); + this._textColor = cc.color.WHITE; + this._placeholderColor = cc.color.GRAY; + + this._renderCmd._createLabels(); + this.createDomElementIfNeeded(); + this.initWithSizeAndBackgroundSprite(size, normal9SpriteBg); + + this._touchListener = cc.EventListener.create({ + event: cc.EventListener.TOUCH_ONE_BY_ONE, + swallowTouches: true, + onTouchBegan: this._onTouchBegan.bind(this), + onTouchEnded: this._onTouchEnded.bind(this) + }); + cc.eventManager.addListener(this._touchListener, this); + + this.setInputFlag(this._editBoxInputFlag); + }, + + setTouchEnabled: function (enable) { + if (this._touchEnabled === enable) { + return; + } + this._touchEnabled = enable; + if (this._touchEnabled) { + cc.eventManager.addListener(this._touchListener, this); + } else { + cc.eventManager.removeListener(this._touchListener); + } + }, + + _createRenderCmd: function () { + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) { + return new cc.EditBox.CanvasRenderCmd(this); + } else { + return new cc.EditBox.WebGLRenderCmd(this); + } + }, + + setContentSize: function (width, height) { + if (width.width !== undefined && width.height !== undefined) { + height = width.height; + width = width.width; + } + cc.Node.prototype.setContentSize.call(this, width, height); + this._updateEditBoxSize(width, height); + }, + + setVisible: function (visible) { + cc.Node.prototype.setVisible.call(this, visible); + this._renderCmd.updateVisibility(); + }, + + createDomElementIfNeeded: function () { + if (!this._renderCmd._edTxt) { + this._renderCmd._createDomTextArea(); + } + }, + + setTabIndex: function (index) { + if (this._renderCmd._edTxt) { + this._renderCmd._edTxt.tabIndex = index; + } + }, + + getTabIndex: function () { + if (this._renderCmd._edTxt) { + return this._renderCmd._edTxt.tabIndex; + } + cc.warn('The dom control is not created!'); + return -1; + }, + + setFocus: function () { + if (this._renderCmd._edTxt) { + this._renderCmd._edTxt.focus(); + } + }, + + isFocused: function () { + if (this._renderCmd._edTxt) { + return document.activeElement === this._renderCmd._edTxt; + } + cc.warn('The dom control is not created!'); + return false; + }, + + stayOnTop: function (flag) { + if (this._alwaysOnTop === flag) return; + + this._alwaysOnTop = flag; + this._renderCmd.stayOnTop(this._alwaysOnTop); + }, + + cleanup: function () { + this._super(); + + this._renderCmd._removeDomFromGameContainer(); + }, + + _isAncestorsVisible: function (node) { + if (null == node) + return true; + + var parent = node.getParent(); + + if (parent && !parent.isVisible()) + return false; + return this._isAncestorsVisible(parent); + }, + + _onTouchBegan: function (touch) { + if (!this.isVisible() || !this._isAncestorsVisible(this)) { + return; + } + var touchPoint = touch.getLocation(); + var bb = cc.rect(0, 0, this._contentSize.width, this._contentSize.height); + var hitted = cc.rectContainsPoint(bb, this.convertToNodeSpace(touchPoint)); + if (hitted) { + return true; + } + else { + this._renderCmd._endEditing(); + return false; + } + }, + + _onTouchEnded: function () { + if (!this.isVisible() || !this._isAncestorsVisible(this)) { + return; + } + this._renderCmd._beginEditing(); + }, + + _updateBackgroundSpriteSize: function (width, height) { + if (this._backgroundSprite) { + this._backgroundSprite.setContentSize(width, height); + } + }, + + _updateEditBoxSize: function (size, height) { + var newWidth = (typeof size.width === 'number') ? size.width : size; + var newHeight = (typeof size.height === 'number') ? size.height : height; + + this._updateBackgroundSpriteSize(newWidth, newHeight); + this._renderCmd.updateSize(newWidth, newHeight); + }, + + setLineHeight: function (lineHeight) { + this._renderCmd.setLineHeight(lineHeight); + }, + + /** + * Sets the font. + * @param {String} fontName The font name. + * @param {Number} fontSize The font size. + */ + setFont: function (fontName, fontSize) { + this._renderCmd.setFont(fontName, fontSize); + }, + + _setFont: function (fontStyle) { + this._renderCmd._setFont(fontStyle); + }, + + getBackgroundSprite: function () { + return this._backgroundSprite; + }, + + /** + * Sets fontName + * @param {String} fontName + */ + setFontName: function (fontName) { + this._renderCmd.setFontName(fontName); + }, + + /** + * Sets fontSize + * @param {Number} fontSize + */ + setFontSize: function (fontSize) { + this._renderCmd.setFontSize(fontSize); + }, + + /** + * Sets the text entered in the edit box. + * @param {string} text The given text. + */ + setString: function (text) { + if (text.length >= this._maxLength) { + text = text.slice(0, this._maxLength); + } + this._text = text; + this._renderCmd.setString(text); + }, + + /** + * Sets the font color of the widget's text. + * @param {cc.Color} color + */ + setFontColor: function (color) { + this._textColor = color; + this._renderCmd.setFontColor(color); + }, + + /** + * Sets the maximum input length of the edit box.
+ * Setting this value enables multiline input mode by default. + * @param {Number} maxLength The maximum length. + */ + setMaxLength: function (maxLength) { + if (!isNaN(maxLength)) { + if (maxLength < 0) { + //we can't set Number.MAX_VALUE to input's maxLength property + //so we use a magic number here, it should works at most use cases. + maxLength = 65535; + } + this._maxLength = maxLength; + this._renderCmd.setMaxLength(maxLength); + } + }, + + /** + * Gets the maximum input length of the edit box. + * @return {Number} Maximum input length. + */ + getMaxLength: function () { + return this._maxLength; + }, + + /** + * Sets a text in the edit box that acts as a placeholder when an edit box is empty. + * @param {string} text The given text. + */ + setPlaceHolder: function (text) { + if (text !== null) { + this._renderCmd.setPlaceHolder(text); + this._placeholderText = text; + } + }, + + /** + * Sets the placeholder's font. + * @param {String} fontName + * @param {Number} fontSize + */ + setPlaceholderFont: function (fontName, fontSize) { + this._placeholderFontName = fontName; + this._placeholderFontSize = fontSize; + this._renderCmd._updateDOMPlaceholderFontStyle(); + }, + + _setPlaceholderFont: function (fontStyle) { + var res = cc.LabelTTF._fontStyleRE.exec(fontStyle); + if (res) { + this._placeholderFontName = res[2]; + this._placeholderFontSize = parseInt(res[1]); + this._renderCmd._updateDOMPlaceholderFontStyle(); + } + }, + + /** + * Sets the placeholder's fontName. + * @param {String} fontName + */ + setPlaceholderFontName: function (fontName) { + this._placeholderFontName = fontName; + this._renderCmd._updateDOMPlaceholderFontStyle(); + }, + + /** + * Sets the placeholder's fontSize. + * @param {Number} fontSize + */ + setPlaceholderFontSize: function (fontSize) { + this._placeholderFontSize = fontSize; + this._renderCmd._updateDOMPlaceholderFontStyle(); + }, + + /** + * Sets the font color of the placeholder text when the edit box is empty. + * @param {cc.Color} color + */ + setPlaceholderFontColor: function (color) { + this._placeholderColor = color; + this._renderCmd.setPlaceholderFontColor(color); + }, + + /** + * Sets the input flags that are to be applied to the edit box. + * @param {Number} inputFlag One of the EditBoxInputFlag constants. + * e.g.cc.EDITBOX_INPUT_FLAG_PASSWORD + */ + setInputFlag: function (inputFlag) { + this._editBoxInputFlag = inputFlag; + this._renderCmd.setInputFlag(inputFlag); + }, + + /** + * Gets the input string of the edit box. + * @return {string} + */ + getString: function () { + return this._text; + }, + + /** + * Init edit box with specified size. + * @param {cc.Size} size + * @param {cc.Color | cc.Scale9Sprite} normal9SpriteBg + */ + initWithSizeAndBackgroundSprite: function (size, normal9SpriteBg) { + if (this._backgroundSprite) { + this._backgroundSprite.removeFromParent(); + } + this._backgroundSprite = normal9SpriteBg; + this.setContentSize(size); + + if (this._backgroundSprite && !this._backgroundSprite.parent) { + this._backgroundSprite.setAnchorPoint(cc.p(0, 0)); + this.addChild(this._backgroundSprite); + + this._updateBackgroundSpriteSize(size.width, size.height); + } + + this.x = 0; + this.y = 0; + return true; + }, + + /** + * Sets the delegate for edit box. + * @param {cc.EditBoxDelegate} delegate + */ + setDelegate: function (delegate) { + this._delegate = delegate; + }, + + /** + * Gets the text in the edit box that acts as a placeholder when an + * edit box is empty. + * @return {String} + */ + getPlaceHolder: function () { + return this._placeholderText; + }, + + /** + * Sets the input mode of the edit box. + * @param {Number} inputMode One of the EditBoxInputMode constants. + */ + setInputMode: function (inputMode) { + if (this._editBoxInputMode === inputMode) return; + + var oldText = this.getString(); + this._editBoxInputMode = inputMode; + + this._renderCmd.setInputMode(inputMode); + this._renderCmd.transform(); + + this.setString(oldText); + this._renderCmd._updateLabelPosition(this.getContentSize()); + }, + + /** + * Sets the return type that are to be applied to the edit box. + * @param {Number} returnType One of the CCKeyboardReturnType constants. + */ + setReturnType: function (returnType) { + this._keyboardReturnType = returnType; + this._renderCmd._updateDomInputType(); + }, + + /** + * @warning HTML5 Only + * @param {cc.Size} size + * @param {cc.color} bgColor + */ + initWithBackgroundColor: function (size, bgColor) { + this._edWidth = size.width; + this.dom.style.width = this._edWidth.toString() + 'px'; + this._edHeight = size.height; + this.dom.style.height = this._edHeight.toString() + 'px'; + this.dom.style.backgroundColor = cc.colorToHex(bgColor); + } +}); + +var _p = cc.EditBox.prototype; + +// Extended properties +/** @expose */ +_p.font; +cc.defineGetterSetter(_p, 'font', null, _p._setFont); +/** @expose */ +_p.fontName; +cc.defineGetterSetter(_p, 'fontName', null, _p.setFontName); +/** @expose */ +_p.fontSize; +cc.defineGetterSetter(_p, 'fontSize', null, _p.setFontSize); +/** @expose */ +_p.fontColor; +cc.defineGetterSetter(_p, 'fontColor', null, _p.setFontColor); +/** @expose */ +_p.string; +cc.defineGetterSetter(_p, 'string', _p.getString, _p.setString); +/** @expose */ +_p.maxLength; +cc.defineGetterSetter(_p, 'maxLength', _p.getMaxLength, _p.setMaxLength); +/** @expose */ +_p.placeHolder; +cc.defineGetterSetter(_p, 'placeholder', _p.getPlaceHolder, _p.setPlaceHolder); +/** @expose */ +_p.placeHolderFont; +cc.defineGetterSetter(_p, 'placeholderFont', null, _p._setPlaceholderFont); +/** @expose */ +_p.placeHolderFontName; +cc.defineGetterSetter(_p, 'placeholderFontName', null, _p.setPlaceholderFontName); +/** @expose */ +_p.placeHolderFontSize; +cc.defineGetterSetter(_p, 'placeholderFontSize', null, _p.setPlaceholderFontSize); +/** @expose */ +_p.placeHolderFontColor; +cc.defineGetterSetter(_p, 'placeholderFontColor', null, _p.setPlaceholderFontColor); +/** @expose */ +_p.inputFlag; +cc.defineGetterSetter(_p, 'inputFlag', null, _p.setInputFlag); +/** @expose */ +_p.delegate; +cc.defineGetterSetter(_p, 'delegate', null, _p.setDelegate); +/** @expose */ +_p.inputMode; +cc.defineGetterSetter(_p, 'inputMode', null, _p.setInputMode); +/** @expose */ +_p.returnType; +cc.defineGetterSetter(_p, 'returnType', null, _p.setReturnType); + +_p = null; + +/** + * create a edit box with size and background-color or + * @deprecated since v3.0, please use new cc.EditBox(size, normal9SpriteBg, press9SpriteBg, disabled9SpriteBg) instead + * @param {cc.Size} size + * @param {cc.Scale9Sprite } normal9SpriteBg + * @param {cc.Scale9Sprite } [press9SpriteBg] + * @param {cc.Scale9Sprite } [disabled9SpriteBg] + * @return {cc.EditBox} + */ +cc.EditBox.create = function (size, normal9SpriteBg, press9SpriteBg, disabled9SpriteBg) { + return new cc.EditBox(size, normal9SpriteBg, press9SpriteBg, disabled9SpriteBg); +}; + + +(function (editbox) { + editbox._polyfill = { + zoomInvalid: false + }; + + if (cc.sys.OS_ANDROID === cc.sys.os + && (cc.sys.browserType === cc.sys.BROWSER_TYPE_SOUGOU + || cc.sys.browserType === cc.sys.BROWSER_TYPE_360)) { + editbox._polyfill.zoomInvalid = true; + } +})(cc.EditBox); + +(function (polyfill) { + // https://segmentfault.com/q/1010000002914610 + var SCROLLY = 40; + var TIMER_NAME = 400; + var LEFT_PADDING = 2; + + function adjustEditBoxPosition (editBox) { + var worldPos = editBox.convertToWorldSpace(cc.p(0,0)); + var windowHeight = cc.visibleRect.height; + var windowWidth = cc.visibleRect.width; + var factor = 0.5; + if(windowWidth > windowHeight) { + factor = 0.7; + } + setTimeout(function() { + if(window.scrollY < SCROLLY && worldPos.y < windowHeight * factor) { + var scrollOffset = windowHeight * factor - worldPos.y - window.scrollY; + if (scrollOffset < 35) scrollOffset = 35; + if (scrollOffset > 320) scrollOffset = 320; + window.scrollTo(scrollOffset, scrollOffset); + } + }, TIMER_NAME); + } + + var capitalize = function(string) { + return string.replace(/(?:^|\s)\S/g, function(a) { return a.toUpperCase(); }); + }; + + function capitalizeFirstLetter(string) { + return string.charAt(0).toUpperCase() + string.slice(1); + } + + var EditBoxImpl = function () { + }; + var proto = EditBoxImpl.prototype = Object.create(Object.prototype); + + proto.updateMatrix = function () { + if (!this._edTxt) return; + + var node = this._node, scaleX = cc.view._scaleX, scaleY = cc.view._scaleY; + var dpr = cc.view._devicePixelRatio; + var t = this._worldTransform; + + scaleX /= dpr; + scaleY /= dpr; + + var container = cc.game.container; + var a = t.a * scaleX, b = t.b, c = t.c, d = t.d * scaleY; + + var offsetX = container && container.style.paddingLeft && parseInt(container.style.paddingLeft); + var offsetY = container && container.style.paddingBottom && parseInt(container.style.paddingBottom); + var tx = t.tx * scaleX + offsetX, ty = t.ty * scaleY + offsetY; + + if (polyfill.zoomInvalid) { + this.updateSize(node._contentSize.width * a, node._contentSize.height * d); + a = 1; + d = 1; + } + + var matrix = "matrix(" + a + "," + -b + "," + -c + "," + d + "," + tx + "," + -ty + ")"; + this._edTxt.style['transform'] = matrix; + this._edTxt.style['-webkit-transform'] = matrix; + this._edTxt.style['transform-origin'] = '0px 100% 0px'; + this._edTxt.style['-webkit-transform-origin'] = '0px 100% 0px'; + }; + + proto.updateVisibility = function () { + if (!this._edTxt) return; + + if (this._node.visible) { + this._edTxt.style.visibility = 'visible'; + } else { + this._edTxt.style.visibility = 'hidden'; + } + }; + + proto.stayOnTop = function (flag) { + if (flag) { + this._removeLabels(); + this._edTxt.style.display = ''; + } else { + this._createLabels(); + this._edTxt.style.display = 'none'; + this._showLabels(); + } + }; + + // Called before editbox focus to register cc.view status + proto._beginEditingOnMobile = function (editBox) { + this.__orientationChanged = function () { + adjustEditBoxPosition(editBox); + }; + + window.addEventListener('orientationchange', this.__orientationChanged); + + if (cc.view.isAutoFullScreenEnabled()) { + this.__fullscreen = true; + cc.view.enableAutoFullScreen(false); + cc.screen.exitFullScreen(); + } else { + this.__fullscreen = false; + } + this.__autoResize = cc.view.__resizeWithBrowserSize; + cc.view.resizeWithBrowserSize(false); + }; + // Called after keyboard disappeared to readapte the game view + proto._endEditingOnMobile = function () { + if (this.__rotateScreen) { + var containerStyle = cc.game.container.style; + containerStyle['-webkit-transform'] = 'rotate(90deg)'; + containerStyle.transform = 'rotate(90deg)'; + + var view = cc.view; + var width = view._originalDesignResolutionSize.width; + var height = view._originalDesignResolutionSize.height; + if (width > 0) { + view.setDesignResolutionSize(width, height, view._resolutionPolicy); + } + this.__rotateScreen = false; + } + + window.removeEventListener('orientationchange', this.__orientationChanged); + + window.scrollTo(0, 0); + if (this.__fullscreen) { + cc.view.enableAutoFullScreen(true); + } + if (this.__autoResize) { + cc.view.resizeWithBrowserSize(true); + } + }; + // Called after editbox focus to readapte the game view + proto._onFocusOnMobile = function (editBox) { + if (cc.view._isRotated) { + var containerStyle = cc.game.container.style; + containerStyle['-webkit-transform'] = 'rotate(0deg)'; + containerStyle.transform = 'rotate(0deg)'; + containerStyle.margin = '0px'; + // cc.view._isRotated = false; + // var policy = cc.view.getResolutionPolicy(); + // policy.apply(cc.view, cc.view.getDesignResolutionSize()); + // cc.view._isRotated = true; + //use window scrollTo to adjust the input area + window.scrollTo(35, 35); + this.__rotateScreen = true; + } else { + this.__rotateScreen = false; + } + adjustEditBoxPosition(editBox); + }; + + + proto._createDomInput = function () { + this._removeDomFromGameContainer(); + var thisPointer = this; + var tmpEdTxt = this._edTxt = document.createElement('input'); + tmpEdTxt.type = 'text'; + tmpEdTxt.style.fontFamily = this._edFontName; + tmpEdTxt.style.fontSize = this._edFontSize + 'px'; + tmpEdTxt.style.color = '#000000'; + tmpEdTxt.style.border = 0; + tmpEdTxt.style.background = 'transparent'; + tmpEdTxt.style.width = '100%'; + tmpEdTxt.style.height = '100%'; + tmpEdTxt.style.active = 0; + tmpEdTxt.style.outline = 'medium'; + tmpEdTxt.style.padding = '0'; + tmpEdTxt.style.textTransform = 'uppercase'; + tmpEdTxt.style.display = 'none'; + + tmpEdTxt.style.position = "absolute"; + tmpEdTxt.style.bottom = "0px"; + tmpEdTxt.style.left = LEFT_PADDING + "px"; + tmpEdTxt.style.className = "cocosEditBox"; + this.setMaxLength(thisPointer._editBox._maxLength); + + tmpEdTxt.addEventListener('input', function () { + var editBox = thisPointer._editBox; + + + if (this.value.length > this.maxLength) { + this.value = this.value.slice(0, this.maxLength); + } + + if (editBox._delegate && editBox._delegate.editBoxTextChanged) { + if (editBox._text !== this.value) { + editBox._text = this.value; + thisPointer._updateDomTextCases(); + editBox._delegate.editBoxTextChanged(editBox, editBox._text); + } + } + }); + tmpEdTxt.addEventListener('keypress', function (e) { + var editBox = thisPointer._editBox; + + if (e.keyCode === cc.KEY.enter) { + e.stopPropagation(); + e.preventDefault(); + if (this.value === '') { + this.style.fontSize = editBox._placeholderFontSize + 'px'; + this.style.color = cc.colorToHex(editBox._placeholderColor); + } + + editBox._text = this.value; + thisPointer._updateDomTextCases(); + + thisPointer._endEditing(); + if (editBox._delegate && editBox._delegate.editBoxReturn) { + editBox._delegate.editBoxReturn(editBox); + } + cc._canvas.focus(); + } + }); + + tmpEdTxt.addEventListener('focus', function () { + var editBox = thisPointer._editBox; + this.style.fontSize = thisPointer._edFontSize + 'px'; + this.style.color = cc.colorToHex(editBox._textColor); + thisPointer._hiddenLabels(); + + if (cc.sys.isMobile) { + thisPointer._onFocusOnMobile(editBox); + } + + if (editBox._delegate && editBox._delegate.editBoxEditingDidBegin) { + editBox._delegate.editBoxEditingDidBegin(editBox); + } + }); + tmpEdTxt.addEventListener('blur', function () { + var editBox = thisPointer._editBox; + editBox._text = this.value; + thisPointer._updateDomTextCases(); + + if (editBox._delegate && editBox._delegate.editBoxEditingDidEnd) { + editBox._delegate.editBoxEditingDidEnd(editBox); + } + + if (this.value === '') { + this.style.fontSize = editBox._placeholderFontSize + 'px'; + this.style.color = cc.colorToHex(editBox._placeholderColor); + } + thisPointer._endEditing(); + }); + + this._addDomToGameContainer(); + return tmpEdTxt; + }; + + proto._createDomTextArea = function () { + this._removeDomFromGameContainer(); + var thisPointer = this; + var tmpEdTxt = this._edTxt = document.createElement('textarea'); + tmpEdTxt.type = 'text'; + tmpEdTxt.style.fontFamily = this._edFontName; + tmpEdTxt.style.fontSize = this._edFontSize + 'px'; + tmpEdTxt.style.color = '#000000'; + tmpEdTxt.style.border = 0; + tmpEdTxt.style.background = 'transparent'; + tmpEdTxt.style.width = '100%'; + tmpEdTxt.style.height = '100%'; + tmpEdTxt.style.active = 0; + tmpEdTxt.style.outline = 'medium'; + tmpEdTxt.style.padding = '0'; + tmpEdTxt.style.resize = 'none'; + tmpEdTxt.style.textTransform = 'uppercase'; + tmpEdTxt.style.overflow_y = 'scroll'; + tmpEdTxt.style.display = 'none'; + tmpEdTxt.style.position = "absolute"; + tmpEdTxt.style.bottom = "0px"; + tmpEdTxt.style.left = LEFT_PADDING + "px"; + tmpEdTxt.style.className = "cocosEditBox"; + this.setMaxLength(thisPointer._editBox._maxLength); + + tmpEdTxt.addEventListener('input', function () { + if (this.value.length > this.maxLength) { + this.value = this.value.slice(0, this.maxLength); + } + + var editBox = thisPointer._editBox; + if (editBox._delegate && editBox._delegate.editBoxTextChanged) { + if (editBox._text.toLowerCase() !== this.value.toLowerCase()) { + editBox._text = this.value; + thisPointer._updateDomTextCases(); + editBox._delegate.editBoxTextChanged(editBox, editBox._text); + } + } + }); + + tmpEdTxt.addEventListener('focus', function () { + var editBox = thisPointer._editBox; + thisPointer._hiddenLabels(); + + this.style.fontSize = thisPointer._edFontSize + 'px'; + this.style.color = cc.colorToHex(editBox._textColor); + + if (cc.sys.isMobile) { + thisPointer._onFocusOnMobile(editBox); + } + + if (editBox._delegate && editBox._delegate.editBoxEditingDidBegin) { + editBox._delegate.editBoxEditingDidBegin(editBox); + } + + }); + tmpEdTxt.addEventListener('keypress', function (e) { + var editBox = thisPointer._editBox; + + if (e.keyCode === cc.KEY.enter) { + e.stopPropagation(); + + if (editBox._delegate && editBox._delegate.editBoxReturn) { + editBox._delegate.editBoxReturn(editBox); + } + } + }); + tmpEdTxt.addEventListener('blur', function () { + var editBox = thisPointer._editBox; + editBox._text = this.value; + thisPointer._updateDomTextCases(); + + if (editBox._delegate && editBox._delegate.editBoxEditingDidEnd) { + editBox._delegate.editBoxEditingDidEnd(editBox); + } + + if (this.value === '') { + this.style.fontSize = editBox._placeholderFontSize + 'px'; + this.style.color = cc.colorToHex(editBox._placeholderColor); + } + + thisPointer._endEditing(); + }); + + this._addDomToGameContainer(); + return tmpEdTxt; + }; + + proto._createLabels = function () { + var editBoxSize = this._editBox.getContentSize(); + if (!this._textLabel) { + this._textLabel = new cc.LabelTTF(); + this._textLabel.setAnchorPoint(cc.p(0, 1)); + this._editBox.addChild(this._textLabel, 100); + } + + if (!this._placeholderLabel) { + this._placeholderLabel = new cc.LabelTTF(); + this._placeholderLabel.setAnchorPoint(cc.p(0, 1)); + this._placeholderLabel.setColor(cc.color.GRAY); + this._editBox.addChild(this._placeholderLabel, 100); + } + + this._updateLabelPosition(editBoxSize); + }; + + proto._removeLabels = function () { + if (!this._textLabel) return; + + this._editBox.removeChild(this._textLabel); + this._textLabel = null; + }; + + proto._updateLabelPosition = function (editBoxSize) { + if (!this._textLabel || !this._placeholderLabel) return; + + var labelContentSize = cc.size(editBoxSize.width - LEFT_PADDING, editBoxSize.height); + this._textLabel.setContentSize(labelContentSize); + this._textLabel.setDimensions(labelContentSize); + this._placeholderLabel.setLineHeight(editBoxSize.height); + var placeholderLabelSize = this._placeholderLabel.getContentSize(); + + if (this._editBox._editBoxInputMode === cc.EDITBOX_INPUT_MODE_ANY) { + this._textLabel.setPosition(LEFT_PADDING, editBoxSize.height); + this._placeholderLabel.setPosition(LEFT_PADDING, editBoxSize.height); + this._placeholderLabel.setVerticalAlignment(cc.VERTICAL_TEXT_ALIGNMENT_TOP); + this._textLabel.setVerticalAlignment(cc.VERTICAL_TEXT_ALIGNMENT_TOP); + // this._textLabel.enableWrapText(true); + } + else { + // this._textLabel.enableWrapText(false); + this._textLabel.setPosition(LEFT_PADDING, editBoxSize.height); + this._placeholderLabel.setPosition(LEFT_PADDING, (editBoxSize.height + placeholderLabelSize.height) / 2); + this._placeholderLabel.setVerticalAlignment(cc.VERTICAL_TEXT_ALIGNMENT_CENTER); + this._textLabel.setVerticalAlignment(cc.VERTICAL_TEXT_ALIGNMENT_CENTER); + } + + }; + + proto.setLineHeight = function (lineHeight) { + if (this._textLabel) { + this._textLabel.setLineHeight(lineHeight); + } + }; + + proto._hiddenLabels = function () { + if (this._textLabel) { + this._textLabel.setVisible(false); + } + + if (this._placeholderLabel) { + this._placeholderLabel.setVisible(false); + } + }; + + proto._updateDomTextCases = function () { + var inputFlag = this._editBox._editBoxInputFlag; + if (inputFlag === cc.EDITBOX_INPUT_FLAG_INITIAL_CAPS_ALL_CHARACTERS) { + this._editBox._text = this._editBox._text.toUpperCase(); + } + else if (inputFlag === cc.EDITBOX_INPUT_FLAG_INITIAL_CAPS_WORD) { + this._editBox._text = capitalize(this._editBox._text); + } + else if (inputFlag === cc.EDITBOX_INPUT_FLAG_INITIAL_CAPS_SENTENCE) { + this._editBox._text = capitalizeFirstLetter(this._editBox._text); + } + }; + + proto._updateLabelStringStyle = function () { + if (this._edTxt.type === 'password') { + var passwordString = ''; + var len = this._editBox._text.length; + for (var i = 0; i < len; ++i) { + passwordString += '\u25CF'; + } + if (this._textLabel) { + this._textLabel.setString(passwordString); + } + } else { + this._updateDomTextCases(); + if (this._textLabel) { + this._textLabel.setString(this._editBox._text); + } + } + }; + + proto._showLabels = function () { + this._hiddenLabels(); + if (this._edTxt.value === '') { + if (this._placeholderLabel) { + this._placeholderLabel.setVisible(true); + this._placeholderLabel.setString(this._editBox._placeholderText); + } + } + else { + if (this._textLabel) { + this._textLabel.setVisible(true); + this._textLabel.setString(this._editBox._text); + } + } + this._updateLabelStringStyle(); + }; + + proto._beginEditing = function () { + if (!this._editBox._alwaysOnTop) { + if (this._edTxt.style.display === 'none') { + this._edTxt.style.display = ''; + this._edTxt.focus(); + } + } + + if (cc.sys.isMobile && !this._editingMode) { + // Pre adaptation and + this._beginEditingOnMobile(this._editBox); + } + this._editingMode = true; + }; + + proto._endEditing = function () { + if (!this._editBox._alwaysOnTop) { + this._edTxt.style.display = 'none'; + } + this._showLabels(); + if (cc.sys.isMobile && this._editingMode) { + var self = this; + // Delay end editing adaptation to ensure virtual keyboard is disapeared + setTimeout(function () { + self._endEditingOnMobile(); + }, TIMER_NAME); + } + this._editingMode = false; + }; + + proto._setFont = function (fontStyle) { + var res = cc.LabelTTF._fontStyleRE.exec(fontStyle); + var textFontName = res[2]; + var textFontSize = parseInt(res[1]); + if (res) { + this.setFont(textFontName, textFontSize); + } + }; + + proto.setFont = function (fontName, fontSize) { + this._edFontName = fontName || this._edFontName; + this._edFontSize = fontSize || this._edFontSize; + this._updateDOMFontStyle(); + }; + + proto.setFontName = function (fontName) { + this._edFontName = fontName || this._edFontName; + this._updateDOMFontStyle(); + }; + + proto.setFontSize = function (fontSize) { + this._edFontSize = fontSize || this._edFontSize; + this._updateDOMFontStyle(); + }; + + proto.setFontColor = function (color) { + if (!this._edTxt) return; + + if (this._edTxt.value !== this._editBox._placeholderText) { + this._edTxt.style.color = cc.colorToHex(color); + } + if (this._textLabel) { + this._textLabel.setColor(color); + } + }; + + proto.setPlaceHolder = function (text) { + this._placeholderLabel.setString(text); + }; + + proto.setMaxLength = function (maxLength) { + if (!this._edTxt) return; + this._edTxt.maxLength = maxLength; + }; + + proto._updateDOMPlaceholderFontStyle = function () { + this._placeholderLabel.setFontName(this._editBox._placeholderFontName); + this._placeholderLabel.setFontSize(this._editBox._placeholderFontSize); + }; + + proto.setPlaceholderFontColor = function (color) { + this._placeholderLabel.setColor(color); + }; + + proto._updateDomInputType = function () { + var inputMode = this._editBox._editBoxInputMode; + if (inputMode === cc.EDITBOX_INPUT_MODE_EMAILADDR) { + this._edTxt.type = 'email'; + } else if (inputMode === cc.EDITBOX_INPUT_MODE_DECIMAL || + inputMode === cc.EDITBOX_INPUT_MODE_NUMERIC) { + this._edTxt.type = 'number'; + } else if (inputMode === cc.EDITBOX_INPUT_MODE_PHONENUMBER) { + this._edTxt.type = 'number'; + this._edTxt.pattern = '[0-9]*'; + } else if (inputMode === cc.EDITBOX_INPUT_MODE_URL) { + this._edTxt.type = 'url'; + } else { + this._edTxt.type = 'text'; + + if (this._editBox._keyboardReturnType === cc.KEYBOARD_RETURNTYPE_SEARCH) { + this._edTxt.type = 'search'; + } + } + + if (this._editBox._editBoxInputFlag === cc.EDITBOX_INPUT_FLAG_PASSWORD) { + this._edTxt.type = 'password'; + } + }; + + proto.setInputFlag = function (inputFlag) { + if (!this._edTxt) return; + + this._updateDomInputType(); + + this._edTxt.style.textTransform = 'none'; + + if (inputFlag === cc.EDITBOX_INPUT_FLAG_INITIAL_CAPS_ALL_CHARACTERS) { + this._edTxt.style.textTransform = 'uppercase'; + } + else if (inputFlag === cc.EDITBOX_INPUT_FLAG_INITIAL_CAPS_WORD) { + this._edTxt.style.textTransform = 'capitalize'; + } + this._updateLabelStringStyle(); + }; + + proto.setInputMode = function (inputMode) { + if (inputMode === cc.EDITBOX_INPUT_MODE_ANY) { + this._createDomTextArea(); + } + else { + this._createDomInput(); + } + + this._updateDomInputType(); + var contentSize = this._node.getContentSize(); + this.updateSize(contentSize.width, contentSize.height); + }; + + proto.setString = function (text) { + if (!this._edTxt) return; + + if (text !== null) { + this._edTxt.value = text; + + if (text === '') { + if (this._placeholderLabel) { + this._placeholderLabel.setString(this._editBox._placeholderText); + this._placeholderLabel.setColor(this._editBox._placeholderColor); + } + if (!this._editingMode) { + if (this._placeholderLabel) { + this._placeholderLabel.setVisible(true); + } + + if (this._textLabel) { + this._textLabel.setVisible(false); + } + } + } + else { + this._edTxt.style.color = cc.colorToHex(this._editBox._textColor); + if (this._textLabel) { + this._textLabel.setColor(this._editBox._textColor); + } + if (!this._editingMode) { + if (this._placeholderLabel) { + this._placeholderLabel.setVisible(false); + } + if (this._textLabel) { + this._textLabel.setVisible(true); + } + } + + this._updateLabelStringStyle(); + } + } + }; + + proto._updateDOMFontStyle = function () { + if (!this._edTxt) return; + + if (this._edTxt.value !== '') { + this._edTxt.style.fontFamily = this._edFontName; + this._edTxt.style.fontSize = this._edFontSize + 'px'; + } + if (this._textLabel) { + this._textLabel.setFontSize(this._edFontSize); + this._textLabel.setFontName(this._edFontName); + } + }; + + + proto.updateSize = function (newWidth, newHeight) { + var editboxDomNode = this._edTxt; + if (!editboxDomNode) return; + + editboxDomNode.style['width'] = newWidth + 'px'; + editboxDomNode.style['height'] = newHeight + 'px'; + + this._updateLabelPosition(cc.size(newWidth, newHeight)); + }; + + proto._addDomToGameContainer = function () { + cc.game.container.appendChild(this._edTxt); + }; + + proto._removeDomFromGameContainer = function () { + var editBox = this._edTxt; + if (editBox) { + var hasChild = false; + if ('contains' in cc.game.container) { + hasChild = cc.game.container.contains(editBox); + } else { + hasChild = cc.game.container.compareDocumentPosition(editBox) % 16; + } + if (hasChild) + cc.game.container.removeChild(editBox); + } + this._edTxt = null; + }; + + proto.initializeRenderCmd = function (node) { + this._editBox = node; + + //it's a dom node, may be assigned with Input or TextArea. + this._edFontSize = 14; + this._edFontName = 'Arial'; + this._textLabel = null; + this._placeholderLabel = null; + this._editingMode = false; + + this.__fullscreen = false; + this.__autoResize = false; + this.__rotateScreen = false; + this.__orientationChanged = null; + }; + + //define the canvas render command + cc.EditBox.CanvasRenderCmd = function (node) { + this._rootCtor(node); + this.initializeRenderCmd(node); + }; + + var canvasRenderCmdProto = cc.EditBox.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + + cc.inject(proto, canvasRenderCmdProto); + canvasRenderCmdProto.constructor = cc.EditBox.CanvasRenderCmd; + + canvasRenderCmdProto.transform = function (parentCmd, recursive) { + this.originTransform(parentCmd, recursive); + this.updateMatrix(); + }; + + + //define the webgl render command + cc.EditBox.WebGLRenderCmd = function (node) { + this._rootCtor(node); + this.initializeRenderCmd(node); + }; + + var webGLRenderCmdProto = cc.EditBox.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + cc.inject(proto, webGLRenderCmdProto); + webGLRenderCmdProto.constructor = cc.EditBox.WebGLRenderCmd; + + webGLRenderCmdProto.transform = function (parentCmd, recursive) { + this.originTransform(parentCmd, recursive); + this.updateMatrix(); + }; + +}(cc.EditBox._polyfill)); diff --git a/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControl.js b/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControl.js new file mode 100644 index 0000000..e4071a3 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControl.js @@ -0,0 +1,378 @@ +/** + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2011-2012 cocos2d-x.org + * Copyright (c) 2013-2014 Chukong Technologies Inc. + * Copyright 2011 Yannick Loriot. + * http://yannickloriot.com + * + * 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. + * + */ + +/** Number of kinds of control event. */ +cc.CONTROL_EVENT_TOTAL_NUMBER = 9; + +/** Kinds of possible events for the control objects. */ +cc.CONTROL_EVENT_TOUCH_DOWN = 1 << 0; // A touch-down event in the control. +cc.CONTROL_EVENT_TOUCH_DRAG_INSIDE = 1 << 1; // An event where a finger is dragged inside the bounds of the control. +cc.CONTROL_EVENT_TOUCH_DRAG_OUTSIDE = 1 << 2; // An event where a finger is dragged just outside the bounds of the control. +cc.CONTROL_EVENT_TOUCH_DRAG_ENTER = 1 << 3; // An event where a finger is dragged into the bounds of the control. +cc.CONTROL_EVENT_TOUCH_DRAG_EXIT = 1 << 4; // An event where a finger is dragged from within a control to outside its bounds. +cc.CONTROL_EVENT_TOUCH_UP_INSIDE = 1 << 5; // A touch-up event in the control where the finger is inside the bounds of the control. +cc.CONTROL_EVENT_TOUCH_UP_OUTSIDE = 1 << 6; // A touch-up event in the control where the finger is outside the bounds of the control. +cc.CONTROL_EVENT_TOUCH_CANCEL = 1 << 7; // A system event canceling the current touches for the control. +cc.CONTROL_EVENT_VALUECHANGED = 1 << 8; // A touch dragging or otherwise manipulating a control; causing it to emit a series of different values. + +/** The possible state for a control. */ +cc.CONTROL_STATE_NORMAL = 1 << 0; // The normal; or default state of a control梩hat is; enabled but neither selected nor highlighted. +cc.CONTROL_STATE_HIGHLIGHTED = 1 << 1; // Highlighted state of a control. A control enters this state when a touch down; drag inside or drag enter is performed. You can retrieve and set this value through the highlighted property. +cc.CONTROL_STATE_DISABLED = 1 << 2; // Disabled state of a control. This state indicates that the control is currently disabled. You can retrieve and set this value through the enabled property. +cc.CONTROL_STATE_SELECTED = 1 << 3; // Selected state of a control. This state indicates that the control is currently selected. You can retrieve and set this value through the selected property. +cc.CONTROL_STATE_INITIAL = 1 << 3; + +/** + * CCControl is inspired by the UIControl API class from the UIKit library of + * CocoaTouch. It provides a base class for control CCSprites such as CCButton + * or CCSlider that convey user intent to the application. + * The goal of CCControl is to define an interface and base implementation for + * preparing action messages and initially dispatching them to their targets when + * certain events occur. + * To use the CCControl you have to subclass it. + * @class + * @extends cc.Layer + * + * @property {Number} state - <@readonly> The current control state: cc.CONTROL_STATE_NORMAL | cc.CONTROL_STATE_HIGHLIGHTED | cc.CONTROL_STATE_DISABLED | cc.CONTROL_STATE_SELECTED | cc.CONTROL_STATE_INITIAL + * @property {Boolean} enabled - Indicate whether the control node is enbaled + * @property {Boolean} selected - Indicate whether the control node is selected + * @property {Boolean} highlighted - Indicate whether the control node is highlighted + */ +cc.Control = cc.Layer.extend(/** @lends cc.Control# */{ + _isOpacityModifyRGB: false, + _hasVisibleParents: false, + _touchListener: null, + _className: "Control", + + isOpacityModifyRGB: function () { + return this._isOpacityModifyRGB; + }, + setOpacityModifyRGB: function (opacityModifyRGB) { + this._isOpacityModifyRGB = opacityModifyRGB; + + var children = this.getChildren(); + for (var i = 0, len = children.length; i < len; i++) { + var selNode = children[i]; + if (selNode) + selNode.setOpacityModifyRGB(opacityModifyRGB); + } + }, + + /** The current control state constant. */ + _state: cc.CONTROL_STATE_NORMAL, + getState: function () { + return this._state; + }, + + _enabled: false, + _selected: false, + _highlighted: false, + + _dispatchTable: null, + + /** + * Tells whether the control is enabled + * @param {Boolean} enabled + */ + setEnabled: function (enabled) { + this._enabled = enabled; + this._state = enabled ? cc.CONTROL_STATE_NORMAL : cc.CONTROL_STATE_DISABLED; + + this.needsLayout(); + }, + isEnabled: function () { + return this._enabled; + }, + + /** + * A Boolean value that determines the control selected state. + * @param {Boolean} selected + */ + setSelected: function (selected) { + this._selected = selected; + this.needsLayout(); + }, + isSelected: function () { + return this._selected; + }, + + /** + * A Boolean value that determines whether the control is highlighted. + * @param {Boolean} highlighted + */ + setHighlighted: function (highlighted) { + this._highlighted = highlighted; + this.needsLayout(); + }, + isHighlighted: function () { + return this._highlighted; + }, + + hasVisibleParents: function () { + var parent = this.getParent(); + for (var c = parent; c != null; c = c.getParent()) { + if (!c.isVisible()) + return false; + } + return true; + }, + + ctor: function () { + cc.Layer.prototype.ctor.call(this); + this._dispatchTable = {}; + this._color = cc.color.WHITE; + }, + + init: function () { + // Initialise instance variables + this._state = cc.CONTROL_STATE_NORMAL; + this._enabled = true; + this._selected = false; + this._highlighted = false; + + var listener = cc.EventListener.create({ + event: cc.EventListener.TOUCH_ONE_BY_ONE, + swallowTouches: true + }); + if (this.onTouchBegan) + listener.onTouchBegan = this.onTouchBegan.bind(this); + if (this.onTouchMoved) + listener.onTouchMoved = this.onTouchMoved.bind(this); + if (this.onTouchEnded) + listener.onTouchEnded = this.onTouchEnded.bind(this); + if (this.onTouchCancelled) + listener.onTouchCancelled = this.onTouchCancelled.bind(this); + this._touchListener = listener; + return true; + }, + + onEnter: function () { + var locListener = this._touchListener; + if (!locListener._isRegistered()) + cc.eventManager.addListener(locListener, this); + cc.Node.prototype.onEnter.call(this); + }, + + /** + * Sends action messages for the given control events. + * which action messages are sent. See "CCControlEvent" for bitmask constants. + * @param {Number} controlEvents A bitmask whose set flags specify the control events for + */ + sendActionsForControlEvents: function (controlEvents) { + // For each control events + for (var i = 0, len = cc.CONTROL_EVENT_TOTAL_NUMBER; i < len; i++) { + // If the given controlEvents bitmask contains the curent event + if ((controlEvents & (1 << i))) { + // Call invocations + // + var invocationList = this._dispatchListforControlEvent(1 << i); + for (var j = 0, inLen = invocationList.length; j < inLen; j++) { + invocationList[j].invoke(this); + } + } + } + }, + + /** + *

+ * Adds a target and action for a particular event (or events) to an internal
+ * dispatch table.
+ * The action message may optionally include the sender and the event as
+ * parameters, in that order.
+ * When you call this method, target is not retained. + *

+ * @param {Object} target The target object that is, the object to which the action message is sent. It cannot be nil. The target is not retained. + * @param {function} action A selector identifying an action message. It cannot be NULL. + * @param {Number} controlEvents A bitmask specifying the control events for which the action message is sent. See "CCControlEvent" for bitmask constants. + */ + addTargetWithActionForControlEvents: function (target, action, controlEvents) { + // For each control events + for (var i = 0, len = cc.CONTROL_EVENT_TOTAL_NUMBER; i < len; i++) { + // If the given controlEvents bit mask contains the current event + if ((controlEvents & (1 << i))) + this._addTargetWithActionForControlEvent(target, action, 1 << i); + } + }, + + /** + * Removes a target and action for a particular event (or events) from an internal dispatch table. + * + * @param {Object} target The target object that is, the object to which the action message is sent. Pass nil to remove all targets paired with action and the specified control events. + * @param {function} action A selector identifying an action message. Pass NULL to remove all action messages paired with target. + * @param {Number} controlEvents A bitmask specifying the control events associated with target and action. See "CCControlEvent" for bitmask constants. + */ + removeTargetWithActionForControlEvents: function (target, action, controlEvents) { + // For each control events + for (var i = 0, len = cc.CONTROL_EVENT_TOTAL_NUMBER; i < len; i++) { + // If the given controlEvents bitmask contains the current event + if ((controlEvents & (1 << i))) + this._removeTargetWithActionForControlEvent(target, action, 1 << i); + } + }, + + /** + * Returns a point corresponding to the touh location converted into the + * control space coordinates. + * @param {cc.Touch} touch A CCTouch object that represents a touch. + */ + getTouchLocation: function (touch) { + var touchLocation = touch.getLocation(); // Get the touch position + return this.convertToNodeSpace(touchLocation); // Convert to the node space of this class + }, + + /** + * Returns a boolean value that indicates whether a touch is inside the bounds of the receiver. The given touch must be relative to the world. + * + * @param {cc.Touch} touch A cc.Touch object that represents a touch. + * @return {Boolean} YES whether a touch is inside the receiver's rect. + */ + isTouchInside: function (touch) { + var touchLocation = touch.getLocation(); // Get the touch position + touchLocation = this.getParent().convertToNodeSpace(touchLocation); + return cc.rectContainsPoint(this.getBoundingBox(), touchLocation); + }, + + /** + *

+ * Returns an cc.Invocation object able to construct messages using a given
+ * target-action pair. (The invocation may optionally include the sender and
+ * the event as parameters, in that order) + *

+ * @param {Object} target The target object. + * @param {function} action A selector identifying an action message. + * @param {Number} controlEvent A control events for which the action message is sent. See "CCControlEvent" for constants. + * + * @return {cc.Invocation} an CCInvocation object able to construct messages using a given target-action pair. + */ + _invocationWithTargetAndActionForControlEvent: function (target, action, controlEvent) { + return null; + }, + + /** + * Returns the cc.Invocation list for the given control event. If the list does not exist, it'll create an empty array before returning it. + * + * @param {Number} controlEvent A control events for which the action message is sent. See "CCControlEvent" for constants. + * @return {cc.Invocation} the cc.Invocation list for the given control event. + */ + _dispatchListforControlEvent: function (controlEvent) { + controlEvent = controlEvent.toString(); + // If the invocation list does not exist for the dispatch table, we create it + if (!this._dispatchTable[controlEvent]) + this._dispatchTable[controlEvent] = []; + return this._dispatchTable[controlEvent]; + }, + + /** + * Adds a target and action for a particular event to an internal dispatch + * table. + * The action message may optionally include the sender and the event as + * parameters, in that order. + * When you call this method, target is not retained. + * + * @param target The target object that is, the object to which the action + * message is sent. It cannot be nil. The target is not retained. + * @param action A selector identifying an action message. It cannot be NULL. + * @param controlEvent A control event for which the action message is sent. + * See "CCControlEvent" for constants. + */ + _addTargetWithActionForControlEvent: function (target, action, controlEvent) { + // Create the invocation object + var invocation = new cc.Invocation(target, action, controlEvent); + + // Add the invocation into the dispatch list for the given control event + var eventInvocationList = this._dispatchListforControlEvent(controlEvent); + eventInvocationList.push(invocation); + }, + + /** + * Removes a target and action for a particular event from an internal dispatch table. + * + * @param {Object} target The target object that is, the object to which the action message is sent. Pass nil to remove all targets paired with action and the specified control events. + * @param {function} action A selector identifying an action message. Pass NULL to remove all action messages paired with target. + * @param {Number} controlEvent A control event for which the action message is sent. See "CCControlEvent" for constants. + */ + _removeTargetWithActionForControlEvent: function (target, action, controlEvent) { + // Retrieve all invocations for the given control event + // + var eventInvocationList = this._dispatchListforControlEvent(controlEvent); + + //remove all invocations if the target and action are null + //TODO: should the invocations be deleted, or just removed from the array? Won't that cause issues if you add a single invocation for multiple events? + var bDeleteObjects = true; + if (!target && !action) { + //remove objects + eventInvocationList.length = 0; + } else { + //normally we would use a predicate, but this won't work here. Have to do it manually + for (var i = 0; i < eventInvocationList.length;) { + var invocation = eventInvocationList[i]; + var shouldBeRemoved = true; + if (target) + shouldBeRemoved = (target === invocation.getTarget()); + if (action) + shouldBeRemoved = (shouldBeRemoved && (action === invocation.getAction())); + // Remove the corresponding invocation object + if (shouldBeRemoved) + cc.arrayRemoveObject(eventInvocationList, invocation); + else + i++; + } + } + }, + + /** + * Updates the control layout using its current internal state. + */ + needsLayout: function () { + } +}); + +var _p = cc.Control.prototype; + +// Extended properties +/** @expose */ +_p.state; +cc.defineGetterSetter(_p, "state", _p.getState); +/** @expose */ +_p.enabled; +cc.defineGetterSetter(_p, "enabled", _p.isEnabled, _p.setEnabled); +/** @expose */ +_p.selected; +cc.defineGetterSetter(_p, "selected", _p.isSelected, _p.setSelected); +/** @expose */ +_p.highlighted; +cc.defineGetterSetter(_p, "highlighted", _p.isHighlighted, _p.setHighlighted); + +_p = null; + +cc.Control.create = function () { + var retControl = new cc.Control(); + if (retControl && retControl.init()) + return retControl; + return null; +}; + diff --git a/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlButton.js b/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlButton.js new file mode 100644 index 0000000..9e70ef2 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlButton.js @@ -0,0 +1,688 @@ +/** + * CCControlButton.m + * + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2011-2012 cocos2d-x.org + * Copyright (c) 2013-2014 Chukong Technologies Inc. + * Copyright 2011 Yannick Loriot. + * http://yannickloriot.com + * + * 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. + */ + +/** + * @ignore + */ +cc.CONTROL_ZOOM_ACTION_TAG = 0xCCCB0001; + +/** + * CCControlButton: Button control for Cocos2D. + * @class + * @extends cc.Control + * + * @property {Boolean} adjustBackgroundImage - Indicate whether the background image will be adjusted + * @property {Boolean} zoomOnTouchDown - Indicate whether the button will be zoomed while touch down + * @property {cc.Size} preferredSize - The preferred size of the control button + * @property {Boolean} labelAnchor - The anchor point for the label of the control button + */ +cc.ControlButton = cc.Control.extend(/** @lends cc.ControlButton# */{ + _doesAdjustBackgroundImage: false, + zoomOnTouchDown: false, + _preferredSize: null, + _labelAnchorPoint: null, + _currentTitle: null, + _currentTitleColor: null, + _titleLabel: null, + _backgroundSprite: null, + _opacity: 0, + _isPushed: false, + _titleDispatchTable: null, + _titleColorDispatchTable: null, + _titleLabelDispatchTable: null, + _backgroundSpriteDispatchTable: null, + _parentInited: false, + + _marginV: 0, + _marginH: 0, + _className: "ControlButton", + + ctor: function (label, backgroundSprite, fontSize) { + cc.Control.prototype.ctor.call(this); + this._preferredSize = cc.size(0, 0); + this._labelAnchorPoint = cc.p(0, 0); + this._currentTitle = ""; + this._currentTitleColor = cc.color.WHITE; + this._titleDispatchTable = {}; + this._titleColorDispatchTable = {}; + this._titleLabelDispatchTable = {}; + this._backgroundSpriteDispatchTable = {}; + + if(fontSize != undefined) + this.initWithTitleAndFontNameAndFontSize(label, backgroundSprite, fontSize); + else if(backgroundSprite != undefined) + this.initWithLabelAndBackgroundSprite(label, backgroundSprite); + else if(label != undefined) + this.initWithBackgroundSprite(label); + else + this.init(); + }, + + init: function () { + return this.initWithLabelAndBackgroundSprite(new cc.LabelTTF("", "Arial", 12), new cc.Scale9Sprite()); + }, + + needsLayout: function () { + if (!this._parentInited) { + return; + } + // Hide the background and the label + if (this._titleLabel) + this._titleLabel.setVisible(false); + if (this._backgroundSprite) + this._backgroundSprite.setVisible(false); + + // Update anchor of all labels + this.setLabelAnchorPoint(this._labelAnchorPoint); + + // Update the label to match with the current state + //CC_SAFE_RELEASE(this._currentTitle) + var locState = this._state; + + this._currentTitle = this.getTitleForState(locState); + this._currentTitleColor = this.getTitleColorForState(locState); + this._titleLabel = this.getTitleLabelForState(locState); + + var label = this._titleLabel; + if (label && label.setString) + label.setString(this._currentTitle); + if (label) + label.setColor(this._currentTitleColor); + + var locContentSize = this.getContentSize(); + if (label) + label.setPosition(locContentSize.width / 2, locContentSize.height / 2); + + // Update the background sprite + this._backgroundSprite = this.getBackgroundSpriteForState(locState); + var locBackgroundSprite = this._backgroundSprite; + if (locBackgroundSprite) + locBackgroundSprite.setPosition(locContentSize.width / 2, locContentSize.height / 2); + + // Get the title label size + var titleLabelSize = cc.size(0, 0); + if (label) { + var boundingBox = label.getBoundingBox(); + titleLabelSize.width = boundingBox.width; + titleLabelSize.height = boundingBox.height; + } + // Adjust the background image if necessary + if (this._doesAdjustBackgroundImage) { + // Add the margins + if (locBackgroundSprite) + locBackgroundSprite.setContentSize(titleLabelSize.width + this._marginH * 2, titleLabelSize.height + this._marginV * 2); + } else { + //TODO: should this also have margins if one of the preferred sizes is relaxed? + if (locBackgroundSprite) { + var preferredSize = locBackgroundSprite.getPreferredSize(); + preferredSize = cc.size(preferredSize.width, preferredSize.height); + if (preferredSize.width <= 0) + preferredSize.width = titleLabelSize.width; + if (preferredSize.height <= 0) + preferredSize.height = titleLabelSize.height; + + locBackgroundSprite.setContentSize(preferredSize); + } + } + + // Set the content size + var rectTitle = label ? label.getBoundingBox() : cc.rect(0, 0, 0, 0); + var rectBackground = locBackgroundSprite ? locBackgroundSprite.getBoundingBox() : cc.rect(0, 0, 0, 0); + var maxRect = cc.rectUnion(rectTitle, rectBackground); + this.setContentSize(maxRect.width, maxRect.height); + locContentSize = this.getContentSize(); + if (label) { + label.setPosition(locContentSize.width / 2, locContentSize.height / 2); + label.setVisible(true); + } + if (locBackgroundSprite) { + locBackgroundSprite.setPosition(locContentSize.width / 2, locContentSize.height / 2); + locBackgroundSprite.setVisible(true); + } + }, + + initWithLabelAndBackgroundSprite: function (label, backgroundSprite) { + if (!label) + throw new Error("cc.ControlButton.initWithLabelAndBackgroundSprite(): label should be non-null"); + if (!backgroundSprite) + throw new Error("cc.ControlButton.initWithLabelAndBackgroundSprite(): backgroundSprite should be non-null"); + if (cc.Control.prototype.init.call(this, true)) { + this._parentInited = true; + + // Initialize the button state tables + this._titleDispatchTable = {}; + this._titleColorDispatchTable = {}; + this._titleLabelDispatchTable = {}; + this._backgroundSpriteDispatchTable = {}; + + this._isPushed = false; + this.zoomOnTouchDown = true; + + this._currentTitle = null; + + // Adjust the background image by default + this.setAdjustBackgroundImage(true); + this.setPreferredSize(cc.size(0, 0)); + + // Zooming button by default + this.zoomOnTouchDown = true; + + // Set the default anchor point + this.ignoreAnchorPointForPosition(false); + this.setAnchorPoint(0.5, 0.5); + + // Set the nodes + this._titleLabel = label; + this._backgroundSprite = backgroundSprite; + + // Set the default color and opacity + this.setOpacity(255); + this.setOpacityModifyRGB(true); + + // Initialize the dispatch table + var tempString = label.getString(); + //tempString.autorelease(); + this.setTitleForState(tempString, cc.CONTROL_STATE_NORMAL); + this.setTitleColorForState(label.getColor(), cc.CONTROL_STATE_NORMAL); + this.setTitleLabelForState(label, cc.CONTROL_STATE_NORMAL); + this.setBackgroundSpriteForState(backgroundSprite, cc.CONTROL_STATE_NORMAL); + + this._state = cc.CONTROL_STATE_NORMAL; + + //default margins + this._marginH = 24; + this._marginV = 12; + + this._labelAnchorPoint = cc.p(0.5, 0.5); + + this.setPreferredSize(cc.size(0, 0)); + + // Layout update + this.needsLayout(); + return true; + }//couldn't init the CCControl + else + return false; + }, + + initWithTitleAndFontNameAndFontSize: function (title, fontName, fontSize) { + var label = new cc.LabelTTF(title, fontName, fontSize); + return this.initWithLabelAndBackgroundSprite(label, new cc.Scale9Sprite()); + }, + + initWithBackgroundSprite: function (sprite) { + var label = new cc.LabelTTF("", "Arial", 30);// + return this.initWithLabelAndBackgroundSprite(label, sprite); + }, + + /** + * Adjust the background image. YES by default. If the property is set to NO, the background will use the preferred size of the background image. + * @return {Boolean} + */ + doesAdjustBackgroundImage: function () { + return this._doesAdjustBackgroundImage; + }, + + setAdjustBackgroundImage: function (adjustBackgroundImage) { + this._doesAdjustBackgroundImage = adjustBackgroundImage; + this.needsLayout(); + }, + + /** Adjust the button zooming on touchdown. Default value is YES. */ + getZoomOnTouchDown: function () { + return this.zoomOnTouchDown; + }, + + setZoomOnTouchDown: function (zoomOnTouchDown) { + return this.zoomOnTouchDown = zoomOnTouchDown; + }, + + /** The preferred size of the button, if label is larger it will be expanded. */ + getPreferredSize: function () { + return this._preferredSize; + }, + + setPreferredSize: function (size) { + if (size.width === 0 && size.height === 0) { + this._doesAdjustBackgroundImage = true; + } else { + this._doesAdjustBackgroundImage = false; + var locTable = this._backgroundSpriteDispatchTable; + for (var itemKey in locTable) + locTable[itemKey].setPreferredSize(size); + } + this._preferredSize = size; + this.needsLayout(); + }, + + getLabelAnchorPoint: function () { + return this._labelAnchorPoint; + }, + setLabelAnchorPoint: function (labelAnchorPoint) { + this._labelAnchorPoint = labelAnchorPoint; + if (this._titleLabel) + this._titleLabel.setAnchorPoint(labelAnchorPoint); + }, + + /** + * The current title that is displayed on the button. + * @return {string} + */ + _getCurrentTitle: function () { + return this._currentTitle; + }, + + /** The current color used to display the title. */ + _getCurrentTitleColor: function () { + return this._currentTitleColor; + }, + + /* Override setter to affect a background sprite too */ + getOpacity: function () { + return this._opacity; + }, + + setOpacity: function (opacity) { + // XXX fixed me if not correct + cc.Control.prototype.setOpacity.call(this, opacity); + /*this._opacity = opacity; + var controlChildren = this.getChildren(); + for (var i = 0; i < controlChildren.length; i++) { + var selChild = controlChildren[i]; + if (selChild) + selChild.setOpacity(opacity); + }*/ + var locTable = this._backgroundSpriteDispatchTable; + for (var itemKey in locTable) + locTable[itemKey].setOpacity(opacity); + }, + + setColor: function (color) { + cc.Control.prototype.setColor.call(this, color); + var locTable = this._backgroundSpriteDispatchTable; + for (var key in locTable) + locTable[key].setColor(color); + }, + + getColor: function () { + var locRealColor = this._realColor; + return cc.color(locRealColor.r, locRealColor.g, locRealColor.b, locRealColor.a); + }, + + + /** Flag to know if the button is currently pushed. */ + isPushed: function () { + return this._isPushed; + }, + + /* Define the button margin for Top/Bottom edge */ + _getVerticalMargin: function () { + return this._marginV; + }, + /* Define the button margin for Left/Right edge */ + _getHorizontalOrigin: function () { + return this._marginH; + }, + + /** + * set the margins at once (so we only have to do one call of needsLayout) + * @param {Number} marginH + * @param {Number} marginV + */ + setMargins: function (marginH, marginV) { + this._marginV = marginV; + this._marginH = marginH; + this.needsLayout(); + }, + + setEnabled: function (enabled) { + cc.Control.prototype.setEnabled.call(this, enabled); + this.needsLayout(); + }, + setSelected: function (enabled) { + cc.Control.prototype.setSelected.call(this, enabled); + this.needsLayout(); + }, + + setHighlighted: function (enabled) { + this._state = enabled ? cc.CONTROL_STATE_HIGHLIGHTED : cc.CONTROL_STATE_NORMAL; + + cc.Control.prototype.setHighlighted.call(this, enabled); + var action = this.getActionByTag(cc.CONTROL_ZOOM_ACTION_TAG); + if (action) + this.stopAction(action); + + //this.needsLayout();// needn't + if (this.zoomOnTouchDown) { + var scaleValue = (this.isHighlighted() && this.isEnabled() && !this.isSelected()) ? 1.1 : 1.0; + var zoomAction = cc.scaleTo(0.05, scaleValue); + zoomAction.setTag(cc.CONTROL_ZOOM_ACTION_TAG); + this.runAction(zoomAction); + } + }, + + onTouchBegan: function (touch, event) { + if (!this.isTouchInside(touch) || !this.isEnabled() || !this.isVisible() || !this.hasVisibleParents()) + return false; + + this._isPushed = true; + this.setHighlighted(true); + this.sendActionsForControlEvents(cc.CONTROL_EVENT_TOUCH_DOWN); + return true; + }, + + onTouchMoved: function (touch, event) { + if (!this._enabled || !this._isPushed || this._selected) { + if (this._highlighted) + this.setHighlighted(false); + return; + } + + var isTouchMoveInside = this.isTouchInside(touch); + if (isTouchMoveInside && !this._highlighted) { + this.setHighlighted(true); + this.sendActionsForControlEvents(cc.CONTROL_EVENT_TOUCH_DRAG_ENTER); + } else if (isTouchMoveInside && this._highlighted) { + this.sendActionsForControlEvents(cc.CONTROL_EVENT_TOUCH_DRAG_INSIDE); + } else if (!isTouchMoveInside && this._highlighted) { + this.setHighlighted(false); + this.sendActionsForControlEvents(cc.CONTROL_EVENT_TOUCH_DRAG_EXIT); + } else if (!isTouchMoveInside && !this._highlighted) { + this.sendActionsForControlEvents(cc.CONTROL_EVENT_TOUCH_DRAG_OUTSIDE); + } + }, + onTouchEnded: function (touch, event) { + this._isPushed = false; + this.setHighlighted(false); + + if (this.isTouchInside(touch)) { + this.sendActionsForControlEvents(cc.CONTROL_EVENT_TOUCH_UP_INSIDE); + } else { + this.sendActionsForControlEvents(cc.CONTROL_EVENT_TOUCH_UP_OUTSIDE); + } + }, + + onTouchCancelled: function (touch, event) { + this._isPushed = false; + this.setHighlighted(false); + this.sendActionsForControlEvents(cc.CONTROL_EVENT_TOUCH_CANCEL); + }, + + /** + * Returns the title used for a state. + * + * @param {Number} state The state that uses the title. Possible values are described in "CCControlState". + * @return {string} The title for the specified state. + */ + getTitleForState: function (state) { + var locTable = this._titleDispatchTable; + if (locTable) { + if (locTable[state]) + return locTable[state]; + return locTable[cc.CONTROL_STATE_NORMAL]; + } + return ""; + }, + + /** + *

+ * Sets the title string to use for the specified state.
+ * If a property is not specified for a state, the default is to use the CCButtonStateNormal value. + *

+ * @param {string} title The title string to use for the specified state. + * @param {Number} state The state that uses the specified title. The values are described in "CCControlState". + */ + setTitleForState: function (title, state) { + this._titleDispatchTable[state] = title || ""; + + // If the current state if equal to the given state we update the layout + if (this.getState() === state) + this.needsLayout(); + }, + + /** + * Returns the title color used for a state. + * + * @param {Number} state The state that uses the specified color. The values are described in "CCControlState". + * @return {cc.Color} The color of the title for the specified state. + */ + getTitleColorForState: function (state) { + var colorObject = this._titleColorDispatchTable[state]; + if (colorObject) + return colorObject; + colorObject = this._titleColorDispatchTable[cc.CONTROL_STATE_NORMAL]; + if (colorObject) + return colorObject; + return cc.color.WHITE; + }, + + /** + * Sets the color of the title to use for the specified state. + * + * @param {cc.Color} color The color of the title to use for the specified state. + * @param {Number} state The state that uses the specified color. The values are described in "CCControlState". + */ + setTitleColorForState: function (color, state) { + //ccColor3B* colorValue=&color; + this._titleColorDispatchTable[state] = color; + + // If the current state if equal to the given state we update the layout + if (this.getState() === state) + this.needsLayout(); + }, + + /** + * Returns the title label used for a state. + * + * @param state The state that uses the title label. Possible values are described in "CCControlState". + * @return {cc.Node} the title label used for a state. + */ + getTitleLabelForState: function (state) { + var locTable = this._titleLabelDispatchTable; + if (locTable[state]) + return locTable[state]; + + return locTable[cc.CONTROL_STATE_NORMAL]; + }, + + /** + *

Sets the title label to use for the specified state.
+ * If a property is not specified for a state, the default is to use the CCButtonStateNormal value.

+ * + * @param {cc.Node} titleLabel The title label to use for the specified state. + * @param {Number} state The state that uses the specified title. The values are described in "CCControlState". + */ + setTitleLabelForState: function (titleLabel, state) { + var locTable = this._titleLabelDispatchTable; + if (locTable[state]) { + var previousLabel = locTable[state]; + if (previousLabel) + this.removeChild(previousLabel, true); + } + + locTable[state] = titleLabel; + titleLabel.setVisible(false); + titleLabel.setAnchorPoint(0.5, 0.5); + this.addChild(titleLabel, 1); + + // If the current state if equal to the given state we update the layout + if (this.getState() === state) + this.needsLayout(); + }, + + /** + * Sets the title TTF filename to use for the specified state. + * @param {string} fntFile + * @param {Number} state + */ + setTitleTTFForState: function (fntFile, state) { + var title = this.getTitleForState(state); + if (!title) + title = ""; + this.setTitleLabelForState(new cc.LabelTTF(title, fntFile, 12), state); + }, + + /** + * return the title TTF filename to use for the specified state. + * @param {Number} state + * @returns {string} + */ + getTitleTTFForState: function (state) { + var labelTTF = this.getTitleLabelForState(state); + if ((labelTTF != null) && (labelTTF instanceof cc.LabelTTF)) { + return labelTTF.getFontName(); + } else { + return ""; + } + }, + + /** + * @param {Number} size + * @param {Number} state + */ + setTitleTTFSizeForState: function (size, state) { + var labelTTF = this.getTitleLabelForState(state); + if ((labelTTF != null) && (labelTTF instanceof cc.LabelTTF)) { + labelTTF.setFontSize(size); + } + }, + + /** + * return the font size of LabelTTF to use for the specified state + * @param {Number} state + * @returns {Number} + */ + getTitleTTFSizeForState: function (state) { + var labelTTF = this.getTitleLabelForState(state); + if ((labelTTF != null) && (labelTTF instanceof cc.LabelTTF)) { + return labelTTF.getFontSize(); + } + return 0; + }, + + /** + * Sets the font of the label, changes the label to a CCLabelBMFont if necessary. + * @param {string} fntFile The name of the font to change to + * @param {Number} state The state that uses the specified fntFile. The values are described in "CCControlState". + */ + setTitleBMFontForState: function (fntFile, state) { + var title = this.getTitleForState(state); + if (!title) + title = ""; + this.setTitleLabelForState(new cc.LabelBMFont(title, fntFile), state); + }, + + getTitleBMFontForState: function (state) { + var labelBMFont = this.getTitleLabelForState(state); + if ((labelBMFont != null) && (labelBMFont instanceof cc.LabelBMFont)) { + return labelBMFont.getFntFile(); + } + return ""; + }, + + /** + * Returns the background sprite used for a state. + * + * @param {Number} state The state that uses the background sprite. Possible values are described in "CCControlState". + */ + getBackgroundSpriteForState: function (state) { + var locTable = this._backgroundSpriteDispatchTable; + if (locTable[state]) { + return locTable[state]; + } + return locTable[cc.CONTROL_STATE_NORMAL]; + }, + + /** + * Sets the background sprite to use for the specified button state. + * + * @param {Scale9Sprite} sprite The background sprite to use for the specified state. + * @param {Number} state The state that uses the specified image. The values are described in "CCControlState". + */ + setBackgroundSpriteForState: function (sprite, state) { + var locTable = this._backgroundSpriteDispatchTable; + if (locTable[state]) { + var previousSprite = locTable[state]; + if (previousSprite) + this.removeChild(previousSprite, true); + } + + locTable[state] = sprite; + sprite.setVisible(false); + sprite.setAnchorPoint(0.5, 0.5); + this.addChild(sprite); + + var locPreferredSize = this._preferredSize; + if (locPreferredSize.width !== 0 || locPreferredSize.height !== 0) { + sprite.setPreferredSize(locPreferredSize); + } + + // If the current state if equal to the given state we update the layout + if (this._state === state) + this.needsLayout(); + }, + + /** + * Sets the background spriteFrame to use for the specified button state. + * + * @param {SpriteFrame} spriteFrame The background spriteFrame to use for the specified state. + * @param {Number} state The state that uses the specified image. The values are described in "CCControlState". + */ + setBackgroundSpriteFrameForState: function (spriteFrame, state) { + var sprite = cc.Scale9Sprite.createWithSpriteFrame(spriteFrame); + this.setBackgroundSpriteForState(sprite, state); + } +}); + +var _p = cc.ControlButton.prototype; + +// Extended properties +/** @expose */ +_p.adjustBackground; +cc.defineGetterSetter(_p, "adjustBackground", _p.getAdjustBackgroundImage, _p.setAdjustBackgroundImage); +/** @expose */ +_p.preferredSize; +cc.defineGetterSetter(_p, "preferredSize", _p.getPreferredSize, _p.setPreferredSize); +/** @expose */ +_p.labelAnchor; +cc.defineGetterSetter(_p, "labelAnchor", _p.getLabelAnchorPoint, _p.setLabelAnchorPoint); + +_p = null; + +/** + * @deprecated + * @param label + * @param backgroundSprite + * @param fontSize + * @returns {ControlButton} + */ +cc.ControlButton.create = function (label, backgroundSprite, fontSize) { + return new cc.ControlButton(label, backgroundSprite, fontSize); +}; + + diff --git a/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlColourPicker.js b/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlColourPicker.js new file mode 100644 index 0000000..6cdfd6b --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlColourPicker.js @@ -0,0 +1,187 @@ +/** + * + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2011-2012 cocos2d-x.org + * Copyright (c) 2013-2014 Chukong Technologies Inc. + * + * Copyright 2012 Stewart Hamilton-Arrandale. + * http://creativewax.co.uk + * + * Modified by Yannick Loriot. + * http://yannickloriot.com + * + * 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. * + * + */ + +/** + * ControlColourPicker: color picker ui component. + * @class + * @extends cc.Control + * + * @property {cc.Sprite} background - <@readonly> The background sprite + */ +cc.ControlColourPicker = cc.Control.extend(/** @lends cc.ControlColourPicker# */{ + _hsv:null, + _colourPicker:null, + _huePicker:null, + + _background:null, + _className:"ControlColourPicker", + ctor:function () { + cc.Control.prototype.ctor.call(this); + this.init(); + }, + hueSliderValueChanged:function (sender, controlEvent) { + this._hsv.h = sender.getHue(); + + // Update the value + var rgb = cc.ControlUtils.RGBfromHSV(this._hsv); + cc.Control.prototype.setColor.call(this,cc.color(0 | (rgb.r * 255), 0 | (rgb.g * 255), 0 | (rgb.b * 255))); + + // Send CCControl callback + this.sendActionsForControlEvents(cc.CONTROL_EVENT_VALUECHANGED); + this._updateControlPicker(); + }, + + colourSliderValueChanged:function (sender, controlEvent) { + this._hsv.s = sender.getSaturation(); + this._hsv.v = sender.getBrightness(); + + + // Update the value + var rgb = cc.ControlUtils.RGBfromHSV(this._hsv); + cc.Control.prototype.setColor.call(this,cc.color(0 | (rgb.r * 255), 0 | (rgb.g * 255), 0 | (rgb.b * 255))); + + // Send CCControl callback + this.sendActionsForControlEvents(cc.CONTROL_EVENT_VALUECHANGED); + }, + + setColor:function (color) { + cc.Control.prototype.setColor.call(this,color); + //this._colorValue = color; + var rgba = new cc.RGBA(); + rgba.r = color.r / 255.0; + rgba.g = color.g / 255.0; + rgba.b = color.b / 255.0; + rgba.a = 1.0; + + this._hsv = cc.ControlUtils.HSVfromRGB(rgba); + this._updateHueAndControlPicker(); + }, + + getBackground:function () { + return this._background; + }, + + init:function () { + if (cc.Control.prototype.init.call(this)) { + // Cache the sprites + cc.spriteFrameCache.addSpriteFrames(res.CCControlColourPickerSpriteSheet_plist); + + // Create the sprite batch node + var spriteSheet = new cc.SpriteBatchNode(res.CCControlColourPickerSpriteSheet_png); + this.addChild(spriteSheet); + + /*// MIPMAP + //TODO WebGL code + var params = [gl.LINEAR_MIPMAP_NEAREST, gl.LINEAR, gl.REPEAT, gl.CLAMP_TO_EDGE]; + spriteSheet.getTexture().setAliasTexParameters(); + spriteSheet.getTexture().setTexParameters(params); + spriteSheet.getTexture().generateMipmap();*/ + + // Init default color + this._hsv = new cc.HSV(0, 0, 0); + + // Add image + this._background = cc.ControlUtils.addSpriteToTargetWithPosAndAnchor("menuColourPanelBackground.png", spriteSheet, cc.p(0,0), cc.p(0.5, 0.5)); + + var backgroundPointZero = cc.pSub(this._background.getPosition(), + cc.p(this._background.getContentSize().width / 2, this._background.getContentSize().height / 2)); + + // Setup panels . currently hard-coded... + var hueShift = 8; + var colourShift = 28; + + this._huePicker = new cc.ControlHuePicker(spriteSheet, cc.p(backgroundPointZero.x + hueShift, backgroundPointZero.y + hueShift)); + this._colourPicker = new cc.ControlSaturationBrightnessPicker(spriteSheet, cc.p(backgroundPointZero.x + colourShift, backgroundPointZero.y + colourShift)); + + // Setup events + this._huePicker.addTargetWithActionForControlEvents(this, this.hueSliderValueChanged, cc.CONTROL_EVENT_VALUECHANGED); + this._colourPicker.addTargetWithActionForControlEvents(this, this.colourSliderValueChanged, cc.CONTROL_EVENT_VALUECHANGED); + + // Set defaults + this._updateHueAndControlPicker(); + this.addChild(this._huePicker); + this.addChild(this._colourPicker); + + // Set content size + this.setContentSize(this._background.getContentSize()); + return true; + } + else + return false; + }, + + _updateControlPicker:function () { + this._huePicker.setHue(this._hsv.h); + this._colourPicker.updateWithHSV(this._hsv); + }, + + _updateHueAndControlPicker:function () { + this._huePicker.setHue(this._hsv.h); + this._colourPicker.updateWithHSV(this._hsv); + this._colourPicker.updateDraggerWithHSV(this._hsv); + }, + setEnabled:function (enabled) { + cc.Control.prototype.setEnabled.call(this, enabled); + if (this._huePicker !== null) { + this._huePicker.setEnabled(enabled); + } + if (this._colourPicker) { + this._colourPicker.setEnabled(enabled); + } + }, + onTouchBegan:function () { + //ignore all touches, handled by children + return false; + } +}); + +var _p = cc.ControlColourPicker.prototype; + +// Extended properties +/** @expose */ +_p.background; +cc.defineGetterSetter(_p, "background", _p.getBackground); + +_p = null; + +/** + * @deprecated + * @returns {ControlColourPicker} + */ +cc.ControlColourPicker.create = function () { + return new cc.ControlColourPicker(); +}; + +// compatible with NPM +var res = res || {}; +res.CCControlColourPickerSpriteSheet_plist = res.CCControlColourPickerSpriteSheet_plist || "res/extensions/CCControlColourPickerSpriteSheet.plist"; +res.CCControlColourPickerSpriteSheet_png = res.CCControlColourPickerSpriteSheet_png || "res/extensions/CCControlColourPickerSpriteSheet.png"; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlHuePicker.js b/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlHuePicker.js new file mode 100644 index 0000000..9c00274 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlHuePicker.js @@ -0,0 +1,218 @@ +/** + * + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2011-2012 cocos2d-x.org + * Copyright (c) 2013-2014 Chukong Technologies Inc. + * + * Copyright 2012 Stewart Hamilton-Arrandale. + * http://creativewax.co.uk + * + * Modified by Yannick Loriot. + * http://yannickloriot.com + * + * 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. + * + * + * converted to Javascript / cocos2d-x by Angus C + */ + +/** + * ControlHuePicker: HUE picker ui component. + * @class + * @extends cc.Control + * + * @property {Number} hue - The hue value + * @property {Number} huePercent - The hue value in percentage + * @property {cc.Sprite} background - <@readonly> The background sprite + * @property {cc.Sprite} slider - <@readonly> The slider sprite + * @property {cc.Point} startPos - <@readonly> The start position of the picker + */ +cc.ControlHuePicker = cc.Control.extend(/** @lends cc.ControlHuePicker# */{ + _hue:0, + _huePercentage:0, + _background:null, + _slider:null, + _startPos:null, + _className:"ControlHuePicker", + + /** + * The constructor of cc.ControlHuePicker + * @param {cc.Node} target + * @param {cc.Point} pos position + */ + ctor:function(target, pos) { + cc.Control.prototype.ctor.call(this); + pos && this.initWithTargetAndPos(target, pos); + }, + + //maunally put in the setters + getHue:function () { + return this._hue; + }, + setHue:function (hueValue) { + this._hue = hueValue; + this.setHuePercentage(this._hue / 360.0); + }, + + getHuePercentage:function () { + return this._huePercentage; + }, + setHuePercentage:function (hueValueInPercent) { + this._huePercentage = hueValueInPercent; + this._hue = this._huePercentage * 360.0; + + // Clamp the position of the icon within the circle + var backgroundBox = this._background.getBoundingBox(); + + // Get the center point of the background image + var centerX = this._startPos.x + backgroundBox.width * 0.5; + var centerY = this._startPos.y + backgroundBox.height * 0.5; + + // Work out the limit to the distance of the picker when moving around the hue bar + var limit = backgroundBox.width * 0.5 - 15.0; + + // Update angle + var angleDeg = this._huePercentage * 360.0 - 180.0; + var angle = cc.degreesToRadians(angleDeg); + + // Set new position of the slider + var x = centerX + limit * Math.cos(angle); + var y = centerY + limit * Math.sin(angle); + this._slider.setPosition(x, y); + }, + + setEnabled:function (enabled) { + cc.Control.prototype.setEnabled.call(this, enabled); + if (this._slider) { + this._slider.setOpacity(enabled ? 255 : 128); + } + }, + + getBackground:function () { + return this._background; + }, + getSlider:function () { + return this._slider; + }, + getStartPos:function () { + return this._startPos; + }, + + initWithTargetAndPos:function (target, pos) { + if (cc.Control.prototype.init.call(this)) { + // Add background and slider sprites + this._background = cc.ControlUtils.addSpriteToTargetWithPosAndAnchor("huePickerBackground.png", target, pos, cc.p(0.0, 0.0)); + this._slider = cc.ControlUtils.addSpriteToTargetWithPosAndAnchor("colourPicker.png", target, pos, cc.p(0.5, 0.5)); + + this._slider.setPosition(pos.x, pos.y + this._background.getBoundingBox().height * 0.5); + this._startPos = pos; + + // Sets the default value + this._hue = 0.0; + this._huePercentage = 0.0; + return true; + } else + return false; + }, + + _updateSliderPosition:function (location) { + // Clamp the position of the icon within the circle + var backgroundBox = this._background.getBoundingBox(); + + // Get the center point of the background image + var centerX = this._startPos.x + backgroundBox.width * 0.5; + var centerY = this._startPos.y + backgroundBox.height * 0.5; + + // Work out the distance difference between the location and center + var dx = location.x - centerX; + var dy = location.y - centerY; + + // Update angle by using the direction of the location + var angle = Math.atan2(dy, dx); + var angleDeg = cc.radiansToDegrees(angle) + 180.0; + + // use the position / slider width to determin the percentage the dragger is at + this.setHue(angleDeg); + + // send CCControl callback + this.sendActionsForControlEvents(cc.CONTROL_EVENT_VALUECHANGED); + }, + _checkSliderPosition:function (location) { + // compute the distance between the current location and the center + var distance = Math.sqrt(Math.pow(location.x + 10, 2) + Math.pow(location.y, 2)); + + // check that the touch location is within the circle + if (80 > distance && distance > 59) { + this._updateSliderPosition(location); + return true; + } + return false; + }, + + onTouchBegan:function (touch, event) { + if (!this.isEnabled() || !this.isVisible()) { + return false; + } + var touchLocation = this.getTouchLocation(touch); + + // Check the touch position on the slider + return this._checkSliderPosition(touchLocation); + }, + onTouchMoved:function (touch, event) { + // Get the touch location + var touchLocation = this.getTouchLocation(touch); + + //small modification: this allows changing of the colour, even if the touch leaves the bounding area + //this._updateSliderPosition(touchLocation); + //this.sendActionsForControlEvents(cc.CONTROL_EVENT_VALUECHANGED); + // Check the touch position on the slider + this._checkSliderPosition(touchLocation); + } +}); + +var _p = cc.ControlHuePicker.prototype; + +// Extended properties +/** @expose */ +_p.hue; +cc.defineGetterSetter(_p, "hue", _p.getHue, _p.setHue); +/** @expose */ +_p.huePercent; +cc.defineGetterSetter(_p, "huePercent", _p.getHuePercentage, _p.setHuePercentage); +/** @expose */ +_p.background; +cc.defineGetterSetter(_p, "background", _p.getBackground); +/** @expose */ +_p.slider; +cc.defineGetterSetter(_p, "slider", _p.getSlider); +/** @expose */ +_p.startPos; +cc.defineGetterSetter(_p, "startPos", _p.getStartPos); + +_p = null; + +/** + * @deprecated + * @param target + * @param pos + * @returns {ControlHuePicker} + */ +cc.ControlHuePicker.create = function (target, pos) { + return new cc.ControlHuePicker(target, pos); +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlPotentiometer.js b/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlPotentiometer.js new file mode 100644 index 0000000..7c035ef --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlPotentiometer.js @@ -0,0 +1,300 @@ +/** + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2011-2012 cocos2d-x.org + * Copyright (c) 2013-2014 Chukong Technologies Inc. + * + * http://www.cocos2d-x.org + * + * Copyright 2012 Yannick Loriot. All rights reserved. + * http://yannickloriot.com + * + * 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. + * + */ + +/** + * CCControlPotentiometer: Potentiometer control for Cocos2D. + * @class + * @extends cc.Control + * + * @property {Number} value - The current value of the potentionmeter + * @property {Number} minValue - The minimum value of the potentionmeter + * @property {Number} maxValue - The maximum value of the potentionmeter + * @property {cc.ProgressTimer} progressTimer - The progress timer of the potentionmeter + * @property {cc.Sprite} thumbSprite - The thumb sprite of the potentionmeter + * @property {cc.Point} prevLocation - The previous location of the potentionmeter + */ +cc.ControlPotentiometer = cc.Control.extend(/** @lends cc.ControlPotentiometer# */{ + _thumbSprite:null, + _progressTimer:null, + _previousLocation:null, + /** Contains the receiver’s current value. */ + _value:0, + /** Contains the minimum value of the receiver. + * The default value of this property is 0.0. */ + _minimumValue:0, + /** Contains the maximum value of the receiver. + * The default value of this property is 1.0. */ + _maximumValue:1, + _className:"ControlPotentiometer", + + ctor:function (backgroundFile, progressFile, thumbFile) { + cc.Control.prototype.ctor.call(this); + if (thumbFile != undefined) { + // Prepare track for potentiometer + var backgroundSprite = new cc.Sprite(backgroundFile); + + // Prepare thumb for potentiometer + var thumbSprite = new cc.Sprite(thumbFile); + + // Prepare progress for potentiometer + var progressTimer = new cc.ProgressTimer(new cc.Sprite(progressFile)); + this.initWithTrackSprite_ProgressTimer_ThumbSprite(backgroundSprite, progressTimer, thumbSprite); + } + }, + + /** + * + * @param {cc.Sprite} trackSprite + * @param {cc.ProgressTimer} progressTimer + * @param {cc.Sprite} thumbSprite + * @return {Boolean} + */ + initWithTrackSprite_ProgressTimer_ThumbSprite:function (trackSprite, progressTimer, thumbSprite) { + if (this.init()) { + this.setProgressTimer(progressTimer); + this.setThumbSprite(thumbSprite); + this._thumbSprite.setPosition(progressTimer.getPosition()); + + this.addChild(thumbSprite, 2); + this.addChild(progressTimer, 1); + this.addChild(trackSprite); + + this.setContentSize(trackSprite.getContentSize()); + + // Init default values + this._minimumValue = 0.0; + this._maximumValue = 1.0; + this.setValue(this._minimumValue); + return true; + } + return false; + }, + + setEnabled:function (enabled) { + this.setEnabled(enabled); + if (this._thumbSprite !== null) { + this._thumbSprite.setOpacity((enabled) ? 255 : 128); + } + }, + + setValue:function (value) { + // set new value with sentinel + if (value < this._minimumValue) { + value = this._minimumValue; + } + + if (value > this._maximumValue) { + value = this._maximumValue; + } + + this._value = value; + + // Update thumb and progress position for new value + var percent = (value - this._minimumValue) / (this._maximumValue - this._minimumValue); + this._progressTimer.setPercentage(percent * 100.0); + this._thumbSprite.setRotation(percent * 360.0); + + this.sendActionsForControlEvents(cc.CONTROL_EVENT_VALUECHANGED); + }, + + getValue:function () { + return this._value; + }, + + setMinimumValue:function (minimumValue) { + this._minimumValue = minimumValue; + + if (this._minimumValue >= this._maximumValue) { + this._maximumValue = this._minimumValue + 1.0; + } + + this.setValue(this._maximumValue); + }, + + getMinimumValue:function () { + return this._minimumValue; + }, + + setMaximumValue:function (maximumValue) { + this._maximumValue = maximumValue; + + if (this._maximumValue <= this._minimumValue) { + this._minimumValue = this._maximumValue - 1.0; + } + + this.setValue(this._minimumValue); + }, + + getMaximumValue:function () { + return this._maximumValue; + }, + + isTouchInside:function (touch) { + var touchLocation = this.getTouchLocation(touch); + + var distance = this.distanceBetweenPointAndPoint(this._progressTimer.getPosition(), touchLocation); + + return distance < Math.min(this.getContentSize().width / 2, this.getContentSize().height / 2); + }, + + onTouchBegan:function (touch, event) { + if (!this.isTouchInside(touch) || !this.isEnabled() || !this.isVisible()) { + return false; + } + + this._previousLocation = this.getTouchLocation(touch); + + this.potentiometerBegan(this._previousLocation); + + return true; + }, + + onTouchMoved:function (touch, event) { + var location = this.getTouchLocation(touch); + + this.potentiometerMoved(location); + }, + + onTouchEnded:function (touch, event) { + this.potentiometerEnded(cc.p(0, 0)); + }, + + /** + * the distance between the point1 and point2 + * @param {cc.Point} point1 + * @param {cc.Point} point2 + * @return {Number} + */ + distanceBetweenPointAndPoint:function (point1, point2) { + var dx = point1.x - point2.x; + var dy = point1.y - point2.y; + return Math.sqrt(dx * dx + dy * dy); + }, + + /** + * the angle in degree between line1 and line2. + * @param {cc.Point} beginLineA + * @param {cc.Point} endLineA + * @param {cc.Point} beginLineB + * @param {cc.Point} endLineB + * @return {Number} + */ + angleInDegreesBetweenLineFromPoint_toPoint_toLineFromPoint_toPoint:function (beginLineA, endLineA, beginLineB, endLineB) { + var a = endLineA.x - beginLineA.x; + var b = endLineA.y - beginLineA.y; + var c = endLineB.x - beginLineB.x; + var d = endLineB.y - beginLineB.y; + + var atanA = Math.atan2(a, b); + var atanB = Math.atan2(c, d); + + // convert radiants to degrees + return (atanA - atanB) * 180 / Math.PI; + }, + + potentiometerBegan:function (location) { + this.setSelected(true); + this.getThumbSprite().setColor(cc.color.GRAY); + }, + + potentiometerMoved:function (location) { + var angle = this.angleInDegreesBetweenLineFromPoint_toPoint_toLineFromPoint_toPoint(this._progressTimer.getPosition(), location, this._progressTimer.getPosition(), this._previousLocation); + + // fix value, if the 12 o'clock position is between location and previousLocation + if (angle > 180) { + angle -= 360; + } + else if (angle < -180) { + angle += 360; + } + + this.setValue(this._value + angle / 360.0 * (this._maximumValue - this._minimumValue)); + + this._previousLocation = location; + }, + + potentiometerEnded:function (location) { + this.getThumbSprite().setColor(cc.color.WHITE); + this.setSelected(false); + }, + setThumbSprite:function (sprite) { + this._thumbSprite = sprite; + }, + getThumbSprite:function () { + return this._thumbSprite; + }, + setProgressTimer:function (sprite) { + this._progressTimer = sprite; + }, + getProgressTimer:function () { + return this._progressTimer; + }, + setPreviousLocation:function (point) { + this._previousLocation = point; + }, + getPreviousLocation:function () { + return this._previousLocation; + } +}); + +var _p = cc.ControlPotentiometer.prototype; + +// Extended properties +/** @expose */ +_p.value; +cc.defineGetterSetter(_p, "value", _p.getValue, _p.setValue); +/** @expose */ +_p.minValue; +cc.defineGetterSetter(_p, "minValue", _p.getMinimumValue, _p.setMinimumValue); +/** @expose */ +_p.maxValue; +cc.defineGetterSetter(_p, "maxValue", _p.getMaximumValue, _p.setMaximumValue); +/** @expose */ +_p.progressTimer; +cc.defineGetterSetter(_p, "progressTimer", _p.getProgressTimer, _p.setProgressTimer); +/** @expose */ +_p.thumbSprite; +cc.defineGetterSetter(_p, "thumbSprite", _p.getThumbSprite, _p.setThumbSprite); +/** @expose */ +_p.prevLocation; +cc.defineGetterSetter(_p, "prevLocation", _p.getPreviousLocation, _p.setPreviousLocation); + +_p = null; + +/** + * @deprecated + * @param backgroundFile + * @param progressFile + * @param thumbFile + * @returns {ControlPotentiometer} + */ +cc.ControlPotentiometer.create = function (backgroundFile, progressFile, thumbFile) { + return new cc.ControlPotentiometer(backgroundFile, progressFile, thumbFile); +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlSaturationBrightnessPicker.js b/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlSaturationBrightnessPicker.js new file mode 100644 index 0000000..a9045c4 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlSaturationBrightnessPicker.js @@ -0,0 +1,257 @@ +/** + * + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2011-2012 cocos2d-x.org + * Copyright (c) 2013-2014 Chukong Technologies Inc. + * + * Copyright 2012 Stewart Hamilton-Arrandale. + * http://creativewax.co.uk + * + * Modified by Yannick Loriot. + * http://yannickloriot.com + * + * 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. + * + * + * converted to Javascript / cocos2d-x by Angus C + */ + +/** + * ControlSaturationBrightnessPicker: Saturation brightness picker ui component. + * @class + * @extends cc.Control + * + * @property {Number} saturation - <@readonly> Saturation value of the picker + * @property {Number} brightness - <@readonly> Brightness value of the picker + * @property {cc.Sprite} background - <@readonly> The background sprite + * @property {cc.Sprite} overlay - <@readonly> The overlay sprite + * @property {cc.Sprite} shadow - <@readonly> The shadow sprite + * @property {cc.Sprite} slider - <@readonly> The slider sprite + * @property {cc.Point} startPos - <@readonly> The start position of the picker + */ +cc.ControlSaturationBrightnessPicker = cc.Control.extend(/** @lends cc.ControlSaturationBrightnessPicker# */{ + _saturation:0, + _brightness:0, + + _background:null, + _overlay:null, + _shadow:null, + _slider:null, + _startPos:null, + + _boxPos:0, + _boxSize:0, + _className:"ControlSaturationBrightnessPicker", + + /** + * The constructor of cc.ControlSaturationBrightnessPicker + * @param {cc.Node} target + * @param {cc.Point} pos position + */ + ctor:function (target, pos) { + cc.Control.prototype.ctor.call(this); + pos && this.initWithTargetAndPos(target, pos); + }, + getSaturation:function () { + return this._saturation; + }, + getBrightness:function () { + return this._brightness; + }, + + //not sure if these need to be there actually. I suppose someone might want to access the sprite? + getBackground:function () { + return this._background; + }, + getOverlay:function () { + return this._brightness; + }, + getShadow:function () { + return this._shadow; + }, + getSlider:function () { + return this._slider; + }, + getStartPos:function () { + return this._startPos; + }, + + initWithTargetAndPos:function (target, pos) { + if (cc.Control.prototype.init.call(this)) { + // Add background and slider sprites + this._background = cc.ControlUtils.addSpriteToTargetWithPosAndAnchor("colourPickerBackground.png", target, pos, cc.p(0.0, 0.0)); + this._overlay = cc.ControlUtils.addSpriteToTargetWithPosAndAnchor("colourPickerOverlay.png", target, pos, cc.p(0.0, 0.0)); + this._shadow = cc.ControlUtils.addSpriteToTargetWithPosAndAnchor("colourPickerShadow.png", target, pos, cc.p(0.0, 0.0)); + this._slider = cc.ControlUtils.addSpriteToTargetWithPosAndAnchor("colourPicker.png", target, pos, cc.p(0.5, 0.5)); + + this._startPos = pos; // starting position of the colour picker + this._boxPos = 35; // starting position of the virtual box area for picking a colour + this._boxSize = this._background.getContentSize().width / 2; // the size (width and height) of the virtual box for picking a colour from + return true; + } else + return false; + }, + + setEnabled:function (enabled) { + cc.Control.prototype.setEnabled.call(this, enabled); + if (this._slider) { + this._slider.setOpacity(enabled ? 255 : 128); + } + }, + + updateWithHSV:function (hsv) { + var hsvTemp = new cc.HSV(); + hsvTemp.s = 1; + hsvTemp.h = hsv.h; + hsvTemp.v = 1; + + var rgb = cc.ControlUtils.RGBfromHSV(hsvTemp); + this._background.setColor(cc.color(0 | (rgb.r * 255), 0 | (rgb.g * 255), 0 | (rgb.b * 255))); + }, + updateDraggerWithHSV:function (hsv) { + // Set the position of the slider to the correct saturation and brightness + var pos = cc.p(this._startPos.x + this._boxPos + (this._boxSize * (1 - hsv.s)), + this._startPos.y + this._boxPos + (this._boxSize * hsv.v)); + + // update + this._updateSliderPosition(pos); + }, + + _updateSliderPosition:function (sliderPosition) { + // Clamp the position of the icon within the circle + + // Get the center point of the bkgd image + var centerX = this._startPos.x + this._background.getBoundingBox().width * 0.5; + var centerY = this._startPos.y + this._background.getBoundingBox().height * 0.5; + + // Work out the distance difference between the location and center + var dx = sliderPosition.x - centerX; + var dy = sliderPosition.y - centerY; + var dist = Math.sqrt(dx * dx + dy * dy); + + // Update angle by using the direction of the location + var angle = Math.atan2(dy, dx); + + // Set the limit to the slider movement within the colour picker + var limit = this._background.getBoundingBox().width * 0.5; + + // Check distance doesn't exceed the bounds of the circle + if (dist > limit) { + sliderPosition.x = centerX + limit * Math.cos(angle); + sliderPosition.y = centerY + limit * Math.sin(angle); + } + + // Set the position of the dragger + this._slider.setPosition(sliderPosition); + + + // Clamp the position within the virtual box for colour selection + if (sliderPosition.x < this._startPos.x + this._boxPos) + sliderPosition.x = this._startPos.x + this._boxPos; + else if (sliderPosition.x > this._startPos.x + this._boxPos + this._boxSize - 1) + sliderPosition.x = this._startPos.x + this._boxPos + this._boxSize - 1; + if (sliderPosition.y < this._startPos.y + this._boxPos) + sliderPosition.y = this._startPos.y + this._boxPos; + else if (sliderPosition.y > this._startPos.y + this._boxPos + this._boxSize) + sliderPosition.y = this._startPos.y + this._boxPos + this._boxSize; + + // Use the position / slider width to determin the percentage the dragger is at + this._saturation = 1.0 - Math.abs((this._startPos.x + this._boxPos - sliderPosition.x) / this._boxSize); + this._brightness = Math.abs((this._startPos.y + this._boxPos - sliderPosition.y) / this._boxSize); + }, + + _checkSliderPosition:function (location) { + // Clamp the position of the icon within the circle + // get the center point of the bkgd image + var centerX = this._startPos.x + this._background.getBoundingBox().width * 0.5; + var centerY = this._startPos.y + this._background.getBoundingBox().height * 0.5; + + // work out the distance difference between the location and center + var dx = location.x - centerX; + var dy = location.y - centerY; + var dist = Math.sqrt(dx * dx + dy * dy); + + // check that the touch location is within the bounding rectangle before sending updates + if (dist <= this._background.getBoundingBox().width * 0.5) { + this._updateSliderPosition(location); + this.sendActionsForControlEvents(cc.CONTROL_EVENT_VALUECHANGED); + return true; + } + return false; + }, + + onTouchBegan:function (touch, event) { + if (!this.isEnabled() || !this.isVisible()) { + return false; + } + // Get the touch location + var touchLocation = this.getTouchLocation(touch); + + // Check the touch position on the slider + return this._checkSliderPosition(touchLocation); + }, + + onTouchMoved:function (touch, event) { + // Get the touch location + var touchLocation = this.getTouchLocation(touch); + + //small modification: this allows changing of the colour, even if the touch leaves the bounding area + //this._updateSliderPosition(touchLocation); + //this.sendActionsForControlEvents(cc.CONTROL_EVENT_VALUECHANGED); + // Check the touch position on the slider + this._checkSliderPosition(touchLocation); + } +}); + +var _p = cc.ControlSaturationBrightnessPicker.prototype; + +// Extended properties +/** @expose */ +_p.saturation; +cc.defineGetterSetter(_p, "saturation", _p.getSaturation); +/** @expose */ +_p.brightness; +cc.defineGetterSetter(_p, "brightness", _p.getBrightness); +/** @expose */ +_p.background; +cc.defineGetterSetter(_p, "background", _p.getBackground); +/** @expose */ +_p.overlay; +cc.defineGetterSetter(_p, "overlay", _p.getOverlay); +/** @expose */ +_p.shadow; +cc.defineGetterSetter(_p, "shadow", _p.getShadow); +/** @expose */ +_p.slider; +cc.defineGetterSetter(_p, "slider", _p.getSlider); +/** @expose */ +_p.startPos; +cc.defineGetterSetter(_p, "startPos", _p.getStartPos); + +_p = null; + +/** + * Creates a cc.ControlSaturationBrightnessPicker + * @param {cc.Node} target + * @param {cc.Point} pos position + * @returns {ControlSaturationBrightnessPicker} + */ +cc.ControlSaturationBrightnessPicker.create = function (target, pos) { + return new cc.ControlSaturationBrightnessPicker(target, pos); +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlSlider.js b/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlSlider.js new file mode 100644 index 0000000..e4a757b --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlSlider.js @@ -0,0 +1,308 @@ +/** + * + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2011-2012 cocos2d-x.org + * Copyright (c) 2013-2014 Chukong Technologies Inc. + * + * Copyright 2011 Yannick Loriot. All rights reserved. + * http://yannickloriot.com + * + * 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. + * + * + * converted to Javascript / cocos2d-x by Angus C + */ + +/** + * @ignore + */ +cc.SLIDER_MARGIN_H = 24; +cc.SLIDER_MARGIN_V = 8; + +/** + * ControlSlider: Slider ui component. + * @class + * @extends cc.Control + * + * @property {Number} value - The value of the slider + * @property {Number} minValue - The minimum value of the slider + * @property {Number} maxValue - The maximum value of the slider + * @property {Number} minAllowedValue - The minimum allowed value of the slider + * @property {Number} maxAllowedValue - The maximum allowed value of the slider + * @property {Number} thumbSprite - <@readonly> Brightness value of the picker + * @property {cc.Sprite} progressSprite - <@readonly> The background sprite + * @property {cc.Sprite} backgroundSprite - <@readonly> The overlay sprite + */ +cc.ControlSlider = cc.Control.extend(/** @lends cc.ControlSlider# */{ + _value:0, + _minimumValue:0, + _maximumValue:0, + _minimumAllowedValue:0, + _maximumAllowedValue:0, + + _thumbSprite:null, + _progressSprite:null, + _backgroundSprite:null, + _className:"ControlSlider", + + ctor:function (bgFile, progressFile, thumbFile) { + cc.Control.prototype.ctor.call(this); + if (thumbFile != undefined) { + // Prepare background for slider + var bgSprite = new cc.Sprite(bgFile); + + // Prepare progress for slider + var progressSprite = new cc.Sprite(progressFile); + + // Prepare thumb (menuItem) for slider + var thumbSprite = new cc.Sprite(thumbFile); + + this.initWithSprites(bgSprite, progressSprite, thumbSprite); + } + }, + + getValue:function () { + return this._value; + }, + setValue:function (value) { + //clamp between the two bounds + value = Math.max(value, this._minimumValue); + value = Math.min(value, this._maximumValue); + this._value = value; + this.needsLayout(); + this.sendActionsForControlEvents(cc.CONTROL_EVENT_VALUECHANGED); + }, + + getMinimumValue:function () { + return this._minimumValue; + }, + setMinimumValue:function (minimumValue) { + this._minimumValue = minimumValue; + this._minimumAllowedValue = minimumValue; + if (this._minimumValue >= this._maximumValue) + this._maximumValue = this._minimumValue + 1.0; + this.setValue(this._value); + }, + + getMaximumValue:function () { + return this._maximumValue; + }, + setMaximumValue:function (maximumValue) { + this._maximumValue = maximumValue; + this._maximumAllowedValue = maximumValue; + if (this._maximumValue <= this._minimumValue) + this._minimumValue = this._maximumValue - 1.0; + this.setValue(this._value); + }, + isTouchInside:function (touch) { + var touchLocation = touch.getLocation(); + touchLocation = this.getParent().convertToNodeSpace(touchLocation); + + var rect = this.getBoundingBox(); + rect.width += this._thumbSprite.getContentSize().width; + rect.x -= this._thumbSprite.getContentSize().width / 2; + + return cc.rectContainsPoint(rect, touchLocation); + }, + locationFromTouch:function (touch) { + var touchLocation = touch.getLocation(); // Get the touch position + touchLocation = this.convertToNodeSpace(touchLocation); // Convert to the node space of this class + + if (touchLocation.x < 0) { + touchLocation.x = 0; + } else if (touchLocation.x > this._backgroundSprite.getContentSize().width) { + touchLocation.x = this._backgroundSprite.getContentSize().width; + } + + return touchLocation; + }, + getMinimumAllowedValue:function () { + return this._minimumAllowedValue; + }, + setMinimumAllowedValue:function (val) { + this._minimumAllowedValue = val; + }, + + getMaximumAllowedValue:function () { + return this._maximumAllowedValue; + }, + + setMaximumAllowedValue:function (val) { + this._maximumAllowedValue = val; + }, + + getThumbSprite:function () { + return this._thumbSprite; + }, + getProgressSprite:function () { + return this._progressSprite; + }, + getBackgroundSprite:function () { + return this._backgroundSprite; + }, + + /** + * Initializes a slider with a background sprite, a progress bar and a thumb + * item. + * + * @param {cc.Sprite} backgroundSprite CCSprite, that is used as a background. + * @param {cc.Sprite} progressSprite CCSprite, that is used as a progress bar. + * @param {cc.Sprite} thumbSprite CCMenuItem, that is used as a thumb. + */ + initWithSprites:function (backgroundSprite, progressSprite, thumbSprite) { + if (cc.Control.prototype.init.call(this)) { + this.ignoreAnchorPointForPosition(false); + + this._backgroundSprite = backgroundSprite; + this._progressSprite = progressSprite; + this._thumbSprite = thumbSprite; + + // Defines the content size + var maxRect = cc.ControlUtils.CCRectUnion(backgroundSprite.getBoundingBox(), thumbSprite.getBoundingBox()); + this.setContentSize(maxRect.width, maxRect.height); + + // Add the slider background + this._backgroundSprite.setAnchorPoint(0.5, 0.5); + this._backgroundSprite.setPosition(maxRect.width / 2, maxRect.height / 2); + this.addChild(this._backgroundSprite); + + // Add the progress bar + this._progressSprite.setAnchorPoint(0.0, 0.5); + this._progressSprite.setPosition(0, maxRect.height / 2); + this.addChild(this._progressSprite); + + // Add the slider thumb + this._thumbSprite.setPosition(0, maxRect.height / 2); + this.addChild(this._thumbSprite); + + // Init default values + this._minimumValue = 0.0; + this._maximumValue = 1.0; + this.setValue(this._minimumValue); + return true; + } else + return false; + }, + + setEnabled:function (enabled) { + cc.Control.prototype.setEnabled.call(this, enabled); + if (this._thumbSprite) { + this._thumbSprite.setOpacity(enabled ? 255 : 128); + } + }, + + sliderBegan:function (location) { + this.setSelected(true); + this._thumbSprite.setColor(cc.color.GRAY); + this.setValue(this.valueForLocation(location)); + }, + sliderMoved:function (location) { + this.setValue(this.valueForLocation(location)); + }, + sliderEnded:function (location) { + if (this.isSelected()) { + this.setValue(this.valueForLocation(this._thumbSprite.getPosition())); + } + this._thumbSprite.setColor(cc.color.WHITE); + this.setSelected(false); + }, + + getTouchLocationInControl:function (touch) { + var touchLocation = touch.getLocation(); // Get the touch position + touchLocation = this.convertToNodeSpace(touchLocation); // Convert to the node space of this class + + if (touchLocation.x < 0) { + touchLocation.x = 0; + } else if (touchLocation.x > this._backgroundSprite.getContentSize().width + cc.SLIDER_MARGIN_H) { + touchLocation.x = this._backgroundSprite.getContentSize().width + cc.SLIDER_MARGIN_H; + } + return touchLocation; + }, + + onTouchBegan:function (touch, event) { + if (!this.isTouchInside(touch)|| !this.isEnabled() || !this.isVisible()) + return false; + + var location = this.locationFromTouch(touch); + this.sliderBegan(location); + return true; + }, + onTouchMoved:function (touch, event) { + var location = this.locationFromTouch(touch); + this.sliderMoved(location); + }, + onTouchEnded:function (touch, event) { + this.sliderEnded(cc.p(0,0)); + }, + needsLayout:function(){ + var percent = (this._value - this._minimumValue) / (this._maximumValue - this._minimumValue); + this._thumbSprite.setPositionX(percent * this._backgroundSprite.getContentSize().width); + + // Stretches content proportional to newLevel + var textureRect = this._progressSprite.getTextureRect(); + textureRect = cc.rect(textureRect.x, textureRect.y, this._thumbSprite.getPositionX(), textureRect.height); + this._progressSprite.setTextureRect(textureRect, this._progressSprite.isTextureRectRotated()); + this._thumbSprite._renderCmd.transform(this._renderCmd); + }, + /** Returns the value for the given location. */ + valueForLocation:function (location) { + var percent = location.x / this._backgroundSprite.getContentSize().width; + return Math.max(Math.min(this._minimumValue + percent * (this._maximumValue - this._minimumValue), this._maximumAllowedValue), this._minimumAllowedValue); + } +}); + +var _p = cc.ControlSlider.prototype; + +// Extended properties +/** @expose */ +_p.value; +cc.defineGetterSetter(_p, "value", _p.getValue, _p.setValue); +/** @expose */ +_p.minValue; +cc.defineGetterSetter(_p, "minValue", _p.getMinimumValue, _p.setMinimumValue); +/** @expose */ +_p.maxValue; +cc.defineGetterSetter(_p, "maxValue", _p.getMaximumValue, _p.setMaximumValue); +/** @expose */ +_p.minAllowedValue; +cc.defineGetterSetter(_p, "minAllowedValue", _p.getMinimumAllowedValue, _p.setMinimumAllowedValue); +/** @expose */ +_p.maxAllowedValue; +cc.defineGetterSetter(_p, "maxAllowedValue", _p.getMaximumAllowedValue, _p.setMaximumAllowedValue); +/** @expose */ +_p.thumbSprite; +cc.defineGetterSetter(_p, "thumbSprite", _p.getThumbSprite); +/** @expose */ +_p.progressSprite; +cc.defineGetterSetter(_p, "progressSprite", _p.getProgressSprite); +/** @expose */ +_p.backgroundSprite; +cc.defineGetterSetter(_p, "backgroundSprite", _p.getBackgroundSprite); + +_p = null; + +/** + * Creates a slider with a given background sprite and a progress bar and a + * thumb item. + * @deprecated + * @see cc.ControlSlider + */ +cc.ControlSlider.create = function (bgFile, progressFile, thumbFile) { + return new cc.ControlSlider(bgFile, progressFile, thumbFile); +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlStepper.js b/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlStepper.js new file mode 100644 index 0000000..6ac29fb --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlStepper.js @@ -0,0 +1,390 @@ +/** + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2011-2012 cocos2d-x.org + * Copyright (c) 2013-2014 Chukong Technologies Inc. + * + * http://www.cocos2d-x.org + * + * Copyright 2012 Yannick Loriot. All rights reserved. + * http://yannickloriot.com + * + * 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. + * + */ + +/** + * @ignore + */ +cc.CONTROL_STEPPER_PARTMINUS = 0; +cc.CONTROL_STEPPER_PARTPLUS = 1; +cc.CONTROL_STEPPER_PARTNONE = 2; +cc.CONTROL_STEPPER_LABELCOLOR_ENABLED = cc.color(55, 55, 55); +cc.CONTROL_STEPPER_LABELCOLOR_DISABLED = cc.color(147, 147, 147); +cc.CONTROL_STEPPER_LABELFONT = "CourierNewPSMT"; +cc.AUTOREPEAT_DELTATIME = 0.15; +cc.AUTOREPEAT_INCREASETIME_INCREMENT = 12; + +/** + * ControlStepper: Stepper ui component. + * @class + * @extends cc.Control + * + * @property {Boolean} wraps - Indicate whether the stepper wraps + * @property {Number} value - The value of the stepper control + * @property {Number} minValue - The minimum value of the stepper control + * @property {Number} maxValue - The maximum value of the stepper control + * @property {Number} stepValue - The interval value for each step of the stepper control + * @property {Boolean} continuous - <@readonly> Indicate whether the stepper value is continuous + * @property {cc.Sprite} minusSprite - The sprite for minus button of the stepper control + * @property {cc.Sprite} plusSprite - The sprite for plus button of the stepper control + * @property {cc.LabelTTF} minusLabel - The label for minus button of the stepper control + * @property {cc.LabelTTF} plusSLabel - The label for plus button of the stepper control + */ +cc.ControlStepper = cc.Control.extend(/** @lends cc.ControlStepper# */{ + _minusSprite:null, + _plusSprite:null, + _minusLabel:null, + _plusLabel:null, + _value:0, + _continuous:false, + _autorepeat:false, + _wraps:false, + _minimumValue:0, + _maximumValue:0, + _stepValue:0, + _touchInsideFlag:false, + _touchedPart:cc.CONTROL_STEPPER_PARTNONE, + _autorepeatCount:0, + _className:"ControlStepper", + ctor:function (minusSprite, plusSprite) { + cc.Control.prototype.ctor.call(this); + this._minusSprite = null; + this._plusSprite = null; + this._minusLabel = null; + this._plusLabel = null; + this._value = 0; + this._continuous = false; + this._autorepeat = false; + this._wraps = false; + this._minimumValue = 0; + this._maximumValue = 0; + this._stepValue = 0; + this._touchInsideFlag = false; + this._touchedPart = cc.CONTROL_STEPPER_PARTNONE; + this._autorepeatCount = 0; + + plusSprite && this.initWithMinusSpriteAndPlusSprite(minusSprite, plusSprite); + + }, + + initWithMinusSpriteAndPlusSprite:function (minusSprite, plusSprite) { + if(!minusSprite) + throw new Error("cc.ControlStepper.initWithMinusSpriteAndPlusSprite(): Minus sprite should be non-null."); + if(!plusSprite) + throw new Error("cc.ControlStepper.initWithMinusSpriteAndPlusSprite(): Plus sprite should be non-null."); + + if (this.init()) { + // Set the default values + this._autorepeat = true; + this._continuous = true; + this._minimumValue = 0; + this._maximumValue = 100; + this._value = 0; + this._stepValue = 1; + this._wraps = false; + this.ignoreAnchorPointForPosition(false); + + // Add the minus components + this.setMinusSprite(minusSprite); + this._minusSprite.setPosition(minusSprite.getContentSize().width / 2, minusSprite.getContentSize().height / 2); + this.addChild(this._minusSprite); + + this.setMinusLabel(new cc.LabelTTF("-", cc.CONTROL_STEPPER_LABELFONT, 40, cc.size(40, 40), cc.TEXT_ALIGNMENT_CENTER, cc.VERTICAL_TEXT_ALIGNMENT_CENTER)); + this._minusLabel.setColor(cc.CONTROL_STEPPER_LABELCOLOR_DISABLED); + this._minusLabel.setPosition(this._minusSprite.getContentSize().width / 2, this._minusSprite.getContentSize().height / 2); + this._minusSprite.addChild(this._minusLabel); + + // Add the plus components + this.setPlusSprite(plusSprite); + this._plusSprite.setPosition(minusSprite.getContentSize().width + plusSprite.getContentSize().width / 2, + minusSprite.getContentSize().height / 2); + this.addChild(this._plusSprite); + + this.setPlusLabel(new cc.LabelTTF("+", cc.CONTROL_STEPPER_LABELFONT, 40, cc.size(40, 40), cc.TEXT_ALIGNMENT_CENTER, cc.VERTICAL_TEXT_ALIGNMENT_CENTER)); + this._plusLabel.setColor(cc.CONTROL_STEPPER_LABELCOLOR_ENABLED); + this._plusLabel.setPosition(this._plusSprite.getContentSize().width / 2, this._plusSprite.getContentSize().height / 2); + this._plusSprite.addChild(this._plusLabel); + + // Defines the content size + var maxRect = cc.ControlUtils.CCRectUnion(this._minusSprite.getBoundingBox(), this._plusSprite.getBoundingBox()); + this.setContentSize(this._minusSprite.getContentSize().width + this._plusSprite.getContentSize().height, maxRect.height); + return true; + } + return false; + }, + +//#pragma mark Properties + + setWraps: function (wraps) { + this._wraps = wraps; + + if (this._wraps) { + this._minusLabel.setColor(cc.CONTROL_STEPPER_LABELCOLOR_ENABLED); + this._plusLabel.setColor(cc.CONTROL_STEPPER_LABELCOLOR_ENABLED); + } + + this.setValue(this._value); + }, + + getWraps: function () { + return this._wraps; + }, + + setMinimumValue:function (minimumValue) { + if (minimumValue >= this._maximumValue) + throw new Error("cc.ControlStepper.setMinimumValue(): minimumValue should be numerically less than maximumValue."); + + this._minimumValue = minimumValue; + this.setValue(this._value); + }, + getMinimumValue: function () { + return this._minimumValue; + }, + + setMaximumValue:function (maximumValue) { + if (maximumValue <= this._minimumValue) + throw new Error("cc.ControlStepper.setMaximumValue(): maximumValue should be numerically less than maximumValue."); + + this._maximumValue = maximumValue; + this.setValue(this._value); + }, + getMaximumValue: function () { + return this._maximumValue; + }, + + setValue:function (value) { + this.setValueWithSendingEvent(value, true); + }, + + getValue:function () { + return this._value; + }, + + setStepValue:function (stepValue) { + if (stepValue <= 0) + throw new Error("cc.ControlStepper.setMaximumValue(): stepValue should be numerically greater than 0."); + this._stepValue = stepValue; + }, + + getStepValue:function () { + return this._stepValue; + }, + + isContinuous:function () { + return this._continuous; + }, + + setValueWithSendingEvent:function (value, send) { + if (value < this._minimumValue) { + value = this._wraps ? this._maximumValue : this._minimumValue; + } else if (value > this._maximumValue) { + value = this._wraps ? this._minimumValue : this._maximumValue; + } + + this._value = value; + + if (!this._wraps) { + this._minusLabel.setColor((value === this._minimumValue) ? cc.CONTROL_STEPPER_LABELCOLOR_DISABLED : cc.CONTROL_STEPPER_LABELCOLOR_ENABLED); + this._plusLabel.setColor((value === this._maximumValue) ? cc.CONTROL_STEPPER_LABELCOLOR_DISABLED : cc.CONTROL_STEPPER_LABELCOLOR_ENABLED); + } + + if (send) { + this.sendActionsForControlEvents(cc.CONTROL_EVENT_VALUECHANGED); + } + }, + + startAutorepeat:function () { + this._autorepeatCount = -1; + this.schedule(this.update, cc.AUTOREPEAT_DELTATIME, cc.REPEAT_FOREVER, cc.AUTOREPEAT_DELTATIME * 3); + }, + + /** Stop the autorepeat. */ + stopAutorepeat:function () { + this.unschedule(this.update); + }, + + update:function (dt) { + this._autorepeatCount++; + + if ((this._autorepeatCount < cc.AUTOREPEAT_INCREASETIME_INCREMENT) && (this._autorepeatCount % 3) !== 0) + return; + + if (this._touchedPart === cc.CONTROL_STEPPER_PARTMINUS) { + this.setValueWithSendingEvent(this._value - this._stepValue, this._continuous); + } else if (this._touchedPart === cc.CONTROL_STEPPER_PARTPLUS) { + this.setValueWithSendingEvent(this._value + this._stepValue, this._continuous); + } + }, + +//#pragma mark CCControlStepper Private Methods + + updateLayoutUsingTouchLocation:function (location) { + if (location.x < this._minusSprite.getContentSize().width + && this._value > this._minimumValue) { + this._touchedPart = cc.CONTROL_STEPPER_PARTMINUS; + this._minusSprite.setColor(cc.color.GRAY); + this._plusSprite.setColor(cc.color.WHITE); + + } else if (location.x >= this._minusSprite.getContentSize().width + && this._value < this._maximumValue) { + this._touchedPart = cc.CONTROL_STEPPER_PARTPLUS; + this._minusSprite.setColor(cc.color.WHITE); + this._plusSprite.setColor(cc.color.GRAY); + + } else { + this._touchedPart = cc.CONTROL_STEPPER_PARTNONE; + this._minusSprite.setColor(cc.color.WHITE); + this._plusSprite.setColor(cc.color.WHITE); + } + }, + + + onTouchBegan:function (touch, event) { + if (!this.isTouchInside(touch) || !this.isEnabled() || !this.isVisible()) { + return false; + } + + var location = this.getTouchLocation(touch); + this.updateLayoutUsingTouchLocation(location); + this._touchInsideFlag = true; + + if (this._autorepeat) { + this.startAutorepeat(); + } + + return true; + }, + + onTouchMoved:function (touch, event) { + if (this.isTouchInside(touch)) { + var location = this.getTouchLocation(touch); + this.updateLayoutUsingTouchLocation(location); + + if (!this._touchInsideFlag) { + this._touchInsideFlag = true; + + if (this._autorepeat) { + this.startAutorepeat(); + } + } + } else { + this._touchInsideFlag = false; + this._touchedPart = cc.CONTROL_STEPPER_PARTNONE; + this._minusSprite.setColor(cc.color.WHITE); + this._plusSprite.setColor(cc.color.WHITE); + if (this._autorepeat) { + this.stopAutorepeat(); + } + } + }, + + onTouchEnded:function (touch, event) { + this._minusSprite.setColor(cc.color.WHITE); + this._plusSprite.setColor(cc.color.WHITE); + + if (this._autorepeat) { + this.stopAutorepeat(); + } + + if (this.isTouchInside(touch)) { + var location = this.getTouchLocation(touch); + this.setValue(this._value + ((location.x < this._minusSprite.getContentSize().width) ? (0.0 - this._stepValue) : this._stepValue)); + } + }, + setMinusSprite:function (sprite) { + this._minusSprite = sprite; + }, + getMinusSprite:function () { + return this._minusSprite; + }, + setPlusSprite:function (sprite) { + this._plusSprite = sprite; + }, + getPlusSprite:function () { + return this._plusSprite; + }, + setMinusLabel:function (sprite) { + this._minusLabel = sprite; + }, + getMinusLabel:function () { + return this._minusLabel; + }, + setPlusLabel:function (sprite) { + this._plusLabel = sprite; + }, + getPlusLabel:function () { + return this._plusLabel; + } +}); + +var _p = cc.ControlStepper.prototype; + +// Extedned properties +/** @expose */ +_p.wraps; +cc.defineGetterSetter(_p, "wraps", _p.getWraps, _p.setWraps); +/** @expose */ +_p.value; +cc.defineGetterSetter(_p, "value", _p.getValue, _p.setValue); +/** @expose */ +_p.minValue; +cc.defineGetterSetter(_p, "minValue", _p.getMinimumValue, _p.setMinimumValue); +/** @expose */ +_p.maxValue; +cc.defineGetterSetter(_p, "maxValue", _p.getMaximumValue, _p.setMaximumValue); +/** @expose */ +_p.stepValue; +cc.defineGetterSetter(_p, "stepValue", _p.getStepValue, _p.setStepValue); +/** @expose */ +_p.continuous; +cc.defineGetterSetter(_p, "continuous", _p.isContinuous); +/** @expose */ +_p.minusSprite; +cc.defineGetterSetter(_p, "minusSprite", _p.getMinusSprite, _p.setMinusSprite); +/** @expose */ +_p.plusSprite; +cc.defineGetterSetter(_p, "plusSprite", _p.getPlusSprite, _p.setPlusSprite); +/** @expose */ +_p.minusLabel; +cc.defineGetterSetter(_p, "minusLabel", _p.getMinusLabel, _p.setMinusLabel); +/** @expose */ +_p.plusLabel; +cc.defineGetterSetter(_p, "plusLabel", _p.getPlusLabel, _p.setPlusLabel); + +_p = null; + +/** + * Creates a cc.ControlStepper + * @param {cc.Sprite} minusSprite + * @param {cc.Sprite} plusSprite + * @returns {ControlStepper} + */ +cc.ControlStepper.create = function (minusSprite, plusSprite) { + return new cc.ControlStepper(minusSprite, plusSprite); +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlSwitch.js b/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlSwitch.js new file mode 100644 index 0000000..99b7368 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlSwitch.js @@ -0,0 +1,425 @@ +/** + * + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2011-2012 cocos2d-x.org + * Copyright (c) 2013-2014 Chukong Technologies Inc. + * + * Copyright 2011 Yannick Loriot. All rights reserved. + * http://yannickloriot.com + * + * 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. + */ + +/** + * CCControlSwitch: Switch control ui component + * @class + * @extends cc.Control + */ +cc.ControlSwitch = cc.Control.extend(/** @lends cc.ControlSwitch# */{ + /** Sprite which represents the view. */ + _switchSprite:null, + _initialTouchXPosition:0, + + _moved:false, + /** A Boolean value that determines the off/on state of the switch. */ + _on:false, + _className:"ControlSwitch", + ctor:function (maskSprite, onSprite, offSprite, thumbSprite, onLabel, offLabel) { + cc.Control.prototype.ctor.call(this); + + offLabel && this.initWithMaskSprite(maskSprite, onSprite, offSprite, thumbSprite, onLabel, offLabel); + }, + + /** Creates a switch with a mask sprite, on/off sprites for on/off states, a thumb sprite and an on/off labels. */ + initWithMaskSprite:function (maskSprite, onSprite, offSprite, thumbSprite, onLabel, offLabel) { + if(!maskSprite) + throw new Error("cc.ControlSwitch.initWithMaskSprite(): maskSprite should be non-null."); + if(!onSprite) + throw new Error("cc.ControlSwitch.initWithMaskSprite(): onSprite should be non-null."); + if(!offSprite) + throw new Error("cc.ControlSwitch.initWithMaskSprite(): offSprite should be non-null."); + if(!thumbSprite) + throw new Error("cc.ControlSwitch.initWithMaskSprite(): thumbSprite should be non-null."); + if (this.init()) { + this._on = true; + + this._switchSprite = new cc.ControlSwitchSprite(); + this._switchSprite.initWithMaskSprite(maskSprite, onSprite, offSprite, thumbSprite, onLabel, offLabel); + this._switchSprite.setPosition(this._switchSprite.getContentSize().width / 2, this._switchSprite.getContentSize().height / 2); + this.addChild(this._switchSprite); + + this.ignoreAnchorPointForPosition(false); + this.setAnchorPoint(0.5, 0.5); + this.setContentSize(this._switchSprite.getContentSize()); + return true; + } + return false; + }, + + setOn:function (isOn, animated) { + animated = animated || false; + this._on = isOn; + var xPosition = (this._on) ? this._switchSprite.getOnPosition() : this._switchSprite.getOffPosition(); + if(animated){ + this._switchSprite.runAction(new cc.ActionTween(0.2, "sliderXPosition", this._switchSprite.getSliderXPosition(),xPosition)); + }else{ + this._switchSprite.setSliderXPosition(xPosition); + } + this.sendActionsForControlEvents(cc.CONTROL_EVENT_VALUECHANGED); + }, + + isOn:function () { + return this._on; + }, + + hasMoved:function () { + return this._moved; + }, + + setEnabled:function (enabled) { + this._enabled = enabled; + + this._switchSprite.setOpacity((enabled) ? 255 : 128); + }, + + locationFromTouch:function (touch) { + var touchLocation = touch.getLocation(); // Get the touch position + touchLocation = this.convertToNodeSpace(touchLocation); // Convert to the node space of this class + + return touchLocation; + }, + + onTouchBegan:function (touch, event) { + if (!this.isTouchInside(touch) || !this.isEnabled()|| !this.isVisible()) { + return false; + } + + this._moved = false; + + var location = this.locationFromTouch(touch); + + this._initialTouchXPosition = location.x - this._switchSprite.getSliderXPosition(); + + this._switchSprite.getThumbSprite().setColor(cc.color.GRAY); + this._switchSprite.needsLayout(); + + return true; + }, + + onTouchMoved:function (touch, event) { + var location = this.locationFromTouch(touch); + location = cc.p(location.x - this._initialTouchXPosition, 0); + + this._moved = true; + + this._switchSprite.setSliderXPosition(location.x); + }, + + onTouchEnded:function (touch, event) { + var location = this.locationFromTouch(touch); + + this._switchSprite.getThumbSprite().setColor(cc.color.WHITE); + + if (this.hasMoved()) { + this.setOn(!(location.x < this._switchSprite.getContentSize().width / 2), true); + } else { + this.setOn(!this._on, true); + } + }, + + onTouchCancelled:function (touch, event) { + var location = this.locationFromTouch(touch); + + this._switchSprite.getThumbSprite().setColor(cc.color.WHITE); + + if (this.hasMoved()) { + this.setOn(!(location.x < this._switchSprite.getContentSize().width / 2), true); + } else { + this.setOn(!this._on, true); + } + } +}); + +/** Creates a switch with a mask sprite, on/off sprites for on/off states and a thumb sprite. + * @deprecated + */ +cc.ControlSwitch.create = function (maskSprite, onSprite, offSprite, thumbSprite, onLabel, offLabel) { + return new cc.ControlSwitch(maskSprite, onSprite, offSprite, thumbSprite, onLabel, offLabel); +}; + +/** + * ControlSwitchSprite: Sprite switch control ui component + * @class + * @extends cc.Sprite + * + * @property {Number} sliderX - Slider's x position + * @property {cc.Point} onPos - The position of slider when switch is on + * @property {cc.Point} offPos - The position of slider when switch is off + * @property {cc.Texture2D} maskTexture - The texture of the mask + * @property {cc.Point} texturePos - The position of the texture + * @property {cc.Point} maskPos - The position of the mask + * @property {cc.Sprite} onSprite - The sprite of switch on + * @property {cc.Sprite} offSprite - The sprite of switch off + * @property {cc.Sprite} thumbSprite - The thumb sprite of the switch control + * @property {cc.LabelTTF} onLabel - The sprite of switch on + * @property {cc.LabelTTF} offLabel - The sprite of switch off + * @property {Number} onSideWidth - <@readonly> The width of the on side of the switch control + * @property {Number} offSideWidth - <@readonly> The width of the off side of the switch control + */ +cc.ControlSwitchSprite = cc.Sprite.extend({ + _sliderXPosition:0, + _onPosition:0, + _offPosition:0, + + _textureLocation:0, + _maskLocation:0, + _maskSize:null, + + _onSprite:null, + _offSprite:null, + _thumbSprite:null, + _onLabel:null, + _offLabel:null, + _clipper:null, + _stencil:null, + _backRT:null, + + ctor:function () { + cc.Sprite.prototype.ctor.call(this); + this._sliderXPosition = 0; + this._onPosition = 0; + this._offPosition = 0; + this._maskLocation = 0; + this._maskSize = cc.size(0, 0); + this._onSprite = null; + this._offSprite = null; + this._thumbSprite = null; + this._onLabel = null; + this._offLabel = null; + }, + + initWithMaskSprite:function (maskSprite, onSprite, offSprite, thumbSprite, onLabel, offLabel) { + if (cc.Sprite.prototype.init.call(this)) { + this.setSpriteFrame(maskSprite.displayFrame()); + // Sets the default values + this._onPosition = 0; + this._offPosition = -onSprite.getContentSize().width + thumbSprite.getContentSize().width / 2; + this._sliderXPosition = this._onPosition; + + this.setOnSprite(onSprite); + this.setOffSprite(offSprite); + this.setThumbSprite(thumbSprite); + this.setOnLabel(onLabel); + this.setOffLabel(offLabel); + + // Set up the mask with the Mask shader + this._stencil = maskSprite; + var maskSize = this._maskSize = this._stencil.getContentSize(); + this._stencil.setPosition(0, 0); + + // Init clipper for mask + this._clipper = new cc.ClippingNode(); + this._clipper.setAnchorPoint(0.5, 0.5); + this._clipper.setPosition(maskSize.width / 2, maskSize.height / 2); + this._clipper.setStencil(this._stencil); + this.addChild(this._clipper); + + this._clipper.addChild(onSprite); + this._clipper.addChild(offSprite); + this._clipper.addChild(onLabel); + this._clipper.addChild(offLabel); + + this.addChild(this._thumbSprite); + + this.needsLayout(); + return true; + } + return false; + }, + + needsLayout:function () { + var maskSize = this._maskSize; + this._onSprite.setPosition( + this._onSprite.getContentSize().width / 2 + this._sliderXPosition - maskSize.width / 2, + this._onSprite.getContentSize().height / 2 - maskSize.height / 2 + ); + this._offSprite.setPosition( + this._onSprite.getContentSize().width + this._offSprite.getContentSize().width / 2 + this._sliderXPosition - maskSize.width / 2, + this._offSprite.getContentSize().height / 2 - maskSize.height / 2 + ); + + if (this._onLabel) { + this._onLabel.setPosition( + this._onSprite.getPositionX() - this._thumbSprite.getContentSize().width / 6, + this._onSprite.getContentSize().height / 2 - maskSize.height / 2 + ); + } + if (this._offLabel) { + this._offLabel.setPosition( + this._offSprite.getPositionX() + this._thumbSprite.getContentSize().width / 6, + this._offSprite.getContentSize().height / 2 - maskSize.height / 2 + ); + } + this._thumbSprite.setPosition( + this._onSprite.getContentSize().width + this._sliderXPosition, + this._maskSize.height / 2 + ); + }, + + setSliderXPosition:function (sliderXPosition) { + if (sliderXPosition <= this._offPosition) { + // Off + sliderXPosition = this._offPosition; + } else if (sliderXPosition >= this._onPosition) { + // On + sliderXPosition = this._onPosition; + } + + this._sliderXPosition = sliderXPosition; + + this.needsLayout(); + }, + getSliderXPosition:function () { + return this._sliderXPosition; + }, + + _getOnSideWidth:function () { + return this._onSprite.getContentSize().width; + }, + + _getOffSideWidth:function () { + return this._offSprite.getContentSize().height; + }, + + updateTweenAction:function (value, key) { + if (key === "sliderXPosition") + this.setSliderXPosition(value); + }, + + setOnPosition:function (onPosition) { + this._onPosition = onPosition; + }, + getOnPosition:function () { + return this._onPosition; + }, + + setOffPosition:function (offPosition) { + this._offPosition = offPosition; + }, + getOffPosition:function () { + return this._offPosition; + }, + + setMaskTexture:function (maskTexture) { + this._stencil.setTexture(maskTexture); + }, + getMaskTexture:function () { + return this._stencil.getTexture(); + }, + + setTextureLocation:function (textureLocation) { + this._textureLocation = textureLocation; + }, + getTextureLocation:function () { + return this._textureLocation; + }, + + setMaskLocation:function (maskLocation) { + this._maskLocation = maskLocation; + }, + getMaskLocation:function () { + return this._maskLocation; + }, + + setOnSprite:function (onSprite) { + this._onSprite = onSprite; + }, + getOnSprite:function () { + return this._onSprite; + }, + + setOffSprite:function (offSprite) { + this._offSprite = offSprite; + }, + getOffSprite:function () { + return this._offSprite; + }, + + setThumbSprite:function (thumbSprite) { + this._thumbSprite = thumbSprite; + }, + getThumbSprite:function () { + return this._thumbSprite; + }, + + setOnLabel:function (onLabel) { + this._onLabel = onLabel; + }, + getOnLabel:function () { + return this._onLabel; + }, + + setOffLabel:function (offLabel) { + this._offLabel = offLabel; + }, + getOffLabel:function () { + return this._offLabel; + } +}); + +var _p = cc.ControlSwitchSprite.prototype; + +/** @expose */ +_p.sliderX; +cc.defineGetterSetter(_p, "sliderX", _p.getSliderXPosition, _p.setSliderXPosition); +/** @expose */ +_p.onPos; +cc.defineGetterSetter(_p, "onPos", _p.getOnPosition, _p.setOnPosition); +/** @expose */ +_p.offPos; +cc.defineGetterSetter(_p, "offPos", _p.getOffPosition, _p.setOffPosition); +/** @expose */ +_p.maskTexture; +cc.defineGetterSetter(_p, "maskTexture", _p.getMaskTexture, _p.setMaskTexture); +/** @expose */ +_p.maskPos; +cc.defineGetterSetter(_p, "maskPos", _p.getMaskLocation, _p.setMaskLocation); +/** @expose */ +_p.onSprite; +cc.defineGetterSetter(_p, "onSprite", _p.getOnSprite, _p.setOnSprite); +/** @expose */ +_p.offSprite; +cc.defineGetterSetter(_p, "offSprite", _p.getOffSprite, _p.setOffSprite); +/** @expose */ +_p.thumbSprite; +cc.defineGetterSetter(_p, "thumbSprite", _p.getThumbSprite, _p.setThumbSprite); +/** @expose */ +_p.onLabel; +cc.defineGetterSetter(_p, "onLabel", _p.getOnLabel, _p.setOnLabel); +/** @expose */ +_p.offLabel; +cc.defineGetterSetter(_p, "offLabel", _p.getOffLabel, _p.setOffLabel); +/** @expose */ +_p.onSideWidth; +cc.defineGetterSetter(_p, "onSideWidth", _p._getOnSideWidth); +/** @expose */ +_p.offSideWidth; +cc.defineGetterSetter(_p, "offSideWidth", _p._getOffSideWidth); + +_p = null; diff --git a/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlUtils.js b/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlUtils.js new file mode 100644 index 0000000..5e803bd --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/gui/control-extension/CCControlUtils.js @@ -0,0 +1,177 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + Copyright 2012 Stewart Hamilton-Arrandale. + http://creativewax.co.uk + + Modified by Yannick Loriot. + http://yannickloriot.com + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * An RGBA color class, its value present as percent + * @param {Number} r + * @param {Number} g + * @param {Number} b + * @param {Number} a + * @constructor + */ +cc.RGBA = function(r,g,b,a){ + this.r = r ; // percent + this.g = g ; // percent + this.b = b ; // percent + this.a = a ; // percent +}; + +cc.HSV = function(h,s,v){ + this.h = h ; // angle in degrees + this.s = s ; // percent + this.v = v ; // percent +}; + +cc.ControlUtils = {}; + +cc.ControlUtils.addSpriteToTargetWithPosAndAnchor = function(spriteName,target,pos,anchor){ + var sprite = new cc.Sprite("#" + spriteName); + + if (!sprite) + return null; + + sprite.setPosition(pos); + sprite.setAnchorPoint(anchor); + target.addChild(sprite); + return sprite; +}; + +cc.ControlUtils.HSVfromRGB = function(rgbaValue){ + var out = new cc.HSV(); + var min, max, delta; + + min = rgbaValue.r < rgbaValue.g ? rgbaValue.r : rgbaValue.g; + min = min < rgbaValue.b ? min : rgbaValue.b; + + max = rgbaValue.r > rgbaValue.g ? rgbaValue.r : rgbaValue.g; + max = max > rgbaValue.b ? max : rgbaValue.b; + + out.v = max; // v + delta = max - min; + if( max > 0.0 ){ + out.s = (delta / max); // s + } else { + // r = g = b = 0 // s = 0, v is undefined + out.s = 0.0; + out.h = -1; // its now undefined (don't know if setting to NAN is a good idea) + return out; + } + + if( rgbaValue.r >= max ){ // > is bogus, just keeps compilor happy + out.h = ( rgbaValue.g - rgbaValue.b ) / delta; // between yellow & magenta + } else { + if( rgbaValue.g >= max ) + out.h = 2.0 + ( rgbaValue.b - rgbaValue.r ) / delta; // between cyan & yellow + else + out.h = 4.0 + ( rgbaValue.r - rgbaValue.g ) / delta; // between magenta & cyan + } + + out.h *= 60.0; // degrees + + if( out.h < 0.0 ) + out.h += 360.0; + + return out; +}; + +cc.ControlUtils.RGBfromHSV = function(hsvValue){ + var hh, p, q, t, ff; + var i; + var out = new cc.RGBA(); + out.a = 1; + + if (hsvValue.s <= 0.0){ // < is bogus, just shuts up warnings + + if (!hsvValue.h){ // value.h == NAN + out.r = hsvValue.v; + out.g = hsvValue.v; + out.b = hsvValue.v; + return out; + } + + // error - should never happen + out.r = 0.0; + out.g = 0.0; + out.b = 0.0; + return out; + } + + hh = hsvValue.h; + if(hh >= 360.0) + hh = 0.0; + hh /= 60.0; + + i = 0 | hh; + ff = hh - i; + p = hsvValue.v * (1.0 - hsvValue.s); + q = hsvValue.v * (1.0 - (hsvValue.s * ff)); + t = hsvValue.v * (1.0 - (hsvValue.s * (1.0 - ff))); + + switch(i) { + case 0: + out.r = hsvValue.v; + out.g = t; + out.b = p; + break; + case 1: + out.r = q; + out.g = hsvValue.v; + out.b = p; + break; + case 2: + out.r = p; + out.g = hsvValue.v; + out.b = t; + break; + + case 3: + out.r = p; + out.g = q; + out.b = hsvValue.v; + break; + case 4: + out.r = t; + out.g = p; + out.b = hsvValue.v; + break; + default: + out.r = hsvValue.v; + out.g = p; + out.b = q; + break; + } + return out; +}; + +cc.ControlUtils.CCRectUnion = function(rect1, rect2){ + return cc.rectUnion(rect1,rect2); +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/gui/control-extension/CCInvocation.js b/frameworks/cocos2d-html5/extensions/gui/control-extension/CCInvocation.js new file mode 100644 index 0000000..82b0189 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/gui/control-extension/CCInvocation.js @@ -0,0 +1,65 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * An Invocation class + * @class + * @extends cc.Class + */ +cc.Invocation = cc.Class.extend(/** @lends cc.Invocation# */{ + _action:null, + _target:null, + _controlEvent:null, + + ctor:function(target,action,controlEvent){ + this._target=target; + this._action=action; + this._controlEvent=controlEvent; + }, + + getAction:function(){ + return this._action; + }, + + getTarget:function(){ + return this._target ; + }, + + getControlEvent:function(){ + return this._controlEvent; + }, + + invoke:function(sender){ + if (this._target && this._action) { + if (cc.isString(this._action)) { + this._target[this._action](sender, this._controlEvent); + } else{ + this._action.call(this._target, sender, this._controlEvent); + } + } + } +}); + diff --git a/frameworks/cocos2d-html5/extensions/gui/control-extension/CCMenuPassive.js b/frameworks/cocos2d-html5/extensions/gui/control-extension/CCMenuPassive.js new file mode 100644 index 0000000..82a3fd3 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/gui/control-extension/CCMenuPassive.js @@ -0,0 +1,415 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The Spacer class + * @class + * @extends cc.Layer + */ +cc.Spacer = cc.Layer.extend(/** @lends cc.Spacer */{}); + +cc.Spacer.verticalSpacer = function (space) { + var pRet = new cc.Spacer(); + pRet.init(); + pRet.setContentSize(0, space); + return pRet; +}; + +cc.Spacer.horizontalSpacer = function (space) { + var pRet = new cc.Spacer(); + pRet.init(); + pRet.setContentSize(space, 0); + return pRet; +}; + +/** + * MenuPassive: The menu passive ui component + * @class + * @extends cc.Layer + */ +cc.MenuPassive = cc.Layer.extend(/** @lends cc.MenuPassive# */{ + + _color:null, + _opacity:0, + _className:"MenuPassive", + + ctor:function () { + }, + + /** Color: conforms with CCRGBAProtocol protocol */ + getColor:function () { + var locColor = this._color; + return cc.color(locColor.r, locColor.g, locColor.b, locColor.a); + }, + setColor:function (color) { + var locColor = this._color; + locColor.r = color.r; + locColor.g = color.g; + locColor.b = color.b; + + if (this._children && this._children.length > 0) { + for (var i = 0; i < this._children.length; i++) { + if (this._children[i]) { + this._children[i].setColor(color); + } + } + } + if (color.a !== undefined && !color.a_undefined) { + this.setOpacity(color.a); + } + }, + + /** Opacity: conforms with CCRGBAProtocol protocol */ + getOpacity:function () { + return this._opacity; + }, + + setOpacity:function (opacity) { + this._opacity = opacity; + + if (this._children && this._children.length > 0) { + for (var i = 0; i < this._children.length; i++) { + if (this._children[i]) { + this._children[i].setOpacity(opacity); + } + } + } + + this._color.a = opacity; + }, + + /** initializes a CCMenu with it's items */ + initWithItems:function (item, args) { + if (this.init()) { + //this.m_bIsTouchEnabled = false; + + // menu in the center of the screen + var winSize = cc.director.getWinSize(); + + // Set the default anchor point + this.ignoreAnchorPointForPosition(true); + this.setAnchorPoint(0.5, 0.5); + this.setContentSize(winSize); + + this.setPosition(winSize.width / 2, winSize.height / 2); + var z = 0; + + if (item) { + this.addChild(item, z); + for (var i = 0; i < args.length; i++) { + if (args[i]) { + z++; + this.addChild(args[i], z); + } + } + } + return true; + } + return false; + }, + + /** align items vertically */ + alignItemsVertically:function () { + this.alignItemsVerticallyWithPadding(cc.DEFAULT_PADDING); + }, + + /** align items vertically with padding + @since v0.7.2 + */ + alignItemsVerticallyWithPadding:function (padding) { + var height = -padding; + + var i; + if (this._children && this._children.length > 0) { + for (i = 0; i < this._children.length; i++) { + if (this._children[i]) { + height += this._children[i].getContentSize().height * this._children[i].getScaleY() + padding; + } + } + } + + var width = 0; + var y = height / 2.0; + if (this._children && this._children.length > 0) { + for (i = 0; i < this._children.length; i++) { + if (this._children[i]) { + width = Math.max(width, this._children[i].getContentSize().width); + this._children[i].setPosition(0, y - this._children[i].getContentSize().height * this._children[i].getScaleY() / 2.0); + y -= this._children[i].getContentSize().height * this._children[i].getScaleY() + padding; + } + } + } + this.setContentSize(width, height); + }, + + /** align items horizontally */ + alignItemsHorizontally:function () { + this.alignItemsHorizontallyWithPadding(cc.DEFAULT_PADDING); + }, + + /** align items horizontally with padding + @since v0.7.2 + */ + alignItemsHorizontallyWithPadding:function (padding) { + var width = -padding; + var i; + if (this._children && this._children.length > 0) { + for (i = 0; i < this._children.length; i++) { + if (this._children[i]) { + width += this._children[i].getContentSize().width * this._children[i].getScaleX() + padding; + } + } + } + + var height = 0; + var x = -width / 2.0; + if (this._children && this._children.length > 0) { + for (i = 0; i < this._children.length; i++) { + if (this._children[i]) { + height = Math.max(height, this._children[i].getContentSize().height); + this._children[i].setPosition(x + this._children[i].getContentSize().width * this._children[i].getScaleX() / 2.0, 0); + x += this._children[i].getContentSize().width * this._children[i].getScaleX() + padding; + } + } + } + this.setContentSize(width, height); + }, + + /** align items in rows of columns */ + alignItemsInColumns:function (columns) { + var rows = []; + var i; + for (i = 1; i < arguments.length; i++) { + rows.push(arguments[i]); + } + + var height = -5; + var row = 0; + var rowHeight = 0; + var columnsOccupied = 0; + var rowColumns; + + var tmp; + if (this._children && this._children.length > 0) { + for (i = 0; i < this._children.length; i++) { + if (this._children[i]) { + if(row >= rows.length){ + cc.log("cc.MenuPassive.alignItemsInColumns(): invalid row index"); + continue; + } + + rowColumns = rows[row]; + // can not have zero columns on a row + if(!rowColumns) { + cc.log("cc.MenuPassive.alignItemsInColumns(): can not have zero columns on a row"); + continue; + } + + tmp = this._children[i].getContentSize().height; + rowHeight = 0 | ((rowHeight >= tmp || (tmp == null)) ? rowHeight : tmp); + + ++columnsOccupied; + if (columnsOccupied >= rowColumns) { + height += rowHeight + 5; + + columnsOccupied = 0; + rowHeight = 0; + ++row; + } + } + } + } + + // check if too many rows/columns for available menu items + //cc.assert(!columnsOccupied, ""); //? + + var winSize = cc.director.getWinSize(); + + row = 0; + rowHeight = 0; + rowColumns = 0; + var w = 0.0; + var x = 0.0; + var y = (height / 2); + if (this._children && this._children.length > 0) { + for (i = 0; i < this._children.length; i++) { + if (this._children[i]) { + if (rowColumns === 0) { + rowColumns = rows[row]; + w = winSize.width / (1 + rowColumns); + x = w; + } + + tmp = this._children[i].getContentSize().height; + rowHeight = 0 | ((rowHeight >= tmp || (tmp == null)) ? rowHeight : tmp); + + this._children[i].setPosition(x - winSize.width / 2, + y - this._children[i].getContentSize().height / 2); + + x += w; + ++columnsOccupied; + + if (columnsOccupied >= rowColumns) { + y -= rowHeight + 5; + + columnsOccupied = 0; + rowColumns = 0; + rowHeight = 0; + ++row; + } + } + } + } + }, + + /** align items in columns of rows */ + alignItemsInRows:function (rows) { + var columns = []; + var i; + for (i = 1; i < arguments.length; i++) { + columns.push(arguments[i]); + } + + var columnWidths = []; + var columnHeights = []; + + var width = -10; + var columnHeight = -5; + var column = 0; + var columnWidth = 0; + var rowsOccupied = 0; + var columnRows; + + var tmp; + if (this._children && this._children.length > 0) { + for (i = 0; i < this._children.length; i++) { + if (this._children[i]) { + // check if too many menu items for the amount of rows/columns + if(column >= columns.length){ + cc.log("cc.MenuPassive.alignItemsInRows(): invalid row index"); + continue; + } + + columnRows = columns[column]; + // can't have zero rows on a column + if(!columnRows) { + cc.log("cc.MenuPassive.alignItemsInColumns(): can't have zero rows on a column"); + continue; + } + + // columnWidth = fmaxf(columnWidth, [item contentSize].width); + tmp = this._children[i].getContentSize().width; + columnWidth = 0 | ((columnWidth >= tmp || (tmp == null)) ? columnWidth : tmp); + + columnHeight += 0 | (this._children[i].getContentSize().height + 5); + ++rowsOccupied; + + if (rowsOccupied >= columnRows) { + columnWidths.push(columnWidth); + columnHeights.push(columnHeight); + width += columnWidth + 10; + + rowsOccupied = 0; + columnWidth = 0; + columnHeight = -5; + ++column; + } + } + } + } + + // check if too many rows/columns for available menu items. + //cc.assert(!rowsOccupied, ""); //? + + var winSize = cc.director.getWinSize(); + + column = 0; + columnWidth = 0; + columnRows = null; + var x = (-width / 2); + var y = 0.0; + if (this._children && this._children.length > 0) { + for (i = 0; i < this._children.length; i++) { + if (this._children[i]) { + if (columnRows == null) { + columnRows = columns[column]; + y = columnHeights[column]; + } + + // columnWidth = fmaxf(columnWidth, [item contentSize].width); + tmp = this._children[i].getContentSize().width; + columnWidth = 0 | ((columnWidth >= tmp || (tmp == null)) ? columnWidth : tmp); + + this._children[i].setPosition(x + columnWidths[column] / 2, y - winSize.height / 2); + + y -= this._children[i].getContentSize().height + 10; + ++rowsOccupied; + + if (rowsOccupied >= columnRows) { + x += columnWidth + 5; + rowsOccupied = 0; + columnRows = 0; + columnWidth = 0; + ++column; + } + } + } + } + }, + + //RGBA protocol + setOpacityModifyRGB:function (bValue) { + }, + isOpacityModifyRGB:function () { + return false; + } +}); + +/** creates an empty CCMenu */ +cc.MenuPassive.create = function (item) { + if (!item) { + item = null; + } + + var argArr = []; + for (var i = 1; i < arguments.length; i++) { + argArr.push(arguments[i]); + } + + var pRet = new cc.MenuPassive(); + if (pRet && pRet.initWithItems(item, argArr)) { + return pRet; + } + return null; +}; + +/** creates a CCMenu with it's item, then use addChild() to add + * other items. It is used for script, it can't init with undetermined + * number of variables. + */ +cc.MenuPassive.createWithItem = function (item) { + return cc.MenuPassive.create(item, null); +}; \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/gui/scrollview/CCScrollView.js b/frameworks/cocos2d-html5/extensions/gui/scrollview/CCScrollView.js new file mode 100644 index 0000000..bedba0a --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/gui/scrollview/CCScrollView.js @@ -0,0 +1,850 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2010 Sangwoo Im + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * @ignore + */ +cc.SCROLLVIEW_DIRECTION_NONE = -1; + +cc.SCROLLVIEW_DIRECTION_HORIZONTAL = 0; + +cc.SCROLLVIEW_DIRECTION_VERTICAL = 1; + +cc.SCROLLVIEW_DIRECTION_BOTH = 2; + +var SCROLL_DEACCEL_RATE = 0.95; +var SCROLL_DEACCEL_DIST = 1.0; +var BOUNCE_DURATION = 0.15; +var INSET_RATIO = 0.2; +var MOVE_INCH = 7.0 / 160.0; +var BOUNCE_BACK_FACTOR = 0.35; + +cc.convertDistanceFromPointToInch = function (pointDis) { + var eglViewer = cc.view; + var factor = (eglViewer.getScaleX() + eglViewer.getScaleY()) / 2; + return (pointDis * factor) / 160; // CCDevice::getDPI() default value +}; + +cc.ScrollViewDelegate = cc.Class.extend({ + scrollViewDidScroll: function (view) { + }, + scrollViewDidZoom: function (view) { + } +}); + +/** + * ScrollView support for cocos2d -x. + * It provides scroll view functionalities to cocos2d projects natively. + * @class + * @extends cc.Layer + * + * @property {cc.Point} minOffset - <@readonly> The current container's minimum offset + * @property {cc.Point} maxOffset - <@readonly> The current container's maximum offset + * @property {Boolean} bounceable - Indicate whether the scroll view is bounceable + * @property {cc.Size} viewSize - The size of the scroll view + * @property {cc.Layer} container - The inside container of the scroll view + * @property {Number} direction - The direction allowed to scroll: cc.SCROLLVIEW_DIRECTION_BOTH by default, or cc.SCROLLVIEW_DIRECTION_NONE | cc.SCROLLVIEW_DIRECTION_HORIZONTAL | cc.SCROLLVIEW_DIRECTION_VERTICAL + * @property {cc.ScrollViewDelegate} delegate - The inside container of the scroll view + * @property {Boolean} clippingToBounds - Indicate whether the scroll view clips its children + */ +cc.ScrollView = cc.Layer.extend(/** @lends cc.ScrollView# */{ + _zoomScale: 0, + _minZoomScale: 0, + _maxZoomScale: 0, + _delegate: null, + _direction: cc.SCROLLVIEW_DIRECTION_BOTH, + _dragging: false, + _contentOffset: null, + _container: null, + _touchMoved: false, + _maxInset: null, + _minInset: null, + _bounceable: false, + _clippingToBounds: false, + _scrollDistance: null, + _touchPoint: null, + _touchLength: 0, + _touches: null, + _viewSize: null, + _minScale: 0, + _maxScale: 0, + + //scissor rect for parent, just for restoring GL_SCISSOR_BOX + _parentScissorRect: null, + _scissorRestored: false, + + // cache object + _tmpViewRect: null, + _touchListener: null, + _className: "ScrollView", + + /** + * @contructor + * @param size + * @param container + * @returns {ScrollView} + */ + ctor: function (size, container) { + cc.Layer.prototype.ctor.call(this); + this._contentOffset = cc.p(0, 0); + this._maxInset = cc.p(0, 0); + this._minInset = cc.p(0, 0); + this._scrollDistance = cc.p(0, 0); + this._touchPoint = cc.p(0, 0); + this._touches = []; + this._viewSize = cc.size(0, 0); + this._parentScissorRect = new cc.Rect(0, 0, 0, 0); + this._tmpViewRect = new cc.Rect(0, 0, 0, 0); + + if (container != undefined) + this.initWithViewSize(size, container); + else + this.initWithViewSize(cc.size(200, 200), null); + + }, + + init: function () { + return this.initWithViewSize(cc.size(200, 200), null); + }, + + /** + * initialized whether success or fail + * @param {cc.Size} size + * @param {cc.Node} container + * @return {Boolean} + */ + initWithViewSize: function (size, container) { + var pZero = cc.p(0, 0); + if (cc.Layer.prototype.init.call(this)) { + if (!container && !this._container) { + container = new cc.Layer(); + } + if (container) { + this.setContainer(container); + } + this.setViewSize(size); + + this.setTouchEnabled(true); + this._touches.length = 0; + this._delegate = null; + this._bounceable = true; + this._clippingToBounds = true; + + //this._container.setContentSize(CCSizeZero); + this._direction = cc.SCROLLVIEW_DIRECTION_BOTH; + this._container.setPosition(pZero); + this._touchLength = 0.0; + + this._minScale = this._maxScale = 1.0; + return true; + } + return false; + }, + + visit: function (parent) { + var cmd = this._renderCmd, parentCmd = parent ? parent._renderCmd : null; + + // quick return if not visible + if (!this._visible) { + cmd._propagateFlagsDown(parentCmd); + return; + } + + var renderer = cc.renderer; + cmd.visit(parentCmd); + + if (this._clippingToBounds) { + renderer.pushRenderCommand(cmd.startCmd); + } + + var i, children = this._children, len = children.length; + if (len > 0) { + if (this._reorderChildDirty) { + this.sortAllChildren(); + } + for (i = 0; i < len; i++) { + children[i].visit(this); + } + } + + if (this._clippingToBounds) { + renderer.pushRenderCommand(cmd.endCmd); + } + cmd._dirtyFlag = 0; + }, + + /** + * Sets a new content offset. It ignores max/min offset. It just sets what's given. (just like UIKit's UIScrollView) + * + * @param {cc.Point} offset new offset + * @param {Number} [animated=] If true, the view will scroll to the new offset + */ + setContentOffset: function (offset, animated) { + if (animated) { //animate scrolling + this.setContentOffsetInDuration(offset, BOUNCE_DURATION); + return; + } + if (!this._bounceable) { + var minOffset = this.minContainerOffset(); + var maxOffset = this.maxContainerOffset(); + + offset.x = Math.max(minOffset.x, Math.min(maxOffset.x, offset.x)); + offset.y = Math.max(minOffset.y, Math.min(maxOffset.y, offset.y)); + } + + this._container.setPosition(offset); + var locDelegate = this._delegate; + if (locDelegate != null && locDelegate.scrollViewDidScroll) { + locDelegate.scrollViewDidScroll(this); + } + + }, + + getContentOffset: function () { + var locPos = this._container.getPosition(); + return cc.p(locPos.x, locPos.y); + }, + + /** + *

Sets a new content offset. It ignores max/min offset. It just sets what's given. (just like UIKit's UIScrollView)
+ * You can override the animation duration with this method. + *

+ * @param {cc.Point} offset new offset + * @param {Number} dt animation duration + */ + setContentOffsetInDuration: function (offset, dt) { + var scroll = cc.moveTo(dt, offset); + var expire = cc.callFunc(this._stoppedAnimatedScroll, this); + this._container.runAction(cc.sequence(scroll, expire)); + this.schedule(this._performedAnimatedScroll); + }, + + /** + * Sets a new scale and does that for a predefined duration. + * + * @param {Number} scale a new scale vale + * @param {Boolean} [animated=null] if YES, scaling is animated + */ + setZoomScale: function (scale, animated) { + if (animated) { + this.setZoomScaleInDuration(scale, BOUNCE_DURATION); + return; + } + + var locContainer = this._container; + if (locContainer.getScale() !== scale) { + var oldCenter, newCenter; + var center; + + if (this._touchLength === 0.0) { + var locViewSize = this._viewSize; + center = cc.p(locViewSize.width * 0.5, locViewSize.height * 0.5); + center = this.convertToWorldSpace(center); + } else + center = this._touchPoint; + + oldCenter = locContainer.convertToNodeSpace(center); + locContainer.setScale(Math.max(this._minScale, Math.min(this._maxScale, scale))); + newCenter = locContainer.convertToWorldSpace(oldCenter); + + var offset = cc.pSub(center, newCenter); + if (this._delegate && this._delegate.scrollViewDidZoom) + this._delegate.scrollViewDidZoom(this); + this.setContentOffset(cc.pAdd(locContainer.getPosition(), offset)); + } + }, + + getZoomScale: function () { + return this._container.getScale(); + }, + + /** + * Sets a new scale for container in a given duration. + * + * @param {Number} s a new scale value + * @param {Number} dt animation duration + */ + setZoomScaleInDuration: function (s, dt) { + if (dt > 0) { + var locScale = this._container.getScale(); + if (locScale !== s) { + var scaleAction = cc.actionTween(dt, "zoomScale", locScale, s); + this.runAction(scaleAction); + } + } else { + this.setZoomScale(s); + } + }, + + /** + * Returns the current container's minimum offset. You may want this while you animate scrolling by yourself + * @return {cc.Point} Returns the current container's minimum offset. + */ + minContainerOffset: function () { + var locContainer = this._container; + var locContentSize = locContainer.getContentSize(), locViewSize = this._viewSize; + return cc.p(locViewSize.width - locContentSize.width * locContainer.getScaleX(), + locViewSize.height - locContentSize.height * locContainer.getScaleY()); + }, + + /** + * Returns the current container's maximum offset. You may want this while you animate scrolling by yourself + * @return {cc.Point} Returns the current container's maximum offset. + */ + maxContainerOffset: function () { + return cc.p(0.0, 0.0); + }, + + /** + * Determines if a given node's bounding box is in visible bounds + * @param {cc.Node} node + * @return {Boolean} YES if it is in visible bounds + */ + isNodeVisible: function (node) { + var offset = this.getContentOffset(); + var size = this.getViewSize(); + var scale = this.getZoomScale(); + + var viewRect = cc.rect(-offset.x / scale, -offset.y / scale, size.width / scale, size.height / scale); + + return cc.rectIntersectsRect(viewRect, node.getBoundingBox()); + }, + + /** + * Provided to make scroll view compatible with SWLayer's pause method + */ + pause: function (sender) { + this._container.pause(); + var selChildren = this._container.getChildren(); + for (var i = 0; i < selChildren.length; i++) { + selChildren[i].pause(); + } + this._super(); + }, + + /** + * Provided to make scroll view compatible with SWLayer's resume method + */ + resume: function (sender) { + var selChildren = this._container.getChildren(); + for (var i = 0, len = selChildren.length; i < len; i++) { + selChildren[i].resume(); + } + this._container.resume(); + this._super(); + }, + + isDragging: function () { + return this._dragging; + }, + isTouchMoved: function () { + return this._touchMoved; + }, + isBounceable: function () { + return this._bounceable; + }, + setBounceable: function (bounceable) { + this._bounceable = bounceable; + }, + + /** + *

+ * size to clip. CCNode boundingBox uses contentSize directly.
+ * It's semantically different what it actually means to common scroll views.
+ * Hence, this scroll view will use a separate size property. + *

+ */ + getViewSize: function () { + return this._viewSize; + }, + + setViewSize: function (size) { + this._viewSize = size; + cc.Node.prototype.setContentSize.call(this, size); + }, + + getContainer: function () { + return this._container; + }, + + setContainer: function (container) { + // Make sure that 'm_pContainer' has a non-NULL value since there are + // lots of logic that use 'm_pContainer'. + if (!container) + return; + + this.removeAllChildren(true); + + this._container = container; + container.ignoreAnchorPointForPosition(false); + container.setAnchorPoint(0, 0); + + this.addChild(container); + this.setViewSize(this._viewSize); + }, + + /** + * direction allowed to scroll. CCScrollViewDirectionBoth by default. + */ + getDirection: function () { + return this._direction; + }, + setDirection: function (direction) { + this._direction = direction; + }, + + getDelegate: function () { + return this._delegate; + }, + setDelegate: function (delegate) { + this._delegate = delegate; + }, + + /** override functions */ + // optional + onTouchBegan: function (touch, event) { + for (var c = this; c != null; c = c.parent) { + if (!c.isVisible()) + return false; + } + //var frameOriginal = this.getParent().convertToWorldSpace(this.getPosition()); + //var frame = cc.rect(frameOriginal.x, frameOriginal.y, this._viewSize.width, this._viewSize.height); + var frame = this._getViewRect(); + + //dispatcher does not know about clipping. reject touches outside visible bounds. + var locContainer = this._container; + var locPoint = locContainer.convertToWorldSpace(locContainer.convertTouchToNodeSpace(touch)); + var locTouches = this._touches; + if (locTouches.length > 2 || this._touchMoved || !cc.rectContainsPoint(frame, locPoint)) + return false; + + locTouches.push(touch); + //} + + if (locTouches.length === 1) { // scrolling + this._touchPoint = this.convertTouchToNodeSpace(touch); + this._touchMoved = false; + this._dragging = true; //dragging started + this._scrollDistance.x = 0; + this._scrollDistance.y = 0; + this._touchLength = 0.0; + } else if (locTouches.length === 2) { + this._touchPoint = cc.pMidpoint(this.convertTouchToNodeSpace(locTouches[0]), + this.convertTouchToNodeSpace(locTouches[1])); + this._touchLength = cc.pDistance(locContainer.convertTouchToNodeSpace(locTouches[0]), + locContainer.convertTouchToNodeSpace(locTouches[1])); + this._dragging = false; + } + return true; + }, + + onTouchMoved: function (touch, event) { + if (!this.isVisible()) + return; + + this.setNodeDirty(); + + if (this._touches.length === 1 && this._dragging) { // scrolling + this._touchMoved = true; + //var frameOriginal = this.getParent().convertToWorldSpace(this.getPosition()); + //var frame = cc.rect(frameOriginal.x, frameOriginal.y, this._viewSize.width, this._viewSize.height); + var frame = this._getViewRect(); + + //var newPoint = this.convertTouchToNodeSpace(this._touches[0]); + var newPoint = this.convertTouchToNodeSpace(touch); + var moveDistance = cc.pSub(newPoint, this._touchPoint); + + var dis = 0.0, locDirection = this._direction, pos; + if (locDirection === cc.SCROLLVIEW_DIRECTION_VERTICAL) { + dis = moveDistance.y; + pos = this._container.getPositionY(); + if (!(this.minContainerOffset().y <= pos && pos <= this.maxContainerOffset().y)) + moveDistance.y *= BOUNCE_BACK_FACTOR; + } else if (locDirection === cc.SCROLLVIEW_DIRECTION_HORIZONTAL) { + dis = moveDistance.x; + pos = this._container.getPositionX(); + if (!(this.minContainerOffset().x <= pos && pos <= this.maxContainerOffset().x)) + moveDistance.x *= BOUNCE_BACK_FACTOR; + } else { + dis = Math.sqrt(moveDistance.x * moveDistance.x + moveDistance.y * moveDistance.y); + + pos = this._container.getPositionY(); + var _minOffset = this.minContainerOffset(), _maxOffset = this.maxContainerOffset(); + if (!(_minOffset.y <= pos && pos <= _maxOffset.y)) + moveDistance.y *= BOUNCE_BACK_FACTOR; + + pos = this._container.getPositionX(); + if (!(_minOffset.x <= pos && pos <= _maxOffset.x)) + moveDistance.x *= BOUNCE_BACK_FACTOR; + } + + if (!this._touchMoved && Math.abs(cc.convertDistanceFromPointToInch(dis)) < MOVE_INCH) { + //CCLOG("Invalid movement, distance = [%f, %f], disInch = %f", moveDistance.x, moveDistance.y); + return; + } + + if (!this._touchMoved) { + moveDistance.x = 0; + moveDistance.y = 0; + } + + this._touchPoint = newPoint; + this._touchMoved = true; + + if (this._dragging) { + switch (locDirection) { + case cc.SCROLLVIEW_DIRECTION_VERTICAL: + moveDistance.x = 0.0; + break; + case cc.SCROLLVIEW_DIRECTION_HORIZONTAL: + moveDistance.y = 0.0; + break; + default: + break; + } + + var locPosition = this._container.getPosition(); + var newX = locPosition.x + moveDistance.x; + var newY = locPosition.y + moveDistance.y; + + this._scrollDistance = moveDistance; + this.setContentOffset(cc.p(newX, newY)); + } + } else if (this._touches.length === 2 && !this._dragging) { + var len = cc.pDistance(this._container.convertTouchToNodeSpace(this._touches[0]), + this._container.convertTouchToNodeSpace(this._touches[1])); + this.setZoomScale(this.getZoomScale() * len / this._touchLength); + } + }, + + onTouchEnded: function (touch, event) { + if (!this.isVisible()) + return; + + if (this._touches.length === 1 && this._touchMoved) + this.schedule(this._deaccelerateScrolling); + + this._touches.length = 0; + this._dragging = false; + this._touchMoved = false; + }, + + onTouchCancelled: function (touch, event) { + if (!this.isVisible()) + return; + + this._touches.length = 0; + this._dragging = false; + this._touchMoved = false; + }, + + setContentSize: function (size, height) { + if (this.getContainer() !== null) { + if (height === undefined) + this.getContainer().setContentSize(size); + else + this.getContainer().setContentSize(size, height); + this.updateInset(); + } + }, + _setWidth: function (value) { + var container = this.getContainer(); + if (container !== null) { + container._setWidth(value); + this.updateInset(); + } + }, + _setHeight: function (value) { + var container = this.getContainer(); + if (container !== null) { + container._setHeight(value); + this.updateInset(); + } + }, + + getContentSize: function () { + return this._container.getContentSize(); + }, + + updateInset: function () { + if (this.getContainer() !== null) { + var locViewSize = this._viewSize; + var tempOffset = this.maxContainerOffset(); + this._maxInset.x = tempOffset.x + locViewSize.width * INSET_RATIO; + this._maxInset.y = tempOffset.y + locViewSize.height * INSET_RATIO; + tempOffset = this.minContainerOffset(); + this._minInset.x = tempOffset.x - locViewSize.width * INSET_RATIO; + this._minInset.y = tempOffset.y - locViewSize.height * INSET_RATIO; + } + }, + + /** + * Determines whether it clips its children or not. + */ + isClippingToBounds: function () { + return this._clippingToBounds; + }, + + setClippingToBounds: function (clippingToBounds) { + this._clippingToBounds = clippingToBounds; + }, + + addChild: function (child, zOrder, tag) { + if (!child) + throw new Error("child must not nil!"); + + zOrder = zOrder || child.getLocalZOrder(); + tag = tag || child.getTag(); + + //child.ignoreAnchorPointForPosition(false); + //child.setAnchorPoint(0, 0); + if (this._container !== child) { + this._container.addChild(child, zOrder, tag); + } else { + cc.Layer.prototype.addChild.call(this, child, zOrder, tag); + } + }, + + isTouchEnabled: function () { + return this._touchListener !== null; + }, + + setTouchEnabled: function (e) { + if (this._touchListener) + cc.eventManager.removeListener(this._touchListener); + this._touchListener = null; + if (!e) { + this._dragging = false; + this._touchMoved = false; + this._touches.length = 0; + } else { + var listener = cc.EventListener.create({ + event: cc.EventListener.TOUCH_ONE_BY_ONE + }); + if (this.onTouchBegan) + listener.onTouchBegan = this.onTouchBegan.bind(this); + if (this.onTouchMoved) + listener.onTouchMoved = this.onTouchMoved.bind(this); + if (this.onTouchEnded) + listener.onTouchEnded = this.onTouchEnded.bind(this); + if (this.onTouchCancelled) + listener.onTouchCancelled = this.onTouchCancelled.bind(this); + this._touchListener = listener; + cc.eventManager.addListener(listener, this); + } + }, + + /** + * Init this object with a given size to clip its content. + * + * @param size view size + * @return initialized scroll view object + */ + _initWithViewSize: function (size) { + return null; + }, + + /** + * Relocates the container at the proper offset, in bounds of max/min offsets. + * + * @param animated If YES, relocation is animated + */ + _relocateContainer: function (animated) { + var min = this.minContainerOffset(); + var max = this.maxContainerOffset(); + var locDirection = this._direction; + + var oldPoint = this._container.getPosition(); + var newX = oldPoint.x; + var newY = oldPoint.y; + if (locDirection === cc.SCROLLVIEW_DIRECTION_BOTH || locDirection === cc.SCROLLVIEW_DIRECTION_HORIZONTAL) { + newX = Math.max(newX, min.x); + newX = Math.min(newX, max.x); + } + + if (locDirection === cc.SCROLLVIEW_DIRECTION_BOTH || locDirection === cc.SCROLLVIEW_DIRECTION_VERTICAL) { + newY = Math.min(newY, max.y); + newY = Math.max(newY, min.y); + } + + if (newY !== oldPoint.y || newX !== oldPoint.x) { + this.setContentOffset(cc.p(newX, newY), animated); + } + }, + /** + * implements auto-scrolling behavior. change SCROLL_DEACCEL_RATE as needed to choose
+ * deacceleration speed. it must be less than 1.0. + * + * @param {Number} dt delta + */ + _deaccelerateScrolling: function (dt) { + if (this._dragging) { + this.unschedule(this._deaccelerateScrolling); + return; + } + + var maxInset, minInset; + var oldPosition = this._container.getPosition(); + var locScrollDistance = this._scrollDistance; + this._container.setPosition(oldPosition.x + locScrollDistance.x, oldPosition.y + locScrollDistance.y); + if (this._bounceable) { + maxInset = this._maxInset; + minInset = this._minInset; + } else { + maxInset = this.maxContainerOffset(); + minInset = this.minContainerOffset(); + } + + //check to see if offset lies within the inset bounds + var newX = this._container.getPositionX(); + var newY = this._container.getPositionY(); + + locScrollDistance.x = locScrollDistance.x * SCROLL_DEACCEL_RATE; + locScrollDistance.y = locScrollDistance.y * SCROLL_DEACCEL_RATE; + + this.setContentOffset(cc.p(newX, newY)); + + if ((Math.abs(locScrollDistance.x) <= SCROLL_DEACCEL_DIST && + Math.abs(locScrollDistance.y) <= SCROLL_DEACCEL_DIST) || + newY > maxInset.y || newY < minInset.y || + newX > maxInset.x || newX < minInset.x || + newX === maxInset.x || newX === minInset.x || + newY === maxInset.y || newY === minInset.y) { + this.unschedule(this._deaccelerateScrolling); + this._relocateContainer(true); + } + }, + /** + * This method makes sure auto scrolling causes delegate to invoke its method + */ + _performedAnimatedScroll: function (dt) { + if (this._dragging) { + this.unschedule(this._performedAnimatedScroll); + return; + } + + if (this._delegate && this._delegate.scrollViewDidScroll) + this._delegate.scrollViewDidScroll(this); + }, + /** + * Expire animated scroll delegate calls + */ + _stoppedAnimatedScroll: function (node) { + this.unschedule(this._performedAnimatedScroll); + // After the animation stopped, "scrollViewDidScroll" should be invoked, this could fix the bug of lack of tableview cells. + if (this._delegate && this._delegate.scrollViewDidScroll) { + this._delegate.scrollViewDidScroll(this); + } + }, + + /** + * Zoom handling + */ + _handleZoom: function () { + }, + + _getViewRect: function () { + var screenPos = this.convertToWorldSpace(cc.p(0, 0)); + var locViewSize = this._viewSize; + + var scaleX = this.getScaleX(); + var scaleY = this.getScaleY(); + + for (var p = this._parent; p != null; p = p.getParent()) { + scaleX *= p.getScaleX(); + scaleY *= p.getScaleY(); + } + + // Support negative scaling. Not doing so causes intersectsRect calls + // (eg: to check if the touch was within the bounds) to return false. + // Note, CCNode::getScale will assert if X and Y scales are different. + if (scaleX < 0) { + screenPos.x += locViewSize.width * scaleX; + scaleX = -scaleX; + } + if (scaleY < 0) { + screenPos.y += locViewSize.height * scaleY; + scaleY = -scaleY; + } + + var locViewRect = this._tmpViewRect; + locViewRect.x = screenPos.x; + locViewRect.y = screenPos.y; + locViewRect.width = locViewSize.width * scaleX; + locViewRect.height = locViewSize.height * scaleY; + return locViewRect; + }, + + _createRenderCmd: function () { + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) { + return new cc.ScrollView.CanvasRenderCmd(this); + } else { + return new cc.ScrollView.WebGLRenderCmd(this); + } + } +}); + +var _p = cc.ScrollView.prototype; + +// Extended properties +/** @expose */ +_p.minOffset; +cc.defineGetterSetter(_p, "minOffset", _p.minContainerOffset); +/** @expose */ +_p.maxOffset; +cc.defineGetterSetter(_p, "maxOffset", _p.maxContainerOffset); +/** @expose */ +_p.bounceable; +cc.defineGetterSetter(_p, "bounceable", _p.isBounceable, _p.setBounceable); +/** @expose */ +_p.viewSize; +cc.defineGetterSetter(_p, "viewSize", _p.getViewSize, _p.setViewSize); +/** @expose */ +_p.container; +cc.defineGetterSetter(_p, "container", _p.getContainer, _p.setContainer); +/** @expose */ +_p.direction; +cc.defineGetterSetter(_p, "direction", _p.getDirection, _p.setDirection); +/** @expose */ +_p.delegate; +cc.defineGetterSetter(_p, "delegate", _p.getDelegate, _p.setDelegate); +/** @expose */ +_p.clippingToBounds; +cc.defineGetterSetter(_p, "clippingToBounds", _p.isClippingToBounds, _p.setClippingToBounds); + +_p = null; + +/** + * Returns an autoreleased scroll view object. + * @deprecated + * @param {cc.Size} size view size + * @param {cc.Node} container parent object + * @return {cc.ScrollView} scroll view object + */ +cc.ScrollView.create = function (size, container) { + return new cc.ScrollView(size, container); +}; diff --git a/frameworks/cocos2d-html5/extensions/gui/scrollview/CCScrollViewCanvasRenderCmd.js b/frameworks/cocos2d-html5/extensions/gui/scrollview/CCScrollViewCanvasRenderCmd.js new file mode 100644 index 0000000..0cc063f --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/gui/scrollview/CCScrollViewCanvasRenderCmd.js @@ -0,0 +1,64 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + cc.ScrollView.CanvasRenderCmd = function (renderable) { + this._layerCmdCtor(renderable); + this._needDraw = false; + + this.startCmd = new cc.CustomRenderCmd(this, this._startCmd); + this.startCmd._canUseDirtyRegion = true; + this.endCmd = new cc.CustomRenderCmd(this, this._endCmd); + this.endCmd._canUseDirtyRegion = true; + }; + + var proto = cc.ScrollView.CanvasRenderCmd.prototype = Object.create(cc.Layer.CanvasRenderCmd.prototype); + proto.constructor = cc.ScrollView.CanvasRenderCmd; + + proto._startCmd = function (ctx, scaleX, scaleY) { + var node = this._node; + var wrapper = ctx || cc._renderContext, context = wrapper.getContext(); + wrapper.save(); + + if (node._clippingToBounds) { + this._scissorRestored = false; + wrapper.setTransform(this._worldTransform, scaleX, scaleY); + + var locScaleX = node.getScaleX(), locScaleY = node.getScaleY(); + + var getWidth = (node._viewSize.width * locScaleX); + var getHeight = (node._viewSize.height * locScaleY); + + context.beginPath(); + context.rect(0, 0, getWidth, -getHeight); + context.closePath(); + context.clip(); + } + }; + + proto._endCmd = function (wrapper) { + wrapper = wrapper || cc._renderContext; + wrapper.restore(); + }; +})(); diff --git a/frameworks/cocos2d-html5/extensions/gui/scrollview/CCScrollViewWebGLRenderCmd.js b/frameworks/cocos2d-html5/extensions/gui/scrollview/CCScrollViewWebGLRenderCmd.js new file mode 100644 index 0000000..3d837db --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/gui/scrollview/CCScrollViewWebGLRenderCmd.js @@ -0,0 +1,71 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + cc.ScrollView.WebGLRenderCmd = function (renderable) { + this._layerCmdCtor(renderable); + this._needDraw = false; + + this.startCmd = new cc.CustomRenderCmd(this, this._startCmd); + this.endCmd = new cc.CustomRenderCmd(this, this._endCmd); + }; + + var proto = cc.ScrollView.WebGLRenderCmd.prototype = Object.create(cc.Layer.WebGLRenderCmd.prototype); + proto.constructor = cc.ScrollView.WebGLRenderCmd; + + proto._startCmd = function () { + var node = this._node; + var EGLViewer = cc.view; + var frame = node._getViewRect(); + if (EGLViewer.isScissorEnabled()) { + node._scissorRestored = true; + node._parentScissorRect = EGLViewer.getScissorRect(); + //set the intersection of m_tParentScissorRect and frame as the new scissor rect + if (cc.rectIntersection(frame, node._parentScissorRect)) { + var locPSRect = node._parentScissorRect; + var x = Math.max(frame.x, locPSRect.x); + var y = Math.max(frame.y, locPSRect.y); + var xx = Math.min(frame.x + frame.width, locPSRect.x + locPSRect.width); + var yy = Math.min(frame.y + frame.height, locPSRect.y + locPSRect.height); + EGLViewer.setScissorInPoints(x, y, xx - x, yy - y); + } + } else { + var ctx = cc._renderContext; + ctx.enable(ctx.SCISSOR_TEST); + //clip + EGLViewer.setScissorInPoints(frame.x, frame.y, frame.width, frame.height); + } + }; + + proto._endCmd = function () { + var node = this._node; + if (node._scissorRestored) { //restore the parent's scissor rect + var rect = node._parentScissorRect; + cc.view.setScissorInPoints(rect.x, rect.y, rect.width, rect.height); + } else { + var ctx = cc._renderContext; + ctx.disable(ctx.SCISSOR_TEST); + } + }; +})(); diff --git a/frameworks/cocos2d-html5/extensions/gui/scrollview/CCSorting.js b/frameworks/cocos2d-html5/extensions/gui/scrollview/CCSorting.js new file mode 100644 index 0000000..cda5fba --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/gui/scrollview/CCSorting.js @@ -0,0 +1,239 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2010 Sangwoo Im + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The sortable object interface + * @class + * @extends cc.Class + */ +cc.SortableObject = cc.Class.extend(/** @lends cc.SortableObject */{ + setObjectID:function (objectId) { + }, + getObjectID:function () { + return 0; + } +}); + +/** + * The SortedObject class + * @class + * @extends cc.SortableObject + */ +cc.SortedObject = cc.SortableObject.extend(/** @lends cc.SortedObject */{ + _objectID:0, + + ctor:function () { + this._objectID = 0; + }, + + setObjectID:function (objectID) { + this._objectID = objectID; + }, + + getObjectID:function () { + return this._objectID; + } +}); + +var _compareObject = function (val1, val2) { + return (val1.getObjectID() - val2.getObjectID()); +}; + +/** + * Array for object sorting utils + * @class + * @extend cc.Class + */ +cc.ArrayForObjectSorting = cc.Class.extend(/** @lends cc.ArrayForObjectSorting# */{ + _saveObjectArr:null, + + ctor:function () { + this._saveObjectArr = []; + }, + /** + * Inserts a given object into array. + * + * Inserts a given object into array with key and value that are used in + * sorting. "value" must respond to message, compare:, which returns + * (NSComparisonResult). If it does not respond to the message, it is appended. + * If the compare message does not result NSComparisonResult, sorting behavior + * is not defined. It ignores duplicate entries and inserts next to it. + * + * @function + * @param {Object} addObject Object to insert + */ + insertSortedObject:function (addObject) { + if(!addObject) + throw new Error("cc.ArrayForObjectSorting.insertSortedObject(): addObject should be non-null."); + var idx = this.indexOfSortedObject(addObject); + this.insertObject(addObject, idx); + }, + + /*! + * Removes an object in array. + * + * Removes an object with given key and value. If no object is found in array + * with the key and value, no action is taken. + * + * @function + * @param {Object} delObject Object to remove + */ + removeSortedObject:function (delObject) { + if (this.count() === 0) { + return; + } + + var idx = this.indexOfSortedObject(delObject); + if (idx < this.count() && idx !== cc.INVALID_INDEX) { + var foundObj = this.objectAtIndex(idx); + if (foundObj.getObjectID() === delObject.getObjectID()) { + this.removeObjectAtIndex(idx); + } + } + }, + + /*! + * Sets a new value of the key for the given object. + * + * In case where sorting value must be changed, this message must be sent to + * keep consistency of being sorted. If it is changed externally, it must be + * sorted completely again. + * + * @function + * @param {Number} tag Tag to set + * @param {Object} setObject The object which would be set + */ + setObjectID_ofSortedObject:function (tag, setObject) { + var idx = this.indexOfSortedObject(setObject); + if (idx < this.count() && idx !== cc.INVALID_INDEX) { + var foundObj = this.objectAtIndex(idx); + if (foundObj.getObjectID() === setObject.getObjectID()) { + this.removeObjectAtIndex(idx); + foundObj.setObjectID(tag); + this.insertSortedObject(foundObj); + } + } + }, + + objectWithObjectID:function (tag) { + if (this.count() === 0) { + return null; + } + var foundObj = new cc.SortedObject(); + foundObj.setObjectID(tag); + + var idx = this.indexOfSortedObject(foundObj); + if (idx < this.count() && idx !== cc.INVALID_INDEX) { + foundObj = this.objectAtIndex(idx); + if (foundObj.getObjectID() !== tag) + foundObj = null; + } + return foundObj; + }, + + /*! + * Returns an object with given key and value. + * + * Returns an object with given key and value. If no object is found, + * it returns nil. + * + * @function + * @param {Number} tag Tag to locate object + * @return {Object|null} + */ + getObjectWithObjectID:function (tag) { + return null; + }, + + /*! + * Returns an index of the object with given key and value. + * + * Returns the index of an object with given key and value. + * If no object is found, it returns an index at which the given object value + * would have been located. If object must be located at the end of array, + * it returns the length of the array, which is out of bound. + * + * @function + * @param {Number} idxObj Id to locate object + * @return {Number} index of an object found + */ + indexOfSortedObject:function (idxObj) { + var idx = 0; + if (idxObj) { + // CCObject* pObj = (CCObject*)bsearch((CCObject*)&object, data.arr, data.num, sizeof(CCObject*), _compareObject); + // FIXME: need to use binary search to improve performance + var uPrevObjectID = 0; + var uOfSortObjectID = idxObj.getObjectID(); + + var locObjectArr = this._saveObjectArr; + for (var i = 0; i < locObjectArr.length; i++) { + var pSortableObj = locObjectArr[i]; + var curObjectID = pSortableObj.getObjectID(); + if ((uOfSortObjectID === curObjectID) || + (uOfSortObjectID >= uPrevObjectID && uOfSortObjectID < curObjectID)) { + break; + } + uPrevObjectID = curObjectID; + idx++; + } + } else { + idx = cc.INVALID_INDEX; + } + return idx; + }, + + //implement array method + count:function () { + return this._saveObjectArr.length; + }, + + lastObject:function () { + var locObjectArr = this._saveObjectArr; + if (locObjectArr.length === 0) + return null; + return locObjectArr[locObjectArr.length - 1]; + }, + + objectAtIndex:function (idx) { + return this._saveObjectArr[idx]; + }, + + addObject:function (addObj) { + this._saveObjectArr.push(addObj); + this._saveObjectArr.sort(_compareObject); + }, + + removeObjectAtIndex:function (idx) { + this._saveObjectArr.splice(idx, 1); + this._saveObjectArr.sort(_compareObject); + }, + + insertObject:function (addObj, idx) { + this._saveObjectArr.splice(idx, 0, addObj); + this._saveObjectArr.sort(_compareObject); + } +}); diff --git a/frameworks/cocos2d-html5/extensions/gui/scrollview/CCTableView.js b/frameworks/cocos2d-html5/extensions/gui/scrollview/CCTableView.js new file mode 100644 index 0000000..a197ada --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/gui/scrollview/CCTableView.js @@ -0,0 +1,724 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2010 Sangwoo Im + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The constant value of the fill style from top to bottom for cc.TableView + * @constant + * @type {number} + */ +cc.TABLEVIEW_FILL_TOPDOWN = 0; + +/** + * The constant value of the fill style from bottom to top for cc.TableView + * @constant + * @type {number} + */ +cc.TABLEVIEW_FILL_BOTTOMUP = 1; + +/** + * Abstract class for SWTableView cell node + * @class + * @abstract + * @extends cc.Node + * + * @property {Number} objectId - The index used internally by SWTableView and its subclasses + */ +cc.TableViewCell = cc.Node.extend(/** @lends cc.TableViewCell# */{ + _idx: 0, + _className: "TableViewCell", + + /** + * The index used internally by SWTableView and its subclasses + */ + getIdx: function () { + return this._idx; + }, + setIdx: function (idx) { + this._idx = idx; + }, + + /** + * Cleans up any resources linked to this cell and resets idx property. + */ + reset: function () { + this._idx = cc.INVALID_INDEX; + }, + + setObjectID: function (idx) { + this._idx = idx; + }, + getObjectID: function () { + return this._idx; + } +}); + +var _p = cc.TableViewCell.prototype; + +/** @expose */ +_p.objectId; +cc.defineGetterSetter(_p, "objectId", _p.getObjectID, _p.setObjectID); + +_p = null; + +/** + * Sole purpose of this delegate is to single touch event in this version. + */ +cc.TableViewDelegate = cc.ScrollViewDelegate.extend(/** @lends cc.TableViewDelegate# */{ + /** + * Delegate to respond touch event + * + * @param {cc.TableView} table table contains the given cell + * @param {cc.TableViewCell} cell cell that is touched + */ + tableCellTouched: function (table, cell) { + }, + + /** + * Delegate to respond a table cell press event. + * + * @param {cc.TableView} table table contains the given cell + * @param {cc.TableViewCell} cell cell that is pressed + */ + tableCellHighlight: function (table, cell) { + }, + + /** + * Delegate to respond a table cell release event + * + * @param {cc.TableView} table table contains the given cell + * @param {cc.TableViewCell} cell cell that is pressed + */ + tableCellUnhighlight: function (table, cell) { + + }, + + /** + *

+ * Delegate called when the cell is about to be recycled. Immediately
+ * after this call the cell will be removed from the scene graph and
+ * recycled. + *

+ * @param table table contains the given cell + * @param cell cell that is pressed + */ + tableCellWillRecycle: function (table, cell) { + + } +}); + +/** + * Data source that governs table backend data. + */ +cc.TableViewDataSource = cc.Class.extend(/** @lends cc.TableViewDataSource# */{ + /** + * cell size for a given index + * @param {cc.TableView} table table to hold the instances of Class + * @param {Number} idx the index of a cell to get a size + * @return {cc.Size} size of a cell at given index + */ + tableCellSizeForIndex: function (table, idx) { + return this.cellSizeForTable(table); + }, + /** + * cell height for a given table. + * + * @param {cc.TableView} table table to hold the instances of Class + * @return {cc.Size} cell size + */ + cellSizeForTable: function (table) { + return cc.size(0, 0); + }, + + /** + * a cell instance at a given index + * @param {cc.TableView} table table to hold the instances of Class + * @param idx index to search for a cell + * @return {cc.TableView} cell found at idx + */ + tableCellAtIndex: function (table, idx) { + return null; + }, + + /** + * Returns number of cells in a given table view. + * @param {cc.TableView} table table to hold the instances of Class + * @return {Number} number of cells + */ + numberOfCellsInTableView: function (table) { + return 0; + } +}); + +/** + * UITableView counterpart for cocos2d for iphone. + * this is a very basic, minimal implementation to bring UITableView-like component into cocos2d world. + * + * @class + * @extends cc.ScrollView + * + * @property {cc.TableViewDataSource} dataSource - The data source of the table view + * @property {cc.TableViewDelegate} delegate - The event delegate of the table view + * @property {Number} verticalFillOrder - The index to determine how cell is ordered and filled in the view + * + */ +cc.TableView = cc.ScrollView.extend(/** @lends cc.TableView# */{ + _vOrdering: null, + _indices: null, + _cellsFreed: null, + _dataSource: null, + _tableViewDelegate: null, + _oldDirection: null, + _cellsPositions: null, //vector with all cell positions + _touchedCell: null, + + /** + * The + * @param dataSource + * @param size + * @param container + */ + ctor: function (dataSource, size, container) { + cc.ScrollView.prototype.ctor.call(this); + this._oldDirection = cc.SCROLLVIEW_DIRECTION_NONE; + this._cellsPositions = []; + + this.initWithViewSize(size, container); + this.setDataSource(dataSource); + this._updateCellPositions(); + this._updateContentSize(); + }, + + __indexFromOffset: function (offset) { + var low = 0; + var high = this._dataSource.numberOfCellsInTableView(this) - 1; + var search; + switch (this.getDirection()) { + case cc.SCROLLVIEW_DIRECTION_HORIZONTAL: + search = offset.x; + break; + default: + search = offset.y; + break; + } + + var locCellsPositions = this._cellsPositions; + while (high >= low) { + var index = 0 | (low + (high - low) / 2); + var cellStart = locCellsPositions[index]; + var cellEnd = locCellsPositions[index + 1]; + + if (search >= cellStart && search <= cellEnd) { + return index; + } else if (search < cellStart) { + high = index - 1; + } else { + low = index + 1; + } + } + + if (low <= 0) + return 0; + return -1; + }, + + _indexFromOffset: function (offset) { + var locOffset = {x: offset.x, y: offset.y}; + var locDataSource = this._dataSource; + var maxIdx = locDataSource.numberOfCellsInTableView(this) - 1; + + if (this._vOrdering === cc.TABLEVIEW_FILL_TOPDOWN) + locOffset.y = this.getContainer().getContentSize().height - locOffset.y; + + var index = this.__indexFromOffset(locOffset); + if (index !== -1) { + index = Math.max(0, index); + if (index > maxIdx) + index = cc.INVALID_INDEX; + } + return index; + }, + + __offsetFromIndex: function (index) { + var offset; + switch (this.getDirection()) { + case cc.SCROLLVIEW_DIRECTION_HORIZONTAL: + offset = cc.p(this._cellsPositions[index], 0); + break; + default: + offset = cc.p(0, this._cellsPositions[index]); + break; + } + + return offset; + }, + + _offsetFromIndex: function (index) { + var offset = this.__offsetFromIndex(index); + + var cellSize = this._dataSource.tableCellSizeForIndex(this, index); + if (this._vOrdering === cc.TABLEVIEW_FILL_TOPDOWN) + offset.y = this.getContainer().getContentSize().height - offset.y - cellSize.height; + + return offset; + }, + + _updateCellPositions: function () { + var cellsCount = this._dataSource.numberOfCellsInTableView(this); + var locCellsPositions = this._cellsPositions; + + if (cellsCount > 0) { + var currentPos = 0; + var cellSize, locDataSource = this._dataSource; + for (var i = 0; i < cellsCount; i++) { + locCellsPositions[i] = currentPos; + cellSize = locDataSource.tableCellSizeForIndex(this, i); + switch (this.getDirection()) { + case cc.SCROLLVIEW_DIRECTION_HORIZONTAL: + currentPos += cellSize.width; + break; + default: + currentPos += cellSize.height; + break; + } + } + this._cellsPositions[cellsCount] = currentPos;//1 extra value allows us to get right/bottom of the last cell + } + }, + + _updateContentSize: function () { + var size = cc.size(0, 0); + + var cellsCount = this._dataSource.numberOfCellsInTableView(this); + + if (cellsCount > 0) { + var maxPosition = this._cellsPositions[cellsCount]; + switch (this.getDirection()) { + case cc.SCROLLVIEW_DIRECTION_HORIZONTAL: + size = cc.size(maxPosition, this._viewSize.height); + break; + default: + size = cc.size(this._viewSize.width, maxPosition); + break; + } + } + + this.setContentSize(size); + + if (this._oldDirection !== this._direction) { + if (this._direction === cc.SCROLLVIEW_DIRECTION_HORIZONTAL) { + this.setContentOffset(cc.p(0, 0)); + } else { + this.setContentOffset(cc.p(0, this.minContainerOffset().y)); + } + this._oldDirection = this._direction; + } + }, + + _moveCellOutOfSight: function (cell) { + if (this._tableViewDelegate && this._tableViewDelegate.tableCellWillRecycle) + this._tableViewDelegate.tableCellWillRecycle(this, cell); + + this._cellsFreed.addObject(cell); + this._cellsUsed.removeSortedObject(cell); + cc.arrayRemoveObject(this._indices, cell.getIdx()); + + cell.reset(); + if (cell.getParent() === this.getContainer()) { + this.getContainer().removeChild(cell, true); + } + }, + + _setIndexForCell: function (index, cell) { + cell.setAnchorPoint(0, 0); + cell.setPosition(this._offsetFromIndex(index)); + cell.setIdx(index); + }, + + _addCellIfNecessary: function (cell) { + if (cell.getParent() !== this.getContainer()) { + this.getContainer().addChild(cell); + } + this._cellsUsed.insertSortedObject(cell); + var locIndices = this._indices, addIdx = cell.getIdx(); + if (locIndices.indexOf(addIdx) === -1) { + locIndices.push(addIdx); + //sort + locIndices.sort(function (a, b) { + return a - b; + }); + } + }, + + /** + * data source + */ + getDataSource: function () { + return this._dataSource; + }, + setDataSource: function (source) { + this._dataSource = source; + }, + + /** + * delegate + */ + getDelegate: function () { + return this._tableViewDelegate; + }, + + setDelegate: function (delegate) { + this._tableViewDelegate = delegate; + }, + + /** + * determines how cell is ordered and filled in the view. + */ + setVerticalFillOrder: function (fillOrder) { + if (this._vOrdering !== fillOrder) { + this._vOrdering = fillOrder; + if (this._cellsUsed.count() > 0) { + this.reloadData(); + } + } + }, + getVerticalFillOrder: function () { + return this._vOrdering; + }, + + initWithViewSize: function (size, container) { + if (cc.ScrollView.prototype.initWithViewSize.call(this, size, container)) { + this._cellsUsed = new cc.ArrayForObjectSorting(); + this._cellsFreed = new cc.ArrayForObjectSorting(); + this._indices = []; + this._tableViewDelegate = null; + this._vOrdering = cc.TABLEVIEW_FILL_BOTTOMUP; + this.setDirection(cc.SCROLLVIEW_DIRECTION_VERTICAL); + + cc.ScrollView.prototype.setDelegate.call(this, this); + return true; + } + return false; + }, + + /** + * Updates the content of the cell at a given index. + * + * @param idx index to find a cell + */ + updateCellAtIndex: function (idx) { + if (idx === cc.INVALID_INDEX || idx > this._dataSource.numberOfCellsInTableView(this) - 1) + return; + + var cell = this.cellAtIndex(idx); + if (cell) + this._moveCellOutOfSight(cell); + + cell = this._dataSource.tableCellAtIndex(this, idx); + this._setIndexForCell(idx, cell); + this._addCellIfNecessary(cell); + }, + + /** + * Inserts a new cell at a given index + * + * @param idx location to insert + */ + insertCellAtIndex: function (idx) { + if (idx === cc.INVALID_INDEX || idx > this._dataSource.numberOfCellsInTableView(this) - 1) + return; + + var newIdx, locCellsUsed = this._cellsUsed; + var cell = locCellsUsed.objectWithObjectID(idx); + if (cell) { + newIdx = locCellsUsed.indexOfSortedObject(cell); + for (var i = newIdx; i < locCellsUsed.count(); i++) { + cell = locCellsUsed.objectAtIndex(i); + this._setIndexForCell(cell.getIdx() + 1, cell); + } + } + + //insert a new cell + cell = this._dataSource.tableCellAtIndex(this, idx); + this._setIndexForCell(idx, cell); + this._addCellIfNecessary(cell); + + this._updateCellPositions(); + this._updateContentSize(); + }, + + /** + * Removes a cell at a given index + * + * @param idx index to find a cell + */ + removeCellAtIndex: function (idx) { + if (idx === cc.INVALID_INDEX || idx > this._dataSource.numberOfCellsInTableView(this) - 1) + return; + + var cell = this.cellAtIndex(idx); + if (!cell) + return; + + var locCellsUsed = this._cellsUsed; + var newIdx = locCellsUsed.indexOfSortedObject(cell); + + //remove first + this._moveCellOutOfSight(cell); + cc.arrayRemoveObject(this._indices, idx); + this._updateCellPositions(); + + for (var i = locCellsUsed.count() - 1; i > newIdx; i--) { + cell = locCellsUsed.objectAtIndex(i); + this._setIndexForCell(cell.getIdx() - 1, cell); + } + }, + + /** + * reloads data from data source. the view will be refreshed. + */ + reloadData: function () { + this._oldDirection = cc.SCROLLVIEW_DIRECTION_NONE; + var locCellsUsed = this._cellsUsed, locCellsFreed = this._cellsFreed, locContainer = this.getContainer(); + for (var i = 0, len = locCellsUsed.count(); i < len; i++) { + var cell = locCellsUsed.objectAtIndex(i); + + if (this._tableViewDelegate && this._tableViewDelegate.tableCellWillRecycle) + this._tableViewDelegate.tableCellWillRecycle(this, cell); + + locCellsFreed.addObject(cell); + cell.reset(); + if (cell.getParent() === locContainer) + locContainer.removeChild(cell, true); + } + + this._indices = []; + this._cellsUsed = new cc.ArrayForObjectSorting(); + + this._updateCellPositions(); + this._updateContentSize(); + if (this._dataSource.numberOfCellsInTableView(this) > 0) + this.scrollViewDidScroll(this); + + this.setNodeDirty(); + }, + + /** + * Dequeues a free cell if available. nil if not. + * + * @return {TableViewCell} free cell + */ + dequeueCell: function () { + if (this._cellsFreed.count() === 0) { + return null; + } else { + var cell = this._cellsFreed.objectAtIndex(0); + this._cellsFreed.removeObjectAtIndex(0); + return cell; + } + }, + + /** + * Returns an existing cell at a given index. Returns nil if a cell is nonexistent at the moment of query. + * + * @param idx index + * @return {cc.TableViewCell} a cell at a given index + */ + cellAtIndex: function (idx) { + var i = this._indices.indexOf(idx); + if (i === -1) + return null; + return this._cellsUsed.objectWithObjectID(idx); + }, + + scrollViewDidScroll: function (view) { + var locDataSource = this._dataSource; + var countOfItems = locDataSource.numberOfCellsInTableView(this); + if (0 === countOfItems) + return; + + if (this._tableViewDelegate !== null && this._tableViewDelegate.scrollViewDidScroll) + this._tableViewDelegate.scrollViewDidScroll(this); + + var idx = 0, locViewSize = this._viewSize, locContainer = this.getContainer(); + var offset = this.getContentOffset(); + offset.x *= -1; + offset.y *= -1; + + var maxIdx = Math.max(countOfItems - 1, 0); + + if (this._vOrdering === cc.TABLEVIEW_FILL_TOPDOWN) + offset.y = offset.y + locViewSize.height / locContainer.getScaleY(); + var startIdx = this._indexFromOffset(offset); + if (startIdx === cc.INVALID_INDEX) + startIdx = countOfItems - 1; + + if (this._vOrdering === cc.TABLEVIEW_FILL_TOPDOWN) + offset.y -= locViewSize.height / locContainer.getScaleY(); + else + offset.y += locViewSize.height / locContainer.getScaleY(); + offset.x += locViewSize.width / locContainer.getScaleX(); + + var endIdx = this._indexFromOffset(offset); + if (endIdx === cc.INVALID_INDEX) + endIdx = countOfItems - 1; + + var cell, locCellsUsed = this._cellsUsed; + if (locCellsUsed.count() > 0) { + cell = locCellsUsed.objectAtIndex(0); + idx = cell.getIdx(); + while (idx < startIdx) { + this._moveCellOutOfSight(cell); + if (locCellsUsed.count() > 0) { + cell = locCellsUsed.objectAtIndex(0); + idx = cell.getIdx(); + } else + break; + } + } + + if (locCellsUsed.count() > 0) { + cell = locCellsUsed.lastObject(); + idx = cell.getIdx(); + while (idx <= maxIdx && idx > endIdx) { + this._moveCellOutOfSight(cell); + if (locCellsUsed.count() > 0) { + cell = locCellsUsed.lastObject(); + idx = cell.getIdx(); + } else + break; + } + } + + var locIndices = this._indices; + for (var i = startIdx; i <= endIdx; i++) { + if (locIndices.indexOf(i) !== -1) + continue; + this.updateCellAtIndex(i); + } + }, + + scrollViewDidZoom: function (view) { + }, + + onTouchEnded: function (touch, event) { + if (!this.isVisible()) + return; + + if (this._touchedCell) { + var bb = this.getBoundingBox(); + var tmpOrigin = cc.p(bb.x, bb.y); + tmpOrigin = this._parent.convertToWorldSpace(tmpOrigin); + bb.x = tmpOrigin.x; + bb.y = tmpOrigin.y; + var locTableViewDelegate = this._tableViewDelegate; + if (cc.rectContainsPoint(bb, touch.getLocation()) && locTableViewDelegate !== null) { + if (locTableViewDelegate.tableCellUnhighlight) + locTableViewDelegate.tableCellUnhighlight(this, this._touchedCell); + if (locTableViewDelegate.tableCellTouched) + locTableViewDelegate.tableCellTouched(this, this._touchedCell); + } + this._touchedCell = null; + } + cc.ScrollView.prototype.onTouchEnded.call(this, touch, event); + }, + + onTouchBegan: function (touch, event) { + for (var c = this; c != null; c = c.parent) { + if (!c.isVisible()) + return false; + } + + var touchResult = cc.ScrollView.prototype.onTouchBegan.call(this, touch, event); + + if (this._touches.length === 1) { + var index, point; + + point = this.getContainer().convertTouchToNodeSpace(touch); + + index = this._indexFromOffset(point); + if (index === cc.INVALID_INDEX) + this._touchedCell = null; + else + this._touchedCell = this.cellAtIndex(index); + + if (this._touchedCell && this._tableViewDelegate !== null && this._tableViewDelegate.tableCellHighlight) + this._tableViewDelegate.tableCellHighlight(this, this._touchedCell); + } else if (this._touchedCell) { + if (this._tableViewDelegate !== null && this._tableViewDelegate.tableCellUnhighlight) + this._tableViewDelegate.tableCellUnhighlight(this, this._touchedCell); + this._touchedCell = null; + } + + return touchResult; + }, + + onTouchMoved: function (touch, event) { + cc.ScrollView.prototype.onTouchMoved.call(this, touch, event); + + if (this._touchedCell && this.isTouchMoved()) { + if (this._tableViewDelegate !== null && this._tableViewDelegate.tableCellUnhighlight) + this._tableViewDelegate.tableCellUnhighlight(this, this._touchedCell); + this._touchedCell = null; + } + }, + + onTouchCancelled: function (touch, event) { + cc.ScrollView.prototype.onTouchCancelled.call(this, touch, event); + + if (this._touchedCell) { + if (this._tableViewDelegate !== null && this._tableViewDelegate.tableCellUnhighlight) + this._tableViewDelegate.tableCellUnhighlight(this, this._touchedCell); + this._touchedCell = null; + } + } +}); + +var _p = cc.TableView.prototype; + +/** @expose */ +_p.dataSource; +cc.defineGetterSetter(_p, "dataSource", _p.getDataSource, _p.setDataSource); +/** @expose */ +_p.delegate; +cc.defineGetterSetter(_p, "delegate", _p.getDelegate, _p.setDelegate); +/** @expose */ +_p.verticalFillOrder; +cc.defineGetterSetter(_p, "verticalFillOrder", _p.getVerticalFillOrder, _p.setVerticalFillOrder); + +_p = null; + +/** + * An initialized table view object + * @deprecated + * @param {cc.TableViewDataSource} dataSource data source; + * @param {cc.Size} size view size + * @param {cc.Node} [container] parent object for cells + * @return {cc.TableView} table view + */ +cc.TableView.create = function (dataSource, size, container) { + return new cc.TableView(dataSource, size, container); +}; diff --git a/frameworks/cocos2d-html5/extensions/runtime/CCLoaderLayer.js b/frameworks/cocos2d-html5/extensions/runtime/CCLoaderLayer.js new file mode 100644 index 0000000..5877918 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/runtime/CCLoaderLayer.js @@ -0,0 +1,988 @@ +/**************************************************************************** + Copyright (c) 2014-2015 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ +(function () { + +var INT_MAX = Number.MAX_VALUE; +var GROUP_JSON_PATH = "group.json"; + +cc.LoaderLayer = cc.Layer.extend({ + _backgroundSprite: null, + _progressBackgroundSprite: null, + _progressBarSprite: null, + _logoSprite: null, + _titleSprite: null, + _groupname: null, + _callback: null, + _selector: null, + _preloadCount: 0, + _isPreloadFromFailed: false, + _progressOriginalWidth: 0, + _isLandScape: false, + _scaleFactor: null, + + ctor: function (config) { + this._super(); + if (config) { + cc.LoaderLayer.setConfig(config); + } + }, + onEnter: function () { + this._super(); + this.initView(); + var config = cc.LoaderLayer._finalConfig; + if (config.onEnter) { + config.onEnter(this); + } + }, + onExit: function () { + this._super(); + var config = cc.LoaderLayer._finalConfig; + if (config.logo.action) { + config.logo.action.release(); + } + if(config.title.action){ + config.title.action.release(); + } + if (config.onExit) { + config.onExit(this); + } + }, + initView: function () { + var config = cc.LoaderLayer._finalConfig; + this._contentLayer = new cc.Layer(); + this._isLandScape = cc.winSize.width > cc.winSize.height; + this._scaleFactor = !cc.LoaderLayer._useDefaultSource ? 1 : cc.winSize.width > cc.winSize.height ? cc.winSize.width / 720 : cc.winSize.width / 480; + + //background + this.backgroundSprite = new cc.Sprite(config.background.res); + this.addChild(this.backgroundSprite); + this.backgroundSprite.x = 0, this.backgroundSprite.y = 0, this.backgroundSprite.anchorX = 0, this.backgroundSprite.anchorY = 0; + if (cc.LoaderLayer._useDefaultSource) { + this.backgroundSprite.scaleX = cc.winSize.width / this.backgroundSprite.width; + this.backgroundSprite.scaleY = cc.winSize.height / this.backgroundSprite.height; + } + + //title + if (config.title.show) { + this.titleSprite = new cc.Sprite(config.title.res); + var defaultTitlePosition = cc.pAdd(cc.visibleRect.center, cc.p(0, this._scaleFactor < 1 ? 0 : this._isLandScape ? -80 : 30)); + this.titleSprite.setPosition(config.title.position ? config.title.position : defaultTitlePosition); + this._contentLayer.addChild(this.titleSprite); + if (config.title.action) { + this.titleSprite.runAction(config.title.action); + } + } + + //logo + if (config.logo.show) { + this.logoSprite = new cc.Sprite(config.logo.res); + var defaultLogoPosition = cc.pAdd(cc.visibleRect.top, cc.p(0, this._scaleFactor < 1 ? 0 : -this.logoSprite.height / 2 - (this._isLandScape ? 56 : 76))); + this.logoSprite.setPosition(config.logo.position ? config.logo.position : defaultLogoPosition); + this._contentLayer.addChild(this.logoSprite); + if (config.logo.action) { + this.logoSprite.runAction(config.logo.action); + } + } + + //progressbar + if (config.progressBar.show) { + this.progressBarSprite = new cc.Sprite(config.progressBar.res); + this._progressOriginalWidth = this.progressBarSprite.width; + this.progressBackgroundSprite = new cc.Sprite(config.progressBar.barBackgroundRes); + this.progressBarSprite.anchorX = 0; + this.progressBarSprite.anchorY = 0; + if (cc.LoaderLayer._isDefaultProgress) { + this._barPoint = new cc.Sprite(config.progressBar.barPoint); + this.progressBarSprite.addChild(this._barPoint); + } + if (config.progressBar.barBackgroundRes == null) { + this.progressBackgroundSprite.setTextureRect(cc.rect(0, 0, this.progressBarSprite.width, this.progressBarSprite.height)); + } + if (config.progressBar.offset == null) { + var deltaProgressWithX = (this.progressBackgroundSprite.width - this.progressBarSprite.width) / 2; + var deltaProgressWithY = (this.progressBackgroundSprite.height - this.progressBarSprite.height) / 2; + config.progressBar.offset = cc.p(deltaProgressWithX, deltaProgressWithY); + } + this.progressBarSprite.setPosition(config.progressBar.offset); + this.progressBackgroundSprite.addChild(this.progressBarSprite); + var defaultProgressPosition = cc.pAdd(cc.visibleRect.bottom, cc.p(0, this.progressBarSprite.height / 2 + this._isLandScape ? 60 : 80)); + this.progressBackgroundSprite.setPosition(config.progressBar.position ? config.progressBar.position : defaultProgressPosition); + this._contentLayer.addChild(this.progressBackgroundSprite); + this._setProgress(0); + } + + //tips + if (config.tips.show) { + this.tipsLabel = new cc.LabelTTF("100%", "Arial", config.tips.fontSize); + this.tipsLabel.setColor(config.tips.color ? config.tips.color : cc.color(255, 255, 255)); + this.tipsLabel.setPosition(config.tips.position ? config.tips.position : this.progressBackgroundSprite ? cc.p(this.progressBackgroundSprite.x, this.progressBackgroundSprite.y + this.progressBackgroundSprite.height / 2 + 20) : cc.pAdd(cc.visibleRect.bottom, cc.p(0, 100))); + this._contentLayer.addChild(this.tipsLabel); + } + this.addChild(this._contentLayer); + if (this._scaleFactor < 1) { + this._contentLayer.setScale(this._scaleFactor); + this._contentLayer.setPosition(cc.pAdd(this._contentLayer.getPosition(), cc.p(0, -50))); + } + + }, + _setProgress: function (percent) { + if (this.progressBarSprite) { + percent < 1 ? percent : 1; + var width = percent * this._progressOriginalWidth; + this.progressBarSprite.setTextureRect(cc.rect(0, 0, width, this.progressBarSprite.height)); + if (cc.LoaderLayer._isDefaultProgress) { + this._barPoint.setPosition(cc.p(this.progressBarSprite.width, this.progressBarSprite.height / 2)); + } + } + }, + setTipsString: function (str) { + if (this.tipsLabel != null) { + this.tipsLabel.setString(str); + } + }, + getProgressBar: function () { + return this.progressBarSprite; + }, + getTipsLabel: function () { + return this.tipsLabel; + }, + getLogoSprite: function () { + return this.logoSprite; + }, + getTitleSprite: function () { + return this.titleSprite; + }, + updateGroup: function (groupname, callback, target) { + this._groupname = groupname; + this._callback = callback; + this._selector = target; + }, + _resetLoadingLabel: function () { + this.setTipsString(""); + this._setProgress(0); + }, + _preloadSource: function () { + cc.log("cc.LoaderLayer is preloading resource group: " + this._groupname); + this._resetLoadingLabel(); + if (cc.sys.isNative) { + cc.Loader.preload(this._groupname, this._preload_native, this); + } else { + this._preload_html5(); + } + }, + _preload_html5: function () { + var res = ""; + var groupIndex = []; + var config = cc.LoaderLayer._finalConfig; + var groups = cc.LoaderLayer.groups; + if (cc.isString(this._groupname)) { + if (this._groupname.indexOf(".") != -1) { + res = [this._groupname]; + } else { + res = groups[this._groupname]; + } + } else if (cc.isArray(this._groupname)) { + res = []; + for (var i = 0; i < this._groupname.length; i++) { + var group = groups[this._groupname[i]]; + var files = group && group.files; + var preCount = i > 0 ? groupIndex[i - 1] : 0; + groupIndex.push(preCount + files ? files.length : 0); + res = res.concat(files); + } + } + var self = this; + //var progressFunction = self.config.progressCallback ? self.config.progressCallback : null; + cc.loader.load(res, function (result, count, loadedCount) { + var checkGroupName = function (loadedCount) { + for (var i = 0; i < groupIndex.length; i++) { + if (groupIndex[i] >= loadedCount) { + return self._groupname[i]; + } + } + }; + var groupName = checkGroupName(loadedCount); + var status = { + groupName: groupName, + isCompleted: false, + percent: (loadedCount / count * 100) | 0,//(float), + stage: 1, //(1 download,2 unzip) + isFailed: false + } + if (status.percent != 0) { + self._setProgress(status.percent / 100); + } + config.tips.tipsProgress(status, self); + }, function () { + self.removeFromParent(); + self._preloadCount--; + if (self._callback) { + if (self._selector) { + self._callback(self._selector, true); + } else { + self._callback(true); + } + } + }); + }, + _preload_native: function (status) { + cc.log(JSON.stringify(status)); + var config = cc.LoaderLayer._finalConfig; + if (status.percent) { + this._setProgress(status.percent / 100); + } + if (config.tips.tipsProgress) { + config.tips.tipsProgress(status, this); + } + if (status.isCompleted || status.isFailed) { + this._preloadCount--; + + if (status.isCompleted) { + cc.log("preload finish!"); + this._isPreloadFromFailed = false; + } + if (status.isFailed) { + cc.log("preload failed!"); + this._isPreloadFromFailed = true; + } + + // Remove loading layer from scene after loading was done. + if (this._preloadCount == 0 && !this._isPreloadFromFailed) { + this.removeFromParent(); + if (cc.LoaderLayer._useDefaultSource) { + var _config = cc.runtime.config.design_resolution || {width: 480, height: 720, policy: "SHOW_ALL"}; + cc.view.setDesignResolutionSize(_config.width, _config.height, cc.ResolutionPolicy[_config.policy]); + } + } + + // Callback must be invoked after removeFromParent. + this._callback.call(this._target, status); + } + }, + _addToScene: function () { + if (this._preloadCount == 0 && !this._isPreloadFromFailed) { + if (cc.sys.isNative && cc.LoaderLayer._useDefaultSource) { + var config = cc.runtime.config.design_resolution; + var isLandscape = false; + var isLargeThanResource = false; + if (config) { + var orientation = cc.runtime.config.orientation; + cc.log("_addToScene orientation is " + orientation); + if (orientation == "landscape") { + isLandscape = true; + isLargeThanResource = config.width > 720 || config.height > 480; + } else { + isLargeThanResource = config.width > 480 || config.height > 720; + } + } + cc.log("isLargeThanResource is " + isLargeThanResource); + cc.view.setDesignResolutionSize(isLargeThanResource ? config.width : isLandscape ? 720 : 480, isLargeThanResource ? config.height : isLandscape ? 480 : 720, cc.ResolutionPolicy["FIXED_HEIGHT"]); + } + cc.director.getRunningScene().addChild(this, INT_MAX - 1); + } + this._preloadCount++; + } +}); +cc.LoaderLayer._config = {//default setting for loaderlayer + background: { + res: "res_engine/preload_bg.jpg" + }, + title: { + show: true, + res: "res_engine/preload_title.png", + position: null, + action: null + }, + logo: { + res: "res_engine/preload_logo.png", + show: true, + position: null + }, + progressBar: { + show: true, + res: "res_engine/progress_bar.png", + offset: null, + position: null, + barBackgroundRes: "res_engine/progress_bg.png", + barPoint: "res_engine/progress_light.png", + barShadow: "res_engine/shadow.png" + }, + tips: { + show: true, + fontSize: 22, + position: null, + color: null, + tipsProgress: function (status, loaderlayer) { + if(loaderlayer.getTipsLabel()){ + var statusStr = "正在"; + if (status.stage == cc.network.preloadstatus.DOWNLOAD) { + statusStr += "下载"; + } else if (status.stage == cc.network.preloadstatus.UNZIP) { + statusStr += "解压"; + } + if (status.groupName) { + statusStr += status.groupName; + } + statusStr += "进度:" + status.percent.toFixed(2) + "%"; + loaderlayer.getTipsLabel().setString(statusStr); + } + } + }, + progressCallback: function (progress) { + + }, + onEnter: function (layer) { + cc.log("LoaderLayer onEnter"); + }, + onExit: function (layer) { + cc.log("LoaderLayer onExit"); + } +} + +var res_engine_loaded = false; + +cc.LoaderLayer.preload = function (groupname, callback, target) { + var loaderLayer = new cc.LoaderLayer(); + var preloadCb = function (status) { + if (status.isFailed) { + var tips, conirmfunc, cancelfunc; + switch (status.errorCode) { + case "err_no_space": + { + tips = "空间不足,请清理磁盘空间"; + conirmfunc = function () { + callPreload(); + }; + cancelfunc = function () { + cc.director.end(); + }; + break; + } + case "err_verify": + { + tips = "校验失败,是否重新下载?"; + conirmfunc = function () { + callPreload(); + } + cancelfunc = function () { + cc.director.end(); + } + break; + } + case "err_network": + { + tips = "网络异常是否重新下载"; + conirmfunc = function () { + callPreload(); + } + cancelfunc = function () { + cc.director.end(); + } + break; + } + default : + { + conirmfunc = cancelfunc = function () { + + } + } + } + cc._NetworkErrorDialog._show(status.errorCode, tips, conirmfunc, cancelfunc); + } else { + if (callback) { + if (target) { + callback.call(target, !status.isFailed); + } else { + callback(!status.isFailed) + } + } + } + } + var callPreload = function () { + loaderLayer.updateGroup(groupname, preloadCb, target); + loaderLayer._addToScene(); + loaderLayer._preloadSource(); + }; + + if (res_engine_loaded) { + callPreload(); + return; + } + + if (!cc.director.getRunningScene()) { + cc.director.runScene(new cc.Scene()); + } + + // Res engine not loaded, load them + cc.loader.load([ + GROUP_JSON_PATH, + cc.LoaderLayer._finalConfig.background.res, + cc.LoaderLayer._finalConfig.title.res, + cc.LoaderLayer._finalConfig.logo.res, + cc.LoaderLayer._finalConfig.progressBar.res, + cc.LoaderLayer._finalConfig.progressBar.barBackgroundRes, + cc.LoaderLayer._finalConfig.progressBar.barPoint, + cc.LoaderLayer._finalConfig.progressBar.barShadow, + cc.Dialog._finalConfig.background.res, + cc.Dialog._finalConfig.confirmBtn.normalRes, + cc.Dialog._finalConfig.confirmBtn.pressRes, + cc.Dialog._finalConfig.cancelBtn.normalRes, + cc.Dialog._finalConfig.cancelBtn.pressRes + ], + function (result, count, loadedCount) { + var percent = (loadedCount / count * 100) | 0; + percent = Math.min(percent, 100); + cc.log("Preloading engine resources... " + percent + "%"); + }, function () { + var groups = cc.loader.getRes(GROUP_JSON_PATH); + if (groups) { + cc.LoaderLayer.groups = groups; + } + else { + cc.warn("Group versions haven't been loaded, you can also set group data with 'cc.LoaderLayer.groups'"); + } + res_engine_loaded = true; + callPreload(); + }); +}; + +cc.LoaderLayer._useDefaultSource = true; +cc.LoaderLayer._isDefaultProgress = true; +cc.LoaderLayer._finalConfig = cc.LoaderLayer._config; +cc.LoaderLayer.groups = {}; +cc.LoaderLayer.setUseDefaultSource = function (status) { + cc.LoaderLayer._useDefaultSource = status; +}; +cc.LoaderLayer.setConfig = function (config) { + if(config.title && config.title.action){ + config.title.action.retain(); + } + if(config.logo && config.logo.action){ + config.logo.action.retain(); + } + this._initData(config); +}; +cc.LoaderLayer._initData = function (uConfig) { + this._finalConfig = cc.clone(this._config); + var config = this._finalConfig; + if (uConfig != null) { + if (uConfig.background && uConfig.background.res) { + config.background.res = uConfig.background.res; + } + if (uConfig.title) { + var uTitle = uConfig.title; + var title = config.title; + title.show = typeof uTitle.show != "undefined" ? uTitle.show : title.show; + title.res = uTitle.res ? uTitle.res : title.res; + title.position = uTitle.position ? uTitle.position : title.position; + title.action = uTitle.action ? uTitle.action : title.action; + if (title.action) { + title.action = uTitle.action; + title.action.retain(); + } + } + if (uConfig.logo) { + var uLogo = uConfig.logo; + var logo = config.logo; + logo.show = typeof uLogo.show != "undefined" ? uLogo.show : logo.show; + logo.res = uLogo.res ? uLogo.res : logo.res; + logo.position = uLogo.position ? uLogo.position : logo.position; + if (typeof uLogo.action != "undefined") { + logo.action = uLogo.action; + if (logo.action) { + logo.action.retain(); + } + } + } + if (uConfig.progressBar) { + var uProgress = uConfig.progressBar; + var progress = config.progressBar; + progress.show = typeof uProgress.show != "undefined" ? uProgress.show : progress.show; + if (uProgress.res) { + progress.res = uProgress.res; + this._isDefaultProgress = false; + } + progress.offset = uProgress.offset ? uProgress.offset : progress.offset; + progress.position = uProgress.position ? uProgress.position : progress.position; + progress.barBackgroundRes = uProgress.barBackgroundRes ? uProgress.barBackgroundRes : progress.barBackgroundRes; + } + if (uConfig.tips) { + var uTips = uConfig.tips; + var tips = config.tips; + tips.show = typeof uTips.show != "undefined" ? uTips.show : tips.show; + tips.res = uTips.res ? uTips.res : tips.res; + tips.offset = uTips.offset ? uTips.offset : tips.offset; + tips.fontSize = uTips.fontSize ? uTips.fontSize : tips.fontSize; + tips.position = uTips.position ? uTips.position : tips.position; + tips.color = uTips.color ? uTips.color : tips.color; + if (uConfig.tips.tipsProgress && typeof uConfig.tips.tipsProgress == "function") { + tips.tipsProgress = uConfig.tips.tipsProgress; + } + } + if (typeof uConfig.onEnter == "function") { + config.onEnter = uConfig.onEnter; + } + if (typeof uConfig.onExit == "function") { + config.onExit = uConfig.onExit; + } + } + + if (typeof config.logo.action == "undefined" && this._useDefaultSource) { + config.logo.action = cc.sequence( + cc.spawn(cc.moveBy(0.4, cc.p(0, 40)).easing(cc.easeIn(0.5)), cc.scaleTo(0.4, 0.95, 1.05).easing(cc.easeIn(0.5))), + cc.delayTime(0.08), + cc.spawn(cc.moveBy(0.4, cc.p(0, -40)).easing(cc.easeOut(0.5)), cc.scaleTo(0.4, 1.05, 0.95).easing(cc.easeOut(0.5))) + ).repeatForever(); + config.logo.action.retain(); + } + if (!config.tips.color) { + config.tips.color = cc.color(255, 255, 255); + } +}; + +cc.Dialog = cc.Layer.extend({ + _defaultConfig: null, + backgroundSprite: null, + _menuItemConfirm: null, + _menuItemCancel: null, + _messageLabel: null, + _eventListener: null, + _scaleFactor: null, + + ctor: function (config) { + this._super(); + this.setConfig(config); + }, + setConfig: function (config) { + this.removeAllChildren(); + if (config) { + cc.Dialog.setConfig(config); + } + }, + initView: function () { + var useDefaultSource = cc.Dialog._useDefaultSource; + var winSize = cc.director.getWinSize(); + this._scaleFactor = !useDefaultSource ? 1 : winSize.width > winSize.height ? winSize.width / 720 : winSize.width / 480; + var config = cc.Dialog._finalConfig; + + //bg + this.backgroundSprite = new cc.Scale9Sprite(config.background.res); + this._setScale(this.backgroundSprite); + if (this._scaleFactor < 1) { + this.backgroundSprite.setScale(this._scaleFactor); + } + this.backgroundSprite.setPosition(config.position ? config.position : cc.p(winSize.width / 2, winSize.height / 2)); + + //menu + this.menuItemConfirm = this._createMenuItemSprite(config.confirmBtn, this._confirmCallback); + this.menuItemCancel = this._createMenuItemSprite(config.cancelBtn, this._cancelCallback); + this.menuItemCancel.setPosition(config.cancelBtn.position ? config.cancelBtn.position : cc.p(this.backgroundSprite.width / 2 - this.menuItemCancel.width / 2 - 20, this.menuItemCancel.height + 20)); + this.menuItemConfirm.setPosition(config.confirmBtn.position ? config.confirmBtn.position : cc.p(this.backgroundSprite.width / 2 + this.menuItemConfirm.width / 2 + 20, this.menuItemConfirm.height + 20)); + var menu = new cc.Menu(this.menuItemConfirm, this.menuItemCancel); + menu.setPosition(cc.p(0, 0)); + this.backgroundSprite.addChild(menu); + + //message + var fontSize = config.messageLabel.fontSize ? config.messageLabel.fontSize : this._scaleFactor > 1 ? 16 * this._scaleFactor : 16; + this.messageLabel = new cc.LabelTTF(config.messageLabel.text, "Arial", fontSize); + this.messageLabel.setDimensions(config.messageLabel.dimensions ? config.messageLabel.dimensions : cc.size(this.backgroundSprite.width - 30, this.backgroundSprite.height - this.menuItemConfirm.y - 10)); + this.messageLabel.setColor(config.messageLabel.color ? config.messageLabel.color : cc.color(255, 255, 255)); + this.messageLabel.setPosition(config.messageLabel.position ? config.messageLabel.position : cc.p(this.backgroundSprite.width / 2, this.backgroundSprite.height - this.messageLabel.height / 2 - 20)); + this.backgroundSprite.addChild(this.messageLabel); + if (!config.action) { + var action = cc.sequence(cc.EaseIn.create(cc.scaleTo(0.1, this.backgroundSprite.scale + 0.02), 0.4), cc.EaseOut.create(cc.scaleTo(0.1, this.backgroundSprite.scale), 0.3)); + this.backgroundSprite.runAction(action); + } else { + this.backgroundSprite.runAction(config.action); + } + this.addChild(this.backgroundSprite); + + }, + _createMenuItemSprite: function (res, callback) { + var spriteNormal = new cc.Scale9Sprite(res.normalRes); + var spritePress = new cc.Scale9Sprite(res.pressRes); + this._setScale(spriteNormal); + this._setScale(spritePress); + var fontSize = res.fontSize ? res.fontSize : this._scaleFactor > 1 ? 16 * this._scaleFactor : 16; + var menuLabel = new cc.LabelTTF(res.text, "Arial", fontSize); + menuLabel.setColor(res.textColor); + var menuItem = new cc.MenuItemSprite(spriteNormal, spritePress, callback, this); + menuLabel.setPosition(cc.p(menuItem.width / 2, menuItem.height / 2)); + menuItem.addChild(menuLabel); + return menuItem; + }, + _setScale: function (s9Sprite) { + if (this._scaleFactor > 1) { + s9Sprite.setContentSize(cc.size(this._scaleFactor * s9Sprite.width, this._scaleFactor * s9Sprite.height)); + } + }, + _confirmCallback: function () { + var config = cc.Dialog._finalConfig; + if (config.confirmBtn.callback) { + if (config.target) { + config.confirmBtn.callback.call(config.target, this); + } else { + config.confirmBtn.callback(this); + } + } + this.removeFromParent(); + }, + _cancelCallback: function () { + var config = cc.Dialog._finalConfig; + if (config.cancelBtn.callback) { + if (config.target) { + config.cancelBtn.callback.call(config.target, this); + } else { + config.cancelBtn.callback(this); + } + } + this.removeFromParent(); + }, + onEnter: function () { + this._super(); + var config = cc.Dialog._finalConfig; + this.initView(); + config.onEnter(this); + var self = this; + self._eventListener = cc.EventListener.create({ + event: cc.EventListener.TOUCH_ONE_BY_ONE, + swallowTouches: true, + onTouchBegan: function (touch, event) { + return true; + } + }); + cc.eventManager.addListener(self._eventListener, self); + }, + onExit: function () { + this._super(); + var config = cc.Dialog._finalConfig; + config.onExit(this); + this.removeAllChildren(); + cc.Dialog._dialog = null; + cc.eventManager.removeListener(this._eventListener); + } +}); + +cc.Dialog._dialog = null; +cc.Dialog._clearDialog = function () { + if (cc.Dialog._dialog != null) { + cc.Dialog._dialog.removeFromParent(); + cc.Dialog._dialog = null; + } +} + +cc.Dialog.show = function (tips, confirmCb, cancelCb) { + if (cc.Dialog._dialog != null) { + cc.log("other dialog is on the screen,this dialog can't show now"); + return; + } + + var conf; + if (typeof tips == "string") { + conf = { + messageLabel: { + text: tips + }, + confirmBtn: { + callback: confirmCb + }, + cancelBtn: { + callback: cancelCb + } + } + } else if (typeof tips == "object") { + conf = tips; + } else { + cc.log("tips is invalid"); + return; + } + + cc.Dialog._dialog = new cc.Dialog(conf); + if (cc.director.getRunningScene()) { + cc.director.getRunningScene().addChild(cc.Dialog._dialog, INT_MAX); + } else { + cc.log("Current scene is null we can't show dialog"); + } +}; +cc.Dialog._useDefaultSource = true; +cc.Dialog.setUseDefaultSource = function (status) { + cc.Dialog._useDefaultSource = status; +} +cc.Dialog._defaultConfig = { + position: null, + target: null, + action: null, + background: { + res: "res_engine/dialog_bg.png" + }, + confirmBtn: { + normalRes: "res_engine/dialog_confirm_normal.png", + pressRes: "res_engine/dialog_confirm_press.png", + text: "确定", + textColor: null, + fontSize: null, + position: null, + callback: function () { + cc.log("this is confirm callback"); + } + }, + cancelBtn: { + normalRes: "res_engine/dialog_cancel_normal.png", + pressRes: "res_engine/dialog_cancel_press.png", + text: "取消", + textColor: null, + position: null, + fontSize: null, + callback: function () { + cc.log("this is cancel callback"); + } + }, + messageLabel: { + text: "", + color: null, + dimensions: null, + fontSize: null, + position: null + }, + onEnter: function (dialog) { + cc.log("dialog call onEnter"); + }, + onExit: function (dialog) { + cc.log("dialog call onExit"); + } +}; +cc.Dialog._finalConfig = cc.Dialog._defaultConfig; +cc.Dialog.setConfig = function (config) { + this._initData(config); +}; +cc.Dialog._initData = function (uConfig) { + this._finalConfig = cc.clone(this._defaultConfig); + var config = this._finalConfig; + if (uConfig != null) { + if (uConfig.position) { + config.position = uConfig.position; + } + if (uConfig.action) { + config.action = uConfig.action; + } + if (uConfig.background && uConfig.background.res) { + config.background = uConfig.background; + } + if (uConfig.confirmBtn) { + var uConfirmBtn = uConfig.confirmBtn; + var confirmBtn = config.confirmBtn; + confirmBtn.normalRes = uConfirmBtn.normalRes ? uConfirmBtn.normalRes : confirmBtn.normalRes; + confirmBtn.pressRes = uConfirmBtn.pressRes ? uConfirmBtn.pressRes : confirmBtn.pressRes; + confirmBtn.text = typeof uConfirmBtn.text != "undefined" ? uConfirmBtn.text : confirmBtn.text; + confirmBtn.textColor = uConfirmBtn.textColor ? uConfirmBtn.textColor : confirmBtn.textColor; + confirmBtn.fontSize = uConfirmBtn.fontSize ? uConfirmBtn.fontSize : confirmBtn.fontSize; + confirmBtn.position = uConfirmBtn.position ? uConfirmBtn.position : confirmBtn.position; + confirmBtn.callback = uConfirmBtn.callback ? uConfirmBtn.callback : confirmBtn.callback; + } + if (uConfig.cancelBtn) { + var uCancelBtn = uConfig.cancelBtn; + var cancelBtn = config.cancelBtn; + cancelBtn.normalRes = uCancelBtn.normalRes ? uCancelBtn.normalRes : cancelBtn.normalRes; + cancelBtn.pressRes = uCancelBtn.pressRes ? uCancelBtn.pressRes : cancelBtn.pressRes; + cancelBtn.text = typeof uCancelBtn.text != "undefined" ? uCancelBtn.text : cancelBtn.text; + cancelBtn.textColor = uCancelBtn.textColor ? uCancelBtn.textColor : cancelBtn.textColor; + cancelBtn.fontSize = uCancelBtn.fontSize ? uCancelBtn.fontSize : cancelBtn.fontSize; + cancelBtn.position = uCancelBtn.position ? uCancelBtn.position : cancelBtn.position; + cancelBtn.callback = uCancelBtn.callback ? uCancelBtn.callback : cancelBtn.callback; + } + if (uConfig.messageLabel) { + var uMessageLabel = uConfig.messageLabel; + var messageLabel = config.messageLabel; + messageLabel.text = typeof uMessageLabel.text != "undefined" ? uMessageLabel.text : messageLabel.text; + messageLabel.color = uMessageLabel.color ? uMessageLabel.color : messageLabel.color; + messageLabel.fontSize = uMessageLabel.fontSize ? uMessageLabel.fontSize : messageLabel.fontSize; + messageLabel.position = uMessageLabel.position ? uMessageLabel.position : messageLabel.position; + messageLabel.dimensions = uMessageLabel.dimensions ? uMessageLabel.dimensions : messageLabel.dimensions; + } + if (uConfig.target) { + config.target = uConfig.target; + } + if (typeof uConfig.onEnter == "function") { + config.onEnter = uConfig.onEnter; + } + if (typeof uConfig.onExit == "function") { + config.onExit = uConfig.onExit; + } + } + + if (!config.cancelBtn.textColor) { + config.cancelBtn.textColor = cc.color(255, 255, 255); + } + if (!config.confirmBtn.textColor) { + config.confirmBtn.textColor = cc.color(255, 255, 255); + } +}; + +cc._NetworkErrorDialog = function () { + cc.Dialog._clearDialog(); + cc.Dialog._dialog = new cc.Dialog(cc._NetworkErrorDialog._config); + return cc.Dialog._dialog; +} +cc._NetworkErrorDialog._config = { + networkError: {}, + spaceError: {}, + verifyError: {} +}; +cc._NetworkErrorDialog._show = function (type, tips, confirmCb, cancelCb) { + var networkDialog = cc._NetworkErrorDialog(); + var config; + switch (type) { + case "err_network": + { + config = cc._NetworkErrorDialog._config.networkError; + break; + } + case "err_no_space": + { + config = cc._NetworkErrorDialog._config.spaceError; + break; + } + case "err_verify": + { + config = cc._NetworkErrorDialog._config.verifyError; + break; + } + default: + { + cc.log("type is not found"); + return; + } + } + if (!networkDialog.getParent()) { + + config.confirmBtn = config.confirmBtn || {}; + config.confirmBtn.callback = function () { + if (confirmCb) + confirmCb(); + } + + config.cancelBtn = config.cancelBtn || {}; + config.cancelBtn.callback = function () { + if (cancelCb) + cancelCb(); + } + + config.messageLabel = config.messageLabel || {}; + if (typeof config.messageLabel.text == "undefined") { + config.messageLabel.text = tips; + } + + networkDialog.setConfig(config); + if (cc.director.getRunningScene()) { + cc.director.getRunningScene().addChild(networkDialog, INT_MAX); + } else { + cc.log("Current scene is null we can't show dialog"); + } + } +} + +cc._NetworkErrorDialog._setConfig = function (key, config) { + if (key && config) { + switch (key) { + case "err_network": + { + cc._NetworkErrorDialog._config.networkError = config; + break; + } + case "err_no_space": + { + cc._NetworkErrorDialog._config.spaceError = config; + break; + } + case "err_verify": + { + cc._NetworkErrorDialog._config.verifyError = config; + break; + } + } + } +} + +cc.runtime = cc.runtime || {}; + +cc.runtime.setOption = function (promptype, config) { + if (config) { + switch (promptype) { + case "network_error_dialog": + { + cc._NetworkErrorDialog._setConfig("err_network", config); + break; + } + case "no_space_error_dialog": + { + cc._NetworkErrorDialog._setConfig("err_no_space", config); + break; + } + case "verify_error_dialog": + { + cc._NetworkErrorDialog._setConfig("err_verify", config); + break; + } + default : + { + cc.log("promptype not found please check your promptype"); + } + } + } else { + cc.log("config is null please check your config"); + } +} + +/** + * only use in JSB get network type + * @type {{}|*|cc.network} + */ +cc.network = cc.network || {}; +cc.network.type = { + NO_NETWORK: -1, + MOBILE: 0, + WIFI: 1 +} +cc.network.preloadstatus = { + DOWNLOAD: 1, + UNZIP: 2 +} +cc.runtime.network = cc.network; + +})(); + +cc.LoaderScene._preload = cc.LoaderScene.preload; +cc.LoaderScene.preload = function (arr, cb, target) { + // No extension + var isGroups = (arr[0] && arr[0].indexOf('.') === -1); + if (isGroups) { + if (arr.indexOf('boot') === -1) { + arr.splice(0, 0, 'boot'); + } + cc.LoaderLayer.preload(arr, cb, target); + } + else { + cc.LoaderScene._preload(arr, cb, target); + } +} diff --git a/frameworks/cocos2d-html5/extensions/spine/CCSkeleton.js b/frameworks/cocos2d-html5/extensions/spine/CCSkeleton.js new file mode 100644 index 0000000..97a99f4 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/spine/CCSkeleton.js @@ -0,0 +1,381 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2014 Shengxiang Chen (Nero Chan) + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * The main namespace of Spine, all classes, functions, properties and constants of Spine are defined in this namespace + * @namespace + * @name sp + */ +var sp = sp || {}; + +var spine = sp.spine; + +/** + *

+ * The skeleton of Spine.
+ * Skeleton has a reference to a SkeletonData and stores the state for skeleton instance, + * which consists of the current pose's bone SRT, slot colors, and which slot attachments are visible.
+ * Multiple skeletons can use the same SkeletonData (which includes all animations, skins, and attachments).
+ *

+ * @class + * @extends cc.Node + */ +sp.Skeleton = cc.Node.extend(/** @lends sp.Skeleton# */{ + _skeleton: null, + _rootBone: null, + _timeScale: 1, + _debugSlots: false, + _debugBones: false, + _premultipliedAlpha: false, + _ownsSkeletonData: null, + _atlas: null, + + /** + * The constructor of sp.Skeleton. override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + */ + ctor:function(skeletonDataFile, atlasFile, scale){ + cc.Node.prototype.ctor.call(this); + + if(arguments.length === 0) + this.init(); + else + this.initWithArgs(skeletonDataFile, atlasFile, scale); + }, + + _createRenderCmd:function () { + if(cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return new sp.Skeleton.CanvasRenderCmd(this); + else + return new sp.Skeleton.WebGLRenderCmd(this); + }, + + /** + * Initializes a sp.Skeleton. please do not call this function by yourself, you should pass the parameters to constructor to initialize it. + */ + init: function () { + cc.Node.prototype.init.call(this); + this._premultipliedAlpha = (cc._renderType === cc.game.RENDER_TYPE_WEBGL && cc.OPTIMIZE_BLEND_FUNC_FOR_PREMULTIPLIED_ALPHA); + }, + + onEnter: function () { + cc.Node.prototype.onEnter.call(this); + this.scheduleUpdate(); + }, + + onExit: function () { + this.unscheduleUpdate(); + cc.Node.prototype.onExit.call(this); + }, + + /** + * Sets whether open debug slots. + * @param {boolean} enable true to open, false to close. + */ + setDebugSolots:function(enable){ + this._debugSlots = enable; + }, + + /** + * Sets whether open debug bones. + * @param {boolean} enable + */ + setDebugBones:function(enable){ + this._debugBones = enable; + }, + + /** + * Sets whether open debug slots. + * @param {boolean} enabled true to open, false to close. + */ + setDebugSlotsEnabled: function(enabled) { + this._debugSlots = enabled; + }, + + /** + * Gets whether open debug slots. + * @returns {boolean} true to open, false to close. + */ + getDebugSlotsEnabled: function() { + return this._debugSlots; + }, + + /** + * Sets whether open debug bones. + * @param {boolean} enabled + */ + setDebugBonesEnabled: function(enabled) { + this._debugBones = enabled; + }, + + /** + * Gets whether open debug bones. + * @returns {boolean} true to open, false to close. + */ + getDebugBonesEnabled: function() { + return this._debugBones; + }, + + /** + * Sets the time scale of sp.Skeleton. + * @param {Number} scale + */ + setTimeScale:function(scale){ + this._timeScale = scale; + }, + + getTimeScale: function(){ + return this._timeScale; + }, + + /** + * Initializes sp.Skeleton with Data. + * @param {sp.spine.SkeletonData|String} skeletonDataFile + * @param {String|spine.Atlas|spine.SkeletonData} atlasFile atlas filename or atlas data or owns SkeletonData + * @param {Number} [scale] scale can be specified on the JSON or binary loader which will scale the bone positions, image sizes, and animation translations. + */ + initWithArgs: function (skeletonDataFile, atlasFile, scale) { + var argSkeletonFile = skeletonDataFile, argAtlasFile = atlasFile, + skeletonData, atlas, ownsSkeletonData; + + if (cc.isString(argSkeletonFile)) { + if (cc.isString(argAtlasFile)) { + var data = cc.loader.getRes(argAtlasFile); + sp._atlasLoader.setAtlasFile(argAtlasFile); + atlas = new spine.TextureAtlas(data, sp._atlasLoader.load.bind(sp._atlasLoader)); + } else { + atlas = atlasFile; + } + scale = scale || 1 / cc.director.getContentScaleFactor(); + + var attachmentLoader = new spine.AtlasAttachmentLoader(atlas); + var skeletonJsonReader = new spine.SkeletonJson(attachmentLoader); + skeletonJsonReader.scale = scale; + + var skeletonJson = cc.loader.getRes(argSkeletonFile); + skeletonData = skeletonJsonReader.readSkeletonData(skeletonJson); + atlas.dispose(skeletonJsonReader); + ownsSkeletonData = true; + } else { + skeletonData = skeletonDataFile; + ownsSkeletonData = atlasFile; + } + this.setSkeletonData(skeletonData, ownsSkeletonData); + this.init(); + }, + + /** + * Returns the bounding box of sp.Skeleton. + * @returns {cc.Rect} + */ + getBoundingBox: function () { + var minX = cc.FLT_MAX, minY = cc.FLT_MAX, maxX = cc.FLT_MIN, maxY = cc.FLT_MIN; + var scaleX = this.getScaleX(), scaleY = this.getScaleY(), vertices, + slots = this._skeleton.slots, VERTEX = spine.RegionAttachment; + + for (var i = 0, slotCount = slots.length; i < slotCount; ++i) { + var slot = slots[i]; + var attachment = slot.attachment; + if (!attachment || !(attachment instanceof spine.RegionAttachment)) + continue; + vertices = spine.Utils.setArraySize(new Array(), 8, 0); + attachment.computeWorldVertices(slot.bone, vertices, 0, 2); + minX = Math.min(minX, vertices[VERTEX.OX1] * scaleX, vertices[VERTEX.OX4] * scaleX, vertices[VERTEX.OX2] * scaleX, vertices[VERTEX.OX3] * scaleX); + minY = Math.min(minY, vertices[VERTEX.OY1] * scaleY, vertices[VERTEX.OY4] * scaleY, vertices[VERTEX.OY2] * scaleY, vertices[VERTEX.OY3] * scaleY); + maxX = Math.max(maxX, vertices[VERTEX.OX1] * scaleX, vertices[VERTEX.OX4] * scaleX, vertices[VERTEX.OX2] * scaleX, vertices[VERTEX.OX3] * scaleX); + maxY = Math.max(maxY, vertices[VERTEX.OY1] * scaleY, vertices[VERTEX.OY4] * scaleY, vertices[VERTEX.OY2] * scaleY, vertices[VERTEX.OY3] * scaleY); + } + var position = this.getPosition(); + return cc.rect(position.x + minX, position.y + minY, maxX - minX, maxY - minY); + }, + + /** + * Computes the world SRT from the local SRT for each bone. + */ + updateWorldTransform: function () { + this._skeleton.updateWorldTransform(); + }, + + /** + * Sets the bones and slots to the setup pose. + */ + setToSetupPose: function () { + this._skeleton.setToSetupPose(); + }, + + /** + * Sets the bones to the setup pose, using the values from the `BoneData` list in the `SkeletonData`. + */ + setBonesToSetupPose: function () { + this._skeleton.setBonesToSetupPose(); + }, + + /** + * Sets the slots to the setup pose, using the values from the `SlotData` list in the `SkeletonData`. + */ + setSlotsToSetupPose: function () { + this._skeleton.setSlotsToSetupPose(); + }, + + /** + * Finds a bone by name. This does a string comparison for every bone. + * @param {String} boneName + * @returns {sp.spine.Bone} + */ + findBone: function (boneName) { + return this._skeleton.findBone(boneName); + }, + + /** + * Finds a slot by name. This does a string comparison for every slot. + * @param {String} slotName + * @returns {sp.spine.Slot} + */ + findSlot: function (slotName) { + return this._skeleton.findSlot(slotName); + }, + + /** + * Finds a skin by name and makes it the active skin. This does a string comparison for every skin. Note that setting the skin does not change which attachments are visible. + * @param {string} skinName + * @returns {sp.spine.Skin} + */ + setSkin: function (skinName) { + return this._skeleton.setSkinByName(skinName); + }, + + /** + * Returns the attachment for the slot and attachment name. The skeleton looks first in its skin, then in the skeleton data’s default skin. + * @param {String} slotName + * @param {String} attachmentName + * @returns {sp.spine.Attachment} + */ + getAttachment: function (slotName, attachmentName) { + return this._skeleton.getAttachmentByName(slotName, attachmentName); + }, + + /** + * Sets the attachment for the slot and attachment name. The skeleton looks first in its skin, then in the skeleton data’s default skin. + * @param {String} slotName + * @param {String} attachmentName + */ + setAttachment: function (slotName, attachmentName) { + this._skeleton.setAttachment(slotName, attachmentName); + }, + + /** + * Sets the premultiplied alpha value to sp.Skeleton. + * @param {Number} alpha + */ + setPremultipliedAlpha: function (premultiplied) { + this._premultipliedAlpha = premultiplied; + }, + + /** + * Returns whether to enable premultiplied alpha. + * @returns {boolean} + */ + isPremultipliedAlpha: function () { + return this._premultipliedAlpha; + }, + + /** + * Sets skeleton data to sp.Skeleton. + * @param {sp.spine.SkeletonData} skeletonData + * @param {sp.spine.SkeletonData} ownsSkeletonData + */ + setSkeletonData: function (skeletonData, ownsSkeletonData) { + if(skeletonData.width != null && skeletonData.height != null) + this.setContentSize(skeletonData.width / cc.director.getContentScaleFactor(), skeletonData.height / cc.director.getContentScaleFactor()); + + this._skeleton = new spine.Skeleton(skeletonData); + this._skeleton.updateWorldTransform(); + this._rootBone = this._skeleton.getRootBone(); + this._ownsSkeletonData = ownsSkeletonData; + + this._renderCmd._createChildFormSkeletonData(); + }, + + /** + * Return the renderer of attachment. + * @param {sp.spine.RegionAttachment|sp.spine.BoundingBoxAttachment} regionAttachment + * @returns {sp.spine.TextureAtlasRegion} + */ + getTextureAtlas: function (regionAttachment) { + return regionAttachment.region; + }, + + /** + * Returns the blendFunc of sp.Skeleton. + * @returns {cc.BlendFunc} + */ + getBlendFunc: function () { + var slot = this._skeleton.drawOrder[0]; + if (slot) { + var blend = this._renderCmd._getBlendFunc(slot.data.blendMode, this._premultipliedAlpha); + return blend; + } + else { + return {}; + } + }, + + /** + * Sets the blendFunc of sp.Skeleton, it won't have any effect for skeleton, skeleton is using slot's data to determine the blend function. + * @param {cc.BlendFunc|Number} src + * @param {Number} [dst] + */ + setBlendFunc: function (src, dst) { + return; + }, + + /** + * Update will be called automatically every frame if "scheduleUpdate" is called when the node is "live". + * @param {Number} dt Delta time since last update + */ + update: function (dt) { + this._skeleton.update(dt); + } +}); + +cc.defineGetterSetter(sp.Skeleton.prototype, "opacityModifyRGB", sp.Skeleton.prototype.isOpacityModifyRGB); + +// For renderer webgl to identify skeleton's default texture and blend function +cc.defineGetterSetter(sp.Skeleton.prototype, "_blendFunc", sp.Skeleton.prototype.getBlendFunc); +cc.defineGetterSetter(sp.Skeleton.prototype, '_texture', function () { + return this._renderCmd._currTexture; +}); + +/** + * Creates a skeleton object. + * @deprecated since v3.0, please use new sp.Skeleton(skeletonDataFile, atlasFile, scale) instead. + * @param {spine.SkeletonData|String} skeletonDataFile + * @param {String|spine.Atlas|spine.SkeletonData} atlasFile atlas filename or atlas data or owns SkeletonData + * @param {Number} [scale] scale can be specified on the JSON or binary loader which will scale the bone positions, image sizes, and animation translations. + * @returns {sp.Skeleton} + */ +sp.Skeleton.create = function (skeletonDataFile, atlasFile/* or atlas*/, scale) { + return new sp.Skeleton(skeletonDataFile, atlasFile, scale); +}; diff --git a/frameworks/cocos2d-html5/extensions/spine/CCSkeletonAnimation.js b/frameworks/cocos2d-html5/extensions/spine/CCSkeletonAnimation.js new file mode 100644 index 0000000..17f0f25 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/spine/CCSkeletonAnimation.js @@ -0,0 +1,351 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2014 Shengxiang Chen (Nero Chan) + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +sp._atlasLoader = { + spAtlasFile:null, + setAtlasFile:function(spAtlasFile){ + this.spAtlasFile = spAtlasFile; + }, + load:function(line){ + var texturePath = cc.path.join(cc.path.dirname(this.spAtlasFile), line); + var texture = cc.textureCache.addImage(texturePath); + var tex = new sp.SkeletonTexture({ width: texture.getPixelsWide(), height: texture.getPixelsHigh() }); + tex.setRealTexture(texture); + return tex; + }, + unload:function(obj){ + } +}; + +/** + * The event type of spine skeleton animation. It contains event types: START(0), END(1), COMPLETE(2), EVENT(3). + * @constant + * @type {{START: number, END: number, COMPLETE: number, EVENT: number}} + */ +sp.ANIMATION_EVENT_TYPE = { + START: 0, + INTERRUPT: 1, + END: 2, + DISPOSE: 3, + COMPLETE: 4, + EVENT: 5 +}; + +sp.TrackEntryListeners = function (startListener, endListener, completeListener, eventListener, interruptListener, disposeListener) { + this.startListener = startListener || null; + this.endListener = endListener || null; + this.completeListener = completeListener || null; + this.eventListener = eventListener || null; + this.interruptListener = interruptListener || null; + this.disposeListener = disposeListener || null; + this.callback = null; + this.callbackTarget = null; + this.skeletonNode = null; +}; + +var proto = sp.TrackEntryListeners.prototype; +proto.start = function(trackEntry) { + if (this.startListener) { + this.startListener(trackEntry); + } + if (this.callback) { + this.callback.call(this.callbackTarget, this.skeletonNode, trackEntry, sp.ANIMATION_EVENT_TYPE.START, null, 0); + } +}; + +proto.interrupt = function(trackEntry) { + if (this.interruptListener) { + this.interruptListener(trackEntry); + } + if (this.callback) { + this.callback.call(this.callbackTarget, this.skeletonNode, trackEntry, sp.ANIMATION_EVENT_TYPE.INTERRUPT, null, 0); + } +}; + +proto.end = function (trackEntry) { + if (this.endListener) { + this.endListener(trackEntry); + } + if (this.callback) { + this.callback.call(this.callbackTarget, this.skeletonNode, trackEntry, sp.ANIMATION_EVENT_TYPE.END, null, 0); + } +}; + +proto.dispose = function (trackEntry) { + if (this.disposeListener) { + this.disposeListener(trackEntry); + } + if (this.callback) { + this.callback.call(this.callbackTarget, this.skeletonNode, trackEntry, sp.ANIMATION_EVENT_TYPE.DISPOSE, null, 0); + } +}; + +proto.complete = function (trackEntry) { + var loopCount = Math.floor(trackEntry.trackTime / trackEntry.animationEnd); + if (this.completeListener) { + this.completeListener(trackEntry, loopCount); + } + if (this.callback) { + this.callback.call(this.callbackTarget, this.skeletonNode, trackEntry, sp.ANIMATION_EVENT_TYPE.COMPLETE, null, loopCount); + } +}; + +proto.event = function (trackEntry, event) { + if (this.eventListener) { + this.eventListener(trackEntry, event); + } + if (this.callback) { + this.callback.call(this.callbackTarget, this.skeletonNode, trackEntry, sp.ANIMATION_EVENT_TYPE.EVENT, event, 0); + } +}; + +sp.TrackEntryListeners.getListeners = function(entry){ + if(!entry.listener){ + entry.listener = new sp.TrackEntryListeners(); + } + return entry.listener; +}; + +/** + * The skeleton animation of spine. It updates animation's state and skeleton's world transform. + * @class + * @extends sp.Skeleton + * @example + * var spineBoy = new sp.SkeletonAnimation('res/skeletons/spineboy.json', 'res/skeletons/spineboy.atlas'); + * this.addChild(spineBoy, 4); + */ +sp.SkeletonAnimation = sp.Skeleton.extend(/** @lends sp.SkeletonAnimation# */{ + _state: null, + + _ownsAnimationStateData: false, + _listener: null, + + /** + * Initializes a sp.SkeletonAnimation. please do not call this function by yourself, you should pass the parameters to constructor to initialize it. + * @override + */ + init: function () { + sp.Skeleton.prototype.init.call(this); + this._ownsAnimationStateData = true; + this.setAnimationStateData(new spine.AnimationStateData(this._skeleton.data)); + }, + + /** + * Sets animation state data to sp.SkeletonAnimation. + * @param {sp.spine.AnimationStateData} stateData + */ + setAnimationStateData: function (stateData) { + var state = new spine.AnimationState(stateData); + this._listener = new sp.TrackEntryListeners(); + state.rendererObject = this; + state.addListener(this._listener); + this._state = state; + }, + + /** + * Mix applies all keyframe values, interpolated for the specified time and mixed with the current values.
+ * @param {String} fromAnimation + * @param {String} toAnimation + * @param {Number} duration + */ + setMix: function (fromAnimation, toAnimation, duration) { + this._state.data.setMixWith(fromAnimation, toAnimation, duration); + }, + + /** + * Sets event listener of sp.SkeletonAnimation. + * @param {Object} target + * @param {Function} callback + */ + setAnimationListener: function (target, callback) { + this._listener.callbackTarget = target; + this._listener.callback = callback; + this._listener.skeletonNode = this; + }, + + /** + * Set the current animation. Any queued animations are cleared. + * @param {Number} trackIndex + * @param {String} name + * @param {Boolean} loop + * @returns {sp.spine.TrackEntry|null} + */ + setAnimation: function (trackIndex, name, loop) { + var animation = this._skeleton.data.findAnimation(name); + if (!animation) { + cc.log("Spine: Animation not found: " + name); + return null; + } + return this._state.setAnimationWith(trackIndex, animation, loop); + }, + + /** + * Adds an animation to be played delay seconds after the current or last queued animation. + * @param {Number} trackIndex + * @param {String} name + * @param {Boolean} loop + * @param {Number} [delay=0] + * @returns {sp.spine.TrackEntry|null} + */ + addAnimation: function (trackIndex, name, loop, delay) { + delay = delay == null ? 0 : delay; + var animation = this._skeleton.data.findAnimation(name); + if (!animation) { + cc.log("Spine: Animation not found:" + name); + return null; + } + return this._state.addAnimationWith(trackIndex, animation, loop, delay); + }, + + /** + * Find animation with specified name + * @param {String} name + * @returns {sp.spine.Animation|null} + */ + findAnimation: function (name) { + return this._skeleton.data.findAnimation(name); + }, + + /** + * Returns track entry by trackIndex. + * @param trackIndex + * @returns {sp.spine.TrackEntry|null} + */ + getCurrent: function (trackIndex) { + return this._state.getCurrent(trackIndex); + }, + + /** + * Clears all tracks of animation state. + */ + clearTracks: function () { + this._state.clearTracks(); + }, + + /** + * Clears track of animation state by trackIndex. + * @param {Number} trackIndex + */ + clearTrack: function (trackIndex) { + this._state.clearTrack(trackIndex); + }, + + /** + * Update will be called automatically every frame if "scheduleUpdate" is called when the node is "live". + * It updates animation's state and skeleton's world transform. + * @param {Number} dt Delta time since last update + * @override + */ + update: function (dt) { + this._super(dt); + dt *= this._timeScale; + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.contentDirty); + this._state.update(dt); + this._state.apply(this._skeleton); + this._skeleton.updateWorldTransform(); + this._renderCmd._updateChild(); + }, + + /** + * Set the start event listener. + * @param {function} listener + */ + setStartListener: function(listener){ + this._listener.startListener = listener; + }, + + /** + * Set the interrupt listener + * @param {function} listener + */ + setInterruptListener: function(listener) { + this._listener.interruptListener = listener; + }, + + /** + * Set the end event listener. + * @param {function} listener + */ + setEndListener: function(listener) { + this._listener.endListener = listener; + }, + + /** + * Set the dispose listener + * @param {function} listener + */ + setDisposeListener: function(listener) { + this._listener.disposeListener = listener; + }, + + setCompleteListener: function(listener) { + this._listener.completeListener = listener; + }, + + setEventListener: function(listener){ + this._listener.eventListener = listener; + }, + + setTrackStartListener: function(entry, listener){ + sp.TrackEntryListeners.getListeners(entry).startListener = listener; + }, + + setTrackInterruptListener: function(entry, listener){ + sp.TrackEntryListeners.getListeners(entry).interruptListener = listener; + }, + + setTrackEndListener: function(entry, listener){ + sp.TrackEntryListeners.getListeners(entry).endListener = listener; + }, + + setTrackDisposeListener: function(entry, listener){ + sp.TrackEntryListeners.getListeners(entry).disposeListener = listener; + }, + + setTrackCompleteListener: function(entry, listener){ + sp.TrackEntryListeners.getListeners(entry).completeListener = listener; + }, + + setTrackEventListener: function(entry, listener){ + sp.TrackEntryListeners.getListeners(entry).eventListener = listener; + }, + + getState: function(){ + return this._state; + } +}); + +/** + * Creates a skeleton animation object. + * @deprecated since v3.0, please use new sp.SkeletonAnimation(skeletonDataFile, atlasFile, scale) instead. + * @param {spine.SkeletonData|String} skeletonDataFile + * @param {String|spine.Atlas|spine.SkeletonData} atlasFile atlas filename or atlas data or owns SkeletonData + * @param {Number} [scale] scale can be specified on the JSON or binary loader which will scale the bone positions, image sizes, and animation translations. + * @returns {sp.Skeleton} + */ +sp.SkeletonAnimation.createWithJsonFile = sp.SkeletonAnimation.create = function (skeletonDataFile, atlasFile/* or atlas*/, scale) { + return new sp.SkeletonAnimation(skeletonDataFile, atlasFile, scale); +}; diff --git a/frameworks/cocos2d-html5/extensions/spine/CCSkeletonCanvasRenderCmd.js b/frameworks/cocos2d-html5/extensions/spine/CCSkeletonCanvasRenderCmd.js new file mode 100644 index 0000000..f31924e --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/spine/CCSkeletonCanvasRenderCmd.js @@ -0,0 +1,247 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + +var spine = sp.spine; + +sp.Skeleton.CanvasRenderCmd = function (renderableObject) { + this._rootCtor(renderableObject); + this._needDraw = true; +}; + +var proto = sp.Skeleton.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); +proto.constructor = sp.Skeleton.CanvasRenderCmd; + +proto.rendering = function (wrapper, scaleX, scaleY) { + var node = this._node, i, n, slot, slotNode; + wrapper = wrapper || cc._renderContext; + + var locSkeleton = node._skeleton, drawOrder = locSkeleton.drawOrder; + for (i = 0, n = drawOrder.length; i < n; i++) { + slot = drawOrder[i]; + slotNode = slot._slotNode; + if (slotNode._visible && slotNode._renderCmd && slot.currentSprite) { + slotNode._renderCmd.transform(this, true); + slot.currentSprite._renderCmd.rendering(wrapper, scaleX, scaleY); + slotNode._renderCmd._dirtyFlag = slot.currentSprite._renderCmd._dirtyFlag = 0; + } + } + + if (!node._debugSlots && !node._debugBones) + return; + + wrapper.setTransform(this._worldTransform, scaleX, scaleY); + wrapper.setGlobalAlpha(1); + var attachment, drawingUtil = cc._drawingUtil; + if (node._debugSlots) { + // Slots. + drawingUtil.setDrawColor(0, 0, 255, 255); + drawingUtil.setLineWidth(1); + + var points = []; + for (i = 0, n = locSkeleton.slots.length; i < n; i++) { + slot = locSkeleton.drawOrder[i]; + if (!slot.attachment || !(slot.attachment instanceof spine.RegionAttachment)) + continue; + attachment = slot.attachment; + this._updateRegionAttachmentSlot(attachment, slot, points); + drawingUtil.drawPoly(points, 4, true); + } + } + + if (node._debugBones) { + // Bone lengths. + var bone; + drawingUtil.setLineWidth(2); + drawingUtil.setDrawColor(255, 0, 0, 255); + + for (i = 0, n = locSkeleton.bones.length; i < n; i++) { + bone = locSkeleton.bones[i]; + var x = bone.data.length * bone.a + bone.worldX; + var y = bone.data.length * bone.c + bone.worldY; + drawingUtil.drawLine( + {x: bone.worldX, y: bone.worldY}, + {x: x, y: y}); + } + + // Bone origins. + var pointSize = 4; + drawingUtil.setDrawColor(0, 0, 255, 255); // Root bone is blue. + + for (i = 0, n = locSkeleton.bones.length; i < n; i++) { + bone = locSkeleton.bones[i]; + drawingUtil.drawPoint({x: bone.worldX, y: bone.worldY}, pointSize); + if (i === 0) + drawingUtil.setDrawColor(0, 255, 0, 255); + } + } +}; + +proto.updateStatus = function() { + this.originUpdateStatus(); + this._updateCurrentRegions(); + this._regionFlag = cc.Node.CanvasRenderCmd.RegionStatus.DirtyDouble; + this._dirtyFlag &= ~cc.Node._dirtyFlags.contentDirty; +}; + +proto.getLocalBB = function() { + return this._node.getBoundingBox(); +}; + +proto._updateRegionAttachmentSlot = function (attachment, slot, points) { + if (!points) + return; + + var vertices = spine.Utils.setArraySize(new Array(), 8, 0); + attachment.computeWorldVertices(slot.bone, vertices, 0, 2); + var VERTEX = spine.RegionAttachment; + points.length = 0; + points.push(cc.p(vertices[VERTEX.OX1], vertices[VERTEX.OY1])); + points.push(cc.p(vertices[VERTEX.OX4], vertices[VERTEX.OY4])); + points.push(cc.p(vertices[VERTEX.OX3], vertices[VERTEX.OY3])); + points.push(cc.p(vertices[VERTEX.OX2], vertices[VERTEX.OY2])); +}; + +proto._createChildFormSkeletonData = function () { + var node = this._node; + var locSkeleton = node._skeleton, spriteName, sprite; + for (var i = 0, n = locSkeleton.slots.length; i < n; i++) { + var slot = locSkeleton.slots[i], attachment = slot.attachment; + var slotNode = new cc.Node(); + slot._slotNode = slotNode; + + if (attachment instanceof spine.RegionAttachment) { + spriteName = attachment.region.name; + sprite = this._createSprite(slot, attachment); + slot.currentSprite = sprite; + slot.currentSpriteName = spriteName; + slotNode.addChild(sprite); + } else if (attachment instanceof spine.MeshAttachment) { + //todo for mesh + } + } +}; + +var loaded = function (sprite, texture, attachment) { + var rendererObject = attachment.region; + var rect = new cc.Rect(rendererObject.x, rendererObject.y, rendererObject.width, rendererObject.height); + sprite.initWithTexture(texture, rect, rendererObject.rotate, false); + sprite._rect.width = attachment.width; + sprite._rect.height = attachment.height; + sprite.setContentSize(attachment.width, attachment.height); + sprite.setRotation(-attachment.rotation); + sprite.setScale(rendererObject.width / rendererObject.originalWidth * attachment.scaleX, + rendererObject.height / rendererObject.originalHeight * attachment.scaleY); +}; + +proto._createSprite = function (slot, attachment) { + var rendererObject = attachment.region; + var texture = rendererObject.texture.getRealTexture(); + var sprite = new cc.Sprite(); + if (texture.isLoaded()) { + loaded(sprite, texture, attachment); + } else { + texture.addEventListener('load', function () { + loaded(sprite, texture, attachment); + }, this); + } + slot.sprites = slot.sprites || {}; + slot.sprites[rendererObject.name] = sprite; + return sprite; +}; + +proto._updateChild = function () { + var locSkeleton = this._node._skeleton, slots = locSkeleton.slots; + var color = this._displayedColor, opacity = this._displayedOpacity; + var i, n, selSprite, ax, ay; + + var slot, attachment, slotNode; + for (i = 0, n = slots.length; i < n; i++) { + slot = slots[i]; + attachment = slot.attachment; + slotNode = slot._slotNode; + if (!attachment) { + slotNode.setVisible(false); + continue; + } + if (attachment instanceof spine.RegionAttachment) { + if (attachment.region) { + if (!slot.currentSpriteName || slot.currentSpriteName !== attachment.name) { + var spriteName = attachment.region.name; + if (slot.currentSprite !== undefined) + slot.currentSprite.setVisible(false); + slot.sprites = slot.sprites || {}; + if (slot.sprites[spriteName] !== undefined) + slot.sprites[spriteName].setVisible(true); + else { + var sprite = this._createSprite(slot, attachment); + slotNode.addChild(sprite); + } + slot.currentSprite = slot.sprites[spriteName]; + slot.currentSpriteName = spriteName; + } + } + var bone = slot.bone; + if (attachment.region.offsetX === 0 && attachment.region.offsetY === 0) { + ax = attachment.x; + ay = attachment.y; + } + else { + //var regionScaleX = attachment.width / attachment.regionOriginalWidth * attachment.scaleX; + //ax = attachment.x + attachment.regionOffsetX * regionScaleX - (attachment.width * attachment.scaleX - attachment.regionWidth * regionScaleX) / 2; + ax = (attachment.offset[0] + attachment.offset[4]) * 0.5; + ay = (attachment.offset[1] + attachment.offset[5]) * 0.5; + } + slotNode.setPosition(bone.worldX + ax * bone.a + ay * bone.b, bone.worldY + ax * bone.c + ay * bone.d); + slotNode.setScale(bone.getWorldScaleX(), bone.getWorldScaleY()); + + //set the color and opacity + selSprite = slot.currentSprite; + selSprite._flippedX = bone.skeleton.flipX; + selSprite._flippedY = bone.skeleton.flipY; + if (selSprite._flippedY || selSprite._flippedX) { + slotNode.setRotation(bone.getWorldRotationX()); + selSprite.setRotation(attachment.rotation); + } else { + slotNode.setRotation(-bone.getWorldRotationX()); + selSprite.setRotation(-attachment.rotation); + } + + //hack for sprite + selSprite._renderCmd._displayedOpacity = 0 | (opacity * slot.color.a); + var r = 0 | (color.r * slot.color.r), g = 0 | (color.g * slot.color.g), b = 0 | (color.b * slot.color.b); + selSprite.setColor(cc.color(r, g, b)); + selSprite._renderCmd._updateColor(); + } else if (attachment instanceof spine.MeshAttachment) { + // Can not render mesh + } else { + slotNode.setVisible(false); + continue; + } + slotNode.setVisible(true); + } +}; + +})(); diff --git a/frameworks/cocos2d-html5/extensions/spine/CCSkeletonTexture.js b/frameworks/cocos2d-html5/extensions/spine/CCSkeletonTexture.js new file mode 100644 index 0000000..9250369 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/spine/CCSkeletonTexture.js @@ -0,0 +1,67 @@ +/**************************************************************************** + Copyright (c) 2017 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +sp.SkeletonTexture = function (image) { + sp.spine.Texture.call(this, image); +}; +cc.inherits(sp.SkeletonTexture, sp.spine.Texture); +cc.extend(sp.SkeletonTexture.prototype, { + name: 'sp.SkeletonTexture', + _texture: null, + + setRealTexture: function(tex) { + this._texture = tex; + }, + + getRealTexture: function() { + return this._texture; + }, + + setFilters: function(minFilter, magFilter) { + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + var gl = cc._renderContext; + this.bind(); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); + } + }, + + setWraps: function(uWrap, vWrap) { + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + var gl = cc._renderContext; + this.bind(); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, uWrap); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, vWrap); + } + }, + + dispose: function() { + }, + + bind: function() { + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + cc.glBindTexture2D(this._texture); + } + } +}); \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/spine/CCSkeletonWebGLRenderCmd.js b/frameworks/cocos2d-html5/extensions/spine/CCSkeletonWebGLRenderCmd.js new file mode 100644 index 0000000..535e4c0 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/spine/CCSkeletonWebGLRenderCmd.js @@ -0,0 +1,347 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +(function () { + +var spine = sp.spine; + +sp.Skeleton.WebGLRenderCmd = function (renderableObject) { + this._rootCtor(renderableObject); + this._needDraw = true; + this._matrix = new cc.math.Matrix4(); + this._matrix.identity(); + this._currTexture = null; + this._currBlendFunc = {}; + this.vertexType = cc.renderer.VertexType.CUSTOM; + this.setShaderProgram(cc.shaderCache.programForKey(cc.SHADER_SPRITE_POSITION_TEXTURECOLOR)); +}; + +var proto = sp.Skeleton.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); +proto.constructor = sp.Skeleton.WebGLRenderCmd; + +proto.uploadData = function (f32buffer, ui32buffer, vertexDataOffset){ + var node = this._node; + var color = this._displayedColor, locSkeleton = node._skeleton; + + var attachment, slot, i, n; + var premultiAlpha = node._premultipliedAlpha; + + locSkeleton.r = color.r / 255; + locSkeleton.g = color.g / 255; + locSkeleton.b = color.b / 255; + locSkeleton.a = this._displayedOpacity / 255; + if (premultiAlpha) { + locSkeleton.r *= locSkeleton.a; + locSkeleton.g *= locSkeleton.a; + locSkeleton.b *= locSkeleton.a; + } + + var debugSlotsInfo = null; + if (this._node._debugSlots) { + debugSlotsInfo = []; + } + + for (i = 0, n = locSkeleton.drawOrder.length; i < n; i++) { + slot = locSkeleton.drawOrder[i]; + if (!slot.attachment) + continue; + attachment = slot.attachment; + + // get the vertices length + var vertCount = 0; + if (attachment instanceof spine.RegionAttachment) { + vertCount = 6; // a quad = two triangles = six vertices + } + else if (attachment instanceof spine.MeshAttachment) { + vertCount = attachment.regionUVs.length / 2; + } + else { + continue; + } + + // no vertices to render + if (vertCount === 0) { + continue; + } + + var regionTextureAtlas = node.getTextureAtlas(attachment); + + // Broken for changing batch info + this._currTexture = regionTextureAtlas.texture.getRealTexture(); + var batchBroken = cc.renderer._updateBatchedInfo(this._currTexture, this._getBlendFunc(slot.data.blendMode, premultiAlpha), this._glProgramState); + + // keep the same logic with RendererWebGL.js, avoid vertex data overflow + var uploadAll = vertexDataOffset / 6 + vertCount > (cc.BATCH_VERTEX_COUNT - 200) * 0.5; + // Broken for vertex data overflow + if (!batchBroken && uploadAll) { + // render the cached data + cc.renderer._batchRendering(); + batchBroken = true; + } + if (batchBroken) { + vertexDataOffset = 0; + } + + // update the vertex buffer + var slotDebugPoints = null; + if (attachment instanceof spine.RegionAttachment) { + slotDebugPoints = this._uploadRegionAttachmentData(attachment, slot, premultiAlpha, f32buffer, ui32buffer, vertexDataOffset); + } + else if (attachment instanceof spine.MeshAttachment) { + this._uploadMeshAttachmentData(attachment, slot, premultiAlpha, f32buffer, ui32buffer, vertexDataOffset); + } + else { + continue; + } + + if (this._node._debugSlots) { + debugSlotsInfo[i] = slotDebugPoints; + } + + // update the index buffer + if (attachment instanceof spine.RegionAttachment) { + cc.renderer._increaseBatchingSize(vertCount, cc.renderer.VertexType.TRIANGLE); + } else { + cc.renderer._increaseBatchingSize(vertCount, cc.renderer.VertexType.CUSTOM, attachment.triangles); + } + + // update the index data + vertexDataOffset += vertCount * 6; + } + + if (node._debugBones || node._debugSlots) { + // flush previous vertices + cc.renderer._batchRendering(); + + var wt = this._worldTransform, mat = this._matrix.mat; + mat[0] = wt.a; + mat[4] = wt.c; + mat[12] = wt.tx; + mat[1] = wt.b; + mat[5] = wt.d; + mat[13] = wt.ty; + cc.kmGLMatrixMode(cc.KM_GL_MODELVIEW); + cc.current_stack.stack.push(cc.current_stack.top); + cc.current_stack.top = this._matrix; + var drawingUtil = cc._drawingUtil; + + if (node._debugSlots && debugSlotsInfo && debugSlotsInfo.length > 0) { + // Slots. + drawingUtil.setDrawColor(0, 0, 255, 255); + drawingUtil.setLineWidth(1); + + for (i = 0, n = locSkeleton.slots.length; i < n; i++) { + var points = debugSlotsInfo[i]; + if (points) { + drawingUtil.drawPoly(points, 4, true); + } + } + } + + if (node._debugBones) { + // Bone lengths. + var bone; + drawingUtil.setLineWidth(2); + drawingUtil.setDrawColor(255, 0, 0, 255); + + for (i = 0, n = locSkeleton.bones.length; i < n; i++) { + bone = locSkeleton.bones[i]; + var x = bone.data.length * bone.a + bone.worldX; + var y = bone.data.length * bone.c + bone.worldY; + drawingUtil.drawLine(cc.p(bone.worldX, bone.worldY), cc.p(x, y)); + } + + // Bone origins. + drawingUtil.setPointSize(4); + drawingUtil.setDrawColor(0, 0, 255, 255); // Root bone is blue. + + for (i = 0, n = locSkeleton.bones.length; i < n; i++) { + bone = locSkeleton.bones[i]; + drawingUtil.drawPoint(cc.p(bone.worldX, bone.worldY)); + if (i == 0) { + drawingUtil.setDrawColor(0, 255, 0, 255); + } + } + } + cc.kmGLPopMatrix(); + } + + return 0; +}; + +proto._getBlendFunc = function (blendMode, premultiAlpha) { + var ret = this._currBlendFunc; + switch (blendMode) { + case spine.BlendMode.Normal: + ret.src = premultiAlpha ? cc.ONE : cc.SRC_ALPHA; + ret.dst = cc.ONE_MINUS_SRC_ALPHA; + break; + case spine.BlendMode.Additive: + ret.src = premultiAlpha ? cc.ONE : cc.SRC_ALPHA; + ret.dst = cc.ONE; + break; + case spine.BlendMode.Multiply: + ret.src = cc.DST_COLOR; + ret.dst = cc.ONE_MINUS_SRC_ALPHA; + break; + case spine.BlendMode.Screen: + ret.src = cc.ONE; + ret.dst = cc.ONE_MINUS_SRC_COLOR; + break; + default: + ret = this._node._blendFunc; + break; + } + + return ret; +}; + +proto._createChildFormSkeletonData = function(){}; + +proto._updateChild = function(){}; + +proto._uploadRegionAttachmentData = function(attachment, slot, premultipliedAlpha, f32buffer, ui32buffer, vertexDataOffset) { + // the vertices in format: + // [ + // X1, Y1, C1R, C1G, C1B, C1A, U1, V1, // bottom left + // X2, Y2, C2R, C2G, C2B, C2A, U2, V2, // top left + // X3, Y3, C3R, C3G, C3B, C3A, U3, V3, // top right + // X4, Y4, C4R, C4G, C4B, C4A, U4, V4 // bottom right + // ] + // + var nodeColor = this._displayedColor; + var nodeR = nodeColor.r, + nodeG = nodeColor.g, + nodeB = nodeColor.b, + nodeA = this._displayedOpacity; + + var vertices = spine.Utils.setArraySize(new Array(), 8, 0); + attachment.computeWorldVertices(slot.bone, vertices, 0, 2); + + var uvs = attachment.uvs; + + // get the colors data + var skeleton = slot.bone.skeleton; + var skeletonColor = skeleton.color; + var slotColor = slot.color; + var regionColor = attachment.color; + var alpha = skeletonColor.a * slotColor.a * regionColor.a; + var multiplier = premultipliedAlpha ? alpha : 1; + var colors = attachment.tempColor; + colors.set(skeletonColor.r * slotColor.r * regionColor.r * multiplier, + skeletonColor.g * slotColor.g * regionColor.g * multiplier, + skeletonColor.b * slotColor.b * regionColor.b * multiplier, + alpha); + + var wt = this._worldTransform, + wa = wt.a, wb = wt.b, wc = wt.c, wd = wt.d, + wx = wt.tx, wy = wt.ty, + z = this._node.vertexZ; + + var offset = vertexDataOffset; + // generate 6 vertices data (two triangles) from the quad vertices + // using two angles : (0, 1, 2) & (0, 2, 3) + for (var i = 0; i < 6; i++) { + var srcIdx = i < 4 ? i % 3 : i - 2; + var vx = vertices[srcIdx * 2], + vy = vertices[srcIdx * 2 + 1]; + var x = vx * wa + vy * wc + wx, + y = vx * wb + vy * wd + wy; + var r = colors.r * nodeR, + g = colors.g * nodeG, + b = colors.b * nodeB, + a = colors.a * nodeA; + var color = ((a<<24) | (b<<16) | (g<<8) | r); + f32buffer[offset] = x; + f32buffer[offset + 1] = y; + f32buffer[offset + 2] = z; + ui32buffer[offset + 3] = color; + f32buffer[offset + 4] = uvs[srcIdx * 2]; + f32buffer[offset + 5] = uvs[srcIdx * 2 + 1]; + offset += 6; + } + + if (this._node._debugSlots) { + // return the quad points info if debug slot enabled + var VERTEX = spine.RegionAttachment; + return [ + cc.p(vertices[VERTEX.OX1], vertices[VERTEX.OY1]), + cc.p(vertices[VERTEX.OX2], vertices[VERTEX.OY2]), + cc.p(vertices[VERTEX.OX3], vertices[VERTEX.OY3]), + cc.p(vertices[VERTEX.OX4], vertices[VERTEX.OY4]) + ]; + } +}; + +proto._uploadMeshAttachmentData = function(attachment, slot, premultipliedAlpha, f32buffer, ui32buffer, vertexDataOffset) { + var wt = this._worldTransform, + wa = wt.a, wb = wt.b, wc = wt.c, wd = wt.d, + wx = wt.tx, wy = wt.ty, + z = this._node.vertexZ; + // get the vertex data + var verticesLength = attachment.worldVerticesLength; + var vertices = spine.Utils.setArraySize(new Array(), verticesLength, 0); + attachment.computeWorldVertices(slot, 0, verticesLength, vertices, 0, 2); + + var uvs = attachment.uvs; + + // get the colors data + var skeleton = slot.bone.skeleton; + var skeletonColor = skeleton.color, slotColor = slot.color, meshColor = attachment.color; + var alpha = skeletonColor.a * slotColor.a * meshColor.a; + var multiplier = premultipliedAlpha ? alpha : 1; + var colors = attachment.tempColor; + colors.set(skeletonColor.r * slotColor.r * meshColor.r * multiplier, + skeletonColor.g * slotColor.g * meshColor.g * multiplier, + skeletonColor.b * slotColor.b * meshColor.b * multiplier, + alpha); + + var offset = vertexDataOffset; + var nodeColor = this._displayedColor; + var nodeR = nodeColor.r, + nodeG = nodeColor.g, + nodeB = nodeColor.b, + nodeA = this._displayedOpacity; + for (var i = 0, n = vertices.length; i < n; i += 2) { + var vx = vertices[i], + vy = vertices[i + 1]; + var x = vx * wa + vy * wb + wx, + y = vx * wc + vy * wd + wy; + var r = colors.r * nodeR, + g = colors.g * nodeG, + b = colors.b * nodeB, + a = colors.a * nodeA; + var color = ((a<<24) | (b<<16) | (g<<8) | r); + + f32buffer[offset] = x; + f32buffer[offset + 1] = y; + f32buffer[offset + 2] = z; + ui32buffer[offset + 3] = color; + f32buffer[offset + 4] = uvs[i]; + f32buffer[offset + 5] = uvs[i + 1]; + offset += 6; + } +}; + +})(); diff --git a/frameworks/cocos2d-html5/extensions/spine/LICENSE b/frameworks/cocos2d-html5/extensions/spine/LICENSE new file mode 100644 index 0000000..daceab9 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/spine/LICENSE @@ -0,0 +1,27 @@ +Spine Runtimes Software License v2.5 + +Copyright (c) 2013-2016, Esoteric Software +All rights reserved. + +You are granted a perpetual, non-exclusive, non-sublicensable, and +non-transferable license to use, install, execute, and perform the Spine +Runtimes software and derivative works solely for personal or internal +use. Without the written permission of Esoteric Software (see Section 2 of +the Spine Software License Agreement), you may not (a) modify, translate, +adapt, or develop new applications using the Spine Runtimes or otherwise +create derivative works or improvements of the Spine Runtimes or (b) remove, +delete, alter, or obscure any trademarks or any copyright, trademark, patent, +or other intellectual property or proprietary rights notices on or in the +Software, including any copy thereof. Redistributions in binary or source +form must include this license and terms. + +THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "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 ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF +USE, DATA, OR PROFITS) 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. \ No newline at end of file diff --git a/frameworks/cocos2d-html5/extensions/spine/Spine.js b/frameworks/cocos2d-html5/extensions/spine/Spine.js new file mode 100644 index 0000000..e7c0d89 --- /dev/null +++ b/frameworks/cocos2d-html5/extensions/spine/Spine.js @@ -0,0 +1,6458 @@ +// Spine runtime version 3.6.39 + +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var spine; +(function (spine) { + var Animation = (function () { + function Animation(name, timelines, duration) { + if (name == null) + throw new Error("name cannot be null."); + if (timelines == null) + throw new Error("timelines cannot be null."); + this.name = name; + this.timelines = timelines; + this.duration = duration; + } + Animation.prototype.apply = function (skeleton, lastTime, time, loop, events, alpha, pose, direction) { + if (skeleton == null) + throw new Error("skeleton cannot be null."); + if (loop && this.duration != 0) { + time %= this.duration; + if (lastTime > 0) + lastTime %= this.duration; + } + var timelines = this.timelines; + for (var i = 0, n = timelines.length; i < n; i++) + timelines[i].apply(skeleton, lastTime, time, events, alpha, pose, direction); + }; + Animation.binarySearch = function (values, target, step) { + if (step === void 0) { step = 1; } + var low = 0; + var high = values.length / step - 2; + if (high == 0) + return step; + var current = high >>> 1; + while (true) { + if (values[(current + 1) * step] <= target) + low = current + 1; + else + high = current; + if (low == high) + return (low + 1) * step; + current = (low + high) >>> 1; + } + }; + Animation.linearSearch = function (values, target, step) { + for (var i = 0, last = values.length - step; i <= last; i += step) + if (values[i] > target) + return i; + return -1; + }; + return Animation; + }()); + spine.Animation = Animation; + var MixPose; + (function (MixPose) { + MixPose[MixPose["setup"] = 0] = "setup"; + MixPose[MixPose["current"] = 1] = "current"; + MixPose[MixPose["currentLayered"] = 2] = "currentLayered"; + })(MixPose = spine.MixPose || (spine.MixPose = {})); + var MixDirection; + (function (MixDirection) { + MixDirection[MixDirection["in"] = 0] = "in"; + MixDirection[MixDirection["out"] = 1] = "out"; + })(MixDirection = spine.MixDirection || (spine.MixDirection = {})); + var TimelineType; + (function (TimelineType) { + TimelineType[TimelineType["rotate"] = 0] = "rotate"; + TimelineType[TimelineType["translate"] = 1] = "translate"; + TimelineType[TimelineType["scale"] = 2] = "scale"; + TimelineType[TimelineType["shear"] = 3] = "shear"; + TimelineType[TimelineType["attachment"] = 4] = "attachment"; + TimelineType[TimelineType["color"] = 5] = "color"; + TimelineType[TimelineType["deform"] = 6] = "deform"; + TimelineType[TimelineType["event"] = 7] = "event"; + TimelineType[TimelineType["drawOrder"] = 8] = "drawOrder"; + TimelineType[TimelineType["ikConstraint"] = 9] = "ikConstraint"; + TimelineType[TimelineType["transformConstraint"] = 10] = "transformConstraint"; + TimelineType[TimelineType["pathConstraintPosition"] = 11] = "pathConstraintPosition"; + TimelineType[TimelineType["pathConstraintSpacing"] = 12] = "pathConstraintSpacing"; + TimelineType[TimelineType["pathConstraintMix"] = 13] = "pathConstraintMix"; + TimelineType[TimelineType["twoColor"] = 14] = "twoColor"; + })(TimelineType = spine.TimelineType || (spine.TimelineType = {})); + var CurveTimeline = (function () { + function CurveTimeline(frameCount) { + if (frameCount <= 0) + throw new Error("frameCount must be > 0: " + frameCount); + this.curves = spine.Utils.newFloatArray((frameCount - 1) * CurveTimeline.BEZIER_SIZE); + } + CurveTimeline.prototype.getFrameCount = function () { + return this.curves.length / CurveTimeline.BEZIER_SIZE + 1; + }; + CurveTimeline.prototype.setLinear = function (frameIndex) { + this.curves[frameIndex * CurveTimeline.BEZIER_SIZE] = CurveTimeline.LINEAR; + }; + CurveTimeline.prototype.setStepped = function (frameIndex) { + this.curves[frameIndex * CurveTimeline.BEZIER_SIZE] = CurveTimeline.STEPPED; + }; + CurveTimeline.prototype.getCurveType = function (frameIndex) { + var index = frameIndex * CurveTimeline.BEZIER_SIZE; + if (index == this.curves.length) + return CurveTimeline.LINEAR; + var type = this.curves[index]; + if (type == CurveTimeline.LINEAR) + return CurveTimeline.LINEAR; + if (type == CurveTimeline.STEPPED) + return CurveTimeline.STEPPED; + return CurveTimeline.BEZIER; + }; + CurveTimeline.prototype.setCurve = function (frameIndex, cx1, cy1, cx2, cy2) { + var tmpx = (-cx1 * 2 + cx2) * 0.03, tmpy = (-cy1 * 2 + cy2) * 0.03; + var dddfx = ((cx1 - cx2) * 3 + 1) * 0.006, dddfy = ((cy1 - cy2) * 3 + 1) * 0.006; + var ddfx = tmpx * 2 + dddfx, ddfy = tmpy * 2 + dddfy; + var dfx = cx1 * 0.3 + tmpx + dddfx * 0.16666667, dfy = cy1 * 0.3 + tmpy + dddfy * 0.16666667; + var i = frameIndex * CurveTimeline.BEZIER_SIZE; + var curves = this.curves; + curves[i++] = CurveTimeline.BEZIER; + var x = dfx, y = dfy; + for (var n = i + CurveTimeline.BEZIER_SIZE - 1; i < n; i += 2) { + curves[i] = x; + curves[i + 1] = y; + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + x += dfx; + y += dfy; + } + }; + CurveTimeline.prototype.getCurvePercent = function (frameIndex, percent) { + percent = spine.MathUtils.clamp(percent, 0, 1); + var curves = this.curves; + var i = frameIndex * CurveTimeline.BEZIER_SIZE; + var type = curves[i]; + if (type == CurveTimeline.LINEAR) + return percent; + if (type == CurveTimeline.STEPPED) + return 0; + i++; + var x = 0; + for (var start = i, n = i + CurveTimeline.BEZIER_SIZE - 1; i < n; i += 2) { + x = curves[i]; + if (x >= percent) { + var prevX = void 0, prevY = void 0; + if (i == start) { + prevX = 0; + prevY = 0; + } + else { + prevX = curves[i - 2]; + prevY = curves[i - 1]; + } + return prevY + (curves[i + 1] - prevY) * (percent - prevX) / (x - prevX); + } + } + var y = curves[i - 1]; + return y + (1 - y) * (percent - x) / (1 - x); + }; + return CurveTimeline; + }()); + CurveTimeline.LINEAR = 0; + CurveTimeline.STEPPED = 1; + CurveTimeline.BEZIER = 2; + CurveTimeline.BEZIER_SIZE = 10 * 2 - 1; + spine.CurveTimeline = CurveTimeline; + var RotateTimeline = (function (_super) { + __extends(RotateTimeline, _super); + function RotateTimeline(frameCount) { + var _this = _super.call(this, frameCount) || this; + _this.frames = spine.Utils.newFloatArray(frameCount << 1); + return _this; + } + RotateTimeline.prototype.getPropertyId = function () { + return (TimelineType.rotate << 24) + this.boneIndex; + }; + RotateTimeline.prototype.setFrame = function (frameIndex, time, degrees) { + frameIndex <<= 1; + this.frames[frameIndex] = time; + this.frames[frameIndex + RotateTimeline.ROTATION] = degrees; + }; + RotateTimeline.prototype.apply = function (skeleton, lastTime, time, events, alpha, pose, direction) { + var frames = this.frames; + var bone = skeleton.bones[this.boneIndex]; + if (time < frames[0]) { + switch (pose) { + case MixPose.setup: + bone.rotation = bone.data.rotation; + return; + case MixPose.current: + var r_1 = bone.data.rotation - bone.rotation; + r_1 -= (16384 - ((16384.499999999996 - r_1 / 360) | 0)) * 360; + bone.rotation += r_1 * alpha; + } + return; + } + if (time >= frames[frames.length - RotateTimeline.ENTRIES]) { + if (pose == MixPose.setup) + bone.rotation = bone.data.rotation + frames[frames.length + RotateTimeline.PREV_ROTATION] * alpha; + else { + var r_2 = bone.data.rotation + frames[frames.length + RotateTimeline.PREV_ROTATION] - bone.rotation; + r_2 -= (16384 - ((16384.499999999996 - r_2 / 360) | 0)) * 360; + bone.rotation += r_2 * alpha; + } + return; + } + var frame = Animation.binarySearch(frames, time, RotateTimeline.ENTRIES); + var prevRotation = frames[frame + RotateTimeline.PREV_ROTATION]; + var frameTime = frames[frame]; + var percent = this.getCurvePercent((frame >> 1) - 1, 1 - (time - frameTime) / (frames[frame + RotateTimeline.PREV_TIME] - frameTime)); + var r = frames[frame + RotateTimeline.ROTATION] - prevRotation; + r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360; + r = prevRotation + r * percent; + if (pose == MixPose.setup) { + r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360; + bone.rotation = bone.data.rotation + r * alpha; + } + else { + r = bone.data.rotation + r - bone.rotation; + r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360; + bone.rotation += r * alpha; + } + }; + return RotateTimeline; + }(CurveTimeline)); + RotateTimeline.ENTRIES = 2; + RotateTimeline.PREV_TIME = -2; + RotateTimeline.PREV_ROTATION = -1; + RotateTimeline.ROTATION = 1; + spine.RotateTimeline = RotateTimeline; + var TranslateTimeline = (function (_super) { + __extends(TranslateTimeline, _super); + function TranslateTimeline(frameCount) { + var _this = _super.call(this, frameCount) || this; + _this.frames = spine.Utils.newFloatArray(frameCount * TranslateTimeline.ENTRIES); + return _this; + } + TranslateTimeline.prototype.getPropertyId = function () { + return (TimelineType.translate << 24) + this.boneIndex; + }; + TranslateTimeline.prototype.setFrame = function (frameIndex, time, x, y) { + frameIndex *= TranslateTimeline.ENTRIES; + this.frames[frameIndex] = time; + this.frames[frameIndex + TranslateTimeline.X] = x; + this.frames[frameIndex + TranslateTimeline.Y] = y; + }; + TranslateTimeline.prototype.apply = function (skeleton, lastTime, time, events, alpha, pose, direction) { + var frames = this.frames; + var bone = skeleton.bones[this.boneIndex]; + if (time < frames[0]) { + switch (pose) { + case MixPose.setup: + bone.x = bone.data.x; + bone.y = bone.data.y; + return; + case MixPose.current: + bone.x += (bone.data.x - bone.x) * alpha; + bone.y += (bone.data.y - bone.y) * alpha; + } + return; + } + var x = 0, y = 0; + if (time >= frames[frames.length - TranslateTimeline.ENTRIES]) { + x = frames[frames.length + TranslateTimeline.PREV_X]; + y = frames[frames.length + TranslateTimeline.PREV_Y]; + } + else { + var frame = Animation.binarySearch(frames, time, TranslateTimeline.ENTRIES); + x = frames[frame + TranslateTimeline.PREV_X]; + y = frames[frame + TranslateTimeline.PREV_Y]; + var frameTime = frames[frame]; + var percent = this.getCurvePercent(frame / TranslateTimeline.ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + TranslateTimeline.PREV_TIME] - frameTime)); + x += (frames[frame + TranslateTimeline.X] - x) * percent; + y += (frames[frame + TranslateTimeline.Y] - y) * percent; + } + if (pose == MixPose.setup) { + bone.x = bone.data.x + x * alpha; + bone.y = bone.data.y + y * alpha; + } + else { + bone.x += (bone.data.x + x - bone.x) * alpha; + bone.y += (bone.data.y + y - bone.y) * alpha; + } + }; + return TranslateTimeline; + }(CurveTimeline)); + TranslateTimeline.ENTRIES = 3; + TranslateTimeline.PREV_TIME = -3; + TranslateTimeline.PREV_X = -2; + TranslateTimeline.PREV_Y = -1; + TranslateTimeline.X = 1; + TranslateTimeline.Y = 2; + spine.TranslateTimeline = TranslateTimeline; + var ScaleTimeline = (function (_super) { + __extends(ScaleTimeline, _super); + function ScaleTimeline(frameCount) { + return _super.call(this, frameCount) || this; + } + ScaleTimeline.prototype.getPropertyId = function () { + return (TimelineType.scale << 24) + this.boneIndex; + }; + ScaleTimeline.prototype.apply = function (skeleton, lastTime, time, events, alpha, pose, direction) { + var frames = this.frames; + var bone = skeleton.bones[this.boneIndex]; + if (time < frames[0]) { + switch (pose) { + case MixPose.setup: + bone.scaleX = bone.data.scaleX; + bone.scaleY = bone.data.scaleY; + return; + case MixPose.current: + bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha; + bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha; + } + return; + } + var x = 0, y = 0; + if (time >= frames[frames.length - ScaleTimeline.ENTRIES]) { + x = frames[frames.length + ScaleTimeline.PREV_X] * bone.data.scaleX; + y = frames[frames.length + ScaleTimeline.PREV_Y] * bone.data.scaleY; + } + else { + var frame = Animation.binarySearch(frames, time, ScaleTimeline.ENTRIES); + x = frames[frame + ScaleTimeline.PREV_X]; + y = frames[frame + ScaleTimeline.PREV_Y]; + var frameTime = frames[frame]; + var percent = this.getCurvePercent(frame / ScaleTimeline.ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + ScaleTimeline.PREV_TIME] - frameTime)); + x = (x + (frames[frame + ScaleTimeline.X] - x) * percent) * bone.data.scaleX; + y = (y + (frames[frame + ScaleTimeline.Y] - y) * percent) * bone.data.scaleY; + } + if (alpha == 1) { + bone.scaleX = x; + bone.scaleY = y; + } + else { + var bx = 0, by = 0; + if (pose == MixPose.setup) { + bx = bone.data.scaleX; + by = bone.data.scaleY; + } + else { + bx = bone.scaleX; + by = bone.scaleY; + } + if (direction == MixDirection.out) { + x = Math.abs(x) * spine.MathUtils.signum(bx); + y = Math.abs(y) * spine.MathUtils.signum(by); + } + else { + bx = Math.abs(bx) * spine.MathUtils.signum(x); + by = Math.abs(by) * spine.MathUtils.signum(y); + } + bone.scaleX = bx + (x - bx) * alpha; + bone.scaleY = by + (y - by) * alpha; + } + }; + return ScaleTimeline; + }(TranslateTimeline)); + spine.ScaleTimeline = ScaleTimeline; + var ShearTimeline = (function (_super) { + __extends(ShearTimeline, _super); + function ShearTimeline(frameCount) { + return _super.call(this, frameCount) || this; + } + ShearTimeline.prototype.getPropertyId = function () { + return (TimelineType.shear << 24) + this.boneIndex; + }; + ShearTimeline.prototype.apply = function (skeleton, lastTime, time, events, alpha, pose, direction) { + var frames = this.frames; + var bone = skeleton.bones[this.boneIndex]; + if (time < frames[0]) { + switch (pose) { + case MixPose.setup: + bone.shearX = bone.data.shearX; + bone.shearY = bone.data.shearY; + return; + case MixPose.current: + bone.shearX += (bone.data.shearX - bone.shearX) * alpha; + bone.shearY += (bone.data.shearY - bone.shearY) * alpha; + } + return; + } + var x = 0, y = 0; + if (time >= frames[frames.length - ShearTimeline.ENTRIES]) { + x = frames[frames.length + ShearTimeline.PREV_X]; + y = frames[frames.length + ShearTimeline.PREV_Y]; + } + else { + var frame = Animation.binarySearch(frames, time, ShearTimeline.ENTRIES); + x = frames[frame + ShearTimeline.PREV_X]; + y = frames[frame + ShearTimeline.PREV_Y]; + var frameTime = frames[frame]; + var percent = this.getCurvePercent(frame / ShearTimeline.ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + ShearTimeline.PREV_TIME] - frameTime)); + x = x + (frames[frame + ShearTimeline.X] - x) * percent; + y = y + (frames[frame + ShearTimeline.Y] - y) * percent; + } + if (pose == MixPose.setup) { + bone.shearX = bone.data.shearX + x * alpha; + bone.shearY = bone.data.shearY + y * alpha; + } + else { + bone.shearX += (bone.data.shearX + x - bone.shearX) * alpha; + bone.shearY += (bone.data.shearY + y - bone.shearY) * alpha; + } + }; + return ShearTimeline; + }(TranslateTimeline)); + spine.ShearTimeline = ShearTimeline; + var ColorTimeline = (function (_super) { + __extends(ColorTimeline, _super); + function ColorTimeline(frameCount) { + var _this = _super.call(this, frameCount) || this; + _this.frames = spine.Utils.newFloatArray(frameCount * ColorTimeline.ENTRIES); + return _this; + } + ColorTimeline.prototype.getPropertyId = function () { + return (TimelineType.color << 24) + this.slotIndex; + }; + ColorTimeline.prototype.setFrame = function (frameIndex, time, r, g, b, a) { + frameIndex *= ColorTimeline.ENTRIES; + this.frames[frameIndex] = time; + this.frames[frameIndex + ColorTimeline.R] = r; + this.frames[frameIndex + ColorTimeline.G] = g; + this.frames[frameIndex + ColorTimeline.B] = b; + this.frames[frameIndex + ColorTimeline.A] = a; + }; + ColorTimeline.prototype.apply = function (skeleton, lastTime, time, events, alpha, pose, direction) { + var slot = skeleton.slots[this.slotIndex]; + var frames = this.frames; + if (time < frames[0]) { + switch (pose) { + case MixPose.setup: + slot.color.setFromColor(slot.data.color); + return; + case MixPose.current: + var color = slot.color, setup = slot.data.color; + color.add((setup.r - color.r) * alpha, (setup.g - color.g) * alpha, (setup.b - color.b) * alpha, (setup.a - color.a) * alpha); + } + return; + } + var r = 0, g = 0, b = 0, a = 0; + if (time >= frames[frames.length - ColorTimeline.ENTRIES]) { + var i = frames.length; + r = frames[i + ColorTimeline.PREV_R]; + g = frames[i + ColorTimeline.PREV_G]; + b = frames[i + ColorTimeline.PREV_B]; + a = frames[i + ColorTimeline.PREV_A]; + } + else { + var frame = Animation.binarySearch(frames, time, ColorTimeline.ENTRIES); + r = frames[frame + ColorTimeline.PREV_R]; + g = frames[frame + ColorTimeline.PREV_G]; + b = frames[frame + ColorTimeline.PREV_B]; + a = frames[frame + ColorTimeline.PREV_A]; + var frameTime = frames[frame]; + var percent = this.getCurvePercent(frame / ColorTimeline.ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + ColorTimeline.PREV_TIME] - frameTime)); + r += (frames[frame + ColorTimeline.R] - r) * percent; + g += (frames[frame + ColorTimeline.G] - g) * percent; + b += (frames[frame + ColorTimeline.B] - b) * percent; + a += (frames[frame + ColorTimeline.A] - a) * percent; + } + if (alpha == 1) + slot.color.set(r, g, b, a); + else { + var color = slot.color; + if (pose == MixPose.setup) + color.setFromColor(slot.data.color); + color.add((r - color.r) * alpha, (g - color.g) * alpha, (b - color.b) * alpha, (a - color.a) * alpha); + } + }; + return ColorTimeline; + }(CurveTimeline)); + ColorTimeline.ENTRIES = 5; + ColorTimeline.PREV_TIME = -5; + ColorTimeline.PREV_R = -4; + ColorTimeline.PREV_G = -3; + ColorTimeline.PREV_B = -2; + ColorTimeline.PREV_A = -1; + ColorTimeline.R = 1; + ColorTimeline.G = 2; + ColorTimeline.B = 3; + ColorTimeline.A = 4; + spine.ColorTimeline = ColorTimeline; + var TwoColorTimeline = (function (_super) { + __extends(TwoColorTimeline, _super); + function TwoColorTimeline(frameCount) { + var _this = _super.call(this, frameCount) || this; + _this.frames = spine.Utils.newFloatArray(frameCount * TwoColorTimeline.ENTRIES); + return _this; + } + TwoColorTimeline.prototype.getPropertyId = function () { + return (TimelineType.twoColor << 24) + this.slotIndex; + }; + TwoColorTimeline.prototype.setFrame = function (frameIndex, time, r, g, b, a, r2, g2, b2) { + frameIndex *= TwoColorTimeline.ENTRIES; + this.frames[frameIndex] = time; + this.frames[frameIndex + TwoColorTimeline.R] = r; + this.frames[frameIndex + TwoColorTimeline.G] = g; + this.frames[frameIndex + TwoColorTimeline.B] = b; + this.frames[frameIndex + TwoColorTimeline.A] = a; + this.frames[frameIndex + TwoColorTimeline.R2] = r2; + this.frames[frameIndex + TwoColorTimeline.G2] = g2; + this.frames[frameIndex + TwoColorTimeline.B2] = b2; + }; + TwoColorTimeline.prototype.apply = function (skeleton, lastTime, time, events, alpha, pose, direction) { + var slot = skeleton.slots[this.slotIndex]; + var frames = this.frames; + if (time < frames[0]) { + switch (pose) { + case MixPose.setup: + slot.color.setFromColor(slot.data.color); + slot.darkColor.setFromColor(slot.data.darkColor); + return; + case MixPose.current: + var light = slot.color, dark = slot.darkColor, setupLight = slot.data.color, setupDark = slot.data.darkColor; + light.add((setupLight.r - light.r) * alpha, (setupLight.g - light.g) * alpha, (setupLight.b - light.b) * alpha, (setupLight.a - light.a) * alpha); + dark.add((setupDark.r - dark.r) * alpha, (setupDark.g - dark.g) * alpha, (setupDark.b - dark.b) * alpha, 0); + } + return; + } + var r = 0, g = 0, b = 0, a = 0, r2 = 0, g2 = 0, b2 = 0; + if (time >= frames[frames.length - TwoColorTimeline.ENTRIES]) { + var i = frames.length; + r = frames[i + TwoColorTimeline.PREV_R]; + g = frames[i + TwoColorTimeline.PREV_G]; + b = frames[i + TwoColorTimeline.PREV_B]; + a = frames[i + TwoColorTimeline.PREV_A]; + r2 = frames[i + TwoColorTimeline.PREV_R2]; + g2 = frames[i + TwoColorTimeline.PREV_G2]; + b2 = frames[i + TwoColorTimeline.PREV_B2]; + } + else { + var frame = Animation.binarySearch(frames, time, TwoColorTimeline.ENTRIES); + r = frames[frame + TwoColorTimeline.PREV_R]; + g = frames[frame + TwoColorTimeline.PREV_G]; + b = frames[frame + TwoColorTimeline.PREV_B]; + a = frames[frame + TwoColorTimeline.PREV_A]; + r2 = frames[frame + TwoColorTimeline.PREV_R2]; + g2 = frames[frame + TwoColorTimeline.PREV_G2]; + b2 = frames[frame + TwoColorTimeline.PREV_B2]; + var frameTime = frames[frame]; + var percent = this.getCurvePercent(frame / TwoColorTimeline.ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + TwoColorTimeline.PREV_TIME] - frameTime)); + r += (frames[frame + TwoColorTimeline.R] - r) * percent; + g += (frames[frame + TwoColorTimeline.G] - g) * percent; + b += (frames[frame + TwoColorTimeline.B] - b) * percent; + a += (frames[frame + TwoColorTimeline.A] - a) * percent; + r2 += (frames[frame + TwoColorTimeline.R2] - r2) * percent; + g2 += (frames[frame + TwoColorTimeline.G2] - g2) * percent; + b2 += (frames[frame + TwoColorTimeline.B2] - b2) * percent; + } + if (alpha == 1) { + slot.color.set(r, g, b, a); + slot.darkColor.set(r2, g2, b2, 1); + } + else { + var light = slot.color, dark = slot.darkColor; + if (pose == MixPose.setup) { + light.setFromColor(slot.data.color); + dark.setFromColor(slot.data.darkColor); + } + light.add((r - light.r) * alpha, (g - light.g) * alpha, (b - light.b) * alpha, (a - light.a) * alpha); + dark.add((r2 - dark.r) * alpha, (g2 - dark.g) * alpha, (b2 - dark.b) * alpha, 0); + } + }; + return TwoColorTimeline; + }(CurveTimeline)); + TwoColorTimeline.ENTRIES = 8; + TwoColorTimeline.PREV_TIME = -8; + TwoColorTimeline.PREV_R = -7; + TwoColorTimeline.PREV_G = -6; + TwoColorTimeline.PREV_B = -5; + TwoColorTimeline.PREV_A = -4; + TwoColorTimeline.PREV_R2 = -3; + TwoColorTimeline.PREV_G2 = -2; + TwoColorTimeline.PREV_B2 = -1; + TwoColorTimeline.R = 1; + TwoColorTimeline.G = 2; + TwoColorTimeline.B = 3; + TwoColorTimeline.A = 4; + TwoColorTimeline.R2 = 5; + TwoColorTimeline.G2 = 6; + TwoColorTimeline.B2 = 7; + spine.TwoColorTimeline = TwoColorTimeline; + var AttachmentTimeline = (function () { + function AttachmentTimeline(frameCount) { + this.frames = spine.Utils.newFloatArray(frameCount); + this.attachmentNames = new Array(frameCount); + } + AttachmentTimeline.prototype.getPropertyId = function () { + return (TimelineType.attachment << 24) + this.slotIndex; + }; + AttachmentTimeline.prototype.getFrameCount = function () { + return this.frames.length; + }; + AttachmentTimeline.prototype.setFrame = function (frameIndex, time, attachmentName) { + this.frames[frameIndex] = time; + this.attachmentNames[frameIndex] = attachmentName; + }; + AttachmentTimeline.prototype.apply = function (skeleton, lastTime, time, events, alpha, pose, direction) { + var slot = skeleton.slots[this.slotIndex]; + if (direction == MixDirection.out && pose == MixPose.setup) { + var attachmentName_1 = slot.data.attachmentName; + slot.setAttachment(attachmentName_1 == null ? null : skeleton.getAttachment(this.slotIndex, attachmentName_1)); + return; + } + var frames = this.frames; + if (time < frames[0]) { + if (pose == MixPose.setup) { + var attachmentName_2 = slot.data.attachmentName; + slot.setAttachment(attachmentName_2 == null ? null : skeleton.getAttachment(this.slotIndex, attachmentName_2)); + } + return; + } + var frameIndex = 0; + if (time >= frames[frames.length - 1]) + frameIndex = frames.length - 1; + else + frameIndex = Animation.binarySearch(frames, time, 1) - 1; + var attachmentName = this.attachmentNames[frameIndex]; + skeleton.slots[this.slotIndex] + .setAttachment(attachmentName == null ? null : skeleton.getAttachment(this.slotIndex, attachmentName)); + }; + return AttachmentTimeline; + }()); + spine.AttachmentTimeline = AttachmentTimeline; + var zeros = null; + var DeformTimeline = (function (_super) { + __extends(DeformTimeline, _super); + function DeformTimeline(frameCount) { + var _this = _super.call(this, frameCount) || this; + _this.frames = spine.Utils.newFloatArray(frameCount); + _this.frameVertices = new Array(frameCount); + if (zeros == null) + zeros = spine.Utils.newFloatArray(64); + return _this; + } + DeformTimeline.prototype.getPropertyId = function () { + return (TimelineType.deform << 27) + +this.attachment.id + this.slotIndex; + }; + DeformTimeline.prototype.setFrame = function (frameIndex, time, vertices) { + this.frames[frameIndex] = time; + this.frameVertices[frameIndex] = vertices; + }; + DeformTimeline.prototype.apply = function (skeleton, lastTime, time, firedEvents, alpha, pose, direction) { + var slot = skeleton.slots[this.slotIndex]; + var slotAttachment = slot.getAttachment(); + if (!(slotAttachment instanceof spine.VertexAttachment) || !slotAttachment.applyDeform(this.attachment)) + return; + var verticesArray = slot.attachmentVertices; + var frameVertices = this.frameVertices; + var vertexCount = frameVertices[0].length; + var vertices = spine.Utils.setArraySize(verticesArray, vertexCount); + var frames = this.frames; + if (time < frames[0]) { + var vertexAttachment = slotAttachment; + switch (pose) { + case MixPose.setup: + var zeroVertices; + if (vertexAttachment.bones == null) { + zeroVertices = vertexAttachment.vertices; + } + else { + zeroVertices = zeros; + if (zeroVertices.length < vertexCount) + zeros = zeroVertices = spine.Utils.newFloatArray(vertexCount); + } + spine.Utils.arrayCopy(zeroVertices, 0, vertices, 0, vertexCount); + return; + case MixPose.current: + if (alpha == 1) + break; + if (vertexAttachment.bones == null) { + var setupVertices = vertexAttachment.vertices; + for (var i = 0; i < vertexCount; i++) + vertices[i] += (setupVertices[i] - vertices[i]) * alpha; + } + else { + alpha = 1 - alpha; + for (var i = 0; i < vertexCount; i++) + vertices[i] *= alpha; + } + } + return; + } + if (time >= frames[frames.length - 1]) { + var lastVertices = frameVertices[frames.length - 1]; + if (alpha == 1) { + spine.Utils.arrayCopy(lastVertices, 0, vertices, 0, vertexCount); + } + else if (pose == MixPose.setup) { + var vertexAttachment = slotAttachment; + if (vertexAttachment.bones == null) { + var setupVertices_1 = vertexAttachment.vertices; + for (var i_1 = 0; i_1 < vertexCount; i_1++) { + var setup = setupVertices_1[i_1]; + vertices[i_1] = setup + (lastVertices[i_1] - setup) * alpha; + } + } + else { + for (var i_2 = 0; i_2 < vertexCount; i_2++) + vertices[i_2] = lastVertices[i_2] * alpha; + } + } + else { + for (var i_3 = 0; i_3 < vertexCount; i_3++) + vertices[i_3] += (lastVertices[i_3] - vertices[i_3]) * alpha; + } + return; + } + var frame = Animation.binarySearch(frames, time); + var prevVertices = frameVertices[frame - 1]; + var nextVertices = frameVertices[frame]; + var frameTime = frames[frame]; + var percent = this.getCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1] - frameTime)); + if (alpha == 1) { + for (var i_4 = 0; i_4 < vertexCount; i_4++) { + var prev = prevVertices[i_4]; + vertices[i_4] = prev + (nextVertices[i_4] - prev) * percent; + } + } + else if (pose == MixPose.setup) { + var vertexAttachment = slotAttachment; + if (vertexAttachment.bones == null) { + var setupVertices_2 = vertexAttachment.vertices; + for (var i_5 = 0; i_5 < vertexCount; i_5++) { + var prev = prevVertices[i_5], setup = setupVertices_2[i_5]; + vertices[i_5] = setup + (prev + (nextVertices[i_5] - prev) * percent - setup) * alpha; + } + } + else { + for (var i_6 = 0; i_6 < vertexCount; i_6++) { + var prev = prevVertices[i_6]; + vertices[i_6] = (prev + (nextVertices[i_6] - prev) * percent) * alpha; + } + } + } + else { + for (var i_7 = 0; i_7 < vertexCount; i_7++) { + var prev = prevVertices[i_7]; + vertices[i_7] += (prev + (nextVertices[i_7] - prev) * percent - vertices[i_7]) * alpha; + } + } + }; + return DeformTimeline; + }(CurveTimeline)); + spine.DeformTimeline = DeformTimeline; + var EventTimeline = (function () { + function EventTimeline(frameCount) { + this.frames = spine.Utils.newFloatArray(frameCount); + this.events = new Array(frameCount); + } + EventTimeline.prototype.getPropertyId = function () { + return TimelineType.event << 24; + }; + EventTimeline.prototype.getFrameCount = function () { + return this.frames.length; + }; + EventTimeline.prototype.setFrame = function (frameIndex, event) { + this.frames[frameIndex] = event.time; + this.events[frameIndex] = event; + }; + EventTimeline.prototype.apply = function (skeleton, lastTime, time, firedEvents, alpha, pose, direction) { + if (firedEvents == null) + return; + var frames = this.frames; + var frameCount = this.frames.length; + if (lastTime > time) { + this.apply(skeleton, lastTime, Number.MAX_VALUE, firedEvents, alpha, pose, direction); + lastTime = -1; + } + else if (lastTime >= frames[frameCount - 1]) + return; + if (time < frames[0]) + return; + var frame = 0; + if (lastTime < frames[0]) + frame = 0; + else { + frame = Animation.binarySearch(frames, lastTime); + var frameTime = frames[frame]; + while (frame > 0) { + if (frames[frame - 1] != frameTime) + break; + frame--; + } + } + for (; frame < frameCount && time >= frames[frame]; frame++) + firedEvents.push(this.events[frame]); + }; + return EventTimeline; + }()); + spine.EventTimeline = EventTimeline; + var DrawOrderTimeline = (function () { + function DrawOrderTimeline(frameCount) { + this.frames = spine.Utils.newFloatArray(frameCount); + this.drawOrders = new Array(frameCount); + } + DrawOrderTimeline.prototype.getPropertyId = function () { + return TimelineType.drawOrder << 24; + }; + DrawOrderTimeline.prototype.getFrameCount = function () { + return this.frames.length; + }; + DrawOrderTimeline.prototype.setFrame = function (frameIndex, time, drawOrder) { + this.frames[frameIndex] = time; + this.drawOrders[frameIndex] = drawOrder; + }; + DrawOrderTimeline.prototype.apply = function (skeleton, lastTime, time, firedEvents, alpha, pose, direction) { + var drawOrder = skeleton.drawOrder; + var slots = skeleton.slots; + if (direction == MixDirection.out && pose == MixPose.setup) { + spine.Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length); + return; + } + var frames = this.frames; + if (time < frames[0]) { + if (pose == MixPose.setup) + spine.Utils.arrayCopy(skeleton.slots, 0, skeleton.drawOrder, 0, skeleton.slots.length); + return; + } + var frame = 0; + if (time >= frames[frames.length - 1]) + frame = frames.length - 1; + else + frame = Animation.binarySearch(frames, time) - 1; + var drawOrderToSetupIndex = this.drawOrders[frame]; + if (drawOrderToSetupIndex == null) + spine.Utils.arrayCopy(slots, 0, drawOrder, 0, slots.length); + else { + for (var i = 0, n = drawOrderToSetupIndex.length; i < n; i++) + drawOrder[i] = slots[drawOrderToSetupIndex[i]]; + } + }; + return DrawOrderTimeline; + }()); + spine.DrawOrderTimeline = DrawOrderTimeline; + var IkConstraintTimeline = (function (_super) { + __extends(IkConstraintTimeline, _super); + function IkConstraintTimeline(frameCount) { + var _this = _super.call(this, frameCount) || this; + _this.frames = spine.Utils.newFloatArray(frameCount * IkConstraintTimeline.ENTRIES); + return _this; + } + IkConstraintTimeline.prototype.getPropertyId = function () { + return (TimelineType.ikConstraint << 24) + this.ikConstraintIndex; + }; + IkConstraintTimeline.prototype.setFrame = function (frameIndex, time, mix, bendDirection) { + frameIndex *= IkConstraintTimeline.ENTRIES; + this.frames[frameIndex] = time; + this.frames[frameIndex + IkConstraintTimeline.MIX] = mix; + this.frames[frameIndex + IkConstraintTimeline.BEND_DIRECTION] = bendDirection; + }; + IkConstraintTimeline.prototype.apply = function (skeleton, lastTime, time, firedEvents, alpha, pose, direction) { + var frames = this.frames; + var constraint = skeleton.ikConstraints[this.ikConstraintIndex]; + if (time < frames[0]) { + switch (pose) { + case MixPose.setup: + constraint.mix = constraint.data.mix; + constraint.bendDirection = constraint.data.bendDirection; + return; + case MixPose.current: + constraint.mix += (constraint.data.mix - constraint.mix) * alpha; + constraint.bendDirection = constraint.data.bendDirection; + } + return; + } + if (time >= frames[frames.length - IkConstraintTimeline.ENTRIES]) { + if (pose == MixPose.setup) { + constraint.mix = constraint.data.mix + (frames[frames.length + IkConstraintTimeline.PREV_MIX] - constraint.data.mix) * alpha; + constraint.bendDirection = direction == MixDirection.out ? constraint.data.bendDirection + : frames[frames.length + IkConstraintTimeline.PREV_BEND_DIRECTION]; + } + else { + constraint.mix += (frames[frames.length + IkConstraintTimeline.PREV_MIX] - constraint.mix) * alpha; + if (direction == MixDirection["in"]) + constraint.bendDirection = frames[frames.length + IkConstraintTimeline.PREV_BEND_DIRECTION]; + } + return; + } + var frame = Animation.binarySearch(frames, time, IkConstraintTimeline.ENTRIES); + var mix = frames[frame + IkConstraintTimeline.PREV_MIX]; + var frameTime = frames[frame]; + var percent = this.getCurvePercent(frame / IkConstraintTimeline.ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + IkConstraintTimeline.PREV_TIME] - frameTime)); + if (pose == MixPose.setup) { + constraint.mix = constraint.data.mix + (mix + (frames[frame + IkConstraintTimeline.MIX] - mix) * percent - constraint.data.mix) * alpha; + constraint.bendDirection = direction == MixDirection.out ? constraint.data.bendDirection : frames[frame + IkConstraintTimeline.PREV_BEND_DIRECTION]; + } + else { + constraint.mix += (mix + (frames[frame + IkConstraintTimeline.MIX] - mix) * percent - constraint.mix) * alpha; + if (direction == MixDirection["in"]) + constraint.bendDirection = frames[frame + IkConstraintTimeline.PREV_BEND_DIRECTION]; + } + }; + return IkConstraintTimeline; + }(CurveTimeline)); + IkConstraintTimeline.ENTRIES = 3; + IkConstraintTimeline.PREV_TIME = -3; + IkConstraintTimeline.PREV_MIX = -2; + IkConstraintTimeline.PREV_BEND_DIRECTION = -1; + IkConstraintTimeline.MIX = 1; + IkConstraintTimeline.BEND_DIRECTION = 2; + spine.IkConstraintTimeline = IkConstraintTimeline; + var TransformConstraintTimeline = (function (_super) { + __extends(TransformConstraintTimeline, _super); + function TransformConstraintTimeline(frameCount) { + var _this = _super.call(this, frameCount) || this; + _this.frames = spine.Utils.newFloatArray(frameCount * TransformConstraintTimeline.ENTRIES); + return _this; + } + TransformConstraintTimeline.prototype.getPropertyId = function () { + return (TimelineType.transformConstraint << 24) + this.transformConstraintIndex; + }; + TransformConstraintTimeline.prototype.setFrame = function (frameIndex, time, rotateMix, translateMix, scaleMix, shearMix) { + frameIndex *= TransformConstraintTimeline.ENTRIES; + this.frames[frameIndex] = time; + this.frames[frameIndex + TransformConstraintTimeline.ROTATE] = rotateMix; + this.frames[frameIndex + TransformConstraintTimeline.TRANSLATE] = translateMix; + this.frames[frameIndex + TransformConstraintTimeline.SCALE] = scaleMix; + this.frames[frameIndex + TransformConstraintTimeline.SHEAR] = shearMix; + }; + TransformConstraintTimeline.prototype.apply = function (skeleton, lastTime, time, firedEvents, alpha, pose, direction) { + var frames = this.frames; + var constraint = skeleton.transformConstraints[this.transformConstraintIndex]; + if (time < frames[0]) { + var data = constraint.data; + switch (pose) { + case MixPose.setup: + constraint.rotateMix = data.rotateMix; + constraint.translateMix = data.translateMix; + constraint.scaleMix = data.scaleMix; + constraint.shearMix = data.shearMix; + return; + case MixPose.current: + constraint.rotateMix += (data.rotateMix - constraint.rotateMix) * alpha; + constraint.translateMix += (data.translateMix - constraint.translateMix) * alpha; + constraint.scaleMix += (data.scaleMix - constraint.scaleMix) * alpha; + constraint.shearMix += (data.shearMix - constraint.shearMix) * alpha; + } + return; + } + var rotate = 0, translate = 0, scale = 0, shear = 0; + if (time >= frames[frames.length - TransformConstraintTimeline.ENTRIES]) { + var i = frames.length; + rotate = frames[i + TransformConstraintTimeline.PREV_ROTATE]; + translate = frames[i + TransformConstraintTimeline.PREV_TRANSLATE]; + scale = frames[i + TransformConstraintTimeline.PREV_SCALE]; + shear = frames[i + TransformConstraintTimeline.PREV_SHEAR]; + } + else { + var frame = Animation.binarySearch(frames, time, TransformConstraintTimeline.ENTRIES); + rotate = frames[frame + TransformConstraintTimeline.PREV_ROTATE]; + translate = frames[frame + TransformConstraintTimeline.PREV_TRANSLATE]; + scale = frames[frame + TransformConstraintTimeline.PREV_SCALE]; + shear = frames[frame + TransformConstraintTimeline.PREV_SHEAR]; + var frameTime = frames[frame]; + var percent = this.getCurvePercent(frame / TransformConstraintTimeline.ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + TransformConstraintTimeline.PREV_TIME] - frameTime)); + rotate += (frames[frame + TransformConstraintTimeline.ROTATE] - rotate) * percent; + translate += (frames[frame + TransformConstraintTimeline.TRANSLATE] - translate) * percent; + scale += (frames[frame + TransformConstraintTimeline.SCALE] - scale) * percent; + shear += (frames[frame + TransformConstraintTimeline.SHEAR] - shear) * percent; + } + if (pose == MixPose.setup) { + var data = constraint.data; + constraint.rotateMix = data.rotateMix + (rotate - data.rotateMix) * alpha; + constraint.translateMix = data.translateMix + (translate - data.translateMix) * alpha; + constraint.scaleMix = data.scaleMix + (scale - data.scaleMix) * alpha; + constraint.shearMix = data.shearMix + (shear - data.shearMix) * alpha; + } + else { + constraint.rotateMix += (rotate - constraint.rotateMix) * alpha; + constraint.translateMix += (translate - constraint.translateMix) * alpha; + constraint.scaleMix += (scale - constraint.scaleMix) * alpha; + constraint.shearMix += (shear - constraint.shearMix) * alpha; + } + }; + return TransformConstraintTimeline; + }(CurveTimeline)); + TransformConstraintTimeline.ENTRIES = 5; + TransformConstraintTimeline.PREV_TIME = -5; + TransformConstraintTimeline.PREV_ROTATE = -4; + TransformConstraintTimeline.PREV_TRANSLATE = -3; + TransformConstraintTimeline.PREV_SCALE = -2; + TransformConstraintTimeline.PREV_SHEAR = -1; + TransformConstraintTimeline.ROTATE = 1; + TransformConstraintTimeline.TRANSLATE = 2; + TransformConstraintTimeline.SCALE = 3; + TransformConstraintTimeline.SHEAR = 4; + spine.TransformConstraintTimeline = TransformConstraintTimeline; + var PathConstraintPositionTimeline = (function (_super) { + __extends(PathConstraintPositionTimeline, _super); + function PathConstraintPositionTimeline(frameCount) { + var _this = _super.call(this, frameCount) || this; + _this.frames = spine.Utils.newFloatArray(frameCount * PathConstraintPositionTimeline.ENTRIES); + return _this; + } + PathConstraintPositionTimeline.prototype.getPropertyId = function () { + return (TimelineType.pathConstraintPosition << 24) + this.pathConstraintIndex; + }; + PathConstraintPositionTimeline.prototype.setFrame = function (frameIndex, time, value) { + frameIndex *= PathConstraintPositionTimeline.ENTRIES; + this.frames[frameIndex] = time; + this.frames[frameIndex + PathConstraintPositionTimeline.VALUE] = value; + }; + PathConstraintPositionTimeline.prototype.apply = function (skeleton, lastTime, time, firedEvents, alpha, pose, direction) { + var frames = this.frames; + var constraint = skeleton.pathConstraints[this.pathConstraintIndex]; + if (time < frames[0]) { + switch (pose) { + case MixPose.setup: + constraint.position = constraint.data.position; + return; + case MixPose.current: + constraint.position += (constraint.data.position - constraint.position) * alpha; + } + return; + } + var position = 0; + if (time >= frames[frames.length - PathConstraintPositionTimeline.ENTRIES]) + position = frames[frames.length + PathConstraintPositionTimeline.PREV_VALUE]; + else { + var frame = Animation.binarySearch(frames, time, PathConstraintPositionTimeline.ENTRIES); + position = frames[frame + PathConstraintPositionTimeline.PREV_VALUE]; + var frameTime = frames[frame]; + var percent = this.getCurvePercent(frame / PathConstraintPositionTimeline.ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PathConstraintPositionTimeline.PREV_TIME] - frameTime)); + position += (frames[frame + PathConstraintPositionTimeline.VALUE] - position) * percent; + } + if (pose == MixPose.setup) + constraint.position = constraint.data.position + (position - constraint.data.position) * alpha; + else + constraint.position += (position - constraint.position) * alpha; + }; + return PathConstraintPositionTimeline; + }(CurveTimeline)); + PathConstraintPositionTimeline.ENTRIES = 2; + PathConstraintPositionTimeline.PREV_TIME = -2; + PathConstraintPositionTimeline.PREV_VALUE = -1; + PathConstraintPositionTimeline.VALUE = 1; + spine.PathConstraintPositionTimeline = PathConstraintPositionTimeline; + var PathConstraintSpacingTimeline = (function (_super) { + __extends(PathConstraintSpacingTimeline, _super); + function PathConstraintSpacingTimeline(frameCount) { + return _super.call(this, frameCount) || this; + } + PathConstraintSpacingTimeline.prototype.getPropertyId = function () { + return (TimelineType.pathConstraintSpacing << 24) + this.pathConstraintIndex; + }; + PathConstraintSpacingTimeline.prototype.apply = function (skeleton, lastTime, time, firedEvents, alpha, pose, direction) { + var frames = this.frames; + var constraint = skeleton.pathConstraints[this.pathConstraintIndex]; + if (time < frames[0]) { + switch (pose) { + case MixPose.setup: + constraint.spacing = constraint.data.spacing; + return; + case MixPose.current: + constraint.spacing += (constraint.data.spacing - constraint.spacing) * alpha; + } + return; + } + var spacing = 0; + if (time >= frames[frames.length - PathConstraintSpacingTimeline.ENTRIES]) + spacing = frames[frames.length + PathConstraintSpacingTimeline.PREV_VALUE]; + else { + var frame = Animation.binarySearch(frames, time, PathConstraintSpacingTimeline.ENTRIES); + spacing = frames[frame + PathConstraintSpacingTimeline.PREV_VALUE]; + var frameTime = frames[frame]; + var percent = this.getCurvePercent(frame / PathConstraintSpacingTimeline.ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PathConstraintSpacingTimeline.PREV_TIME] - frameTime)); + spacing += (frames[frame + PathConstraintSpacingTimeline.VALUE] - spacing) * percent; + } + if (pose == MixPose.setup) + constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha; + else + constraint.spacing += (spacing - constraint.spacing) * alpha; + }; + return PathConstraintSpacingTimeline; + }(PathConstraintPositionTimeline)); + spine.PathConstraintSpacingTimeline = PathConstraintSpacingTimeline; + var PathConstraintMixTimeline = (function (_super) { + __extends(PathConstraintMixTimeline, _super); + function PathConstraintMixTimeline(frameCount) { + var _this = _super.call(this, frameCount) || this; + _this.frames = spine.Utils.newFloatArray(frameCount * PathConstraintMixTimeline.ENTRIES); + return _this; + } + PathConstraintMixTimeline.prototype.getPropertyId = function () { + return (TimelineType.pathConstraintMix << 24) + this.pathConstraintIndex; + }; + PathConstraintMixTimeline.prototype.setFrame = function (frameIndex, time, rotateMix, translateMix) { + frameIndex *= PathConstraintMixTimeline.ENTRIES; + this.frames[frameIndex] = time; + this.frames[frameIndex + PathConstraintMixTimeline.ROTATE] = rotateMix; + this.frames[frameIndex + PathConstraintMixTimeline.TRANSLATE] = translateMix; + }; + PathConstraintMixTimeline.prototype.apply = function (skeleton, lastTime, time, firedEvents, alpha, pose, direction) { + var frames = this.frames; + var constraint = skeleton.pathConstraints[this.pathConstraintIndex]; + if (time < frames[0]) { + switch (pose) { + case MixPose.setup: + constraint.rotateMix = constraint.data.rotateMix; + constraint.translateMix = constraint.data.translateMix; + return; + case MixPose.current: + constraint.rotateMix += (constraint.data.rotateMix - constraint.rotateMix) * alpha; + constraint.translateMix += (constraint.data.translateMix - constraint.translateMix) * alpha; + } + return; + } + var rotate = 0, translate = 0; + if (time >= frames[frames.length - PathConstraintMixTimeline.ENTRIES]) { + rotate = frames[frames.length + PathConstraintMixTimeline.PREV_ROTATE]; + translate = frames[frames.length + PathConstraintMixTimeline.PREV_TRANSLATE]; + } + else { + var frame = Animation.binarySearch(frames, time, PathConstraintMixTimeline.ENTRIES); + rotate = frames[frame + PathConstraintMixTimeline.PREV_ROTATE]; + translate = frames[frame + PathConstraintMixTimeline.PREV_TRANSLATE]; + var frameTime = frames[frame]; + var percent = this.getCurvePercent(frame / PathConstraintMixTimeline.ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PathConstraintMixTimeline.PREV_TIME] - frameTime)); + rotate += (frames[frame + PathConstraintMixTimeline.ROTATE] - rotate) * percent; + translate += (frames[frame + PathConstraintMixTimeline.TRANSLATE] - translate) * percent; + } + if (pose == MixPose.setup) { + constraint.rotateMix = constraint.data.rotateMix + (rotate - constraint.data.rotateMix) * alpha; + constraint.translateMix = constraint.data.translateMix + (translate - constraint.data.translateMix) * alpha; + } + else { + constraint.rotateMix += (rotate - constraint.rotateMix) * alpha; + constraint.translateMix += (translate - constraint.translateMix) * alpha; + } + }; + return PathConstraintMixTimeline; + }(CurveTimeline)); + PathConstraintMixTimeline.ENTRIES = 3; + PathConstraintMixTimeline.PREV_TIME = -3; + PathConstraintMixTimeline.PREV_ROTATE = -2; + PathConstraintMixTimeline.PREV_TRANSLATE = -1; + PathConstraintMixTimeline.ROTATE = 1; + PathConstraintMixTimeline.TRANSLATE = 2; + spine.PathConstraintMixTimeline = PathConstraintMixTimeline; +})(spine || (spine = {})); +var spine; +(function (spine) { + var AnimationState = (function () { + function AnimationState(data) { + this.tracks = new Array(); + this.events = new Array(); + this.listeners = new Array(); + this.queue = new EventQueue(this); + this.propertyIDs = new spine.IntSet(); + this.mixingTo = new Array(); + this.animationsChanged = false; + this.timeScale = 1; + this.trackEntryPool = new spine.Pool(function () { return new TrackEntry(); }); + this.data = data; + } + AnimationState.prototype.update = function (delta) { + delta *= this.timeScale; + var tracks = this.tracks; + for (var i = 0, n = tracks.length; i < n; i++) { + var current = tracks[i]; + if (current == null) + continue; + current.animationLast = current.nextAnimationLast; + current.trackLast = current.nextTrackLast; + var currentDelta = delta * current.timeScale; + if (current.delay > 0) { + current.delay -= currentDelta; + if (current.delay > 0) + continue; + currentDelta = -current.delay; + current.delay = 0; + } + var next = current.next; + if (next != null) { + var nextTime = current.trackLast - next.delay; + if (nextTime >= 0) { + next.delay = 0; + next.trackTime = nextTime + delta * next.timeScale; + current.trackTime += currentDelta; + this.setCurrent(i, next, true); + while (next.mixingFrom != null) { + next.mixTime += currentDelta; + next = next.mixingFrom; + } + continue; + } + } + else if (current.trackLast >= current.trackEnd && current.mixingFrom == null) { + tracks[i] = null; + this.queue.end(current); + this.disposeNext(current); + continue; + } + if (current.mixingFrom != null && this.updateMixingFrom(current, delta)) { + var from = current.mixingFrom; + current.mixingFrom = null; + while (from != null) { + this.queue.end(from); + from = from.mixingFrom; + } + } + current.trackTime += currentDelta; + } + this.queue.drain(); + }; + AnimationState.prototype.updateMixingFrom = function (to, delta) { + var from = to.mixingFrom; + if (from == null) + return true; + var finished = this.updateMixingFrom(from, delta); + if (to.mixTime > 0 && (to.mixTime >= to.mixDuration || to.timeScale == 0)) { + if (from.totalAlpha == 0 || to.mixDuration == 0) { + to.mixingFrom = from.mixingFrom; + to.interruptAlpha = from.interruptAlpha; + this.queue.end(from); + } + return finished; + } + from.animationLast = from.nextAnimationLast; + from.trackLast = from.nextTrackLast; + from.trackTime += delta * from.timeScale; + to.mixTime += delta * to.timeScale; + return false; + }; + AnimationState.prototype.apply = function (skeleton) { + if (skeleton == null) + throw new Error("skeleton cannot be null."); + if (this.animationsChanged) + this._animationsChanged(); + var events = this.events; + var tracks = this.tracks; + var applied = false; + for (var i = 0, n = tracks.length; i < n; i++) { + var current = tracks[i]; + if (current == null || current.delay > 0) + continue; + applied = true; + var currentPose = i == 0 ? spine.MixPose.current : spine.MixPose.currentLayered; + var mix = current.alpha; + if (current.mixingFrom != null) + mix *= this.applyMixingFrom(current, skeleton, currentPose); + else if (current.trackTime >= current.trackEnd && current.next == null) + mix = 0; + var animationLast = current.animationLast, animationTime = current.getAnimationTime(); + var timelineCount = current.animation.timelines.length; + var timelines = current.animation.timelines; + if (mix == 1) { + for (var ii = 0; ii < timelineCount; ii++) + timelines[ii].apply(skeleton, animationLast, animationTime, events, 1, spine.MixPose.setup, spine.MixDirection["in"]); + } + else { + var timelineData = current.timelineData; + var firstFrame = current.timelinesRotation.length == 0; + if (firstFrame) + spine.Utils.setArraySize(current.timelinesRotation, timelineCount << 1, null); + var timelinesRotation = current.timelinesRotation; + for (var ii = 0; ii < timelineCount; ii++) { + var timeline = timelines[ii]; + var pose = timelineData[ii] >= AnimationState.FIRST ? spine.MixPose.setup : currentPose; + if (timeline instanceof spine.RotateTimeline) { + this.applyRotateTimeline(timeline, skeleton, animationTime, mix, pose, timelinesRotation, ii << 1, firstFrame); + } + else + timeline.apply(skeleton, animationLast, animationTime, events, mix, pose, spine.MixDirection["in"]); + } + } + this.queueEvents(current, animationTime); + events.length = 0; + current.nextAnimationLast = animationTime; + current.nextTrackLast = current.trackTime; + } + this.queue.drain(); + return applied; + }; + AnimationState.prototype.applyMixingFrom = function (to, skeleton, currentPose) { + var from = to.mixingFrom; + if (from.mixingFrom != null) + this.applyMixingFrom(from, skeleton, currentPose); + var mix = 0; + if (to.mixDuration == 0) + mix = 1; + else { + mix = to.mixTime / to.mixDuration; + if (mix > 1) + mix = 1; + } + var events = mix < from.eventThreshold ? this.events : null; + var attachments = mix < from.attachmentThreshold, drawOrder = mix < from.drawOrderThreshold; + var animationLast = from.animationLast, animationTime = from.getAnimationTime(); + var timelineCount = from.animation.timelines.length; + var timelines = from.animation.timelines; + var timelineData = from.timelineData; + var timelineDipMix = from.timelineDipMix; + var firstFrame = from.timelinesRotation.length == 0; + if (firstFrame) + spine.Utils.setArraySize(from.timelinesRotation, timelineCount << 1, null); + var timelinesRotation = from.timelinesRotation; + var pose; + var alphaDip = from.alpha * to.interruptAlpha, alphaMix = alphaDip * (1 - mix), alpha = 0; + from.totalAlpha = 0; + for (var i = 0; i < timelineCount; i++) { + var timeline = timelines[i]; + switch (timelineData[i]) { + case AnimationState.SUBSEQUENT: + if (!attachments && timeline instanceof spine.AttachmentTimeline) + continue; + if (!drawOrder && timeline instanceof spine.DrawOrderTimeline) + continue; + pose = currentPose; + alpha = alphaMix; + break; + case AnimationState.FIRST: + pose = spine.MixPose.setup; + alpha = alphaMix; + break; + case AnimationState.DIP: + pose = spine.MixPose.setup; + alpha = alphaDip; + break; + default: + pose = spine.MixPose.setup; + alpha = alphaDip; + var dipMix = timelineDipMix[i]; + alpha *= Math.max(0, 1 - dipMix.mixTime / dipMix.mixDuration); + break; + } + from.totalAlpha += alpha; + if (timeline instanceof spine.RotateTimeline) + this.applyRotateTimeline(timeline, skeleton, animationTime, alpha, pose, timelinesRotation, i << 1, firstFrame); + else { + timeline.apply(skeleton, animationLast, animationTime, events, alpha, pose, spine.MixDirection.out); + } + } + if (to.mixDuration > 0) + this.queueEvents(from, animationTime); + this.events.length = 0; + from.nextAnimationLast = animationTime; + from.nextTrackLast = from.trackTime; + return mix; + }; + AnimationState.prototype.applyRotateTimeline = function (timeline, skeleton, time, alpha, pose, timelinesRotation, i, firstFrame) { + if (firstFrame) + timelinesRotation[i] = 0; + if (alpha == 1) { + timeline.apply(skeleton, 0, time, null, 1, pose, spine.MixDirection["in"]); + return; + } + var rotateTimeline = timeline; + var frames = rotateTimeline.frames; + var bone = skeleton.bones[rotateTimeline.boneIndex]; + if (time < frames[0]) { + if (pose == spine.MixPose.setup) + bone.rotation = bone.data.rotation; + return; + } + var r2 = 0; + if (time >= frames[frames.length - spine.RotateTimeline.ENTRIES]) + r2 = bone.data.rotation + frames[frames.length + spine.RotateTimeline.PREV_ROTATION]; + else { + var frame = spine.Animation.binarySearch(frames, time, spine.RotateTimeline.ENTRIES); + var prevRotation = frames[frame + spine.RotateTimeline.PREV_ROTATION]; + var frameTime = frames[frame]; + var percent = rotateTimeline.getCurvePercent((frame >> 1) - 1, 1 - (time - frameTime) / (frames[frame + spine.RotateTimeline.PREV_TIME] - frameTime)); + r2 = frames[frame + spine.RotateTimeline.ROTATION] - prevRotation; + r2 -= (16384 - ((16384.499999999996 - r2 / 360) | 0)) * 360; + r2 = prevRotation + r2 * percent + bone.data.rotation; + r2 -= (16384 - ((16384.499999999996 - r2 / 360) | 0)) * 360; + } + var r1 = pose == spine.MixPose.setup ? bone.data.rotation : bone.rotation; + var total = 0, diff = r2 - r1; + if (diff == 0) { + total = timelinesRotation[i]; + } + else { + diff -= (16384 - ((16384.499999999996 - diff / 360) | 0)) * 360; + var lastTotal = 0, lastDiff = 0; + if (firstFrame) { + lastTotal = 0; + lastDiff = diff; + } + else { + lastTotal = timelinesRotation[i]; + lastDiff = timelinesRotation[i + 1]; + } + var current = diff > 0, dir = lastTotal >= 0; + if (spine.MathUtils.signum(lastDiff) != spine.MathUtils.signum(diff) && Math.abs(lastDiff) <= 90) { + if (Math.abs(lastTotal) > 180) + lastTotal += 360 * spine.MathUtils.signum(lastTotal); + dir = current; + } + total = diff + lastTotal - lastTotal % 360; + if (dir != current) + total += 360 * spine.MathUtils.signum(lastTotal); + timelinesRotation[i] = total; + } + timelinesRotation[i + 1] = diff; + r1 += total * alpha; + bone.rotation = r1 - (16384 - ((16384.499999999996 - r1 / 360) | 0)) * 360; + }; + AnimationState.prototype.queueEvents = function (entry, animationTime) { + var animationStart = entry.animationStart, animationEnd = entry.animationEnd; + var duration = animationEnd - animationStart; + var trackLastWrapped = entry.trackLast % duration; + var events = this.events; + var i = 0, n = events.length; + for (; i < n; i++) { + var event_1 = events[i]; + if (event_1.time < trackLastWrapped) + break; + if (event_1.time > animationEnd) + continue; + this.queue.event(entry, event_1); + } + if (entry.loop ? (trackLastWrapped > entry.trackTime % duration) + : (animationTime >= animationEnd && entry.animationLast < animationEnd)) { + this.queue.complete(entry); + } + for (; i < n; i++) { + var event_2 = events[i]; + if (event_2.time < animationStart) + continue; + this.queue.event(entry, events[i]); + } + }; + AnimationState.prototype.clearTracks = function () { + var oldDrainDisabled = this.queue.drainDisabled; + this.queue.drainDisabled = true; + for (var i = 0, n = this.tracks.length; i < n; i++) + this.clearTrack(i); + this.tracks.length = 0; + this.queue.drainDisabled = oldDrainDisabled; + this.queue.drain(); + }; + AnimationState.prototype.clearTrack = function (trackIndex) { + if (trackIndex >= this.tracks.length) + return; + var current = this.tracks[trackIndex]; + if (current == null) + return; + this.queue.end(current); + this.disposeNext(current); + var entry = current; + while (true) { + var from = entry.mixingFrom; + if (from == null) + break; + this.queue.end(from); + entry.mixingFrom = null; + entry = from; + } + this.tracks[current.trackIndex] = null; + this.queue.drain(); + }; + AnimationState.prototype.setCurrent = function (index, current, interrupt) { + var from = this.expandToIndex(index); + this.tracks[index] = current; + if (from != null) { + if (interrupt) + this.queue.interrupt(from); + current.mixingFrom = from; + current.mixTime = 0; + if (from.mixingFrom != null && from.mixDuration > 0) + current.interruptAlpha *= Math.min(1, from.mixTime / from.mixDuration); + from.timelinesRotation.length = 0; + } + this.queue.start(current); + }; + AnimationState.prototype.setAnimation = function (trackIndex, animationName, loop) { + var animation = this.data.skeletonData.findAnimation(animationName); + if (animation == null) + throw new Error("Animation not found: " + animationName); + return this.setAnimationWith(trackIndex, animation, loop); + }; + AnimationState.prototype.setAnimationWith = function (trackIndex, animation, loop) { + if (animation == null) + throw new Error("animation cannot be null."); + var interrupt = true; + var current = this.expandToIndex(trackIndex); + if (current != null) { + if (current.nextTrackLast == -1) { + this.tracks[trackIndex] = current.mixingFrom; + this.queue.interrupt(current); + this.queue.end(current); + this.disposeNext(current); + current = current.mixingFrom; + interrupt = false; + } + else + this.disposeNext(current); + } + var entry = this.trackEntry(trackIndex, animation, loop, current); + this.setCurrent(trackIndex, entry, interrupt); + this.queue.drain(); + return entry; + }; + AnimationState.prototype.addAnimation = function (trackIndex, animationName, loop, delay) { + var animation = this.data.skeletonData.findAnimation(animationName); + if (animation == null) + throw new Error("Animation not found: " + animationName); + return this.addAnimationWith(trackIndex, animation, loop, delay); + }; + AnimationState.prototype.addAnimationWith = function (trackIndex, animation, loop, delay) { + if (animation == null) + throw new Error("animation cannot be null."); + var last = this.expandToIndex(trackIndex); + if (last != null) { + while (last.next != null) + last = last.next; + } + var entry = this.trackEntry(trackIndex, animation, loop, last); + if (last == null) { + this.setCurrent(trackIndex, entry, true); + this.queue.drain(); + } + else { + last.next = entry; + if (delay <= 0) { + var duration = last.animationEnd - last.animationStart; + if (duration != 0) + delay += duration * (1 + ((last.trackTime / duration) | 0)) - this.data.getMix(last.animation, animation); + else + delay = 0; + } + } + entry.delay = delay; + return entry; + }; + AnimationState.prototype.setEmptyAnimation = function (trackIndex, mixDuration) { + var entry = this.setAnimationWith(trackIndex, AnimationState.emptyAnimation, false); + entry.mixDuration = mixDuration; + entry.trackEnd = mixDuration; + return entry; + }; + AnimationState.prototype.addEmptyAnimation = function (trackIndex, mixDuration, delay) { + if (delay <= 0) + delay -= mixDuration; + var entry = this.addAnimationWith(trackIndex, AnimationState.emptyAnimation, false, delay); + entry.mixDuration = mixDuration; + entry.trackEnd = mixDuration; + return entry; + }; + AnimationState.prototype.setEmptyAnimations = function (mixDuration) { + var oldDrainDisabled = this.queue.drainDisabled; + this.queue.drainDisabled = true; + for (var i = 0, n = this.tracks.length; i < n; i++) { + var current = this.tracks[i]; + if (current != null) + this.setEmptyAnimation(current.trackIndex, mixDuration); + } + this.queue.drainDisabled = oldDrainDisabled; + this.queue.drain(); + }; + AnimationState.prototype.expandToIndex = function (index) { + if (index < this.tracks.length) + return this.tracks[index]; + spine.Utils.ensureArrayCapacity(this.tracks, index - this.tracks.length + 1, null); + this.tracks.length = index + 1; + return null; + }; + AnimationState.prototype.trackEntry = function (trackIndex, animation, loop, last) { + var entry = this.trackEntryPool.obtain(); + entry.trackIndex = trackIndex; + entry.animation = animation; + entry.loop = loop; + entry.eventThreshold = 0; + entry.attachmentThreshold = 0; + entry.drawOrderThreshold = 0; + entry.animationStart = 0; + entry.animationEnd = animation.duration; + entry.animationLast = -1; + entry.nextAnimationLast = -1; + entry.delay = 0; + entry.trackTime = 0; + entry.trackLast = -1; + entry.nextTrackLast = -1; + entry.trackEnd = Number.MAX_VALUE; + entry.timeScale = 1; + entry.alpha = 1; + entry.interruptAlpha = 1; + entry.mixTime = 0; + entry.mixDuration = last == null ? 0 : this.data.getMix(last.animation, animation); + return entry; + }; + AnimationState.prototype.disposeNext = function (entry) { + var next = entry.next; + while (next != null) { + this.queue.dispose(next); + next = next.next; + } + entry.next = null; + }; + AnimationState.prototype._animationsChanged = function () { + this.animationsChanged = false; + var propertyIDs = this.propertyIDs; + propertyIDs.clear(); + var mixingTo = this.mixingTo; + for (var i = 0, n = this.tracks.length; i < n; i++) { + var entry = this.tracks[i]; + if (entry != null) + entry.setTimelineData(null, mixingTo, propertyIDs); + } + }; + AnimationState.prototype.getCurrent = function (trackIndex) { + if (trackIndex >= this.tracks.length) + return null; + return this.tracks[trackIndex]; + }; + AnimationState.prototype.addListener = function (listener) { + if (listener == null) + throw new Error("listener cannot be null."); + this.listeners.push(listener); + }; + AnimationState.prototype.removeListener = function (listener) { + var index = this.listeners.indexOf(listener); + if (index >= 0) + this.listeners.splice(index, 1); + }; + AnimationState.prototype.clearListeners = function () { + this.listeners.length = 0; + }; + AnimationState.prototype.clearListenerNotifications = function () { + this.queue.clear(); + }; + return AnimationState; + }()); + AnimationState.emptyAnimation = new spine.Animation("", [], 0); + AnimationState.SUBSEQUENT = 0; + AnimationState.FIRST = 1; + AnimationState.DIP = 2; + AnimationState.DIP_MIX = 3; + spine.AnimationState = AnimationState; + var TrackEntry = (function () { + function TrackEntry() { + this.timelineData = new Array(); + this.timelineDipMix = new Array(); + this.timelinesRotation = new Array(); + } + TrackEntry.prototype.reset = function () { + this.next = null; + this.mixingFrom = null; + this.animation = null; + this.listener = null; + this.timelineData.length = 0; + this.timelineDipMix.length = 0; + this.timelinesRotation.length = 0; + }; + TrackEntry.prototype.setTimelineData = function (to, mixingToArray, propertyIDs) { + if (to != null) + mixingToArray.push(to); + var lastEntry = this.mixingFrom != null ? this.mixingFrom.setTimelineData(this, mixingToArray, propertyIDs) : this; + if (to != null) + mixingToArray.pop(); + var mixingTo = mixingToArray; + var mixingToLast = mixingToArray.length - 1; + var timelines = this.animation.timelines; + var timelinesCount = this.animation.timelines.length; + var timelineData = spine.Utils.setArraySize(this.timelineData, timelinesCount); + this.timelineDipMix.length = 0; + var timelineDipMix = spine.Utils.setArraySize(this.timelineDipMix, timelinesCount); + outer: for (var i = 0; i < timelinesCount; i++) { + var id = timelines[i].getPropertyId(); + if (!propertyIDs.add(id)) + timelineData[i] = AnimationState.SUBSEQUENT; + else if (to == null || !to.hasTimeline(id)) + timelineData[i] = AnimationState.FIRST; + else { + for (var ii = mixingToLast; ii >= 0; ii--) { + var entry = mixingTo[ii]; + if (!entry.hasTimeline(id)) { + if (entry.mixDuration > 0) { + timelineData[i] = AnimationState.DIP_MIX; + timelineDipMix[i] = entry; + continue outer; + } + } + } + timelineData[i] = AnimationState.DIP; + } + } + return lastEntry; + }; + TrackEntry.prototype.hasTimeline = function (id) { + var timelines = this.animation.timelines; + for (var i = 0, n = timelines.length; i < n; i++) + if (timelines[i].getPropertyId() == id) + return true; + return false; + }; + TrackEntry.prototype.getAnimationTime = function () { + if (this.loop) { + var duration = this.animationEnd - this.animationStart; + if (duration == 0) + return this.animationStart; + return (this.trackTime % duration) + this.animationStart; + } + return Math.min(this.trackTime + this.animationStart, this.animationEnd); + }; + TrackEntry.prototype.setAnimationLast = function (animationLast) { + this.animationLast = animationLast; + this.nextAnimationLast = animationLast; + }; + TrackEntry.prototype.isComplete = function () { + return this.trackTime >= this.animationEnd - this.animationStart; + }; + TrackEntry.prototype.resetRotationDirections = function () { + this.timelinesRotation.length = 0; + }; + return TrackEntry; + }()); + spine.TrackEntry = TrackEntry; + var EventQueue = (function () { + function EventQueue(animState) { + this.objects = []; + this.drainDisabled = false; + this.animState = animState; + } + EventQueue.prototype.start = function (entry) { + this.objects.push(EventType.start); + this.objects.push(entry); + this.animState.animationsChanged = true; + }; + EventQueue.prototype.interrupt = function (entry) { + this.objects.push(EventType.interrupt); + this.objects.push(entry); + }; + EventQueue.prototype.end = function (entry) { + this.objects.push(EventType.end); + this.objects.push(entry); + this.animState.animationsChanged = true; + }; + EventQueue.prototype.dispose = function (entry) { + this.objects.push(EventType.dispose); + this.objects.push(entry); + }; + EventQueue.prototype.complete = function (entry) { + this.objects.push(EventType.complete); + this.objects.push(entry); + }; + EventQueue.prototype.event = function (entry, event) { + this.objects.push(EventType.event); + this.objects.push(entry); + this.objects.push(event); + }; + EventQueue.prototype.drain = function () { + if (this.drainDisabled) + return; + this.drainDisabled = true; + var objects = this.objects; + var listeners = this.animState.listeners; + for (var i = 0; i < objects.length; i += 2) { + var type = objects[i]; + var entry = objects[i + 1]; + switch (type) { + case EventType.start: + if (entry.listener != null && entry.listener.start) + entry.listener.start(entry); + for (var ii = 0; ii < listeners.length; ii++) + if (listeners[ii].start) + listeners[ii].start(entry); + break; + case EventType.interrupt: + if (entry.listener != null && entry.listener.interrupt) + entry.listener.interrupt(entry); + for (var ii = 0; ii < listeners.length; ii++) + if (listeners[ii].interrupt) + listeners[ii].interrupt(entry); + break; + case EventType.end: + if (entry.listener != null && entry.listener.end) + entry.listener.end(entry); + for (var ii = 0; ii < listeners.length; ii++) + if (listeners[ii].end) + listeners[ii].end(entry); + case EventType.dispose: + if (entry.listener != null && entry.listener.dispose) + entry.listener.dispose(entry); + for (var ii = 0; ii < listeners.length; ii++) + if (listeners[ii].dispose) + listeners[ii].dispose(entry); + this.animState.trackEntryPool.free(entry); + break; + case EventType.complete: + if (entry.listener != null && entry.listener.complete) + entry.listener.complete(entry); + for (var ii = 0; ii < listeners.length; ii++) + if (listeners[ii].complete) + listeners[ii].complete(entry); + break; + case EventType.event: + var event_3 = objects[i++ + 2]; + if (entry.listener != null && entry.listener.event) + entry.listener.event(entry, event_3); + for (var ii = 0; ii < listeners.length; ii++) + if (listeners[ii].event) + listeners[ii].event(entry, event_3); + break; + } + } + this.clear(); + this.drainDisabled = false; + }; + EventQueue.prototype.clear = function () { + this.objects.length = 0; + }; + return EventQueue; + }()); + spine.EventQueue = EventQueue; + var EventType; + (function (EventType) { + EventType[EventType["start"] = 0] = "start"; + EventType[EventType["interrupt"] = 1] = "interrupt"; + EventType[EventType["end"] = 2] = "end"; + EventType[EventType["dispose"] = 3] = "dispose"; + EventType[EventType["complete"] = 4] = "complete"; + EventType[EventType["event"] = 5] = "event"; + })(EventType = spine.EventType || (spine.EventType = {})); + var AnimationStateAdapter2 = (function () { + function AnimationStateAdapter2() { + } + AnimationStateAdapter2.prototype.start = function (entry) { + }; + AnimationStateAdapter2.prototype.interrupt = function (entry) { + }; + AnimationStateAdapter2.prototype.end = function (entry) { + }; + AnimationStateAdapter2.prototype.dispose = function (entry) { + }; + AnimationStateAdapter2.prototype.complete = function (entry) { + }; + AnimationStateAdapter2.prototype.event = function (entry, event) { + }; + return AnimationStateAdapter2; + }()); + spine.AnimationStateAdapter2 = AnimationStateAdapter2; +})(spine || (spine = {})); +var spine; +(function (spine) { + var AnimationStateData = (function () { + function AnimationStateData(skeletonData) { + this.animationToMixTime = {}; + this.defaultMix = 0; + if (skeletonData == null) + throw new Error("skeletonData cannot be null."); + this.skeletonData = skeletonData; + } + AnimationStateData.prototype.setMix = function (fromName, toName, duration) { + var from = this.skeletonData.findAnimation(fromName); + if (from == null) + throw new Error("Animation not found: " + fromName); + var to = this.skeletonData.findAnimation(toName); + if (to == null) + throw new Error("Animation not found: " + toName); + this.setMixWith(from, to, duration); + }; + AnimationStateData.prototype.setMixWith = function (from, to, duration) { + if (from == null) + throw new Error("from cannot be null."); + if (to == null) + throw new Error("to cannot be null."); + var key = from.name + to.name; + this.animationToMixTime[key] = duration; + }; + AnimationStateData.prototype.getMix = function (from, to) { + var key = from.name + to.name; + var value = this.animationToMixTime[key]; + return value === undefined ? this.defaultMix : value; + }; + return AnimationStateData; + }()); + spine.AnimationStateData = AnimationStateData; +})(spine || (spine = {})); +var spine; +(function (spine) { + var AssetManager = (function () { + function AssetManager(textureLoader, pathPrefix) { + if (pathPrefix === void 0) { pathPrefix = ""; } + this.assets = {}; + this.errors = {}; + this.toLoad = 0; + this.loaded = 0; + this.textureLoader = textureLoader; + this.pathPrefix = pathPrefix; + } + AssetManager.prototype.loadText = function (path, success, error) { + var _this = this; + if (success === void 0) { success = null; } + if (error === void 0) { error = null; } + path = this.pathPrefix + path; + this.toLoad++; + var request = new XMLHttpRequest(); + request.onreadystatechange = function () { + if (request.readyState == XMLHttpRequest.DONE) { + if (request.status >= 200 && request.status < 300) { + _this.assets[path] = request.responseText; + if (success) + success(path, request.responseText); + } + else { + _this.errors[path] = "Couldn't load text " + path + ": status " + request.status + ", " + request.responseText; + if (error) + error(path, "Couldn't load text " + path + ": status " + request.status + ", " + request.responseText); + } + _this.toLoad--; + _this.loaded++; + } + }; + request.open("GET", path, true); + request.send(); + }; + AssetManager.prototype.loadTexture = function (path, success, error) { + var _this = this; + if (success === void 0) { success = null; } + if (error === void 0) { error = null; } + path = this.pathPrefix + path; + this.toLoad++; + var img = new Image(); + img.crossOrigin = "anonymous"; + img.onload = function (ev) { + var texture = _this.textureLoader(img); + _this.assets[path] = texture; + _this.toLoad--; + _this.loaded++; + if (success) + success(path, img); + }; + img.onerror = function (ev) { + _this.errors[path] = "Couldn't load image " + path; + _this.toLoad--; + _this.loaded++; + if (error) + error(path, "Couldn't load image " + path); + }; + img.src = path; + }; + AssetManager.prototype.loadTextureData = function (path, data, success, error) { + var _this = this; + if (success === void 0) { success = null; } + if (error === void 0) { error = null; } + path = this.pathPrefix + path; + this.toLoad++; + var img = new Image(); + img.onload = function (ev) { + var texture = _this.textureLoader(img); + _this.assets[path] = texture; + _this.toLoad--; + _this.loaded++; + if (success) + success(path, img); + }; + img.onerror = function (ev) { + _this.errors[path] = "Couldn't load image " + path; + _this.toLoad--; + _this.loaded++; + if (error) + error(path, "Couldn't load image " + path); + }; + img.src = data; + }; + AssetManager.prototype.get = function (path) { + path = this.pathPrefix + path; + return this.assets[path]; + }; + AssetManager.prototype.remove = function (path) { + path = this.pathPrefix + path; + var asset = this.assets[path]; + if (asset.dispose) + asset.dispose(); + this.assets[path] = null; + }; + AssetManager.prototype.removeAll = function () { + for (var key in this.assets) { + var asset = this.assets[key]; + if (asset.dispose) + asset.dispose(); + } + this.assets = {}; + }; + AssetManager.prototype.isLoadingComplete = function () { + return this.toLoad == 0; + }; + AssetManager.prototype.getToLoad = function () { + return this.toLoad; + }; + AssetManager.prototype.getLoaded = function () { + return this.loaded; + }; + AssetManager.prototype.dispose = function () { + this.removeAll(); + }; + AssetManager.prototype.hasErrors = function () { + return Object.keys(this.errors).length > 0; + }; + AssetManager.prototype.getErrors = function () { + return this.errors; + }; + return AssetManager; + }()); + spine.AssetManager = AssetManager; +})(spine || (spine = {})); +var spine; +(function (spine) { + var AtlasAttachmentLoader = (function () { + function AtlasAttachmentLoader(atlas) { + this.atlas = atlas; + } + AtlasAttachmentLoader.prototype.newRegionAttachment = function (skin, name, path) { + var region = this.atlas.findRegion(path); + if (region == null) + throw new Error("Region not found in atlas: " + path + " (region attachment: " + name + ")"); + region.renderObject = region; + var attachment = new spine.RegionAttachment(name); + attachment.setRegion(region); + return attachment; + }; + AtlasAttachmentLoader.prototype.newMeshAttachment = function (skin, name, path) { + var region = this.atlas.findRegion(path); + if (region == null) + throw new Error("Region not found in atlas: " + path + " (mesh attachment: " + name + ")"); + region.renderObject = region; + var attachment = new spine.MeshAttachment(name); + attachment.region = region; + return attachment; + }; + AtlasAttachmentLoader.prototype.newBoundingBoxAttachment = function (skin, name) { + return new spine.BoundingBoxAttachment(name); + }; + AtlasAttachmentLoader.prototype.newPathAttachment = function (skin, name) { + return new spine.PathAttachment(name); + }; + AtlasAttachmentLoader.prototype.newPointAttachment = function (skin, name) { + return new spine.PointAttachment(name); + }; + AtlasAttachmentLoader.prototype.newClippingAttachment = function (skin, name) { + return new spine.ClippingAttachment(name); + }; + return AtlasAttachmentLoader; + }()); + spine.AtlasAttachmentLoader = AtlasAttachmentLoader; +})(spine || (spine = {})); +var spine; +(function (spine) { + var BlendMode; + (function (BlendMode) { + BlendMode[BlendMode["Normal"] = 0] = "Normal"; + BlendMode[BlendMode["Additive"] = 1] = "Additive"; + BlendMode[BlendMode["Multiply"] = 2] = "Multiply"; + BlendMode[BlendMode["Screen"] = 3] = "Screen"; + })(BlendMode = spine.BlendMode || (spine.BlendMode = {})); +})(spine || (spine = {})); +var spine; +(function (spine) { + var Bone = (function () { + function Bone(data, skeleton, parent) { + this.children = new Array(); + this.x = 0; + this.y = 0; + this.rotation = 0; + this.scaleX = 0; + this.scaleY = 0; + this.shearX = 0; + this.shearY = 0; + this.ax = 0; + this.ay = 0; + this.arotation = 0; + this.ascaleX = 0; + this.ascaleY = 0; + this.ashearX = 0; + this.ashearY = 0; + this.appliedValid = false; + this.a = 0; + this.b = 0; + this.worldX = 0; + this.c = 0; + this.d = 0; + this.worldY = 0; + this.sorted = false; + if (data == null) + throw new Error("data cannot be null."); + if (skeleton == null) + throw new Error("skeleton cannot be null."); + this.data = data; + this.skeleton = skeleton; + this.parent = parent; + this.setToSetupPose(); + } + Bone.prototype.update = function () { + this.updateWorldTransformWith(this.x, this.y, this.rotation, this.scaleX, this.scaleY, this.shearX, this.shearY); + }; + Bone.prototype.updateWorldTransform = function () { + this.updateWorldTransformWith(this.x, this.y, this.rotation, this.scaleX, this.scaleY, this.shearX, this.shearY); + }; + Bone.prototype.updateWorldTransformWith = function (x, y, rotation, scaleX, scaleY, shearX, shearY) { + this.ax = x; + this.ay = y; + this.arotation = rotation; + this.ascaleX = scaleX; + this.ascaleY = scaleY; + this.ashearX = shearX; + this.ashearY = shearY; + this.appliedValid = true; + var parent = this.parent; + if (parent == null) { + var rotationY = rotation + 90 + shearY; + var la = spine.MathUtils.cosDeg(rotation + shearX) * scaleX; + var lb = spine.MathUtils.cosDeg(rotationY) * scaleY; + var lc = spine.MathUtils.sinDeg(rotation + shearX) * scaleX; + var ld = spine.MathUtils.sinDeg(rotationY) * scaleY; + var skeleton = this.skeleton; + if (skeleton.flipX) { + x = -x; + la = -la; + lb = -lb; + } + if (skeleton.flipY) { + y = -y; + lc = -lc; + ld = -ld; + } + this.a = la; + this.b = lb; + this.c = lc; + this.d = ld; + this.worldX = x + skeleton.x; + this.worldY = y + skeleton.y; + return; + } + var pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d; + this.worldX = pa * x + pb * y + parent.worldX; + this.worldY = pc * x + pd * y + parent.worldY; + switch (this.data.transformMode) { + case spine.TransformMode.Normal: { + var rotationY = rotation + 90 + shearY; + var la = spine.MathUtils.cosDeg(rotation + shearX) * scaleX; + var lb = spine.MathUtils.cosDeg(rotationY) * scaleY; + var lc = spine.MathUtils.sinDeg(rotation + shearX) * scaleX; + var ld = spine.MathUtils.sinDeg(rotationY) * scaleY; + this.a = pa * la + pb * lc; + this.b = pa * lb + pb * ld; + this.c = pc * la + pd * lc; + this.d = pc * lb + pd * ld; + return; + } + case spine.TransformMode.OnlyTranslation: { + var rotationY = rotation + 90 + shearY; + this.a = spine.MathUtils.cosDeg(rotation + shearX) * scaleX; + this.b = spine.MathUtils.cosDeg(rotationY) * scaleY; + this.c = spine.MathUtils.sinDeg(rotation + shearX) * scaleX; + this.d = spine.MathUtils.sinDeg(rotationY) * scaleY; + break; + } + case spine.TransformMode.NoRotationOrReflection: { + var s = pa * pa + pc * pc; + var prx = 0; + if (s > 0.0001) { + s = Math.abs(pa * pd - pb * pc) / s; + pb = pc * s; + pd = pa * s; + prx = Math.atan2(pc, pa) * spine.MathUtils.radDeg; + } + else { + pa = 0; + pc = 0; + prx = 90 - Math.atan2(pd, pb) * spine.MathUtils.radDeg; + } + var rx = rotation + shearX - prx; + var ry = rotation + shearY - prx + 90; + var la = spine.MathUtils.cosDeg(rx) * scaleX; + var lb = spine.MathUtils.cosDeg(ry) * scaleY; + var lc = spine.MathUtils.sinDeg(rx) * scaleX; + var ld = spine.MathUtils.sinDeg(ry) * scaleY; + this.a = pa * la - pb * lc; + this.b = pa * lb - pb * ld; + this.c = pc * la + pd * lc; + this.d = pc * lb + pd * ld; + break; + } + case spine.TransformMode.NoScale: + case spine.TransformMode.NoScaleOrReflection: { + var cos = spine.MathUtils.cosDeg(rotation); + var sin = spine.MathUtils.sinDeg(rotation); + var za = pa * cos + pb * sin; + var zc = pc * cos + pd * sin; + var s = Math.sqrt(za * za + zc * zc); + if (s > 0.00001) + s = 1 / s; + za *= s; + zc *= s; + s = Math.sqrt(za * za + zc * zc); + var r = Math.PI / 2 + Math.atan2(zc, za); + var zb = Math.cos(r) * s; + var zd = Math.sin(r) * s; + var la = spine.MathUtils.cosDeg(shearX) * scaleX; + var lb = spine.MathUtils.cosDeg(90 + shearY) * scaleY; + var lc = spine.MathUtils.sinDeg(shearX) * scaleX; + var ld = spine.MathUtils.sinDeg(90 + shearY) * scaleY; + if (this.data.transformMode != spine.TransformMode.NoScaleOrReflection ? pa * pd - pb * pc < 0 : this.skeleton.flipX != this.skeleton.flipY) { + zb = -zb; + zd = -zd; + } + this.a = za * la + zb * lc; + this.b = za * lb + zb * ld; + this.c = zc * la + zd * lc; + this.d = zc * lb + zd * ld; + return; + } + } + if (this.skeleton.flipX) { + this.a = -this.a; + this.b = -this.b; + } + if (this.skeleton.flipY) { + this.c = -this.c; + this.d = -this.d; + } + }; + Bone.prototype.setToSetupPose = function () { + var data = this.data; + this.x = data.x; + this.y = data.y; + this.rotation = data.rotation; + this.scaleX = data.scaleX; + this.scaleY = data.scaleY; + this.shearX = data.shearX; + this.shearY = data.shearY; + }; + Bone.prototype.getWorldRotationX = function () { + return Math.atan2(this.c, this.a) * spine.MathUtils.radDeg; + }; + Bone.prototype.getWorldRotationY = function () { + return Math.atan2(this.d, this.b) * spine.MathUtils.radDeg; + }; + Bone.prototype.getWorldScaleX = function () { + return Math.sqrt(this.a * this.a + this.c * this.c); + }; + Bone.prototype.getWorldScaleY = function () { + return Math.sqrt(this.b * this.b + this.d * this.d); + }; + Bone.prototype.updateAppliedTransform = function () { + this.appliedValid = true; + var parent = this.parent; + if (parent == null) { + this.ax = this.worldX; + this.ay = this.worldY; + this.arotation = Math.atan2(this.c, this.a) * spine.MathUtils.radDeg; + this.ascaleX = Math.sqrt(this.a * this.a + this.c * this.c); + this.ascaleY = Math.sqrt(this.b * this.b + this.d * this.d); + this.ashearX = 0; + this.ashearY = Math.atan2(this.a * this.b + this.c * this.d, this.a * this.d - this.b * this.c) * spine.MathUtils.radDeg; + return; + } + var pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d; + var pid = 1 / (pa * pd - pb * pc); + var dx = this.worldX - parent.worldX, dy = this.worldY - parent.worldY; + this.ax = (dx * pd * pid - dy * pb * pid); + this.ay = (dy * pa * pid - dx * pc * pid); + var ia = pid * pd; + var id = pid * pa; + var ib = pid * pb; + var ic = pid * pc; + var ra = ia * this.a - ib * this.c; + var rb = ia * this.b - ib * this.d; + var rc = id * this.c - ic * this.a; + var rd = id * this.d - ic * this.b; + this.ashearX = 0; + this.ascaleX = Math.sqrt(ra * ra + rc * rc); + if (this.ascaleX > 0.0001) { + var det = ra * rd - rb * rc; + this.ascaleY = det / this.ascaleX; + this.ashearY = Math.atan2(ra * rb + rc * rd, det) * spine.MathUtils.radDeg; + this.arotation = Math.atan2(rc, ra) * spine.MathUtils.radDeg; + } + else { + this.ascaleX = 0; + this.ascaleY = Math.sqrt(rb * rb + rd * rd); + this.ashearY = 0; + this.arotation = 90 - Math.atan2(rd, rb) * spine.MathUtils.radDeg; + } + }; + Bone.prototype.worldToLocal = function (world) { + var a = this.a, b = this.b, c = this.c, d = this.d; + var invDet = 1 / (a * d - b * c); + var x = world.x - this.worldX, y = world.y - this.worldY; + world.x = (x * d * invDet - y * b * invDet); + world.y = (y * a * invDet - x * c * invDet); + return world; + }; + Bone.prototype.localToWorld = function (local) { + var x = local.x, y = local.y; + local.x = x * this.a + y * this.b + this.worldX; + local.y = x * this.c + y * this.d + this.worldY; + return local; + }; + Bone.prototype.worldToLocalRotation = function (worldRotation) { + var sin = spine.MathUtils.sinDeg(worldRotation), cos = spine.MathUtils.cosDeg(worldRotation); + return Math.atan2(this.a * sin - this.c * cos, this.d * cos - this.b * sin) * spine.MathUtils.radDeg; + }; + Bone.prototype.localToWorldRotation = function (localRotation) { + var sin = spine.MathUtils.sinDeg(localRotation), cos = spine.MathUtils.cosDeg(localRotation); + return Math.atan2(cos * this.c + sin * this.d, cos * this.a + sin * this.b) * spine.MathUtils.radDeg; + }; + Bone.prototype.rotateWorld = function (degrees) { + var a = this.a, b = this.b, c = this.c, d = this.d; + var cos = spine.MathUtils.cosDeg(degrees), sin = spine.MathUtils.sinDeg(degrees); + this.a = cos * a - sin * c; + this.b = cos * b - sin * d; + this.c = sin * a + cos * c; + this.d = sin * b + cos * d; + this.appliedValid = false; + }; + return Bone; + }()); + spine.Bone = Bone; +})(spine || (spine = {})); +var spine; +(function (spine) { + var BoneData = (function () { + function BoneData(index, name, parent) { + this.x = 0; + this.y = 0; + this.rotation = 0; + this.scaleX = 1; + this.scaleY = 1; + this.shearX = 0; + this.shearY = 0; + this.transformMode = TransformMode.Normal; + if (index < 0) + throw new Error("index must be >= 0."); + if (name == null) + throw new Error("name cannot be null."); + this.index = index; + this.name = name; + this.parent = parent; + } + return BoneData; + }()); + spine.BoneData = BoneData; + var TransformMode; + (function (TransformMode) { + TransformMode[TransformMode["Normal"] = 0] = "Normal"; + TransformMode[TransformMode["OnlyTranslation"] = 1] = "OnlyTranslation"; + TransformMode[TransformMode["NoRotationOrReflection"] = 2] = "NoRotationOrReflection"; + TransformMode[TransformMode["NoScale"] = 3] = "NoScale"; + TransformMode[TransformMode["NoScaleOrReflection"] = 4] = "NoScaleOrReflection"; + })(TransformMode = spine.TransformMode || (spine.TransformMode = {})); +})(spine || (spine = {})); +var spine; +(function (spine) { + var Event = (function () { + function Event(time, data) { + if (data == null) + throw new Error("data cannot be null."); + this.time = time; + this.data = data; + } + return Event; + }()); + spine.Event = Event; +})(spine || (spine = {})); +var spine; +(function (spine) { + var EventData = (function () { + function EventData(name) { + this.name = name; + } + return EventData; + }()); + spine.EventData = EventData; +})(spine || (spine = {})); +var spine; +(function (spine) { + var IkConstraint = (function () { + function IkConstraint(data, skeleton) { + this.mix = 1; + this.bendDirection = 0; + if (data == null) + throw new Error("data cannot be null."); + if (skeleton == null) + throw new Error("skeleton cannot be null."); + this.data = data; + this.mix = data.mix; + this.bendDirection = data.bendDirection; + this.bones = new Array(); + for (var i = 0; i < data.bones.length; i++) + this.bones.push(skeleton.findBone(data.bones[i].name)); + this.target = skeleton.findBone(data.target.name); + } + IkConstraint.prototype.getOrder = function () { + return this.data.order; + }; + IkConstraint.prototype.apply = function () { + this.update(); + }; + IkConstraint.prototype.update = function () { + var target = this.target; + var bones = this.bones; + switch (bones.length) { + case 1: + this.apply1(bones[0], target.worldX, target.worldY, this.mix); + break; + case 2: + this.apply2(bones[0], bones[1], target.worldX, target.worldY, this.bendDirection, this.mix); + break; + } + }; + IkConstraint.prototype.apply1 = function (bone, targetX, targetY, alpha) { + if (!bone.appliedValid) + bone.updateAppliedTransform(); + var p = bone.parent; + var id = 1 / (p.a * p.d - p.b * p.c); + var x = targetX - p.worldX, y = targetY - p.worldY; + var tx = (x * p.d - y * p.b) * id - bone.ax, ty = (y * p.a - x * p.c) * id - bone.ay; + var rotationIK = Math.atan2(ty, tx) * spine.MathUtils.radDeg - bone.ashearX - bone.arotation; + if (bone.ascaleX < 0) + rotationIK += 180; + if (rotationIK > 180) + rotationIK -= 360; + else if (rotationIK < -180) + rotationIK += 360; + bone.updateWorldTransformWith(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, bone.ascaleX, bone.ascaleY, bone.ashearX, bone.ashearY); + }; + IkConstraint.prototype.apply2 = function (parent, child, targetX, targetY, bendDir, alpha) { + if (alpha == 0) { + child.updateWorldTransform(); + return; + } + if (!parent.appliedValid) + parent.updateAppliedTransform(); + if (!child.appliedValid) + child.updateAppliedTransform(); + var px = parent.ax, py = parent.ay, psx = parent.ascaleX, psy = parent.ascaleY, csx = child.ascaleX; + var os1 = 0, os2 = 0, s2 = 0; + if (psx < 0) { + psx = -psx; + os1 = 180; + s2 = -1; + } + else { + os1 = 0; + s2 = 1; + } + if (psy < 0) { + psy = -psy; + s2 = -s2; + } + if (csx < 0) { + csx = -csx; + os2 = 180; + } + else + os2 = 0; + var cx = child.ax, cy = 0, cwx = 0, cwy = 0, a = parent.a, b = parent.b, c = parent.c, d = parent.d; + var u = Math.abs(psx - psy) <= 0.0001; + if (!u) { + cy = 0; + cwx = a * cx + parent.worldX; + cwy = c * cx + parent.worldY; + } + else { + cy = child.ay; + cwx = a * cx + b * cy + parent.worldX; + cwy = c * cx + d * cy + parent.worldY; + } + var pp = parent.parent; + a = pp.a; + b = pp.b; + c = pp.c; + d = pp.d; + var id = 1 / (a * d - b * c), x = targetX - pp.worldX, y = targetY - pp.worldY; + var tx = (x * d - y * b) * id - px, ty = (y * a - x * c) * id - py; + x = cwx - pp.worldX; + y = cwy - pp.worldY; + var dx = (x * d - y * b) * id - px, dy = (y * a - x * c) * id - py; + var l1 = Math.sqrt(dx * dx + dy * dy), l2 = child.data.length * csx, a1 = 0, a2 = 0; + outer: if (u) { + l2 *= psx; + var cos = (tx * tx + ty * ty - l1 * l1 - l2 * l2) / (2 * l1 * l2); + if (cos < -1) + cos = -1; + else if (cos > 1) + cos = 1; + a2 = Math.acos(cos) * bendDir; + a = l1 + l2 * cos; + b = l2 * Math.sin(a2); + a1 = Math.atan2(ty * a - tx * b, tx * a + ty * b); + } + else { + a = psx * l2; + b = psy * l2; + var aa = a * a, bb = b * b, dd = tx * tx + ty * ty, ta = Math.atan2(ty, tx); + c = bb * l1 * l1 + aa * dd - aa * bb; + var c1 = -2 * bb * l1, c2 = bb - aa; + d = c1 * c1 - 4 * c2 * c; + if (d >= 0) { + var q = Math.sqrt(d); + if (c1 < 0) + q = -q; + q = -(c1 + q) / 2; + var r0 = q / c2, r1 = c / q; + var r = Math.abs(r0) < Math.abs(r1) ? r0 : r1; + if (r * r <= dd) { + y = Math.sqrt(dd - r * r) * bendDir; + a1 = ta - Math.atan2(y, r); + a2 = Math.atan2(y / psy, (r - l1) / psx); + break outer; + } + } + var minAngle = spine.MathUtils.PI, minX = l1 - a, minDist = minX * minX, minY = 0; + var maxAngle = 0, maxX = l1 + a, maxDist = maxX * maxX, maxY = 0; + c = -a * l1 / (aa - bb); + if (c >= -1 && c <= 1) { + c = Math.acos(c); + x = a * Math.cos(c) + l1; + y = b * Math.sin(c); + d = x * x + y * y; + if (d < minDist) { + minAngle = c; + minDist = d; + minX = x; + minY = y; + } + if (d > maxDist) { + maxAngle = c; + maxDist = d; + maxX = x; + maxY = y; + } + } + if (dd <= (minDist + maxDist) / 2) { + a1 = ta - Math.atan2(minY * bendDir, minX); + a2 = minAngle * bendDir; + } + else { + a1 = ta - Math.atan2(maxY * bendDir, maxX); + a2 = maxAngle * bendDir; + } + } + var os = Math.atan2(cy, cx) * s2; + var rotation = parent.arotation; + a1 = (a1 - os) * spine.MathUtils.radDeg + os1 - rotation; + if (a1 > 180) + a1 -= 360; + else if (a1 < -180) + a1 += 360; + parent.updateWorldTransformWith(px, py, rotation + a1 * alpha, parent.ascaleX, parent.ascaleY, 0, 0); + rotation = child.arotation; + a2 = ((a2 + os) * spine.MathUtils.radDeg - child.ashearX) * s2 + os2 - rotation; + if (a2 > 180) + a2 -= 360; + else if (a2 < -180) + a2 += 360; + child.updateWorldTransformWith(cx, cy, rotation + a2 * alpha, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY); + }; + return IkConstraint; + }()); + spine.IkConstraint = IkConstraint; +})(spine || (spine = {})); +var spine; +(function (spine) { + var IkConstraintData = (function () { + function IkConstraintData(name) { + this.order = 0; + this.bones = new Array(); + this.bendDirection = 1; + this.mix = 1; + this.name = name; + } + return IkConstraintData; + }()); + spine.IkConstraintData = IkConstraintData; +})(spine || (spine = {})); +var spine; +(function (spine) { + var PathConstraint = (function () { + function PathConstraint(data, skeleton) { + this.position = 0; + this.spacing = 0; + this.rotateMix = 0; + this.translateMix = 0; + this.spaces = new Array(); + this.positions = new Array(); + this.world = new Array(); + this.curves = new Array(); + this.lengths = new Array(); + this.segments = new Array(); + if (data == null) + throw new Error("data cannot be null."); + if (skeleton == null) + throw new Error("skeleton cannot be null."); + this.data = data; + this.bones = new Array(); + for (var i = 0, n = data.bones.length; i < n; i++) + this.bones.push(skeleton.findBone(data.bones[i].name)); + this.target = skeleton.findSlot(data.target.name); + this.position = data.position; + this.spacing = data.spacing; + this.rotateMix = data.rotateMix; + this.translateMix = data.translateMix; + } + PathConstraint.prototype.apply = function () { + this.update(); + }; + PathConstraint.prototype.update = function () { + var attachment = this.target.getAttachment(); + if (!(attachment instanceof spine.PathAttachment)) + return; + var rotateMix = this.rotateMix, translateMix = this.translateMix; + var translate = translateMix > 0, rotate = rotateMix > 0; + if (!translate && !rotate) + return; + var data = this.data; + var spacingMode = data.spacingMode; + var lengthSpacing = spacingMode == spine.SpacingMode.Length; + var rotateMode = data.rotateMode; + var tangents = rotateMode == spine.RotateMode.Tangent, scale = rotateMode == spine.RotateMode.ChainScale; + var boneCount = this.bones.length, spacesCount = tangents ? boneCount : boneCount + 1; + var bones = this.bones; + var spaces = spine.Utils.setArraySize(this.spaces, spacesCount), lengths = null; + var spacing = this.spacing; + if (scale || lengthSpacing) { + if (scale) + lengths = spine.Utils.setArraySize(this.lengths, boneCount); + for (var i = 0, n = spacesCount - 1; i < n;) { + var bone = bones[i]; + var setupLength = bone.data.length; + if (setupLength == 0) + setupLength = 0.0000001; + var x = setupLength * bone.a, y = setupLength * bone.c; + var length_1 = Math.sqrt(x * x + y * y); + if (scale) + lengths[i] = length_1; + spaces[++i] = (lengthSpacing ? setupLength + spacing : spacing) * length_1 / setupLength; + } + } + else { + for (var i = 1; i < spacesCount; i++) + spaces[i] = spacing; + } + var positions = this.computeWorldPositions(attachment, spacesCount, tangents, data.positionMode == spine.PositionMode.Percent, spacingMode == spine.SpacingMode.Percent); + var boneX = positions[0], boneY = positions[1], offsetRotation = data.offsetRotation; + var tip = false; + if (offsetRotation == 0) + tip = rotateMode == spine.RotateMode.Chain; + else { + tip = false; + var p = this.target.bone; + offsetRotation *= p.a * p.d - p.b * p.c > 0 ? spine.MathUtils.degRad : -spine.MathUtils.degRad; + } + for (var i = 0, p = 3; i < boneCount; i++, p += 3) { + var bone = bones[i]; + bone.worldX += (boneX - bone.worldX) * translateMix; + bone.worldY += (boneY - bone.worldY) * translateMix; + var x = positions[p], y = positions[p + 1], dx = x - boneX, dy = y - boneY; + if (scale) { + var length_2 = lengths[i]; + if (length_2 != 0) { + var s = (Math.sqrt(dx * dx + dy * dy) / length_2 - 1) * rotateMix + 1; + bone.a *= s; + bone.c *= s; + } + } + boneX = x; + boneY = y; + if (rotate) { + var a = bone.a, b = bone.b, c = bone.c, d = bone.d, r = 0, cos = 0, sin = 0; + if (tangents) + r = positions[p - 1]; + else if (spaces[i + 1] == 0) + r = positions[p + 2]; + else + r = Math.atan2(dy, dx); + r -= Math.atan2(c, a); + if (tip) { + cos = Math.cos(r); + sin = Math.sin(r); + var length_3 = bone.data.length; + boneX += (length_3 * (cos * a - sin * c) - dx) * rotateMix; + boneY += (length_3 * (sin * a + cos * c) - dy) * rotateMix; + } + else { + r += offsetRotation; + } + if (r > spine.MathUtils.PI) + r -= spine.MathUtils.PI2; + else if (r < -spine.MathUtils.PI) + r += spine.MathUtils.PI2; + r *= rotateMix; + cos = Math.cos(r); + sin = Math.sin(r); + bone.a = cos * a - sin * c; + bone.b = cos * b - sin * d; + bone.c = sin * a + cos * c; + bone.d = sin * b + cos * d; + } + bone.appliedValid = false; + } + }; + PathConstraint.prototype.computeWorldPositions = function (path, spacesCount, tangents, percentPosition, percentSpacing) { + var target = this.target; + var position = this.position; + var spaces = this.spaces, out = spine.Utils.setArraySize(this.positions, spacesCount * 3 + 2), world = null; + var closed = path.closed; + var verticesLength = path.worldVerticesLength, curveCount = verticesLength / 6, prevCurve = PathConstraint.NONE; + if (!path.constantSpeed) { + var lengths = path.lengths; + curveCount -= closed ? 1 : 2; + var pathLength_1 = lengths[curveCount]; + if (percentPosition) + position *= pathLength_1; + if (percentSpacing) { + for (var i = 0; i < spacesCount; i++) + spaces[i] *= pathLength_1; + } + world = spine.Utils.setArraySize(this.world, 8); + for (var i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) { + var space = spaces[i]; + position += space; + var p = position; + if (closed) { + p %= pathLength_1; + if (p < 0) + p += pathLength_1; + curve = 0; + } + else if (p < 0) { + if (prevCurve != PathConstraint.BEFORE) { + prevCurve = PathConstraint.BEFORE; + path.computeWorldVertices(target, 2, 4, world, 0, 2); + } + this.addBeforePosition(p, world, 0, out, o); + continue; + } + else if (p > pathLength_1) { + if (prevCurve != PathConstraint.AFTER) { + prevCurve = PathConstraint.AFTER; + path.computeWorldVertices(target, verticesLength - 6, 4, world, 0, 2); + } + this.addAfterPosition(p - pathLength_1, world, 0, out, o); + continue; + } + for (;; curve++) { + var length_4 = lengths[curve]; + if (p > length_4) + continue; + if (curve == 0) + p /= length_4; + else { + var prev = lengths[curve - 1]; + p = (p - prev) / (length_4 - prev); + } + break; + } + if (curve != prevCurve) { + prevCurve = curve; + if (closed && curve == curveCount) { + path.computeWorldVertices(target, verticesLength - 4, 4, world, 0, 2); + path.computeWorldVertices(target, 0, 4, world, 4, 2); + } + else + path.computeWorldVertices(target, curve * 6 + 2, 8, world, 0, 2); + } + this.addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o, tangents || (i > 0 && space == 0)); + } + return out; + } + if (closed) { + verticesLength += 2; + world = spine.Utils.setArraySize(this.world, verticesLength); + path.computeWorldVertices(target, 2, verticesLength - 4, world, 0, 2); + path.computeWorldVertices(target, 0, 2, world, verticesLength - 4, 2); + world[verticesLength - 2] = world[0]; + world[verticesLength - 1] = world[1]; + } + else { + curveCount--; + verticesLength -= 4; + world = spine.Utils.setArraySize(this.world, verticesLength); + path.computeWorldVertices(target, 2, verticesLength, world, 0, 2); + } + var curves = spine.Utils.setArraySize(this.curves, curveCount); + var pathLength = 0; + var x1 = world[0], y1 = world[1], cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, x2 = 0, y2 = 0; + var tmpx = 0, tmpy = 0, dddfx = 0, dddfy = 0, ddfx = 0, ddfy = 0, dfx = 0, dfy = 0; + for (var i = 0, w = 2; i < curveCount; i++, w += 6) { + cx1 = world[w]; + cy1 = world[w + 1]; + cx2 = world[w + 2]; + cy2 = world[w + 3]; + x2 = world[w + 4]; + y2 = world[w + 5]; + tmpx = (x1 - cx1 * 2 + cx2) * 0.1875; + tmpy = (y1 - cy1 * 2 + cy2) * 0.1875; + dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.09375; + dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.09375; + ddfx = tmpx * 2 + dddfx; + ddfy = tmpy * 2 + dddfy; + dfx = (cx1 - x1) * 0.75 + tmpx + dddfx * 0.16666667; + dfy = (cy1 - y1) * 0.75 + tmpy + dddfy * 0.16666667; + pathLength += Math.sqrt(dfx * dfx + dfy * dfy); + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + pathLength += Math.sqrt(dfx * dfx + dfy * dfy); + dfx += ddfx; + dfy += ddfy; + pathLength += Math.sqrt(dfx * dfx + dfy * dfy); + dfx += ddfx + dddfx; + dfy += ddfy + dddfy; + pathLength += Math.sqrt(dfx * dfx + dfy * dfy); + curves[i] = pathLength; + x1 = x2; + y1 = y2; + } + if (percentPosition) + position *= pathLength; + if (percentSpacing) { + for (var i = 0; i < spacesCount; i++) + spaces[i] *= pathLength; + } + var segments = this.segments; + var curveLength = 0; + for (var i = 0, o = 0, curve = 0, segment = 0; i < spacesCount; i++, o += 3) { + var space = spaces[i]; + position += space; + var p = position; + if (closed) { + p %= pathLength; + if (p < 0) + p += pathLength; + curve = 0; + } + else if (p < 0) { + this.addBeforePosition(p, world, 0, out, o); + continue; + } + else if (p > pathLength) { + this.addAfterPosition(p - pathLength, world, verticesLength - 4, out, o); + continue; + } + for (;; curve++) { + var length_5 = curves[curve]; + if (p > length_5) + continue; + if (curve == 0) + p /= length_5; + else { + var prev = curves[curve - 1]; + p = (p - prev) / (length_5 - prev); + } + break; + } + if (curve != prevCurve) { + prevCurve = curve; + var ii = curve * 6; + x1 = world[ii]; + y1 = world[ii + 1]; + cx1 = world[ii + 2]; + cy1 = world[ii + 3]; + cx2 = world[ii + 4]; + cy2 = world[ii + 5]; + x2 = world[ii + 6]; + y2 = world[ii + 7]; + tmpx = (x1 - cx1 * 2 + cx2) * 0.03; + tmpy = (y1 - cy1 * 2 + cy2) * 0.03; + dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.006; + dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.006; + ddfx = tmpx * 2 + dddfx; + ddfy = tmpy * 2 + dddfy; + dfx = (cx1 - x1) * 0.3 + tmpx + dddfx * 0.16666667; + dfy = (cy1 - y1) * 0.3 + tmpy + dddfy * 0.16666667; + curveLength = Math.sqrt(dfx * dfx + dfy * dfy); + segments[0] = curveLength; + for (ii = 1; ii < 8; ii++) { + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + curveLength += Math.sqrt(dfx * dfx + dfy * dfy); + segments[ii] = curveLength; + } + dfx += ddfx; + dfy += ddfy; + curveLength += Math.sqrt(dfx * dfx + dfy * dfy); + segments[8] = curveLength; + dfx += ddfx + dddfx; + dfy += ddfy + dddfy; + curveLength += Math.sqrt(dfx * dfx + dfy * dfy); + segments[9] = curveLength; + segment = 0; + } + p *= curveLength; + for (;; segment++) { + var length_6 = segments[segment]; + if (p > length_6) + continue; + if (segment == 0) + p /= length_6; + else { + var prev = segments[segment - 1]; + p = segment + (p - prev) / (length_6 - prev); + } + break; + } + this.addCurvePosition(p * 0.1, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o, tangents || (i > 0 && space == 0)); + } + return out; + }; + PathConstraint.prototype.addBeforePosition = function (p, temp, i, out, o) { + var x1 = temp[i], y1 = temp[i + 1], dx = temp[i + 2] - x1, dy = temp[i + 3] - y1, r = Math.atan2(dy, dx); + out[o] = x1 + p * Math.cos(r); + out[o + 1] = y1 + p * Math.sin(r); + out[o + 2] = r; + }; + PathConstraint.prototype.addAfterPosition = function (p, temp, i, out, o) { + var x1 = temp[i + 2], y1 = temp[i + 3], dx = x1 - temp[i], dy = y1 - temp[i + 1], r = Math.atan2(dy, dx); + out[o] = x1 + p * Math.cos(r); + out[o + 1] = y1 + p * Math.sin(r); + out[o + 2] = r; + }; + PathConstraint.prototype.addCurvePosition = function (p, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o, tangents) { + if (p == 0 || isNaN(p)) + p = 0.0001; + var tt = p * p, ttt = tt * p, u = 1 - p, uu = u * u, uuu = uu * u; + var ut = u * p, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * p; + var x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt; + out[o] = x; + out[o + 1] = y; + if (tangents) + out[o + 2] = Math.atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt)); + }; + PathConstraint.prototype.getOrder = function () { + return this.data.order; + }; + return PathConstraint; + }()); + PathConstraint.NONE = -1; + PathConstraint.BEFORE = -2; + PathConstraint.AFTER = -3; + spine.PathConstraint = PathConstraint; +})(spine || (spine = {})); +var spine; +(function (spine) { + var PathConstraintData = (function () { + function PathConstraintData(name) { + this.order = 0; + this.bones = new Array(); + this.name = name; + } + return PathConstraintData; + }()); + spine.PathConstraintData = PathConstraintData; + var PositionMode; + (function (PositionMode) { + PositionMode[PositionMode["Fixed"] = 0] = "Fixed"; + PositionMode[PositionMode["Percent"] = 1] = "Percent"; + })(PositionMode = spine.PositionMode || (spine.PositionMode = {})); + var SpacingMode; + (function (SpacingMode) { + SpacingMode[SpacingMode["Length"] = 0] = "Length"; + SpacingMode[SpacingMode["Fixed"] = 1] = "Fixed"; + SpacingMode[SpacingMode["Percent"] = 2] = "Percent"; + })(SpacingMode = spine.SpacingMode || (spine.SpacingMode = {})); + var RotateMode; + (function (RotateMode) { + RotateMode[RotateMode["Tangent"] = 0] = "Tangent"; + RotateMode[RotateMode["Chain"] = 1] = "Chain"; + RotateMode[RotateMode["ChainScale"] = 2] = "ChainScale"; + })(RotateMode = spine.RotateMode || (spine.RotateMode = {})); +})(spine || (spine = {})); +(function () { + if (!Math.fround) { + Math.fround = (function (array) { + return function (x) { + return array[0] = x, array[0]; + }; + })(new Float32Array(1)); + } +})(); +var spine; +(function (spine) { + var Assets = (function () { + function Assets(clientId) { + this.toLoad = new Array(); + this.assets = {}; + this.clientId = clientId; + } + Assets.prototype.loaded = function () { + var i = 0; + for (var v in this.assets) + i++; + return i; + }; + return Assets; + }()); + var SharedAssetManager = (function () { + function SharedAssetManager(pathPrefix) { + if (pathPrefix === void 0) { pathPrefix = ""; } + this.clientAssets = {}; + this.queuedAssets = {}; + this.rawAssets = {}; + this.errors = {}; + this.pathPrefix = pathPrefix; + } + SharedAssetManager.prototype.queueAsset = function (clientId, textureLoader, path) { + var clientAssets = this.clientAssets[clientId]; + if (clientAssets === null || clientAssets === undefined) { + clientAssets = new Assets(clientId); + this.clientAssets[clientId] = clientAssets; + } + if (textureLoader !== null) + clientAssets.textureLoader = textureLoader; + clientAssets.toLoad.push(path); + if (this.queuedAssets[path] === path) { + return false; + } + else { + this.queuedAssets[path] = path; + return true; + } + }; + SharedAssetManager.prototype.loadText = function (clientId, path) { + var _this = this; + path = this.pathPrefix + path; + if (!this.queueAsset(clientId, null, path)) + return; + var request = new XMLHttpRequest(); + request.onreadystatechange = function () { + if (request.readyState == XMLHttpRequest.DONE) { + if (request.status >= 200 && request.status < 300) { + _this.rawAssets[path] = request.responseText; + } + else { + _this.errors[path] = "Couldn't load text " + path + ": status " + request.status + ", " + request.responseText; + } + } + }; + request.open("GET", path, true); + request.send(); + }; + SharedAssetManager.prototype.loadJson = function (clientId, path) { + var _this = this; + path = this.pathPrefix + path; + if (!this.queueAsset(clientId, null, path)) + return; + var request = new XMLHttpRequest(); + request.onreadystatechange = function () { + if (request.readyState == XMLHttpRequest.DONE) { + if (request.status >= 200 && request.status < 300) { + _this.rawAssets[path] = JSON.parse(request.responseText); + } + else { + _this.errors[path] = "Couldn't load text " + path + ": status " + request.status + ", " + request.responseText; + } + } + }; + request.open("GET", path, true); + request.send(); + }; + SharedAssetManager.prototype.loadTexture = function (clientId, textureLoader, path) { + var _this = this; + path = this.pathPrefix + path; + if (!this.queueAsset(clientId, textureLoader, path)) + return; + var img = new Image(); + img.src = path; + img.crossOrigin = "anonymous"; + img.onload = function (ev) { + _this.rawAssets[path] = img; + }; + img.onerror = function (ev) { + _this.errors[path] = "Couldn't load image " + path; + }; + }; + SharedAssetManager.prototype.get = function (clientId, path) { + path = this.pathPrefix + path; + var clientAssets = this.clientAssets[clientId]; + if (clientAssets === null || clientAssets === undefined) + return true; + return clientAssets.assets[path]; + }; + SharedAssetManager.prototype.updateClientAssets = function (clientAssets) { + for (var i = 0; i < clientAssets.toLoad.length; i++) { + var path = clientAssets.toLoad[i]; + var asset = clientAssets.assets[path]; + if (asset === null || asset === undefined) { + var rawAsset = this.rawAssets[path]; + if (rawAsset === null || rawAsset === undefined) + continue; + if (rawAsset instanceof HTMLImageElement) { + clientAssets.assets[path] = clientAssets.textureLoader(rawAsset); + } + else { + clientAssets.assets[path] = rawAsset; + } + } + } + }; + SharedAssetManager.prototype.isLoadingComplete = function (clientId) { + var clientAssets = this.clientAssets[clientId]; + if (clientAssets === null || clientAssets === undefined) + return true; + this.updateClientAssets(clientAssets); + return clientAssets.toLoad.length == clientAssets.loaded(); + }; + SharedAssetManager.prototype.dispose = function () { + }; + SharedAssetManager.prototype.hasErrors = function () { + return Object.keys(this.errors).length > 0; + }; + SharedAssetManager.prototype.getErrors = function () { + return this.errors; + }; + return SharedAssetManager; + }()); + spine.SharedAssetManager = SharedAssetManager; +})(spine || (spine = {})); +var spine; +(function (spine) { + var Skeleton = (function () { + function Skeleton(data) { + this._updateCache = new Array(); + this.updateCacheReset = new Array(); + this.time = 0; + this.flipX = false; + this.flipY = false; + this.x = 0; + this.y = 0; + if (data == null) + throw new Error("data cannot be null."); + this.data = data; + this.bones = new Array(); + for (var i = 0; i < data.bones.length; i++) { + var boneData = data.bones[i]; + var bone = void 0; + if (boneData.parent == null) + bone = new spine.Bone(boneData, this, null); + else { + var parent_1 = this.bones[boneData.parent.index]; + bone = new spine.Bone(boneData, this, parent_1); + parent_1.children.push(bone); + } + this.bones.push(bone); + } + this.slots = new Array(); + this.drawOrder = new Array(); + for (var i = 0; i < data.slots.length; i++) { + var slotData = data.slots[i]; + var bone = this.bones[slotData.boneData.index]; + var slot = new spine.Slot(slotData, bone); + this.slots.push(slot); + this.drawOrder.push(slot); + } + this.ikConstraints = new Array(); + for (var i = 0; i < data.ikConstraints.length; i++) { + var ikConstraintData = data.ikConstraints[i]; + this.ikConstraints.push(new spine.IkConstraint(ikConstraintData, this)); + } + this.transformConstraints = new Array(); + for (var i = 0; i < data.transformConstraints.length; i++) { + var transformConstraintData = data.transformConstraints[i]; + this.transformConstraints.push(new spine.TransformConstraint(transformConstraintData, this)); + } + this.pathConstraints = new Array(); + for (var i = 0; i < data.pathConstraints.length; i++) { + var pathConstraintData = data.pathConstraints[i]; + this.pathConstraints.push(new spine.PathConstraint(pathConstraintData, this)); + } + this.color = new spine.Color(1, 1, 1, 1); + this.updateCache(); + } + Skeleton.prototype.updateCache = function () { + var updateCache = this._updateCache; + updateCache.length = 0; + this.updateCacheReset.length = 0; + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + bones[i].sorted = false; + var ikConstraints = this.ikConstraints; + var transformConstraints = this.transformConstraints; + var pathConstraints = this.pathConstraints; + var ikCount = ikConstraints.length, transformCount = transformConstraints.length, pathCount = pathConstraints.length; + var constraintCount = ikCount + transformCount + pathCount; + outer: for (var i = 0; i < constraintCount; i++) { + for (var ii = 0; ii < ikCount; ii++) { + var constraint = ikConstraints[ii]; + if (constraint.data.order == i) { + this.sortIkConstraint(constraint); + continue outer; + } + } + for (var ii = 0; ii < transformCount; ii++) { + var constraint = transformConstraints[ii]; + if (constraint.data.order == i) { + this.sortTransformConstraint(constraint); + continue outer; + } + } + for (var ii = 0; ii < pathCount; ii++) { + var constraint = pathConstraints[ii]; + if (constraint.data.order == i) { + this.sortPathConstraint(constraint); + continue outer; + } + } + } + for (var i = 0, n = bones.length; i < n; i++) + this.sortBone(bones[i]); + }; + Skeleton.prototype.sortIkConstraint = function (constraint) { + var target = constraint.target; + this.sortBone(target); + var constrained = constraint.bones; + var parent = constrained[0]; + this.sortBone(parent); + if (constrained.length > 1) { + var child = constrained[constrained.length - 1]; + if (!(this._updateCache.indexOf(child) > -1)) + this.updateCacheReset.push(child); + } + this._updateCache.push(constraint); + this.sortReset(parent.children); + constrained[constrained.length - 1].sorted = true; + }; + Skeleton.prototype.sortPathConstraint = function (constraint) { + var slot = constraint.target; + var slotIndex = slot.data.index; + var slotBone = slot.bone; + if (this.skin != null) + this.sortPathConstraintAttachment(this.skin, slotIndex, slotBone); + if (this.data.defaultSkin != null && this.data.defaultSkin != this.skin) + this.sortPathConstraintAttachment(this.data.defaultSkin, slotIndex, slotBone); + for (var i = 0, n = this.data.skins.length; i < n; i++) + this.sortPathConstraintAttachment(this.data.skins[i], slotIndex, slotBone); + var attachment = slot.getAttachment(); + if (attachment instanceof spine.PathAttachment) + this.sortPathConstraintAttachmentWith(attachment, slotBone); + var constrained = constraint.bones; + var boneCount = constrained.length; + for (var i = 0; i < boneCount; i++) + this.sortBone(constrained[i]); + this._updateCache.push(constraint); + for (var i = 0; i < boneCount; i++) + this.sortReset(constrained[i].children); + for (var i = 0; i < boneCount; i++) + constrained[i].sorted = true; + }; + Skeleton.prototype.sortTransformConstraint = function (constraint) { + this.sortBone(constraint.target); + var constrained = constraint.bones; + var boneCount = constrained.length; + if (constraint.data.local) { + for (var i = 0; i < boneCount; i++) { + var child = constrained[i]; + this.sortBone(child.parent); + if (!(this._updateCache.indexOf(child) > -1)) + this.updateCacheReset.push(child); + } + } + else { + for (var i = 0; i < boneCount; i++) { + this.sortBone(constrained[i]); + } + } + this._updateCache.push(constraint); + for (var ii = 0; ii < boneCount; ii++) + this.sortReset(constrained[ii].children); + for (var ii = 0; ii < boneCount; ii++) + constrained[ii].sorted = true; + }; + Skeleton.prototype.sortPathConstraintAttachment = function (skin, slotIndex, slotBone) { + var attachments = skin.attachments[slotIndex]; + if (!attachments) + return; + for (var key in attachments) { + this.sortPathConstraintAttachmentWith(attachments[key], slotBone); + } + }; + Skeleton.prototype.sortPathConstraintAttachmentWith = function (attachment, slotBone) { + if (!(attachment instanceof spine.PathAttachment)) + return; + var pathBones = attachment.bones; + if (pathBones == null) + this.sortBone(slotBone); + else { + var bones = this.bones; + var i = 0; + while (i < pathBones.length) { + var boneCount = pathBones[i++]; + for (var n = i + boneCount; i < n; i++) { + var boneIndex = pathBones[i]; + this.sortBone(bones[boneIndex]); + } + } + } + }; + Skeleton.prototype.sortBone = function (bone) { + if (bone.sorted) + return; + var parent = bone.parent; + if (parent != null) + this.sortBone(parent); + bone.sorted = true; + this._updateCache.push(bone); + }; + Skeleton.prototype.sortReset = function (bones) { + for (var i = 0, n = bones.length; i < n; i++) { + var bone = bones[i]; + if (bone.sorted) + this.sortReset(bone.children); + bone.sorted = false; + } + }; + Skeleton.prototype.updateWorldTransform = function () { + var updateCacheReset = this.updateCacheReset; + for (var i = 0, n = updateCacheReset.length; i < n; i++) { + var bone = updateCacheReset[i]; + bone.ax = bone.x; + bone.ay = bone.y; + bone.arotation = bone.rotation; + bone.ascaleX = bone.scaleX; + bone.ascaleY = bone.scaleY; + bone.ashearX = bone.shearX; + bone.ashearY = bone.shearY; + bone.appliedValid = true; + } + var updateCache = this._updateCache; + for (var i = 0, n = updateCache.length; i < n; i++) + updateCache[i].update(); + }; + Skeleton.prototype.setToSetupPose = function () { + this.setBonesToSetupPose(); + this.setSlotsToSetupPose(); + }; + Skeleton.prototype.setBonesToSetupPose = function () { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + bones[i].setToSetupPose(); + var ikConstraints = this.ikConstraints; + for (var i = 0, n = ikConstraints.length; i < n; i++) { + var constraint = ikConstraints[i]; + constraint.bendDirection = constraint.data.bendDirection; + constraint.mix = constraint.data.mix; + } + var transformConstraints = this.transformConstraints; + for (var i = 0, n = transformConstraints.length; i < n; i++) { + var constraint = transformConstraints[i]; + var data = constraint.data; + constraint.rotateMix = data.rotateMix; + constraint.translateMix = data.translateMix; + constraint.scaleMix = data.scaleMix; + constraint.shearMix = data.shearMix; + } + var pathConstraints = this.pathConstraints; + for (var i = 0, n = pathConstraints.length; i < n; i++) { + var constraint = pathConstraints[i]; + var data = constraint.data; + constraint.position = data.position; + constraint.spacing = data.spacing; + constraint.rotateMix = data.rotateMix; + constraint.translateMix = data.translateMix; + } + }; + Skeleton.prototype.setSlotsToSetupPose = function () { + var slots = this.slots; + spine.Utils.arrayCopy(slots, 0, this.drawOrder, 0, slots.length); + for (var i = 0, n = slots.length; i < n; i++) + slots[i].setToSetupPose(); + }; + Skeleton.prototype.getRootBone = function () { + if (this.bones.length == 0) + return null; + return this.bones[0]; + }; + Skeleton.prototype.findBone = function (boneName) { + if (boneName == null) + throw new Error("boneName cannot be null."); + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) { + var bone = bones[i]; + if (bone.data.name == boneName) + return bone; + } + return null; + }; + Skeleton.prototype.findBoneIndex = function (boneName) { + if (boneName == null) + throw new Error("boneName cannot be null."); + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + if (bones[i].data.name == boneName) + return i; + return -1; + }; + Skeleton.prototype.findSlot = function (slotName) { + if (slotName == null) + throw new Error("slotName cannot be null."); + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) { + var slot = slots[i]; + if (slot.data.name == slotName) + return slot; + } + return null; + }; + Skeleton.prototype.findSlotIndex = function (slotName) { + if (slotName == null) + throw new Error("slotName cannot be null."); + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) + if (slots[i].data.name == slotName) + return i; + return -1; + }; + Skeleton.prototype.setSkinByName = function (skinName) { + var skin = this.data.findSkin(skinName); + if (skin == null) + throw new Error("Skin not found: " + skinName); + this.setSkin(skin); + }; + Skeleton.prototype.setSkin = function (newSkin) { + if (newSkin != null) { + if (this.skin != null) + newSkin.attachAll(this, this.skin); + else { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) { + var slot = slots[i]; + var name_1 = slot.data.attachmentName; + if (name_1 != null) { + var attachment = newSkin.getAttachment(i, name_1); + if (attachment != null) + slot.setAttachment(attachment); + } + } + } + } + this.skin = newSkin; + }; + Skeleton.prototype.getAttachmentByName = function (slotName, attachmentName) { + return this.getAttachment(this.data.findSlotIndex(slotName), attachmentName); + }; + Skeleton.prototype.getAttachment = function (slotIndex, attachmentName) { + if (attachmentName == null) + throw new Error("attachmentName cannot be null."); + if (this.skin != null) { + var attachment = this.skin.getAttachment(slotIndex, attachmentName); + if (attachment != null) + return attachment; + } + if (this.data.defaultSkin != null) + return this.data.defaultSkin.getAttachment(slotIndex, attachmentName); + return null; + }; + Skeleton.prototype.setAttachment = function (slotName, attachmentName) { + if (slotName == null) + throw new Error("slotName cannot be null."); + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) { + var slot = slots[i]; + if (slot.data.name == slotName) { + var attachment = null; + if (attachmentName != null) { + attachment = this.getAttachment(i, attachmentName); + if (attachment == null) + throw new Error("Attachment not found: " + attachmentName + ", for slot: " + slotName); + } + slot.setAttachment(attachment); + return; + } + } + throw new Error("Slot not found: " + slotName); + }; + Skeleton.prototype.findIkConstraint = function (constraintName) { + if (constraintName == null) + throw new Error("constraintName cannot be null."); + var ikConstraints = this.ikConstraints; + for (var i = 0, n = ikConstraints.length; i < n; i++) { + var ikConstraint = ikConstraints[i]; + if (ikConstraint.data.name == constraintName) + return ikConstraint; + } + return null; + }; + Skeleton.prototype.findTransformConstraint = function (constraintName) { + if (constraintName == null) + throw new Error("constraintName cannot be null."); + var transformConstraints = this.transformConstraints; + for (var i = 0, n = transformConstraints.length; i < n; i++) { + var constraint = transformConstraints[i]; + if (constraint.data.name == constraintName) + return constraint; + } + return null; + }; + Skeleton.prototype.findPathConstraint = function (constraintName) { + if (constraintName == null) + throw new Error("constraintName cannot be null."); + var pathConstraints = this.pathConstraints; + for (var i = 0, n = pathConstraints.length; i < n; i++) { + var constraint = pathConstraints[i]; + if (constraint.data.name == constraintName) + return constraint; + } + return null; + }; + Skeleton.prototype.getBounds = function (offset, size, temp) { + if (offset == null) + throw new Error("offset cannot be null."); + if (size == null) + throw new Error("size cannot be null."); + var drawOrder = this.drawOrder; + var minX = Number.POSITIVE_INFINITY, minY = Number.POSITIVE_INFINITY, maxX = Number.NEGATIVE_INFINITY, maxY = Number.NEGATIVE_INFINITY; + for (var i = 0, n = drawOrder.length; i < n; i++) { + var slot = drawOrder[i]; + var verticesLength = 0; + var vertices = null; + var attachment = slot.getAttachment(); + if (attachment instanceof spine.RegionAttachment) { + verticesLength = 8; + vertices = spine.Utils.setArraySize(temp, verticesLength, 0); + attachment.computeWorldVertices(slot.bone, vertices, 0, 2); + } + else if (attachment instanceof spine.MeshAttachment) { + var mesh = attachment; + verticesLength = mesh.worldVerticesLength; + vertices = spine.Utils.setArraySize(temp, verticesLength, 0); + mesh.computeWorldVertices(slot, 0, verticesLength, vertices, 0, 2); + } + if (vertices != null) { + for (var ii = 0, nn = vertices.length; ii < nn; ii += 2) { + var x = vertices[ii], y = vertices[ii + 1]; + minX = Math.min(minX, x); + minY = Math.min(minY, y); + maxX = Math.max(maxX, x); + maxY = Math.max(maxY, y); + } + } + } + offset.set(minX, minY); + size.set(maxX - minX, maxY - minY); + }; + Skeleton.prototype.update = function (delta) { + this.time += delta; + }; + return Skeleton; + }()); + spine.Skeleton = Skeleton; +})(spine || (spine = {})); +var spine; +(function (spine) { + var SkeletonBounds = (function () { + function SkeletonBounds() { + this.minX = 0; + this.minY = 0; + this.maxX = 0; + this.maxY = 0; + this.boundingBoxes = new Array(); + this.polygons = new Array(); + this.polygonPool = new spine.Pool(function () { + return spine.Utils.newFloatArray(16); + }); + } + SkeletonBounds.prototype.update = function (skeleton, updateAabb) { + if (skeleton == null) + throw new Error("skeleton cannot be null."); + var boundingBoxes = this.boundingBoxes; + var polygons = this.polygons; + var polygonPool = this.polygonPool; + var slots = skeleton.slots; + var slotCount = slots.length; + boundingBoxes.length = 0; + polygonPool.freeAll(polygons); + polygons.length = 0; + for (var i = 0; i < slotCount; i++) { + var slot = slots[i]; + var attachment = slot.getAttachment(); + if (attachment instanceof spine.BoundingBoxAttachment) { + var boundingBox = attachment; + boundingBoxes.push(boundingBox); + var polygon = polygonPool.obtain(); + if (polygon.length != boundingBox.worldVerticesLength) { + polygon = spine.Utils.newFloatArray(boundingBox.worldVerticesLength); + } + polygons.push(polygon); + boundingBox.computeWorldVertices(slot, 0, boundingBox.worldVerticesLength, polygon, 0, 2); + } + } + if (updateAabb) { + this.aabbCompute(); + } + else { + this.minX = Number.POSITIVE_INFINITY; + this.minY = Number.POSITIVE_INFINITY; + this.maxX = Number.NEGATIVE_INFINITY; + this.maxY = Number.NEGATIVE_INFINITY; + } + }; + SkeletonBounds.prototype.aabbCompute = function () { + var minX = Number.POSITIVE_INFINITY, minY = Number.POSITIVE_INFINITY, maxX = Number.NEGATIVE_INFINITY, maxY = Number.NEGATIVE_INFINITY; + var polygons = this.polygons; + for (var i = 0, n = polygons.length; i < n; i++) { + var polygon = polygons[i]; + var vertices = polygon; + for (var ii = 0, nn = polygon.length; ii < nn; ii += 2) { + var x = vertices[ii]; + var y = vertices[ii + 1]; + minX = Math.min(minX, x); + minY = Math.min(minY, y); + maxX = Math.max(maxX, x); + maxY = Math.max(maxY, y); + } + } + this.minX = minX; + this.minY = minY; + this.maxX = maxX; + this.maxY = maxY; + }; + SkeletonBounds.prototype.aabbContainsPoint = function (x, y) { + return x >= this.minX && x <= this.maxX && y >= this.minY && y <= this.maxY; + }; + SkeletonBounds.prototype.aabbIntersectsSegment = function (x1, y1, x2, y2) { + var minX = this.minX; + var minY = this.minY; + var maxX = this.maxX; + var maxY = this.maxY; + if ((x1 <= minX && x2 <= minX) || (y1 <= minY && y2 <= minY) || (x1 >= maxX && x2 >= maxX) || (y1 >= maxY && y2 >= maxY)) + return false; + var m = (y2 - y1) / (x2 - x1); + var y = m * (minX - x1) + y1; + if (y > minY && y < maxY) + return true; + y = m * (maxX - x1) + y1; + if (y > minY && y < maxY) + return true; + var x = (minY - y1) / m + x1; + if (x > minX && x < maxX) + return true; + x = (maxY - y1) / m + x1; + if (x > minX && x < maxX) + return true; + return false; + }; + SkeletonBounds.prototype.aabbIntersectsSkeleton = function (bounds) { + return this.minX < bounds.maxX && this.maxX > bounds.minX && this.minY < bounds.maxY && this.maxY > bounds.minY; + }; + SkeletonBounds.prototype.containsPoint = function (x, y) { + var polygons = this.polygons; + for (var i = 0, n = polygons.length; i < n; i++) + if (this.containsPointPolygon(polygons[i], x, y)) + return this.boundingBoxes[i]; + return null; + }; + SkeletonBounds.prototype.containsPointPolygon = function (polygon, x, y) { + var vertices = polygon; + var nn = polygon.length; + var prevIndex = nn - 2; + var inside = false; + for (var ii = 0; ii < nn; ii += 2) { + var vertexY = vertices[ii + 1]; + var prevY = vertices[prevIndex + 1]; + if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) { + var vertexX = vertices[ii]; + if (vertexX + (y - vertexY) / (prevY - vertexY) * (vertices[prevIndex] - vertexX) < x) + inside = !inside; + } + prevIndex = ii; + } + return inside; + }; + SkeletonBounds.prototype.intersectsSegment = function (x1, y1, x2, y2) { + var polygons = this.polygons; + for (var i = 0, n = polygons.length; i < n; i++) + if (this.intersectsSegmentPolygon(polygons[i], x1, y1, x2, y2)) + return this.boundingBoxes[i]; + return null; + }; + SkeletonBounds.prototype.intersectsSegmentPolygon = function (polygon, x1, y1, x2, y2) { + var vertices = polygon; + var nn = polygon.length; + var width12 = x1 - x2, height12 = y1 - y2; + var det1 = x1 * y2 - y1 * x2; + var x3 = vertices[nn - 2], y3 = vertices[nn - 1]; + for (var ii = 0; ii < nn; ii += 2) { + var x4 = vertices[ii], y4 = vertices[ii + 1]; + var det2 = x3 * y4 - y3 * x4; + var width34 = x3 - x4, height34 = y3 - y4; + var det3 = width12 * height34 - height12 * width34; + var x = (det1 * width34 - width12 * det2) / det3; + if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) { + var y = (det1 * height34 - height12 * det2) / det3; + if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))) + return true; + } + x3 = x4; + y3 = y4; + } + return false; + }; + SkeletonBounds.prototype.getPolygon = function (boundingBox) { + if (boundingBox == null) + throw new Error("boundingBox cannot be null."); + var index = this.boundingBoxes.indexOf(boundingBox); + return index == -1 ? null : this.polygons[index]; + }; + SkeletonBounds.prototype.getWidth = function () { + return this.maxX - this.minX; + }; + SkeletonBounds.prototype.getHeight = function () { + return this.maxY - this.minY; + }; + return SkeletonBounds; + }()); + spine.SkeletonBounds = SkeletonBounds; +})(spine || (spine = {})); +var spine; +(function (spine) { + var SkeletonClipping = (function () { + function SkeletonClipping() { + this.triangulator = new spine.Triangulator(); + this.clippingPolygon = new Array(); + this.clipOutput = new Array(); + this.clippedVertices = new Array(); + this.clippedTriangles = new Array(); + this.scratch = new Array(); + } + SkeletonClipping.prototype.clipStart = function (slot, clip) { + if (this.clipAttachment != null) + return 0; + this.clipAttachment = clip; + var n = clip.worldVerticesLength; + var vertices = spine.Utils.setArraySize(this.clippingPolygon, n); + clip.computeWorldVertices(slot, 0, n, vertices, 0, 2); + var clippingPolygon = this.clippingPolygon; + SkeletonClipping.makeClockwise(clippingPolygon); + var clippingPolygons = this.clippingPolygons = this.triangulator.decompose(clippingPolygon, this.triangulator.triangulate(clippingPolygon)); + for (var i = 0, n_1 = clippingPolygons.length; i < n_1; i++) { + var polygon = clippingPolygons[i]; + SkeletonClipping.makeClockwise(polygon); + polygon.push(polygon[0]); + polygon.push(polygon[1]); + } + return clippingPolygons.length; + }; + SkeletonClipping.prototype.clipEndWithSlot = function (slot) { + if (this.clipAttachment != null && this.clipAttachment.endSlot == slot.data) + this.clipEnd(); + }; + SkeletonClipping.prototype.clipEnd = function () { + if (this.clipAttachment == null) + return; + this.clipAttachment = null; + this.clippingPolygons = null; + this.clippedVertices.length = 0; + this.clippedTriangles.length = 0; + this.clippingPolygon.length = 0; + }; + SkeletonClipping.prototype.isClipping = function () { + return this.clipAttachment != null; + }; + SkeletonClipping.prototype.clipTriangles = function (vertices, verticesLength, triangles, trianglesLength, uvs, light, dark, twoColor) { + var clipOutput = this.clipOutput, clippedVertices = this.clippedVertices; + var clippedTriangles = this.clippedTriangles; + var polygons = this.clippingPolygons; + var polygonsCount = this.clippingPolygons.length; + var vertexSize = twoColor ? 12 : 8; + var index = 0; + clippedVertices.length = 0; + clippedTriangles.length = 0; + outer: for (var i = 0; i < trianglesLength; i += 3) { + var vertexOffset = triangles[i] << 1; + var x1 = vertices[vertexOffset], y1 = vertices[vertexOffset + 1]; + var u1 = uvs[vertexOffset], v1 = uvs[vertexOffset + 1]; + vertexOffset = triangles[i + 1] << 1; + var x2 = vertices[vertexOffset], y2 = vertices[vertexOffset + 1]; + var u2 = uvs[vertexOffset], v2 = uvs[vertexOffset + 1]; + vertexOffset = triangles[i + 2] << 1; + var x3 = vertices[vertexOffset], y3 = vertices[vertexOffset + 1]; + var u3 = uvs[vertexOffset], v3 = uvs[vertexOffset + 1]; + for (var p = 0; p < polygonsCount; p++) { + var s = clippedVertices.length; + if (this.clip(x1, y1, x2, y2, x3, y3, polygons[p], clipOutput)) { + var clipOutputLength = clipOutput.length; + if (clipOutputLength == 0) + continue; + var d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1; + var d = 1 / (d0 * d2 + d1 * (y1 - y3)); + var clipOutputCount = clipOutputLength >> 1; + var clipOutputItems = this.clipOutput; + var clippedVerticesItems = spine.Utils.setArraySize(clippedVertices, s + clipOutputCount * vertexSize); + for (var ii = 0; ii < clipOutputLength; ii += 2) { + var x = clipOutputItems[ii], y = clipOutputItems[ii + 1]; + clippedVerticesItems[s] = x; + clippedVerticesItems[s + 1] = y; + clippedVerticesItems[s + 2] = light.r; + clippedVerticesItems[s + 3] = light.g; + clippedVerticesItems[s + 4] = light.b; + clippedVerticesItems[s + 5] = light.a; + var c0 = x - x3, c1 = y - y3; + var a = (d0 * c0 + d1 * c1) * d; + var b = (d4 * c0 + d2 * c1) * d; + var c = 1 - a - b; + clippedVerticesItems[s + 6] = u1 * a + u2 * b + u3 * c; + clippedVerticesItems[s + 7] = v1 * a + v2 * b + v3 * c; + if (twoColor) { + clippedVerticesItems[s + 8] = dark.r; + clippedVerticesItems[s + 9] = dark.g; + clippedVerticesItems[s + 10] = dark.b; + clippedVerticesItems[s + 11] = dark.a; + } + s += vertexSize; + } + s = clippedTriangles.length; + var clippedTrianglesItems = spine.Utils.setArraySize(clippedTriangles, s + 3 * (clipOutputCount - 2)); + clipOutputCount--; + for (var ii = 1; ii < clipOutputCount; ii++) { + clippedTrianglesItems[s] = index; + clippedTrianglesItems[s + 1] = (index + ii); + clippedTrianglesItems[s + 2] = (index + ii + 1); + s += 3; + } + index += clipOutputCount + 1; + } + else { + var clippedVerticesItems = spine.Utils.setArraySize(clippedVertices, s + 3 * vertexSize); + clippedVerticesItems[s] = x1; + clippedVerticesItems[s + 1] = y1; + clippedVerticesItems[s + 2] = light.r; + clippedVerticesItems[s + 3] = light.g; + clippedVerticesItems[s + 4] = light.b; + clippedVerticesItems[s + 5] = light.a; + if (!twoColor) { + clippedVerticesItems[s + 6] = u1; + clippedVerticesItems[s + 7] = v1; + clippedVerticesItems[s + 8] = x2; + clippedVerticesItems[s + 9] = y2; + clippedVerticesItems[s + 10] = light.r; + clippedVerticesItems[s + 11] = light.g; + clippedVerticesItems[s + 12] = light.b; + clippedVerticesItems[s + 13] = light.a; + clippedVerticesItems[s + 14] = u2; + clippedVerticesItems[s + 15] = v2; + clippedVerticesItems[s + 16] = x3; + clippedVerticesItems[s + 17] = y3; + clippedVerticesItems[s + 18] = light.r; + clippedVerticesItems[s + 19] = light.g; + clippedVerticesItems[s + 20] = light.b; + clippedVerticesItems[s + 21] = light.a; + clippedVerticesItems[s + 22] = u3; + clippedVerticesItems[s + 23] = v3; + } + else { + clippedVerticesItems[s + 6] = u1; + clippedVerticesItems[s + 7] = v1; + clippedVerticesItems[s + 8] = dark.r; + clippedVerticesItems[s + 9] = dark.g; + clippedVerticesItems[s + 10] = dark.b; + clippedVerticesItems[s + 11] = dark.a; + clippedVerticesItems[s + 12] = x2; + clippedVerticesItems[s + 13] = y2; + clippedVerticesItems[s + 14] = light.r; + clippedVerticesItems[s + 15] = light.g; + clippedVerticesItems[s + 16] = light.b; + clippedVerticesItems[s + 17] = light.a; + clippedVerticesItems[s + 18] = u2; + clippedVerticesItems[s + 19] = v2; + clippedVerticesItems[s + 20] = dark.r; + clippedVerticesItems[s + 21] = dark.g; + clippedVerticesItems[s + 22] = dark.b; + clippedVerticesItems[s + 23] = dark.a; + clippedVerticesItems[s + 24] = x3; + clippedVerticesItems[s + 25] = y3; + clippedVerticesItems[s + 26] = light.r; + clippedVerticesItems[s + 27] = light.g; + clippedVerticesItems[s + 28] = light.b; + clippedVerticesItems[s + 29] = light.a; + clippedVerticesItems[s + 30] = u3; + clippedVerticesItems[s + 31] = v3; + clippedVerticesItems[s + 32] = dark.r; + clippedVerticesItems[s + 33] = dark.g; + clippedVerticesItems[s + 34] = dark.b; + clippedVerticesItems[s + 35] = dark.a; + } + s = clippedTriangles.length; + var clippedTrianglesItems = spine.Utils.setArraySize(clippedTriangles, s + 3); + clippedTrianglesItems[s] = index; + clippedTrianglesItems[s + 1] = (index + 1); + clippedTrianglesItems[s + 2] = (index + 2); + index += 3; + continue outer; + } + } + } + }; + SkeletonClipping.prototype.clip = function (x1, y1, x2, y2, x3, y3, clippingArea, output) { + var originalOutput = output; + var clipped = false; + var input = null; + if (clippingArea.length % 4 >= 2) { + input = output; + output = this.scratch; + } + else + input = this.scratch; + input.length = 0; + input.push(x1); + input.push(y1); + input.push(x2); + input.push(y2); + input.push(x3); + input.push(y3); + input.push(x1); + input.push(y1); + output.length = 0; + var clippingVertices = clippingArea; + var clippingVerticesLast = clippingArea.length - 4; + for (var i = 0;; i += 2) { + var edgeX = clippingVertices[i], edgeY = clippingVertices[i + 1]; + var edgeX2 = clippingVertices[i + 2], edgeY2 = clippingVertices[i + 3]; + var deltaX = edgeX - edgeX2, deltaY = edgeY - edgeY2; + var inputVertices = input; + var inputVerticesLength = input.length - 2, outputStart = output.length; + for (var ii = 0; ii < inputVerticesLength; ii += 2) { + var inputX = inputVertices[ii], inputY = inputVertices[ii + 1]; + var inputX2 = inputVertices[ii + 2], inputY2 = inputVertices[ii + 3]; + var side2 = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0; + if (deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0) { + if (side2) { + output.push(inputX2); + output.push(inputY2); + continue; + } + var c0 = inputY2 - inputY, c2 = inputX2 - inputX; + var ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY)); + output.push(edgeX + (edgeX2 - edgeX) * ua); + output.push(edgeY + (edgeY2 - edgeY) * ua); + } + else if (side2) { + var c0 = inputY2 - inputY, c2 = inputX2 - inputX; + var ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY)); + output.push(edgeX + (edgeX2 - edgeX) * ua); + output.push(edgeY + (edgeY2 - edgeY) * ua); + output.push(inputX2); + output.push(inputY2); + } + clipped = true; + } + if (outputStart == output.length) { + originalOutput.length = 0; + return true; + } + output.push(output[0]); + output.push(output[1]); + if (i == clippingVerticesLast) + break; + var temp = output; + output = input; + output.length = 0; + input = temp; + } + if (originalOutput != output) { + originalOutput.length = 0; + for (var i = 0, n = output.length - 2; i < n; i++) + originalOutput[i] = output[i]; + } + else + originalOutput.length = originalOutput.length - 2; + return clipped; + }; + SkeletonClipping.makeClockwise = function (polygon) { + var vertices = polygon; + var verticeslength = polygon.length; + var area = vertices[verticeslength - 2] * vertices[1] - vertices[0] * vertices[verticeslength - 1], p1x = 0, p1y = 0, p2x = 0, p2y = 0; + for (var i = 0, n = verticeslength - 3; i < n; i += 2) { + p1x = vertices[i]; + p1y = vertices[i + 1]; + p2x = vertices[i + 2]; + p2y = vertices[i + 3]; + area += p1x * p2y - p2x * p1y; + } + if (area < 0) + return; + for (var i = 0, lastX = verticeslength - 2, n = verticeslength >> 1; i < n; i += 2) { + var x = vertices[i], y = vertices[i + 1]; + var other = lastX - i; + vertices[i] = vertices[other]; + vertices[i + 1] = vertices[other + 1]; + vertices[other] = x; + vertices[other + 1] = y; + } + }; + return SkeletonClipping; + }()); + spine.SkeletonClipping = SkeletonClipping; +})(spine || (spine = {})); +var spine; +(function (spine) { + var SkeletonData = (function () { + function SkeletonData() { + this.bones = new Array(); + this.slots = new Array(); + this.skins = new Array(); + this.events = new Array(); + this.animations = new Array(); + this.ikConstraints = new Array(); + this.transformConstraints = new Array(); + this.pathConstraints = new Array(); + this.fps = 0; + } + SkeletonData.prototype.findBone = function (boneName) { + if (boneName == null) + throw new Error("boneName cannot be null."); + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) { + var bone = bones[i]; + if (bone.name == boneName) + return bone; + } + return null; + }; + SkeletonData.prototype.findBoneIndex = function (boneName) { + if (boneName == null) + throw new Error("boneName cannot be null."); + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + if (bones[i].name == boneName) + return i; + return -1; + }; + SkeletonData.prototype.findSlot = function (slotName) { + if (slotName == null) + throw new Error("slotName cannot be null."); + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) { + var slot = slots[i]; + if (slot.name == slotName) + return slot; + } + return null; + }; + SkeletonData.prototype.findSlotIndex = function (slotName) { + if (slotName == null) + throw new Error("slotName cannot be null."); + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) + if (slots[i].name == slotName) + return i; + return -1; + }; + SkeletonData.prototype.findSkin = function (skinName) { + if (skinName == null) + throw new Error("skinName cannot be null."); + var skins = this.skins; + for (var i = 0, n = skins.length; i < n; i++) { + var skin = skins[i]; + if (skin.name == skinName) + return skin; + } + return null; + }; + SkeletonData.prototype.findEvent = function (eventDataName) { + if (eventDataName == null) + throw new Error("eventDataName cannot be null."); + var events = this.events; + for (var i = 0, n = events.length; i < n; i++) { + var event_4 = events[i]; + if (event_4.name == eventDataName) + return event_4; + } + return null; + }; + SkeletonData.prototype.findAnimation = function (animationName) { + if (animationName == null) + throw new Error("animationName cannot be null."); + var animations = this.animations; + for (var i = 0, n = animations.length; i < n; i++) { + var animation = animations[i]; + if (animation.name == animationName) + return animation; + } + return null; + }; + SkeletonData.prototype.findIkConstraint = function (constraintName) { + if (constraintName == null) + throw new Error("constraintName cannot be null."); + var ikConstraints = this.ikConstraints; + for (var i = 0, n = ikConstraints.length; i < n; i++) { + var constraint = ikConstraints[i]; + if (constraint.name == constraintName) + return constraint; + } + return null; + }; + SkeletonData.prototype.findTransformConstraint = function (constraintName) { + if (constraintName == null) + throw new Error("constraintName cannot be null."); + var transformConstraints = this.transformConstraints; + for (var i = 0, n = transformConstraints.length; i < n; i++) { + var constraint = transformConstraints[i]; + if (constraint.name == constraintName) + return constraint; + } + return null; + }; + SkeletonData.prototype.findPathConstraint = function (constraintName) { + if (constraintName == null) + throw new Error("constraintName cannot be null."); + var pathConstraints = this.pathConstraints; + for (var i = 0, n = pathConstraints.length; i < n; i++) { + var constraint = pathConstraints[i]; + if (constraint.name == constraintName) + return constraint; + } + return null; + }; + SkeletonData.prototype.findPathConstraintIndex = function (pathConstraintName) { + if (pathConstraintName == null) + throw new Error("pathConstraintName cannot be null."); + var pathConstraints = this.pathConstraints; + for (var i = 0, n = pathConstraints.length; i < n; i++) + if (pathConstraints[i].name == pathConstraintName) + return i; + return -1; + }; + return SkeletonData; + }()); + spine.SkeletonData = SkeletonData; +})(spine || (spine = {})); +var spine; +(function (spine) { + var SkeletonJson = (function () { + function SkeletonJson(attachmentLoader) { + this.scale = 1; + this.linkedMeshes = new Array(); + this.attachmentLoader = attachmentLoader; + } + SkeletonJson.prototype.readSkeletonData = function (json) { + var scale = this.scale; + var skeletonData = new spine.SkeletonData(); + var root = typeof (json) === "string" ? JSON.parse(json) : json; + var skeletonMap = root.skeleton; + if (skeletonMap != null) { + skeletonData.hash = skeletonMap.hash; + skeletonData.version = skeletonMap.spine; + skeletonData.width = skeletonMap.width; + skeletonData.height = skeletonMap.height; + skeletonData.fps = skeletonMap.fps; + skeletonData.imagesPath = skeletonMap.images; + } + if (root.bones) { + for (var i = 0; i < root.bones.length; i++) { + var boneMap = root.bones[i]; + var parent_2 = null; + var parentName = this.getValue(boneMap, "parent", null); + if (parentName != null) { + parent_2 = skeletonData.findBone(parentName); + if (parent_2 == null) + throw new Error("Parent bone not found: " + parentName); + } + var data = new spine.BoneData(skeletonData.bones.length, boneMap.name, parent_2); + data.length = this.getValue(boneMap, "length", 0) * scale; + data.x = this.getValue(boneMap, "x", 0) * scale; + data.y = this.getValue(boneMap, "y", 0) * scale; + data.rotation = this.getValue(boneMap, "rotation", 0); + data.scaleX = this.getValue(boneMap, "scaleX", 1); + data.scaleY = this.getValue(boneMap, "scaleY", 1); + data.shearX = this.getValue(boneMap, "shearX", 0); + data.shearY = this.getValue(boneMap, "shearY", 0); + data.transformMode = SkeletonJson.transformModeFromString(this.getValue(boneMap, "transform", "normal")); + skeletonData.bones.push(data); + } + } + if (root.slots) { + for (var i = 0; i < root.slots.length; i++) { + var slotMap = root.slots[i]; + var slotName = slotMap.name; + var boneName = slotMap.bone; + var boneData = skeletonData.findBone(boneName); + if (boneData == null) + throw new Error("Slot bone not found: " + boneName); + var data = new spine.SlotData(skeletonData.slots.length, slotName, boneData); + var color = this.getValue(slotMap, "color", null); + if (color != null) + data.color.setFromString(color); + var dark = this.getValue(slotMap, "dark", null); + if (dark != null) { + data.darkColor = new spine.Color(1, 1, 1, 1); + data.darkColor.setFromString(dark); + } + data.attachmentName = this.getValue(slotMap, "attachment", null); + data.blendMode = SkeletonJson.blendModeFromString(this.getValue(slotMap, "blend", "normal")); + skeletonData.slots.push(data); + } + } + if (root.ik) { + for (var i = 0; i < root.ik.length; i++) { + var constraintMap = root.ik[i]; + var data = new spine.IkConstraintData(constraintMap.name); + data.order = this.getValue(constraintMap, "order", 0); + for (var j = 0; j < constraintMap.bones.length; j++) { + var boneName = constraintMap.bones[j]; + var bone = skeletonData.findBone(boneName); + if (bone == null) + throw new Error("IK bone not found: " + boneName); + data.bones.push(bone); + } + var targetName = constraintMap.target; + data.target = skeletonData.findBone(targetName); + if (data.target == null) + throw new Error("IK target bone not found: " + targetName); + data.bendDirection = this.getValue(constraintMap, "bendPositive", true) ? 1 : -1; + data.mix = this.getValue(constraintMap, "mix", 1); + skeletonData.ikConstraints.push(data); + } + } + if (root.transform) { + for (var i = 0; i < root.transform.length; i++) { + var constraintMap = root.transform[i]; + var data = new spine.TransformConstraintData(constraintMap.name); + data.order = this.getValue(constraintMap, "order", 0); + for (var j = 0; j < constraintMap.bones.length; j++) { + var boneName = constraintMap.bones[j]; + var bone = skeletonData.findBone(boneName); + if (bone == null) + throw new Error("Transform constraint bone not found: " + boneName); + data.bones.push(bone); + } + var targetName = constraintMap.target; + data.target = skeletonData.findBone(targetName); + if (data.target == null) + throw new Error("Transform constraint target bone not found: " + targetName); + data.local = this.getValue(constraintMap, "local", false); + data.relative = this.getValue(constraintMap, "relative", false); + data.offsetRotation = this.getValue(constraintMap, "rotation", 0); + data.offsetX = this.getValue(constraintMap, "x", 0) * scale; + data.offsetY = this.getValue(constraintMap, "y", 0) * scale; + data.offsetScaleX = this.getValue(constraintMap, "scaleX", 0); + data.offsetScaleY = this.getValue(constraintMap, "scaleY", 0); + data.offsetShearY = this.getValue(constraintMap, "shearY", 0); + data.rotateMix = this.getValue(constraintMap, "rotateMix", 1); + data.translateMix = this.getValue(constraintMap, "translateMix", 1); + data.scaleMix = this.getValue(constraintMap, "scaleMix", 1); + data.shearMix = this.getValue(constraintMap, "shearMix", 1); + skeletonData.transformConstraints.push(data); + } + } + if (root.path) { + for (var i = 0; i < root.path.length; i++) { + var constraintMap = root.path[i]; + var data = new spine.PathConstraintData(constraintMap.name); + data.order = this.getValue(constraintMap, "order", 0); + for (var j = 0; j < constraintMap.bones.length; j++) { + var boneName = constraintMap.bones[j]; + var bone = skeletonData.findBone(boneName); + if (bone == null) + throw new Error("Transform constraint bone not found: " + boneName); + data.bones.push(bone); + } + var targetName = constraintMap.target; + data.target = skeletonData.findSlot(targetName); + if (data.target == null) + throw new Error("Path target slot not found: " + targetName); + data.positionMode = SkeletonJson.positionModeFromString(this.getValue(constraintMap, "positionMode", "percent")); + data.spacingMode = SkeletonJson.spacingModeFromString(this.getValue(constraintMap, "spacingMode", "length")); + data.rotateMode = SkeletonJson.rotateModeFromString(this.getValue(constraintMap, "rotateMode", "tangent")); + data.offsetRotation = this.getValue(constraintMap, "rotation", 0); + data.position = this.getValue(constraintMap, "position", 0); + if (data.positionMode == spine.PositionMode.Fixed) + data.position *= scale; + data.spacing = this.getValue(constraintMap, "spacing", 0); + if (data.spacingMode == spine.SpacingMode.Length || data.spacingMode == spine.SpacingMode.Fixed) + data.spacing *= scale; + data.rotateMix = this.getValue(constraintMap, "rotateMix", 1); + data.translateMix = this.getValue(constraintMap, "translateMix", 1); + skeletonData.pathConstraints.push(data); + } + } + if (root.skins) { + for (var skinName in root.skins) { + var skinMap = root.skins[skinName]; + var skin = new spine.Skin(skinName); + for (var slotName in skinMap) { + var slotIndex = skeletonData.findSlotIndex(slotName); + if (slotIndex == -1) + throw new Error("Slot not found: " + slotName); + var slotMap = skinMap[slotName]; + for (var entryName in slotMap) { + var attachment = this.readAttachment(slotMap[entryName], skin, slotIndex, entryName, skeletonData); + if (attachment != null) + skin.addAttachment(slotIndex, entryName, attachment); + } + } + skeletonData.skins.push(skin); + if (skin.name == "default") + skeletonData.defaultSkin = skin; + } + } + for (var i = 0, n = this.linkedMeshes.length; i < n; i++) { + var linkedMesh = this.linkedMeshes[i]; + var skin = linkedMesh.skin == null ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin); + if (skin == null) + throw new Error("Skin not found: " + linkedMesh.skin); + var parent_3 = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent); + if (parent_3 == null) + throw new Error("Parent mesh not found: " + linkedMesh.parent); + linkedMesh.mesh.setParentMesh(parent_3); + linkedMesh.mesh.updateUVs(); + } + this.linkedMeshes.length = 0; + if (root.events) { + for (var eventName in root.events) { + var eventMap = root.events[eventName]; + var data = new spine.EventData(eventName); + data.intValue = this.getValue(eventMap, "int", 0); + data.floatValue = this.getValue(eventMap, "float", 0); + data.stringValue = this.getValue(eventMap, "string", ""); + skeletonData.events.push(data); + } + } + if (root.animations) { + for (var animationName in root.animations) { + var animationMap = root.animations[animationName]; + this.readAnimation(animationMap, animationName, skeletonData); + } + } + return skeletonData; + }; + SkeletonJson.prototype.readAttachment = function (map, skin, slotIndex, name, skeletonData) { + var scale = this.scale; + name = this.getValue(map, "name", name); + var type = this.getValue(map, "type", "region"); + switch (type) { + case "region": { + var path = this.getValue(map, "path", name); + var region = this.attachmentLoader.newRegionAttachment(skin, name, path); + if (region == null) + return null; + region.path = path; + region.x = this.getValue(map, "x", 0) * scale; + region.y = this.getValue(map, "y", 0) * scale; + region.scaleX = this.getValue(map, "scaleX", 1); + region.scaleY = this.getValue(map, "scaleY", 1); + region.rotation = this.getValue(map, "rotation", 0); + region.width = map.width * scale; + region.height = map.height * scale; + var color = this.getValue(map, "color", null); + if (color != null) + region.color.setFromString(color); + region.updateOffset(); + return region; + } + case "boundingbox": { + var box = this.attachmentLoader.newBoundingBoxAttachment(skin, name); + if (box == null) + return null; + this.readVertices(map, box, map.vertexCount << 1); + var color = this.getValue(map, "color", null); + if (color != null) + box.color.setFromString(color); + return box; + } + case "mesh": + case "linkedmesh": { + var path = this.getValue(map, "path", name); + var mesh = this.attachmentLoader.newMeshAttachment(skin, name, path); + if (mesh == null) + return null; + mesh.path = path; + var color = this.getValue(map, "color", null); + if (color != null) + mesh.color.setFromString(color); + var parent_4 = this.getValue(map, "parent", null); + if (parent_4 != null) { + mesh.inheritDeform = this.getValue(map, "deform", true); + this.linkedMeshes.push(new LinkedMesh(mesh, this.getValue(map, "skin", null), slotIndex, parent_4)); + return mesh; + } + var uvs = map.uvs; + this.readVertices(map, mesh, uvs.length); + mesh.triangles = map.triangles; + mesh.regionUVs = uvs; + mesh.updateUVs(); + mesh.hullLength = this.getValue(map, "hull", 0) * 2; + return mesh; + } + case "path": { + var path = this.attachmentLoader.newPathAttachment(skin, name); + if (path == null) + return null; + path.closed = this.getValue(map, "closed", false); + path.constantSpeed = this.getValue(map, "constantSpeed", true); + var vertexCount = map.vertexCount; + this.readVertices(map, path, vertexCount << 1); + var lengths = spine.Utils.newArray(vertexCount / 3, 0); + for (var i = 0; i < map.lengths.length; i++) + lengths[i] = map.lengths[i] * scale; + path.lengths = lengths; + var color = this.getValue(map, "color", null); + if (color != null) + path.color.setFromString(color); + return path; + } + case "point": { + var point = this.attachmentLoader.newPointAttachment(skin, name); + if (point == null) + return null; + point.x = this.getValue(map, "x", 0) * scale; + point.y = this.getValue(map, "y", 0) * scale; + point.rotation = this.getValue(map, "rotation", 0); + var color = this.getValue(map, "color", null); + if (color != null) + point.color.setFromString(color); + return point; + } + case "clipping": { + var clip = this.attachmentLoader.newClippingAttachment(skin, name); + if (clip == null) + return null; + var end = this.getValue(map, "end", null); + if (end != null) { + var slot = skeletonData.findSlot(end); + if (slot == null) + throw new Error("Clipping end slot not found: " + end); + clip.endSlot = slot; + } + var vertexCount = map.vertexCount; + this.readVertices(map, clip, vertexCount << 1); + var color = this.getValue(map, "color", null); + if (color != null) + clip.color.setFromString(color); + return clip; + } + } + return null; + }; + SkeletonJson.prototype.readVertices = function (map, attachment, verticesLength) { + var scale = this.scale; + attachment.worldVerticesLength = verticesLength; + var vertices = map.vertices; + if (verticesLength == vertices.length) { + var scaledVertices = spine.Utils.toFloatArray(vertices); + if (scale != 1) { + for (var i = 0, n = vertices.length; i < n; i++) + scaledVertices[i] *= scale; + } + attachment.vertices = scaledVertices; + return; + } + var weights = new Array(); + var bones = new Array(); + for (var i = 0, n = vertices.length; i < n;) { + var boneCount = vertices[i++]; + bones.push(boneCount); + for (var nn = i + boneCount * 4; i < nn; i += 4) { + bones.push(vertices[i]); + weights.push(vertices[i + 1] * scale); + weights.push(vertices[i + 2] * scale); + weights.push(vertices[i + 3]); + } + } + attachment.bones = bones; + attachment.vertices = spine.Utils.toFloatArray(weights); + }; + SkeletonJson.prototype.readAnimation = function (map, name, skeletonData) { + var scale = this.scale; + var timelines = new Array(); + var duration = 0; + if (map.slots) { + for (var slotName in map.slots) { + var slotMap = map.slots[slotName]; + var slotIndex = skeletonData.findSlotIndex(slotName); + if (slotIndex == -1) + throw new Error("Slot not found: " + slotName); + for (var timelineName in slotMap) { + var timelineMap = slotMap[timelineName]; + if (timelineName == "attachment") { + var timeline = new spine.AttachmentTimeline(timelineMap.length); + timeline.slotIndex = slotIndex; + var frameIndex = 0; + for (var i = 0; i < timelineMap.length; i++) { + var valueMap = timelineMap[i]; + timeline.setFrame(frameIndex++, valueMap.time, valueMap.name); + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } + else if (timelineName == "color") { + var timeline = new spine.ColorTimeline(timelineMap.length); + timeline.slotIndex = slotIndex; + var frameIndex = 0; + for (var i = 0; i < timelineMap.length; i++) { + var valueMap = timelineMap[i]; + var color = new spine.Color(); + color.setFromString(valueMap.color); + timeline.setFrame(frameIndex, valueMap.time, color.r, color.g, color.b, color.a); + this.readCurve(valueMap, timeline, frameIndex); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * spine.ColorTimeline.ENTRIES]); + } + else if (timelineName == "twoColor") { + var timeline = new spine.TwoColorTimeline(timelineMap.length); + timeline.slotIndex = slotIndex; + var frameIndex = 0; + for (var i = 0; i < timelineMap.length; i++) { + var valueMap = timelineMap[i]; + var light = new spine.Color(); + var dark = new spine.Color(); + light.setFromString(valueMap.light); + dark.setFromString(valueMap.dark); + timeline.setFrame(frameIndex, valueMap.time, light.r, light.g, light.b, light.a, dark.r, dark.g, dark.b); + this.readCurve(valueMap, timeline, frameIndex); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * spine.TwoColorTimeline.ENTRIES]); + } + else + throw new Error("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"); + } + } + } + if (map.bones) { + for (var boneName in map.bones) { + var boneMap = map.bones[boneName]; + var boneIndex = skeletonData.findBoneIndex(boneName); + if (boneIndex == -1) + throw new Error("Bone not found: " + boneName); + for (var timelineName in boneMap) { + var timelineMap = boneMap[timelineName]; + if (timelineName === "rotate") { + var timeline = new spine.RotateTimeline(timelineMap.length); + timeline.boneIndex = boneIndex; + var frameIndex = 0; + for (var i = 0; i < timelineMap.length; i++) { + var valueMap = timelineMap[i]; + timeline.setFrame(frameIndex, valueMap.time, valueMap.angle); + this.readCurve(valueMap, timeline, frameIndex); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * spine.RotateTimeline.ENTRIES]); + } + else if (timelineName === "translate" || timelineName === "scale" || timelineName === "shear") { + var timeline = null; + var timelineScale = 1; + if (timelineName === "scale") + timeline = new spine.ScaleTimeline(timelineMap.length); + else if (timelineName === "shear") + timeline = new spine.ShearTimeline(timelineMap.length); + else { + timeline = new spine.TranslateTimeline(timelineMap.length); + timelineScale = scale; + } + timeline.boneIndex = boneIndex; + var frameIndex = 0; + for (var i = 0; i < timelineMap.length; i++) { + var valueMap = timelineMap[i]; + var x = this.getValue(valueMap, "x", 0), y = this.getValue(valueMap, "y", 0); + timeline.setFrame(frameIndex, valueMap.time, x * timelineScale, y * timelineScale); + this.readCurve(valueMap, timeline, frameIndex); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * spine.TranslateTimeline.ENTRIES]); + } + else + throw new Error("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"); + } + } + } + if (map.ik) { + for (var constraintName in map.ik) { + var constraintMap = map.ik[constraintName]; + var constraint = skeletonData.findIkConstraint(constraintName); + var timeline = new spine.IkConstraintTimeline(constraintMap.length); + timeline.ikConstraintIndex = skeletonData.ikConstraints.indexOf(constraint); + var frameIndex = 0; + for (var i = 0; i < constraintMap.length; i++) { + var valueMap = constraintMap[i]; + timeline.setFrame(frameIndex, valueMap.time, this.getValue(valueMap, "mix", 1), this.getValue(valueMap, "bendPositive", true) ? 1 : -1); + this.readCurve(valueMap, timeline, frameIndex); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * spine.IkConstraintTimeline.ENTRIES]); + } + } + if (map.transform) { + for (var constraintName in map.transform) { + var constraintMap = map.transform[constraintName]; + var constraint = skeletonData.findTransformConstraint(constraintName); + var timeline = new spine.TransformConstraintTimeline(constraintMap.length); + timeline.transformConstraintIndex = skeletonData.transformConstraints.indexOf(constraint); + var frameIndex = 0; + for (var i = 0; i < constraintMap.length; i++) { + var valueMap = constraintMap[i]; + timeline.setFrame(frameIndex, valueMap.time, this.getValue(valueMap, "rotateMix", 1), this.getValue(valueMap, "translateMix", 1), this.getValue(valueMap, "scaleMix", 1), this.getValue(valueMap, "shearMix", 1)); + this.readCurve(valueMap, timeline, frameIndex); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * spine.TransformConstraintTimeline.ENTRIES]); + } + } + if (map.paths) { + for (var constraintName in map.paths) { + var constraintMap = map.paths[constraintName]; + var index = skeletonData.findPathConstraintIndex(constraintName); + if (index == -1) + throw new Error("Path constraint not found: " + constraintName); + var data = skeletonData.pathConstraints[index]; + for (var timelineName in constraintMap) { + var timelineMap = constraintMap[timelineName]; + if (timelineName === "position" || timelineName === "spacing") { + var timeline = null; + var timelineScale = 1; + if (timelineName === "spacing") { + timeline = new spine.PathConstraintSpacingTimeline(timelineMap.length); + if (data.spacingMode == spine.SpacingMode.Length || data.spacingMode == spine.SpacingMode.Fixed) + timelineScale = scale; + } + else { + timeline = new spine.PathConstraintPositionTimeline(timelineMap.length); + if (data.positionMode == spine.PositionMode.Fixed) + timelineScale = scale; + } + timeline.pathConstraintIndex = index; + var frameIndex = 0; + for (var i = 0; i < timelineMap.length; i++) { + var valueMap = timelineMap[i]; + timeline.setFrame(frameIndex, valueMap.time, this.getValue(valueMap, timelineName, 0) * timelineScale); + this.readCurve(valueMap, timeline, frameIndex); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * spine.PathConstraintPositionTimeline.ENTRIES]); + } + else if (timelineName === "mix") { + var timeline = new spine.PathConstraintMixTimeline(timelineMap.length); + timeline.pathConstraintIndex = index; + var frameIndex = 0; + for (var i = 0; i < timelineMap.length; i++) { + var valueMap = timelineMap[i]; + timeline.setFrame(frameIndex, valueMap.time, this.getValue(valueMap, "rotateMix", 1), this.getValue(valueMap, "translateMix", 1)); + this.readCurve(valueMap, timeline, frameIndex); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[(timeline.getFrameCount() - 1) * spine.PathConstraintMixTimeline.ENTRIES]); + } + } + } + } + if (map.deform) { + for (var deformName in map.deform) { + var deformMap = map.deform[deformName]; + var skin = skeletonData.findSkin(deformName); + if (skin == null) + throw new Error("Skin not found: " + deformName); + for (var slotName in deformMap) { + var slotMap = deformMap[slotName]; + var slotIndex = skeletonData.findSlotIndex(slotName); + if (slotIndex == -1) + throw new Error("Slot not found: " + slotMap.name); + for (var timelineName in slotMap) { + var timelineMap = slotMap[timelineName]; + var attachment = skin.getAttachment(slotIndex, timelineName); + if (attachment == null) + throw new Error("Deform attachment not found: " + timelineMap.name); + var weighted = attachment.bones != null; + var vertices = attachment.vertices; + var deformLength = weighted ? vertices.length / 3 * 2 : vertices.length; + var timeline = new spine.DeformTimeline(timelineMap.length); + timeline.slotIndex = slotIndex; + timeline.attachment = attachment; + var frameIndex = 0; + for (var j = 0; j < timelineMap.length; j++) { + var valueMap = timelineMap[j]; + var deform = void 0; + var verticesValue = this.getValue(valueMap, "vertices", null); + if (verticesValue == null) + deform = weighted ? spine.Utils.newFloatArray(deformLength) : vertices; + else { + deform = spine.Utils.newFloatArray(deformLength); + var start = this.getValue(valueMap, "offset", 0); + spine.Utils.arrayCopy(verticesValue, 0, deform, start, verticesValue.length); + if (scale != 1) { + for (var i = start, n = i + verticesValue.length; i < n; i++) + deform[i] *= scale; + } + if (!weighted) { + for (var i = 0; i < deformLength; i++) + deform[i] += vertices[i]; + } + } + timeline.setFrame(frameIndex, valueMap.time, deform); + this.readCurve(valueMap, timeline, frameIndex); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } + } + } + } + var drawOrderNode = map.drawOrder; + if (drawOrderNode == null) + drawOrderNode = map.draworder; + if (drawOrderNode != null) { + var timeline = new spine.DrawOrderTimeline(drawOrderNode.length); + var slotCount = skeletonData.slots.length; + var frameIndex = 0; + for (var j = 0; j < drawOrderNode.length; j++) { + var drawOrderMap = drawOrderNode[j]; + var drawOrder = null; + var offsets = this.getValue(drawOrderMap, "offsets", null); + if (offsets != null) { + drawOrder = spine.Utils.newArray(slotCount, -1); + var unchanged = spine.Utils.newArray(slotCount - offsets.length, 0); + var originalIndex = 0, unchangedIndex = 0; + for (var i = 0; i < offsets.length; i++) { + var offsetMap = offsets[i]; + var slotIndex = skeletonData.findSlotIndex(offsetMap.slot); + if (slotIndex == -1) + throw new Error("Slot not found: " + offsetMap.slot); + while (originalIndex != slotIndex) + unchanged[unchangedIndex++] = originalIndex++; + drawOrder[originalIndex + offsetMap.offset] = originalIndex++; + } + while (originalIndex < slotCount) + unchanged[unchangedIndex++] = originalIndex++; + for (var i = slotCount - 1; i >= 0; i--) + if (drawOrder[i] == -1) + drawOrder[i] = unchanged[--unchangedIndex]; + } + timeline.setFrame(frameIndex++, drawOrderMap.time, drawOrder); + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } + if (map.events) { + var timeline = new spine.EventTimeline(map.events.length); + var frameIndex = 0; + for (var i = 0; i < map.events.length; i++) { + var eventMap = map.events[i]; + var eventData = skeletonData.findEvent(eventMap.name); + if (eventData == null) + throw new Error("Event not found: " + eventMap.name); + var event_5 = new spine.Event(spine.Utils.toSinglePrecision(eventMap.time), eventData); + event_5.intValue = this.getValue(eventMap, "int", eventData.intValue); + event_5.floatValue = this.getValue(eventMap, "float", eventData.floatValue); + event_5.stringValue = this.getValue(eventMap, "string", eventData.stringValue); + timeline.setFrame(frameIndex++, event_5); + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } + if (isNaN(duration)) { + throw new Error("Error while parsing animation, duration is NaN"); + } + skeletonData.animations.push(new spine.Animation(name, timelines, duration)); + }; + SkeletonJson.prototype.readCurve = function (map, timeline, frameIndex) { + if (!map.curve) + return; + if (map.curve === "stepped") + timeline.setStepped(frameIndex); + else if (Object.prototype.toString.call(map.curve) === '[object Array]') { + var curve = map.curve; + timeline.setCurve(frameIndex, curve[0], curve[1], curve[2], curve[3]); + } + }; + SkeletonJson.prototype.getValue = function (map, prop, defaultValue) { + return map[prop] !== undefined ? map[prop] : defaultValue; + }; + SkeletonJson.blendModeFromString = function (str) { + str = str.toLowerCase(); + if (str == "normal") + return spine.BlendMode.Normal; + if (str == "additive") + return spine.BlendMode.Additive; + if (str == "multiply") + return spine.BlendMode.Multiply; + if (str == "screen") + return spine.BlendMode.Screen; + throw new Error("Unknown blend mode: " + str); + }; + SkeletonJson.positionModeFromString = function (str) { + str = str.toLowerCase(); + if (str == "fixed") + return spine.PositionMode.Fixed; + if (str == "percent") + return spine.PositionMode.Percent; + throw new Error("Unknown position mode: " + str); + }; + SkeletonJson.spacingModeFromString = function (str) { + str = str.toLowerCase(); + if (str == "length") + return spine.SpacingMode.Length; + if (str == "fixed") + return spine.SpacingMode.Fixed; + if (str == "percent") + return spine.SpacingMode.Percent; + throw new Error("Unknown position mode: " + str); + }; + SkeletonJson.rotateModeFromString = function (str) { + str = str.toLowerCase(); + if (str == "tangent") + return spine.RotateMode.Tangent; + if (str == "chain") + return spine.RotateMode.Chain; + if (str == "chainscale") + return spine.RotateMode.ChainScale; + throw new Error("Unknown rotate mode: " + str); + }; + SkeletonJson.transformModeFromString = function (str) { + str = str.toLowerCase(); + if (str == "normal") + return spine.TransformMode.Normal; + if (str == "onlytranslation") + return spine.TransformMode.OnlyTranslation; + if (str == "norotationorreflection") + return spine.TransformMode.NoRotationOrReflection; + if (str == "noscale") + return spine.TransformMode.NoScale; + if (str == "noscaleorreflection") + return spine.TransformMode.NoScaleOrReflection; + throw new Error("Unknown transform mode: " + str); + }; + return SkeletonJson; + }()); + spine.SkeletonJson = SkeletonJson; + var LinkedMesh = (function () { + function LinkedMesh(mesh, skin, slotIndex, parent) { + this.mesh = mesh; + this.skin = skin; + this.slotIndex = slotIndex; + this.parent = parent; + } + return LinkedMesh; + }()); +})(spine || (spine = {})); +var spine; +(function (spine) { + var Skin = (function () { + function Skin(name) { + this.attachments = new Array(); + if (name == null) + throw new Error("name cannot be null."); + this.name = name; + } + Skin.prototype.addAttachment = function (slotIndex, name, attachment) { + if (attachment == null) + throw new Error("attachment cannot be null."); + var attachments = this.attachments; + if (slotIndex >= attachments.length) + attachments.length = slotIndex + 1; + if (!attachments[slotIndex]) + attachments[slotIndex] = {}; + attachments[slotIndex][name] = attachment; + }; + Skin.prototype.getAttachment = function (slotIndex, name) { + var dictionary = this.attachments[slotIndex]; + return dictionary ? dictionary[name] : null; + }; + Skin.prototype.attachAll = function (skeleton, oldSkin) { + var slotIndex = 0; + for (var i = 0; i < skeleton.slots.length; i++) { + var slot = skeleton.slots[i]; + var slotAttachment = slot.getAttachment(); + if (slotAttachment && slotIndex < oldSkin.attachments.length) { + var dictionary = oldSkin.attachments[slotIndex]; + for (var key in dictionary) { + var skinAttachment = dictionary[key]; + if (slotAttachment == skinAttachment) { + var attachment = this.getAttachment(slotIndex, key); + if (attachment != null) + slot.setAttachment(attachment); + break; + } + } + } + slotIndex++; + } + }; + return Skin; + }()); + spine.Skin = Skin; +})(spine || (spine = {})); +var spine; +(function (spine) { + var Slot = (function () { + function Slot(data, bone) { + this.attachmentVertices = new Array(); + if (data == null) + throw new Error("data cannot be null."); + if (bone == null) + throw new Error("bone cannot be null."); + this.data = data; + this.bone = bone; + this.color = new spine.Color(); + this.darkColor = data.darkColor == null ? null : new spine.Color(); + this.setToSetupPose(); + } + Slot.prototype.getAttachment = function () { + return this.attachment; + }; + Slot.prototype.setAttachment = function (attachment) { + if (this.attachment == attachment) + return; + this.attachment = attachment; + this.attachmentTime = this.bone.skeleton.time; + this.attachmentVertices.length = 0; + }; + Slot.prototype.setAttachmentTime = function (time) { + this.attachmentTime = this.bone.skeleton.time - time; + }; + Slot.prototype.getAttachmentTime = function () { + return this.bone.skeleton.time - this.attachmentTime; + }; + Slot.prototype.setToSetupPose = function () { + this.color.setFromColor(this.data.color); + if (this.darkColor != null) + this.darkColor.setFromColor(this.data.darkColor); + if (this.data.attachmentName == null) + this.attachment = null; + else { + this.attachment = null; + this.setAttachment(this.bone.skeleton.getAttachment(this.data.index, this.data.attachmentName)); + } + }; + return Slot; + }()); + spine.Slot = Slot; +})(spine || (spine = {})); +var spine; +(function (spine) { + var SlotData = (function () { + function SlotData(index, name, boneData) { + this.color = new spine.Color(1, 1, 1, 1); + if (index < 0) + throw new Error("index must be >= 0."); + if (name == null) + throw new Error("name cannot be null."); + if (boneData == null) + throw new Error("boneData cannot be null."); + this.index = index; + this.name = name; + this.boneData = boneData; + } + return SlotData; + }()); + spine.SlotData = SlotData; +})(spine || (spine = {})); +var spine; +(function (spine) { + var Texture = (function () { + function Texture(image) { + this._image = image; + } + Texture.prototype.getImage = function () { + return this._image; + }; + Texture.filterFromString = function (text) { + switch (text.toLowerCase()) { + case "nearest": return TextureFilter.Nearest; + case "linear": return TextureFilter.Linear; + case "mipmap": return TextureFilter.MipMap; + case "mipmapnearestnearest": return TextureFilter.MipMapNearestNearest; + case "mipmaplinearnearest": return TextureFilter.MipMapLinearNearest; + case "mipmapnearestlinear": return TextureFilter.MipMapNearestLinear; + case "mipmaplinearlinear": return TextureFilter.MipMapLinearLinear; + default: throw new Error("Unknown texture filter " + text); + } + }; + Texture.wrapFromString = function (text) { + switch (text.toLowerCase()) { + case "mirroredtepeat": return TextureWrap.MirroredRepeat; + case "clamptoedge": return TextureWrap.ClampToEdge; + case "repeat": return TextureWrap.Repeat; + default: throw new Error("Unknown texture wrap " + text); + } + }; + return Texture; + }()); + spine.Texture = Texture; + var TextureFilter; + (function (TextureFilter) { + TextureFilter[TextureFilter["Nearest"] = 9728] = "Nearest"; + TextureFilter[TextureFilter["Linear"] = 9729] = "Linear"; + TextureFilter[TextureFilter["MipMap"] = 9987] = "MipMap"; + TextureFilter[TextureFilter["MipMapNearestNearest"] = 9984] = "MipMapNearestNearest"; + TextureFilter[TextureFilter["MipMapLinearNearest"] = 9985] = "MipMapLinearNearest"; + TextureFilter[TextureFilter["MipMapNearestLinear"] = 9986] = "MipMapNearestLinear"; + TextureFilter[TextureFilter["MipMapLinearLinear"] = 9987] = "MipMapLinearLinear"; + })(TextureFilter = spine.TextureFilter || (spine.TextureFilter = {})); + var TextureWrap; + (function (TextureWrap) { + TextureWrap[TextureWrap["MirroredRepeat"] = 33648] = "MirroredRepeat"; + TextureWrap[TextureWrap["ClampToEdge"] = 33071] = "ClampToEdge"; + TextureWrap[TextureWrap["Repeat"] = 10497] = "Repeat"; + })(TextureWrap = spine.TextureWrap || (spine.TextureWrap = {})); + var TextureRegion = (function () { + function TextureRegion() { + this.u = 0; + this.v = 0; + this.u2 = 0; + this.v2 = 0; + this.width = 0; + this.height = 0; + this.rotate = false; + this.offsetX = 0; + this.offsetY = 0; + this.originalWidth = 0; + this.originalHeight = 0; + } + return TextureRegion; + }()); + spine.TextureRegion = TextureRegion; +})(spine || (spine = {})); +var spine; +(function (spine) { + var TextureAtlas = (function () { + function TextureAtlas(atlasText, textureLoader) { + this.pages = new Array(); + this.regions = new Array(); + this.load(atlasText, textureLoader); + } + TextureAtlas.prototype.load = function (atlasText, textureLoader) { + if (textureLoader == null) + throw new Error("textureLoader cannot be null."); + var reader = new TextureAtlasReader(atlasText); + var tuple = new Array(4); + var page = null; + while (true) { + var line = reader.readLine(); + if (line == null) + break; + line = line.trim(); + if (line.length == 0) + page = null; + else if (!page) { + page = new TextureAtlasPage(); + page.name = line; + if (reader.readTuple(tuple) == 2) { + page.width = parseInt(tuple[0]); + page.height = parseInt(tuple[1]); + reader.readTuple(tuple); + } + reader.readTuple(tuple); + page.minFilter = spine.Texture.filterFromString(tuple[0]); + page.magFilter = spine.Texture.filterFromString(tuple[1]); + var direction = reader.readValue(); + page.uWrap = spine.TextureWrap.ClampToEdge; + page.vWrap = spine.TextureWrap.ClampToEdge; + if (direction == "x") + page.uWrap = spine.TextureWrap.Repeat; + else if (direction == "y") + page.vWrap = spine.TextureWrap.Repeat; + else if (direction == "xy") + page.uWrap = page.vWrap = spine.TextureWrap.Repeat; + page.texture = textureLoader(line); + page.texture.setFilters(page.minFilter, page.magFilter); + page.texture.setWraps(page.uWrap, page.vWrap); + page.width = page.texture.getImage().width; + page.height = page.texture.getImage().height; + this.pages.push(page); + } + else { + var region = new TextureAtlasRegion(); + region.name = line; + region.page = page; + region.rotate = reader.readValue() == "true"; + reader.readTuple(tuple); + var x = parseInt(tuple[0]); + var y = parseInt(tuple[1]); + reader.readTuple(tuple); + var width = parseInt(tuple[0]); + var height = parseInt(tuple[1]); + region.u = x / page.width; + region.v = y / page.height; + if (region.rotate) { + region.u2 = (x + height) / page.width; + region.v2 = (y + width) / page.height; + } + else { + region.u2 = (x + width) / page.width; + region.v2 = (y + height) / page.height; + } + region.x = x; + region.y = y; + region.width = Math.abs(width); + region.height = Math.abs(height); + if (reader.readTuple(tuple) == 4) { + if (reader.readTuple(tuple) == 4) { + reader.readTuple(tuple); + } + } + region.originalWidth = parseInt(tuple[0]); + region.originalHeight = parseInt(tuple[1]); + reader.readTuple(tuple); + region.offsetX = parseInt(tuple[0]); + region.offsetY = parseInt(tuple[1]); + region.index = parseInt(reader.readValue()); + region.texture = page.texture; + this.regions.push(region); + } + } + }; + TextureAtlas.prototype.findRegion = function (name) { + for (var i = 0; i < this.regions.length; i++) { + if (this.regions[i].name == name) { + return this.regions[i]; + } + } + return null; + }; + TextureAtlas.prototype.dispose = function () { + for (var i = 0; i < this.pages.length; i++) { + this.pages[i].texture.dispose(); + } + }; + return TextureAtlas; + }()); + spine.TextureAtlas = TextureAtlas; + var TextureAtlasReader = (function () { + function TextureAtlasReader(text) { + this.index = 0; + this.lines = text.split(/\r\n|\r|\n/); + } + TextureAtlasReader.prototype.readLine = function () { + if (this.index >= this.lines.length) + return null; + return this.lines[this.index++]; + }; + TextureAtlasReader.prototype.readValue = function () { + var line = this.readLine(); + var colon = line.indexOf(":"); + if (colon == -1) + throw new Error("Invalid line: " + line); + return line.substring(colon + 1).trim(); + }; + TextureAtlasReader.prototype.readTuple = function (tuple) { + var line = this.readLine(); + var colon = line.indexOf(":"); + if (colon == -1) + throw new Error("Invalid line: " + line); + var i = 0, lastMatch = colon + 1; + for (; i < 3; i++) { + var comma = line.indexOf(",", lastMatch); + if (comma == -1) + break; + tuple[i] = line.substr(lastMatch, comma - lastMatch).trim(); + lastMatch = comma + 1; + } + tuple[i] = line.substring(lastMatch).trim(); + return i + 1; + }; + return TextureAtlasReader; + }()); + var TextureAtlasPage = (function () { + function TextureAtlasPage() { + } + return TextureAtlasPage; + }()); + spine.TextureAtlasPage = TextureAtlasPage; + var TextureAtlasRegion = (function (_super) { + __extends(TextureAtlasRegion, _super); + function TextureAtlasRegion() { + return _super !== null && _super.apply(this, arguments) || this; + } + return TextureAtlasRegion; + }(spine.TextureRegion)); + spine.TextureAtlasRegion = TextureAtlasRegion; +})(spine || (spine = {})); +var spine; +(function (spine) { + var TransformConstraint = (function () { + function TransformConstraint(data, skeleton) { + this.rotateMix = 0; + this.translateMix = 0; + this.scaleMix = 0; + this.shearMix = 0; + this.temp = new spine.Vector2(); + if (data == null) + throw new Error("data cannot be null."); + if (skeleton == null) + throw new Error("skeleton cannot be null."); + this.data = data; + this.rotateMix = data.rotateMix; + this.translateMix = data.translateMix; + this.scaleMix = data.scaleMix; + this.shearMix = data.shearMix; + this.bones = new Array(); + for (var i = 0; i < data.bones.length; i++) + this.bones.push(skeleton.findBone(data.bones[i].name)); + this.target = skeleton.findBone(data.target.name); + } + TransformConstraint.prototype.apply = function () { + this.update(); + }; + TransformConstraint.prototype.update = function () { + if (this.data.local) { + if (this.data.relative) + this.applyRelativeLocal(); + else + this.applyAbsoluteLocal(); + } + else { + if (this.data.relative) + this.applyRelativeWorld(); + else + this.applyAbsoluteWorld(); + } + }; + TransformConstraint.prototype.applyAbsoluteWorld = function () { + var rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix; + var target = this.target; + var ta = target.a, tb = target.b, tc = target.c, td = target.d; + var degRadReflect = ta * td - tb * tc > 0 ? spine.MathUtils.degRad : -spine.MathUtils.degRad; + var offsetRotation = this.data.offsetRotation * degRadReflect; + var offsetShearY = this.data.offsetShearY * degRadReflect; + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) { + var bone = bones[i]; + var modified = false; + if (rotateMix != 0) { + var a = bone.a, b = bone.b, c = bone.c, d = bone.d; + var r = Math.atan2(tc, ta) - Math.atan2(c, a) + offsetRotation; + if (r > spine.MathUtils.PI) + r -= spine.MathUtils.PI2; + else if (r < -spine.MathUtils.PI) + r += spine.MathUtils.PI2; + r *= rotateMix; + var cos = Math.cos(r), sin = Math.sin(r); + bone.a = cos * a - sin * c; + bone.b = cos * b - sin * d; + bone.c = sin * a + cos * c; + bone.d = sin * b + cos * d; + modified = true; + } + if (translateMix != 0) { + var temp = this.temp; + target.localToWorld(temp.set(this.data.offsetX, this.data.offsetY)); + bone.worldX += (temp.x - bone.worldX) * translateMix; + bone.worldY += (temp.y - bone.worldY) * translateMix; + modified = true; + } + if (scaleMix > 0) { + var s = Math.sqrt(bone.a * bone.a + bone.c * bone.c); + var ts = Math.sqrt(ta * ta + tc * tc); + if (s > 0.00001) + s = (s + (ts - s + this.data.offsetScaleX) * scaleMix) / s; + bone.a *= s; + bone.c *= s; + s = Math.sqrt(bone.b * bone.b + bone.d * bone.d); + ts = Math.sqrt(tb * tb + td * td); + if (s > 0.00001) + s = (s + (ts - s + this.data.offsetScaleY) * scaleMix) / s; + bone.b *= s; + bone.d *= s; + modified = true; + } + if (shearMix > 0) { + var b = bone.b, d = bone.d; + var by = Math.atan2(d, b); + var r = Math.atan2(td, tb) - Math.atan2(tc, ta) - (by - Math.atan2(bone.c, bone.a)); + if (r > spine.MathUtils.PI) + r -= spine.MathUtils.PI2; + else if (r < -spine.MathUtils.PI) + r += spine.MathUtils.PI2; + r = by + (r + offsetShearY) * shearMix; + var s = Math.sqrt(b * b + d * d); + bone.b = Math.cos(r) * s; + bone.d = Math.sin(r) * s; + modified = true; + } + if (modified) + bone.appliedValid = false; + } + }; + TransformConstraint.prototype.applyRelativeWorld = function () { + var rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix; + var target = this.target; + var ta = target.a, tb = target.b, tc = target.c, td = target.d; + var degRadReflect = ta * td - tb * tc > 0 ? spine.MathUtils.degRad : -spine.MathUtils.degRad; + var offsetRotation = this.data.offsetRotation * degRadReflect, offsetShearY = this.data.offsetShearY * degRadReflect; + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) { + var bone = bones[i]; + var modified = false; + if (rotateMix != 0) { + var a = bone.a, b = bone.b, c = bone.c, d = bone.d; + var r = Math.atan2(tc, ta) + offsetRotation; + if (r > spine.MathUtils.PI) + r -= spine.MathUtils.PI2; + else if (r < -spine.MathUtils.PI) + r += spine.MathUtils.PI2; + r *= rotateMix; + var cos = Math.cos(r), sin = Math.sin(r); + bone.a = cos * a - sin * c; + bone.b = cos * b - sin * d; + bone.c = sin * a + cos * c; + bone.d = sin * b + cos * d; + modified = true; + } + if (translateMix != 0) { + var temp = this.temp; + target.localToWorld(temp.set(this.data.offsetX, this.data.offsetY)); + bone.worldX += temp.x * translateMix; + bone.worldY += temp.y * translateMix; + modified = true; + } + if (scaleMix > 0) { + var s = (Math.sqrt(ta * ta + tc * tc) - 1 + this.data.offsetScaleX) * scaleMix + 1; + bone.a *= s; + bone.c *= s; + s = (Math.sqrt(tb * tb + td * td) - 1 + this.data.offsetScaleY) * scaleMix + 1; + bone.b *= s; + bone.d *= s; + modified = true; + } + if (shearMix > 0) { + var r = Math.atan2(td, tb) - Math.atan2(tc, ta); + if (r > spine.MathUtils.PI) + r -= spine.MathUtils.PI2; + else if (r < -spine.MathUtils.PI) + r += spine.MathUtils.PI2; + var b = bone.b, d = bone.d; + r = Math.atan2(d, b) + (r - spine.MathUtils.PI / 2 + offsetShearY) * shearMix; + var s = Math.sqrt(b * b + d * d); + bone.b = Math.cos(r) * s; + bone.d = Math.sin(r) * s; + modified = true; + } + if (modified) + bone.appliedValid = false; + } + }; + TransformConstraint.prototype.applyAbsoluteLocal = function () { + var rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix; + var target = this.target; + if (!target.appliedValid) + target.updateAppliedTransform(); + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) { + var bone = bones[i]; + if (!bone.appliedValid) + bone.updateAppliedTransform(); + var rotation = bone.arotation; + if (rotateMix != 0) { + var r = target.arotation - rotation + this.data.offsetRotation; + r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360; + rotation += r * rotateMix; + } + var x = bone.ax, y = bone.ay; + if (translateMix != 0) { + x += (target.ax - x + this.data.offsetX) * translateMix; + y += (target.ay - y + this.data.offsetY) * translateMix; + } + var scaleX = bone.ascaleX, scaleY = bone.ascaleY; + if (scaleMix > 0) { + if (scaleX > 0.00001) + scaleX = (scaleX + (target.ascaleX - scaleX + this.data.offsetScaleX) * scaleMix) / scaleX; + if (scaleY > 0.00001) + scaleY = (scaleY + (target.ascaleY - scaleY + this.data.offsetScaleY) * scaleMix) / scaleY; + } + var shearY = bone.ashearY; + if (shearMix > 0) { + var r = target.ashearY - shearY + this.data.offsetShearY; + r -= (16384 - ((16384.499999999996 - r / 360) | 0)) * 360; + bone.shearY += r * shearMix; + } + bone.updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY); + } + }; + TransformConstraint.prototype.applyRelativeLocal = function () { + var rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix, shearMix = this.shearMix; + var target = this.target; + if (!target.appliedValid) + target.updateAppliedTransform(); + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) { + var bone = bones[i]; + if (!bone.appliedValid) + bone.updateAppliedTransform(); + var rotation = bone.arotation; + if (rotateMix != 0) + rotation += (target.arotation + this.data.offsetRotation) * rotateMix; + var x = bone.ax, y = bone.ay; + if (translateMix != 0) { + x += (target.ax + this.data.offsetX) * translateMix; + y += (target.ay + this.data.offsetY) * translateMix; + } + var scaleX = bone.ascaleX, scaleY = bone.ascaleY; + if (scaleMix > 0) { + if (scaleX > 0.00001) + scaleX *= ((target.ascaleX - 1 + this.data.offsetScaleX) * scaleMix) + 1; + if (scaleY > 0.00001) + scaleY *= ((target.ascaleY - 1 + this.data.offsetScaleY) * scaleMix) + 1; + } + var shearY = bone.ashearY; + if (shearMix > 0) + shearY += (target.ashearY + this.data.offsetShearY) * shearMix; + bone.updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY); + } + }; + TransformConstraint.prototype.getOrder = function () { + return this.data.order; + }; + return TransformConstraint; + }()); + spine.TransformConstraint = TransformConstraint; +})(spine || (spine = {})); +var spine; +(function (spine) { + var TransformConstraintData = (function () { + function TransformConstraintData(name) { + this.order = 0; + this.bones = new Array(); + this.rotateMix = 0; + this.translateMix = 0; + this.scaleMix = 0; + this.shearMix = 0; + this.offsetRotation = 0; + this.offsetX = 0; + this.offsetY = 0; + this.offsetScaleX = 0; + this.offsetScaleY = 0; + this.offsetShearY = 0; + this.relative = false; + this.local = false; + if (name == null) + throw new Error("name cannot be null."); + this.name = name; + } + return TransformConstraintData; + }()); + spine.TransformConstraintData = TransformConstraintData; +})(spine || (spine = {})); +var spine; +(function (spine) { + var Triangulator = (function () { + function Triangulator() { + this.convexPolygons = new Array(); + this.convexPolygonsIndices = new Array(); + this.indicesArray = new Array(); + this.isConcaveArray = new Array(); + this.triangles = new Array(); + this.polygonPool = new spine.Pool(function () { + return new Array(); + }); + this.polygonIndicesPool = new spine.Pool(function () { + return new Array(); + }); + } + Triangulator.prototype.triangulate = function (verticesArray) { + var vertices = verticesArray; + var vertexCount = verticesArray.length >> 1; + var indices = this.indicesArray; + indices.length = 0; + for (var i = 0; i < vertexCount; i++) + indices[i] = i; + var isConcave = this.isConcaveArray; + isConcave.length = 0; + for (var i = 0, n = vertexCount; i < n; ++i) + isConcave[i] = Triangulator.isConcave(i, vertexCount, vertices, indices); + var triangles = this.triangles; + triangles.length = 0; + while (vertexCount > 3) { + var previous = vertexCount - 1, i = 0, next = 1; + while (true) { + outer: if (!isConcave[i]) { + var p1 = indices[previous] << 1, p2 = indices[i] << 1, p3 = indices[next] << 1; + var p1x = vertices[p1], p1y = vertices[p1 + 1]; + var p2x = vertices[p2], p2y = vertices[p2 + 1]; + var p3x = vertices[p3], p3y = vertices[p3 + 1]; + for (var ii = (next + 1) % vertexCount; ii != previous; ii = (ii + 1) % vertexCount) { + if (!isConcave[ii]) + continue; + var v = indices[ii] << 1; + var vx = vertices[v], vy = vertices[v + 1]; + if (Triangulator.positiveArea(p3x, p3y, p1x, p1y, vx, vy)) { + if (Triangulator.positiveArea(p1x, p1y, p2x, p2y, vx, vy)) { + if (Triangulator.positiveArea(p2x, p2y, p3x, p3y, vx, vy)) + break outer; + } + } + } + break; + } + if (next == 0) { + do { + if (!isConcave[i]) + break; + i--; + } while (i > 0); + break; + } + previous = i; + i = next; + next = (next + 1) % vertexCount; + } + triangles.push(indices[(vertexCount + i - 1) % vertexCount]); + triangles.push(indices[i]); + triangles.push(indices[(i + 1) % vertexCount]); + indices.splice(i, 1); + isConcave.splice(i, 1); + vertexCount--; + var previousIndex = (vertexCount + i - 1) % vertexCount; + var nextIndex = i == vertexCount ? 0 : i; + isConcave[previousIndex] = Triangulator.isConcave(previousIndex, vertexCount, vertices, indices); + isConcave[nextIndex] = Triangulator.isConcave(nextIndex, vertexCount, vertices, indices); + } + if (vertexCount == 3) { + triangles.push(indices[2]); + triangles.push(indices[0]); + triangles.push(indices[1]); + } + return triangles; + }; + Triangulator.prototype.decompose = function (verticesArray, triangles) { + var vertices = verticesArray; + var convexPolygons = this.convexPolygons; + this.polygonPool.freeAll(convexPolygons); + convexPolygons.length = 0; + var convexPolygonsIndices = this.convexPolygonsIndices; + this.polygonIndicesPool.freeAll(convexPolygonsIndices); + convexPolygonsIndices.length = 0; + var polygonIndices = this.polygonIndicesPool.obtain(); + polygonIndices.length = 0; + var polygon = this.polygonPool.obtain(); + polygon.length = 0; + var fanBaseIndex = -1, lastWinding = 0; + for (var i = 0, n = triangles.length; i < n; i += 3) { + var t1 = triangles[i] << 1, t2 = triangles[i + 1] << 1, t3 = triangles[i + 2] << 1; + var x1 = vertices[t1], y1 = vertices[t1 + 1]; + var x2 = vertices[t2], y2 = vertices[t2 + 1]; + var x3 = vertices[t3], y3 = vertices[t3 + 1]; + var merged = false; + if (fanBaseIndex == t1) { + var o = polygon.length - 4; + var winding1 = Triangulator.winding(polygon[o], polygon[o + 1], polygon[o + 2], polygon[o + 3], x3, y3); + var winding2 = Triangulator.winding(x3, y3, polygon[0], polygon[1], polygon[2], polygon[3]); + if (winding1 == lastWinding && winding2 == lastWinding) { + polygon.push(x3); + polygon.push(y3); + polygonIndices.push(t3); + merged = true; + } + } + if (!merged) { + if (polygon.length > 0) { + convexPolygons.push(polygon); + convexPolygonsIndices.push(polygonIndices); + } + else { + this.polygonPool.free(polygon); + this.polygonIndicesPool.free(polygonIndices); + } + polygon = this.polygonPool.obtain(); + polygon.length = 0; + polygon.push(x1); + polygon.push(y1); + polygon.push(x2); + polygon.push(y2); + polygon.push(x3); + polygon.push(y3); + polygonIndices = this.polygonIndicesPool.obtain(); + polygonIndices.length = 0; + polygonIndices.push(t1); + polygonIndices.push(t2); + polygonIndices.push(t3); + lastWinding = Triangulator.winding(x1, y1, x2, y2, x3, y3); + fanBaseIndex = t1; + } + } + if (polygon.length > 0) { + convexPolygons.push(polygon); + convexPolygonsIndices.push(polygonIndices); + } + for (var i = 0, n = convexPolygons.length; i < n; i++) { + polygonIndices = convexPolygonsIndices[i]; + if (polygonIndices.length == 0) + continue; + var firstIndex = polygonIndices[0]; + var lastIndex = polygonIndices[polygonIndices.length - 1]; + polygon = convexPolygons[i]; + var o = polygon.length - 4; + var prevPrevX = polygon[o], prevPrevY = polygon[o + 1]; + var prevX = polygon[o + 2], prevY = polygon[o + 3]; + var firstX = polygon[0], firstY = polygon[1]; + var secondX = polygon[2], secondY = polygon[3]; + var winding = Triangulator.winding(prevPrevX, prevPrevY, prevX, prevY, firstX, firstY); + for (var ii = 0; ii < n; ii++) { + if (ii == i) + continue; + var otherIndices = convexPolygonsIndices[ii]; + if (otherIndices.length != 3) + continue; + var otherFirstIndex = otherIndices[0]; + var otherSecondIndex = otherIndices[1]; + var otherLastIndex = otherIndices[2]; + var otherPoly = convexPolygons[ii]; + var x3 = otherPoly[otherPoly.length - 2], y3 = otherPoly[otherPoly.length - 1]; + if (otherFirstIndex != firstIndex || otherSecondIndex != lastIndex) + continue; + var winding1 = Triangulator.winding(prevPrevX, prevPrevY, prevX, prevY, x3, y3); + var winding2 = Triangulator.winding(x3, y3, firstX, firstY, secondX, secondY); + if (winding1 == winding && winding2 == winding) { + otherPoly.length = 0; + otherIndices.length = 0; + polygon.push(x3); + polygon.push(y3); + polygonIndices.push(otherLastIndex); + prevPrevX = prevX; + prevPrevY = prevY; + prevX = x3; + prevY = y3; + ii = 0; + } + } + } + for (var i = convexPolygons.length - 1; i >= 0; i--) { + polygon = convexPolygons[i]; + if (polygon.length == 0) { + convexPolygons.splice(i, 1); + this.polygonPool.free(polygon); + polygonIndices = convexPolygonsIndices[i]; + convexPolygonsIndices.splice(i, 1); + this.polygonIndicesPool.free(polygonIndices); + } + } + return convexPolygons; + }; + Triangulator.isConcave = function (index, vertexCount, vertices, indices) { + var previous = indices[(vertexCount + index - 1) % vertexCount] << 1; + var current = indices[index] << 1; + var next = indices[(index + 1) % vertexCount] << 1; + return !this.positiveArea(vertices[previous], vertices[previous + 1], vertices[current], vertices[current + 1], vertices[next], vertices[next + 1]); + }; + Triangulator.positiveArea = function (p1x, p1y, p2x, p2y, p3x, p3y) { + return p1x * (p3y - p2y) + p2x * (p1y - p3y) + p3x * (p2y - p1y) >= 0; + }; + Triangulator.winding = function (p1x, p1y, p2x, p2y, p3x, p3y) { + var px = p2x - p1x, py = p2y - p1y; + return p3x * py - p3y * px + px * p1y - p1x * py >= 0 ? 1 : -1; + }; + return Triangulator; + }()); + spine.Triangulator = Triangulator; +})(spine || (spine = {})); +var spine; +(function (spine) { + var IntSet = (function () { + function IntSet() { + this.array = new Array(); + } + IntSet.prototype.add = function (value) { + var contains = this.contains(value); + this.array[value | 0] = value | 0; + return !contains; + }; + IntSet.prototype.contains = function (value) { + return this.array[value | 0] != undefined; + }; + IntSet.prototype.remove = function (value) { + this.array[value | 0] = undefined; + }; + IntSet.prototype.clear = function () { + this.array.length = 0; + }; + return IntSet; + }()); + spine.IntSet = IntSet; + var Color = (function () { + function Color(r, g, b, a) { + if (r === void 0) { r = 0; } + if (g === void 0) { g = 0; } + if (b === void 0) { b = 0; } + if (a === void 0) { a = 0; } + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } + Color.prototype.set = function (r, g, b, a) { + this.r = r; + this.g = g; + this.b = b; + this.a = a; + this.clamp(); + return this; + }; + Color.prototype.setFromColor = function (c) { + this.r = c.r; + this.g = c.g; + this.b = c.b; + this.a = c.a; + return this; + }; + Color.prototype.setFromString = function (hex) { + hex = hex.charAt(0) == '#' ? hex.substr(1) : hex; + this.r = parseInt(hex.substr(0, 2), 16) / 255.0; + this.g = parseInt(hex.substr(2, 2), 16) / 255.0; + this.b = parseInt(hex.substr(4, 2), 16) / 255.0; + this.a = (hex.length != 8 ? 255 : parseInt(hex.substr(6, 2), 16)) / 255.0; + return this; + }; + Color.prototype.add = function (r, g, b, a) { + this.r += r; + this.g += g; + this.b += b; + this.a += a; + this.clamp(); + return this; + }; + Color.prototype.clamp = function () { + if (this.r < 0) + this.r = 0; + else if (this.r > 1) + this.r = 1; + if (this.g < 0) + this.g = 0; + else if (this.g > 1) + this.g = 1; + if (this.b < 0) + this.b = 0; + else if (this.b > 1) + this.b = 1; + if (this.a < 0) + this.a = 0; + else if (this.a > 1) + this.a = 1; + return this; + }; + return Color; + }()); + Color.WHITE = new Color(1, 1, 1, 1); + Color.RED = new Color(1, 0, 0, 1); + Color.GREEN = new Color(0, 1, 0, 1); + Color.BLUE = new Color(0, 0, 1, 1); + Color.MAGENTA = new Color(1, 0, 1, 1); + spine.Color = Color; + var MathUtils = (function () { + function MathUtils() { + } + MathUtils.clamp = function (value, min, max) { + if (value < min) + return min; + if (value > max) + return max; + return value; + }; + MathUtils.cosDeg = function (degrees) { + return Math.cos(degrees * MathUtils.degRad); + }; + MathUtils.sinDeg = function (degrees) { + return Math.sin(degrees * MathUtils.degRad); + }; + MathUtils.signum = function (value) { + return value > 0 ? 1 : value < 0 ? -1 : 0; + }; + MathUtils.toInt = function (x) { + return x > 0 ? Math.floor(x) : Math.ceil(x); + }; + MathUtils.cbrt = function (x) { + var y = Math.pow(Math.abs(x), 1 / 3); + return x < 0 ? -y : y; + }; + MathUtils.randomTriangular = function (min, max) { + return MathUtils.randomTriangularWith(min, max, (min + max) * 0.5); + }; + MathUtils.randomTriangularWith = function (min, max, mode) { + var u = Math.random(); + var d = max - min; + if (u <= (mode - min) / d) + return min + Math.sqrt(u * d * (mode - min)); + return max - Math.sqrt((1 - u) * d * (max - mode)); + }; + return MathUtils; + }()); + MathUtils.PI = 3.1415927; + MathUtils.PI2 = MathUtils.PI * 2; + MathUtils.radiansToDegrees = 180 / MathUtils.PI; + MathUtils.radDeg = MathUtils.radiansToDegrees; + MathUtils.degreesToRadians = MathUtils.PI / 180; + MathUtils.degRad = MathUtils.degreesToRadians; + spine.MathUtils = MathUtils; + var Interpolation = (function () { + function Interpolation() { + } + Interpolation.prototype.apply = function (start, end, a) { + return start + (end - start) * this.applyInternal(a); + }; + return Interpolation; + }()); + spine.Interpolation = Interpolation; + var Pow = (function (_super) { + __extends(Pow, _super); + function Pow(power) { + var _this = _super.call(this) || this; + _this.power = 2; + _this.power = power; + return _this; + } + Pow.prototype.applyInternal = function (a) { + if (a <= 0.5) + return Math.pow(a * 2, this.power) / 2; + return Math.pow((a - 1) * 2, this.power) / (this.power % 2 == 0 ? -2 : 2) + 1; + }; + return Pow; + }(Interpolation)); + spine.Pow = Pow; + var PowOut = (function (_super) { + __extends(PowOut, _super); + function PowOut(power) { + return _super.call(this, power) || this; + } + PowOut.prototype.applyInternal = function (a) { + return Math.pow(a - 1, this.power) * (this.power % 2 == 0 ? -1 : 1) + 1; + }; + return PowOut; + }(Pow)); + spine.PowOut = PowOut; + var Utils = (function () { + function Utils() { + } + Utils.arrayCopy = function (source, sourceStart, dest, destStart, numElements) { + for (var i = sourceStart, j = destStart; i < sourceStart + numElements; i++, j++) { + dest[j] = source[i]; + } + }; + Utils.setArraySize = function (array, size, value) { + if (value === void 0) { value = 0; } + var oldSize = array.length; + if (oldSize == size) + return array; + array.length = size; + if (oldSize < size) { + for (var i = oldSize; i < size; i++) + array[i] = value; + } + return array; + }; + Utils.ensureArrayCapacity = function (array, size, value) { + if (value === void 0) { value = 0; } + if (array.length >= size) + return array; + return Utils.setArraySize(array, size, value); + }; + Utils.newArray = function (size, defaultValue) { + var array = new Array(size); + for (var i = 0; i < size; i++) + array[i] = defaultValue; + return array; + }; + Utils.newFloatArray = function (size) { + if (Utils.SUPPORTS_TYPED_ARRAYS) { + return new Float32Array(size); + } + else { + var array = new Array(size); + for (var i = 0; i < array.length; i++) + array[i] = 0; + return array; + } + }; + Utils.newShortArray = function (size) { + if (Utils.SUPPORTS_TYPED_ARRAYS) { + return new Int16Array(size); + } + else { + var array = new Array(size); + for (var i = 0; i < array.length; i++) + array[i] = 0; + return array; + } + }; + Utils.toFloatArray = function (array) { + return Utils.SUPPORTS_TYPED_ARRAYS ? new Float32Array(array) : array; + }; + Utils.toSinglePrecision = function (value) { + return Utils.SUPPORTS_TYPED_ARRAYS ? Math.fround(value) : value; + }; + return Utils; + }()); + Utils.SUPPORTS_TYPED_ARRAYS = typeof (Float32Array) !== "undefined"; + spine.Utils = Utils; + var DebugUtils = (function () { + function DebugUtils() { + } + DebugUtils.logBones = function (skeleton) { + for (var i = 0; i < skeleton.bones.length; i++) { + var bone = skeleton.bones[i]; + console.log(bone.data.name + ", " + bone.a + ", " + bone.b + ", " + bone.c + ", " + bone.d + ", " + bone.worldX + ", " + bone.worldY); + } + }; + return DebugUtils; + }()); + spine.DebugUtils = DebugUtils; + var Pool = (function () { + function Pool(instantiator) { + this.items = new Array(); + this.instantiator = instantiator; + } + Pool.prototype.obtain = function () { + return this.items.length > 0 ? this.items.pop() : this.instantiator(); + }; + Pool.prototype.free = function (item) { + if (item.reset) + item.reset(); + this.items.push(item); + }; + Pool.prototype.freeAll = function (items) { + for (var i = 0; i < items.length; i++) { + if (items[i].reset) + items[i].reset(); + this.items[i] = items[i]; + } + }; + Pool.prototype.clear = function () { + this.items.length = 0; + }; + return Pool; + }()); + spine.Pool = Pool; + var Vector2 = (function () { + function Vector2(x, y) { + if (x === void 0) { x = 0; } + if (y === void 0) { y = 0; } + this.x = x; + this.y = y; + } + Vector2.prototype.set = function (x, y) { + this.x = x; + this.y = y; + return this; + }; + Vector2.prototype.length = function () { + var x = this.x; + var y = this.y; + return Math.sqrt(x * x + y * y); + }; + Vector2.prototype.normalize = function () { + var len = this.length(); + if (len != 0) { + this.x /= len; + this.y /= len; + } + return this; + }; + return Vector2; + }()); + spine.Vector2 = Vector2; + var TimeKeeper = (function () { + function TimeKeeper() { + this.maxDelta = 0.064; + this.framesPerSecond = 0; + this.delta = 0; + this.totalTime = 0; + this.lastTime = Date.now() / 1000; + this.frameCount = 0; + this.frameTime = 0; + } + TimeKeeper.prototype.update = function () { + var now = Date.now() / 1000; + this.delta = now - this.lastTime; + this.frameTime += this.delta; + this.totalTime += this.delta; + if (this.delta > this.maxDelta) + this.delta = this.maxDelta; + this.lastTime = now; + this.frameCount++; + if (this.frameTime > 1) { + this.framesPerSecond = this.frameCount / this.frameTime; + this.frameTime = 0; + this.frameCount = 0; + } + }; + return TimeKeeper; + }()); + spine.TimeKeeper = TimeKeeper; + var WindowedMean = (function () { + function WindowedMean(windowSize) { + if (windowSize === void 0) { windowSize = 32; } + this.addedValues = 0; + this.lastValue = 0; + this.mean = 0; + this.dirty = true; + this.values = new Array(windowSize); + } + WindowedMean.prototype.hasEnoughData = function () { + return this.addedValues >= this.values.length; + }; + WindowedMean.prototype.addValue = function (value) { + if (this.addedValues < this.values.length) + this.addedValues++; + this.values[this.lastValue++] = value; + if (this.lastValue > this.values.length - 1) + this.lastValue = 0; + this.dirty = true; + }; + WindowedMean.prototype.getMean = function () { + if (this.hasEnoughData()) { + if (this.dirty) { + var mean = 0; + for (var i = 0; i < this.values.length; i++) { + mean += this.values[i]; + } + this.mean = mean / this.values.length; + this.dirty = false; + } + return this.mean; + } + else { + return 0; + } + }; + return WindowedMean; + }()); + spine.WindowedMean = WindowedMean; +})(spine || (spine = {})); +var spine; +(function (spine) { + var Attachment = (function () { + function Attachment(name) { + if (name == null) + throw new Error("name cannot be null."); + this.name = name; + } + return Attachment; + }()); + spine.Attachment = Attachment; + var VertexAttachment = (function (_super) { + __extends(VertexAttachment, _super); + function VertexAttachment(name) { + var _this = _super.call(this, name) || this; + _this.id = (VertexAttachment.nextID++ & 65535) << 11; + _this.worldVerticesLength = 0; + return _this; + } + VertexAttachment.prototype.computeWorldVertices = function (slot, start, count, worldVertices, offset, stride) { + count = offset + (count >> 1) * stride; + var skeleton = slot.bone.skeleton; + var deformArray = slot.attachmentVertices; + var vertices = this.vertices; + var bones = this.bones; + if (bones == null) { + if (deformArray.length > 0) + vertices = deformArray; + var bone = slot.bone; + var x = bone.worldX; + var y = bone.worldY; + var a = bone.a, b = bone.b, c = bone.c, d = bone.d; + for (var v_1 = start, w = offset; w < count; v_1 += 2, w += stride) { + var vx = vertices[v_1], vy = vertices[v_1 + 1]; + worldVertices[w] = vx * a + vy * b + x; + worldVertices[w + 1] = vx * c + vy * d + y; + } + return; + } + var v = 0, skip = 0; + for (var i = 0; i < start; i += 2) { + var n = bones[v]; + v += n + 1; + skip += n; + } + var skeletonBones = skeleton.bones; + if (deformArray.length == 0) { + for (var w = offset, b = skip * 3; w < count; w += stride) { + var wx = 0, wy = 0; + var n = bones[v++]; + n += v; + for (; v < n; v++, b += 3) { + var bone = skeletonBones[bones[v]]; + var vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2]; + wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight; + wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight; + } + worldVertices[w] = wx; + worldVertices[w + 1] = wy; + } + } + else { + var deform = deformArray; + for (var w = offset, b = skip * 3, f = skip << 1; w < count; w += stride) { + var wx = 0, wy = 0; + var n = bones[v++]; + n += v; + for (; v < n; v++, b += 3, f += 2) { + var bone = skeletonBones[bones[v]]; + var vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1], weight = vertices[b + 2]; + wx += (vx * bone.a + vy * bone.b + bone.worldX) * weight; + wy += (vx * bone.c + vy * bone.d + bone.worldY) * weight; + } + worldVertices[w] = wx; + worldVertices[w + 1] = wy; + } + } + }; + VertexAttachment.prototype.applyDeform = function (sourceAttachment) { + return this == sourceAttachment; + }; + return VertexAttachment; + }(Attachment)); + VertexAttachment.nextID = 0; + spine.VertexAttachment = VertexAttachment; +})(spine || (spine = {})); +var spine; +(function (spine) { + var AttachmentType; + (function (AttachmentType) { + AttachmentType[AttachmentType["Region"] = 0] = "Region"; + AttachmentType[AttachmentType["BoundingBox"] = 1] = "BoundingBox"; + AttachmentType[AttachmentType["Mesh"] = 2] = "Mesh"; + AttachmentType[AttachmentType["LinkedMesh"] = 3] = "LinkedMesh"; + AttachmentType[AttachmentType["Path"] = 4] = "Path"; + AttachmentType[AttachmentType["Point"] = 5] = "Point"; + })(AttachmentType = spine.AttachmentType || (spine.AttachmentType = {})); +})(spine || (spine = {})); +var spine; +(function (spine) { + var BoundingBoxAttachment = (function (_super) { + __extends(BoundingBoxAttachment, _super); + function BoundingBoxAttachment(name) { + var _this = _super.call(this, name) || this; + _this.color = new spine.Color(1, 1, 1, 1); + return _this; + } + return BoundingBoxAttachment; + }(spine.VertexAttachment)); + spine.BoundingBoxAttachment = BoundingBoxAttachment; +})(spine || (spine = {})); +var spine; +(function (spine) { + var ClippingAttachment = (function (_super) { + __extends(ClippingAttachment, _super); + function ClippingAttachment(name) { + var _this = _super.call(this, name) || this; + _this.color = new spine.Color(0.2275, 0.2275, 0.8078, 1); + return _this; + } + return ClippingAttachment; + }(spine.VertexAttachment)); + spine.ClippingAttachment = ClippingAttachment; +})(spine || (spine = {})); +var spine; +(function (spine) { + var MeshAttachment = (function (_super) { + __extends(MeshAttachment, _super); + function MeshAttachment(name) { + var _this = _super.call(this, name) || this; + _this.color = new spine.Color(1, 1, 1, 1); + _this.inheritDeform = false; + _this.tempColor = new spine.Color(0, 0, 0, 0); + return _this; + } + MeshAttachment.prototype.updateUVs = function () { + var u = 0, v = 0, width = 0, height = 0; + if (this.region == null) { + u = v = 0; + width = height = 1; + } + else { + u = this.region.u; + v = this.region.v; + width = this.region.u2 - u; + height = this.region.v2 - v; + } + var regionUVs = this.regionUVs; + if (this.uvs == null || this.uvs.length != regionUVs.length) + this.uvs = spine.Utils.newFloatArray(regionUVs.length); + var uvs = this.uvs; + if (this.region.rotate) { + for (var i = 0, n = uvs.length; i < n; i += 2) { + uvs[i] = u + regionUVs[i + 1] * width; + uvs[i + 1] = v + height - regionUVs[i] * height; + } + } + else { + for (var i = 0, n = uvs.length; i < n; i += 2) { + uvs[i] = u + regionUVs[i] * width; + uvs[i + 1] = v + regionUVs[i + 1] * height; + } + } + }; + MeshAttachment.prototype.applyDeform = function (sourceAttachment) { + return this == sourceAttachment || (this.inheritDeform && this.parentMesh == sourceAttachment); + }; + MeshAttachment.prototype.getParentMesh = function () { + return this.parentMesh; + }; + MeshAttachment.prototype.setParentMesh = function (parentMesh) { + this.parentMesh = parentMesh; + if (parentMesh != null) { + this.bones = parentMesh.bones; + this.vertices = parentMesh.vertices; + this.worldVerticesLength = parentMesh.worldVerticesLength; + this.regionUVs = parentMesh.regionUVs; + this.triangles = parentMesh.triangles; + this.hullLength = parentMesh.hullLength; + this.worldVerticesLength = parentMesh.worldVerticesLength; + } + }; + return MeshAttachment; + }(spine.VertexAttachment)); + spine.MeshAttachment = MeshAttachment; +})(spine || (spine = {})); +var spine; +(function (spine) { + var PathAttachment = (function (_super) { + __extends(PathAttachment, _super); + function PathAttachment(name) { + var _this = _super.call(this, name) || this; + _this.closed = false; + _this.constantSpeed = false; + _this.color = new spine.Color(1, 1, 1, 1); + return _this; + } + return PathAttachment; + }(spine.VertexAttachment)); + spine.PathAttachment = PathAttachment; +})(spine || (spine = {})); +var spine; +(function (spine) { + var PointAttachment = (function (_super) { + __extends(PointAttachment, _super); + function PointAttachment(name) { + var _this = _super.call(this, name) || this; + _this.color = new spine.Color(0.38, 0.94, 0, 1); + return _this; + } + PointAttachment.prototype.computeWorldPosition = function (bone, point) { + point.x = this.x * bone.a + this.y * bone.b + bone.worldX; + point.y = this.x * bone.c + this.y * bone.d + bone.worldY; + return point; + }; + PointAttachment.prototype.computeWorldRotation = function (bone) { + var cos = spine.MathUtils.cosDeg(this.rotation), sin = spine.MathUtils.sinDeg(this.rotation); + var x = cos * bone.a + sin * bone.b; + var y = cos * bone.c + sin * bone.d; + return Math.atan2(y, x) * spine.MathUtils.radDeg; + }; + return PointAttachment; + }(spine.VertexAttachment)); + spine.PointAttachment = PointAttachment; +})(spine || (spine = {})); +var spine; +(function (spine) { + var RegionAttachment = (function (_super) { + __extends(RegionAttachment, _super); + function RegionAttachment(name) { + var _this = _super.call(this, name) || this; + _this.x = 0; + _this.y = 0; + _this.scaleX = 1; + _this.scaleY = 1; + _this.rotation = 0; + _this.width = 0; + _this.height = 0; + _this.color = new spine.Color(1, 1, 1, 1); + _this.offset = spine.Utils.newFloatArray(8); + _this.uvs = spine.Utils.newFloatArray(8); + _this.tempColor = new spine.Color(1, 1, 1, 1); + return _this; + } + RegionAttachment.prototype.updateOffset = function () { + var regionScaleX = this.width / this.region.originalWidth * this.scaleX; + var regionScaleY = this.height / this.region.originalHeight * this.scaleY; + var localX = -this.width / 2 * this.scaleX + this.region.offsetX * regionScaleX; + var localY = -this.height / 2 * this.scaleY + this.region.offsetY * regionScaleY; + var localX2 = localX + this.region.width * regionScaleX; + var localY2 = localY + this.region.height * regionScaleY; + var radians = this.rotation * Math.PI / 180; + var cos = Math.cos(radians); + var sin = Math.sin(radians); + var localXCos = localX * cos + this.x; + var localXSin = localX * sin; + var localYCos = localY * cos + this.y; + var localYSin = localY * sin; + var localX2Cos = localX2 * cos + this.x; + var localX2Sin = localX2 * sin; + var localY2Cos = localY2 * cos + this.y; + var localY2Sin = localY2 * sin; + var offset = this.offset; + offset[RegionAttachment.OX1] = localXCos - localYSin; + offset[RegionAttachment.OY1] = localYCos + localXSin; + offset[RegionAttachment.OX2] = localXCos - localY2Sin; + offset[RegionAttachment.OY2] = localY2Cos + localXSin; + offset[RegionAttachment.OX3] = localX2Cos - localY2Sin; + offset[RegionAttachment.OY3] = localY2Cos + localX2Sin; + offset[RegionAttachment.OX4] = localX2Cos - localYSin; + offset[RegionAttachment.OY4] = localYCos + localX2Sin; + }; + RegionAttachment.prototype.setRegion = function (region) { + this.region = region; + var uvs = this.uvs; + if (region.rotate) { + uvs[2] = region.u; + uvs[3] = region.v2; + uvs[4] = region.u; + uvs[5] = region.v; + uvs[6] = region.u2; + uvs[7] = region.v; + uvs[0] = region.u2; + uvs[1] = region.v2; + } + else { + uvs[0] = region.u; + uvs[1] = region.v2; + uvs[2] = region.u; + uvs[3] = region.v; + uvs[4] = region.u2; + uvs[5] = region.v; + uvs[6] = region.u2; + uvs[7] = region.v2; + } + }; + RegionAttachment.prototype.computeWorldVertices = function (bone, worldVertices, offset, stride) { + var vertexOffset = this.offset; + var x = bone.worldX, y = bone.worldY; + var a = bone.a, b = bone.b, c = bone.c, d = bone.d; + var offsetX = 0, offsetY = 0; + offsetX = vertexOffset[RegionAttachment.OX1]; + offsetY = vertexOffset[RegionAttachment.OY1]; + worldVertices[offset] = offsetX * a + offsetY * b + x; + worldVertices[offset + 1] = offsetX * c + offsetY * d + y; + offset += stride; + offsetX = vertexOffset[RegionAttachment.OX2]; + offsetY = vertexOffset[RegionAttachment.OY2]; + worldVertices[offset] = offsetX * a + offsetY * b + x; + worldVertices[offset + 1] = offsetX * c + offsetY * d + y; + offset += stride; + offsetX = vertexOffset[RegionAttachment.OX3]; + offsetY = vertexOffset[RegionAttachment.OY3]; + worldVertices[offset] = offsetX * a + offsetY * b + x; + worldVertices[offset + 1] = offsetX * c + offsetY * d + y; + offset += stride; + offsetX = vertexOffset[RegionAttachment.OX4]; + offsetY = vertexOffset[RegionAttachment.OY4]; + worldVertices[offset] = offsetX * a + offsetY * b + x; + worldVertices[offset + 1] = offsetX * c + offsetY * d + y; + }; + return RegionAttachment; + }(spine.Attachment)); + RegionAttachment.OX1 = 0; + RegionAttachment.OY1 = 1; + RegionAttachment.OX2 = 2; + RegionAttachment.OY2 = 3; + RegionAttachment.OX3 = 4; + RegionAttachment.OY3 = 5; + RegionAttachment.OX4 = 6; + RegionAttachment.OY4 = 7; + RegionAttachment.X1 = 0; + RegionAttachment.Y1 = 1; + RegionAttachment.C1R = 2; + RegionAttachment.C1G = 3; + RegionAttachment.C1B = 4; + RegionAttachment.C1A = 5; + RegionAttachment.U1 = 6; + RegionAttachment.V1 = 7; + RegionAttachment.X2 = 8; + RegionAttachment.Y2 = 9; + RegionAttachment.C2R = 10; + RegionAttachment.C2G = 11; + RegionAttachment.C2B = 12; + RegionAttachment.C2A = 13; + RegionAttachment.U2 = 14; + RegionAttachment.V2 = 15; + RegionAttachment.X3 = 16; + RegionAttachment.Y3 = 17; + RegionAttachment.C3R = 18; + RegionAttachment.C3G = 19; + RegionAttachment.C3B = 20; + RegionAttachment.C3A = 21; + RegionAttachment.U3 = 22; + RegionAttachment.V3 = 23; + RegionAttachment.X4 = 24; + RegionAttachment.Y4 = 25; + RegionAttachment.C4R = 26; + RegionAttachment.C4G = 27; + RegionAttachment.C4B = 28; + RegionAttachment.C4A = 29; + RegionAttachment.U4 = 30; + RegionAttachment.V4 = 31; + spine.RegionAttachment = RegionAttachment; +})(spine || (spine = {})); +var spine; +(function (spine) { + var JitterEffect = (function () { + function JitterEffect(jitterX, jitterY) { + this.jitterX = 0; + this.jitterY = 0; + this.jitterX = jitterX; + this.jitterY = jitterY; + } + JitterEffect.prototype.begin = function (skeleton) { + }; + JitterEffect.prototype.transform = function (position, uv, light, dark) { + position.x += spine.MathUtils.randomTriangular(-this.jitterX, this.jitterY); + position.y += spine.MathUtils.randomTriangular(-this.jitterX, this.jitterY); + }; + JitterEffect.prototype.end = function () { + }; + return JitterEffect; + }()); + spine.JitterEffect = JitterEffect; +})(spine || (spine = {})); +var spine; +(function (spine) { + var SwirlEffect = (function () { + function SwirlEffect(radius) { + this.centerX = 0; + this.centerY = 0; + this.radius = 0; + this.angle = 0; + this.worldX = 0; + this.worldY = 0; + this.radius = radius; + } + SwirlEffect.prototype.begin = function (skeleton) { + this.worldX = skeleton.x + this.centerX; + this.worldY = skeleton.y + this.centerY; + }; + SwirlEffect.prototype.transform = function (position, uv, light, dark) { + var radAngle = this.angle * spine.MathUtils.degreesToRadians; + var x = position.x - this.worldX; + var y = position.y - this.worldY; + var dist = Math.sqrt(x * x + y * y); + if (dist < this.radius) { + var theta = SwirlEffect.interpolation.apply(0, radAngle, (this.radius - dist) / this.radius); + var cos = Math.cos(theta); + var sin = Math.sin(theta); + position.x = cos * x - sin * y + this.worldX; + position.y = sin * x + cos * y + this.worldY; + } + }; + SwirlEffect.prototype.end = function () { + }; + return SwirlEffect; + }()); + SwirlEffect.interpolation = new spine.PowOut(2); + spine.SwirlEffect = SwirlEffect; +})(spine || (spine = {})); + +var sp = sp || {}; +sp.spine = spine; diff --git a/frameworks/cocos2d-html5/external/box2d/box2d.js b/frameworks/cocos2d-html5/external/box2d/box2d.js new file mode 100644 index 0000000..2f9447d --- /dev/null +++ b/frameworks/cocos2d-html5/external/box2d/box2d.js @@ -0,0 +1,10882 @@ +/* + * Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * + * Sean Lin 2012-5-8, + * + * The library is box2dweb, http://code.google.com/p/box2dweb/ + * + * It is a port of Box2DFlash 2.1a to JavaScript. + * You can read the documentation for Box2dFlash, since nearly everything is + * organized the same way. http://www.box2dflash.org/docs/2.1a/reference/ + * + */ + +var Box2D = {}; + +(function (a2j, undefined) { + + if(!(Object.defineProperty instanceof Function) + && Object.prototype.__defineGetter__ instanceof Function + && Object.prototype.__defineSetter__ instanceof Function) + { + Object.defineProperty = function(obj, p, cfg) { + if(cfg.get instanceof Function) + obj.__defineGetter__(p, cfg.get); + if(cfg.set instanceof Function) + obj.__defineSetter__(p, cfg.set); + } + } + + function emptyFn() {}; + a2j.inherit = function(cls, base) { + var tmpCtr = cls; + emptyFn.prototype = base.prototype; + cls.prototype = new emptyFn; + cls.prototype.constructor = tmpCtr; + }; + + a2j.generateCallback = function generateCallback(context, cb) { + return function () { + cb.apply(context, arguments); + }; + }; + + a2j.NVector = function NVector(length) { + if (length === undefined) length = 0; + var tmp = new Array(length || 0); + for (var i = 0; i < length; ++i) + tmp[i] = 0; + return tmp; + }; + + a2j.is = function is(o1, o2) { + if (o1 === null) return false; + if ((o2 instanceof Function) && (o1 instanceof o2)) return true; + if ((o1.constructor.__implements != undefined) && (o1.constructor.__implements[o2])) return true; + return false; + }; + + a2j.parseUInt = function(v) { + return Math.abs(parseInt(v)); + } + +})(Box2D); + +//#TODO remove assignments from global namespace +var Vector = Array; +var Vector_a2j_Number = Box2D.NVector; +//package structure +if (typeof(Box2D) === "undefined") Box2D = {}; +if (typeof(Box2D.Collision) === "undefined") Box2D.Collision = {}; +if (typeof(Box2D.Collision.Shapes) === "undefined") Box2D.Collision.Shapes = {}; +if (typeof(Box2D.Common) === "undefined") Box2D.Common = {}; +if (typeof(Box2D.Common.Math) === "undefined") Box2D.Common.Math = {}; +if (typeof(Box2D.Dynamics) === "undefined") Box2D.Dynamics = {}; +if (typeof(Box2D.Dynamics.Contacts) === "undefined") Box2D.Dynamics.Contacts = {}; +if (typeof(Box2D.Dynamics.Controllers) === "undefined") Box2D.Dynamics.Controllers = {}; +if (typeof(Box2D.Dynamics.Joints) === "undefined") Box2D.Dynamics.Joints = {}; +//pre-definitions +(function () { + Box2D.Collision.IBroadPhase = 'Box2D.Collision.IBroadPhase'; + + function b2AABB() { + b2AABB.b2AABB.apply(this, arguments); + }; + Box2D.Collision.b2AABB = b2AABB; + + function b2Bound() { + b2Bound.b2Bound.apply(this, arguments); + }; + Box2D.Collision.b2Bound = b2Bound; + + function b2BoundValues() { + b2BoundValues.b2BoundValues.apply(this, arguments); + if (this.constructor === b2BoundValues) this.b2BoundValues.apply(this, arguments); + }; + Box2D.Collision.b2BoundValues = b2BoundValues; + + function b2Collision() { + b2Collision.b2Collision.apply(this, arguments); + }; + Box2D.Collision.b2Collision = b2Collision; + + function b2ContactID() { + b2ContactID.b2ContactID.apply(this, arguments); + if (this.constructor === b2ContactID) this.b2ContactID.apply(this, arguments); + }; + Box2D.Collision.b2ContactID = b2ContactID; + + function b2ContactPoint() { + b2ContactPoint.b2ContactPoint.apply(this, arguments); + }; + Box2D.Collision.b2ContactPoint = b2ContactPoint; + + function b2Distance() { + b2Distance.b2Distance.apply(this, arguments); + }; + Box2D.Collision.b2Distance = b2Distance; + + function b2DistanceInput() { + b2DistanceInput.b2DistanceInput.apply(this, arguments); + }; + Box2D.Collision.b2DistanceInput = b2DistanceInput; + + function b2DistanceOutput() { + b2DistanceOutput.b2DistanceOutput.apply(this, arguments); + }; + Box2D.Collision.b2DistanceOutput = b2DistanceOutput; + + function b2DistanceProxy() { + b2DistanceProxy.b2DistanceProxy.apply(this, arguments); + }; + Box2D.Collision.b2DistanceProxy = b2DistanceProxy; + + function b2DynamicTree() { + b2DynamicTree.b2DynamicTree.apply(this, arguments); + if (this.constructor === b2DynamicTree) this.b2DynamicTree.apply(this, arguments); + }; + Box2D.Collision.b2DynamicTree = b2DynamicTree; + + function b2DynamicTreeBroadPhase() { + b2DynamicTreeBroadPhase.b2DynamicTreeBroadPhase.apply(this, arguments); + }; + Box2D.Collision.b2DynamicTreeBroadPhase = b2DynamicTreeBroadPhase; + + function b2DynamicTreeNode() { + b2DynamicTreeNode.b2DynamicTreeNode.apply(this, arguments); + }; + Box2D.Collision.b2DynamicTreeNode = b2DynamicTreeNode; + + function b2DynamicTreePair() { + b2DynamicTreePair.b2DynamicTreePair.apply(this, arguments); + }; + Box2D.Collision.b2DynamicTreePair = b2DynamicTreePair; + + function b2Manifold() { + b2Manifold.b2Manifold.apply(this, arguments); + if (this.constructor === b2Manifold) this.b2Manifold.apply(this, arguments); + }; + Box2D.Collision.b2Manifold = b2Manifold; + + function b2ManifoldPoint() { + b2ManifoldPoint.b2ManifoldPoint.apply(this, arguments); + if (this.constructor === b2ManifoldPoint) this.b2ManifoldPoint.apply(this, arguments); + }; + Box2D.Collision.b2ManifoldPoint = b2ManifoldPoint; + + function b2Point() { + b2Point.b2Point.apply(this, arguments); + }; + Box2D.Collision.b2Point = b2Point; + + function b2RayCastInput() { + b2RayCastInput.b2RayCastInput.apply(this, arguments); + if (this.constructor === b2RayCastInput) this.b2RayCastInput.apply(this, arguments); + }; + Box2D.Collision.b2RayCastInput = b2RayCastInput; + + function b2RayCastOutput() { + b2RayCastOutput.b2RayCastOutput.apply(this, arguments); + }; + Box2D.Collision.b2RayCastOutput = b2RayCastOutput; + + function b2Segment() { + b2Segment.b2Segment.apply(this, arguments); + }; + Box2D.Collision.b2Segment = b2Segment; + + function b2SeparationFunction() { + b2SeparationFunction.b2SeparationFunction.apply(this, arguments); + }; + Box2D.Collision.b2SeparationFunction = b2SeparationFunction; + + function b2Simplex() { + b2Simplex.b2Simplex.apply(this, arguments); + if (this.constructor === b2Simplex) this.b2Simplex.apply(this, arguments); + }; + Box2D.Collision.b2Simplex = b2Simplex; + + function b2SimplexCache() { + b2SimplexCache.b2SimplexCache.apply(this, arguments); + }; + Box2D.Collision.b2SimplexCache = b2SimplexCache; + + function b2SimplexVertex() { + b2SimplexVertex.b2SimplexVertex.apply(this, arguments); + }; + Box2D.Collision.b2SimplexVertex = b2SimplexVertex; + + function b2TimeOfImpact() { + b2TimeOfImpact.b2TimeOfImpact.apply(this, arguments); + }; + Box2D.Collision.b2TimeOfImpact = b2TimeOfImpact; + + function b2TOIInput() { + b2TOIInput.b2TOIInput.apply(this, arguments); + }; + Box2D.Collision.b2TOIInput = b2TOIInput; + + function b2WorldManifold() { + b2WorldManifold.b2WorldManifold.apply(this, arguments); + if (this.constructor === b2WorldManifold) this.b2WorldManifold.apply(this, arguments); + }; + Box2D.Collision.b2WorldManifold = b2WorldManifold; + + function ClipVertex() { + ClipVertex.ClipVertex.apply(this, arguments); + }; + Box2D.Collision.ClipVertex = ClipVertex; + + function Features() { + Features.Features.apply(this, arguments); + }; + Box2D.Collision.Features = Features; + + function b2CircleShape() { + b2CircleShape.b2CircleShape.apply(this, arguments); + if (this.constructor === b2CircleShape) this.b2CircleShape.apply(this, arguments); + }; + Box2D.Collision.Shapes.b2CircleShape = b2CircleShape; + + function b2EdgeChainDef() { + b2EdgeChainDef.b2EdgeChainDef.apply(this, arguments); + if (this.constructor === b2EdgeChainDef) this.b2EdgeChainDef.apply(this, arguments); + }; + Box2D.Collision.Shapes.b2EdgeChainDef = b2EdgeChainDef; + + function b2EdgeShape() { + b2EdgeShape.b2EdgeShape.apply(this, arguments); + if (this.constructor === b2EdgeShape) this.b2EdgeShape.apply(this, arguments); + }; + Box2D.Collision.Shapes.b2EdgeShape = b2EdgeShape; + + function b2MassData() { + b2MassData.b2MassData.apply(this, arguments); + }; + Box2D.Collision.Shapes.b2MassData = b2MassData; + + function b2PolygonShape() { + b2PolygonShape.b2PolygonShape.apply(this, arguments); + if (this.constructor === b2PolygonShape) this.b2PolygonShape.apply(this, arguments); + }; + Box2D.Collision.Shapes.b2PolygonShape = b2PolygonShape; + + function b2Shape() { + b2Shape.b2Shape.apply(this, arguments); + if (this.constructor === b2Shape) this.b2Shape.apply(this, arguments); + }; + Box2D.Collision.Shapes.b2Shape = b2Shape; + Box2D.Common.b2internal = 'Box2D.Common.b2internal'; + + function b2Color() { + b2Color.b2Color.apply(this, arguments); + if (this.constructor === b2Color) this.b2Color.apply(this, arguments); + }; + Box2D.Common.b2Color = b2Color; + + function b2Settings() { + b2Settings.b2Settings.apply(this, arguments); + }; + Box2D.Common.b2Settings = b2Settings; + + function b2Mat22() { + b2Mat22.b2Mat22.apply(this, arguments); + if (this.constructor === b2Mat22) this.b2Mat22.apply(this, arguments); + }; + Box2D.Common.Math.b2Mat22 = b2Mat22; + + function b2Mat33() { + b2Mat33.b2Mat33.apply(this, arguments); + if (this.constructor === b2Mat33) this.b2Mat33.apply(this, arguments); + }; + Box2D.Common.Math.b2Mat33 = b2Mat33; + + function b2Math() { + b2Math.b2Math.apply(this, arguments); + }; + Box2D.Common.Math.b2Math = b2Math; + + function b2Sweep() { + b2Sweep.b2Sweep.apply(this, arguments); + }; + Box2D.Common.Math.b2Sweep = b2Sweep; + + function b2Transform() { + b2Transform.b2Transform.apply(this, arguments); + if (this.constructor === b2Transform) this.b2Transform.apply(this, arguments); + }; + Box2D.Common.Math.b2Transform = b2Transform; + + function b2Vec2() { + b2Vec2.b2Vec2.apply(this, arguments); + if (this.constructor === b2Vec2) this.b2Vec2.apply(this, arguments); + }; + Box2D.Common.Math.b2Vec2 = b2Vec2; + + function b2Vec3() { + b2Vec3.b2Vec3.apply(this, arguments); + if (this.constructor === b2Vec3) this.b2Vec3.apply(this, arguments); + }; + Box2D.Common.Math.b2Vec3 = b2Vec3; + + function b2Body() { + b2Body.b2Body.apply(this, arguments); + if (this.constructor === b2Body) this.b2Body.apply(this, arguments); + }; + Box2D.Dynamics.b2Body = b2Body; + + function b2BodyDef() { + b2BodyDef.b2BodyDef.apply(this, arguments); + if (this.constructor === b2BodyDef) this.b2BodyDef.apply(this, arguments); + }; + Box2D.Dynamics.b2BodyDef = b2BodyDef; + + function b2ContactFilter() { + b2ContactFilter.b2ContactFilter.apply(this, arguments); + }; + Box2D.Dynamics.b2ContactFilter = b2ContactFilter; + + function b2ContactImpulse() { + b2ContactImpulse.b2ContactImpulse.apply(this, arguments); + }; + Box2D.Dynamics.b2ContactImpulse = b2ContactImpulse; + + function b2ContactListener() { + b2ContactListener.b2ContactListener.apply(this, arguments); + }; + Box2D.Dynamics.b2ContactListener = b2ContactListener; + + function b2ContactManager() { + b2ContactManager.b2ContactManager.apply(this, arguments); + if (this.constructor === b2ContactManager) this.b2ContactManager.apply(this, arguments); + }; + Box2D.Dynamics.b2ContactManager = b2ContactManager; + + function b2DebugDraw() { + b2DebugDraw.b2DebugDraw.apply(this, arguments); + if (this.constructor === b2DebugDraw) this.b2DebugDraw.apply(this, arguments); + }; + Box2D.Dynamics.b2DebugDraw = b2DebugDraw; + + function b2DestructionListener() { + b2DestructionListener.b2DestructionListener.apply(this, arguments); + }; + Box2D.Dynamics.b2DestructionListener = b2DestructionListener; + + function b2FilterData() { + b2FilterData.b2FilterData.apply(this, arguments); + }; + Box2D.Dynamics.b2FilterData = b2FilterData; + + function b2Fixture() { + b2Fixture.b2Fixture.apply(this, arguments); + if (this.constructor === b2Fixture) this.b2Fixture.apply(this, arguments); + }; + Box2D.Dynamics.b2Fixture = b2Fixture; + + function b2FixtureDef() { + b2FixtureDef.b2FixtureDef.apply(this, arguments); + if (this.constructor === b2FixtureDef) this.b2FixtureDef.apply(this, arguments); + }; + Box2D.Dynamics.b2FixtureDef = b2FixtureDef; + + function b2Island() { + b2Island.b2Island.apply(this, arguments); + if (this.constructor === b2Island) this.b2Island.apply(this, arguments); + }; + Box2D.Dynamics.b2Island = b2Island; + + function b2TimeStep() { + b2TimeStep.b2TimeStep.apply(this, arguments); + }; + Box2D.Dynamics.b2TimeStep = b2TimeStep; + + function b2World() { + b2World.b2World.apply(this, arguments); + if (this.constructor === b2World) this.b2World.apply(this, arguments); + }; + Box2D.Dynamics.b2World = b2World; + + function b2CircleContact() { + b2CircleContact.b2CircleContact.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2CircleContact = b2CircleContact; + + function b2Contact() { + b2Contact.b2Contact.apply(this, arguments); + if (this.constructor === b2Contact) this.b2Contact.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2Contact = b2Contact; + + function b2ContactConstraint() { + b2ContactConstraint.b2ContactConstraint.apply(this, arguments); + if (this.constructor === b2ContactConstraint) this.b2ContactConstraint.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2ContactConstraint = b2ContactConstraint; + + function b2ContactConstraintPoint() { + b2ContactConstraintPoint.b2ContactConstraintPoint.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2ContactConstraintPoint = b2ContactConstraintPoint; + + function b2ContactEdge() { + b2ContactEdge.b2ContactEdge.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2ContactEdge = b2ContactEdge; + + function b2ContactFactory() { + b2ContactFactory.b2ContactFactory.apply(this, arguments); + if (this.constructor === b2ContactFactory) this.b2ContactFactory.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2ContactFactory = b2ContactFactory; + + function b2ContactRegister() { + b2ContactRegister.b2ContactRegister.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2ContactRegister = b2ContactRegister; + + function b2ContactResult() { + b2ContactResult.b2ContactResult.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2ContactResult = b2ContactResult; + + function b2ContactSolver() { + b2ContactSolver.b2ContactSolver.apply(this, arguments); + if (this.constructor === b2ContactSolver) this.b2ContactSolver.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2ContactSolver = b2ContactSolver; + + function b2EdgeAndCircleContact() { + b2EdgeAndCircleContact.b2EdgeAndCircleContact.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2EdgeAndCircleContact = b2EdgeAndCircleContact; + + function b2NullContact() { + b2NullContact.b2NullContact.apply(this, arguments); + if (this.constructor === b2NullContact) this.b2NullContact.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2NullContact = b2NullContact; + + function b2PolyAndCircleContact() { + b2PolyAndCircleContact.b2PolyAndCircleContact.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2PolyAndCircleContact = b2PolyAndCircleContact; + + function b2PolyAndEdgeContact() { + b2PolyAndEdgeContact.b2PolyAndEdgeContact.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2PolyAndEdgeContact = b2PolyAndEdgeContact; + + function b2PolygonContact() { + b2PolygonContact.b2PolygonContact.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2PolygonContact = b2PolygonContact; + + function b2PositionSolverManifold() { + b2PositionSolverManifold.b2PositionSolverManifold.apply(this, arguments); + if (this.constructor === b2PositionSolverManifold) this.b2PositionSolverManifold.apply(this, arguments); + }; + Box2D.Dynamics.Contacts.b2PositionSolverManifold = b2PositionSolverManifold; + + function b2BuoyancyController() { + b2BuoyancyController.b2BuoyancyController.apply(this, arguments); + }; + Box2D.Dynamics.Controllers.b2BuoyancyController = b2BuoyancyController; + + function b2ConstantAccelController() { + b2ConstantAccelController.b2ConstantAccelController.apply(this, arguments); + }; + Box2D.Dynamics.Controllers.b2ConstantAccelController = b2ConstantAccelController; + + function b2ConstantForceController() { + b2ConstantForceController.b2ConstantForceController.apply(this, arguments); + }; + Box2D.Dynamics.Controllers.b2ConstantForceController = b2ConstantForceController; + + function b2Controller() { + b2Controller.b2Controller.apply(this, arguments); + }; + Box2D.Dynamics.Controllers.b2Controller = b2Controller; + + function b2ControllerEdge() { + b2ControllerEdge.b2ControllerEdge.apply(this, arguments); + }; + Box2D.Dynamics.Controllers.b2ControllerEdge = b2ControllerEdge; + + function b2GravityController() { + b2GravityController.b2GravityController.apply(this, arguments); + }; + Box2D.Dynamics.Controllers.b2GravityController = b2GravityController; + + function b2TensorDampingController() { + b2TensorDampingController.b2TensorDampingController.apply(this, arguments); + }; + Box2D.Dynamics.Controllers.b2TensorDampingController = b2TensorDampingController; + + function b2DistanceJoint() { + b2DistanceJoint.b2DistanceJoint.apply(this, arguments); + if (this.constructor === b2DistanceJoint) this.b2DistanceJoint.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2DistanceJoint = b2DistanceJoint; + + function b2DistanceJointDef() { + b2DistanceJointDef.b2DistanceJointDef.apply(this, arguments); + if (this.constructor === b2DistanceJointDef) this.b2DistanceJointDef.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2DistanceJointDef = b2DistanceJointDef; + + function b2FrictionJoint() { + b2FrictionJoint.b2FrictionJoint.apply(this, arguments); + if (this.constructor === b2FrictionJoint) this.b2FrictionJoint.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2FrictionJoint = b2FrictionJoint; + + function b2FrictionJointDef() { + b2FrictionJointDef.b2FrictionJointDef.apply(this, arguments); + if (this.constructor === b2FrictionJointDef) this.b2FrictionJointDef.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2FrictionJointDef = b2FrictionJointDef; + + function b2GearJoint() { + b2GearJoint.b2GearJoint.apply(this, arguments); + if (this.constructor === b2GearJoint) this.b2GearJoint.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2GearJoint = b2GearJoint; + + function b2GearJointDef() { + b2GearJointDef.b2GearJointDef.apply(this, arguments); + if (this.constructor === b2GearJointDef) this.b2GearJointDef.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2GearJointDef = b2GearJointDef; + + function b2Jacobian() { + b2Jacobian.b2Jacobian.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2Jacobian = b2Jacobian; + + function b2Joint() { + b2Joint.b2Joint.apply(this, arguments); + if (this.constructor === b2Joint) this.b2Joint.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2Joint = b2Joint; + + function b2JointDef() { + b2JointDef.b2JointDef.apply(this, arguments); + if (this.constructor === b2JointDef) this.b2JointDef.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2JointDef = b2JointDef; + + function b2JointEdge() { + b2JointEdge.b2JointEdge.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2JointEdge = b2JointEdge; + + function b2LineJoint() { + b2LineJoint.b2LineJoint.apply(this, arguments); + if (this.constructor === b2LineJoint) this.b2LineJoint.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2LineJoint = b2LineJoint; + + function b2LineJointDef() { + b2LineJointDef.b2LineJointDef.apply(this, arguments); + if (this.constructor === b2LineJointDef) this.b2LineJointDef.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2LineJointDef = b2LineJointDef; + + function b2MouseJoint() { + b2MouseJoint.b2MouseJoint.apply(this, arguments); + if (this.constructor === b2MouseJoint) this.b2MouseJoint.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2MouseJoint = b2MouseJoint; + + function b2MouseJointDef() { + b2MouseJointDef.b2MouseJointDef.apply(this, arguments); + if (this.constructor === b2MouseJointDef) this.b2MouseJointDef.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2MouseJointDef = b2MouseJointDef; + + function b2PrismaticJoint() { + b2PrismaticJoint.b2PrismaticJoint.apply(this, arguments); + if (this.constructor === b2PrismaticJoint) this.b2PrismaticJoint.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2PrismaticJoint = b2PrismaticJoint; + + function b2PrismaticJointDef() { + b2PrismaticJointDef.b2PrismaticJointDef.apply(this, arguments); + if (this.constructor === b2PrismaticJointDef) this.b2PrismaticJointDef.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2PrismaticJointDef = b2PrismaticJointDef; + + function b2PulleyJoint() { + b2PulleyJoint.b2PulleyJoint.apply(this, arguments); + if (this.constructor === b2PulleyJoint) this.b2PulleyJoint.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2PulleyJoint = b2PulleyJoint; + + function b2PulleyJointDef() { + b2PulleyJointDef.b2PulleyJointDef.apply(this, arguments); + if (this.constructor === b2PulleyJointDef) this.b2PulleyJointDef.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2PulleyJointDef = b2PulleyJointDef; + + function b2RevoluteJoint() { + b2RevoluteJoint.b2RevoluteJoint.apply(this, arguments); + if (this.constructor === b2RevoluteJoint) this.b2RevoluteJoint.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2RevoluteJoint = b2RevoluteJoint; + + function b2RevoluteJointDef() { + b2RevoluteJointDef.b2RevoluteJointDef.apply(this, arguments); + if (this.constructor === b2RevoluteJointDef) this.b2RevoluteJointDef.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2RevoluteJointDef = b2RevoluteJointDef; + + function b2WeldJoint() { + b2WeldJoint.b2WeldJoint.apply(this, arguments); + if (this.constructor === b2WeldJoint) this.b2WeldJoint.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2WeldJoint = b2WeldJoint; + + function b2WeldJointDef() { + b2WeldJointDef.b2WeldJointDef.apply(this, arguments); + if (this.constructor === b2WeldJointDef) this.b2WeldJointDef.apply(this, arguments); + }; + Box2D.Dynamics.Joints.b2WeldJointDef = b2WeldJointDef; +})(); //definitions +Box2D.postDefs = []; +(function () { + var b2CircleShape = Box2D.Collision.Shapes.b2CircleShape, + b2EdgeChainDef = Box2D.Collision.Shapes.b2EdgeChainDef, + b2EdgeShape = Box2D.Collision.Shapes.b2EdgeShape, + b2MassData = Box2D.Collision.Shapes.b2MassData, + b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape, + b2Shape = Box2D.Collision.Shapes.b2Shape, + b2Color = Box2D.Common.b2Color, + b2internal = Box2D.Common.b2internal, + b2Settings = Box2D.Common.b2Settings, + b2Mat22 = Box2D.Common.Math.b2Mat22, + b2Mat33 = Box2D.Common.Math.b2Mat33, + b2Math = Box2D.Common.Math.b2Math, + b2Sweep = Box2D.Common.Math.b2Sweep, + b2Transform = Box2D.Common.Math.b2Transform, + b2Vec2 = Box2D.Common.Math.b2Vec2, + b2Vec3 = Box2D.Common.Math.b2Vec3, + b2AABB = Box2D.Collision.b2AABB, + b2Bound = Box2D.Collision.b2Bound, + b2BoundValues = Box2D.Collision.b2BoundValues, + b2Collision = Box2D.Collision.b2Collision, + b2ContactID = Box2D.Collision.b2ContactID, + b2ContactPoint = Box2D.Collision.b2ContactPoint, + b2Distance = Box2D.Collision.b2Distance, + b2DistanceInput = Box2D.Collision.b2DistanceInput, + b2DistanceOutput = Box2D.Collision.b2DistanceOutput, + b2DistanceProxy = Box2D.Collision.b2DistanceProxy, + b2DynamicTree = Box2D.Collision.b2DynamicTree, + b2DynamicTreeBroadPhase = Box2D.Collision.b2DynamicTreeBroadPhase, + b2DynamicTreeNode = Box2D.Collision.b2DynamicTreeNode, + b2DynamicTreePair = Box2D.Collision.b2DynamicTreePair, + b2Manifold = Box2D.Collision.b2Manifold, + b2ManifoldPoint = Box2D.Collision.b2ManifoldPoint, + b2Point = Box2D.Collision.b2Point, + b2RayCastInput = Box2D.Collision.b2RayCastInput, + b2RayCastOutput = Box2D.Collision.b2RayCastOutput, + b2Segment = Box2D.Collision.b2Segment, + b2SeparationFunction = Box2D.Collision.b2SeparationFunction, + b2Simplex = Box2D.Collision.b2Simplex, + b2SimplexCache = Box2D.Collision.b2SimplexCache, + b2SimplexVertex = Box2D.Collision.b2SimplexVertex, + b2TimeOfImpact = Box2D.Collision.b2TimeOfImpact, + b2TOIInput = Box2D.Collision.b2TOIInput, + b2WorldManifold = Box2D.Collision.b2WorldManifold, + ClipVertex = Box2D.Collision.ClipVertex, + Features = Box2D.Collision.Features, + IBroadPhase = Box2D.Collision.IBroadPhase; + + b2AABB.b2AABB = function () { + this.lowerBound = new b2Vec2(); + this.upperBound = new b2Vec2(); + }; + b2AABB.prototype.IsValid = function () { + var dX = this.upperBound.x - this.lowerBound.x; + var dY = this.upperBound.y - this.lowerBound.y; + var valid = dX >= 0.0 && dY >= 0.0; + valid = valid && this.lowerBound.IsValid() && this.upperBound.IsValid(); + return valid; + } + b2AABB.prototype.GetCenter = function () { + return new b2Vec2((this.lowerBound.x + this.upperBound.x) / 2, (this.lowerBound.y + this.upperBound.y) / 2); + } + b2AABB.prototype.GetExtents = function () { + return new b2Vec2((this.upperBound.x - this.lowerBound.x) / 2, (this.upperBound.y - this.lowerBound.y) / 2); + } + b2AABB.prototype.Contains = function (aabb) { + var result = true; + result = result && this.lowerBound.x <= aabb.lowerBound.x; + result = result && this.lowerBound.y <= aabb.lowerBound.y; + result = result && aabb.upperBound.x <= this.upperBound.x; + result = result && aabb.upperBound.y <= this.upperBound.y; + return result; + } + b2AABB.prototype.RayCast = function (output, input) { + var tmin = (-Number.MAX_VALUE); + var tmax = Number.MAX_VALUE; + var pX = input.p1.x; + var pY = input.p1.y; + var dX = input.p2.x - input.p1.x; + var dY = input.p2.y - input.p1.y; + var absDX = Math.abs(dX); + var absDY = Math.abs(dY); + var normal = output.normal; + var inv_d = 0; + var t1 = 0; + var t2 = 0; + var t3 = 0; + var s = 0; { + if (absDX < Number.MIN_VALUE) { + if (pX < this.lowerBound.x || this.upperBound.x < pX) return false; + } + else { + inv_d = 1.0 / dX; + t1 = (this.lowerBound.x - pX) * inv_d; + t2 = (this.upperBound.x - pX) * inv_d; + s = (-1.0); + if (t1 > t2) { + t3 = t1; + t1 = t2; + t2 = t3; + s = 1.0; + } + if (t1 > tmin) { + normal.x = s; + normal.y = 0; + tmin = t1; + } + tmax = Math.min(tmax, t2); + if (tmin > tmax) return false; + } + } { + if (absDY < Number.MIN_VALUE) { + if (pY < this.lowerBound.y || this.upperBound.y < pY) return false; + } + else { + inv_d = 1.0 / dY; + t1 = (this.lowerBound.y - pY) * inv_d; + t2 = (this.upperBound.y - pY) * inv_d; + s = (-1.0); + if (t1 > t2) { + t3 = t1; + t1 = t2; + t2 = t3; + s = 1.0; + } + if (t1 > tmin) { + normal.y = s; + normal.x = 0; + tmin = t1; + } + tmax = Math.min(tmax, t2); + if (tmin > tmax) return false; + } + } + output.fraction = tmin; + return true; + } + b2AABB.prototype.TestOverlap = function (other) { + var d1X = other.lowerBound.x - this.upperBound.x; + var d1Y = other.lowerBound.y - this.upperBound.y; + var d2X = this.lowerBound.x - other.upperBound.x; + var d2Y = this.lowerBound.y - other.upperBound.y; + if (d1X > 0.0 || d1Y > 0.0) return false; + if (d2X > 0.0 || d2Y > 0.0) return false; + return true; + } + b2AABB.Combine = function (aabb1, aabb2) { + var aabb = new b2AABB(); + aabb.Combine(aabb1, aabb2); + return aabb; + } + b2AABB.prototype.Combine = function (aabb1, aabb2) { + this.lowerBound.x = Math.min(aabb1.lowerBound.x, aabb2.lowerBound.x); + this.lowerBound.y = Math.min(aabb1.lowerBound.y, aabb2.lowerBound.y); + this.upperBound.x = Math.max(aabb1.upperBound.x, aabb2.upperBound.x); + this.upperBound.y = Math.max(aabb1.upperBound.y, aabb2.upperBound.y); + } + b2Bound.b2Bound = function () {}; + b2Bound.prototype.IsLower = function () { + return (this.value & 1) == 0; + } + b2Bound.prototype.IsUpper = function () { + return (this.value & 1) == 1; + } + b2Bound.prototype.Swap = function (b) { + var tempValue = this.value; + var tempProxy = this.proxy; + var tempStabbingCount = this.stabbingCount; + this.value = b.value; + this.proxy = b.proxy; + this.stabbingCount = b.stabbingCount; + b.value = tempValue; + b.proxy = tempProxy; + b.stabbingCount = tempStabbingCount; + } + b2BoundValues.b2BoundValues = function () {}; + b2BoundValues.prototype.b2BoundValues = function () { + this.lowerValues = new Vector_a2j_Number(); + this.lowerValues[0] = 0.0; + this.lowerValues[1] = 0.0; + this.upperValues = new Vector_a2j_Number(); + this.upperValues[0] = 0.0; + this.upperValues[1] = 0.0; + } + b2Collision.b2Collision = function () {}; + b2Collision.ClipSegmentToLine = function (vOut, vIn, normal, offset) { + if (offset === undefined) offset = 0; + var cv; + var numOut = 0; + cv = vIn[0]; + var vIn0 = cv.v; + cv = vIn[1]; + var vIn1 = cv.v; + var distance0 = normal.x * vIn0.x + normal.y * vIn0.y - offset; + var distance1 = normal.x * vIn1.x + normal.y * vIn1.y - offset; + if (distance0 <= 0.0) vOut[numOut++].Set(vIn[0]); + if (distance1 <= 0.0) vOut[numOut++].Set(vIn[1]); + if (distance0 * distance1 < 0.0) { + var interp = distance0 / (distance0 - distance1); + cv = vOut[numOut]; + var tVec = cv.v; + tVec.x = vIn0.x + interp * (vIn1.x - vIn0.x); + tVec.y = vIn0.y + interp * (vIn1.y - vIn0.y); + cv = vOut[numOut]; + var cv2; + if (distance0 > 0.0) { + cv2 = vIn[0]; + cv.id = cv2.id; + } + else { + cv2 = vIn[1]; + cv.id = cv2.id; + }++numOut; + } + return numOut; + } + b2Collision.EdgeSeparation = function (poly1, xf1, edge1, poly2, xf2) { + if (edge1 === undefined) edge1 = 0; + var count1 = parseInt(poly1.m_vertexCount); + var vertices1 = poly1.m_vertices; + var normals1 = poly1.m_normals; + var count2 = parseInt(poly2.m_vertexCount); + var vertices2 = poly2.m_vertices; + var tMat; + var tVec; + tMat = xf1.R; + tVec = normals1[edge1]; + var normal1WorldX = (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + var normal1WorldY = (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tMat = xf2.R; + var normal1X = (tMat.col1.x * normal1WorldX + tMat.col1.y * normal1WorldY); + var normal1Y = (tMat.col2.x * normal1WorldX + tMat.col2.y * normal1WorldY); + var index = 0; + var minDot = Number.MAX_VALUE; + for (var i = 0; i < count2; ++i) { + tVec = vertices2[i]; + var dot = tVec.x * normal1X + tVec.y * normal1Y; + if (dot < minDot) { + minDot = dot; + index = i; + } + } + tVec = vertices1[edge1]; + tMat = xf1.R; + var v1X = xf1.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + var v1Y = xf1.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tVec = vertices2[index]; + tMat = xf2.R; + var v2X = xf2.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + var v2Y = xf2.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + v2X -= v1X; + v2Y -= v1Y; + var separation = v2X * normal1WorldX + v2Y * normal1WorldY; + return separation; + } + b2Collision.FindMaxSeparation = function (edgeIndex, poly1, xf1, poly2, xf2) { + var count1 = parseInt(poly1.m_vertexCount); + var normals1 = poly1.m_normals; + var tVec; + var tMat; + tMat = xf2.R; + tVec = poly2.m_centroid; + var dX = xf2.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + var dY = xf2.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tMat = xf1.R; + tVec = poly1.m_centroid; + dX -= xf1.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + dY -= xf1.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + var dLocal1X = (dX * xf1.R.col1.x + dY * xf1.R.col1.y); + var dLocal1Y = (dX * xf1.R.col2.x + dY * xf1.R.col2.y); + var edge = 0; + var maxDot = (-Number.MAX_VALUE); + for (var i = 0; i < count1; ++i) { + tVec = normals1[i]; + var dot = (tVec.x * dLocal1X + tVec.y * dLocal1Y); + if (dot > maxDot) { + maxDot = dot; + edge = i; + } + } + var s = b2Collision.EdgeSeparation(poly1, xf1, edge, poly2, xf2); + var prevEdge = parseInt(edge - 1 >= 0 ? edge - 1 : count1 - 1); + var sPrev = b2Collision.EdgeSeparation(poly1, xf1, prevEdge, poly2, xf2); + var nextEdge = parseInt(edge + 1 < count1 ? edge + 1 : 0); + var sNext = b2Collision.EdgeSeparation(poly1, xf1, nextEdge, poly2, xf2); + var bestEdge = 0; + var bestSeparation = 0; + var increment = 0; + if (sPrev > s && sPrev > sNext) { + increment = (-1); + bestEdge = prevEdge; + bestSeparation = sPrev; + } + else if (sNext > s) { + increment = 1; + bestEdge = nextEdge; + bestSeparation = sNext; + } + else { + edgeIndex[0] = edge; + return s; + } + while (true) { + if (increment == (-1)) edge = bestEdge - 1 >= 0 ? bestEdge - 1 : count1 - 1; + else edge = bestEdge + 1 < count1 ? bestEdge + 1 : 0;s = b2Collision.EdgeSeparation(poly1, xf1, edge, poly2, xf2); + if (s > bestSeparation) { + bestEdge = edge; + bestSeparation = s; + } + else { + break; + } + } + edgeIndex[0] = bestEdge; + return bestSeparation; + } + b2Collision.FindIncidentEdge = function (c, poly1, xf1, edge1, poly2, xf2) { + if (edge1 === undefined) edge1 = 0; + var count1 = parseInt(poly1.m_vertexCount); + var normals1 = poly1.m_normals; + var count2 = parseInt(poly2.m_vertexCount); + var vertices2 = poly2.m_vertices; + var normals2 = poly2.m_normals; + var tMat; + var tVec; + tMat = xf1.R; + tVec = normals1[edge1]; + var normal1X = (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + var normal1Y = (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tMat = xf2.R; + var tX = (tMat.col1.x * normal1X + tMat.col1.y * normal1Y); + normal1Y = (tMat.col2.x * normal1X + tMat.col2.y * normal1Y); + normal1X = tX; + var index = 0; + var minDot = Number.MAX_VALUE; + for (var i = 0; i < count2; ++i) { + tVec = normals2[i]; + var dot = (normal1X * tVec.x + normal1Y * tVec.y); + if (dot < minDot) { + minDot = dot; + index = i; + } + } + var tClip; + var i1 = parseInt(index); + var i2 = parseInt(i1 + 1 < count2 ? i1 + 1 : 0); + tClip = c[0]; + tVec = vertices2[i1]; + tMat = xf2.R; + tClip.v.x = xf2.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + tClip.v.y = xf2.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tClip.id.features.referenceEdge = edge1; + tClip.id.features.incidentEdge = i1; + tClip.id.features.incidentVertex = 0; + tClip = c[1]; + tVec = vertices2[i2]; + tMat = xf2.R; + tClip.v.x = xf2.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + tClip.v.y = xf2.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tClip.id.features.referenceEdge = edge1; + tClip.id.features.incidentEdge = i2; + tClip.id.features.incidentVertex = 1; + } + b2Collision.MakeClipPointVector = function () { + var r = new Vector(2); + r[0] = new ClipVertex(); + r[1] = new ClipVertex(); + return r; + } + b2Collision.CollidePolygons = function (manifold, polyA, xfA, polyB, xfB) { + var cv; + manifold.m_pointCount = 0; + var totalRadius = polyA.m_radius + polyB.m_radius; + var edgeA = 0; + b2Collision.s_edgeAO[0] = edgeA; + var separationA = b2Collision.FindMaxSeparation(b2Collision.s_edgeAO, polyA, xfA, polyB, xfB); + edgeA = b2Collision.s_edgeAO[0]; + if (separationA > totalRadius) return; + var edgeB = 0; + b2Collision.s_edgeBO[0] = edgeB; + var separationB = b2Collision.FindMaxSeparation(b2Collision.s_edgeBO, polyB, xfB, polyA, xfA); + edgeB = b2Collision.s_edgeBO[0]; + if (separationB > totalRadius) return; + var poly1; + var poly2; + var xf1; + var xf2; + var edge1 = 0; + var flip = 0; + var k_relativeTol = 0.98; + var k_absoluteTol = 0.001; + var tMat; + if (separationB > k_relativeTol * separationA + k_absoluteTol) { + poly1 = polyB; + poly2 = polyA; + xf1 = xfB; + xf2 = xfA; + edge1 = edgeB; + manifold.m_type = b2Manifold.e_faceB; + flip = 1; + } + else { + poly1 = polyA; + poly2 = polyB; + xf1 = xfA; + xf2 = xfB; + edge1 = edgeA; + manifold.m_type = b2Manifold.e_faceA; + flip = 0; + } + var incidentEdge = b2Collision.s_incidentEdge; + b2Collision.FindIncidentEdge(incidentEdge, poly1, xf1, edge1, poly2, xf2); + var count1 = parseInt(poly1.m_vertexCount); + var vertices1 = poly1.m_vertices; + var local_v11 = vertices1[edge1]; + var local_v12; + if (edge1 + 1 < count1) { + local_v12 = vertices1[parseInt(edge1 + 1)]; + } + else { + local_v12 = vertices1[0]; + } + var localTangent = b2Collision.s_localTangent; + localTangent.Set(local_v12.x - local_v11.x, local_v12.y - local_v11.y); + localTangent.Normalize(); + var localNormal = b2Collision.s_localNormal; + localNormal.x = localTangent.y; + localNormal.y = (-localTangent.x); + var planePoint = b2Collision.s_planePoint; + planePoint.Set(0.5 * (local_v11.x + local_v12.x), 0.5 * (local_v11.y + local_v12.y)); + var tangent = b2Collision.s_tangent; + tMat = xf1.R; + tangent.x = (tMat.col1.x * localTangent.x + tMat.col2.x * localTangent.y); + tangent.y = (tMat.col1.y * localTangent.x + tMat.col2.y * localTangent.y); + var tangent2 = b2Collision.s_tangent2; + tangent2.x = (-tangent.x); + tangent2.y = (-tangent.y); + var normal = b2Collision.s_normal; + normal.x = tangent.y; + normal.y = (-tangent.x); + var v11 = b2Collision.s_v11; + var v12 = b2Collision.s_v12; + v11.x = xf1.position.x + (tMat.col1.x * local_v11.x + tMat.col2.x * local_v11.y); + v11.y = xf1.position.y + (tMat.col1.y * local_v11.x + tMat.col2.y * local_v11.y); + v12.x = xf1.position.x + (tMat.col1.x * local_v12.x + tMat.col2.x * local_v12.y); + v12.y = xf1.position.y + (tMat.col1.y * local_v12.x + tMat.col2.y * local_v12.y); + var frontOffset = normal.x * v11.x + normal.y * v11.y; + var sideOffset1 = (-tangent.x * v11.x) - tangent.y * v11.y + totalRadius; + var sideOffset2 = tangent.x * v12.x + tangent.y * v12.y + totalRadius; + var clipPoints1 = b2Collision.s_clipPoints1; + var clipPoints2 = b2Collision.s_clipPoints2; + var np = 0; + np = b2Collision.ClipSegmentToLine(clipPoints1, incidentEdge, tangent2, sideOffset1); + if (np < 2) return; + np = b2Collision.ClipSegmentToLine(clipPoints2, clipPoints1, tangent, sideOffset2); + if (np < 2) return; + manifold.m_localPlaneNormal.SetV(localNormal); + manifold.m_localPoint.SetV(planePoint); + var pointCount = 0; + for (var i = 0; i < b2Settings.b2_maxManifoldPoints; ++i) { + cv = clipPoints2[i]; + var separation = normal.x * cv.v.x + normal.y * cv.v.y - frontOffset; + if (separation <= totalRadius) { + var cp = manifold.m_points[pointCount]; + tMat = xf2.R; + var tX = cv.v.x - xf2.position.x; + var tY = cv.v.y - xf2.position.y; + cp.m_localPoint.x = (tX * tMat.col1.x + tY * tMat.col1.y); + cp.m_localPoint.y = (tX * tMat.col2.x + tY * tMat.col2.y); + cp.m_id.Set(cv.id); + cp.m_id.features.flip = flip; + ++pointCount; + } + } + manifold.m_pointCount = pointCount; + } + b2Collision.CollideCircles = function (manifold, circle1, xf1, circle2, xf2) { + manifold.m_pointCount = 0; + var tMat; + var tVec; + tMat = xf1.R; + tVec = circle1.m_p; + var p1X = xf1.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + var p1Y = xf1.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tMat = xf2.R; + tVec = circle2.m_p; + var p2X = xf2.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + var p2Y = xf2.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + var dX = p2X - p1X; + var dY = p2Y - p1Y; + var distSqr = dX * dX + dY * dY; + var radius = circle1.m_radius + circle2.m_radius; + if (distSqr > radius * radius) { + return; + } + manifold.m_type = b2Manifold.e_circles; + manifold.m_localPoint.SetV(circle1.m_p); + manifold.m_localPlaneNormal.SetZero(); + manifold.m_pointCount = 1; + manifold.m_points[0].m_localPoint.SetV(circle2.m_p); + manifold.m_points[0].m_id.key = 0; + } + b2Collision.CollidePolygonAndCircle = function (manifold, polygon, xf1, circle, xf2) { + manifold.m_pointCount = 0; + var tPoint; + var dX = 0; + var dY = 0; + var positionX = 0; + var positionY = 0; + var tVec; + var tMat; + tMat = xf2.R; + tVec = circle.m_p; + var cX = xf2.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + var cY = xf2.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + dX = cX - xf1.position.x; + dY = cY - xf1.position.y; + tMat = xf1.R; + var cLocalX = (dX * tMat.col1.x + dY * tMat.col1.y); + var cLocalY = (dX * tMat.col2.x + dY * tMat.col2.y); + var dist = 0; + var normalIndex = 0; + var separation = (-Number.MAX_VALUE); + var radius = polygon.m_radius + circle.m_radius; + var vertexCount = parseInt(polygon.m_vertexCount); + var vertices = polygon.m_vertices; + var normals = polygon.m_normals; + for (var i = 0; i < vertexCount; ++i) { + tVec = vertices[i]; + dX = cLocalX - tVec.x; + dY = cLocalY - tVec.y; + tVec = normals[i]; + var s = tVec.x * dX + tVec.y * dY; + if (s > radius) { + return; + } + if (s > separation) { + separation = s; + normalIndex = i; + } + } + var vertIndex1 = parseInt(normalIndex); + var vertIndex2 = parseInt(vertIndex1 + 1 < vertexCount ? vertIndex1 + 1 : 0); + var v1 = vertices[vertIndex1]; + var v2 = vertices[vertIndex2]; + if (separation < Number.MIN_VALUE) { + manifold.m_pointCount = 1; + manifold.m_type = b2Manifold.e_faceA; + manifold.m_localPlaneNormal.SetV(normals[normalIndex]); + manifold.m_localPoint.x = 0.5 * (v1.x + v2.x); + manifold.m_localPoint.y = 0.5 * (v1.y + v2.y); + manifold.m_points[0].m_localPoint.SetV(circle.m_p); + manifold.m_points[0].m_id.key = 0; + return; + } + var u1 = (cLocalX - v1.x) * (v2.x - v1.x) + (cLocalY - v1.y) * (v2.y - v1.y); + var u2 = (cLocalX - v2.x) * (v1.x - v2.x) + (cLocalY - v2.y) * (v1.y - v2.y); + if (u1 <= 0.0) { + if ((cLocalX - v1.x) * (cLocalX - v1.x) + (cLocalY - v1.y) * (cLocalY - v1.y) > radius * radius) return; + manifold.m_pointCount = 1; + manifold.m_type = b2Manifold.e_faceA; + manifold.m_localPlaneNormal.x = cLocalX - v1.x; + manifold.m_localPlaneNormal.y = cLocalY - v1.y; + manifold.m_localPlaneNormal.Normalize(); + manifold.m_localPoint.SetV(v1); + manifold.m_points[0].m_localPoint.SetV(circle.m_p); + manifold.m_points[0].m_id.key = 0; + } + else if (u2 <= 0) { + if ((cLocalX - v2.x) * (cLocalX - v2.x) + (cLocalY - v2.y) * (cLocalY - v2.y) > radius * radius) return; + manifold.m_pointCount = 1; + manifold.m_type = b2Manifold.e_faceA; + manifold.m_localPlaneNormal.x = cLocalX - v2.x; + manifold.m_localPlaneNormal.y = cLocalY - v2.y; + manifold.m_localPlaneNormal.Normalize(); + manifold.m_localPoint.SetV(v2); + manifold.m_points[0].m_localPoint.SetV(circle.m_p); + manifold.m_points[0].m_id.key = 0; + } + else { + var faceCenterX = 0.5 * (v1.x + v2.x); + var faceCenterY = 0.5 * (v1.y + v2.y); + separation = (cLocalX - faceCenterX) * normals[vertIndex1].x + (cLocalY - faceCenterY) * normals[vertIndex1].y; + if (separation > radius) return; + manifold.m_pointCount = 1; + manifold.m_type = b2Manifold.e_faceA; + manifold.m_localPlaneNormal.x = normals[vertIndex1].x; + manifold.m_localPlaneNormal.y = normals[vertIndex1].y; + manifold.m_localPlaneNormal.Normalize(); + manifold.m_localPoint.Set(faceCenterX, faceCenterY); + manifold.m_points[0].m_localPoint.SetV(circle.m_p); + manifold.m_points[0].m_id.key = 0; + } + } + b2Collision.TestOverlap = function (a, b) { + var t1 = b.lowerBound; + var t2 = a.upperBound; + var d1X = t1.x - t2.x; + var d1Y = t1.y - t2.y; + t1 = a.lowerBound; + t2 = b.upperBound; + var d2X = t1.x - t2.x; + var d2Y = t1.y - t2.y; + if (d1X > 0.0 || d1Y > 0.0) return false; + if (d2X > 0.0 || d2Y > 0.0) return false; + return true; + } + Box2D.postDefs.push(function () { + Box2D.Collision.b2Collision.s_incidentEdge = b2Collision.MakeClipPointVector(); + Box2D.Collision.b2Collision.s_clipPoints1 = b2Collision.MakeClipPointVector(); + Box2D.Collision.b2Collision.s_clipPoints2 = b2Collision.MakeClipPointVector(); + Box2D.Collision.b2Collision.s_edgeAO = new Vector_a2j_Number(1); + Box2D.Collision.b2Collision.s_edgeBO = new Vector_a2j_Number(1); + Box2D.Collision.b2Collision.s_localTangent = new b2Vec2(); + Box2D.Collision.b2Collision.s_localNormal = new b2Vec2(); + Box2D.Collision.b2Collision.s_planePoint = new b2Vec2(); + Box2D.Collision.b2Collision.s_normal = new b2Vec2(); + Box2D.Collision.b2Collision.s_tangent = new b2Vec2(); + Box2D.Collision.b2Collision.s_tangent2 = new b2Vec2(); + Box2D.Collision.b2Collision.s_v11 = new b2Vec2(); + Box2D.Collision.b2Collision.s_v12 = new b2Vec2(); + Box2D.Collision.b2Collision.b2CollidePolyTempVec = new b2Vec2(); + Box2D.Collision.b2Collision.b2_nullFeature = 0x000000ff; + }); + b2ContactID.b2ContactID = function () { + this.features = new Features(); + }; + b2ContactID.prototype.b2ContactID = function () { + this.features._m_id = this; + } + b2ContactID.prototype.Set = function (id) { + this.key = id._key; + } + b2ContactID.prototype.Copy = function () { + var id = new b2ContactID(); + id.key = this.key; + return id; + } + Object.defineProperty(b2ContactID.prototype, 'key', { + enumerable: false, + configurable: true, + get: function () { + return this._key; + } + }); + Object.defineProperty(b2ContactID.prototype, 'key', { + enumerable: false, + configurable: true, + set: function (value) { + if (value === undefined) value = 0; + this._key = value; + this.features._referenceEdge = this._key & 0x000000ff; + this.features._incidentEdge = ((this._key & 0x0000ff00) >> 8) & 0x000000ff; + this.features._incidentVertex = ((this._key & 0x00ff0000) >> 16) & 0x000000ff; + this.features._flip = ((this._key & 0xff000000) >> 24) & 0x000000ff; + } + }); + b2ContactPoint.b2ContactPoint = function () { + this.position = new b2Vec2(); + this.velocity = new b2Vec2(); + this.normal = new b2Vec2(); + this.id = new b2ContactID(); + }; + b2Distance.b2Distance = function () {}; + b2Distance.Distance = function (output, cache, input) { + ++b2Distance.b2_gjkCalls; + var proxyA = input.proxyA; + var proxyB = input.proxyB; + var transformA = input.transformA; + var transformB = input.transformB; + var simplex = b2Distance.s_simplex; + simplex.ReadCache(cache, proxyA, transformA, proxyB, transformB); + var vertices = simplex.m_vertices; + var k_maxIters = 20; + var saveA = b2Distance.s_saveA; + var saveB = b2Distance.s_saveB; + var saveCount = 0; + var closestPoint = simplex.GetClosestPoint(); + var distanceSqr1 = closestPoint.LengthSquared(); + var distanceSqr2 = distanceSqr1; + var i = 0; + var p; + var iter = 0; + while (iter < k_maxIters) { + saveCount = simplex.m_count; + for (i = 0; + i < saveCount; i++) { + saveA[i] = vertices[i].indexA; + saveB[i] = vertices[i].indexB; + } + switch (simplex.m_count) { + case 1: + break; + case 2: + simplex.Solve2(); + break; + case 3: + simplex.Solve3(); + break; + default: + b2Settings.b2Assert(false); + } + if (simplex.m_count == 3) { + break; + } + p = simplex.GetClosestPoint(); + distanceSqr2 = p.LengthSquared(); + if (distanceSqr2 > distanceSqr1) {} + distanceSqr1 = distanceSqr2; + var d = simplex.GetSearchDirection(); + if (d.LengthSquared() < Number.MIN_VALUE * Number.MIN_VALUE) { + break; + } + var vertex = vertices[simplex.m_count]; + vertex.indexA = proxyA.GetSupport(b2Math.MulTMV(transformA.R, d.GetNegative())); + vertex.wA = b2Math.MulX(transformA, proxyA.GetVertex(vertex.indexA)); + vertex.indexB = proxyB.GetSupport(b2Math.MulTMV(transformB.R, d)); + vertex.wB = b2Math.MulX(transformB, proxyB.GetVertex(vertex.indexB)); + vertex.w = b2Math.SubtractVV(vertex.wB, vertex.wA); + ++iter; + ++b2Distance.b2_gjkIters; + var duplicate = false; + for (i = 0; + i < saveCount; i++) { + if (vertex.indexA == saveA[i] && vertex.indexB == saveB[i]) { + duplicate = true; + break; + } + } + if (duplicate) { + break; + }++simplex.m_count; + } + b2Distance.b2_gjkMaxIters = b2Math.Max(b2Distance.b2_gjkMaxIters, iter); + simplex.GetWitnessPoints(output.pointA, output.pointB); + output.distance = b2Math.SubtractVV(output.pointA, output.pointB).Length(); + output.iterations = iter; + simplex.WriteCache(cache); + if (input.useRadii) { + var rA = proxyA.m_radius; + var rB = proxyB.m_radius; + if (output.distance > rA + rB && output.distance > Number.MIN_VALUE) { + output.distance -= rA + rB; + var normal = b2Math.SubtractVV(output.pointB, output.pointA); + normal.Normalize(); + output.pointA.x += rA * normal.x; + output.pointA.y += rA * normal.y; + output.pointB.x -= rB * normal.x; + output.pointB.y -= rB * normal.y; + } + else { + p = new b2Vec2(); + p.x = .5 * (output.pointA.x + output.pointB.x); + p.y = .5 * (output.pointA.y + output.pointB.y); + output.pointA.x = output.pointB.x = p.x; + output.pointA.y = output.pointB.y = p.y; + output.distance = 0.0; + } + } + } + Box2D.postDefs.push(function () { + Box2D.Collision.b2Distance.s_simplex = new b2Simplex(); + Box2D.Collision.b2Distance.s_saveA = new Vector_a2j_Number(3); + Box2D.Collision.b2Distance.s_saveB = new Vector_a2j_Number(3); + }); + b2DistanceInput.b2DistanceInput = function () {}; + b2DistanceOutput.b2DistanceOutput = function () { + this.pointA = new b2Vec2(); + this.pointB = new b2Vec2(); + }; + b2DistanceProxy.b2DistanceProxy = function () {}; + b2DistanceProxy.prototype.Set = function (shape) { + switch (shape.GetType()) { + case b2Shape.e_circleShape: + { + var circle = (shape instanceof b2CircleShape ? shape : null); + this.m_vertices = new Vector(1, true); + this.m_vertices[0] = circle.m_p; + this.m_count = 1; + this.m_radius = circle.m_radius; + } + break; + case b2Shape.e_polygonShape: + { + var polygon = (shape instanceof b2PolygonShape ? shape : null); + this.m_vertices = polygon.m_vertices; + this.m_count = polygon.m_vertexCount; + this.m_radius = polygon.m_radius; + } + break; + default: + b2Settings.b2Assert(false); + } + } + b2DistanceProxy.prototype.GetSupport = function (d) { + var bestIndex = 0; + var bestValue = this.m_vertices[0].x * d.x + this.m_vertices[0].y * d.y; + for (var i = 1; i < this.m_count; ++i) { + var value = this.m_vertices[i].x * d.x + this.m_vertices[i].y * d.y; + if (value > bestValue) { + bestIndex = i; + bestValue = value; + } + } + return bestIndex; + } + b2DistanceProxy.prototype.GetSupportVertex = function (d) { + var bestIndex = 0; + var bestValue = this.m_vertices[0].x * d.x + this.m_vertices[0].y * d.y; + for (var i = 1; i < this.m_count; ++i) { + var value = this.m_vertices[i].x * d.x + this.m_vertices[i].y * d.y; + if (value > bestValue) { + bestIndex = i; + bestValue = value; + } + } + return this.m_vertices[bestIndex]; + } + b2DistanceProxy.prototype.GetVertexCount = function () { + return this.m_count; + } + b2DistanceProxy.prototype.GetVertex = function (index) { + if (index === undefined) index = 0; + b2Settings.b2Assert(0 <= index && index < this.m_count); + return this.m_vertices[index]; + } + b2DynamicTree.b2DynamicTree = function () {}; + b2DynamicTree.prototype.b2DynamicTree = function () { + this.m_root = null; + this.m_freeList = null; + this.m_path = 0; + this.m_insertionCount = 0; + } + b2DynamicTree.prototype.CreateProxy = function (aabb, userData) { + var node = this.AllocateNode(); + var extendX = b2Settings.b2_aabbExtension; + var extendY = b2Settings.b2_aabbExtension; + node.aabb.lowerBound.x = aabb.lowerBound.x - extendX; + node.aabb.lowerBound.y = aabb.lowerBound.y - extendY; + node.aabb.upperBound.x = aabb.upperBound.x + extendX; + node.aabb.upperBound.y = aabb.upperBound.y + extendY; + node.userData = userData; + this.InsertLeaf(node); + return node; + } + b2DynamicTree.prototype.DestroyProxy = function (proxy) { + this.RemoveLeaf(proxy); + this.FreeNode(proxy); + } + b2DynamicTree.prototype.MoveProxy = function (proxy, aabb, displacement) { + b2Settings.b2Assert(proxy.IsLeaf()); + if (proxy.aabb.Contains(aabb)) { + return false; + } + this.RemoveLeaf(proxy); + var extendX = b2Settings.b2_aabbExtension + b2Settings.b2_aabbMultiplier * (displacement.x > 0 ? displacement.x : (-displacement.x)); + var extendY = b2Settings.b2_aabbExtension + b2Settings.b2_aabbMultiplier * (displacement.y > 0 ? displacement.y : (-displacement.y)); + proxy.aabb.lowerBound.x = aabb.lowerBound.x - extendX; + proxy.aabb.lowerBound.y = aabb.lowerBound.y - extendY; + proxy.aabb.upperBound.x = aabb.upperBound.x + extendX; + proxy.aabb.upperBound.y = aabb.upperBound.y + extendY; + this.InsertLeaf(proxy); + return true; + } + b2DynamicTree.prototype.Rebalance = function (iterations) { + if (iterations === undefined) iterations = 0; + if (this.m_root == null) return; + for (var i = 0; i < iterations; i++) { + var node = this.m_root; + var bit = 0; + while (node.IsLeaf() == false) { + node = (this.m_path >> bit) & 1 ? node.child2 : node.child1; + bit = (bit + 1) & 31; + }++this.m_path; + this.RemoveLeaf(node); + this.InsertLeaf(node); + } + } + b2DynamicTree.prototype.GetFatAABB = function (proxy) { + return proxy.aabb; + } + b2DynamicTree.prototype.GetUserData = function (proxy) { + return proxy.userData; + } + b2DynamicTree.prototype.Query = function (callback, aabb) { + if (this.m_root == null) return; + var stack = new Vector(); + var count = 0; + stack[count++] = this.m_root; + while (count > 0) { + var node = stack[--count]; + if (node.aabb.TestOverlap(aabb)) { + if (node.IsLeaf()) { + var proceed = callback(node); + if (!proceed) return; + } + else { + stack[count++] = node.child1; + stack[count++] = node.child2; + } + } + } + } + b2DynamicTree.prototype.RayCast = function (callback, input) { + if (this.m_root == null) return; + var p1 = input.p1; + var p2 = input.p2; + var r = b2Math.SubtractVV(p1, p2); + r.Normalize(); + var v = b2Math.CrossFV(1.0, r); + var abs_v = b2Math.AbsV(v); + var maxFraction = input.maxFraction; + var segmentAABB = new b2AABB(); + var tX = 0; + var tY = 0; { + tX = p1.x + maxFraction * (p2.x - p1.x); + tY = p1.y + maxFraction * (p2.y - p1.y); + segmentAABB.lowerBound.x = Math.min(p1.x, tX); + segmentAABB.lowerBound.y = Math.min(p1.y, tY); + segmentAABB.upperBound.x = Math.max(p1.x, tX); + segmentAABB.upperBound.y = Math.max(p1.y, tY); + } + var stack = new Vector(); + var count = 0; + stack[count++] = this.m_root; + while (count > 0) { + var node = stack[--count]; + if (node.aabb.TestOverlap(segmentAABB) == false) { + continue; + } + var c = node.aabb.GetCenter(); + var h = node.aabb.GetExtents(); + var separation = Math.abs(v.x * (p1.x - c.x) + v.y * (p1.y - c.y)) - abs_v.x * h.x - abs_v.y * h.y; + if (separation > 0.0) continue; + if (node.IsLeaf()) { + var subInput = new b2RayCastInput(); + subInput.p1 = input.p1; + subInput.p2 = input.p2; + subInput.maxFraction = input.maxFraction; + maxFraction = callback(subInput, node); + if (maxFraction == 0.0) return; + if (maxFraction > 0.0) { + tX = p1.x + maxFraction * (p2.x - p1.x); + tY = p1.y + maxFraction * (p2.y - p1.y); + segmentAABB.lowerBound.x = Math.min(p1.x, tX); + segmentAABB.lowerBound.y = Math.min(p1.y, tY); + segmentAABB.upperBound.x = Math.max(p1.x, tX); + segmentAABB.upperBound.y = Math.max(p1.y, tY); + } + } + else { + stack[count++] = node.child1; + stack[count++] = node.child2; + } + } + } + b2DynamicTree.prototype.AllocateNode = function () { + if (this.m_freeList) { + var node = this.m_freeList; + this.m_freeList = node.parent; + node.parent = null; + node.child1 = null; + node.child2 = null; + return node; + } + return new b2DynamicTreeNode(); + } + b2DynamicTree.prototype.FreeNode = function (node) { + node.parent = this.m_freeList; + this.m_freeList = node; + } + b2DynamicTree.prototype.InsertLeaf = function (leaf) { + ++this.m_insertionCount; + if (this.m_root == null) { + this.m_root = leaf; + this.m_root.parent = null; + return; + } + var center = leaf.aabb.GetCenter(); + var sibling = this.m_root; + if (sibling.IsLeaf() == false) { + do { + var child1 = sibling.child1; + var child2 = sibling.child2; + var norm1 = Math.abs((child1.aabb.lowerBound.x + child1.aabb.upperBound.x) / 2 - center.x) + Math.abs((child1.aabb.lowerBound.y + child1.aabb.upperBound.y) / 2 - center.y); + var norm2 = Math.abs((child2.aabb.lowerBound.x + child2.aabb.upperBound.x) / 2 - center.x) + Math.abs((child2.aabb.lowerBound.y + child2.aabb.upperBound.y) / 2 - center.y); + if (norm1 < norm2) { + sibling = child1; + } + else { + sibling = child2; + } + } + while (sibling.IsLeaf() == false) + } + var node1 = sibling.parent; + var node2 = this.AllocateNode(); + node2.parent = node1; + node2.userData = null; + node2.aabb.Combine(leaf.aabb, sibling.aabb); + if (node1) { + if (sibling.parent.child1 == sibling) { + node1.child1 = node2; + } + else { + node1.child2 = node2; + } + node2.child1 = sibling; + node2.child2 = leaf; + sibling.parent = node2; + leaf.parent = node2; + do { + if (node1.aabb.Contains(node2.aabb)) break; + node1.aabb.Combine(node1.child1.aabb, node1.child2.aabb); + node2 = node1; + node1 = node1.parent; + } + while (node1) + } + else { + node2.child1 = sibling; + node2.child2 = leaf; + sibling.parent = node2; + leaf.parent = node2; + this.m_root = node2; + } + } + b2DynamicTree.prototype.RemoveLeaf = function (leaf) { + if (leaf == this.m_root) { + this.m_root = null; + return; + } + var node2 = leaf.parent; + var node1 = node2.parent; + var sibling; + if (node2.child1 == leaf) { + sibling = node2.child2; + } + else { + sibling = node2.child1; + } + if (node1) { + if (node1.child1 == node2) { + node1.child1 = sibling; + } + else { + node1.child2 = sibling; + } + sibling.parent = node1; + this.FreeNode(node2); + while (node1) { + var oldAABB = node1.aabb; + node1.aabb = b2AABB.Combine(node1.child1.aabb, node1.child2.aabb); + if (oldAABB.Contains(node1.aabb)) break; + node1 = node1.parent; + } + } + else { + this.m_root = sibling; + sibling.parent = null; + this.FreeNode(node2); + } + } + b2DynamicTreeBroadPhase.b2DynamicTreeBroadPhase = function () { + this.m_tree = new b2DynamicTree(); + this.m_moveBuffer = new Vector(); + this.m_pairBuffer = new Vector(); + this.m_pairCount = 0; + }; + b2DynamicTreeBroadPhase.prototype.CreateProxy = function (aabb, userData) { + var proxy = this.m_tree.CreateProxy(aabb, userData); + ++this.m_proxyCount; + this.BufferMove(proxy); + return proxy; + } + b2DynamicTreeBroadPhase.prototype.DestroyProxy = function (proxy) { + this.UnBufferMove(proxy); + --this.m_proxyCount; + this.m_tree.DestroyProxy(proxy); + } + b2DynamicTreeBroadPhase.prototype.MoveProxy = function (proxy, aabb, displacement) { + var buffer = this.m_tree.MoveProxy(proxy, aabb, displacement); + if (buffer) { + this.BufferMove(proxy); + } + } + b2DynamicTreeBroadPhase.prototype.TestOverlap = function (proxyA, proxyB) { + var aabbA = this.m_tree.GetFatAABB(proxyA); + var aabbB = this.m_tree.GetFatAABB(proxyB); + return aabbA.TestOverlap(aabbB); + } + b2DynamicTreeBroadPhase.prototype.GetUserData = function (proxy) { + return this.m_tree.GetUserData(proxy); + } + b2DynamicTreeBroadPhase.prototype.GetFatAABB = function (proxy) { + return this.m_tree.GetFatAABB(proxy); + } + b2DynamicTreeBroadPhase.prototype.GetProxyCount = function () { + return this.m_proxyCount; + } + b2DynamicTreeBroadPhase.prototype.UpdatePairs = function (callback) { + var __this = this; + __this.m_pairCount = 0; + var i = 0, + queryProxy; + for (i = 0; + i < __this.m_moveBuffer.length; ++i) { + queryProxy = __this.m_moveBuffer[i]; + + function QueryCallback(proxy) { + if (proxy == queryProxy) return true; + if (__this.m_pairCount == __this.m_pairBuffer.length) { + __this.m_pairBuffer[__this.m_pairCount] = new b2DynamicTreePair(); + } + var pair = __this.m_pairBuffer[__this.m_pairCount]; + pair.proxyA = proxy < queryProxy ? proxy : queryProxy; + pair.proxyB = proxy >= queryProxy ? proxy : queryProxy;++__this.m_pairCount; + return true; + }; + var fatAABB = __this.m_tree.GetFatAABB(queryProxy); + __this.m_tree.Query(QueryCallback, fatAABB); + } + __this.m_moveBuffer.length = 0; + for (var i = 0; i < __this.m_pairCount;) { + var primaryPair = __this.m_pairBuffer[i]; + var userDataA = __this.m_tree.GetUserData(primaryPair.proxyA); + var userDataB = __this.m_tree.GetUserData(primaryPair.proxyB); + callback(userDataA, userDataB); + ++i; + while (i < __this.m_pairCount) { + var pair = __this.m_pairBuffer[i]; + if (pair.proxyA != primaryPair.proxyA || pair.proxyB != primaryPair.proxyB) { + break; + }++i; + } + } + } + b2DynamicTreeBroadPhase.prototype.Query = function (callback, aabb) { + this.m_tree.Query(callback, aabb); + } + b2DynamicTreeBroadPhase.prototype.RayCast = function (callback, input) { + this.m_tree.RayCast(callback, input); + } + b2DynamicTreeBroadPhase.prototype.Validate = function () {} + b2DynamicTreeBroadPhase.prototype.Rebalance = function (iterations) { + if (iterations === undefined) iterations = 0; + this.m_tree.Rebalance(iterations); + } + b2DynamicTreeBroadPhase.prototype.BufferMove = function (proxy) { + this.m_moveBuffer[this.m_moveBuffer.length] = proxy; + } + b2DynamicTreeBroadPhase.prototype.UnBufferMove = function (proxy) { + var i = parseInt(this.m_moveBuffer.indexOf(proxy)); + this.m_moveBuffer.splice(i, 1); + } + b2DynamicTreeBroadPhase.prototype.ComparePairs = function (pair1, pair2) { + return 0; + } + b2DynamicTreeBroadPhase.__implements = {}; + b2DynamicTreeBroadPhase.__implements[IBroadPhase] = true; + b2DynamicTreeNode.b2DynamicTreeNode = function () { + this.aabb = new b2AABB(); + }; + b2DynamicTreeNode.prototype.IsLeaf = function () { + return this.child1 == null; + } + b2DynamicTreePair.b2DynamicTreePair = function () {}; + b2Manifold.b2Manifold = function () { + this.m_pointCount = 0; + }; + b2Manifold.prototype.b2Manifold = function () { + this.m_points = new Vector(b2Settings.b2_maxManifoldPoints); + for (var i = 0; i < b2Settings.b2_maxManifoldPoints; i++) { + this.m_points[i] = new b2ManifoldPoint(); + } + this.m_localPlaneNormal = new b2Vec2(); + this.m_localPoint = new b2Vec2(); + } + b2Manifold.prototype.Reset = function () { + for (var i = 0; i < b2Settings.b2_maxManifoldPoints; i++) { + ((this.m_points[i] instanceof b2ManifoldPoint ? this.m_points[i] : null)).Reset(); + } + this.m_localPlaneNormal.SetZero(); + this.m_localPoint.SetZero(); + this.m_type = 0; + this.m_pointCount = 0; + } + b2Manifold.prototype.Set = function (m) { + this.m_pointCount = m.m_pointCount; + for (var i = 0; i < b2Settings.b2_maxManifoldPoints; i++) { + ((this.m_points[i] instanceof b2ManifoldPoint ? this.m_points[i] : null)).Set(m.m_points[i]); + } + this.m_localPlaneNormal.SetV(m.m_localPlaneNormal); + this.m_localPoint.SetV(m.m_localPoint); + this.m_type = m.m_type; + } + b2Manifold.prototype.Copy = function () { + var copy = new b2Manifold(); + copy.Set(this); + return copy; + } + Box2D.postDefs.push(function () { + Box2D.Collision.b2Manifold.e_circles = 0x0001; + Box2D.Collision.b2Manifold.e_faceA = 0x0002; + Box2D.Collision.b2Manifold.e_faceB = 0x0004; + }); + b2ManifoldPoint.b2ManifoldPoint = function () { + this.m_localPoint = new b2Vec2(); + this.m_id = new b2ContactID(); + }; + b2ManifoldPoint.prototype.b2ManifoldPoint = function () { + this.Reset(); + } + b2ManifoldPoint.prototype.Reset = function () { + this.m_localPoint.SetZero(); + this.m_normalImpulse = 0.0; + this.m_tangentImpulse = 0.0; + this.m_id.key = 0; + } + b2ManifoldPoint.prototype.Set = function (m) { + this.m_localPoint.SetV(m.m_localPoint); + this.m_normalImpulse = m.m_normalImpulse; + this.m_tangentImpulse = m.m_tangentImpulse; + this.m_id.Set(m.m_id); + } + b2Point.b2Point = function () { + this.p = new b2Vec2(); + }; + b2Point.prototype.Support = function (xf, vX, vY) { + if (vX === undefined) vX = 0; + if (vY === undefined) vY = 0; + return this.p; + } + b2Point.prototype.GetFirstVertex = function (xf) { + return this.p; + } + b2RayCastInput.b2RayCastInput = function () { + this.p1 = new b2Vec2(); + this.p2 = new b2Vec2(); + }; + b2RayCastInput.prototype.b2RayCastInput = function (p1, p2, maxFraction) { + if (p1 === undefined) p1 = null; + if (p2 === undefined) p2 = null; + if (maxFraction === undefined) maxFraction = 1; + if (p1) this.p1.SetV(p1); + if (p2) this.p2.SetV(p2); + this.maxFraction = maxFraction; + } + b2RayCastOutput.b2RayCastOutput = function () { + this.normal = new b2Vec2(); + }; + b2Segment.b2Segment = function () { + this.p1 = new b2Vec2(); + this.p2 = new b2Vec2(); + }; + b2Segment.prototype.TestSegment = function (lambda, normal, segment, maxLambda) { + if (maxLambda === undefined) maxLambda = 0; + var s = segment.p1; + var rX = segment.p2.x - s.x; + var rY = segment.p2.y - s.y; + var dX = this.p2.x - this.p1.x; + var dY = this.p2.y - this.p1.y; + var nX = dY; + var nY = (-dX); + var k_slop = 100.0 * Number.MIN_VALUE; + var denom = (-(rX * nX + rY * nY)); + if (denom > k_slop) { + var bX = s.x - this.p1.x; + var bY = s.y - this.p1.y; + var a = (bX * nX + bY * nY); + if (0.0 <= a && a <= maxLambda * denom) { + var mu2 = (-rX * bY) + rY * bX; + if ((-k_slop * denom) <= mu2 && mu2 <= denom * (1.0 + k_slop)) { + a /= denom; + var nLen = Math.sqrt(nX * nX + nY * nY); + nX /= nLen; + nY /= nLen; + lambda[0] = a; + normal.Set(nX, nY); + return true; + } + } + } + return false; + } + b2Segment.prototype.Extend = function (aabb) { + this.ExtendForward(aabb); + this.ExtendBackward(aabb); + } + b2Segment.prototype.ExtendForward = function (aabb) { + var dX = this.p2.x - this.p1.x; + var dY = this.p2.y - this.p1.y; + var lambda = Math.min(dX > 0 ? (aabb.upperBound.x - this.p1.x) / dX : dX < 0 ? (aabb.lowerBound.x - this.p1.x) / dX : Number.POSITIVE_INFINITY, + dY > 0 ? (aabb.upperBound.y - this.p1.y) / dY : dY < 0 ? (aabb.lowerBound.y - this.p1.y) / dY : Number.POSITIVE_INFINITY); + this.p2.x = this.p1.x + dX * lambda; + this.p2.y = this.p1.y + dY * lambda; + } + b2Segment.prototype.ExtendBackward = function (aabb) { + var dX = (-this.p2.x) + this.p1.x; + var dY = (-this.p2.y) + this.p1.y; + var lambda = Math.min(dX > 0 ? (aabb.upperBound.x - this.p2.x) / dX : dX < 0 ? (aabb.lowerBound.x - this.p2.x) / dX : Number.POSITIVE_INFINITY, + dY > 0 ? (aabb.upperBound.y - this.p2.y) / dY : dY < 0 ? (aabb.lowerBound.y - this.p2.y) / dY : Number.POSITIVE_INFINITY); + this.p1.x = this.p2.x + dX * lambda; + this.p1.y = this.p2.y + dY * lambda; + } + b2SeparationFunction.b2SeparationFunction = function () { + this.m_localPoint = new b2Vec2(); + this.m_axis = new b2Vec2(); + }; + b2SeparationFunction.prototype.Initialize = function (cache, proxyA, transformA, proxyB, transformB) { + this.m_proxyA = proxyA; + this.m_proxyB = proxyB; + var count = parseInt(cache.count); + b2Settings.b2Assert(0 < count && count < 3); + var localPointA; + var localPointA1; + var localPointA2; + var localPointB; + var localPointB1; + var localPointB2; + var pointAX = 0; + var pointAY = 0; + var pointBX = 0; + var pointBY = 0; + var normalX = 0; + var normalY = 0; + var tMat; + var tVec; + var s = 0; + var sgn = 0; + if (count == 1) { + this.m_type = b2SeparationFunction.e_points; + localPointA = this.m_proxyA.GetVertex(cache.indexA[0]); + localPointB = this.m_proxyB.GetVertex(cache.indexB[0]); + tVec = localPointA; + tMat = transformA.R; + pointAX = transformA.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + pointAY = transformA.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tVec = localPointB; + tMat = transformB.R; + pointBX = transformB.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + pointBY = transformB.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + this.m_axis.x = pointBX - pointAX; + this.m_axis.y = pointBY - pointAY; + this.m_axis.Normalize(); + } + else if (cache.indexB[0] == cache.indexB[1]) { + this.m_type = b2SeparationFunction.e_faceA; + localPointA1 = this.m_proxyA.GetVertex(cache.indexA[0]); + localPointA2 = this.m_proxyA.GetVertex(cache.indexA[1]); + localPointB = this.m_proxyB.GetVertex(cache.indexB[0]); + this.m_localPoint.x = 0.5 * (localPointA1.x + localPointA2.x); + this.m_localPoint.y = 0.5 * (localPointA1.y + localPointA2.y); + this.m_axis = b2Math.CrossVF(b2Math.SubtractVV(localPointA2, localPointA1), 1.0); + this.m_axis.Normalize(); + tVec = this.m_axis; + tMat = transformA.R; + normalX = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + normalY = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + tVec = this.m_localPoint; + tMat = transformA.R; + pointAX = transformA.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + pointAY = transformA.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tVec = localPointB; + tMat = transformB.R; + pointBX = transformB.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + pointBY = transformB.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + s = (pointBX - pointAX) * normalX + (pointBY - pointAY) * normalY; + if (s < 0.0) { + this.m_axis.NegativeSelf(); + } + } + else if (cache.indexA[0] == cache.indexA[0]) { + this.m_type = b2SeparationFunction.e_faceB; + localPointB1 = this.m_proxyB.GetVertex(cache.indexB[0]); + localPointB2 = this.m_proxyB.GetVertex(cache.indexB[1]); + localPointA = this.m_proxyA.GetVertex(cache.indexA[0]); + this.m_localPoint.x = 0.5 * (localPointB1.x + localPointB2.x); + this.m_localPoint.y = 0.5 * (localPointB1.y + localPointB2.y); + this.m_axis = b2Math.CrossVF(b2Math.SubtractVV(localPointB2, localPointB1), 1.0); + this.m_axis.Normalize(); + tVec = this.m_axis; + tMat = transformB.R; + normalX = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + normalY = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + tVec = this.m_localPoint; + tMat = transformB.R; + pointBX = transformB.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + pointBY = transformB.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tVec = localPointA; + tMat = transformA.R; + pointAX = transformA.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + pointAY = transformA.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + s = (pointAX - pointBX) * normalX + (pointAY - pointBY) * normalY; + if (s < 0.0) { + this.m_axis.NegativeSelf(); + } + } + else { + localPointA1 = this.m_proxyA.GetVertex(cache.indexA[0]); + localPointA2 = this.m_proxyA.GetVertex(cache.indexA[1]); + localPointB1 = this.m_proxyB.GetVertex(cache.indexB[0]); + localPointB2 = this.m_proxyB.GetVertex(cache.indexB[1]); + var pA = b2Math.MulX(transformA, localPointA); + var dA = b2Math.MulMV(transformA.R, b2Math.SubtractVV(localPointA2, localPointA1)); + var pB = b2Math.MulX(transformB, localPointB); + var dB = b2Math.MulMV(transformB.R, b2Math.SubtractVV(localPointB2, localPointB1)); + var a = dA.x * dA.x + dA.y * dA.y; + var e = dB.x * dB.x + dB.y * dB.y; + var r = b2Math.SubtractVV(dB, dA); + var c = dA.x * r.x + dA.y * r.y; + var f = dB.x * r.x + dB.y * r.y; + var b = dA.x * dB.x + dA.y * dB.y; + var denom = a * e - b * b; + s = 0.0; + if (denom != 0.0) { + s = b2Math.Clamp((b * f - c * e) / denom, 0.0, 1.0); + } + var t = (b * s + f) / e; + if (t < 0.0) { + t = 0.0; + s = b2Math.Clamp((b - c) / a, 0.0, 1.0); + } + localPointA = new b2Vec2(); + localPointA.x = localPointA1.x + s * (localPointA2.x - localPointA1.x); + localPointA.y = localPointA1.y + s * (localPointA2.y - localPointA1.y); + localPointB = new b2Vec2(); + localPointB.x = localPointB1.x + s * (localPointB2.x - localPointB1.x); + localPointB.y = localPointB1.y + s * (localPointB2.y - localPointB1.y); + if (s == 0.0 || s == 1.0) { + this.m_type = b2SeparationFunction.e_faceB; + this.m_axis = b2Math.CrossVF(b2Math.SubtractVV(localPointB2, localPointB1), 1.0); + this.m_axis.Normalize(); + this.m_localPoint = localPointB; + tVec = this.m_axis; + tMat = transformB.R; + normalX = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + normalY = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + tVec = this.m_localPoint; + tMat = transformB.R; + pointBX = transformB.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + pointBY = transformB.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tVec = localPointA; + tMat = transformA.R; + pointAX = transformA.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + pointAY = transformA.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + sgn = (pointAX - pointBX) * normalX + (pointAY - pointBY) * normalY; + if (s < 0.0) { + this.m_axis.NegativeSelf(); + } + } + else { + this.m_type = b2SeparationFunction.e_faceA; + this.m_axis = b2Math.CrossVF(b2Math.SubtractVV(localPointA2, localPointA1), 1.0); + this.m_localPoint = localPointA; + tVec = this.m_axis; + tMat = transformA.R; + normalX = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + normalY = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + tVec = this.m_localPoint; + tMat = transformA.R; + pointAX = transformA.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + pointAY = transformA.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tVec = localPointB; + tMat = transformB.R; + pointBX = transformB.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + pointBY = transformB.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + sgn = (pointBX - pointAX) * normalX + (pointBY - pointAY) * normalY; + if (s < 0.0) { + this.m_axis.NegativeSelf(); + } + } + } + } + b2SeparationFunction.prototype.Evaluate = function (transformA, transformB) { + var axisA; + var axisB; + var localPointA; + var localPointB; + var pointA; + var pointB; + var separation = 0; + var normal; + switch (this.m_type) { + case b2SeparationFunction.e_points: + { + axisA = b2Math.MulTMV(transformA.R, this.m_axis); + axisB = b2Math.MulTMV(transformB.R, this.m_axis.GetNegative()); + localPointA = this.m_proxyA.GetSupportVertex(axisA); + localPointB = this.m_proxyB.GetSupportVertex(axisB); + pointA = b2Math.MulX(transformA, localPointA); + pointB = b2Math.MulX(transformB, localPointB); + separation = (pointB.x - pointA.x) * this.m_axis.x + (pointB.y - pointA.y) * this.m_axis.y; + return separation; + } + case b2SeparationFunction.e_faceA: + { + normal = b2Math.MulMV(transformA.R, this.m_axis); + pointA = b2Math.MulX(transformA, this.m_localPoint); + axisB = b2Math.MulTMV(transformB.R, normal.GetNegative()); + localPointB = this.m_proxyB.GetSupportVertex(axisB); + pointB = b2Math.MulX(transformB, localPointB); + separation = (pointB.x - pointA.x) * normal.x + (pointB.y - pointA.y) * normal.y; + return separation; + } + case b2SeparationFunction.e_faceB: + { + normal = b2Math.MulMV(transformB.R, this.m_axis); + pointB = b2Math.MulX(transformB, this.m_localPoint); + axisA = b2Math.MulTMV(transformA.R, normal.GetNegative()); + localPointA = this.m_proxyA.GetSupportVertex(axisA); + pointA = b2Math.MulX(transformA, localPointA); + separation = (pointA.x - pointB.x) * normal.x + (pointA.y - pointB.y) * normal.y; + return separation; + } + default: + b2Settings.b2Assert(false); + return 0.0; + } + } + Box2D.postDefs.push(function () { + Box2D.Collision.b2SeparationFunction.e_points = 0x01; + Box2D.Collision.b2SeparationFunction.e_faceA = 0x02; + Box2D.Collision.b2SeparationFunction.e_faceB = 0x04; + }); + b2Simplex.b2Simplex = function () { + this.m_v1 = new b2SimplexVertex(); + this.m_v2 = new b2SimplexVertex(); + this.m_v3 = new b2SimplexVertex(); + this.m_vertices = new Vector(3); + }; + b2Simplex.prototype.b2Simplex = function () { + this.m_vertices[0] = this.m_v1; + this.m_vertices[1] = this.m_v2; + this.m_vertices[2] = this.m_v3; + } + b2Simplex.prototype.ReadCache = function (cache, proxyA, transformA, proxyB, transformB) { + b2Settings.b2Assert(0 <= cache.count && cache.count <= 3); + var wALocal; + var wBLocal; + this.m_count = cache.count; + var vertices = this.m_vertices; + for (var i = 0; i < this.m_count; i++) { + var v = vertices[i]; + v.indexA = cache.indexA[i]; + v.indexB = cache.indexB[i]; + wALocal = proxyA.GetVertex(v.indexA); + wBLocal = proxyB.GetVertex(v.indexB); + v.wA = b2Math.MulX(transformA, wALocal); + v.wB = b2Math.MulX(transformB, wBLocal); + v.w = b2Math.SubtractVV(v.wB, v.wA); + v.a = 0; + } + if (this.m_count > 1) { + var metric1 = cache.metric; + var metric2 = this.GetMetric(); + if (metric2 < .5 * metric1 || 2.0 * metric1 < metric2 || metric2 < Number.MIN_VALUE) { + this.m_count = 0; + } + } + if (this.m_count == 0) { + v = vertices[0]; + v.indexA = 0; + v.indexB = 0; + wALocal = proxyA.GetVertex(0); + wBLocal = proxyB.GetVertex(0); + v.wA = b2Math.MulX(transformA, wALocal); + v.wB = b2Math.MulX(transformB, wBLocal); + v.w = b2Math.SubtractVV(v.wB, v.wA); + this.m_count = 1; + } + } + b2Simplex.prototype.WriteCache = function (cache) { + cache.metric = this.GetMetric(); + cache.count = Box2D.parseUInt(this.m_count); + var vertices = this.m_vertices; + for (var i = 0; i < this.m_count; i++) { + cache.indexA[i] = Box2D.parseUInt(vertices[i].indexA); + cache.indexB[i] = Box2D.parseUInt(vertices[i].indexB); + } + } + b2Simplex.prototype.GetSearchDirection = function () { + switch (this.m_count) { + case 1: + return this.m_v1.w.GetNegative(); + case 2: + { + var e12 = b2Math.SubtractVV(this.m_v2.w, this.m_v1.w); + var sgn = b2Math.CrossVV(e12, this.m_v1.w.GetNegative()); + if (sgn > 0.0) { + return b2Math.CrossFV(1.0, e12); + } + else { + return b2Math.CrossVF(e12, 1.0); + } + } + default: + b2Settings.b2Assert(false); + return new b2Vec2(); + } + } + b2Simplex.prototype.GetClosestPoint = function () { + switch (this.m_count) { + case 0: + b2Settings.b2Assert(false); + return new b2Vec2(); + case 1: + return this.m_v1.w; + case 2: + return new b2Vec2(this.m_v1.a * this.m_v1.w.x + this.m_v2.a * this.m_v2.w.x, this.m_v1.a * this.m_v1.w.y + this.m_v2.a * this.m_v2.w.y); + default: + b2Settings.b2Assert(false); + return new b2Vec2(); + } + } + b2Simplex.prototype.GetWitnessPoints = function (pA, pB) { + switch (this.m_count) { + case 0: + b2Settings.b2Assert(false); + break; + case 1: + pA.SetV(this.m_v1.wA); + pB.SetV(this.m_v1.wB); + break; + case 2: + pA.x = this.m_v1.a * this.m_v1.wA.x + this.m_v2.a * this.m_v2.wA.x; + pA.y = this.m_v1.a * this.m_v1.wA.y + this.m_v2.a * this.m_v2.wA.y; + pB.x = this.m_v1.a * this.m_v1.wB.x + this.m_v2.a * this.m_v2.wB.x; + pB.y = this.m_v1.a * this.m_v1.wB.y + this.m_v2.a * this.m_v2.wB.y; + break; + case 3: + pB.x = pA.x = this.m_v1.a * this.m_v1.wA.x + this.m_v2.a * this.m_v2.wA.x + this.m_v3.a * this.m_v3.wA.x; + pB.y = pA.y = this.m_v1.a * this.m_v1.wA.y + this.m_v2.a * this.m_v2.wA.y + this.m_v3.a * this.m_v3.wA.y; + break; + default: + b2Settings.b2Assert(false); + break; + } + } + b2Simplex.prototype.GetMetric = function () { + switch (this.m_count) { + case 0: + b2Settings.b2Assert(false); + return 0.0; + case 1: + return 0.0; + case 2: + return b2Math.SubtractVV(this.m_v1.w, this.m_v2.w).Length(); + case 3: + return b2Math.CrossVV(b2Math.SubtractVV(this.m_v2.w, this.m_v1.w), b2Math.SubtractVV(this.m_v3.w, this.m_v1.w)); + default: + b2Settings.b2Assert(false); + return 0.0; + } + } + b2Simplex.prototype.Solve2 = function () { + var w1 = this.m_v1.w; + var w2 = this.m_v2.w; + var e12 = b2Math.SubtractVV(w2, w1); + var d12_2 = (-(w1.x * e12.x + w1.y * e12.y)); + if (d12_2 <= 0.0) { + this.m_v1.a = 1.0; + this.m_count = 1; + return; + } + var d12_1 = (w2.x * e12.x + w2.y * e12.y); + if (d12_1 <= 0.0) { + this.m_v2.a = 1.0; + this.m_count = 1; + this.m_v1.Set(this.m_v2); + return; + } + var inv_d12 = 1.0 / (d12_1 + d12_2); + this.m_v1.a = d12_1 * inv_d12; + this.m_v2.a = d12_2 * inv_d12; + this.m_count = 2; + } + b2Simplex.prototype.Solve3 = function () { + var w1 = this.m_v1.w; + var w2 = this.m_v2.w; + var w3 = this.m_v3.w; + var e12 = b2Math.SubtractVV(w2, w1); + var w1e12 = b2Math.Dot(w1, e12); + var w2e12 = b2Math.Dot(w2, e12); + var d12_1 = w2e12; + var d12_2 = (-w1e12); + var e13 = b2Math.SubtractVV(w3, w1); + var w1e13 = b2Math.Dot(w1, e13); + var w3e13 = b2Math.Dot(w3, e13); + var d13_1 = w3e13; + var d13_2 = (-w1e13); + var e23 = b2Math.SubtractVV(w3, w2); + var w2e23 = b2Math.Dot(w2, e23); + var w3e23 = b2Math.Dot(w3, e23); + var d23_1 = w3e23; + var d23_2 = (-w2e23); + var n123 = b2Math.CrossVV(e12, e13); + var d123_1 = n123 * b2Math.CrossVV(w2, w3); + var d123_2 = n123 * b2Math.CrossVV(w3, w1); + var d123_3 = n123 * b2Math.CrossVV(w1, w2); + if (d12_2 <= 0.0 && d13_2 <= 0.0) { + this.m_v1.a = 1.0; + this.m_count = 1; + return; + } + if (d12_1 > 0.0 && d12_2 > 0.0 && d123_3 <= 0.0) { + var inv_d12 = 1.0 / (d12_1 + d12_2); + this.m_v1.a = d12_1 * inv_d12; + this.m_v2.a = d12_2 * inv_d12; + this.m_count = 2; + return; + } + if (d13_1 > 0.0 && d13_2 > 0.0 && d123_2 <= 0.0) { + var inv_d13 = 1.0 / (d13_1 + d13_2); + this.m_v1.a = d13_1 * inv_d13; + this.m_v3.a = d13_2 * inv_d13; + this.m_count = 2; + this.m_v2.Set(this.m_v3); + return; + } + if (d12_1 <= 0.0 && d23_2 <= 0.0) { + this.m_v2.a = 1.0; + this.m_count = 1; + this.m_v1.Set(this.m_v2); + return; + } + if (d13_1 <= 0.0 && d23_1 <= 0.0) { + this.m_v3.a = 1.0; + this.m_count = 1; + this.m_v1.Set(this.m_v3); + return; + } + if (d23_1 > 0.0 && d23_2 > 0.0 && d123_1 <= 0.0) { + var inv_d23 = 1.0 / (d23_1 + d23_2); + this.m_v2.a = d23_1 * inv_d23; + this.m_v3.a = d23_2 * inv_d23; + this.m_count = 2; + this.m_v1.Set(this.m_v3); + return; + } + var inv_d123 = 1.0 / (d123_1 + d123_2 + d123_3); + this.m_v1.a = d123_1 * inv_d123; + this.m_v2.a = d123_2 * inv_d123; + this.m_v3.a = d123_3 * inv_d123; + this.m_count = 3; + } + b2SimplexCache.b2SimplexCache = function () { + this.indexA = new Vector_a2j_Number(3); + this.indexB = new Vector_a2j_Number(3); + }; + b2SimplexVertex.b2SimplexVertex = function () {}; + b2SimplexVertex.prototype.Set = function (other) { + this.wA.SetV(other.wA); + this.wB.SetV(other.wB); + this.w.SetV(other.w); + this.a = other.a; + this.indexA = other.indexA; + this.indexB = other.indexB; + } + b2TimeOfImpact.b2TimeOfImpact = function () {}; + b2TimeOfImpact.TimeOfImpact = function (input) { + ++b2TimeOfImpact.b2_toiCalls; + var proxyA = input.proxyA; + var proxyB = input.proxyB; + var sweepA = input.sweepA; + var sweepB = input.sweepB; + b2Settings.b2Assert(sweepA.t0 == sweepB.t0); + b2Settings.b2Assert(1.0 - sweepA.t0 > Number.MIN_VALUE); + var radius = proxyA.m_radius + proxyB.m_radius; + var tolerance = input.tolerance; + var alpha = 0.0; + var k_maxIterations = 1000; + var iter = 0; + var target = 0.0; + b2TimeOfImpact.s_cache.count = 0; + b2TimeOfImpact.s_distanceInput.useRadii = false; + for (;;) { + sweepA.GetTransform(b2TimeOfImpact.s_xfA, alpha); + sweepB.GetTransform(b2TimeOfImpact.s_xfB, alpha); + b2TimeOfImpact.s_distanceInput.proxyA = proxyA; + b2TimeOfImpact.s_distanceInput.proxyB = proxyB; + b2TimeOfImpact.s_distanceInput.transformA = b2TimeOfImpact.s_xfA; + b2TimeOfImpact.s_distanceInput.transformB = b2TimeOfImpact.s_xfB; + b2Distance.Distance(b2TimeOfImpact.s_distanceOutput, b2TimeOfImpact.s_cache, b2TimeOfImpact.s_distanceInput); + if (b2TimeOfImpact.s_distanceOutput.distance <= 0.0) { + alpha = 1.0; + break; + } + b2TimeOfImpact.s_fcn.Initialize(b2TimeOfImpact.s_cache, proxyA, b2TimeOfImpact.s_xfA, proxyB, b2TimeOfImpact.s_xfB); + var separation = b2TimeOfImpact.s_fcn.Evaluate(b2TimeOfImpact.s_xfA, b2TimeOfImpact.s_xfB); + if (separation <= 0.0) { + alpha = 1.0; + break; + } + if (iter == 0) { + if (separation > radius) { + target = b2Math.Max(radius - tolerance, 0.75 * radius); + } + else { + target = b2Math.Max(separation - tolerance, 0.02 * radius); + } + } + if (separation - target < 0.5 * tolerance) { + if (iter == 0) { + alpha = 1.0; + break; + } + break; + } + var newAlpha = alpha; { + var x1 = alpha; + var x2 = 1.0; + var f1 = separation; + sweepA.GetTransform(b2TimeOfImpact.s_xfA, x2); + sweepB.GetTransform(b2TimeOfImpact.s_xfB, x2); + var f2 = b2TimeOfImpact.s_fcn.Evaluate(b2TimeOfImpact.s_xfA, b2TimeOfImpact.s_xfB); + if (f2 >= target) { + alpha = 1.0; + break; + } + var rootIterCount = 0; + for (;;) { + var x = 0; + if (rootIterCount & 1) { + x = x1 + (target - f1) * (x2 - x1) / (f2 - f1); + } + else { + x = 0.5 * (x1 + x2); + } + sweepA.GetTransform(b2TimeOfImpact.s_xfA, x); + sweepB.GetTransform(b2TimeOfImpact.s_xfB, x); + var f = b2TimeOfImpact.s_fcn.Evaluate(b2TimeOfImpact.s_xfA, b2TimeOfImpact.s_xfB); + if (b2Math.Abs(f - target) < 0.025 * tolerance) { + newAlpha = x; + break; + } + if (f > target) { + x1 = x; + f1 = f; + } + else { + x2 = x; + f2 = f; + }++rootIterCount; + ++b2TimeOfImpact.b2_toiRootIters; + if (rootIterCount == 50) { + break; + } + } + b2TimeOfImpact.b2_toiMaxRootIters = b2Math.Max(b2TimeOfImpact.b2_toiMaxRootIters, rootIterCount); + } + if (newAlpha < (1.0 + 100.0 * Number.MIN_VALUE) * alpha) { + break; + } + alpha = newAlpha; + iter++; + ++b2TimeOfImpact.b2_toiIters; + if (iter == k_maxIterations) { + break; + } + } + b2TimeOfImpact.b2_toiMaxIters = b2Math.Max(b2TimeOfImpact.b2_toiMaxIters, iter); + return alpha; + } + Box2D.postDefs.push(function () { + Box2D.Collision.b2TimeOfImpact.b2_toiCalls = 0; + Box2D.Collision.b2TimeOfImpact.b2_toiIters = 0; + Box2D.Collision.b2TimeOfImpact.b2_toiMaxIters = 0; + Box2D.Collision.b2TimeOfImpact.b2_toiRootIters = 0; + Box2D.Collision.b2TimeOfImpact.b2_toiMaxRootIters = 0; + Box2D.Collision.b2TimeOfImpact.s_cache = new b2SimplexCache(); + Box2D.Collision.b2TimeOfImpact.s_distanceInput = new b2DistanceInput(); + Box2D.Collision.b2TimeOfImpact.s_xfA = new b2Transform(); + Box2D.Collision.b2TimeOfImpact.s_xfB = new b2Transform(); + Box2D.Collision.b2TimeOfImpact.s_fcn = new b2SeparationFunction(); + Box2D.Collision.b2TimeOfImpact.s_distanceOutput = new b2DistanceOutput(); + }); + b2TOIInput.b2TOIInput = function () { + this.proxyA = new b2DistanceProxy(); + this.proxyB = new b2DistanceProxy(); + this.sweepA = new b2Sweep(); + this.sweepB = new b2Sweep(); + }; + b2WorldManifold.b2WorldManifold = function () { + this.m_normal = new b2Vec2(); + }; + b2WorldManifold.prototype.b2WorldManifold = function () { + this.m_points = new Vector(b2Settings.b2_maxManifoldPoints); + for (var i = 0; i < b2Settings.b2_maxManifoldPoints; i++) { + this.m_points[i] = new b2Vec2(); + } + } + b2WorldManifold.prototype.Initialize = function (manifold, xfA, radiusA, xfB, radiusB) { + if (radiusA === undefined) radiusA = 0; + if (radiusB === undefined) radiusB = 0; + if (manifold.m_pointCount == 0) { + return; + } + var i = 0; + var tVec; + var tMat; + var normalX = 0; + var normalY = 0; + var planePointX = 0; + var planePointY = 0; + var clipPointX = 0; + var clipPointY = 0; + switch (manifold.m_type) { + case b2Manifold.e_circles: + { + tMat = xfA.R; + tVec = manifold.m_localPoint; + var pointAX = xfA.position.x + tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + var pointAY = xfA.position.y + tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + tMat = xfB.R; + tVec = manifold.m_points[0].m_localPoint; + var pointBX = xfB.position.x + tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + var pointBY = xfB.position.y + tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + var dX = pointBX - pointAX; + var dY = pointBY - pointAY; + var d2 = dX * dX + dY * dY; + if (d2 > Number.MIN_VALUE * Number.MIN_VALUE) { + var d = Math.sqrt(d2); + this.m_normal.x = dX / d; + this.m_normal.y = dY / d; + } + else { + this.m_normal.x = 1; + this.m_normal.y = 0; + } + var cAX = pointAX + radiusA * this.m_normal.x; + var cAY = pointAY + radiusA * this.m_normal.y; + var cBX = pointBX - radiusB * this.m_normal.x; + var cBY = pointBY - radiusB * this.m_normal.y; + this.m_points[0].x = 0.5 * (cAX + cBX); + this.m_points[0].y = 0.5 * (cAY + cBY); + } + break; + case b2Manifold.e_faceA: + { + tMat = xfA.R; + tVec = manifold.m_localPlaneNormal; + normalX = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + normalY = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + tMat = xfA.R; + tVec = manifold.m_localPoint; + planePointX = xfA.position.x + tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + planePointY = xfA.position.y + tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + this.m_normal.x = normalX; + this.m_normal.y = normalY; + for (i = 0; + i < manifold.m_pointCount; i++) { + tMat = xfB.R; + tVec = manifold.m_points[i].m_localPoint; + clipPointX = xfB.position.x + tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + clipPointY = xfB.position.y + tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + this.m_points[i].x = clipPointX + 0.5 * (radiusA - (clipPointX - planePointX) * normalX - (clipPointY - planePointY) * normalY - radiusB) * normalX; + this.m_points[i].y = clipPointY + 0.5 * (radiusA - (clipPointX - planePointX) * normalX - (clipPointY - planePointY) * normalY - radiusB) * normalY; + } + } + break; + case b2Manifold.e_faceB: + { + tMat = xfB.R; + tVec = manifold.m_localPlaneNormal; + normalX = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + normalY = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + tMat = xfB.R; + tVec = manifold.m_localPoint; + planePointX = xfB.position.x + tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + planePointY = xfB.position.y + tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + this.m_normal.x = (-normalX); + this.m_normal.y = (-normalY); + for (i = 0; + i < manifold.m_pointCount; i++) { + tMat = xfA.R; + tVec = manifold.m_points[i].m_localPoint; + clipPointX = xfA.position.x + tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + clipPointY = xfA.position.y + tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + this.m_points[i].x = clipPointX + 0.5 * (radiusB - (clipPointX - planePointX) * normalX - (clipPointY - planePointY) * normalY - radiusA) * normalX; + this.m_points[i].y = clipPointY + 0.5 * (radiusB - (clipPointX - planePointX) * normalX - (clipPointY - planePointY) * normalY - radiusA) * normalY; + } + } + break; + } + } + ClipVertex.ClipVertex = function () { + this.v = new b2Vec2(); + this.id = new b2ContactID(); + }; + ClipVertex.prototype.Set = function (other) { + this.v.SetV(other.v); + this.id.Set(other.id); + } + Features.Features = function () {}; + Object.defineProperty(Features.prototype, 'referenceEdge', { + enumerable: false, + configurable: true, + get: function () { + return this._referenceEdge; + } + }); + Object.defineProperty(Features.prototype, 'referenceEdge', { + enumerable: false, + configurable: true, + set: function (value) { + if (value === undefined) value = 0; + this._referenceEdge = value; + this._m_id._key = (this._m_id._key & 0xffffff00) | (this._referenceEdge & 0x000000ff); + } + }); + Object.defineProperty(Features.prototype, 'incidentEdge', { + enumerable: false, + configurable: true, + get: function () { + return this._incidentEdge; + } + }); + Object.defineProperty(Features.prototype, 'incidentEdge', { + enumerable: false, + configurable: true, + set: function (value) { + if (value === undefined) value = 0; + this._incidentEdge = value; + this._m_id._key = (this._m_id._key & 0xffff00ff) | ((this._incidentEdge << 8) & 0x0000ff00); + } + }); + Object.defineProperty(Features.prototype, 'incidentVertex', { + enumerable: false, + configurable: true, + get: function () { + return this._incidentVertex; + } + }); + Object.defineProperty(Features.prototype, 'incidentVertex', { + enumerable: false, + configurable: true, + set: function (value) { + if (value === undefined) value = 0; + this._incidentVertex = value; + this._m_id._key = (this._m_id._key & 0xff00ffff) | ((this._incidentVertex << 16) & 0x00ff0000); + } + }); + Object.defineProperty(Features.prototype, 'flip', { + enumerable: false, + configurable: true, + get: function () { + return this._flip; + } + }); + Object.defineProperty(Features.prototype, 'flip', { + enumerable: false, + configurable: true, + set: function (value) { + if (value === undefined) value = 0; + this._flip = value; + this._m_id._key = (this._m_id._key & 0x00ffffff) | ((this._flip << 24) & 0xff000000); + } + }); +})(); +(function () { + var b2Color = Box2D.Common.b2Color, + b2internal = Box2D.Common.b2internal, + b2Settings = Box2D.Common.b2Settings, + b2CircleShape = Box2D.Collision.Shapes.b2CircleShape, + b2EdgeChainDef = Box2D.Collision.Shapes.b2EdgeChainDef, + b2EdgeShape = Box2D.Collision.Shapes.b2EdgeShape, + b2MassData = Box2D.Collision.Shapes.b2MassData, + b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape, + b2Shape = Box2D.Collision.Shapes.b2Shape, + b2Mat22 = Box2D.Common.Math.b2Mat22, + b2Mat33 = Box2D.Common.Math.b2Mat33, + b2Math = Box2D.Common.Math.b2Math, + b2Sweep = Box2D.Common.Math.b2Sweep, + b2Transform = Box2D.Common.Math.b2Transform, + b2Vec2 = Box2D.Common.Math.b2Vec2, + b2Vec3 = Box2D.Common.Math.b2Vec3, + b2Body = Box2D.Dynamics.b2Body, + b2BodyDef = Box2D.Dynamics.b2BodyDef, + b2ContactFilter = Box2D.Dynamics.b2ContactFilter, + b2ContactImpulse = Box2D.Dynamics.b2ContactImpulse, + b2ContactListener = Box2D.Dynamics.b2ContactListener, + b2ContactManager = Box2D.Dynamics.b2ContactManager, + b2DebugDraw = Box2D.Dynamics.b2DebugDraw, + b2DestructionListener = Box2D.Dynamics.b2DestructionListener, + b2FilterData = Box2D.Dynamics.b2FilterData, + b2Fixture = Box2D.Dynamics.b2Fixture, + b2FixtureDef = Box2D.Dynamics.b2FixtureDef, + b2Island = Box2D.Dynamics.b2Island, + b2TimeStep = Box2D.Dynamics.b2TimeStep, + b2World = Box2D.Dynamics.b2World, + b2AABB = Box2D.Collision.b2AABB, + b2Bound = Box2D.Collision.b2Bound, + b2BoundValues = Box2D.Collision.b2BoundValues, + b2Collision = Box2D.Collision.b2Collision, + b2ContactID = Box2D.Collision.b2ContactID, + b2ContactPoint = Box2D.Collision.b2ContactPoint, + b2Distance = Box2D.Collision.b2Distance, + b2DistanceInput = Box2D.Collision.b2DistanceInput, + b2DistanceOutput = Box2D.Collision.b2DistanceOutput, + b2DistanceProxy = Box2D.Collision.b2DistanceProxy, + b2DynamicTree = Box2D.Collision.b2DynamicTree, + b2DynamicTreeBroadPhase = Box2D.Collision.b2DynamicTreeBroadPhase, + b2DynamicTreeNode = Box2D.Collision.b2DynamicTreeNode, + b2DynamicTreePair = Box2D.Collision.b2DynamicTreePair, + b2Manifold = Box2D.Collision.b2Manifold, + b2ManifoldPoint = Box2D.Collision.b2ManifoldPoint, + b2Point = Box2D.Collision.b2Point, + b2RayCastInput = Box2D.Collision.b2RayCastInput, + b2RayCastOutput = Box2D.Collision.b2RayCastOutput, + b2Segment = Box2D.Collision.b2Segment, + b2SeparationFunction = Box2D.Collision.b2SeparationFunction, + b2Simplex = Box2D.Collision.b2Simplex, + b2SimplexCache = Box2D.Collision.b2SimplexCache, + b2SimplexVertex = Box2D.Collision.b2SimplexVertex, + b2TimeOfImpact = Box2D.Collision.b2TimeOfImpact, + b2TOIInput = Box2D.Collision.b2TOIInput, + b2WorldManifold = Box2D.Collision.b2WorldManifold, + ClipVertex = Box2D.Collision.ClipVertex, + Features = Box2D.Collision.Features, + IBroadPhase = Box2D.Collision.IBroadPhase; + + Box2D.inherit(b2CircleShape, Box2D.Collision.Shapes.b2Shape); + b2CircleShape.prototype.__super = Box2D.Collision.Shapes.b2Shape.prototype; + b2CircleShape.b2CircleShape = function () { + Box2D.Collision.Shapes.b2Shape.b2Shape.apply(this, arguments); + this.m_p = new b2Vec2(); + }; + b2CircleShape.prototype.Copy = function () { + var s = new b2CircleShape(); + s.Set(this); + return s; + } + b2CircleShape.prototype.Set = function (other) { + this.__super.Set.call(this, other); + if (Box2D.is(other, b2CircleShape)) { + var other2 = (other instanceof b2CircleShape ? other : null); + this.m_p.SetV(other2.m_p); + } + } + b2CircleShape.prototype.TestPoint = function (transform, p) { + var tMat = transform.R; + var dX = transform.position.x + (tMat.col1.x * this.m_p.x + tMat.col2.x * this.m_p.y); + var dY = transform.position.y + (tMat.col1.y * this.m_p.x + tMat.col2.y * this.m_p.y); + dX = p.x - dX; + dY = p.y - dY; + return (dX * dX + dY * dY) <= this.m_radius * this.m_radius; + } + b2CircleShape.prototype.RayCast = function (output, input, transform) { + var tMat = transform.R; + var positionX = transform.position.x + (tMat.col1.x * this.m_p.x + tMat.col2.x * this.m_p.y); + var positionY = transform.position.y + (tMat.col1.y * this.m_p.x + tMat.col2.y * this.m_p.y); + var sX = input.p1.x - positionX; + var sY = input.p1.y - positionY; + var b = (sX * sX + sY * sY) - this.m_radius * this.m_radius; + var rX = input.p2.x - input.p1.x; + var rY = input.p2.y - input.p1.y; + var c = (sX * rX + sY * rY); + var rr = (rX * rX + rY * rY); + var sigma = c * c - rr * b; + if (sigma < 0.0 || rr < Number.MIN_VALUE) { + return false; + } + var a = (-(c + Math.sqrt(sigma))); + if (0.0 <= a && a <= input.maxFraction * rr) { + a /= rr; + output.fraction = a; + output.normal.x = sX + a * rX; + output.normal.y = sY + a * rY; + output.normal.Normalize(); + return true; + } + return false; + } + b2CircleShape.prototype.ComputeAABB = function (aabb, transform) { + var tMat = transform.R; + var pX = transform.position.x + (tMat.col1.x * this.m_p.x + tMat.col2.x * this.m_p.y); + var pY = transform.position.y + (tMat.col1.y * this.m_p.x + tMat.col2.y * this.m_p.y); + aabb.lowerBound.Set(pX - this.m_radius, pY - this.m_radius); + aabb.upperBound.Set(pX + this.m_radius, pY + this.m_radius); + } + b2CircleShape.prototype.ComputeMass = function (massData, density) { + if (density === undefined) density = 0; + massData.mass = density * b2Settings.b2_pi * this.m_radius * this.m_radius; + massData.center.SetV(this.m_p); + massData.I = massData.mass * (0.5 * this.m_radius * this.m_radius + (this.m_p.x * this.m_p.x + this.m_p.y * this.m_p.y)); + } + b2CircleShape.prototype.ComputeSubmergedArea = function (normal, offset, xf, c) { + if (offset === undefined) offset = 0; + var p = b2Math.MulX(xf, this.m_p); + var l = (-(b2Math.Dot(normal, p) - offset)); + if (l < (-this.m_radius) + Number.MIN_VALUE) { + return 0; + } + if (l > this.m_radius) { + c.SetV(p); + return Math.PI * this.m_radius * this.m_radius; + } + var r2 = this.m_radius * this.m_radius; + var l2 = l * l; + var area = r2 * (Math.asin(l / this.m_radius) + Math.PI / 2) + l * Math.sqrt(r2 - l2); + var com = (-2 / 3 * Math.pow(r2 - l2, 1.5) / area); + c.x = p.x + normal.x * com; + c.y = p.y + normal.y * com; + return area; + } + b2CircleShape.prototype.GetLocalPosition = function () { + return this.m_p; + } + b2CircleShape.prototype.SetLocalPosition = function (position) { + this.m_p.SetV(position); + } + b2CircleShape.prototype.GetRadius = function () { + return this.m_radius; + } + b2CircleShape.prototype.SetRadius = function (radius) { + if (radius === undefined) radius = 0; + this.m_radius = radius; + } + b2CircleShape.prototype.b2CircleShape = function (radius) { + if (radius === undefined) radius = 0; + this.__super.b2Shape.call(this); + this.m_type = b2Shape.e_circleShape; + this.m_radius = radius; + } + b2EdgeChainDef.b2EdgeChainDef = function () {}; + b2EdgeChainDef.prototype.b2EdgeChainDef = function () { + this.vertexCount = 0; + this.isALoop = true; + this.vertices = []; + } + Box2D.inherit(b2EdgeShape, Box2D.Collision.Shapes.b2Shape); + b2EdgeShape.prototype.__super = Box2D.Collision.Shapes.b2Shape.prototype; + b2EdgeShape.b2EdgeShape = function () { + Box2D.Collision.Shapes.b2Shape.b2Shape.apply(this, arguments); + this.s_supportVec = new b2Vec2(); + this.m_v1 = new b2Vec2(); + this.m_v2 = new b2Vec2(); + this.m_coreV1 = new b2Vec2(); + this.m_coreV2 = new b2Vec2(); + this.m_normal = new b2Vec2(); + this.m_direction = new b2Vec2(); + this.m_cornerDir1 = new b2Vec2(); + this.m_cornerDir2 = new b2Vec2(); + }; + b2EdgeShape.prototype.TestPoint = function (transform, p) { + return false; + } + b2EdgeShape.prototype.RayCast = function (output, input, transform) { + var tMat; + var rX = input.p2.x - input.p1.x; + var rY = input.p2.y - input.p1.y; + tMat = transform.R; + var v1X = transform.position.x + (tMat.col1.x * this.m_v1.x + tMat.col2.x * this.m_v1.y); + var v1Y = transform.position.y + (tMat.col1.y * this.m_v1.x + tMat.col2.y * this.m_v1.y); + var nX = transform.position.y + (tMat.col1.y * this.m_v2.x + tMat.col2.y * this.m_v2.y) - v1Y; + var nY = (-(transform.position.x + (tMat.col1.x * this.m_v2.x + tMat.col2.x * this.m_v2.y) - v1X)); + var k_slop = 100.0 * Number.MIN_VALUE; + var denom = (-(rX * nX + rY * nY)); + if (denom > k_slop) { + var bX = input.p1.x - v1X; + var bY = input.p1.y - v1Y; + var a = (bX * nX + bY * nY); + if (0.0 <= a && a <= input.maxFraction * denom) { + var mu2 = (-rX * bY) + rY * bX; + if ((-k_slop * denom) <= mu2 && mu2 <= denom * (1.0 + k_slop)) { + a /= denom; + output.fraction = a; + var nLen = Math.sqrt(nX * nX + nY * nY); + output.normal.x = nX / nLen; + output.normal.y = nY / nLen; + return true; + } + } + } + return false; + } + b2EdgeShape.prototype.ComputeAABB = function (aabb, transform) { + var tMat = transform.R; + var v1X = transform.position.x + (tMat.col1.x * this.m_v1.x + tMat.col2.x * this.m_v1.y); + var v1Y = transform.position.y + (tMat.col1.y * this.m_v1.x + tMat.col2.y * this.m_v1.y); + var v2X = transform.position.x + (tMat.col1.x * this.m_v2.x + tMat.col2.x * this.m_v2.y); + var v2Y = transform.position.y + (tMat.col1.y * this.m_v2.x + tMat.col2.y * this.m_v2.y); + if (v1X < v2X) { + aabb.lowerBound.x = v1X; + aabb.upperBound.x = v2X; + } + else { + aabb.lowerBound.x = v2X; + aabb.upperBound.x = v1X; + } + if (v1Y < v2Y) { + aabb.lowerBound.y = v1Y; + aabb.upperBound.y = v2Y; + } + else { + aabb.lowerBound.y = v2Y; + aabb.upperBound.y = v1Y; + } + } + b2EdgeShape.prototype.ComputeMass = function (massData, density) { + if (density === undefined) density = 0; + massData.mass = 0; + massData.center.SetV(this.m_v1); + massData.I = 0; + } + b2EdgeShape.prototype.ComputeSubmergedArea = function (normal, offset, xf, c) { + if (offset === undefined) offset = 0; + var v0 = new b2Vec2(normal.x * offset, normal.y * offset); + var v1 = b2Math.MulX(xf, this.m_v1); + var v2 = b2Math.MulX(xf, this.m_v2); + var d1 = b2Math.Dot(normal, v1) - offset; + var d2 = b2Math.Dot(normal, v2) - offset; + if (d1 > 0) { + if (d2 > 0) { + return 0; + } + else { + v1.x = (-d2 / (d1 - d2) * v1.x) + d1 / (d1 - d2) * v2.x; + v1.y = (-d2 / (d1 - d2) * v1.y) + d1 / (d1 - d2) * v2.y; + } + } + else { + if (d2 > 0) { + v2.x = (-d2 / (d1 - d2) * v1.x) + d1 / (d1 - d2) * v2.x; + v2.y = (-d2 / (d1 - d2) * v1.y) + d1 / (d1 - d2) * v2.y; + } + else {} + } + c.x = (v0.x + v1.x + v2.x) / 3; + c.y = (v0.y + v1.y + v2.y) / 3; + return 0.5 * ((v1.x - v0.x) * (v2.y - v0.y) - (v1.y - v0.y) * (v2.x - v0.x)); + } + b2EdgeShape.prototype.GetLength = function () { + return this.m_length; + } + b2EdgeShape.prototype.GetVertex1 = function () { + return this.m_v1; + } + b2EdgeShape.prototype.GetVertex2 = function () { + return this.m_v2; + } + b2EdgeShape.prototype.GetCoreVertex1 = function () { + return this.m_coreV1; + } + b2EdgeShape.prototype.GetCoreVertex2 = function () { + return this.m_coreV2; + } + b2EdgeShape.prototype.GetNormalVector = function () { + return this.m_normal; + } + b2EdgeShape.prototype.GetDirectionVector = function () { + return this.m_direction; + } + b2EdgeShape.prototype.GetCorner1Vector = function () { + return this.m_cornerDir1; + } + b2EdgeShape.prototype.GetCorner2Vector = function () { + return this.m_cornerDir2; + } + b2EdgeShape.prototype.Corner1IsConvex = function () { + return this.m_cornerConvex1; + } + b2EdgeShape.prototype.Corner2IsConvex = function () { + return this.m_cornerConvex2; + } + b2EdgeShape.prototype.GetFirstVertex = function (xf) { + var tMat = xf.R; + return new b2Vec2(xf.position.x + (tMat.col1.x * this.m_coreV1.x + tMat.col2.x * this.m_coreV1.y), xf.position.y + (tMat.col1.y * this.m_coreV1.x + tMat.col2.y * this.m_coreV1.y)); + } + b2EdgeShape.prototype.GetNextEdge = function () { + return this.m_nextEdge; + } + b2EdgeShape.prototype.GetPrevEdge = function () { + return this.m_prevEdge; + } + b2EdgeShape.prototype.Support = function (xf, dX, dY) { + if (dX === undefined) dX = 0; + if (dY === undefined) dY = 0; + var tMat = xf.R; + var v1X = xf.position.x + (tMat.col1.x * this.m_coreV1.x + tMat.col2.x * this.m_coreV1.y); + var v1Y = xf.position.y + (tMat.col1.y * this.m_coreV1.x + tMat.col2.y * this.m_coreV1.y); + var v2X = xf.position.x + (tMat.col1.x * this.m_coreV2.x + tMat.col2.x * this.m_coreV2.y); + var v2Y = xf.position.y + (tMat.col1.y * this.m_coreV2.x + tMat.col2.y * this.m_coreV2.y); + if ((v1X * dX + v1Y * dY) > (v2X * dX + v2Y * dY)) { + this.s_supportVec.x = v1X; + this.s_supportVec.y = v1Y; + } + else { + this.s_supportVec.x = v2X; + this.s_supportVec.y = v2Y; + } + return this.s_supportVec; + } + b2EdgeShape.prototype.b2EdgeShape = function (v1, v2) { + this.__super.b2Shape.call(this); + this.m_type = b2Shape.e_edgeShape; + this.m_prevEdge = null; + this.m_nextEdge = null; + this.m_v1 = v1; + this.m_v2 = v2; + this.m_direction.Set(this.m_v2.x - this.m_v1.x, this.m_v2.y - this.m_v1.y); + this.m_length = this.m_direction.Normalize(); + this.m_normal.Set(this.m_direction.y, (-this.m_direction.x)); + this.m_coreV1.Set((-b2Settings.b2_toiSlop * (this.m_normal.x - this.m_direction.x)) + this.m_v1.x, (-b2Settings.b2_toiSlop * (this.m_normal.y - this.m_direction.y)) + this.m_v1.y); + this.m_coreV2.Set((-b2Settings.b2_toiSlop * (this.m_normal.x + this.m_direction.x)) + this.m_v2.x, (-b2Settings.b2_toiSlop * (this.m_normal.y + this.m_direction.y)) + this.m_v2.y); + this.m_cornerDir1 = this.m_normal; + this.m_cornerDir2.Set((-this.m_normal.x), (-this.m_normal.y)); + } + b2EdgeShape.prototype.SetPrevEdge = function (edge, core, cornerDir, convex) { + this.m_prevEdge = edge; + this.m_coreV1 = core; + this.m_cornerDir1 = cornerDir; + this.m_cornerConvex1 = convex; + } + b2EdgeShape.prototype.SetNextEdge = function (edge, core, cornerDir, convex) { + this.m_nextEdge = edge; + this.m_coreV2 = core; + this.m_cornerDir2 = cornerDir; + this.m_cornerConvex2 = convex; + } + b2MassData.b2MassData = function () { + this.mass = 0.0; + this.center = new b2Vec2(0, 0); + this.I = 0.0; + }; + Box2D.inherit(b2PolygonShape, Box2D.Collision.Shapes.b2Shape); + b2PolygonShape.prototype.__super = Box2D.Collision.Shapes.b2Shape.prototype; + b2PolygonShape.b2PolygonShape = function () { + Box2D.Collision.Shapes.b2Shape.b2Shape.apply(this, arguments); + }; + b2PolygonShape.prototype.Copy = function () { + var s = new b2PolygonShape(); + s.Set(this); + return s; + } + b2PolygonShape.prototype.Set = function (other) { + this.__super.Set.call(this, other); + if (Box2D.is(other, b2PolygonShape)) { + var other2 = (other instanceof b2PolygonShape ? other : null); + this.m_centroid.SetV(other2.m_centroid); + this.m_vertexCount = other2.m_vertexCount; + this.Reserve(this.m_vertexCount); + for (var i = 0; i < this.m_vertexCount; i++) { + this.m_vertices[i].SetV(other2.m_vertices[i]); + this.m_normals[i].SetV(other2.m_normals[i]); + } + } + } + b2PolygonShape.prototype.SetAsArray = function (vertices, vertexCount) { + if (vertexCount === undefined) vertexCount = 0; + var v = new Vector(); + var i = 0, + tVec; + for (i = 0; + i < vertices.length; ++i) { + tVec = vertices[i]; + v.push(tVec); + } + this.SetAsVector(v, vertexCount); + } + b2PolygonShape.AsArray = function (vertices, vertexCount) { + if (vertexCount === undefined) vertexCount = 0; + var polygonShape = new b2PolygonShape(); + polygonShape.SetAsArray(vertices, vertexCount); + return polygonShape; + } + b2PolygonShape.prototype.SetAsVector = function (vertices, vertexCount) { + if (vertexCount === undefined) vertexCount = 0; + if (vertexCount == 0) vertexCount = vertices.length; + b2Settings.b2Assert(2 <= vertexCount); + this.m_vertexCount = vertexCount; + this.Reserve(vertexCount); + var i = 0; + for (i = 0; + i < this.m_vertexCount; i++) { + this.m_vertices[i].SetV(vertices[i]); + } + for (i = 0; + i < this.m_vertexCount; ++i) { + var i1 = parseInt(i); + var i2 = parseInt(i + 1 < this.m_vertexCount ? i + 1 : 0); + var edge = b2Math.SubtractVV(this.m_vertices[i2], this.m_vertices[i1]); + b2Settings.b2Assert(edge.LengthSquared() > Number.MIN_VALUE); + this.m_normals[i].SetV(b2Math.CrossVF(edge, 1.0)); + this.m_normals[i].Normalize(); + } + this.m_centroid = b2PolygonShape.ComputeCentroid(this.m_vertices, this.m_vertexCount); + } + b2PolygonShape.AsVector = function (vertices, vertexCount) { + if (vertexCount === undefined) vertexCount = 0; + var polygonShape = new b2PolygonShape(); + polygonShape.SetAsVector(vertices, vertexCount); + return polygonShape; + } + b2PolygonShape.prototype.SetAsBox = function (hx, hy) { + if (hx === undefined) hx = 0; + if (hy === undefined) hy = 0; + this.m_vertexCount = 4; + this.Reserve(4); + this.m_vertices[0].Set((-hx), (-hy)); + this.m_vertices[1].Set(hx, (-hy)); + this.m_vertices[2].Set(hx, hy); + this.m_vertices[3].Set((-hx), hy); + this.m_normals[0].Set(0.0, (-1.0)); + this.m_normals[1].Set(1.0, 0.0); + this.m_normals[2].Set(0.0, 1.0); + this.m_normals[3].Set((-1.0), 0.0); + this.m_centroid.SetZero(); + } + b2PolygonShape.AsBox = function (hx, hy) { + if (hx === undefined) hx = 0; + if (hy === undefined) hy = 0; + var polygonShape = new b2PolygonShape(); + polygonShape.SetAsBox(hx, hy); + return polygonShape; + } + b2PolygonShape.prototype.SetAsOrientedBox = function (hx, hy, center, angle) { + if (hx === undefined) hx = 0; + if (hy === undefined) hy = 0; + if (center === undefined) center = null; + if (angle === undefined) angle = 0.0; + this.m_vertexCount = 4; + this.Reserve(4); + this.m_vertices[0].Set((-hx), (-hy)); + this.m_vertices[1].Set(hx, (-hy)); + this.m_vertices[2].Set(hx, hy); + this.m_vertices[3].Set((-hx), hy); + this.m_normals[0].Set(0.0, (-1.0)); + this.m_normals[1].Set(1.0, 0.0); + this.m_normals[2].Set(0.0, 1.0); + this.m_normals[3].Set((-1.0), 0.0); + this.m_centroid = center; + var xf = new b2Transform(); + xf.position = center; + xf.R.Set(angle); + for (var i = 0; i < this.m_vertexCount; ++i) { + this.m_vertices[i] = b2Math.MulX(xf, this.m_vertices[i]); + this.m_normals[i] = b2Math.MulMV(xf.R, this.m_normals[i]); + } + } + b2PolygonShape.AsOrientedBox = function (hx, hy, center, angle) { + if (hx === undefined) hx = 0; + if (hy === undefined) hy = 0; + if (center === undefined) center = null; + if (angle === undefined) angle = 0.0; + var polygonShape = new b2PolygonShape(); + polygonShape.SetAsOrientedBox(hx, hy, center, angle); + return polygonShape; + } + b2PolygonShape.prototype.SetAsEdge = function (v1, v2) { + this.m_vertexCount = 2; + this.Reserve(2); + this.m_vertices[0].SetV(v1); + this.m_vertices[1].SetV(v2); + this.m_centroid.x = 0.5 * (v1.x + v2.x); + this.m_centroid.y = 0.5 * (v1.y + v2.y); + this.m_normals[0] = b2Math.CrossVF(b2Math.SubtractVV(v2, v1), 1.0); + this.m_normals[0].Normalize(); + this.m_normals[1].x = (-this.m_normals[0].x); + this.m_normals[1].y = (-this.m_normals[0].y); + } + b2PolygonShape.AsEdge = function (v1, v2) { + var polygonShape = new b2PolygonShape(); + polygonShape.SetAsEdge(v1, v2); + return polygonShape; + } + b2PolygonShape.prototype.TestPoint = function (xf, p) { + var tVec; + var tMat = xf.R; + var tX = p.x - xf.position.x; + var tY = p.y - xf.position.y; + var pLocalX = (tX * tMat.col1.x + tY * tMat.col1.y); + var pLocalY = (tX * tMat.col2.x + tY * tMat.col2.y); + for (var i = 0; i < this.m_vertexCount; ++i) { + tVec = this.m_vertices[i]; + tX = pLocalX - tVec.x; + tY = pLocalY - tVec.y; + tVec = this.m_normals[i]; + var dot = (tVec.x * tX + tVec.y * tY); + if (dot > 0.0) { + return false; + } + } + return true; + } + b2PolygonShape.prototype.RayCast = function (output, input, transform) { + var lower = 0.0; + var upper = input.maxFraction; + var tX = 0; + var tY = 0; + var tMat; + var tVec; + tX = input.p1.x - transform.position.x; + tY = input.p1.y - transform.position.y; + tMat = transform.R; + var p1X = (tX * tMat.col1.x + tY * tMat.col1.y); + var p1Y = (tX * tMat.col2.x + tY * tMat.col2.y); + tX = input.p2.x - transform.position.x; + tY = input.p2.y - transform.position.y; + tMat = transform.R; + var p2X = (tX * tMat.col1.x + tY * tMat.col1.y); + var p2Y = (tX * tMat.col2.x + tY * tMat.col2.y); + var dX = p2X - p1X; + var dY = p2Y - p1Y; + var index = parseInt((-1)); + for (var i = 0; i < this.m_vertexCount; ++i) { + tVec = this.m_vertices[i]; + tX = tVec.x - p1X; + tY = tVec.y - p1Y; + tVec = this.m_normals[i]; + var numerator = (tVec.x * tX + tVec.y * tY); + var denominator = (tVec.x * dX + tVec.y * dY); + if (denominator == 0.0) { + if (numerator < 0.0) { + return false; + } + } + else { + if (denominator < 0.0 && numerator < lower * denominator) { + lower = numerator / denominator; + index = i; + } + else if (denominator > 0.0 && numerator < upper * denominator) { + upper = numerator / denominator; + } + } + if (upper < lower - Number.MIN_VALUE) { + return false; + } + } + if (index >= 0) { + output.fraction = lower; + tMat = transform.R; + tVec = this.m_normals[index]; + output.normal.x = (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + output.normal.y = (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + return true; + } + return false; + } + b2PolygonShape.prototype.ComputeAABB = function (aabb, xf) { + var tMat = xf.R; + var tVec = this.m_vertices[0]; + var lowerX = xf.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + var lowerY = xf.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + var upperX = lowerX; + var upperY = lowerY; + for (var i = 1; i < this.m_vertexCount; ++i) { + tVec = this.m_vertices[i]; + var vX = xf.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + var vY = xf.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + lowerX = lowerX < vX ? lowerX : vX; + lowerY = lowerY < vY ? lowerY : vY; + upperX = upperX > vX ? upperX : vX; + upperY = upperY > vY ? upperY : vY; + } + aabb.lowerBound.x = lowerX - this.m_radius; + aabb.lowerBound.y = lowerY - this.m_radius; + aabb.upperBound.x = upperX + this.m_radius; + aabb.upperBound.y = upperY + this.m_radius; + } + b2PolygonShape.prototype.ComputeMass = function (massData, density) { + if (density === undefined) density = 0; + if (this.m_vertexCount == 2) { + massData.center.x = 0.5 * (this.m_vertices[0].x + this.m_vertices[1].x); + massData.center.y = 0.5 * (this.m_vertices[0].y + this.m_vertices[1].y); + massData.mass = 0.0; + massData.I = 0.0; + return; + } + var centerX = 0.0; + var centerY = 0.0; + var area = 0.0; + var I = 0.0; + var p1X = 0.0; + var p1Y = 0.0; + var k_inv3 = 1.0 / 3.0; + for (var i = 0; i < this.m_vertexCount; ++i) { + var p2 = this.m_vertices[i]; + var p3 = i + 1 < this.m_vertexCount ? this.m_vertices[parseInt(i + 1)] : this.m_vertices[0]; + var e1X = p2.x - p1X; + var e1Y = p2.y - p1Y; + var e2X = p3.x - p1X; + var e2Y = p3.y - p1Y; + var D = e1X * e2Y - e1Y * e2X; + var triangleArea = 0.5 * D;area += triangleArea; + centerX += triangleArea * k_inv3 * (p1X + p2.x + p3.x); + centerY += triangleArea * k_inv3 * (p1Y + p2.y + p3.y); + var px = p1X; + var py = p1Y; + var ex1 = e1X; + var ey1 = e1Y; + var ex2 = e2X; + var ey2 = e2Y; + var intx2 = k_inv3 * (0.25 * (ex1 * ex1 + ex2 * ex1 + ex2 * ex2) + (px * ex1 + px * ex2)) + 0.5 * px * px; + var inty2 = k_inv3 * (0.25 * (ey1 * ey1 + ey2 * ey1 + ey2 * ey2) + (py * ey1 + py * ey2)) + 0.5 * py * py;I += D * (intx2 + inty2); + } + massData.mass = density * area; + centerX *= 1.0 / area; + centerY *= 1.0 / area; + massData.center.Set(centerX, centerY); + massData.I = density * I; + } + b2PolygonShape.prototype.ComputeSubmergedArea = function (normal, offset, xf, c) { + if (offset === undefined) offset = 0; + var normalL = b2Math.MulTMV(xf.R, normal); + var offsetL = offset - b2Math.Dot(normal, xf.position); + var depths = new Vector_a2j_Number(); + var diveCount = 0; + var intoIndex = parseInt((-1)); + var outoIndex = parseInt((-1)); + var lastSubmerged = false; + var i = 0; + for (i = 0; + i < this.m_vertexCount; ++i) { + depths[i] = b2Math.Dot(normalL, this.m_vertices[i]) - offsetL; + var isSubmerged = depths[i] < (-Number.MIN_VALUE); + if (i > 0) { + if (isSubmerged) { + if (!lastSubmerged) { + intoIndex = i - 1; + diveCount++; + } + } + else { + if (lastSubmerged) { + outoIndex = i - 1; + diveCount++; + } + } + } + lastSubmerged = isSubmerged; + } + switch (diveCount) { + case 0: + if (lastSubmerged) { + var md = new b2MassData(); + this.ComputeMass(md, 1); + c.SetV(b2Math.MulX(xf, md.center)); + return md.mass; + } + else { + return 0; + } + break; + case 1: + if (intoIndex == (-1)) { + intoIndex = this.m_vertexCount - 1; + } + else { + outoIndex = this.m_vertexCount - 1; + } + break; + } + var intoIndex2 = parseInt((intoIndex + 1) % this.m_vertexCount); + var outoIndex2 = parseInt((outoIndex + 1) % this.m_vertexCount); + var intoLamdda = (0 - depths[intoIndex]) / (depths[intoIndex2] - depths[intoIndex]); + var outoLamdda = (0 - depths[outoIndex]) / (depths[outoIndex2] - depths[outoIndex]); + var intoVec = new b2Vec2(this.m_vertices[intoIndex].x * (1 - intoLamdda) + this.m_vertices[intoIndex2].x * intoLamdda, this.m_vertices[intoIndex].y * (1 - intoLamdda) + this.m_vertices[intoIndex2].y * intoLamdda); + var outoVec = new b2Vec2(this.m_vertices[outoIndex].x * (1 - outoLamdda) + this.m_vertices[outoIndex2].x * outoLamdda, this.m_vertices[outoIndex].y * (1 - outoLamdda) + this.m_vertices[outoIndex2].y * outoLamdda); + var area = 0; + var center = new b2Vec2(); + var p2 = this.m_vertices[intoIndex2]; + var p3; + i = intoIndex2; + while (i != outoIndex2) { + i = (i + 1) % this.m_vertexCount; + if (i == outoIndex2) p3 = outoVec; + else p3 = this.m_vertices[i]; + var triangleArea = 0.5 * ((p2.x - intoVec.x) * (p3.y - intoVec.y) - (p2.y - intoVec.y) * (p3.x - intoVec.x)); + area += triangleArea; + center.x += triangleArea * (intoVec.x + p2.x + p3.x) / 3; + center.y += triangleArea * (intoVec.y + p2.y + p3.y) / 3; + p2 = p3; + } + center.Multiply(1 / area); + c.SetV(b2Math.MulX(xf, center)); + return area; + } + b2PolygonShape.prototype.GetVertexCount = function () { + return this.m_vertexCount; + } + b2PolygonShape.prototype.GetVertices = function () { + return this.m_vertices; + } + b2PolygonShape.prototype.GetNormals = function () { + return this.m_normals; + } + b2PolygonShape.prototype.GetSupport = function (d) { + var bestIndex = 0; + var bestValue = this.m_vertices[0].x * d.x + this.m_vertices[0].y * d.y; + for (var i = 1; i < this.m_vertexCount; ++i) { + var value = this.m_vertices[i].x * d.x + this.m_vertices[i].y * d.y; + if (value > bestValue) { + bestIndex = i; + bestValue = value; + } + } + return bestIndex; + } + b2PolygonShape.prototype.GetSupportVertex = function (d) { + var bestIndex = 0; + var bestValue = this.m_vertices[0].x * d.x + this.m_vertices[0].y * d.y; + for (var i = 1; i < this.m_vertexCount; ++i) { + var value = this.m_vertices[i].x * d.x + this.m_vertices[i].y * d.y; + if (value > bestValue) { + bestIndex = i; + bestValue = value; + } + } + return this.m_vertices[bestIndex]; + } + b2PolygonShape.prototype.Validate = function () { + return false; + } + b2PolygonShape.prototype.b2PolygonShape = function () { + this.__super.b2Shape.call(this); + this.m_type = b2Shape.e_polygonShape; + this.m_centroid = new b2Vec2(); + this.m_vertices = new Vector(); + this.m_normals = new Vector(); + } + b2PolygonShape.prototype.Reserve = function (count) { + if (count === undefined) count = 0; + for (var i = parseInt(this.m_vertices.length); i < count; i++) { + this.m_vertices[i] = new b2Vec2(); + this.m_normals[i] = new b2Vec2(); + } + } + b2PolygonShape.ComputeCentroid = function (vs, count) { + if (count === undefined) count = 0; + var c = new b2Vec2(); + var area = 0.0; + var p1X = 0.0; + var p1Y = 0.0; + var inv3 = 1.0 / 3.0; + for (var i = 0; i < count; ++i) { + var p2 = vs[i]; + var p3 = i + 1 < count ? vs[parseInt(i + 1)] : vs[0]; + var e1X = p2.x - p1X; + var e1Y = p2.y - p1Y; + var e2X = p3.x - p1X; + var e2Y = p3.y - p1Y; + var D = (e1X * e2Y - e1Y * e2X); + var triangleArea = 0.5 * D;area += triangleArea; + c.x += triangleArea * inv3 * (p1X + p2.x + p3.x); + c.y += triangleArea * inv3 * (p1Y + p2.y + p3.y); + } + c.x *= 1.0 / area; + c.y *= 1.0 / area; + return c; + } + b2PolygonShape.ComputeOBB = function (obb, vs, count) { + if (count === undefined) count = 0; + var i = 0; + var p = new Vector(count + 1); + for (i = 0; + i < count; ++i) { + p[i] = vs[i]; + } + p[count] = p[0]; + var minArea = Number.MAX_VALUE; + for (i = 1; + i <= count; ++i) { + var root = p[parseInt(i - 1)]; + var uxX = p[i].x - root.x; + var uxY = p[i].y - root.y; + var length = Math.sqrt(uxX * uxX + uxY * uxY); + uxX /= length; + uxY /= length; + var uyX = (-uxY); + var uyY = uxX; + var lowerX = Number.MAX_VALUE; + var lowerY = Number.MAX_VALUE; + var upperX = (-Number.MAX_VALUE); + var upperY = (-Number.MAX_VALUE); + for (var j = 0; j < count; ++j) { + var dX = p[j].x - root.x; + var dY = p[j].y - root.y; + var rX = (uxX * dX + uxY * dY); + var rY = (uyX * dX + uyY * dY); + if (rX < lowerX) lowerX = rX; + if (rY < lowerY) lowerY = rY; + if (rX > upperX) upperX = rX; + if (rY > upperY) upperY = rY; + } + var area = (upperX - lowerX) * (upperY - lowerY); + if (area < 0.95 * minArea) { + minArea = area; + obb.R.col1.x = uxX; + obb.R.col1.y = uxY; + obb.R.col2.x = uyX; + obb.R.col2.y = uyY; + var centerX = 0.5 * (lowerX + upperX); + var centerY = 0.5 * (lowerY + upperY); + var tMat = obb.R; + obb.center.x = root.x + (tMat.col1.x * centerX + tMat.col2.x * centerY); + obb.center.y = root.y + (tMat.col1.y * centerX + tMat.col2.y * centerY); + obb.extents.x = 0.5 * (upperX - lowerX); + obb.extents.y = 0.5 * (upperY - lowerY); + } + } + } + Box2D.postDefs.push(function () { + Box2D.Collision.Shapes.b2PolygonShape.s_mat = new b2Mat22(); + }); + b2Shape.b2Shape = function () {}; + b2Shape.prototype.Copy = function () { + return null; + } + b2Shape.prototype.Set = function (other) { + this.m_radius = other.m_radius; + } + b2Shape.prototype.GetType = function () { + return this.m_type; + } + b2Shape.prototype.TestPoint = function (xf, p) { + return false; + } + b2Shape.prototype.RayCast = function (output, input, transform) { + return false; + } + b2Shape.prototype.ComputeAABB = function (aabb, xf) {} + b2Shape.prototype.ComputeMass = function (massData, density) { + if (density === undefined) density = 0; + } + b2Shape.prototype.ComputeSubmergedArea = function (normal, offset, xf, c) { + if (offset === undefined) offset = 0; + return 0; + } + b2Shape.TestOverlap = function (shape1, transform1, shape2, transform2) { + var input = new b2DistanceInput(); + input.proxyA = new b2DistanceProxy(); + input.proxyA.Set(shape1); + input.proxyB = new b2DistanceProxy(); + input.proxyB.Set(shape2); + input.transformA = transform1; + input.transformB = transform2; + input.useRadii = true; + var simplexCache = new b2SimplexCache(); + simplexCache.count = 0; + var output = new b2DistanceOutput(); + b2Distance.Distance(output, simplexCache, input); + return output.distance < 10.0 * Number.MIN_VALUE; + } + b2Shape.prototype.b2Shape = function () { + this.m_type = b2Shape.e_unknownShape; + this.m_radius = b2Settings.b2_linearSlop; + } + Box2D.postDefs.push(function () { + Box2D.Collision.Shapes.b2Shape.e_unknownShape = parseInt((-1)); + Box2D.Collision.Shapes.b2Shape.e_circleShape = 0; + Box2D.Collision.Shapes.b2Shape.e_polygonShape = 1; + Box2D.Collision.Shapes.b2Shape.e_edgeShape = 2; + Box2D.Collision.Shapes.b2Shape.e_shapeTypeCount = 3; + Box2D.Collision.Shapes.b2Shape.e_hitCollide = 1; + Box2D.Collision.Shapes.b2Shape.e_missCollide = 0; + Box2D.Collision.Shapes.b2Shape.e_startsInsideCollide = parseInt((-1)); + }); +})(); +(function () { + var b2Color = Box2D.Common.b2Color, + b2internal = Box2D.Common.b2internal, + b2Settings = Box2D.Common.b2Settings, + b2Mat22 = Box2D.Common.Math.b2Mat22, + b2Mat33 = Box2D.Common.Math.b2Mat33, + b2Math = Box2D.Common.Math.b2Math, + b2Sweep = Box2D.Common.Math.b2Sweep, + b2Transform = Box2D.Common.Math.b2Transform, + b2Vec2 = Box2D.Common.Math.b2Vec2, + b2Vec3 = Box2D.Common.Math.b2Vec3; + + b2Color.b2Color = function () { + this._r = 0; + this._g = 0; + this._b = 0; + }; + b2Color.prototype.b2Color = function (rr, gg, bb) { + if (rr === undefined) rr = 0; + if (gg === undefined) gg = 0; + if (bb === undefined) bb = 0; + this._r = Box2D.parseUInt(255 * b2Math.Clamp(rr, 0.0, 1.0)); + this._g = Box2D.parseUInt(255 * b2Math.Clamp(gg, 0.0, 1.0)); + this._b = Box2D.parseUInt(255 * b2Math.Clamp(bb, 0.0, 1.0)); + } + b2Color.prototype.Set = function (rr, gg, bb) { + if (rr === undefined) rr = 0; + if (gg === undefined) gg = 0; + if (bb === undefined) bb = 0; + this._r = Box2D.parseUInt(255 * b2Math.Clamp(rr, 0.0, 1.0)); + this._g = Box2D.parseUInt(255 * b2Math.Clamp(gg, 0.0, 1.0)); + this._b = Box2D.parseUInt(255 * b2Math.Clamp(bb, 0.0, 1.0)); + } + Object.defineProperty(b2Color.prototype, 'r', { + enumerable: false, + configurable: true, + set: function (rr) { + if (rr === undefined) rr = 0; + this._r = Box2D.parseUInt(255 * b2Math.Clamp(rr, 0.0, 1.0)); + } + }); + Object.defineProperty(b2Color.prototype, 'g', { + enumerable: false, + configurable: true, + set: function (gg) { + if (gg === undefined) gg = 0; + this._g = Box2D.parseUInt(255 * b2Math.Clamp(gg, 0.0, 1.0)); + } + }); + Object.defineProperty(b2Color.prototype, 'b', { + enumerable: false, + configurable: true, + set: function (bb) { + if (bb === undefined) bb = 0; + this._b = Box2D.parseUInt(255 * b2Math.Clamp(bb, 0.0, 1.0)); + } + }); + Object.defineProperty(b2Color.prototype, 'color', { + enumerable: false, + configurable: true, + get: function () { + return (this._r << 16) | (this._g << 8) | (this._b); + } + }); + b2Settings.b2Settings = function () {}; + b2Settings.b2MixFriction = function (friction1, friction2) { + if (friction1 === undefined) friction1 = 0; + if (friction2 === undefined) friction2 = 0; + return Math.sqrt(friction1 * friction2); + } + b2Settings.b2MixRestitution = function (restitution1, restitution2) { + if (restitution1 === undefined) restitution1 = 0; + if (restitution2 === undefined) restitution2 = 0; + return restitution1 > restitution2 ? restitution1 : restitution2; + } + b2Settings.b2Assert = function (a) { + if (!a) { + throw "Assertion Failed"; + } + } + Box2D.postDefs.push(function () { + Box2D.Common.b2Settings.VERSION = "2.1alpha"; + Box2D.Common.b2Settings.USHRT_MAX = 0x0000ffff; + Box2D.Common.b2Settings.b2_pi = Math.PI; + Box2D.Common.b2Settings.b2_maxManifoldPoints = 2; + Box2D.Common.b2Settings.b2_aabbExtension = 0.1; + Box2D.Common.b2Settings.b2_aabbMultiplier = 2.0; + Box2D.Common.b2Settings.b2_polygonRadius = 2.0 * b2Settings.b2_linearSlop; + Box2D.Common.b2Settings.b2_linearSlop = 0.005; + Box2D.Common.b2Settings.b2_angularSlop = 2.0 / 180.0 * b2Settings.b2_pi; + Box2D.Common.b2Settings.b2_toiSlop = 8.0 * b2Settings.b2_linearSlop; + Box2D.Common.b2Settings.b2_maxTOIContactsPerIsland = 32; + Box2D.Common.b2Settings.b2_maxTOIJointsPerIsland = 32; + Box2D.Common.b2Settings.b2_velocityThreshold = 1.0; + Box2D.Common.b2Settings.b2_maxLinearCorrection = 0.2; + Box2D.Common.b2Settings.b2_maxAngularCorrection = 8.0 / 180.0 * b2Settings.b2_pi; + Box2D.Common.b2Settings.b2_maxTranslation = 2.0; + Box2D.Common.b2Settings.b2_maxTranslationSquared = b2Settings.b2_maxTranslation * b2Settings.b2_maxTranslation; + Box2D.Common.b2Settings.b2_maxRotation = 0.5 * b2Settings.b2_pi; + Box2D.Common.b2Settings.b2_maxRotationSquared = b2Settings.b2_maxRotation * b2Settings.b2_maxRotation; + Box2D.Common.b2Settings.b2_contactBaumgarte = 0.2; + Box2D.Common.b2Settings.b2_timeToSleep = 0.5; + Box2D.Common.b2Settings.b2_linearSleepTolerance = 0.01; + Box2D.Common.b2Settings.b2_angularSleepTolerance = 2.0 / 180.0 * b2Settings.b2_pi; + }); +})(); +(function () { + var b2AABB = Box2D.Collision.b2AABB, + b2Color = Box2D.Common.b2Color, + b2internal = Box2D.Common.b2internal, + b2Settings = Box2D.Common.b2Settings, + b2Mat22 = Box2D.Common.Math.b2Mat22, + b2Mat33 = Box2D.Common.Math.b2Mat33, + b2Math = Box2D.Common.Math.b2Math, + b2Sweep = Box2D.Common.Math.b2Sweep, + b2Transform = Box2D.Common.Math.b2Transform, + b2Vec2 = Box2D.Common.Math.b2Vec2, + b2Vec3 = Box2D.Common.Math.b2Vec3; + + b2Mat22.b2Mat22 = function () { + this.col1 = new b2Vec2(); + this.col2 = new b2Vec2(); + }; + b2Mat22.prototype.b2Mat22 = function () { + this.SetIdentity(); + } + b2Mat22.FromAngle = function (angle) { + if (angle === undefined) angle = 0; + var mat = new b2Mat22(); + mat.Set(angle); + return mat; + } + b2Mat22.FromVV = function (c1, c2) { + var mat = new b2Mat22(); + mat.SetVV(c1, c2); + return mat; + } + b2Mat22.prototype.Set = function (angle) { + if (angle === undefined) angle = 0; + var c = Math.cos(angle); + var s = Math.sin(angle); + this.col1.x = c; + this.col2.x = (-s); + this.col1.y = s; + this.col2.y = c; + } + b2Mat22.prototype.SetVV = function (c1, c2) { + this.col1.SetV(c1); + this.col2.SetV(c2); + } + b2Mat22.prototype.Copy = function () { + var mat = new b2Mat22(); + mat.SetM(this); + return mat; + } + b2Mat22.prototype.SetM = function (m) { + this.col1.SetV(m.col1); + this.col2.SetV(m.col2); + } + b2Mat22.prototype.AddM = function (m) { + this.col1.x += m.col1.x; + this.col1.y += m.col1.y; + this.col2.x += m.col2.x; + this.col2.y += m.col2.y; + } + b2Mat22.prototype.SetIdentity = function () { + this.col1.x = 1.0; + this.col2.x = 0.0; + this.col1.y = 0.0; + this.col2.y = 1.0; + } + b2Mat22.prototype.SetZero = function () { + this.col1.x = 0.0; + this.col2.x = 0.0; + this.col1.y = 0.0; + this.col2.y = 0.0; + } + b2Mat22.prototype.GetAngle = function () { + return Math.atan2(this.col1.y, this.col1.x); + } + b2Mat22.prototype.GetInverse = function (out) { + var a = this.col1.x; + var b = this.col2.x; + var c = this.col1.y; + var d = this.col2.y; + var det = a * d - b * c; + if (det != 0.0) { + det = 1.0 / det; + } + out.col1.x = det * d; + out.col2.x = (-det * b); + out.col1.y = (-det * c); + out.col2.y = det * a; + return out; + } + b2Mat22.prototype.Solve = function (out, bX, bY) { + if (bX === undefined) bX = 0; + if (bY === undefined) bY = 0; + var a11 = this.col1.x; + var a12 = this.col2.x; + var a21 = this.col1.y; + var a22 = this.col2.y; + var det = a11 * a22 - a12 * a21; + if (det != 0.0) { + det = 1.0 / det; + } + out.x = det * (a22 * bX - a12 * bY); + out.y = det * (a11 * bY - a21 * bX); + return out; + } + b2Mat22.prototype.Abs = function () { + this.col1.Abs(); + this.col2.Abs(); + } + b2Mat33.b2Mat33 = function () { + this.col1 = new b2Vec3(); + this.col2 = new b2Vec3(); + this.col3 = new b2Vec3(); + }; + b2Mat33.prototype.b2Mat33 = function (c1, c2, c3) { + if (c1 === undefined) c1 = null; + if (c2 === undefined) c2 = null; + if (c3 === undefined) c3 = null; + if (!c1 && !c2 && !c3) { + this.col1.SetZero(); + this.col2.SetZero(); + this.col3.SetZero(); + } + else { + this.col1.SetV(c1); + this.col2.SetV(c2); + this.col3.SetV(c3); + } + } + b2Mat33.prototype.SetVVV = function (c1, c2, c3) { + this.col1.SetV(c1); + this.col2.SetV(c2); + this.col3.SetV(c3); + } + b2Mat33.prototype.Copy = function () { + return new b2Mat33(this.col1, this.col2, this.col3); + } + b2Mat33.prototype.SetM = function (m) { + this.col1.SetV(m.col1); + this.col2.SetV(m.col2); + this.col3.SetV(m.col3); + } + b2Mat33.prototype.AddM = function (m) { + this.col1.x += m.col1.x; + this.col1.y += m.col1.y; + this.col1.z += m.col1.z; + this.col2.x += m.col2.x; + this.col2.y += m.col2.y; + this.col2.z += m.col2.z; + this.col3.x += m.col3.x; + this.col3.y += m.col3.y; + this.col3.z += m.col3.z; + } + b2Mat33.prototype.SetIdentity = function () { + this.col1.x = 1.0; + this.col2.x = 0.0; + this.col3.x = 0.0; + this.col1.y = 0.0; + this.col2.y = 1.0; + this.col3.y = 0.0; + this.col1.z = 0.0; + this.col2.z = 0.0; + this.col3.z = 1.0; + } + b2Mat33.prototype.SetZero = function () { + this.col1.x = 0.0; + this.col2.x = 0.0; + this.col3.x = 0.0; + this.col1.y = 0.0; + this.col2.y = 0.0; + this.col3.y = 0.0; + this.col1.z = 0.0; + this.col2.z = 0.0; + this.col3.z = 0.0; + } + b2Mat33.prototype.Solve22 = function (out, bX, bY) { + if (bX === undefined) bX = 0; + if (bY === undefined) bY = 0; + var a11 = this.col1.x; + var a12 = this.col2.x; + var a21 = this.col1.y; + var a22 = this.col2.y; + var det = a11 * a22 - a12 * a21; + if (det != 0.0) { + det = 1.0 / det; + } + out.x = det * (a22 * bX - a12 * bY); + out.y = det * (a11 * bY - a21 * bX); + return out; + } + b2Mat33.prototype.Solve33 = function (out, bX, bY, bZ) { + if (bX === undefined) bX = 0; + if (bY === undefined) bY = 0; + if (bZ === undefined) bZ = 0; + var a11 = this.col1.x; + var a21 = this.col1.y; + var a31 = this.col1.z; + var a12 = this.col2.x; + var a22 = this.col2.y; + var a32 = this.col2.z; + var a13 = this.col3.x; + var a23 = this.col3.y; + var a33 = this.col3.z; + var det = a11 * (a22 * a33 - a32 * a23) + a21 * (a32 * a13 - a12 * a33) + a31 * (a12 * a23 - a22 * a13); + if (det != 0.0) { + det = 1.0 / det; + } + out.x = det * (bX * (a22 * a33 - a32 * a23) + bY * (a32 * a13 - a12 * a33) + bZ * (a12 * a23 - a22 * a13)); + out.y = det * (a11 * (bY * a33 - bZ * a23) + a21 * (bZ * a13 - bX * a33) + a31 * (bX * a23 - bY * a13)); + out.z = det * (a11 * (a22 * bZ - a32 * bY) + a21 * (a32 * bX - a12 * bZ) + a31 * (a12 * bY - a22 * bX)); + return out; + } + b2Math.b2Math = function () {}; + b2Math.IsValid = function (x) { + if (x === undefined) x = 0; + return isFinite(x); + } + b2Math.Dot = function (a, b) { + return a.x * b.x + a.y * b.y; + } + b2Math.CrossVV = function (a, b) { + return a.x * b.y - a.y * b.x; + } + b2Math.CrossVF = function (a, s) { + if (s === undefined) s = 0; + var v = new b2Vec2(s * a.y, (-s * a.x)); + return v; + } + b2Math.CrossFV = function (s, a) { + if (s === undefined) s = 0; + var v = new b2Vec2((-s * a.y), s * a.x); + return v; + } + b2Math.MulMV = function (A, v) { + var u = new b2Vec2(A.col1.x * v.x + A.col2.x * v.y, A.col1.y * v.x + A.col2.y * v.y); + return u; + } + b2Math.MulTMV = function (A, v) { + var u = new b2Vec2(b2Math.Dot(v, A.col1), b2Math.Dot(v, A.col2)); + return u; + } + b2Math.MulX = function (T, v) { + var a = b2Math.MulMV(T.R, v); + a.x += T.position.x; + a.y += T.position.y; + return a; + } + b2Math.MulXT = function (T, v) { + var a = b2Math.SubtractVV(v, T.position); + var tX = (a.x * T.R.col1.x + a.y * T.R.col1.y); + a.y = (a.x * T.R.col2.x + a.y * T.R.col2.y); + a.x = tX; + return a; + } + b2Math.AddVV = function (a, b) { + var v = new b2Vec2(a.x + b.x, a.y + b.y); + return v; + } + b2Math.SubtractVV = function (a, b) { + var v = new b2Vec2(a.x - b.x, a.y - b.y); + return v; + } + b2Math.Distance = function (a, b) { + var cX = a.x - b.x; + var cY = a.y - b.y; + return Math.sqrt(cX * cX + cY * cY); + } + b2Math.DistanceSquared = function (a, b) { + var cX = a.x - b.x; + var cY = a.y - b.y; + return (cX * cX + cY * cY); + } + b2Math.MulFV = function (s, a) { + if (s === undefined) s = 0; + var v = new b2Vec2(s * a.x, s * a.y); + return v; + } + b2Math.AddMM = function (A, B) { + var C = b2Mat22.FromVV(b2Math.AddVV(A.col1, B.col1), b2Math.AddVV(A.col2, B.col2)); + return C; + } + b2Math.MulMM = function (A, B) { + var C = b2Mat22.FromVV(b2Math.MulMV(A, B.col1), b2Math.MulMV(A, B.col2)); + return C; + } + b2Math.MulTMM = function (A, B) { + var c1 = new b2Vec2(b2Math.Dot(A.col1, B.col1), b2Math.Dot(A.col2, B.col1)); + var c2 = new b2Vec2(b2Math.Dot(A.col1, B.col2), b2Math.Dot(A.col2, B.col2)); + var C = b2Mat22.FromVV(c1, c2); + return C; + } + b2Math.Abs = function (a) { + if (a === undefined) a = 0; + return a > 0.0 ? a : (-a); + } + b2Math.AbsV = function (a) { + var b = new b2Vec2(b2Math.Abs(a.x), b2Math.Abs(a.y)); + return b; + } + b2Math.AbsM = function (A) { + var B = b2Mat22.FromVV(b2Math.AbsV(A.col1), b2Math.AbsV(A.col2)); + return B; + } + b2Math.Min = function (a, b) { + if (a === undefined) a = 0; + if (b === undefined) b = 0; + return a < b ? a : b; + } + b2Math.MinV = function (a, b) { + var c = new b2Vec2(b2Math.Min(a.x, b.x), b2Math.Min(a.y, b.y)); + return c; + } + b2Math.Max = function (a, b) { + if (a === undefined) a = 0; + if (b === undefined) b = 0; + return a > b ? a : b; + } + b2Math.MaxV = function (a, b) { + var c = new b2Vec2(b2Math.Max(a.x, b.x), b2Math.Max(a.y, b.y)); + return c; + } + b2Math.Clamp = function (a, low, high) { + if (a === undefined) a = 0; + if (low === undefined) low = 0; + if (high === undefined) high = 0; + return a < low ? low : a > high ? high : a; + } + b2Math.ClampV = function (a, low, high) { + return b2Math.MaxV(low, b2Math.MinV(a, high)); + } + b2Math.Swap = function (a, b) { + var tmp = a[0]; + a[0] = b[0]; + b[0] = tmp; + } + b2Math.Random = function () { + return Math.random() * 2 - 1; + } + b2Math.RandomRange = function (lo, hi) { + if (lo === undefined) lo = 0; + if (hi === undefined) hi = 0; + var r = Math.random(); + r = (hi - lo) * r + lo; + return r; + } + b2Math.NextPowerOfTwo = function (x) { + if (x === undefined) x = 0; + x |= (x >> 1) & 0x7FFFFFFF; + x |= (x >> 2) & 0x3FFFFFFF; + x |= (x >> 4) & 0x0FFFFFFF; + x |= (x >> 8) & 0x00FFFFFF; + x |= (x >> 16) & 0x0000FFFF; + return x + 1; + } + b2Math.IsPowerOfTwo = function (x) { + if (x === undefined) x = 0; + var result = x > 0 && (x & (x - 1)) == 0; + return result; + } + Box2D.postDefs.push(function () { + Box2D.Common.Math.b2Math.b2Vec2_zero = new b2Vec2(0.0, 0.0); + Box2D.Common.Math.b2Math.b2Mat22_identity = b2Mat22.FromVV(new b2Vec2(1.0, 0.0), new b2Vec2(0.0, 1.0)); + Box2D.Common.Math.b2Math.b2Transform_identity = new b2Transform(b2Math.b2Vec2_zero, b2Math.b2Mat22_identity); + }); + b2Sweep.b2Sweep = function () { + this.localCenter = new b2Vec2(); + this.c0 = new b2Vec2; + this.c = new b2Vec2(); + }; + b2Sweep.prototype.Set = function (other) { + this.localCenter.SetV(other.localCenter); + this.c0.SetV(other.c0); + this.c.SetV(other.c); + this.a0 = other.a0; + this.a = other.a; + this.t0 = other.t0; + } + b2Sweep.prototype.Copy = function () { + var copy = new b2Sweep(); + copy.localCenter.SetV(this.localCenter); + copy.c0.SetV(this.c0); + copy.c.SetV(this.c); + copy.a0 = this.a0; + copy.a = this.a; + copy.t0 = this.t0; + return copy; + } + b2Sweep.prototype.GetTransform = function (xf, alpha) { + if (alpha === undefined) alpha = 0; + xf.position.x = (1.0 - alpha) * this.c0.x + alpha * this.c.x; + xf.position.y = (1.0 - alpha) * this.c0.y + alpha * this.c.y; + var angle = (1.0 - alpha) * this.a0 + alpha * this.a; + xf.R.Set(angle); + var tMat = xf.R; + xf.position.x -= (tMat.col1.x * this.localCenter.x + tMat.col2.x * this.localCenter.y); + xf.position.y -= (tMat.col1.y * this.localCenter.x + tMat.col2.y * this.localCenter.y); + } + b2Sweep.prototype.Advance = function (t) { + if (t === undefined) t = 0; + if (this.t0 < t && 1.0 - this.t0 > Number.MIN_VALUE) { + var alpha = (t - this.t0) / (1.0 - this.t0); + this.c0.x = (1.0 - alpha) * this.c0.x + alpha * this.c.x; + this.c0.y = (1.0 - alpha) * this.c0.y + alpha * this.c.y; + this.a0 = (1.0 - alpha) * this.a0 + alpha * this.a; + this.t0 = t; + } + } + b2Transform.b2Transform = function () { + this.position = new b2Vec2; + this.R = new b2Mat22(); + }; + b2Transform.prototype.b2Transform = function (pos, r) { + if (pos === undefined) pos = null; + if (r === undefined) r = null; + if (pos) { + this.position.SetV(pos); + this.R.SetM(r); + } + } + b2Transform.prototype.Initialize = function (pos, r) { + this.position.SetV(pos); + this.R.SetM(r); + } + b2Transform.prototype.SetIdentity = function () { + this.position.SetZero(); + this.R.SetIdentity(); + } + b2Transform.prototype.Set = function (x) { + this.position.SetV(x.position); + this.R.SetM(x.R); + } + b2Transform.prototype.GetAngle = function () { + return Math.atan2(this.R.col1.y, this.R.col1.x); + } + b2Vec2.b2Vec2 = function () {}; + b2Vec2.prototype.b2Vec2 = function (x_, y_) { + if (x_ === undefined) x_ = 0; + if (y_ === undefined) y_ = 0; + this.x = x_; + this.y = y_; + } + b2Vec2.prototype.SetZero = function () { + this.x = 0.0; + this.y = 0.0; + } + b2Vec2.prototype.Set = function (x_, y_) { + if (x_ === undefined) x_ = 0; + if (y_ === undefined) y_ = 0; + this.x = x_; + this.y = y_; + } + b2Vec2.prototype.SetV = function (v) { + this.x = v.x; + this.y = v.y; + } + b2Vec2.prototype.GetNegative = function () { + return new b2Vec2((-this.x), (-this.y)); + } + b2Vec2.prototype.NegativeSelf = function () { + this.x = (-this.x); + this.y = (-this.y); + } + b2Vec2.Make = function (x_, y_) { + if (x_ === undefined) x_ = 0; + if (y_ === undefined) y_ = 0; + return new b2Vec2(x_, y_); + } + b2Vec2.prototype.Copy = function () { + return new b2Vec2(this.x, this.y); + } + b2Vec2.prototype.Add = function (v) { + this.x += v.x; + this.y += v.y; + } + b2Vec2.prototype.Subtract = function (v) { + this.x -= v.x; + this.y -= v.y; + } + b2Vec2.prototype.Multiply = function (a) { + if (a === undefined) a = 0; + this.x *= a; + this.y *= a; + } + b2Vec2.prototype.MulM = function (A) { + var tX = this.x; + this.x = A.col1.x * tX + A.col2.x * this.y; + this.y = A.col1.y * tX + A.col2.y * this.y; + } + b2Vec2.prototype.MulTM = function (A) { + var tX = b2Math.Dot(this, A.col1); + this.y = b2Math.Dot(this, A.col2); + this.x = tX; + } + b2Vec2.prototype.CrossVF = function (s) { + if (s === undefined) s = 0; + var tX = this.x; + this.x = s * this.y; + this.y = (-s * tX); + } + b2Vec2.prototype.CrossFV = function (s) { + if (s === undefined) s = 0; + var tX = this.x; + this.x = (-s * this.y); + this.y = s * tX; + } + b2Vec2.prototype.MinV = function (b) { + this.x = this.x < b.x ? this.x : b.x; + this.y = this.y < b.y ? this.y : b.y; + } + b2Vec2.prototype.MaxV = function (b) { + this.x = this.x > b.x ? this.x : b.x; + this.y = this.y > b.y ? this.y : b.y; + } + b2Vec2.prototype.Abs = function () { + if (this.x < 0) this.x = (-this.x); + if (this.y < 0) this.y = (-this.y); + } + b2Vec2.prototype.Length = function () { + return Math.sqrt(this.x * this.x + this.y * this.y); + } + b2Vec2.prototype.LengthSquared = function () { + return (this.x * this.x + this.y * this.y); + } + b2Vec2.prototype.Normalize = function () { + var length = Math.sqrt(this.x * this.x + this.y * this.y); + if (length < Number.MIN_VALUE) { + return 0.0; + } + var invLength = 1.0 / length; + this.x *= invLength; + this.y *= invLength; + return length; + } + b2Vec2.prototype.IsValid = function () { + return b2Math.IsValid(this.x) && b2Math.IsValid(this.y); + } + b2Vec3.b2Vec3 = function () {}; + b2Vec3.prototype.b2Vec3 = function (x, y, z) { + if (x === undefined) x = 0; + if (y === undefined) y = 0; + if (z === undefined) z = 0; + this.x = x; + this.y = y; + this.z = z; + } + b2Vec3.prototype.SetZero = function () { + this.x = this.y = this.z = 0.0; + } + b2Vec3.prototype.Set = function (x, y, z) { + if (x === undefined) x = 0; + if (y === undefined) y = 0; + if (z === undefined) z = 0; + this.x = x; + this.y = y; + this.z = z; + } + b2Vec3.prototype.SetV = function (v) { + this.x = v.x; + this.y = v.y; + this.z = v.z; + } + b2Vec3.prototype.GetNegative = function () { + return new b2Vec3((-this.x), (-this.y), (-this.z)); + } + b2Vec3.prototype.NegativeSelf = function () { + this.x = (-this.x); + this.y = (-this.y); + this.z = (-this.z); + } + b2Vec3.prototype.Copy = function () { + return new b2Vec3(this.x, this.y, this.z); + } + b2Vec3.prototype.Add = function (v) { + this.x += v.x; + this.y += v.y; + this.z += v.z; + } + b2Vec3.prototype.Subtract = function (v) { + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + } + b2Vec3.prototype.Multiply = function (a) { + if (a === undefined) a = 0; + this.x *= a; + this.y *= a; + this.z *= a; + } +})(); +(function () { + var b2ControllerEdge = Box2D.Dynamics.Controllers.b2ControllerEdge, + b2Mat22 = Box2D.Common.Math.b2Mat22, + b2Mat33 = Box2D.Common.Math.b2Mat33, + b2Math = Box2D.Common.Math.b2Math, + b2Sweep = Box2D.Common.Math.b2Sweep, + b2Transform = Box2D.Common.Math.b2Transform, + b2Vec2 = Box2D.Common.Math.b2Vec2, + b2Vec3 = Box2D.Common.Math.b2Vec3, + b2Color = Box2D.Common.b2Color, + b2internal = Box2D.Common.b2internal, + b2Settings = Box2D.Common.b2Settings, + b2AABB = Box2D.Collision.b2AABB, + b2Bound = Box2D.Collision.b2Bound, + b2BoundValues = Box2D.Collision.b2BoundValues, + b2Collision = Box2D.Collision.b2Collision, + b2ContactID = Box2D.Collision.b2ContactID, + b2ContactPoint = Box2D.Collision.b2ContactPoint, + b2Distance = Box2D.Collision.b2Distance, + b2DistanceInput = Box2D.Collision.b2DistanceInput, + b2DistanceOutput = Box2D.Collision.b2DistanceOutput, + b2DistanceProxy = Box2D.Collision.b2DistanceProxy, + b2DynamicTree = Box2D.Collision.b2DynamicTree, + b2DynamicTreeBroadPhase = Box2D.Collision.b2DynamicTreeBroadPhase, + b2DynamicTreeNode = Box2D.Collision.b2DynamicTreeNode, + b2DynamicTreePair = Box2D.Collision.b2DynamicTreePair, + b2Manifold = Box2D.Collision.b2Manifold, + b2ManifoldPoint = Box2D.Collision.b2ManifoldPoint, + b2Point = Box2D.Collision.b2Point, + b2RayCastInput = Box2D.Collision.b2RayCastInput, + b2RayCastOutput = Box2D.Collision.b2RayCastOutput, + b2Segment = Box2D.Collision.b2Segment, + b2SeparationFunction = Box2D.Collision.b2SeparationFunction, + b2Simplex = Box2D.Collision.b2Simplex, + b2SimplexCache = Box2D.Collision.b2SimplexCache, + b2SimplexVertex = Box2D.Collision.b2SimplexVertex, + b2TimeOfImpact = Box2D.Collision.b2TimeOfImpact, + b2TOIInput = Box2D.Collision.b2TOIInput, + b2WorldManifold = Box2D.Collision.b2WorldManifold, + ClipVertex = Box2D.Collision.ClipVertex, + Features = Box2D.Collision.Features, + IBroadPhase = Box2D.Collision.IBroadPhase, + b2CircleShape = Box2D.Collision.Shapes.b2CircleShape, + b2EdgeChainDef = Box2D.Collision.Shapes.b2EdgeChainDef, + b2EdgeShape = Box2D.Collision.Shapes.b2EdgeShape, + b2MassData = Box2D.Collision.Shapes.b2MassData, + b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape, + b2Shape = Box2D.Collision.Shapes.b2Shape, + b2Body = Box2D.Dynamics.b2Body, + b2BodyDef = Box2D.Dynamics.b2BodyDef, + b2ContactFilter = Box2D.Dynamics.b2ContactFilter, + b2ContactImpulse = Box2D.Dynamics.b2ContactImpulse, + b2ContactListener = Box2D.Dynamics.b2ContactListener, + b2ContactManager = Box2D.Dynamics.b2ContactManager, + b2DebugDraw = Box2D.Dynamics.b2DebugDraw, + b2DestructionListener = Box2D.Dynamics.b2DestructionListener, + b2FilterData = Box2D.Dynamics.b2FilterData, + b2Fixture = Box2D.Dynamics.b2Fixture, + b2FixtureDef = Box2D.Dynamics.b2FixtureDef, + b2Island = Box2D.Dynamics.b2Island, + b2TimeStep = Box2D.Dynamics.b2TimeStep, + b2World = Box2D.Dynamics.b2World, + b2CircleContact = Box2D.Dynamics.Contacts.b2CircleContact, + b2Contact = Box2D.Dynamics.Contacts.b2Contact, + b2ContactConstraint = Box2D.Dynamics.Contacts.b2ContactConstraint, + b2ContactConstraintPoint = Box2D.Dynamics.Contacts.b2ContactConstraintPoint, + b2ContactEdge = Box2D.Dynamics.Contacts.b2ContactEdge, + b2ContactFactory = Box2D.Dynamics.Contacts.b2ContactFactory, + b2ContactRegister = Box2D.Dynamics.Contacts.b2ContactRegister, + b2ContactResult = Box2D.Dynamics.Contacts.b2ContactResult, + b2ContactSolver = Box2D.Dynamics.Contacts.b2ContactSolver, + b2EdgeAndCircleContact = Box2D.Dynamics.Contacts.b2EdgeAndCircleContact, + b2NullContact = Box2D.Dynamics.Contacts.b2NullContact, + b2PolyAndCircleContact = Box2D.Dynamics.Contacts.b2PolyAndCircleContact, + b2PolyAndEdgeContact = Box2D.Dynamics.Contacts.b2PolyAndEdgeContact, + b2PolygonContact = Box2D.Dynamics.Contacts.b2PolygonContact, + b2PositionSolverManifold = Box2D.Dynamics.Contacts.b2PositionSolverManifold, + b2Controller = Box2D.Dynamics.Controllers.b2Controller, + b2DistanceJoint = Box2D.Dynamics.Joints.b2DistanceJoint, + b2DistanceJointDef = Box2D.Dynamics.Joints.b2DistanceJointDef, + b2FrictionJoint = Box2D.Dynamics.Joints.b2FrictionJoint, + b2FrictionJointDef = Box2D.Dynamics.Joints.b2FrictionJointDef, + b2GearJoint = Box2D.Dynamics.Joints.b2GearJoint, + b2GearJointDef = Box2D.Dynamics.Joints.b2GearJointDef, + b2Jacobian = Box2D.Dynamics.Joints.b2Jacobian, + b2Joint = Box2D.Dynamics.Joints.b2Joint, + b2JointDef = Box2D.Dynamics.Joints.b2JointDef, + b2JointEdge = Box2D.Dynamics.Joints.b2JointEdge, + b2LineJoint = Box2D.Dynamics.Joints.b2LineJoint, + b2LineJointDef = Box2D.Dynamics.Joints.b2LineJointDef, + b2MouseJoint = Box2D.Dynamics.Joints.b2MouseJoint, + b2MouseJointDef = Box2D.Dynamics.Joints.b2MouseJointDef, + b2PrismaticJoint = Box2D.Dynamics.Joints.b2PrismaticJoint, + b2PrismaticJointDef = Box2D.Dynamics.Joints.b2PrismaticJointDef, + b2PulleyJoint = Box2D.Dynamics.Joints.b2PulleyJoint, + b2PulleyJointDef = Box2D.Dynamics.Joints.b2PulleyJointDef, + b2RevoluteJoint = Box2D.Dynamics.Joints.b2RevoluteJoint, + b2RevoluteJointDef = Box2D.Dynamics.Joints.b2RevoluteJointDef, + b2WeldJoint = Box2D.Dynamics.Joints.b2WeldJoint, + b2WeldJointDef = Box2D.Dynamics.Joints.b2WeldJointDef; + + b2Body.b2Body = function () { + this.m_xf = new b2Transform(); + this.m_sweep = new b2Sweep(); + this.m_linearVelocity = new b2Vec2(); + this.m_force = new b2Vec2(); + }; + b2Body.prototype.connectEdges = function (s1, s2, angle1) { + if (angle1 === undefined) angle1 = 0; + var angle2 = Math.atan2(s2.GetDirectionVector().y, s2.GetDirectionVector().x); + var coreOffset = Math.tan((angle2 - angle1) * 0.5); + var core = b2Math.MulFV(coreOffset, s2.GetDirectionVector()); + core = b2Math.SubtractVV(core, s2.GetNormalVector()); + core = b2Math.MulFV(b2Settings.b2_toiSlop, core); + core = b2Math.AddVV(core, s2.GetVertex1()); + var cornerDir = b2Math.AddVV(s1.GetDirectionVector(), s2.GetDirectionVector()); + cornerDir.Normalize(); + var convex = b2Math.Dot(s1.GetDirectionVector(), s2.GetNormalVector()) > 0.0; + s1.SetNextEdge(s2, core, cornerDir, convex); + s2.SetPrevEdge(s1, core, cornerDir, convex); + return angle2; + } + b2Body.prototype.CreateFixture = function (def) { + if (this.m_world.IsLocked() == true) { + return null; + } + var fixture = new b2Fixture(); + fixture.Create(this, this.m_xf, def); + if (this.m_flags & b2Body.e_activeFlag) { + var broadPhase = this.m_world.m_contactManager.m_broadPhase; + fixture.CreateProxy(broadPhase, this.m_xf); + } + fixture.m_next = this.m_fixtureList; + this.m_fixtureList = fixture; + ++this.m_fixtureCount; + fixture.m_body = this; + if (fixture.m_density > 0.0) { + this.ResetMassData(); + } + this.m_world.m_flags |= b2World.e_newFixture; + return fixture; + } + b2Body.prototype.CreateFixture2 = function (shape, density) { + if (density === undefined) density = 0.0; + var def = new b2FixtureDef(); + def.shape = shape; + def.density = density; + return this.CreateFixture(def); + } + b2Body.prototype.DestroyFixture = function (fixture) { + if (this.m_world.IsLocked() == true) { + return; + } + var node = this.m_fixtureList; + var ppF = null; + var found = false; + while (node != null) { + if (node == fixture) { + if (ppF) ppF.m_next = fixture.m_next; + else this.m_fixtureList = fixture.m_next; + found = true; + break; + } + ppF = node; + node = node.m_next; + } + var edge = this.m_contactList; + while (edge) { + var c = edge.contact; + edge = edge.next; + var fixtureA = c.GetFixtureA(); + var fixtureB = c.GetFixtureB(); + if (fixture == fixtureA || fixture == fixtureB) { + this.m_world.m_contactManager.Destroy(c); + } + } + if (this.m_flags & b2Body.e_activeFlag) { + var broadPhase = this.m_world.m_contactManager.m_broadPhase; + fixture.DestroyProxy(broadPhase); + } + else {} + fixture.Destroy(); + fixture.m_body = null; + fixture.m_next = null; + --this.m_fixtureCount; + this.ResetMassData(); + } + b2Body.prototype.SetPositionAndAngle = function (position, angle) { + if (angle === undefined) angle = 0; + var f; + if (this.m_world.IsLocked() == true) { + return; + } + this.m_xf.R.Set(angle); + this.m_xf.position.SetV(position); + var tMat = this.m_xf.R; + var tVec = this.m_sweep.localCenter; + this.m_sweep.c.x = (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + this.m_sweep.c.y = (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + this.m_sweep.c.x += this.m_xf.position.x; + this.m_sweep.c.y += this.m_xf.position.y; + this.m_sweep.c0.SetV(this.m_sweep.c); + this.m_sweep.a0 = this.m_sweep.a = angle; + var broadPhase = this.m_world.m_contactManager.m_broadPhase; + for (f = this.m_fixtureList; + f; f = f.m_next) { + f.Synchronize(broadPhase, this.m_xf, this.m_xf); + } + this.m_world.m_contactManager.FindNewContacts(); + } + b2Body.prototype.SetTransform = function (xf) { + this.SetPositionAndAngle(xf.position, xf.GetAngle()); + } + b2Body.prototype.GetTransform = function () { + return this.m_xf; + } + b2Body.prototype.GetPosition = function () { + return this.m_xf.position; + } + b2Body.prototype.SetPosition = function (position) { + this.SetPositionAndAngle(position, this.GetAngle()); + } + b2Body.prototype.GetAngle = function () { + return this.m_sweep.a; + } + b2Body.prototype.SetAngle = function (angle) { + if (angle === undefined) angle = 0; + this.SetPositionAndAngle(this.GetPosition(), angle); + } + b2Body.prototype.GetWorldCenter = function () { + return this.m_sweep.c; + } + b2Body.prototype.GetLocalCenter = function () { + return this.m_sweep.localCenter; + } + b2Body.prototype.SetLinearVelocity = function (v) { + if (this.m_type == b2Body.b2_staticBody) { + return; + } + this.m_linearVelocity.SetV(v); + } + b2Body.prototype.GetLinearVelocity = function () { + return this.m_linearVelocity; + } + b2Body.prototype.SetAngularVelocity = function (omega) { + if (omega === undefined) omega = 0; + if (this.m_type == b2Body.b2_staticBody) { + return; + } + this.m_angularVelocity = omega; + } + b2Body.prototype.GetAngularVelocity = function () { + return this.m_angularVelocity; + } + b2Body.prototype.GetDefinition = function () { + var bd = new b2BodyDef(); + bd.type = this.GetType(); + bd.allowSleep = (this.m_flags & b2Body.e_allowSleepFlag) == b2Body.e_allowSleepFlag; + bd.angle = this.GetAngle(); + bd.angularDamping = this.m_angularDamping; + bd.angularVelocity = this.m_angularVelocity; + bd.fixedRotation = (this.m_flags & b2Body.e_fixedRotationFlag) == b2Body.e_fixedRotationFlag; + bd.bullet = (this.m_flags & b2Body.e_bulletFlag) == b2Body.e_bulletFlag; + bd.awake = (this.m_flags & b2Body.e_awakeFlag) == b2Body.e_awakeFlag; + bd.linearDamping = this.m_linearDamping; + bd.linearVelocity.SetV(this.GetLinearVelocity()); + bd.position = this.GetPosition(); + bd.userData = this.GetUserData(); + return bd; + } + b2Body.prototype.ApplyForce = function (force, point) { + if (this.m_type != b2Body.b2_dynamicBody) { + return; + } + if (this.IsAwake() == false) { + this.SetAwake(true); + } + this.m_force.x += force.x; + this.m_force.y += force.y; + this.m_torque += ((point.x - this.m_sweep.c.x) * force.y - (point.y - this.m_sweep.c.y) * force.x); + } + b2Body.prototype.ApplyTorque = function (torque) { + if (torque === undefined) torque = 0; + if (this.m_type != b2Body.b2_dynamicBody) { + return; + } + if (this.IsAwake() == false) { + this.SetAwake(true); + } + this.m_torque += torque; + } + b2Body.prototype.ApplyImpulse = function (impulse, point) { + if (this.m_type != b2Body.b2_dynamicBody) { + return; + } + if (this.IsAwake() == false) { + this.SetAwake(true); + } + this.m_linearVelocity.x += this.m_invMass * impulse.x; + this.m_linearVelocity.y += this.m_invMass * impulse.y; + this.m_angularVelocity += this.m_invI * ((point.x - this.m_sweep.c.x) * impulse.y - (point.y - this.m_sweep.c.y) * impulse.x); + } + b2Body.prototype.Split = function (callback) { + var linearVelocity = this.GetLinearVelocity().Copy(); + var angularVelocity = this.GetAngularVelocity(); + var center = this.GetWorldCenter(); + var body1 = this; + var body2 = this.m_world.CreateBody(this.GetDefinition()); + var prev; + for (var f = body1.m_fixtureList; f;) { + if (callback(f)) { + var next = f.m_next; + if (prev) { + prev.m_next = next; + } + else { + body1.m_fixtureList = next; + } + body1.m_fixtureCount--; + f.m_next = body2.m_fixtureList; + body2.m_fixtureList = f; + body2.m_fixtureCount++; + f.m_body = body2; + f = next; + } + else { + prev = f; + f = f.m_next; + } + } + body1.ResetMassData(); + body2.ResetMassData(); + var center1 = body1.GetWorldCenter(); + var center2 = body2.GetWorldCenter(); + var velocity1 = b2Math.AddVV(linearVelocity, b2Math.CrossFV(angularVelocity, b2Math.SubtractVV(center1, center))); + var velocity2 = b2Math.AddVV(linearVelocity, b2Math.CrossFV(angularVelocity, b2Math.SubtractVV(center2, center))); + body1.SetLinearVelocity(velocity1); + body2.SetLinearVelocity(velocity2); + body1.SetAngularVelocity(angularVelocity); + body2.SetAngularVelocity(angularVelocity); + body1.SynchronizeFixtures(); + body2.SynchronizeFixtures(); + return body2; + } + b2Body.prototype.Merge = function (other) { + var f; + for (f = other.m_fixtureList; + f;) { + var next = f.m_next; + other.m_fixtureCount--; + f.m_next = this.m_fixtureList; + this.m_fixtureList = f; + this.m_fixtureCount++; + f.m_body = body2; + f = next; + } + body1.m_fixtureCount = 0; + var body1 = this; + var body2 = other; + var center1 = body1.GetWorldCenter(); + var center2 = body2.GetWorldCenter(); + var velocity1 = body1.GetLinearVelocity().Copy(); + var velocity2 = body2.GetLinearVelocity().Copy(); + var angular1 = body1.GetAngularVelocity(); + var angular = body2.GetAngularVelocity(); + body1.ResetMassData(); + this.SynchronizeFixtures(); + } + b2Body.prototype.GetMass = function () { + return this.m_mass; + } + b2Body.prototype.GetInertia = function () { + return this.m_I; + } + b2Body.prototype.GetMassData = function (data) { + data.mass = this.m_mass; + data.I = this.m_I; + data.center.SetV(this.m_sweep.localCenter); + } + b2Body.prototype.SetMassData = function (massData) { + b2Settings.b2Assert(this.m_world.IsLocked() == false); + if (this.m_world.IsLocked() == true) { + return; + } + if (this.m_type != b2Body.b2_dynamicBody) { + return; + } + this.m_invMass = 0.0; + this.m_I = 0.0; + this.m_invI = 0.0; + this.m_mass = massData.mass; + if (this.m_mass <= 0.0) { + this.m_mass = 1.0; + } + this.m_invMass = 1.0 / this.m_mass; + if (massData.I > 0.0 && (this.m_flags & b2Body.e_fixedRotationFlag) == 0) { + this.m_I = massData.I - this.m_mass * (massData.center.x * massData.center.x + massData.center.y * massData.center.y); + this.m_invI = 1.0 / this.m_I; + } + var oldCenter = this.m_sweep.c.Copy(); + this.m_sweep.localCenter.SetV(massData.center); + this.m_sweep.c0.SetV(b2Math.MulX(this.m_xf, this.m_sweep.localCenter)); + this.m_sweep.c.SetV(this.m_sweep.c0); + this.m_linearVelocity.x += this.m_angularVelocity * (-(this.m_sweep.c.y - oldCenter.y)); + this.m_linearVelocity.y += this.m_angularVelocity * (+(this.m_sweep.c.x - oldCenter.x)); + } + b2Body.prototype.ResetMassData = function () { + this.m_mass = 0.0; + this.m_invMass = 0.0; + this.m_I = 0.0; + this.m_invI = 0.0; + this.m_sweep.localCenter.SetZero(); + if (this.m_type == b2Body.b2_staticBody || this.m_type == b2Body.b2_kinematicBody) { + return; + } + var center = b2Vec2.Make(0, 0); + for (var f = this.m_fixtureList; f; f = f.m_next) { + if (f.m_density == 0.0) { + continue; + } + var massData = f.GetMassData(); + this.m_mass += massData.mass; + center.x += massData.center.x * massData.mass; + center.y += massData.center.y * massData.mass; + this.m_I += massData.I; + } + if (this.m_mass > 0.0) { + this.m_invMass = 1.0 / this.m_mass; + center.x *= this.m_invMass; + center.y *= this.m_invMass; + } + else { + this.m_mass = 1.0; + this.m_invMass = 1.0; + } + if (this.m_I > 0.0 && (this.m_flags & b2Body.e_fixedRotationFlag) == 0) { + this.m_I -= this.m_mass * (center.x * center.x + center.y * center.y); + this.m_I *= this.m_inertiaScale; + b2Settings.b2Assert(this.m_I > 0); + this.m_invI = 1.0 / this.m_I; + } + else { + this.m_I = 0.0; + this.m_invI = 0.0; + } + var oldCenter = this.m_sweep.c.Copy(); + this.m_sweep.localCenter.SetV(center); + this.m_sweep.c0.SetV(b2Math.MulX(this.m_xf, this.m_sweep.localCenter)); + this.m_sweep.c.SetV(this.m_sweep.c0); + this.m_linearVelocity.x += this.m_angularVelocity * (-(this.m_sweep.c.y - oldCenter.y)); + this.m_linearVelocity.y += this.m_angularVelocity * (+(this.m_sweep.c.x - oldCenter.x)); + } + b2Body.prototype.GetWorldPoint = function (localPoint) { + var A = this.m_xf.R; + var u = new b2Vec2(A.col1.x * localPoint.x + A.col2.x * localPoint.y, A.col1.y * localPoint.x + A.col2.y * localPoint.y); + u.x += this.m_xf.position.x; + u.y += this.m_xf.position.y; + return u; + } + b2Body.prototype.GetWorldVector = function (localVector) { + return b2Math.MulMV(this.m_xf.R, localVector); + } + b2Body.prototype.GetLocalPoint = function (worldPoint) { + return b2Math.MulXT(this.m_xf, worldPoint); + } + b2Body.prototype.GetLocalVector = function (worldVector) { + return b2Math.MulTMV(this.m_xf.R, worldVector); + } + b2Body.prototype.GetLinearVelocityFromWorldPoint = function (worldPoint) { + return new b2Vec2(this.m_linearVelocity.x - this.m_angularVelocity * (worldPoint.y - this.m_sweep.c.y), this.m_linearVelocity.y + this.m_angularVelocity * (worldPoint.x - this.m_sweep.c.x)); + } + b2Body.prototype.GetLinearVelocityFromLocalPoint = function (localPoint) { + var A = this.m_xf.R; + var worldPoint = new b2Vec2(A.col1.x * localPoint.x + A.col2.x * localPoint.y, A.col1.y * localPoint.x + A.col2.y * localPoint.y); + worldPoint.x += this.m_xf.position.x; + worldPoint.y += this.m_xf.position.y; + return new b2Vec2(this.m_linearVelocity.x - this.m_angularVelocity * (worldPoint.y - this.m_sweep.c.y), this.m_linearVelocity.y + this.m_angularVelocity * (worldPoint.x - this.m_sweep.c.x)); + } + b2Body.prototype.GetLinearDamping = function () { + return this.m_linearDamping; + } + b2Body.prototype.SetLinearDamping = function (linearDamping) { + if (linearDamping === undefined) linearDamping = 0; + this.m_linearDamping = linearDamping; + } + b2Body.prototype.GetAngularDamping = function () { + return this.m_angularDamping; + } + b2Body.prototype.SetAngularDamping = function (angularDamping) { + if (angularDamping === undefined) angularDamping = 0; + this.m_angularDamping = angularDamping; + } + b2Body.prototype.SetType = function (type) { + if (type === undefined) type = 0; + if (this.m_type == type) { + return; + } + this.m_type = type; + this.ResetMassData(); + if (this.m_type == b2Body.b2_staticBody) { + this.m_linearVelocity.SetZero(); + this.m_angularVelocity = 0.0; + } + this.SetAwake(true); + this.m_force.SetZero(); + this.m_torque = 0.0; + for (var ce = this.m_contactList; ce; ce = ce.next) { + ce.contact.FlagForFiltering(); + } + } + b2Body.prototype.GetType = function () { + return this.m_type; + } + b2Body.prototype.SetBullet = function (flag) { + if (flag) { + this.m_flags |= b2Body.e_bulletFlag; + } + else { + this.m_flags &= ~b2Body.e_bulletFlag; + } + } + b2Body.prototype.IsBullet = function () { + return (this.m_flags & b2Body.e_bulletFlag) == b2Body.e_bulletFlag; + } + b2Body.prototype.SetSleepingAllowed = function (flag) { + if (flag) { + this.m_flags |= b2Body.e_allowSleepFlag; + } + else { + this.m_flags &= ~b2Body.e_allowSleepFlag; + this.SetAwake(true); + } + } + b2Body.prototype.SetAwake = function (flag) { + if (flag) { + this.m_flags |= b2Body.e_awakeFlag; + this.m_sleepTime = 0.0; + } + else { + this.m_flags &= ~b2Body.e_awakeFlag; + this.m_sleepTime = 0.0; + this.m_linearVelocity.SetZero(); + this.m_angularVelocity = 0.0; + this.m_force.SetZero(); + this.m_torque = 0.0; + } + } + b2Body.prototype.IsAwake = function () { + return (this.m_flags & b2Body.e_awakeFlag) == b2Body.e_awakeFlag; + } + b2Body.prototype.SetFixedRotation = function (fixed) { + if (fixed) { + this.m_flags |= b2Body.e_fixedRotationFlag; + } + else { + this.m_flags &= ~b2Body.e_fixedRotationFlag; + } + this.ResetMassData(); + } + b2Body.prototype.IsFixedRotation = function () { + return (this.m_flags & b2Body.e_fixedRotationFlag) == b2Body.e_fixedRotationFlag; + } + b2Body.prototype.SetActive = function (flag) { + if (flag == this.IsActive()) { + return; + } + var broadPhase; + var f; + if (flag) { + this.m_flags |= b2Body.e_activeFlag; + broadPhase = this.m_world.m_contactManager.m_broadPhase; + for (f = this.m_fixtureList; + f; f = f.m_next) { + f.CreateProxy(broadPhase, this.m_xf); + } + } + else { + this.m_flags &= ~b2Body.e_activeFlag; + broadPhase = this.m_world.m_contactManager.m_broadPhase; + for (f = this.m_fixtureList; + f; f = f.m_next) { + f.DestroyProxy(broadPhase); + } + var ce = this.m_contactList; + while (ce) { + var ce0 = ce; + ce = ce.next; + this.m_world.m_contactManager.Destroy(ce0.contact); + } + this.m_contactList = null; + } + } + b2Body.prototype.IsActive = function () { + return (this.m_flags & b2Body.e_activeFlag) == b2Body.e_activeFlag; + } + b2Body.prototype.IsSleepingAllowed = function () { + return (this.m_flags & b2Body.e_allowSleepFlag) == b2Body.e_allowSleepFlag; + } + b2Body.prototype.GetFixtureList = function () { + return this.m_fixtureList; + } + b2Body.prototype.GetJointList = function () { + return this.m_jointList; + } + b2Body.prototype.GetControllerList = function () { + return this.m_controllerList; + } + b2Body.prototype.GetContactList = function () { + return this.m_contactList; + } + b2Body.prototype.GetNext = function () { + return this.m_next; + } + b2Body.prototype.GetUserData = function () { + return this.m_userData; + } + b2Body.prototype.SetUserData = function (data) { + this.m_userData = data; + } + b2Body.prototype.GetWorld = function () { + return this.m_world; + } + b2Body.prototype.b2Body = function (bd, world) { + this.m_flags = 0; + if (bd.bullet) { + this.m_flags |= b2Body.e_bulletFlag; + } + if (bd.fixedRotation) { + this.m_flags |= b2Body.e_fixedRotationFlag; + } + if (bd.allowSleep) { + this.m_flags |= b2Body.e_allowSleepFlag; + } + if (bd.awake) { + this.m_flags |= b2Body.e_awakeFlag; + } + if (bd.active) { + this.m_flags |= b2Body.e_activeFlag; + } + this.m_world = world; + this.m_xf.position.SetV(bd.position); + this.m_xf.R.Set(bd.angle); + this.m_sweep.localCenter.SetZero(); + this.m_sweep.t0 = 1.0; + this.m_sweep.a0 = this.m_sweep.a = bd.angle; + var tMat = this.m_xf.R; + var tVec = this.m_sweep.localCenter; + this.m_sweep.c.x = (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + this.m_sweep.c.y = (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + this.m_sweep.c.x += this.m_xf.position.x; + this.m_sweep.c.y += this.m_xf.position.y; + this.m_sweep.c0.SetV(this.m_sweep.c); + this.m_jointList = null; + this.m_controllerList = null; + this.m_contactList = null; + this.m_controllerCount = 0; + this.m_prev = null; + this.m_next = null; + this.m_linearVelocity.SetV(bd.linearVelocity); + this.m_angularVelocity = bd.angularVelocity; + this.m_linearDamping = bd.linearDamping; + this.m_angularDamping = bd.angularDamping; + this.m_force.Set(0.0, 0.0); + this.m_torque = 0.0; + this.m_sleepTime = 0.0; + this.m_type = bd.type; + if (this.m_type == b2Body.b2_dynamicBody) { + this.m_mass = 1.0; + this.m_invMass = 1.0; + } + else { + this.m_mass = 0.0; + this.m_invMass = 0.0; + } + this.m_I = 0.0; + this.m_invI = 0.0; + this.m_inertiaScale = bd.inertiaScale; + this.m_userData = bd.userData; + this.m_fixtureList = null; + this.m_fixtureCount = 0; + } + b2Body.prototype.SynchronizeFixtures = function () { + var xf1 = b2Body.s_xf1; + xf1.R.Set(this.m_sweep.a0); + var tMat = xf1.R; + var tVec = this.m_sweep.localCenter; + xf1.position.x = this.m_sweep.c0.x - (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + xf1.position.y = this.m_sweep.c0.y - (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + var f; + var broadPhase = this.m_world.m_contactManager.m_broadPhase; + for (f = this.m_fixtureList; + f; f = f.m_next) { + f.Synchronize(broadPhase, xf1, this.m_xf); + } + } + b2Body.prototype.SynchronizeTransform = function () { + this.m_xf.R.Set(this.m_sweep.a); + var tMat = this.m_xf.R; + var tVec = this.m_sweep.localCenter; + this.m_xf.position.x = this.m_sweep.c.x - (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + this.m_xf.position.y = this.m_sweep.c.y - (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + } + b2Body.prototype.ShouldCollide = function (other) { + if (this.m_type != b2Body.b2_dynamicBody && other.m_type != b2Body.b2_dynamicBody) { + return false; + } + for (var jn = this.m_jointList; jn; jn = jn.next) { + if (jn.other == other) if (jn.joint.m_collideConnected == false) { + return false; + } + } + return true; + } + b2Body.prototype.Advance = function (t) { + if (t === undefined) t = 0; + this.m_sweep.Advance(t); + this.m_sweep.c.SetV(this.m_sweep.c0); + this.m_sweep.a = this.m_sweep.a0; + this.SynchronizeTransform(); + } + Box2D.postDefs.push(function () { + Box2D.Dynamics.b2Body.s_xf1 = new b2Transform(); + Box2D.Dynamics.b2Body.e_islandFlag = 0x0001; + Box2D.Dynamics.b2Body.e_awakeFlag = 0x0002; + Box2D.Dynamics.b2Body.e_allowSleepFlag = 0x0004; + Box2D.Dynamics.b2Body.e_bulletFlag = 0x0008; + Box2D.Dynamics.b2Body.e_fixedRotationFlag = 0x0010; + Box2D.Dynamics.b2Body.e_activeFlag = 0x0020; + Box2D.Dynamics.b2Body.b2_staticBody = 0; + Box2D.Dynamics.b2Body.b2_kinematicBody = 1; + Box2D.Dynamics.b2Body.b2_dynamicBody = 2; + }); + b2BodyDef.b2BodyDef = function () { + this.position = new b2Vec2(); + this.linearVelocity = new b2Vec2(); + }; + b2BodyDef.prototype.b2BodyDef = function () { + this.userData = null; + this.position.Set(0.0, 0.0); + this.angle = 0.0; + this.linearVelocity.Set(0, 0); + this.angularVelocity = 0.0; + this.linearDamping = 0.0; + this.angularDamping = 0.0; + this.allowSleep = true; + this.awake = true; + this.fixedRotation = false; + this.bullet = false; + this.type = b2Body.b2_staticBody; + this.active = true; + this.inertiaScale = 1.0; + } + b2ContactFilter.b2ContactFilter = function () {}; + b2ContactFilter.prototype.ShouldCollide = function (fixtureA, fixtureB) { + var filter1 = fixtureA.GetFilterData(); + var filter2 = fixtureB.GetFilterData(); + if (filter1.groupIndex == filter2.groupIndex && filter1.groupIndex != 0) { + return filter1.groupIndex > 0; + } + var collide = (filter1.maskBits & filter2.categoryBits) != 0 && (filter1.categoryBits & filter2.maskBits) != 0; + return collide; + } + b2ContactFilter.prototype.RayCollide = function (userData, fixture) { + if (!userData) return true; + return this.ShouldCollide((userData instanceof b2Fixture ? userData : null), fixture); + } + Box2D.postDefs.push(function () { + Box2D.Dynamics.b2ContactFilter.b2_defaultFilter = new b2ContactFilter(); + }); + b2ContactImpulse.b2ContactImpulse = function () { + this.normalImpulses = new Vector_a2j_Number(b2Settings.b2_maxManifoldPoints); + this.tangentImpulses = new Vector_a2j_Number(b2Settings.b2_maxManifoldPoints); + }; + b2ContactListener.b2ContactListener = function () {}; + b2ContactListener.prototype.BeginContact = function (contact) {} + b2ContactListener.prototype.EndContact = function (contact) {} + b2ContactListener.prototype.PreSolve = function (contact, oldManifold) {} + b2ContactListener.prototype.PostSolve = function (contact, impulse) {} + Box2D.postDefs.push(function () { + Box2D.Dynamics.b2ContactListener.b2_defaultListener = new b2ContactListener(); + }); + b2ContactManager.b2ContactManager = function () {}; + b2ContactManager.prototype.b2ContactManager = function () { + this.m_world = null; + this.m_contactCount = 0; + this.m_contactFilter = b2ContactFilter.b2_defaultFilter; + this.m_contactListener = b2ContactListener.b2_defaultListener; + this.m_contactFactory = new b2ContactFactory(this.m_allocator); + this.m_broadPhase = new b2DynamicTreeBroadPhase(); + } + b2ContactManager.prototype.AddPair = function (proxyUserDataA, proxyUserDataB) { + var fixtureA = (proxyUserDataA instanceof b2Fixture ? proxyUserDataA : null); + var fixtureB = (proxyUserDataB instanceof b2Fixture ? proxyUserDataB : null); + var bodyA = fixtureA.GetBody(); + var bodyB = fixtureB.GetBody(); + if (bodyA == bodyB) return; + var edge = bodyB.GetContactList(); + while (edge) { + if (edge.other == bodyA) { + var fA = edge.contact.GetFixtureA(); + var fB = edge.contact.GetFixtureB(); + if (fA == fixtureA && fB == fixtureB) return; + if (fA == fixtureB && fB == fixtureA) return; + } + edge = edge.next; + } + if (bodyB.ShouldCollide(bodyA) == false) { + return; + } + if (this.m_contactFilter.ShouldCollide(fixtureA, fixtureB) == false) { + return; + } + var c = this.m_contactFactory.Create(fixtureA, fixtureB); + fixtureA = c.GetFixtureA(); + fixtureB = c.GetFixtureB(); + bodyA = fixtureA.m_body; + bodyB = fixtureB.m_body; + c.m_prev = null; + c.m_next = this.m_world.m_contactList; + if (this.m_world.m_contactList != null) { + this.m_world.m_contactList.m_prev = c; + } + this.m_world.m_contactList = c; + c.m_nodeA.contact = c; + c.m_nodeA.other = bodyB; + c.m_nodeA.prev = null; + c.m_nodeA.next = bodyA.m_contactList; + if (bodyA.m_contactList != null) { + bodyA.m_contactList.prev = c.m_nodeA; + } + bodyA.m_contactList = c.m_nodeA; + c.m_nodeB.contact = c; + c.m_nodeB.other = bodyA; + c.m_nodeB.prev = null; + c.m_nodeB.next = bodyB.m_contactList; + if (bodyB.m_contactList != null) { + bodyB.m_contactList.prev = c.m_nodeB; + } + bodyB.m_contactList = c.m_nodeB; + ++this.m_world.m_contactCount; + return; + } + b2ContactManager.prototype.FindNewContacts = function () { + this.m_broadPhase.UpdatePairs(Box2D.generateCallback(this, this.AddPair)); + } + b2ContactManager.prototype.Destroy = function (c) { + var fixtureA = c.GetFixtureA(); + var fixtureB = c.GetFixtureB(); + var bodyA = fixtureA.GetBody(); + var bodyB = fixtureB.GetBody(); + if (c.IsTouching()) { + this.m_contactListener.EndContact(c); + } + if (c.m_prev) { + c.m_prev.m_next = c.m_next; + } + if (c.m_next) { + c.m_next.m_prev = c.m_prev; + } + if (c == this.m_world.m_contactList) { + this.m_world.m_contactList = c.m_next; + } + if (c.m_nodeA.prev) { + c.m_nodeA.prev.next = c.m_nodeA.next; + } + if (c.m_nodeA.next) { + c.m_nodeA.next.prev = c.m_nodeA.prev; + } + if (c.m_nodeA == bodyA.m_contactList) { + bodyA.m_contactList = c.m_nodeA.next; + } + if (c.m_nodeB.prev) { + c.m_nodeB.prev.next = c.m_nodeB.next; + } + if (c.m_nodeB.next) { + c.m_nodeB.next.prev = c.m_nodeB.prev; + } + if (c.m_nodeB == bodyB.m_contactList) { + bodyB.m_contactList = c.m_nodeB.next; + } + this.m_contactFactory.Destroy(c); + --this.m_contactCount; + } + b2ContactManager.prototype.Collide = function () { + var c = this.m_world.m_contactList; + while (c) { + var fixtureA = c.GetFixtureA(); + var fixtureB = c.GetFixtureB(); + var bodyA = fixtureA.GetBody(); + var bodyB = fixtureB.GetBody(); + if (bodyA.IsAwake() == false && bodyB.IsAwake() == false) { + c = c.GetNext(); + continue; + } + if (c.m_flags & b2Contact.e_filterFlag) { + if (bodyB.ShouldCollide(bodyA) == false) { + var cNuke = c; + c = cNuke.GetNext(); + this.Destroy(cNuke); + continue; + } + if (this.m_contactFilter.ShouldCollide(fixtureA, fixtureB) == false) { + cNuke = c; + c = cNuke.GetNext(); + this.Destroy(cNuke); + continue; + } + c.m_flags &= ~b2Contact.e_filterFlag; + } + var proxyA = fixtureA.m_proxy; + var proxyB = fixtureB.m_proxy; + var overlap = this.m_broadPhase.TestOverlap(proxyA, proxyB); + if (overlap == false) { + cNuke = c; + c = cNuke.GetNext(); + this.Destroy(cNuke); + continue; + } + c.Update(this.m_contactListener); + c = c.GetNext(); + } + } + Box2D.postDefs.push(function () { + Box2D.Dynamics.b2ContactManager.s_evalCP = new b2ContactPoint(); + }); + b2DebugDraw.b2DebugDraw = function () {}; + b2DebugDraw.prototype.b2DebugDraw = function () {} + b2DebugDraw.prototype.SetFlags = function (flags) { + if (flags === undefined) flags = 0; + } + b2DebugDraw.prototype.GetFlags = function () {} + b2DebugDraw.prototype.AppendFlags = function (flags) { + if (flags === undefined) flags = 0; + } + b2DebugDraw.prototype.ClearFlags = function (flags) { + if (flags === undefined) flags = 0; + } + b2DebugDraw.prototype.SetSprite = function (sprite) {} + b2DebugDraw.prototype.GetSprite = function () {} + b2DebugDraw.prototype.SetDrawScale = function (drawScale) { + if (drawScale === undefined) drawScale = 0; + } + b2DebugDraw.prototype.GetDrawScale = function () {} + b2DebugDraw.prototype.SetLineThickness = function (lineThickness) { + if (lineThickness === undefined) lineThickness = 0; + } + b2DebugDraw.prototype.GetLineThickness = function () {} + b2DebugDraw.prototype.SetAlpha = function (alpha) { + if (alpha === undefined) alpha = 0; + } + b2DebugDraw.prototype.GetAlpha = function () {} + b2DebugDraw.prototype.SetFillAlpha = function (alpha) { + if (alpha === undefined) alpha = 0; + } + b2DebugDraw.prototype.GetFillAlpha = function () {} + b2DebugDraw.prototype.SetXFormScale = function (xformScale) { + if (xformScale === undefined) xformScale = 0; + } + b2DebugDraw.prototype.GetXFormScale = function () {} + b2DebugDraw.prototype.DrawPolygon = function (vertices, vertexCount, color) { + if (vertexCount === undefined) vertexCount = 0; + } + b2DebugDraw.prototype.DrawSolidPolygon = function (vertices, vertexCount, color) { + if (vertexCount === undefined) vertexCount = 0; + } + b2DebugDraw.prototype.DrawCircle = function (center, radius, color) { + if (radius === undefined) radius = 0; + } + b2DebugDraw.prototype.DrawSolidCircle = function (center, radius, axis, color) { + if (radius === undefined) radius = 0; + } + b2DebugDraw.prototype.DrawSegment = function (p1, p2, color) {} + b2DebugDraw.prototype.DrawTransform = function (xf) {} + Box2D.postDefs.push(function () { + Box2D.Dynamics.b2DebugDraw.e_shapeBit = 0x0001; + Box2D.Dynamics.b2DebugDraw.e_jointBit = 0x0002; + Box2D.Dynamics.b2DebugDraw.e_aabbBit = 0x0004; + Box2D.Dynamics.b2DebugDraw.e_pairBit = 0x0008; + Box2D.Dynamics.b2DebugDraw.e_centerOfMassBit = 0x0010; + Box2D.Dynamics.b2DebugDraw.e_controllerBit = 0x0020; + }); + b2DestructionListener.b2DestructionListener = function () {}; + b2DestructionListener.prototype.SayGoodbyeJoint = function (joint) {} + b2DestructionListener.prototype.SayGoodbyeFixture = function (fixture) {} + b2FilterData.b2FilterData = function () { + this.categoryBits = 0x0001; + this.maskBits = 0xFFFF; + this.groupIndex = 0; + }; + b2FilterData.prototype.Copy = function () { + var copy = new b2FilterData(); + copy.categoryBits = this.categoryBits; + copy.maskBits = this.maskBits; + copy.groupIndex = this.groupIndex; + return copy; + } + b2Fixture.b2Fixture = function () { + this.m_filter = new b2FilterData(); + }; + b2Fixture.prototype.GetType = function () { + return this.m_shape.GetType(); + } + b2Fixture.prototype.GetShape = function () { + return this.m_shape; + } + b2Fixture.prototype.SetSensor = function (sensor) { + if (this.m_isSensor == sensor) return; + this.m_isSensor = sensor; + if (this.m_body == null) return; + var edge = this.m_body.GetContactList(); + while (edge) { + var contact = edge.contact; + var fixtureA = contact.GetFixtureA(); + var fixtureB = contact.GetFixtureB(); + if (fixtureA == this || fixtureB == this) contact.SetSensor(fixtureA.IsSensor() || fixtureB.IsSensor()); + edge = edge.next; + } + } + b2Fixture.prototype.IsSensor = function () { + return this.m_isSensor; + } + b2Fixture.prototype.SetFilterData = function (filter) { + this.m_filter = filter.Copy(); + if (this.m_body) return; + var edge = this.m_body.GetContactList(); + while (edge) { + var contact = edge.contact; + var fixtureA = contact.GetFixtureA(); + var fixtureB = contact.GetFixtureB(); + if (fixtureA == this || fixtureB == this) contact.FlagForFiltering(); + edge = edge.next; + } + } + b2Fixture.prototype.GetFilterData = function () { + return this.m_filter.Copy(); + } + b2Fixture.prototype.GetBody = function () { + return this.m_body; + } + b2Fixture.prototype.GetNext = function () { + return this.m_next; + } + b2Fixture.prototype.GetUserData = function () { + return this.m_userData; + } + b2Fixture.prototype.SetUserData = function (data) { + this.m_userData = data; + } + b2Fixture.prototype.TestPoint = function (p) { + return this.m_shape.TestPoint(this.m_body.GetTransform(), p); + } + b2Fixture.prototype.RayCast = function (output, input) { + return this.m_shape.RayCast(output, input, this.m_body.GetTransform()); + } + b2Fixture.prototype.GetMassData = function (massData) { + if (massData === undefined) massData = null; + if (massData == null) { + massData = new b2MassData(); + } + this.m_shape.ComputeMass(massData, this.m_density); + return massData; + } + b2Fixture.prototype.SetDensity = function (density) { + if (density === undefined) density = 0; + this.m_density = density; + } + b2Fixture.prototype.GetDensity = function () { + return this.m_density; + } + b2Fixture.prototype.GetFriction = function () { + return this.m_friction; + } + b2Fixture.prototype.SetFriction = function (friction) { + if (friction === undefined) friction = 0; + this.m_friction = friction; + } + b2Fixture.prototype.GetRestitution = function () { + return this.m_restitution; + } + b2Fixture.prototype.SetRestitution = function (restitution) { + if (restitution === undefined) restitution = 0; + this.m_restitution = restitution; + } + b2Fixture.prototype.GetAABB = function () { + return this.m_aabb; + } + b2Fixture.prototype.b2Fixture = function () { + this.m_aabb = new b2AABB(); + this.m_userData = null; + this.m_body = null; + this.m_next = null; + this.m_shape = null; + this.m_density = 0.0; + this.m_friction = 0.0; + this.m_restitution = 0.0; + } + b2Fixture.prototype.Create = function (body, xf, def) { + this.m_userData = def.userData; + this.m_friction = def.friction; + this.m_restitution = def.restitution; + this.m_body = body; + this.m_next = null; + this.m_filter = def.filter.Copy(); + this.m_isSensor = def.isSensor; + this.m_shape = def.shape.Copy(); + this.m_density = def.density; + } + b2Fixture.prototype.Destroy = function () { + this.m_shape = null; + } + b2Fixture.prototype.CreateProxy = function (broadPhase, xf) { + this.m_shape.ComputeAABB(this.m_aabb, xf); + this.m_proxy = broadPhase.CreateProxy(this.m_aabb, this); + } + b2Fixture.prototype.DestroyProxy = function (broadPhase) { + if (this.m_proxy == null) { + return; + } + broadPhase.DestroyProxy(this.m_proxy); + this.m_proxy = null; + } + b2Fixture.prototype.Synchronize = function (broadPhase, transform1, transform2) { + if (!this.m_proxy) return; + var aabb1 = new b2AABB(); + var aabb2 = new b2AABB(); + this.m_shape.ComputeAABB(aabb1, transform1); + this.m_shape.ComputeAABB(aabb2, transform2); + this.m_aabb.Combine(aabb1, aabb2); + var displacement = b2Math.SubtractVV(transform2.position, transform1.position); + broadPhase.MoveProxy(this.m_proxy, this.m_aabb, displacement); + } + b2FixtureDef.b2FixtureDef = function () { + this.filter = new b2FilterData(); + }; + b2FixtureDef.prototype.b2FixtureDef = function () { + this.shape = null; + this.userData = null; + this.friction = 0.2; + this.restitution = 0.0; + this.density = 0.0; + this.filter.categoryBits = 0x0001; + this.filter.maskBits = 0xFFFF; + this.filter.groupIndex = 0; + this.isSensor = false; + } + b2Island.b2Island = function () {}; + b2Island.prototype.b2Island = function () { + this.m_bodies = new Vector(); + this.m_contacts = new Vector(); + this.m_joints = new Vector(); + } + b2Island.prototype.Initialize = function (bodyCapacity, contactCapacity, jointCapacity, allocator, listener, contactSolver) { + if (bodyCapacity === undefined) bodyCapacity = 0; + if (contactCapacity === undefined) contactCapacity = 0; + if (jointCapacity === undefined) jointCapacity = 0; + var i = 0; + this.m_bodyCapacity = bodyCapacity; + this.m_contactCapacity = contactCapacity; + this.m_jointCapacity = jointCapacity; + this.m_bodyCount = 0; + this.m_contactCount = 0; + this.m_jointCount = 0; + this.m_allocator = allocator; + this.m_listener = listener; + this.m_contactSolver = contactSolver; + for (i = this.m_bodies.length; + i < bodyCapacity; i++) + this.m_bodies[i] = null; + for (i = this.m_contacts.length; + i < contactCapacity; i++) + this.m_contacts[i] = null; + for (i = this.m_joints.length; + i < jointCapacity; i++) + this.m_joints[i] = null; + } + b2Island.prototype.Clear = function () { + this.m_bodyCount = 0; + this.m_contactCount = 0; + this.m_jointCount = 0; + } + b2Island.prototype.Solve = function (step, gravity, allowSleep) { + var i = 0; + var j = 0; + var b; + var joint; + for (i = 0; + i < this.m_bodyCount; ++i) { + b = this.m_bodies[i]; + if (b.GetType() != b2Body.b2_dynamicBody) continue; + b.m_linearVelocity.x += step.dt * (gravity.x + b.m_invMass * b.m_force.x); + b.m_linearVelocity.y += step.dt * (gravity.y + b.m_invMass * b.m_force.y); + b.m_angularVelocity += step.dt * b.m_invI * b.m_torque; + b.m_linearVelocity.Multiply(b2Math.Clamp(1.0 - step.dt * b.m_linearDamping, 0.0, 1.0)); + b.m_angularVelocity *= b2Math.Clamp(1.0 - step.dt * b.m_angularDamping, 0.0, 1.0); + } + this.m_contactSolver.Initialize(step, this.m_contacts, this.m_contactCount, this.m_allocator); + var contactSolver = this.m_contactSolver; + contactSolver.InitVelocityConstraints(step); + for (i = 0; + i < this.m_jointCount; ++i) { + joint = this.m_joints[i]; + joint.InitVelocityConstraints(step); + } + for (i = 0; + i < step.velocityIterations; ++i) { + for (j = 0; + j < this.m_jointCount; ++j) { + joint = this.m_joints[j]; + joint.SolveVelocityConstraints(step); + } + contactSolver.SolveVelocityConstraints(); + } + for (i = 0; + i < this.m_jointCount; ++i) { + joint = this.m_joints[i]; + joint.FinalizeVelocityConstraints(); + } + contactSolver.FinalizeVelocityConstraints(); + for (i = 0; + i < this.m_bodyCount; ++i) { + b = this.m_bodies[i]; + if (b.GetType() == b2Body.b2_staticBody) continue; + var translationX = step.dt * b.m_linearVelocity.x; + var translationY = step.dt * b.m_linearVelocity.y; + if ((translationX * translationX + translationY * translationY) > b2Settings.b2_maxTranslationSquared) { + b.m_linearVelocity.Normalize(); + b.m_linearVelocity.x *= b2Settings.b2_maxTranslation * step.inv_dt; + b.m_linearVelocity.y *= b2Settings.b2_maxTranslation * step.inv_dt; + } + var rotation = step.dt * b.m_angularVelocity; + if (rotation * rotation > b2Settings.b2_maxRotationSquared) { + if (b.m_angularVelocity < 0.0) { + b.m_angularVelocity = (-b2Settings.b2_maxRotation * step.inv_dt); + } + else { + b.m_angularVelocity = b2Settings.b2_maxRotation * step.inv_dt; + } + } + b.m_sweep.c0.SetV(b.m_sweep.c); + b.m_sweep.a0 = b.m_sweep.a; + b.m_sweep.c.x += step.dt * b.m_linearVelocity.x; + b.m_sweep.c.y += step.dt * b.m_linearVelocity.y; + b.m_sweep.a += step.dt * b.m_angularVelocity; + b.SynchronizeTransform(); + } + for (i = 0; + i < step.positionIterations; ++i) { + var contactsOkay = contactSolver.SolvePositionConstraints(b2Settings.b2_contactBaumgarte); + var jointsOkay = true; + for (j = 0; + j < this.m_jointCount; ++j) { + joint = this.m_joints[j]; + var jointOkay = joint.SolvePositionConstraints(b2Settings.b2_contactBaumgarte); + jointsOkay = jointsOkay && jointOkay; + } + if (contactsOkay && jointsOkay) { + break; + } + } + this.Report(contactSolver.m_constraints); + if (allowSleep) { + var minSleepTime = Number.MAX_VALUE; + var linTolSqr = b2Settings.b2_linearSleepTolerance * b2Settings.b2_linearSleepTolerance; + var angTolSqr = b2Settings.b2_angularSleepTolerance * b2Settings.b2_angularSleepTolerance; + for (i = 0; + i < this.m_bodyCount; ++i) { + b = this.m_bodies[i]; + if (b.GetType() == b2Body.b2_staticBody) { + continue; + } + if ((b.m_flags & b2Body.e_allowSleepFlag) == 0) { + b.m_sleepTime = 0.0; + minSleepTime = 0.0; + } + if ((b.m_flags & b2Body.e_allowSleepFlag) == 0 || b.m_angularVelocity * b.m_angularVelocity > angTolSqr || b2Math.Dot(b.m_linearVelocity, b.m_linearVelocity) > linTolSqr) { + b.m_sleepTime = 0.0; + minSleepTime = 0.0; + } + else { + b.m_sleepTime += step.dt; + minSleepTime = b2Math.Min(minSleepTime, b.m_sleepTime); + } + } + if (minSleepTime >= b2Settings.b2_timeToSleep) { + for (i = 0; + i < this.m_bodyCount; ++i) { + b = this.m_bodies[i]; + b.SetAwake(false); + } + } + } + } + b2Island.prototype.SolveTOI = function (subStep) { + var i = 0; + var j = 0; + this.m_contactSolver.Initialize(subStep, this.m_contacts, this.m_contactCount, this.m_allocator); + var contactSolver = this.m_contactSolver; + for (i = 0; + i < this.m_jointCount; ++i) { + this.m_joints[i].InitVelocityConstraints(subStep); + } + for (i = 0; + i < subStep.velocityIterations; ++i) { + contactSolver.SolveVelocityConstraints(); + for (j = 0; + j < this.m_jointCount; ++j) { + this.m_joints[j].SolveVelocityConstraints(subStep); + } + } + for (i = 0; + i < this.m_bodyCount; ++i) { + var b = this.m_bodies[i]; + if (b.GetType() == b2Body.b2_staticBody) continue; + var translationX = subStep.dt * b.m_linearVelocity.x; + var translationY = subStep.dt * b.m_linearVelocity.y; + if ((translationX * translationX + translationY * translationY) > b2Settings.b2_maxTranslationSquared) { + b.m_linearVelocity.Normalize(); + b.m_linearVelocity.x *= b2Settings.b2_maxTranslation * subStep.inv_dt; + b.m_linearVelocity.y *= b2Settings.b2_maxTranslation * subStep.inv_dt; + } + var rotation = subStep.dt * b.m_angularVelocity; + if (rotation * rotation > b2Settings.b2_maxRotationSquared) { + if (b.m_angularVelocity < 0.0) { + b.m_angularVelocity = (-b2Settings.b2_maxRotation * subStep.inv_dt); + } + else { + b.m_angularVelocity = b2Settings.b2_maxRotation * subStep.inv_dt; + } + } + b.m_sweep.c0.SetV(b.m_sweep.c); + b.m_sweep.a0 = b.m_sweep.a; + b.m_sweep.c.x += subStep.dt * b.m_linearVelocity.x; + b.m_sweep.c.y += subStep.dt * b.m_linearVelocity.y; + b.m_sweep.a += subStep.dt * b.m_angularVelocity; + b.SynchronizeTransform(); + } + var k_toiBaumgarte = 0.75; + for (i = 0; + i < subStep.positionIterations; ++i) { + var contactsOkay = contactSolver.SolvePositionConstraints(k_toiBaumgarte); + var jointsOkay = true; + for (j = 0; + j < this.m_jointCount; ++j) { + var jointOkay = this.m_joints[j].SolvePositionConstraints(b2Settings.b2_contactBaumgarte); + jointsOkay = jointsOkay && jointOkay; + } + if (contactsOkay && jointsOkay) { + break; + } + } + this.Report(contactSolver.m_constraints); + } + b2Island.prototype.Report = function (constraints) { + if (this.m_listener == null) { + return; + } + for (var i = 0; i < this.m_contactCount; ++i) { + var c = this.m_contacts[i]; + var cc = constraints[i]; + for (var j = 0; j < cc.pointCount; ++j) { + b2Island.s_impulse.normalImpulses[j] = cc.points[j].normalImpulse; + b2Island.s_impulse.tangentImpulses[j] = cc.points[j].tangentImpulse; + } + this.m_listener.PostSolve(c, b2Island.s_impulse); + } + } + b2Island.prototype.AddBody = function (body) { + body.m_islandIndex = this.m_bodyCount; + this.m_bodies[this.m_bodyCount++] = body; + } + b2Island.prototype.AddContact = function (contact) { + this.m_contacts[this.m_contactCount++] = contact; + } + b2Island.prototype.AddJoint = function (joint) { + this.m_joints[this.m_jointCount++] = joint; + } + Box2D.postDefs.push(function () { + Box2D.Dynamics.b2Island.s_impulse = new b2ContactImpulse(); + }); + b2TimeStep.b2TimeStep = function () {}; + b2TimeStep.prototype.Set = function (step) { + this.dt = step.dt; + this.inv_dt = step.inv_dt; + this.positionIterations = step.positionIterations; + this.velocityIterations = step.velocityIterations; + this.warmStarting = step.warmStarting; + } + b2World.b2World = function () { + this.s_stack = new Vector(); + this.m_contactManager = new b2ContactManager(); + this.m_contactSolver = new b2ContactSolver(); + this.m_island = new b2Island(); + }; + b2World.prototype.b2World = function (gravity, doSleep) { + this.m_destructionListener = null; + this.m_debugDraw = null; + this.m_bodyList = null; + this.m_contactList = null; + this.m_jointList = null; + this.m_controllerList = null; + this.m_bodyCount = 0; + this.m_contactCount = 0; + this.m_jointCount = 0; + this.m_controllerCount = 0; + b2World.m_warmStarting = true; + b2World.m_continuousPhysics = true; + this.m_allowSleep = doSleep; + gravity.y = gravity.y; + this.m_gravity = gravity; + this.m_inv_dt0 = 0.0; + this.m_contactManager.m_world = this; + var bd = new b2BodyDef(); + this.m_groundBody = this.CreateBody(bd); + } + b2World.prototype.SetDestructionListener = function (listener) { + this.m_destructionListener = listener; + } + b2World.prototype.SetContactFilter = function (filter) { + this.m_contactManager.m_contactFilter = filter; + } + b2World.prototype.SetContactListener = function (listener) { + this.m_contactManager.m_contactListener = listener; + } + b2World.prototype.SetDebugDraw = function (debugDraw) { + this.m_debugDraw = debugDraw; + } + b2World.prototype.SetBroadPhase = function (broadPhase) { + var oldBroadPhase = this.m_contactManager.m_broadPhase; + this.m_contactManager.m_broadPhase = broadPhase; + for (var b = this.m_bodyList; b; b = b.m_next) { + for (var f = b.m_fixtureList; f; f = f.m_next) { + f.m_proxy = broadPhase.CreateProxy(oldBroadPhase.GetFatAABB(f.m_proxy), f); + } + } + } + b2World.prototype.Validate = function () { + this.m_contactManager.m_broadPhase.Validate(); + } + b2World.prototype.GetProxyCount = function () { + return this.m_contactManager.m_broadPhase.GetProxyCount(); + } + b2World.prototype.CreateBody = function (def) { + if (this.IsLocked() == true) { + return null; + } + var b = new b2Body(def, this); + b.m_prev = null; + b.m_next = this.m_bodyList; + if (this.m_bodyList) { + this.m_bodyList.m_prev = b; + } + this.m_bodyList = b; + ++this.m_bodyCount; + return b; + } + b2World.prototype.DestroyBody = function (b) { + if (this.IsLocked() == true) { + return; + } + var jn = b.m_jointList; + while (jn) { + var jn0 = jn; + jn = jn.next; + if (this.m_destructionListener) { + this.m_destructionListener.SayGoodbyeJoint(jn0.joint); + } + this.DestroyJoint(jn0.joint); + } + var coe = b.m_controllerList; + while (coe) { + var coe0 = coe; + coe = coe.nextController; + coe0.controller.RemoveBody(b); + } + var ce = b.m_contactList; + while (ce) { + var ce0 = ce; + ce = ce.next; + this.m_contactManager.Destroy(ce0.contact); + } + b.m_contactList = null; + var f = b.m_fixtureList; + while (f) { + var f0 = f; + f = f.m_next; + if (this.m_destructionListener) { + this.m_destructionListener.SayGoodbyeFixture(f0); + } + f0.DestroyProxy(this.m_contactManager.m_broadPhase); + f0.Destroy(); + } + b.m_fixtureList = null; + b.m_fixtureCount = 0; + if (b.m_prev) { + b.m_prev.m_next = b.m_next; + } + if (b.m_next) { + b.m_next.m_prev = b.m_prev; + } + if (b == this.m_bodyList) { + this.m_bodyList = b.m_next; + }--this.m_bodyCount; + } + b2World.prototype.CreateJoint = function (def) { + var j = b2Joint.Create(def, null); + j.m_prev = null; + j.m_next = this.m_jointList; + if (this.m_jointList) { + this.m_jointList.m_prev = j; + } + this.m_jointList = j; + ++this.m_jointCount; + j.m_edgeA.joint = j; + j.m_edgeA.other = j.m_bodyB; + j.m_edgeA.prev = null; + j.m_edgeA.next = j.m_bodyA.m_jointList; + if (j.m_bodyA.m_jointList) j.m_bodyA.m_jointList.prev = j.m_edgeA; + j.m_bodyA.m_jointList = j.m_edgeA; + j.m_edgeB.joint = j; + j.m_edgeB.other = j.m_bodyA; + j.m_edgeB.prev = null; + j.m_edgeB.next = j.m_bodyB.m_jointList; + if (j.m_bodyB.m_jointList) j.m_bodyB.m_jointList.prev = j.m_edgeB; + j.m_bodyB.m_jointList = j.m_edgeB; + var bodyA = def.bodyA; + var bodyB = def.bodyB; + if (def.collideConnected == false) { + var edge = bodyB.GetContactList(); + while (edge) { + if (edge.other == bodyA) { + edge.contact.FlagForFiltering(); + } + edge = edge.next; + } + } + return j; + } + b2World.prototype.DestroyJoint = function (j) { + var collideConnected = j.m_collideConnected; + if (j.m_prev) { + j.m_prev.m_next = j.m_next; + } + if (j.m_next) { + j.m_next.m_prev = j.m_prev; + } + if (j == this.m_jointList) { + this.m_jointList = j.m_next; + } + var bodyA = j.m_bodyA; + var bodyB = j.m_bodyB; + bodyA.SetAwake(true); + bodyB.SetAwake(true); + if (j.m_edgeA.prev) { + j.m_edgeA.prev.next = j.m_edgeA.next; + } + if (j.m_edgeA.next) { + j.m_edgeA.next.prev = j.m_edgeA.prev; + } + if (j.m_edgeA == bodyA.m_jointList) { + bodyA.m_jointList = j.m_edgeA.next; + } + j.m_edgeA.prev = null; + j.m_edgeA.next = null; + if (j.m_edgeB.prev) { + j.m_edgeB.prev.next = j.m_edgeB.next; + } + if (j.m_edgeB.next) { + j.m_edgeB.next.prev = j.m_edgeB.prev; + } + if (j.m_edgeB == bodyB.m_jointList) { + bodyB.m_jointList = j.m_edgeB.next; + } + j.m_edgeB.prev = null; + j.m_edgeB.next = null; + b2Joint.Destroy(j, null); + --this.m_jointCount; + if (collideConnected == false) { + var edge = bodyB.GetContactList(); + while (edge) { + if (edge.other == bodyA) { + edge.contact.FlagForFiltering(); + } + edge = edge.next; + } + } + } + b2World.prototype.AddController = function (c) { + c.m_next = this.m_controllerList; + c.m_prev = null; + this.m_controllerList = c; + c.m_world = this; + this.m_controllerCount++; + return c; + } + b2World.prototype.RemoveController = function (c) { + if (c.m_prev) c.m_prev.m_next = c.m_next; + if (c.m_next) c.m_next.m_prev = c.m_prev; + if (this.m_controllerList == c) this.m_controllerList = c.m_next; + this.m_controllerCount--; + } + b2World.prototype.CreateController = function (controller) { + if (controller.m_world != this) throw new Error("Controller can only be a member of one world"); + controller.m_next = this.m_controllerList; + controller.m_prev = null; + if (this.m_controllerList) this.m_controllerList.m_prev = controller; + this.m_controllerList = controller; + ++this.m_controllerCount; + controller.m_world = this; + return controller; + } + b2World.prototype.DestroyController = function (controller) { + controller.Clear(); + if (controller.m_next) controller.m_next.m_prev = controller.m_prev; + if (controller.m_prev) controller.m_prev.m_next = controller.m_next; + if (controller == this.m_controllerList) this.m_controllerList = controller.m_next; + --this.m_controllerCount; + } + b2World.prototype.SetWarmStarting = function (flag) { + b2World.m_warmStarting = flag; + } + b2World.prototype.SetContinuousPhysics = function (flag) { + b2World.m_continuousPhysics = flag; + } + b2World.prototype.GetBodyCount = function () { + return this.m_bodyCount; + } + b2World.prototype.GetJointCount = function () { + return this.m_jointCount; + } + b2World.prototype.GetContactCount = function () { + return this.m_contactCount; + } + b2World.prototype.SetGravity = function (gravity) { + this.m_gravity = gravity; + } + b2World.prototype.GetGravity = function () { + return this.m_gravity; + } + b2World.prototype.GetGroundBody = function () { + return this.m_groundBody; + } + b2World.prototype.Step = function (dt, velocityIterations, positionIterations) { + if (dt === undefined) dt = 0; + if (velocityIterations === undefined) velocityIterations = 0; + if (positionIterations === undefined) positionIterations = 0; + if (this.m_flags & b2World.e_newFixture) { + this.m_contactManager.FindNewContacts(); + this.m_flags &= ~b2World.e_newFixture; + } + this.m_flags |= b2World.e_locked; + var step = b2World.s_timestep2; + step.dt = dt; + step.velocityIterations = velocityIterations; + step.positionIterations = positionIterations; + if (dt > 0.0) { + step.inv_dt = 1.0 / dt; + } + else { + step.inv_dt = 0.0; + } + step.dtRatio = this.m_inv_dt0 * dt; + step.warmStarting = b2World.m_warmStarting; + this.m_contactManager.Collide(); + if (step.dt > 0.0) { + this.Solve(step); + } + if (b2World.m_continuousPhysics && step.dt > 0.0) { + this.SolveTOI(step); + } + if (step.dt > 0.0) { + this.m_inv_dt0 = step.inv_dt; + } + this.m_flags &= ~b2World.e_locked; + } + b2World.prototype.ClearForces = function () { + for (var body = this.m_bodyList; body; body = body.m_next) { + body.m_force.SetZero(); + body.m_torque = 0.0; + } + } + b2World.prototype.DrawDebugData = function () { + if (this.m_debugDraw == null) { + return; + } + this.m_debugDraw.m_sprite.graphics.clear(); + var flags = this.m_debugDraw.GetFlags(); + var i = 0; + var b; + var f; + var s; + var j; + var bp; + var invQ = new b2Vec2; + var x1 = new b2Vec2; + var x2 = new b2Vec2; + var xf; + var b1 = new b2AABB(); + var b2 = new b2AABB(); + var vs = [new b2Vec2(), new b2Vec2(), new b2Vec2(), new b2Vec2()]; + var color = new b2Color(0, 0, 0); + if (flags & b2DebugDraw.e_shapeBit) { + for (b = this.m_bodyList; + b; b = b.m_next) { + xf = b.m_xf; + for (f = b.GetFixtureList(); + f; f = f.m_next) { + s = f.GetShape(); + if (b.IsActive() == false) { + color.Set(0.5, 0.5, 0.3); + this.DrawShape(s, xf, color); + } + else if (b.GetType() == b2Body.b2_staticBody) { + color.Set(0.5, 0.9, 0.5); + this.DrawShape(s, xf, color); + } + else if (b.GetType() == b2Body.b2_kinematicBody) { + color.Set(0.5, 0.5, 0.9); + this.DrawShape(s, xf, color); + } + else if (b.IsAwake() == false) { + color.Set(0.6, 0.6, 0.6); + this.DrawShape(s, xf, color); + } + else { + color.Set(0.9, 0.7, 0.7); + this.DrawShape(s, xf, color); + } + } + } + } + if (flags & b2DebugDraw.e_jointBit) { + for (j = this.m_jointList; + j; j = j.m_next) { + this.DrawJoint(j); + } + } + if (flags & b2DebugDraw.e_controllerBit) { + for (var c = this.m_controllerList; c; c = c.m_next) { + c.Draw(this.m_debugDraw); + } + } + if (flags & b2DebugDraw.e_pairBit) { + color.Set(0.3, 0.9, 0.9); + for (var contact = this.m_contactManager.m_contactList; contact; contact = contact.GetNext()) { + var fixtureA = contact.GetFixtureA(); + var fixtureB = contact.GetFixtureB(); + var cA = fixtureA.GetAABB().GetCenter(); + var cB = fixtureB.GetAABB().GetCenter(); + this.m_debugDraw.DrawSegment(cA, cB, color); + } + } + if (flags & b2DebugDraw.e_aabbBit) { + bp = this.m_contactManager.m_broadPhase; + vs = [new b2Vec2(), new b2Vec2(), new b2Vec2(), new b2Vec2()]; + for (b = this.m_bodyList; + b; b = b.GetNext()) { + if (b.IsActive() == false) { + continue; + } + for (f = b.GetFixtureList(); + f; f = f.GetNext()) { + var aabb = bp.GetFatAABB(f.m_proxy); + vs[0].Set(aabb.lowerBound.x, aabb.lowerBound.y); + vs[1].Set(aabb.upperBound.x, aabb.lowerBound.y); + vs[2].Set(aabb.upperBound.x, aabb.upperBound.y); + vs[3].Set(aabb.lowerBound.x, aabb.upperBound.y); + this.m_debugDraw.DrawPolygon(vs, 4, color); + } + } + } + if (flags & b2DebugDraw.e_centerOfMassBit) { + for (b = this.m_bodyList; + b; b = b.m_next) { + xf = b2World.s_xf; + xf.R = b.m_xf.R; + xf.position = b.GetWorldCenter(); + this.m_debugDraw.DrawTransform(xf); + } + } + } + b2World.prototype.QueryAABB = function (callback, aabb) { + var __this = this; + var broadPhase = __this.m_contactManager.m_broadPhase; + + function WorldQueryWrapper(proxy) { + return callback(broadPhase.GetUserData(proxy)); + }; + broadPhase.Query(WorldQueryWrapper, aabb); + } + b2World.prototype.QueryShape = function (callback, shape, transform) { + var __this = this; + if (transform === undefined) transform = null; + if (transform == null) { + transform = new b2Transform(); + transform.SetIdentity(); + } + var broadPhase = __this.m_contactManager.m_broadPhase; + + function WorldQueryWrapper(proxy) { + var fixture = (broadPhase.GetUserData(proxy) instanceof b2Fixture ? broadPhase.GetUserData(proxy) : null); + if (b2Shape.TestOverlap(shape, transform, fixture.GetShape(), fixture.GetBody().GetTransform())) return callback(fixture); + return true; + }; + var aabb = new b2AABB(); + shape.ComputeAABB(aabb, transform); + broadPhase.Query(WorldQueryWrapper, aabb); + } + b2World.prototype.QueryPoint = function (callback, p) { + var __this = this; + var broadPhase = __this.m_contactManager.m_broadPhase; + + function WorldQueryWrapper(proxy) { + var fixture = (broadPhase.GetUserData(proxy) instanceof b2Fixture ? broadPhase.GetUserData(proxy) : null); + if (fixture.TestPoint(p)) return callback(fixture); + return true; + }; + var aabb = new b2AABB(); + aabb.lowerBound.Set(p.x - b2Settings.b2_linearSlop, p.y - b2Settings.b2_linearSlop); + aabb.upperBound.Set(p.x + b2Settings.b2_linearSlop, p.y + b2Settings.b2_linearSlop); + broadPhase.Query(WorldQueryWrapper, aabb); + } + b2World.prototype.RayCast = function (callback, point1, point2) { + var __this = this; + var broadPhase = __this.m_contactManager.m_broadPhase; + var output = new b2RayCastOutput; + + function RayCastWrapper(input, proxy) { + var userData = broadPhase.GetUserData(proxy); + var fixture = (userData instanceof b2Fixture ? userData : null); + var hit = fixture.RayCast(output, input); + if (hit) { + var fraction = output.fraction; + var point = new b2Vec2((1.0 - fraction) * point1.x + fraction * point2.x, (1.0 - fraction) * point1.y + fraction * point2.y); + return callback(fixture, point, output.normal, fraction); + } + return input.maxFraction; + }; + var input = new b2RayCastInput(point1, point2); + broadPhase.RayCast(RayCastWrapper, input); + } + b2World.prototype.RayCastOne = function (point1, point2) { + var __this = this; + var result; + + function RayCastOneWrapper(fixture, point, normal, fraction) { + if (fraction === undefined) fraction = 0; + result = fixture; + return fraction; + }; + __this.RayCast(RayCastOneWrapper, point1, point2); + return result; + } + b2World.prototype.RayCastAll = function (point1, point2) { + var __this = this; + var result = new Vector(); + + function RayCastAllWrapper(fixture, point, normal, fraction) { + if (fraction === undefined) fraction = 0; + result[result.length] = fixture; + return 1; + }; + __this.RayCast(RayCastAllWrapper, point1, point2); + return result; + } + b2World.prototype.GetBodyList = function () { + return this.m_bodyList; + } + b2World.prototype.GetJointList = function () { + return this.m_jointList; + } + b2World.prototype.GetContactList = function () { + return this.m_contactList; + } + b2World.prototype.IsLocked = function () { + return (this.m_flags & b2World.e_locked) > 0; + } + b2World.prototype.Solve = function (step) { + var b; + for (var controller = this.m_controllerList; controller; controller = controller.m_next) { + controller.Step(step); + } + var island = this.m_island; + island.Initialize(this.m_bodyCount, this.m_contactCount, this.m_jointCount, null, this.m_contactManager.m_contactListener, this.m_contactSolver); + for (b = this.m_bodyList; + b; b = b.m_next) { + b.m_flags &= ~b2Body.e_islandFlag; + } + for (var c = this.m_contactList; c; c = c.m_next) { + c.m_flags &= ~b2Contact.e_islandFlag; + } + for (var j = this.m_jointList; j; j = j.m_next) { + j.m_islandFlag = false; + } + var stackSize = parseInt(this.m_bodyCount); + var stack = this.s_stack; + for (var seed = this.m_bodyList; seed; seed = seed.m_next) { + if (seed.m_flags & b2Body.e_islandFlag) { + continue; + } + if (seed.IsAwake() == false || seed.IsActive() == false) { + continue; + } + if (seed.GetType() == b2Body.b2_staticBody) { + continue; + } + island.Clear(); + var stackCount = 0; + stack[stackCount++] = seed; + seed.m_flags |= b2Body.e_islandFlag; + while (stackCount > 0) { + b = stack[--stackCount]; + island.AddBody(b); + if (b.IsAwake() == false) { + b.SetAwake(true); + } + if (b.GetType() == b2Body.b2_staticBody) { + continue; + } + var other; + for (var ce = b.m_contactList; ce; ce = ce.next) { + if (ce.contact.m_flags & b2Contact.e_islandFlag) { + continue; + } + if (ce.contact.IsSensor() == true || ce.contact.IsEnabled() == false || ce.contact.IsTouching() == false) { + continue; + } + island.AddContact(ce.contact); + ce.contact.m_flags |= b2Contact.e_islandFlag; + other = ce.other; + if (other.m_flags & b2Body.e_islandFlag) { + continue; + } + stack[stackCount++] = other; + other.m_flags |= b2Body.e_islandFlag; + } + for (var jn = b.m_jointList; jn; jn = jn.next) { + if (jn.joint.m_islandFlag == true) { + continue; + } + other = jn.other; + if (other.IsActive() == false) { + continue; + } + island.AddJoint(jn.joint); + jn.joint.m_islandFlag = true; + if (other.m_flags & b2Body.e_islandFlag) { + continue; + } + stack[stackCount++] = other; + other.m_flags |= b2Body.e_islandFlag; + } + } + island.Solve(step, this.m_gravity, this.m_allowSleep); + for (var i = 0; i < island.m_bodyCount; ++i) { + b = island.m_bodies[i]; + if (b.GetType() == b2Body.b2_staticBody) { + b.m_flags &= ~b2Body.e_islandFlag; + } + } + } + for (i = 0; + i < stack.length; ++i) { + if (!stack[i]) break; + stack[i] = null; + } + for (b = this.m_bodyList; + b; b = b.m_next) { + if (b.IsAwake() == false || b.IsActive() == false) { + continue; + } + if (b.GetType() == b2Body.b2_staticBody) { + continue; + } + b.SynchronizeFixtures(); + } + this.m_contactManager.FindNewContacts(); + } + b2World.prototype.SolveTOI = function (step) { + var b; + var fA; + var fB; + var bA; + var bB; + var cEdge; + var j; + var island = this.m_island; + island.Initialize(this.m_bodyCount, b2Settings.b2_maxTOIContactsPerIsland, b2Settings.b2_maxTOIJointsPerIsland, null, this.m_contactManager.m_contactListener, this.m_contactSolver); + var queue = b2World.s_queue; + for (b = this.m_bodyList; + b; b = b.m_next) { + b.m_flags &= ~b2Body.e_islandFlag; + b.m_sweep.t0 = 0.0; + } + var c; + for (c = this.m_contactList; + c; c = c.m_next) { + c.m_flags &= ~ (b2Contact.e_toiFlag | b2Contact.e_islandFlag); + } + for (j = this.m_jointList; + j; j = j.m_next) { + j.m_islandFlag = false; + } + for (;;) { + var minContact = null; + var minTOI = 1.0; + for (c = this.m_contactList; + c; c = c.m_next) { + if (c.IsSensor() == true || c.IsEnabled() == false || c.IsContinuous() == false) { + continue; + } + var toi = 1.0; + if (c.m_flags & b2Contact.e_toiFlag) { + toi = c.m_toi; + } + else { + fA = c.m_fixtureA; + fB = c.m_fixtureB; + bA = fA.m_body; + bB = fB.m_body; + if ((bA.GetType() != b2Body.b2_dynamicBody || bA.IsAwake() == false) && (bB.GetType() != b2Body.b2_dynamicBody || bB.IsAwake() == false)) { + continue; + } + var t0 = bA.m_sweep.t0; + if (bA.m_sweep.t0 < bB.m_sweep.t0) { + t0 = bB.m_sweep.t0; + bA.m_sweep.Advance(t0); + } + else if (bB.m_sweep.t0 < bA.m_sweep.t0) { + t0 = bA.m_sweep.t0; + bB.m_sweep.Advance(t0); + } + toi = c.ComputeTOI(bA.m_sweep, bB.m_sweep); + b2Settings.b2Assert(0.0 <= toi && toi <= 1.0); + if (toi > 0.0 && toi < 1.0) { + toi = (1.0 - toi) * t0 + toi; + if (toi > 1) toi = 1; + } + c.m_toi = toi; + c.m_flags |= b2Contact.e_toiFlag; + } + if (Number.MIN_VALUE < toi && toi < minTOI) { + minContact = c; + minTOI = toi; + } + } + if (minContact == null || 1.0 - 100.0 * Number.MIN_VALUE < minTOI) { + break; + } + fA = minContact.m_fixtureA; + fB = minContact.m_fixtureB; + bA = fA.m_body; + bB = fB.m_body; + b2World.s_backupA.Set(bA.m_sweep); + b2World.s_backupB.Set(bB.m_sweep); + bA.Advance(minTOI); + bB.Advance(minTOI); + minContact.Update(this.m_contactManager.m_contactListener); + minContact.m_flags &= ~b2Contact.e_toiFlag; + if (minContact.IsSensor() == true || minContact.IsEnabled() == false) { + bA.m_sweep.Set(b2World.s_backupA); + bB.m_sweep.Set(b2World.s_backupB); + bA.SynchronizeTransform(); + bB.SynchronizeTransform(); + continue; + } + if (minContact.IsTouching() == false) { + continue; + } + var seed = bA; + if (seed.GetType() != b2Body.b2_dynamicBody) { + seed = bB; + } + island.Clear(); + var queueStart = 0; + var queueSize = 0; + queue[queueStart + queueSize++] = seed; + seed.m_flags |= b2Body.e_islandFlag; + while (queueSize > 0) { + b = queue[queueStart++]; + --queueSize; + island.AddBody(b); + if (b.IsAwake() == false) { + b.SetAwake(true); + } + if (b.GetType() != b2Body.b2_dynamicBody) { + continue; + } + for (cEdge = b.m_contactList; + cEdge; cEdge = cEdge.next) { + if (island.m_contactCount == island.m_contactCapacity) { + break; + } + if (cEdge.contact.m_flags & b2Contact.e_islandFlag) { + continue; + } + if (cEdge.contact.IsSensor() == true || cEdge.contact.IsEnabled() == false || cEdge.contact.IsTouching() == false) { + continue; + } + island.AddContact(cEdge.contact); + cEdge.contact.m_flags |= b2Contact.e_islandFlag; + var other = cEdge.other; + if (other.m_flags & b2Body.e_islandFlag) { + continue; + } + if (other.GetType() != b2Body.b2_staticBody) { + other.Advance(minTOI); + other.SetAwake(true); + } + queue[queueStart + queueSize] = other; + ++queueSize; + other.m_flags |= b2Body.e_islandFlag; + } + for (var jEdge = b.m_jointList; jEdge; jEdge = jEdge.next) { + if (island.m_jointCount == island.m_jointCapacity) continue; + if (jEdge.joint.m_islandFlag == true) continue; + other = jEdge.other; + if (other.IsActive() == false) { + continue; + } + island.AddJoint(jEdge.joint); + jEdge.joint.m_islandFlag = true; + if (other.m_flags & b2Body.e_islandFlag) continue; + if (other.GetType() != b2Body.b2_staticBody) { + other.Advance(minTOI); + other.SetAwake(true); + } + queue[queueStart + queueSize] = other; + ++queueSize; + other.m_flags |= b2Body.e_islandFlag; + } + } + var subStep = b2World.s_timestep; + subStep.warmStarting = false; + subStep.dt = (1.0 - minTOI) * step.dt; + subStep.inv_dt = 1.0 / subStep.dt; + subStep.dtRatio = 0.0; + subStep.velocityIterations = step.velocityIterations; + subStep.positionIterations = step.positionIterations; + island.SolveTOI(subStep); + var i = 0; + for (i = 0; + i < island.m_bodyCount; ++i) { + b = island.m_bodies[i]; + b.m_flags &= ~b2Body.e_islandFlag; + if (b.IsAwake() == false) { + continue; + } + if (b.GetType() != b2Body.b2_dynamicBody) { + continue; + } + b.SynchronizeFixtures(); + for (cEdge = b.m_contactList; + cEdge; cEdge = cEdge.next) { + cEdge.contact.m_flags &= ~b2Contact.e_toiFlag; + } + } + for (i = 0; + i < island.m_contactCount; ++i) { + c = island.m_contacts[i]; + c.m_flags &= ~ (b2Contact.e_toiFlag | b2Contact.e_islandFlag); + } + for (i = 0; + i < island.m_jointCount; ++i) { + j = island.m_joints[i]; + j.m_islandFlag = false; + } + this.m_contactManager.FindNewContacts(); + } + } + b2World.prototype.DrawJoint = function (joint) { + var b1 = joint.GetBodyA(); + var b2 = joint.GetBodyB(); + var xf1 = b1.m_xf; + var xf2 = b2.m_xf; + var x1 = xf1.position; + var x2 = xf2.position; + var p1 = joint.GetAnchorA(); + var p2 = joint.GetAnchorB(); + var color = b2World.s_jointColor; + switch (joint.m_type) { + case b2Joint.e_distanceJoint: + this.m_debugDraw.DrawSegment(p1, p2, color); + break; + case b2Joint.e_pulleyJoint: + { + var pulley = ((joint instanceof b2PulleyJoint ? joint : null)); + var s1 = pulley.GetGroundAnchorA(); + var s2 = pulley.GetGroundAnchorB(); + this.m_debugDraw.DrawSegment(s1, p1, color); + this.m_debugDraw.DrawSegment(s2, p2, color); + this.m_debugDraw.DrawSegment(s1, s2, color); + } + break; + case b2Joint.e_mouseJoint: + this.m_debugDraw.DrawSegment(p1, p2, color); + break; + default: + if (b1 != this.m_groundBody) this.m_debugDraw.DrawSegment(x1, p1, color); + this.m_debugDraw.DrawSegment(p1, p2, color); + if (b2 != this.m_groundBody) this.m_debugDraw.DrawSegment(x2, p2, color); + } + } + b2World.prototype.DrawShape = function (shape, xf, color) { + switch (shape.m_type) { + case b2Shape.e_circleShape: + { + var circle = ((shape instanceof b2CircleShape ? shape : null)); + var center = b2Math.MulX(xf, circle.m_p); + var radius = circle.m_radius; + var axis = xf.R.col1; + this.m_debugDraw.DrawSolidCircle(center, radius, axis, color); + } + break; + case b2Shape.e_polygonShape: + { + var i = 0; + var poly = ((shape instanceof b2PolygonShape ? shape : null)); + var vertexCount = parseInt(poly.GetVertexCount()); + var localVertices = poly.GetVertices(); + var vertices = new Vector(vertexCount); + for (i = 0; + i < vertexCount; ++i) { + vertices[i] = b2Math.MulX(xf, localVertices[i]); + } + this.m_debugDraw.DrawSolidPolygon(vertices, vertexCount, color); + } + break; + case b2Shape.e_edgeShape: + { + var edge = (shape instanceof b2EdgeShape ? shape : null); + this.m_debugDraw.DrawSegment(b2Math.MulX(xf, edge.GetVertex1()), b2Math.MulX(xf, edge.GetVertex2()), color); + } + break; + } + } + Box2D.postDefs.push(function () { + Box2D.Dynamics.b2World.s_timestep2 = new b2TimeStep(); + Box2D.Dynamics.b2World.s_xf = new b2Transform(); + Box2D.Dynamics.b2World.s_backupA = new b2Sweep(); + Box2D.Dynamics.b2World.s_backupB = new b2Sweep(); + Box2D.Dynamics.b2World.s_timestep = new b2TimeStep(); + Box2D.Dynamics.b2World.s_queue = new Vector(); + Box2D.Dynamics.b2World.s_jointColor = new b2Color(0.5, 0.8, 0.8); + Box2D.Dynamics.b2World.e_newFixture = 0x0001; + Box2D.Dynamics.b2World.e_locked = 0x0002; + }); +})(); +(function () { + var b2CircleShape = Box2D.Collision.Shapes.b2CircleShape, + b2EdgeChainDef = Box2D.Collision.Shapes.b2EdgeChainDef, + b2EdgeShape = Box2D.Collision.Shapes.b2EdgeShape, + b2MassData = Box2D.Collision.Shapes.b2MassData, + b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape, + b2Shape = Box2D.Collision.Shapes.b2Shape, + b2CircleContact = Box2D.Dynamics.Contacts.b2CircleContact, + b2Contact = Box2D.Dynamics.Contacts.b2Contact, + b2ContactConstraint = Box2D.Dynamics.Contacts.b2ContactConstraint, + b2ContactConstraintPoint = Box2D.Dynamics.Contacts.b2ContactConstraintPoint, + b2ContactEdge = Box2D.Dynamics.Contacts.b2ContactEdge, + b2ContactFactory = Box2D.Dynamics.Contacts.b2ContactFactory, + b2ContactRegister = Box2D.Dynamics.Contacts.b2ContactRegister, + b2ContactResult = Box2D.Dynamics.Contacts.b2ContactResult, + b2ContactSolver = Box2D.Dynamics.Contacts.b2ContactSolver, + b2EdgeAndCircleContact = Box2D.Dynamics.Contacts.b2EdgeAndCircleContact, + b2NullContact = Box2D.Dynamics.Contacts.b2NullContact, + b2PolyAndCircleContact = Box2D.Dynamics.Contacts.b2PolyAndCircleContact, + b2PolyAndEdgeContact = Box2D.Dynamics.Contacts.b2PolyAndEdgeContact, + b2PolygonContact = Box2D.Dynamics.Contacts.b2PolygonContact, + b2PositionSolverManifold = Box2D.Dynamics.Contacts.b2PositionSolverManifold, + b2Body = Box2D.Dynamics.b2Body, + b2BodyDef = Box2D.Dynamics.b2BodyDef, + b2ContactFilter = Box2D.Dynamics.b2ContactFilter, + b2ContactImpulse = Box2D.Dynamics.b2ContactImpulse, + b2ContactListener = Box2D.Dynamics.b2ContactListener, + b2ContactManager = Box2D.Dynamics.b2ContactManager, + b2DebugDraw = Box2D.Dynamics.b2DebugDraw, + b2DestructionListener = Box2D.Dynamics.b2DestructionListener, + b2FilterData = Box2D.Dynamics.b2FilterData, + b2Fixture = Box2D.Dynamics.b2Fixture, + b2FixtureDef = Box2D.Dynamics.b2FixtureDef, + b2Island = Box2D.Dynamics.b2Island, + b2TimeStep = Box2D.Dynamics.b2TimeStep, + b2World = Box2D.Dynamics.b2World, + b2Color = Box2D.Common.b2Color, + b2internal = Box2D.Common.b2internal, + b2Settings = Box2D.Common.b2Settings, + b2Mat22 = Box2D.Common.Math.b2Mat22, + b2Mat33 = Box2D.Common.Math.b2Mat33, + b2Math = Box2D.Common.Math.b2Math, + b2Sweep = Box2D.Common.Math.b2Sweep, + b2Transform = Box2D.Common.Math.b2Transform, + b2Vec2 = Box2D.Common.Math.b2Vec2, + b2Vec3 = Box2D.Common.Math.b2Vec3, + b2AABB = Box2D.Collision.b2AABB, + b2Bound = Box2D.Collision.b2Bound, + b2BoundValues = Box2D.Collision.b2BoundValues, + b2Collision = Box2D.Collision.b2Collision, + b2ContactID = Box2D.Collision.b2ContactID, + b2ContactPoint = Box2D.Collision.b2ContactPoint, + b2Distance = Box2D.Collision.b2Distance, + b2DistanceInput = Box2D.Collision.b2DistanceInput, + b2DistanceOutput = Box2D.Collision.b2DistanceOutput, + b2DistanceProxy = Box2D.Collision.b2DistanceProxy, + b2DynamicTree = Box2D.Collision.b2DynamicTree, + b2DynamicTreeBroadPhase = Box2D.Collision.b2DynamicTreeBroadPhase, + b2DynamicTreeNode = Box2D.Collision.b2DynamicTreeNode, + b2DynamicTreePair = Box2D.Collision.b2DynamicTreePair, + b2Manifold = Box2D.Collision.b2Manifold, + b2ManifoldPoint = Box2D.Collision.b2ManifoldPoint, + b2Point = Box2D.Collision.b2Point, + b2RayCastInput = Box2D.Collision.b2RayCastInput, + b2RayCastOutput = Box2D.Collision.b2RayCastOutput, + b2Segment = Box2D.Collision.b2Segment, + b2SeparationFunction = Box2D.Collision.b2SeparationFunction, + b2Simplex = Box2D.Collision.b2Simplex, + b2SimplexCache = Box2D.Collision.b2SimplexCache, + b2SimplexVertex = Box2D.Collision.b2SimplexVertex, + b2TimeOfImpact = Box2D.Collision.b2TimeOfImpact, + b2TOIInput = Box2D.Collision.b2TOIInput, + b2WorldManifold = Box2D.Collision.b2WorldManifold, + ClipVertex = Box2D.Collision.ClipVertex, + Features = Box2D.Collision.Features, + IBroadPhase = Box2D.Collision.IBroadPhase; + + Box2D.inherit(b2CircleContact, Box2D.Dynamics.Contacts.b2Contact); + b2CircleContact.prototype.__super = Box2D.Dynamics.Contacts.b2Contact.prototype; + b2CircleContact.b2CircleContact = function () { + Box2D.Dynamics.Contacts.b2Contact.b2Contact.apply(this, arguments); + }; + b2CircleContact.Create = function (allocator) { + return new b2CircleContact(); + } + b2CircleContact.Destroy = function (contact, allocator) {} + b2CircleContact.prototype.Reset = function (fixtureA, fixtureB) { + this.__super.Reset.call(this, fixtureA, fixtureB); + } + b2CircleContact.prototype.Evaluate = function () { + var bA = this.m_fixtureA.GetBody(); + var bB = this.m_fixtureB.GetBody(); + b2Collision.CollideCircles(this.m_manifold, (this.m_fixtureA.GetShape() instanceof b2CircleShape ? this.m_fixtureA.GetShape() : null), bA.m_xf, (this.m_fixtureB.GetShape() instanceof b2CircleShape ? this.m_fixtureB.GetShape() : null), bB.m_xf); + } + b2Contact.b2Contact = function () { + this.m_nodeA = new b2ContactEdge(); + this.m_nodeB = new b2ContactEdge(); + this.m_manifold = new b2Manifold(); + this.m_oldManifold = new b2Manifold(); + }; + b2Contact.prototype.GetManifold = function () { + return this.m_manifold; + } + b2Contact.prototype.GetWorldManifold = function (worldManifold) { + var bodyA = this.m_fixtureA.GetBody(); + var bodyB = this.m_fixtureB.GetBody(); + var shapeA = this.m_fixtureA.GetShape(); + var shapeB = this.m_fixtureB.GetShape(); + worldManifold.Initialize(this.m_manifold, bodyA.GetTransform(), shapeA.m_radius, bodyB.GetTransform(), shapeB.m_radius); + } + b2Contact.prototype.IsTouching = function () { + return (this.m_flags & b2Contact.e_touchingFlag) == b2Contact.e_touchingFlag; + } + b2Contact.prototype.IsContinuous = function () { + return (this.m_flags & b2Contact.e_continuousFlag) == b2Contact.e_continuousFlag; + } + b2Contact.prototype.SetSensor = function (sensor) { + if (sensor) { + this.m_flags |= b2Contact.e_sensorFlag; + } + else { + this.m_flags &= ~b2Contact.e_sensorFlag; + } + } + b2Contact.prototype.IsSensor = function () { + return (this.m_flags & b2Contact.e_sensorFlag) == b2Contact.e_sensorFlag; + } + b2Contact.prototype.SetEnabled = function (flag) { + if (flag) { + this.m_flags |= b2Contact.e_enabledFlag; + } + else { + this.m_flags &= ~b2Contact.e_enabledFlag; + } + } + b2Contact.prototype.IsEnabled = function () { + return (this.m_flags & b2Contact.e_enabledFlag) == b2Contact.e_enabledFlag; + } + b2Contact.prototype.GetNext = function () { + return this.m_next; + } + b2Contact.prototype.GetFixtureA = function () { + return this.m_fixtureA; + } + b2Contact.prototype.GetFixtureB = function () { + return this.m_fixtureB; + } + b2Contact.prototype.FlagForFiltering = function () { + this.m_flags |= b2Contact.e_filterFlag; + } + b2Contact.prototype.b2Contact = function () {} + b2Contact.prototype.Reset = function (fixtureA, fixtureB) { + if (fixtureA === undefined) fixtureA = null; + if (fixtureB === undefined) fixtureB = null; + this.m_flags = b2Contact.e_enabledFlag; + if (!fixtureA || !fixtureB) { + this.m_fixtureA = null; + this.m_fixtureB = null; + return; + } + if (fixtureA.IsSensor() || fixtureB.IsSensor()) { + this.m_flags |= b2Contact.e_sensorFlag; + } + var bodyA = fixtureA.GetBody(); + var bodyB = fixtureB.GetBody(); + if (bodyA.GetType() != b2Body.b2_dynamicBody || bodyA.IsBullet() || bodyB.GetType() != b2Body.b2_dynamicBody || bodyB.IsBullet()) { + this.m_flags |= b2Contact.e_continuousFlag; + } + this.m_fixtureA = fixtureA; + this.m_fixtureB = fixtureB; + this.m_manifold.m_pointCount = 0; + this.m_prev = null; + this.m_next = null; + this.m_nodeA.contact = null; + this.m_nodeA.prev = null; + this.m_nodeA.next = null; + this.m_nodeA.other = null; + this.m_nodeB.contact = null; + this.m_nodeB.prev = null; + this.m_nodeB.next = null; + this.m_nodeB.other = null; + } + b2Contact.prototype.Update = function (listener) { + var tManifold = this.m_oldManifold; + this.m_oldManifold = this.m_manifold; + this.m_manifold = tManifold; + this.m_flags |= b2Contact.e_enabledFlag; + var touching = false; + var wasTouching = (this.m_flags & b2Contact.e_touchingFlag) == b2Contact.e_touchingFlag; + var bodyA = this.m_fixtureA.m_body; + var bodyB = this.m_fixtureB.m_body; + var aabbOverlap = this.m_fixtureA.m_aabb.TestOverlap(this.m_fixtureB.m_aabb); + if (this.m_flags & b2Contact.e_sensorFlag) { + if (aabbOverlap) { + var shapeA = this.m_fixtureA.GetShape(); + var shapeB = this.m_fixtureB.GetShape(); + var xfA = bodyA.GetTransform(); + var xfB = bodyB.GetTransform(); + touching = b2Shape.TestOverlap(shapeA, xfA, shapeB, xfB); + } + this.m_manifold.m_pointCount = 0; + } + else { + if (bodyA.GetType() != b2Body.b2_dynamicBody || bodyA.IsBullet() || bodyB.GetType() != b2Body.b2_dynamicBody || bodyB.IsBullet()) { + this.m_flags |= b2Contact.e_continuousFlag; + } + else { + this.m_flags &= ~b2Contact.e_continuousFlag; + } + if (aabbOverlap) { + this.Evaluate(); + touching = this.m_manifold.m_pointCount > 0; + for (var i = 0; i < this.m_manifold.m_pointCount; ++i) { + var mp2 = this.m_manifold.m_points[i]; + mp2.m_normalImpulse = 0.0; + mp2.m_tangentImpulse = 0.0; + var id2 = mp2.m_id; + for (var j = 0; j < this.m_oldManifold.m_pointCount; ++j) { + var mp1 = this.m_oldManifold.m_points[j]; + if (mp1.m_id.key == id2.key) { + mp2.m_normalImpulse = mp1.m_normalImpulse; + mp2.m_tangentImpulse = mp1.m_tangentImpulse; + break; + } + } + } + } + else { + this.m_manifold.m_pointCount = 0; + } + if (touching != wasTouching) { + bodyA.SetAwake(true); + bodyB.SetAwake(true); + } + } + if (touching) { + this.m_flags |= b2Contact.e_touchingFlag; + } + else { + this.m_flags &= ~b2Contact.e_touchingFlag; + } + if (wasTouching == false && touching == true) { + listener.BeginContact(this); + } + if (wasTouching == true && touching == false) { + listener.EndContact(this); + } + if ((this.m_flags & b2Contact.e_sensorFlag) == 0) { + listener.PreSolve(this, this.m_oldManifold); + } + } + b2Contact.prototype.Evaluate = function () {} + b2Contact.prototype.ComputeTOI = function (sweepA, sweepB) { + b2Contact.s_input.proxyA.Set(this.m_fixtureA.GetShape()); + b2Contact.s_input.proxyB.Set(this.m_fixtureB.GetShape()); + b2Contact.s_input.sweepA = sweepA; + b2Contact.s_input.sweepB = sweepB; + b2Contact.s_input.tolerance = b2Settings.b2_linearSlop; + return b2TimeOfImpact.TimeOfImpact(b2Contact.s_input); + } + Box2D.postDefs.push(function () { + Box2D.Dynamics.Contacts.b2Contact.e_sensorFlag = 0x0001; + Box2D.Dynamics.Contacts.b2Contact.e_continuousFlag = 0x0002; + Box2D.Dynamics.Contacts.b2Contact.e_islandFlag = 0x0004; + Box2D.Dynamics.Contacts.b2Contact.e_toiFlag = 0x0008; + Box2D.Dynamics.Contacts.b2Contact.e_touchingFlag = 0x0010; + Box2D.Dynamics.Contacts.b2Contact.e_enabledFlag = 0x0020; + Box2D.Dynamics.Contacts.b2Contact.e_filterFlag = 0x0040; + Box2D.Dynamics.Contacts.b2Contact.s_input = new b2TOIInput(); + }); + b2ContactConstraint.b2ContactConstraint = function () { + this.localPlaneNormal = new b2Vec2(); + this.localPoint = new b2Vec2(); + this.normal = new b2Vec2(); + this.normalMass = new b2Mat22(); + this.K = new b2Mat22(); + }; + b2ContactConstraint.prototype.b2ContactConstraint = function () { + this.points = new Vector(b2Settings.b2_maxManifoldPoints); + for (var i = 0; i < b2Settings.b2_maxManifoldPoints; i++) { + this.points[i] = new b2ContactConstraintPoint(); + } + } + b2ContactConstraintPoint.b2ContactConstraintPoint = function () { + this.localPoint = new b2Vec2(); + this.rA = new b2Vec2(); + this.rB = new b2Vec2(); + }; + b2ContactEdge.b2ContactEdge = function () {}; + b2ContactFactory.b2ContactFactory = function () {}; + b2ContactFactory.prototype.b2ContactFactory = function (allocator) { + this.m_allocator = allocator; + this.InitializeRegisters(); + } + b2ContactFactory.prototype.AddType = function (createFcn, destroyFcn, type1, type2) { + if (type1 === undefined) type1 = 0; + if (type2 === undefined) type2 = 0; + this.m_registers[type1][type2].createFcn = createFcn; + this.m_registers[type1][type2].destroyFcn = destroyFcn; + this.m_registers[type1][type2].primary = true; + if (type1 != type2) { + this.m_registers[type2][type1].createFcn = createFcn; + this.m_registers[type2][type1].destroyFcn = destroyFcn; + this.m_registers[type2][type1].primary = false; + } + } + b2ContactFactory.prototype.InitializeRegisters = function () { + this.m_registers = new Vector(b2Shape.e_shapeTypeCount); + for (var i = 0; i < b2Shape.e_shapeTypeCount; i++) { + this.m_registers[i] = new Vector(b2Shape.e_shapeTypeCount); + for (var j = 0; j < b2Shape.e_shapeTypeCount; j++) { + this.m_registers[i][j] = new b2ContactRegister(); + } + } + this.AddType(b2CircleContact.Create, b2CircleContact.Destroy, b2Shape.e_circleShape, b2Shape.e_circleShape); + this.AddType(b2PolyAndCircleContact.Create, b2PolyAndCircleContact.Destroy, b2Shape.e_polygonShape, b2Shape.e_circleShape); + this.AddType(b2PolygonContact.Create, b2PolygonContact.Destroy, b2Shape.e_polygonShape, b2Shape.e_polygonShape); + this.AddType(b2EdgeAndCircleContact.Create, b2EdgeAndCircleContact.Destroy, b2Shape.e_edgeShape, b2Shape.e_circleShape); + this.AddType(b2PolyAndEdgeContact.Create, b2PolyAndEdgeContact.Destroy, b2Shape.e_polygonShape, b2Shape.e_edgeShape); + } + b2ContactFactory.prototype.Create = function (fixtureA, fixtureB) { + var type1 = parseInt(fixtureA.GetType()); + var type2 = parseInt(fixtureB.GetType()); + var reg = this.m_registers[type1][type2]; + var c; + if (reg.pool) { + c = reg.pool; + reg.pool = c.m_next; + reg.poolCount--; + c.Reset(fixtureA, fixtureB); + return c; + } + var createFcn = reg.createFcn; + if (createFcn != null) { + if (reg.primary) { + c = createFcn(this.m_allocator); + c.Reset(fixtureA, fixtureB); + return c; + } + else { + c = createFcn(this.m_allocator); + c.Reset(fixtureB, fixtureA); + return c; + } + } + else { + return null; + } + } + b2ContactFactory.prototype.Destroy = function (contact) { + if (contact.m_manifold.m_pointCount > 0) { + contact.m_fixtureA.m_body.SetAwake(true); + contact.m_fixtureB.m_body.SetAwake(true); + } + var type1 = parseInt(contact.m_fixtureA.GetType()); + var type2 = parseInt(contact.m_fixtureB.GetType()); + var reg = this.m_registers[type1][type2]; + if (true) { + reg.poolCount++; + contact.m_next = reg.pool; + reg.pool = contact; + } + var destroyFcn = reg.destroyFcn; + destroyFcn(contact, this.m_allocator); + } + b2ContactRegister.b2ContactRegister = function () {}; + b2ContactResult.b2ContactResult = function () { + this.position = new b2Vec2(); + this.normal = new b2Vec2(); + this.id = new b2ContactID(); + }; + b2ContactSolver.b2ContactSolver = function () { + this.m_step = new b2TimeStep(); + this.m_constraints = new Vector(); + }; + b2ContactSolver.prototype.b2ContactSolver = function () {} + b2ContactSolver.prototype.Initialize = function (step, contacts, contactCount, allocator) { + if (contactCount === undefined) contactCount = 0; + var contact; + this.m_step.Set(step); + this.m_allocator = allocator; + var i = 0; + var tVec; + var tMat; + this.m_constraintCount = contactCount; + while (this.m_constraints.length < this.m_constraintCount) { + this.m_constraints[this.m_constraints.length] = new b2ContactConstraint(); + } + for (i = 0; + i < contactCount; ++i) { + contact = contacts[i]; + var fixtureA = contact.m_fixtureA; + var fixtureB = contact.m_fixtureB; + var shapeA = fixtureA.m_shape; + var shapeB = fixtureB.m_shape; + var radiusA = shapeA.m_radius; + var radiusB = shapeB.m_radius; + var bodyA = fixtureA.m_body; + var bodyB = fixtureB.m_body; + var manifold = contact.GetManifold(); + var friction = b2Settings.b2MixFriction(fixtureA.GetFriction(), fixtureB.GetFriction()); + var restitution = b2Settings.b2MixRestitution(fixtureA.GetRestitution(), fixtureB.GetRestitution()); + var vAX = bodyA.m_linearVelocity.x; + var vAY = bodyA.m_linearVelocity.y; + var vBX = bodyB.m_linearVelocity.x; + var vBY = bodyB.m_linearVelocity.y; + var wA = bodyA.m_angularVelocity; + var wB = bodyB.m_angularVelocity; + b2Settings.b2Assert(manifold.m_pointCount > 0); + b2ContactSolver.s_worldManifold.Initialize(manifold, bodyA.m_xf, radiusA, bodyB.m_xf, radiusB); + var normalX = b2ContactSolver.s_worldManifold.m_normal.x; + var normalY = b2ContactSolver.s_worldManifold.m_normal.y; + var cc = this.m_constraints[i]; + cc.bodyA = bodyA; + cc.bodyB = bodyB; + cc.manifold = manifold; + cc.normal.x = normalX; + cc.normal.y = normalY; + cc.pointCount = manifold.m_pointCount; + cc.friction = friction; + cc.restitution = restitution; + cc.localPlaneNormal.x = manifold.m_localPlaneNormal.x; + cc.localPlaneNormal.y = manifold.m_localPlaneNormal.y; + cc.localPoint.x = manifold.m_localPoint.x; + cc.localPoint.y = manifold.m_localPoint.y; + cc.radius = radiusA + radiusB; + cc.type = manifold.m_type; + for (var k = 0; k < cc.pointCount; ++k) { + var cp = manifold.m_points[k]; + var ccp = cc.points[k]; + ccp.normalImpulse = cp.m_normalImpulse; + ccp.tangentImpulse = cp.m_tangentImpulse; + ccp.localPoint.SetV(cp.m_localPoint); + var rAX = ccp.rA.x = b2ContactSolver.s_worldManifold.m_points[k].x - bodyA.m_sweep.c.x; + var rAY = ccp.rA.y = b2ContactSolver.s_worldManifold.m_points[k].y - bodyA.m_sweep.c.y; + var rBX = ccp.rB.x = b2ContactSolver.s_worldManifold.m_points[k].x - bodyB.m_sweep.c.x; + var rBY = ccp.rB.y = b2ContactSolver.s_worldManifold.m_points[k].y - bodyB.m_sweep.c.y; + var rnA = rAX * normalY - rAY * normalX; + var rnB = rBX * normalY - rBY * normalX; + rnA *= rnA; + rnB *= rnB; + var kNormal = bodyA.m_invMass + bodyB.m_invMass + bodyA.m_invI * rnA + bodyB.m_invI * rnB; + ccp.normalMass = 1.0 / kNormal; + var kEqualized = bodyA.m_mass * bodyA.m_invMass + bodyB.m_mass * bodyB.m_invMass; + kEqualized += bodyA.m_mass * bodyA.m_invI * rnA + bodyB.m_mass * bodyB.m_invI * rnB; + ccp.equalizedMass = 1.0 / kEqualized; + var tangentX = normalY; + var tangentY = (-normalX); + var rtA = rAX * tangentY - rAY * tangentX; + var rtB = rBX * tangentY - rBY * tangentX; + rtA *= rtA; + rtB *= rtB; + var kTangent = bodyA.m_invMass + bodyB.m_invMass + bodyA.m_invI * rtA + bodyB.m_invI * rtB; + ccp.tangentMass = 1.0 / kTangent; + ccp.velocityBias = 0.0; + var tX = vBX + ((-wB * rBY)) - vAX - ((-wA * rAY)); + var tY = vBY + (wB * rBX) - vAY - (wA * rAX); + var vRel = cc.normal.x * tX + cc.normal.y * tY; + if (vRel < (-b2Settings.b2_velocityThreshold)) { + ccp.velocityBias += (-cc.restitution * vRel); + } + } + if (cc.pointCount == 2) { + var ccp1 = cc.points[0]; + var ccp2 = cc.points[1]; + var invMassA = bodyA.m_invMass; + var invIA = bodyA.m_invI; + var invMassB = bodyB.m_invMass; + var invIB = bodyB.m_invI; + var rn1A = ccp1.rA.x * normalY - ccp1.rA.y * normalX; + var rn1B = ccp1.rB.x * normalY - ccp1.rB.y * normalX; + var rn2A = ccp2.rA.x * normalY - ccp2.rA.y * normalX; + var rn2B = ccp2.rB.x * normalY - ccp2.rB.y * normalX; + var k11 = invMassA + invMassB + invIA * rn1A * rn1A + invIB * rn1B * rn1B; + var k22 = invMassA + invMassB + invIA * rn2A * rn2A + invIB * rn2B * rn2B; + var k12 = invMassA + invMassB + invIA * rn1A * rn2A + invIB * rn1B * rn2B; + var k_maxConditionNumber = 100.0; + if (k11 * k11 < k_maxConditionNumber * (k11 * k22 - k12 * k12)) { + cc.K.col1.Set(k11, k12); + cc.K.col2.Set(k12, k22); + cc.K.GetInverse(cc.normalMass); + } + else { + cc.pointCount = 1; + } + } + } + } + b2ContactSolver.prototype.InitVelocityConstraints = function (step) { + var tVec; + var tVec2; + var tMat; + for (var i = 0; i < this.m_constraintCount; ++i) { + var c = this.m_constraints[i]; + var bodyA = c.bodyA; + var bodyB = c.bodyB; + var invMassA = bodyA.m_invMass; + var invIA = bodyA.m_invI; + var invMassB = bodyB.m_invMass; + var invIB = bodyB.m_invI; + var normalX = c.normal.x; + var normalY = c.normal.y; + var tangentX = normalY; + var tangentY = (-normalX); + var tX = 0; + var j = 0; + var tCount = 0; + if (step.warmStarting) { + tCount = c.pointCount; + for (j = 0; + j < tCount; ++j) { + var ccp = c.points[j]; + ccp.normalImpulse *= step.dtRatio; + ccp.tangentImpulse *= step.dtRatio; + var PX = ccp.normalImpulse * normalX + ccp.tangentImpulse * tangentX; + var PY = ccp.normalImpulse * normalY + ccp.tangentImpulse * tangentY; + bodyA.m_angularVelocity -= invIA * (ccp.rA.x * PY - ccp.rA.y * PX); + bodyA.m_linearVelocity.x -= invMassA * PX; + bodyA.m_linearVelocity.y -= invMassA * PY; + bodyB.m_angularVelocity += invIB * (ccp.rB.x * PY - ccp.rB.y * PX); + bodyB.m_linearVelocity.x += invMassB * PX; + bodyB.m_linearVelocity.y += invMassB * PY; + } + } + else { + tCount = c.pointCount; + for (j = 0; + j < tCount; ++j) { + var ccp2 = c.points[j]; + ccp2.normalImpulse = 0.0; + ccp2.tangentImpulse = 0.0; + } + } + } + } + b2ContactSolver.prototype.SolveVelocityConstraints = function () { + var j = 0; + var ccp; + var rAX = 0; + var rAY = 0; + var rBX = 0; + var rBY = 0; + var dvX = 0; + var dvY = 0; + var vn = 0; + var vt = 0; + var lambda = 0; + var maxFriction = 0; + var newImpulse = 0; + var PX = 0; + var PY = 0; + var dX = 0; + var dY = 0; + var P1X = 0; + var P1Y = 0; + var P2X = 0; + var P2Y = 0; + var tMat; + var tVec; + for (var i = 0; i < this.m_constraintCount; ++i) { + var c = this.m_constraints[i]; + var bodyA = c.bodyA; + var bodyB = c.bodyB; + var wA = bodyA.m_angularVelocity; + var wB = bodyB.m_angularVelocity; + var vA = bodyA.m_linearVelocity; + var vB = bodyB.m_linearVelocity; + var invMassA = bodyA.m_invMass; + var invIA = bodyA.m_invI; + var invMassB = bodyB.m_invMass; + var invIB = bodyB.m_invI; + var normalX = c.normal.x; + var normalY = c.normal.y; + var tangentX = normalY; + var tangentY = (-normalX); + var friction = c.friction; + var tX = 0; + for (j = 0; + j < c.pointCount; j++) { + ccp = c.points[j]; + dvX = vB.x - wB * ccp.rB.y - vA.x + wA * ccp.rA.y; + dvY = vB.y + wB * ccp.rB.x - vA.y - wA * ccp.rA.x; + vt = dvX * tangentX + dvY * tangentY; + lambda = ccp.tangentMass * (-vt); + maxFriction = friction * ccp.normalImpulse; + newImpulse = b2Math.Clamp(ccp.tangentImpulse + lambda, (-maxFriction), maxFriction); + lambda = newImpulse - ccp.tangentImpulse; + PX = lambda * tangentX; + PY = lambda * tangentY; + vA.x -= invMassA * PX; + vA.y -= invMassA * PY; + wA -= invIA * (ccp.rA.x * PY - ccp.rA.y * PX); + vB.x += invMassB * PX; + vB.y += invMassB * PY; + wB += invIB * (ccp.rB.x * PY - ccp.rB.y * PX); + ccp.tangentImpulse = newImpulse; + } + var tCount = parseInt(c.pointCount); + if (c.pointCount == 1) { + ccp = c.points[0]; + dvX = vB.x + ((-wB * ccp.rB.y)) - vA.x - ((-wA * ccp.rA.y)); + dvY = vB.y + (wB * ccp.rB.x) - vA.y - (wA * ccp.rA.x); + vn = dvX * normalX + dvY * normalY; + lambda = (-ccp.normalMass * (vn - ccp.velocityBias)); + newImpulse = ccp.normalImpulse + lambda; + newImpulse = newImpulse > 0 ? newImpulse : 0.0; + lambda = newImpulse - ccp.normalImpulse; + PX = lambda * normalX; + PY = lambda * normalY; + vA.x -= invMassA * PX; + vA.y -= invMassA * PY; + wA -= invIA * (ccp.rA.x * PY - ccp.rA.y * PX); + vB.x += invMassB * PX; + vB.y += invMassB * PY; + wB += invIB * (ccp.rB.x * PY - ccp.rB.y * PX); + ccp.normalImpulse = newImpulse; + } + else { + var cp1 = c.points[0]; + var cp2 = c.points[1]; + var aX = cp1.normalImpulse; + var aY = cp2.normalImpulse; + var dv1X = vB.x - wB * cp1.rB.y - vA.x + wA * cp1.rA.y; + var dv1Y = vB.y + wB * cp1.rB.x - vA.y - wA * cp1.rA.x; + var dv2X = vB.x - wB * cp2.rB.y - vA.x + wA * cp2.rA.y; + var dv2Y = vB.y + wB * cp2.rB.x - vA.y - wA * cp2.rA.x; + var vn1 = dv1X * normalX + dv1Y * normalY; + var vn2 = dv2X * normalX + dv2Y * normalY; + var bX = vn1 - cp1.velocityBias; + var bY = vn2 - cp2.velocityBias; + tMat = c.K; + bX -= tMat.col1.x * aX + tMat.col2.x * aY; + bY -= tMat.col1.y * aX + tMat.col2.y * aY; + var k_errorTol = 0.001; + for (;;) { + tMat = c.normalMass; + var xX = (-(tMat.col1.x * bX + tMat.col2.x * bY)); + var xY = (-(tMat.col1.y * bX + tMat.col2.y * bY)); + if (xX >= 0.0 && xY >= 0.0) { + dX = xX - aX; + dY = xY - aY; + P1X = dX * normalX; + P1Y = dX * normalY; + P2X = dY * normalX; + P2Y = dY * normalY; + vA.x -= invMassA * (P1X + P2X); + vA.y -= invMassA * (P1Y + P2Y); + wA -= invIA * (cp1.rA.x * P1Y - cp1.rA.y * P1X + cp2.rA.x * P2Y - cp2.rA.y * P2X); + vB.x += invMassB * (P1X + P2X); + vB.y += invMassB * (P1Y + P2Y); + wB += invIB * (cp1.rB.x * P1Y - cp1.rB.y * P1X + cp2.rB.x * P2Y - cp2.rB.y * P2X); + cp1.normalImpulse = xX; + cp2.normalImpulse = xY; + break; + } + xX = (-cp1.normalMass * bX); + xY = 0.0; + vn1 = 0.0; + vn2 = c.K.col1.y * xX + bY; + if (xX >= 0.0 && vn2 >= 0.0) { + dX = xX - aX; + dY = xY - aY; + P1X = dX * normalX; + P1Y = dX * normalY; + P2X = dY * normalX; + P2Y = dY * normalY; + vA.x -= invMassA * (P1X + P2X); + vA.y -= invMassA * (P1Y + P2Y); + wA -= invIA * (cp1.rA.x * P1Y - cp1.rA.y * P1X + cp2.rA.x * P2Y - cp2.rA.y * P2X); + vB.x += invMassB * (P1X + P2X); + vB.y += invMassB * (P1Y + P2Y); + wB += invIB * (cp1.rB.x * P1Y - cp1.rB.y * P1X + cp2.rB.x * P2Y - cp2.rB.y * P2X); + cp1.normalImpulse = xX; + cp2.normalImpulse = xY; + break; + } + xX = 0.0; + xY = (-cp2.normalMass * bY); + vn1 = c.K.col2.x * xY + bX; + vn2 = 0.0; + if (xY >= 0.0 && vn1 >= 0.0) { + dX = xX - aX; + dY = xY - aY; + P1X = dX * normalX; + P1Y = dX * normalY; + P2X = dY * normalX; + P2Y = dY * normalY; + vA.x -= invMassA * (P1X + P2X); + vA.y -= invMassA * (P1Y + P2Y); + wA -= invIA * (cp1.rA.x * P1Y - cp1.rA.y * P1X + cp2.rA.x * P2Y - cp2.rA.y * P2X); + vB.x += invMassB * (P1X + P2X); + vB.y += invMassB * (P1Y + P2Y); + wB += invIB * (cp1.rB.x * P1Y - cp1.rB.y * P1X + cp2.rB.x * P2Y - cp2.rB.y * P2X); + cp1.normalImpulse = xX; + cp2.normalImpulse = xY; + break; + } + xX = 0.0; + xY = 0.0; + vn1 = bX; + vn2 = bY; + if (vn1 >= 0.0 && vn2 >= 0.0) { + dX = xX - aX; + dY = xY - aY; + P1X = dX * normalX; + P1Y = dX * normalY; + P2X = dY * normalX; + P2Y = dY * normalY; + vA.x -= invMassA * (P1X + P2X); + vA.y -= invMassA * (P1Y + P2Y); + wA -= invIA * (cp1.rA.x * P1Y - cp1.rA.y * P1X + cp2.rA.x * P2Y - cp2.rA.y * P2X); + vB.x += invMassB * (P1X + P2X); + vB.y += invMassB * (P1Y + P2Y); + wB += invIB * (cp1.rB.x * P1Y - cp1.rB.y * P1X + cp2.rB.x * P2Y - cp2.rB.y * P2X); + cp1.normalImpulse = xX; + cp2.normalImpulse = xY; + break; + } + break; + } + } + bodyA.m_angularVelocity = wA; + bodyB.m_angularVelocity = wB; + } + } + b2ContactSolver.prototype.FinalizeVelocityConstraints = function () { + for (var i = 0; i < this.m_constraintCount; ++i) { + var c = this.m_constraints[i]; + var m = c.manifold; + for (var j = 0; j < c.pointCount; ++j) { + var point1 = m.m_points[j]; + var point2 = c.points[j]; + point1.m_normalImpulse = point2.normalImpulse; + point1.m_tangentImpulse = point2.tangentImpulse; + } + } + } + b2ContactSolver.prototype.SolvePositionConstraints = function (baumgarte) { + if (baumgarte === undefined) baumgarte = 0; + var minSeparation = 0.0; + for (var i = 0; i < this.m_constraintCount; i++) { + var c = this.m_constraints[i]; + var bodyA = c.bodyA; + var bodyB = c.bodyB; + var invMassA = bodyA.m_mass * bodyA.m_invMass; + var invIA = bodyA.m_mass * bodyA.m_invI; + var invMassB = bodyB.m_mass * bodyB.m_invMass; + var invIB = bodyB.m_mass * bodyB.m_invI; + b2ContactSolver.s_psm.Initialize(c); + var normal = b2ContactSolver.s_psm.m_normal; + for (var j = 0; j < c.pointCount; j++) { + var ccp = c.points[j]; + var point = b2ContactSolver.s_psm.m_points[j]; + var separation = b2ContactSolver.s_psm.m_separations[j]; + var rAX = point.x - bodyA.m_sweep.c.x; + var rAY = point.y - bodyA.m_sweep.c.y; + var rBX = point.x - bodyB.m_sweep.c.x; + var rBY = point.y - bodyB.m_sweep.c.y; + minSeparation = minSeparation < separation ? minSeparation : separation; + var C = b2Math.Clamp(baumgarte * (separation + b2Settings.b2_linearSlop), (-b2Settings.b2_maxLinearCorrection), 0.0); + var impulse = (-ccp.equalizedMass * C); + var PX = impulse * normal.x; + var PY = impulse * normal.y;bodyA.m_sweep.c.x -= invMassA * PX; + bodyA.m_sweep.c.y -= invMassA * PY; + bodyA.m_sweep.a -= invIA * (rAX * PY - rAY * PX); + bodyA.SynchronizeTransform(); + bodyB.m_sweep.c.x += invMassB * PX; + bodyB.m_sweep.c.y += invMassB * PY; + bodyB.m_sweep.a += invIB * (rBX * PY - rBY * PX); + bodyB.SynchronizeTransform(); + } + } + return minSeparation > (-1.5 * b2Settings.b2_linearSlop); + } + Box2D.postDefs.push(function () { + Box2D.Dynamics.Contacts.b2ContactSolver.s_worldManifold = new b2WorldManifold(); + Box2D.Dynamics.Contacts.b2ContactSolver.s_psm = new b2PositionSolverManifold(); + }); + Box2D.inherit(b2EdgeAndCircleContact, Box2D.Dynamics.Contacts.b2Contact); + b2EdgeAndCircleContact.prototype.__super = Box2D.Dynamics.Contacts.b2Contact.prototype; + b2EdgeAndCircleContact.b2EdgeAndCircleContact = function () { + Box2D.Dynamics.Contacts.b2Contact.b2Contact.apply(this, arguments); + }; + b2EdgeAndCircleContact.Create = function (allocator) { + return new b2EdgeAndCircleContact(); + } + b2EdgeAndCircleContact.Destroy = function (contact, allocator) {} + b2EdgeAndCircleContact.prototype.Reset = function (fixtureA, fixtureB) { + this.__super.Reset.call(this, fixtureA, fixtureB); + } + b2EdgeAndCircleContact.prototype.Evaluate = function () { + var bA = this.m_fixtureA.GetBody(); + var bB = this.m_fixtureB.GetBody(); + this.b2CollideEdgeAndCircle(this.m_manifold, (this.m_fixtureA.GetShape() instanceof b2EdgeShape ? this.m_fixtureA.GetShape() : null), bA.m_xf, (this.m_fixtureB.GetShape() instanceof b2CircleShape ? this.m_fixtureB.GetShape() : null), bB.m_xf); + } + b2EdgeAndCircleContact.prototype.b2CollideEdgeAndCircle = function (manifold, edge, xf1, circle, xf2) {} + Box2D.inherit(b2NullContact, Box2D.Dynamics.Contacts.b2Contact); + b2NullContact.prototype.__super = Box2D.Dynamics.Contacts.b2Contact.prototype; + b2NullContact.b2NullContact = function () { + Box2D.Dynamics.Contacts.b2Contact.b2Contact.apply(this, arguments); + }; + b2NullContact.prototype.b2NullContact = function () { + this.__super.b2Contact.call(this); + } + b2NullContact.prototype.Evaluate = function () {} + Box2D.inherit(b2PolyAndCircleContact, Box2D.Dynamics.Contacts.b2Contact); + b2PolyAndCircleContact.prototype.__super = Box2D.Dynamics.Contacts.b2Contact.prototype; + b2PolyAndCircleContact.b2PolyAndCircleContact = function () { + Box2D.Dynamics.Contacts.b2Contact.b2Contact.apply(this, arguments); + }; + b2PolyAndCircleContact.Create = function (allocator) { + return new b2PolyAndCircleContact(); + } + b2PolyAndCircleContact.Destroy = function (contact, allocator) {} + b2PolyAndCircleContact.prototype.Reset = function (fixtureA, fixtureB) { + this.__super.Reset.call(this, fixtureA, fixtureB); + b2Settings.b2Assert(fixtureA.GetType() == b2Shape.e_polygonShape); + b2Settings.b2Assert(fixtureB.GetType() == b2Shape.e_circleShape); + } + b2PolyAndCircleContact.prototype.Evaluate = function () { + var bA = this.m_fixtureA.m_body; + var bB = this.m_fixtureB.m_body; + b2Collision.CollidePolygonAndCircle(this.m_manifold, (this.m_fixtureA.GetShape() instanceof b2PolygonShape ? this.m_fixtureA.GetShape() : null), bA.m_xf, (this.m_fixtureB.GetShape() instanceof b2CircleShape ? this.m_fixtureB.GetShape() : null), bB.m_xf); + } + Box2D.inherit(b2PolyAndEdgeContact, Box2D.Dynamics.Contacts.b2Contact); + b2PolyAndEdgeContact.prototype.__super = Box2D.Dynamics.Contacts.b2Contact.prototype; + b2PolyAndEdgeContact.b2PolyAndEdgeContact = function () { + Box2D.Dynamics.Contacts.b2Contact.b2Contact.apply(this, arguments); + }; + b2PolyAndEdgeContact.Create = function (allocator) { + return new b2PolyAndEdgeContact(); + } + b2PolyAndEdgeContact.Destroy = function (contact, allocator) {} + b2PolyAndEdgeContact.prototype.Reset = function (fixtureA, fixtureB) { + this.__super.Reset.call(this, fixtureA, fixtureB); + b2Settings.b2Assert(fixtureA.GetType() == b2Shape.e_polygonShape); + b2Settings.b2Assert(fixtureB.GetType() == b2Shape.e_edgeShape); + } + b2PolyAndEdgeContact.prototype.Evaluate = function () { + var bA = this.m_fixtureA.GetBody(); + var bB = this.m_fixtureB.GetBody(); + this.b2CollidePolyAndEdge(this.m_manifold, (this.m_fixtureA.GetShape() instanceof b2PolygonShape ? this.m_fixtureA.GetShape() : null), bA.m_xf, (this.m_fixtureB.GetShape() instanceof b2EdgeShape ? this.m_fixtureB.GetShape() : null), bB.m_xf); + } + b2PolyAndEdgeContact.prototype.b2CollidePolyAndEdge = function (manifold, polygon, xf1, edge, xf2) {} + Box2D.inherit(b2PolygonContact, Box2D.Dynamics.Contacts.b2Contact); + b2PolygonContact.prototype.__super = Box2D.Dynamics.Contacts.b2Contact.prototype; + b2PolygonContact.b2PolygonContact = function () { + Box2D.Dynamics.Contacts.b2Contact.b2Contact.apply(this, arguments); + }; + b2PolygonContact.Create = function (allocator) { + return new b2PolygonContact(); + } + b2PolygonContact.Destroy = function (contact, allocator) {} + b2PolygonContact.prototype.Reset = function (fixtureA, fixtureB) { + this.__super.Reset.call(this, fixtureA, fixtureB); + } + b2PolygonContact.prototype.Evaluate = function () { + var bA = this.m_fixtureA.GetBody(); + var bB = this.m_fixtureB.GetBody(); + b2Collision.CollidePolygons(this.m_manifold, (this.m_fixtureA.GetShape() instanceof b2PolygonShape ? this.m_fixtureA.GetShape() : null), bA.m_xf, (this.m_fixtureB.GetShape() instanceof b2PolygonShape ? this.m_fixtureB.GetShape() : null), bB.m_xf); + } + b2PositionSolverManifold.b2PositionSolverManifold = function () {}; + b2PositionSolverManifold.prototype.b2PositionSolverManifold = function () { + this.m_normal = new b2Vec2(); + this.m_separations = new Vector_a2j_Number(b2Settings.b2_maxManifoldPoints); + this.m_points = new Vector(b2Settings.b2_maxManifoldPoints); + for (var i = 0; i < b2Settings.b2_maxManifoldPoints; i++) { + this.m_points[i] = new b2Vec2(); + } + } + b2PositionSolverManifold.prototype.Initialize = function (cc) { + b2Settings.b2Assert(cc.pointCount > 0); + var i = 0; + var clipPointX = 0; + var clipPointY = 0; + var tMat; + var tVec; + var planePointX = 0; + var planePointY = 0; + switch (cc.type) { + case b2Manifold.e_circles: + { + tMat = cc.bodyA.m_xf.R; + tVec = cc.localPoint; + var pointAX = cc.bodyA.m_xf.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + var pointAY = cc.bodyA.m_xf.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tMat = cc.bodyB.m_xf.R; + tVec = cc.points[0].localPoint; + var pointBX = cc.bodyB.m_xf.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + var pointBY = cc.bodyB.m_xf.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + var dX = pointBX - pointAX; + var dY = pointBY - pointAY; + var d2 = dX * dX + dY * dY; + if (d2 > Number.MIN_VALUE * Number.MIN_VALUE) { + var d = Math.sqrt(d2); + this.m_normal.x = dX / d; + this.m_normal.y = dY / d; + } + else { + this.m_normal.x = 1.0; + this.m_normal.y = 0.0; + } + this.m_points[0].x = 0.5 * (pointAX + pointBX); + this.m_points[0].y = 0.5 * (pointAY + pointBY); + this.m_separations[0] = dX * this.m_normal.x + dY * this.m_normal.y - cc.radius; + } + break; + case b2Manifold.e_faceA: + { + tMat = cc.bodyA.m_xf.R; + tVec = cc.localPlaneNormal; + this.m_normal.x = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + this.m_normal.y = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + tMat = cc.bodyA.m_xf.R; + tVec = cc.localPoint; + planePointX = cc.bodyA.m_xf.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + planePointY = cc.bodyA.m_xf.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tMat = cc.bodyB.m_xf.R; + for (i = 0; + i < cc.pointCount; ++i) { + tVec = cc.points[i].localPoint; + clipPointX = cc.bodyB.m_xf.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + clipPointY = cc.bodyB.m_xf.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + this.m_separations[i] = (clipPointX - planePointX) * this.m_normal.x + (clipPointY - planePointY) * this.m_normal.y - cc.radius; + this.m_points[i].x = clipPointX; + this.m_points[i].y = clipPointY; + } + } + break; + case b2Manifold.e_faceB: + { + tMat = cc.bodyB.m_xf.R; + tVec = cc.localPlaneNormal; + this.m_normal.x = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + this.m_normal.y = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + tMat = cc.bodyB.m_xf.R; + tVec = cc.localPoint; + planePointX = cc.bodyB.m_xf.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + planePointY = cc.bodyB.m_xf.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + tMat = cc.bodyA.m_xf.R; + for (i = 0; + i < cc.pointCount; ++i) { + tVec = cc.points[i].localPoint; + clipPointX = cc.bodyA.m_xf.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y); + clipPointY = cc.bodyA.m_xf.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y); + this.m_separations[i] = (clipPointX - planePointX) * this.m_normal.x + (clipPointY - planePointY) * this.m_normal.y - cc.radius; + this.m_points[i].Set(clipPointX, clipPointY); + } + this.m_normal.x *= (-1); + this.m_normal.y *= (-1); + } + break; + } + } + Box2D.postDefs.push(function () { + Box2D.Dynamics.Contacts.b2PositionSolverManifold.circlePointA = new b2Vec2(); + Box2D.Dynamics.Contacts.b2PositionSolverManifold.circlePointB = new b2Vec2(); + }); +})(); +(function () { + var b2Body = Box2D.Dynamics.b2Body, + b2BodyDef = Box2D.Dynamics.b2BodyDef, + b2ContactFilter = Box2D.Dynamics.b2ContactFilter, + b2ContactImpulse = Box2D.Dynamics.b2ContactImpulse, + b2ContactListener = Box2D.Dynamics.b2ContactListener, + b2ContactManager = Box2D.Dynamics.b2ContactManager, + b2DebugDraw = Box2D.Dynamics.b2DebugDraw, + b2DestructionListener = Box2D.Dynamics.b2DestructionListener, + b2FilterData = Box2D.Dynamics.b2FilterData, + b2Fixture = Box2D.Dynamics.b2Fixture, + b2FixtureDef = Box2D.Dynamics.b2FixtureDef, + b2Island = Box2D.Dynamics.b2Island, + b2TimeStep = Box2D.Dynamics.b2TimeStep, + b2World = Box2D.Dynamics.b2World, + b2Mat22 = Box2D.Common.Math.b2Mat22, + b2Mat33 = Box2D.Common.Math.b2Mat33, + b2Math = Box2D.Common.Math.b2Math, + b2Sweep = Box2D.Common.Math.b2Sweep, + b2Transform = Box2D.Common.Math.b2Transform, + b2Vec2 = Box2D.Common.Math.b2Vec2, + b2Vec3 = Box2D.Common.Math.b2Vec3, + b2Color = Box2D.Common.b2Color, + b2internal = Box2D.Common.b2internal, + b2Settings = Box2D.Common.b2Settings, + b2CircleShape = Box2D.Collision.Shapes.b2CircleShape, + b2EdgeChainDef = Box2D.Collision.Shapes.b2EdgeChainDef, + b2EdgeShape = Box2D.Collision.Shapes.b2EdgeShape, + b2MassData = Box2D.Collision.Shapes.b2MassData, + b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape, + b2Shape = Box2D.Collision.Shapes.b2Shape, + b2BuoyancyController = Box2D.Dynamics.Controllers.b2BuoyancyController, + b2ConstantAccelController = Box2D.Dynamics.Controllers.b2ConstantAccelController, + b2ConstantForceController = Box2D.Dynamics.Controllers.b2ConstantForceController, + b2Controller = Box2D.Dynamics.Controllers.b2Controller, + b2ControllerEdge = Box2D.Dynamics.Controllers.b2ControllerEdge, + b2GravityController = Box2D.Dynamics.Controllers.b2GravityController, + b2TensorDampingController = Box2D.Dynamics.Controllers.b2TensorDampingController; + + Box2D.inherit(b2BuoyancyController, Box2D.Dynamics.Controllers.b2Controller); + b2BuoyancyController.prototype.__super = Box2D.Dynamics.Controllers.b2Controller.prototype; + b2BuoyancyController.b2BuoyancyController = function () { + Box2D.Dynamics.Controllers.b2Controller.b2Controller.apply(this, arguments); + this.normal = new b2Vec2(0, (-1)); + this.offset = 0; + this.density = 0; + this.velocity = new b2Vec2(0, 0); + this.linearDrag = 2; + this.angularDrag = 1; + this.useDensity = false; + this.useWorldGravity = true; + this.gravity = null; + }; + b2BuoyancyController.prototype.Step = function (step) { + if (!this.m_bodyList) return; + if (this.useWorldGravity) { + this.gravity = this.GetWorld().GetGravity().Copy(); + } + for (var i = this.m_bodyList; i; i = i.nextBody) { + var body = i.body; + if (body.IsAwake() == false) { + continue; + } + var areac = new b2Vec2(); + var massc = new b2Vec2(); + var area = 0.0; + var mass = 0.0; + for (var fixture = body.GetFixtureList(); fixture; fixture = fixture.GetNext()) { + var sc = new b2Vec2(); + var sarea = fixture.GetShape().ComputeSubmergedArea(this.normal, this.offset, body.GetTransform(), sc); + area += sarea; + areac.x += sarea * sc.x; + areac.y += sarea * sc.y; + var shapeDensity = 0; + if (this.useDensity) { + shapeDensity = 1; + } + else { + shapeDensity = 1; + } + mass += sarea * shapeDensity; + massc.x += sarea * sc.x * shapeDensity; + massc.y += sarea * sc.y * shapeDensity; + } + areac.x /= area; + areac.y /= area; + massc.x /= mass; + massc.y /= mass; + if (area < Number.MIN_VALUE) continue; + var buoyancyForce = this.gravity.GetNegative(); + buoyancyForce.Multiply(this.density * area); + body.ApplyForce(buoyancyForce, massc); + var dragForce = body.GetLinearVelocityFromWorldPoint(areac); + dragForce.Subtract(this.velocity); + dragForce.Multiply((-this.linearDrag * area)); + body.ApplyForce(dragForce, areac); + body.ApplyTorque((-body.GetInertia() / body.GetMass() * area * body.GetAngularVelocity() * this.angularDrag)); + } + } + b2BuoyancyController.prototype.Draw = function (debugDraw) { + var r = 1000; + var p1 = new b2Vec2(); + var p2 = new b2Vec2(); + p1.x = this.normal.x * this.offset + this.normal.y * r; + p1.y = this.normal.y * this.offset - this.normal.x * r; + p2.x = this.normal.x * this.offset - this.normal.y * r; + p2.y = this.normal.y * this.offset + this.normal.x * r; + var color = new b2Color(0, 0, 1); + debugDraw.DrawSegment(p1, p2, color); + } + Box2D.inherit(b2ConstantAccelController, Box2D.Dynamics.Controllers.b2Controller); + b2ConstantAccelController.prototype.__super = Box2D.Dynamics.Controllers.b2Controller.prototype; + b2ConstantAccelController.b2ConstantAccelController = function () { + Box2D.Dynamics.Controllers.b2Controller.b2Controller.apply(this, arguments); + this.A = new b2Vec2(0, 0); + }; + b2ConstantAccelController.prototype.Step = function (step) { + var smallA = new b2Vec2(this.A.x * step.dt, this.A.y * step.dt); + for (var i = this.m_bodyList; i; i = i.nextBody) { + var body = i.body; + if (!body.IsAwake()) continue; + body.SetLinearVelocity(new b2Vec2(body.GetLinearVelocity().x + smallA.x, body.GetLinearVelocity().y + smallA.y)); + } + } + Box2D.inherit(b2ConstantForceController, Box2D.Dynamics.Controllers.b2Controller); + b2ConstantForceController.prototype.__super = Box2D.Dynamics.Controllers.b2Controller.prototype; + b2ConstantForceController.b2ConstantForceController = function () { + Box2D.Dynamics.Controllers.b2Controller.b2Controller.apply(this, arguments); + this.F = new b2Vec2(0, 0); + }; + b2ConstantForceController.prototype.Step = function (step) { + for (var i = this.m_bodyList; i; i = i.nextBody) { + var body = i.body; + if (!body.IsAwake()) continue; + body.ApplyForce(this.F, body.GetWorldCenter()); + } + } + b2Controller.b2Controller = function () {}; + b2Controller.prototype.Step = function (step) {} + b2Controller.prototype.Draw = function (debugDraw) {} + b2Controller.prototype.AddBody = function (body) { + var edge = new b2ControllerEdge(); + edge.controller = this; + edge.body = body; + edge.nextBody = this.m_bodyList; + edge.prevBody = null; + this.m_bodyList = edge; + if (edge.nextBody) edge.nextBody.prevBody = edge; + this.m_bodyCount++; + edge.nextController = body.m_controllerList; + edge.prevController = null; + body.m_controllerList = edge; + if (edge.nextController) edge.nextController.prevController = edge; + body.m_controllerCount++; + } + b2Controller.prototype.RemoveBody = function (body) { + var edge = body.m_controllerList; + while (edge && edge.controller != this) + edge = edge.nextController; + if (edge.prevBody) edge.prevBody.nextBody = edge.nextBody; + if (edge.nextBody) edge.nextBody.prevBody = edge.prevBody; + if (edge.nextController) edge.nextController.prevController = edge.prevController; + if (edge.prevController) edge.prevController.nextController = edge.nextController; + if (this.m_bodyList == edge) this.m_bodyList = edge.nextBody; + if (body.m_controllerList == edge) body.m_controllerList = edge.nextController; + body.m_controllerCount--; + this.m_bodyCount--; + } + b2Controller.prototype.Clear = function () { + while (this.m_bodyList) + this.RemoveBody(this.m_bodyList.body); + } + b2Controller.prototype.GetNext = function () { + return this.m_next; + } + b2Controller.prototype.GetWorld = function () { + return this.m_world; + } + b2Controller.prototype.GetBodyList = function () { + return this.m_bodyList; + } + b2ControllerEdge.b2ControllerEdge = function () {}; + Box2D.inherit(b2GravityController, Box2D.Dynamics.Controllers.b2Controller); + b2GravityController.prototype.__super = Box2D.Dynamics.Controllers.b2Controller.prototype; + b2GravityController.b2GravityController = function () { + Box2D.Dynamics.Controllers.b2Controller.b2Controller.apply(this, arguments); + this.G = 1; + this.invSqr = true; + }; + b2GravityController.prototype.Step = function (step) { + var i = null; + var body1 = null; + var p1 = null; + var mass1 = 0; + var j = null; + var body2 = null; + var p2 = null; + var dx = 0; + var dy = 0; + var r2 = 0; + var f = null; + if (this.invSqr) { + for (i = this.m_bodyList; + i; i = i.nextBody) { + body1 = i.body; + p1 = body1.GetWorldCenter(); + mass1 = body1.GetMass(); + for (j = this.m_bodyList; + j != i; j = j.nextBody) { + body2 = j.body; + p2 = body2.GetWorldCenter(); + dx = p2.x - p1.x; + dy = p2.y - p1.y; + r2 = dx * dx + dy * dy; + if (r2 < Number.MIN_VALUE) continue; + f = new b2Vec2(dx, dy); + f.Multiply(this.G / r2 / Math.sqrt(r2) * mass1 * body2.GetMass()); + if (body1.IsAwake()) body1.ApplyForce(f, p1); + f.Multiply((-1)); + if (body2.IsAwake()) body2.ApplyForce(f, p2); + } + } + } + else { + for (i = this.m_bodyList; + i; i = i.nextBody) { + body1 = i.body; + p1 = body1.GetWorldCenter(); + mass1 = body1.GetMass(); + for (j = this.m_bodyList; + j != i; j = j.nextBody) { + body2 = j.body; + p2 = body2.GetWorldCenter(); + dx = p2.x - p1.x; + dy = p2.y - p1.y; + r2 = dx * dx + dy * dy; + if (r2 < Number.MIN_VALUE) continue; + f = new b2Vec2(dx, dy); + f.Multiply(this.G / r2 * mass1 * body2.GetMass()); + if (body1.IsAwake()) body1.ApplyForce(f, p1); + f.Multiply((-1)); + if (body2.IsAwake()) body2.ApplyForce(f, p2); + } + } + } + } + Box2D.inherit(b2TensorDampingController, Box2D.Dynamics.Controllers.b2Controller); + b2TensorDampingController.prototype.__super = Box2D.Dynamics.Controllers.b2Controller.prototype; + b2TensorDampingController.b2TensorDampingController = function () { + Box2D.Dynamics.Controllers.b2Controller.b2Controller.apply(this, arguments); + this.T = new b2Mat22(); + this.maxTimestep = 0; + }; + b2TensorDampingController.prototype.SetAxisAligned = function (xDamping, yDamping) { + if (xDamping === undefined) xDamping = 0; + if (yDamping === undefined) yDamping = 0; + this.T.col1.x = (-xDamping); + this.T.col1.y = 0; + this.T.col2.x = 0; + this.T.col2.y = (-yDamping); + if (xDamping > 0 || yDamping > 0) { + this.maxTimestep = 1 / Math.max(xDamping, yDamping); + } + else { + this.maxTimestep = 0; + } + } + b2TensorDampingController.prototype.Step = function (step) { + var timestep = step.dt; + if (timestep <= Number.MIN_VALUE) return; + if (timestep > this.maxTimestep && this.maxTimestep > 0) timestep = this.maxTimestep; + for (var i = this.m_bodyList; i; i = i.nextBody) { + var body = i.body; + if (!body.IsAwake()) { + continue; + } + var damping = body.GetWorldVector(b2Math.MulMV(this.T, body.GetLocalVector(body.GetLinearVelocity()))); + body.SetLinearVelocity(new b2Vec2(body.GetLinearVelocity().x + damping.x * timestep, body.GetLinearVelocity().y + damping.y * timestep)); + } + } +})(); +(function () { + var b2Color = Box2D.Common.b2Color, + b2internal = Box2D.Common.b2internal, + b2Settings = Box2D.Common.b2Settings, + b2Mat22 = Box2D.Common.Math.b2Mat22, + b2Mat33 = Box2D.Common.Math.b2Mat33, + b2Math = Box2D.Common.Math.b2Math, + b2Sweep = Box2D.Common.Math.b2Sweep, + b2Transform = Box2D.Common.Math.b2Transform, + b2Vec2 = Box2D.Common.Math.b2Vec2, + b2Vec3 = Box2D.Common.Math.b2Vec3, + b2DistanceJoint = Box2D.Dynamics.Joints.b2DistanceJoint, + b2DistanceJointDef = Box2D.Dynamics.Joints.b2DistanceJointDef, + b2FrictionJoint = Box2D.Dynamics.Joints.b2FrictionJoint, + b2FrictionJointDef = Box2D.Dynamics.Joints.b2FrictionJointDef, + b2GearJoint = Box2D.Dynamics.Joints.b2GearJoint, + b2GearJointDef = Box2D.Dynamics.Joints.b2GearJointDef, + b2Jacobian = Box2D.Dynamics.Joints.b2Jacobian, + b2Joint = Box2D.Dynamics.Joints.b2Joint, + b2JointDef = Box2D.Dynamics.Joints.b2JointDef, + b2JointEdge = Box2D.Dynamics.Joints.b2JointEdge, + b2LineJoint = Box2D.Dynamics.Joints.b2LineJoint, + b2LineJointDef = Box2D.Dynamics.Joints.b2LineJointDef, + b2MouseJoint = Box2D.Dynamics.Joints.b2MouseJoint, + b2MouseJointDef = Box2D.Dynamics.Joints.b2MouseJointDef, + b2PrismaticJoint = Box2D.Dynamics.Joints.b2PrismaticJoint, + b2PrismaticJointDef = Box2D.Dynamics.Joints.b2PrismaticJointDef, + b2PulleyJoint = Box2D.Dynamics.Joints.b2PulleyJoint, + b2PulleyJointDef = Box2D.Dynamics.Joints.b2PulleyJointDef, + b2RevoluteJoint = Box2D.Dynamics.Joints.b2RevoluteJoint, + b2RevoluteJointDef = Box2D.Dynamics.Joints.b2RevoluteJointDef, + b2WeldJoint = Box2D.Dynamics.Joints.b2WeldJoint, + b2WeldJointDef = Box2D.Dynamics.Joints.b2WeldJointDef, + b2Body = Box2D.Dynamics.b2Body, + b2BodyDef = Box2D.Dynamics.b2BodyDef, + b2ContactFilter = Box2D.Dynamics.b2ContactFilter, + b2ContactImpulse = Box2D.Dynamics.b2ContactImpulse, + b2ContactListener = Box2D.Dynamics.b2ContactListener, + b2ContactManager = Box2D.Dynamics.b2ContactManager, + b2DebugDraw = Box2D.Dynamics.b2DebugDraw, + b2DestructionListener = Box2D.Dynamics.b2DestructionListener, + b2FilterData = Box2D.Dynamics.b2FilterData, + b2Fixture = Box2D.Dynamics.b2Fixture, + b2FixtureDef = Box2D.Dynamics.b2FixtureDef, + b2Island = Box2D.Dynamics.b2Island, + b2TimeStep = Box2D.Dynamics.b2TimeStep, + b2World = Box2D.Dynamics.b2World; + + Box2D.inherit(b2DistanceJoint, Box2D.Dynamics.Joints.b2Joint); + b2DistanceJoint.prototype.__super = Box2D.Dynamics.Joints.b2Joint.prototype; + b2DistanceJoint.b2DistanceJoint = function () { + Box2D.Dynamics.Joints.b2Joint.b2Joint.apply(this, arguments); + this.m_localAnchor1 = new b2Vec2(); + this.m_localAnchor2 = new b2Vec2(); + this.m_u = new b2Vec2(); + }; + b2DistanceJoint.prototype.GetAnchorA = function () { + return this.m_bodyA.GetWorldPoint(this.m_localAnchor1); + } + b2DistanceJoint.prototype.GetAnchorB = function () { + return this.m_bodyB.GetWorldPoint(this.m_localAnchor2); + } + b2DistanceJoint.prototype.GetReactionForce = function (inv_dt) { + if (inv_dt === undefined) inv_dt = 0; + return new b2Vec2(inv_dt * this.m_impulse * this.m_u.x, inv_dt * this.m_impulse * this.m_u.y); + } + b2DistanceJoint.prototype.GetReactionTorque = function (inv_dt) { + if (inv_dt === undefined) inv_dt = 0; + return 0.0; + } + b2DistanceJoint.prototype.GetLength = function () { + return this.m_length; + } + b2DistanceJoint.prototype.SetLength = function (length) { + if (length === undefined) length = 0; + this.m_length = length; + } + b2DistanceJoint.prototype.GetFrequency = function () { + return this.m_frequencyHz; + } + b2DistanceJoint.prototype.SetFrequency = function (hz) { + if (hz === undefined) hz = 0; + this.m_frequencyHz = hz; + } + b2DistanceJoint.prototype.GetDampingRatio = function () { + return this.m_dampingRatio; + } + b2DistanceJoint.prototype.SetDampingRatio = function (ratio) { + if (ratio === undefined) ratio = 0; + this.m_dampingRatio = ratio; + } + b2DistanceJoint.prototype.b2DistanceJoint = function (def) { + this.__super.b2Joint.call(this, def); + var tMat; + var tX = 0; + var tY = 0; + this.m_localAnchor1.SetV(def.localAnchorA); + this.m_localAnchor2.SetV(def.localAnchorB); + this.m_length = def.length; + this.m_frequencyHz = def.frequencyHz; + this.m_dampingRatio = def.dampingRatio; + this.m_impulse = 0.0; + this.m_gamma = 0.0; + this.m_bias = 0.0; + } + b2DistanceJoint.prototype.InitVelocityConstraints = function (step) { + var tMat; + var tX = 0; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + tMat = bA.m_xf.R; + var r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + var r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + var r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + var r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + this.m_u.x = bB.m_sweep.c.x + r2X - bA.m_sweep.c.x - r1X; + this.m_u.y = bB.m_sweep.c.y + r2Y - bA.m_sweep.c.y - r1Y; + var length = Math.sqrt(this.m_u.x * this.m_u.x + this.m_u.y * this.m_u.y); + if (length > b2Settings.b2_linearSlop) { + this.m_u.Multiply(1.0 / length); + } + else { + this.m_u.SetZero(); + } + var cr1u = (r1X * this.m_u.y - r1Y * this.m_u.x); + var cr2u = (r2X * this.m_u.y - r2Y * this.m_u.x); + var invMass = bA.m_invMass + bA.m_invI * cr1u * cr1u + bB.m_invMass + bB.m_invI * cr2u * cr2u; + this.m_mass = invMass != 0.0 ? 1.0 / invMass : 0.0; + if (this.m_frequencyHz > 0.0) { + var C = length - this.m_length; + var omega = 2.0 * Math.PI * this.m_frequencyHz; + var d = 2.0 * this.m_mass * this.m_dampingRatio * omega; + var k = this.m_mass * omega * omega; + this.m_gamma = step.dt * (d + step.dt * k); + this.m_gamma = this.m_gamma != 0.0 ? 1 / this.m_gamma : 0.0; + this.m_bias = C * step.dt * k * this.m_gamma; + this.m_mass = invMass + this.m_gamma; + this.m_mass = this.m_mass != 0.0 ? 1.0 / this.m_mass : 0.0; + } + if (step.warmStarting) { + this.m_impulse *= step.dtRatio; + var PX = this.m_impulse * this.m_u.x; + var PY = this.m_impulse * this.m_u.y; + bA.m_linearVelocity.x -= bA.m_invMass * PX; + bA.m_linearVelocity.y -= bA.m_invMass * PY; + bA.m_angularVelocity -= bA.m_invI * (r1X * PY - r1Y * PX); + bB.m_linearVelocity.x += bB.m_invMass * PX; + bB.m_linearVelocity.y += bB.m_invMass * PY; + bB.m_angularVelocity += bB.m_invI * (r2X * PY - r2Y * PX); + } + else { + this.m_impulse = 0.0; + } + } + b2DistanceJoint.prototype.SolveVelocityConstraints = function (step) { + var tMat; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + tMat = bA.m_xf.R; + var r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + var r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + var tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + var r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + var r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var v1X = bA.m_linearVelocity.x + ((-bA.m_angularVelocity * r1Y)); + var v1Y = bA.m_linearVelocity.y + (bA.m_angularVelocity * r1X); + var v2X = bB.m_linearVelocity.x + ((-bB.m_angularVelocity * r2Y)); + var v2Y = bB.m_linearVelocity.y + (bB.m_angularVelocity * r2X); + var Cdot = (this.m_u.x * (v2X - v1X) + this.m_u.y * (v2Y - v1Y)); + var impulse = (-this.m_mass * (Cdot + this.m_bias + this.m_gamma * this.m_impulse)); + this.m_impulse += impulse; + var PX = impulse * this.m_u.x; + var PY = impulse * this.m_u.y; + bA.m_linearVelocity.x -= bA.m_invMass * PX; + bA.m_linearVelocity.y -= bA.m_invMass * PY; + bA.m_angularVelocity -= bA.m_invI * (r1X * PY - r1Y * PX); + bB.m_linearVelocity.x += bB.m_invMass * PX; + bB.m_linearVelocity.y += bB.m_invMass * PY; + bB.m_angularVelocity += bB.m_invI * (r2X * PY - r2Y * PX); + } + b2DistanceJoint.prototype.SolvePositionConstraints = function (baumgarte) { + if (baumgarte === undefined) baumgarte = 0; + var tMat; + if (this.m_frequencyHz > 0.0) { + return true; + } + var bA = this.m_bodyA; + var bB = this.m_bodyB; + tMat = bA.m_xf.R; + var r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + var r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + var tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + var r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + var r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var dX = bB.m_sweep.c.x + r2X - bA.m_sweep.c.x - r1X; + var dY = bB.m_sweep.c.y + r2Y - bA.m_sweep.c.y - r1Y; + var length = Math.sqrt(dX * dX + dY * dY); + dX /= length; + dY /= length; + var C = length - this.m_length; + C = b2Math.Clamp(C, (-b2Settings.b2_maxLinearCorrection), b2Settings.b2_maxLinearCorrection); + var impulse = (-this.m_mass * C); + this.m_u.Set(dX, dY); + var PX = impulse * this.m_u.x; + var PY = impulse * this.m_u.y; + bA.m_sweep.c.x -= bA.m_invMass * PX; + bA.m_sweep.c.y -= bA.m_invMass * PY; + bA.m_sweep.a -= bA.m_invI * (r1X * PY - r1Y * PX); + bB.m_sweep.c.x += bB.m_invMass * PX; + bB.m_sweep.c.y += bB.m_invMass * PY; + bB.m_sweep.a += bB.m_invI * (r2X * PY - r2Y * PX); + bA.SynchronizeTransform(); + bB.SynchronizeTransform(); + return b2Math.Abs(C) < b2Settings.b2_linearSlop; + } + Box2D.inherit(b2DistanceJointDef, Box2D.Dynamics.Joints.b2JointDef); + b2DistanceJointDef.prototype.__super = Box2D.Dynamics.Joints.b2JointDef.prototype; + b2DistanceJointDef.b2DistanceJointDef = function () { + Box2D.Dynamics.Joints.b2JointDef.b2JointDef.apply(this, arguments); + this.localAnchorA = new b2Vec2(); + this.localAnchorB = new b2Vec2(); + }; + b2DistanceJointDef.prototype.b2DistanceJointDef = function () { + this.__super.b2JointDef.call(this); + this.type = b2Joint.e_distanceJoint; + this.length = 1.0; + this.frequencyHz = 0.0; + this.dampingRatio = 0.0; + } + b2DistanceJointDef.prototype.Initialize = function (bA, bB, anchorA, anchorB) { + this.bodyA = bA; + this.bodyB = bB; + this.localAnchorA.SetV(this.bodyA.GetLocalPoint(anchorA)); + this.localAnchorB.SetV(this.bodyB.GetLocalPoint(anchorB)); + var dX = anchorB.x - anchorA.x; + var dY = anchorB.y - anchorA.y; + this.length = Math.sqrt(dX * dX + dY * dY); + this.frequencyHz = 0.0; + this.dampingRatio = 0.0; + } + Box2D.inherit(b2FrictionJoint, Box2D.Dynamics.Joints.b2Joint); + b2FrictionJoint.prototype.__super = Box2D.Dynamics.Joints.b2Joint.prototype; + b2FrictionJoint.b2FrictionJoint = function () { + Box2D.Dynamics.Joints.b2Joint.b2Joint.apply(this, arguments); + this.m_localAnchorA = new b2Vec2(); + this.m_localAnchorB = new b2Vec2(); + this.m_linearMass = new b2Mat22(); + this.m_linearImpulse = new b2Vec2(); + }; + b2FrictionJoint.prototype.GetAnchorA = function () { + return this.m_bodyA.GetWorldPoint(this.m_localAnchorA); + } + b2FrictionJoint.prototype.GetAnchorB = function () { + return this.m_bodyB.GetWorldPoint(this.m_localAnchorB); + } + b2FrictionJoint.prototype.GetReactionForce = function (inv_dt) { + if (inv_dt === undefined) inv_dt = 0; + return new b2Vec2(inv_dt * this.m_linearImpulse.x, inv_dt * this.m_linearImpulse.y); + } + b2FrictionJoint.prototype.GetReactionTorque = function (inv_dt) { + if (inv_dt === undefined) inv_dt = 0; + return inv_dt * this.m_angularImpulse; + } + b2FrictionJoint.prototype.SetMaxForce = function (force) { + if (force === undefined) force = 0; + this.m_maxForce = force; + } + b2FrictionJoint.prototype.GetMaxForce = function () { + return this.m_maxForce; + } + b2FrictionJoint.prototype.SetMaxTorque = function (torque) { + if (torque === undefined) torque = 0; + this.m_maxTorque = torque; + } + b2FrictionJoint.prototype.GetMaxTorque = function () { + return this.m_maxTorque; + } + b2FrictionJoint.prototype.b2FrictionJoint = function (def) { + this.__super.b2Joint.call(this, def); + this.m_localAnchorA.SetV(def.localAnchorA); + this.m_localAnchorB.SetV(def.localAnchorB); + this.m_linearMass.SetZero(); + this.m_angularMass = 0.0; + this.m_linearImpulse.SetZero(); + this.m_angularImpulse = 0.0; + this.m_maxForce = def.maxForce; + this.m_maxTorque = def.maxTorque; + } + b2FrictionJoint.prototype.InitVelocityConstraints = function (step) { + var tMat; + var tX = 0; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + tMat = bA.m_xf.R; + var rAX = this.m_localAnchorA.x - bA.m_sweep.localCenter.x; + var rAY = this.m_localAnchorA.y - bA.m_sweep.localCenter.y; + tX = (tMat.col1.x * rAX + tMat.col2.x * rAY); + rAY = (tMat.col1.y * rAX + tMat.col2.y * rAY); + rAX = tX; + tMat = bB.m_xf.R; + var rBX = this.m_localAnchorB.x - bB.m_sweep.localCenter.x; + var rBY = this.m_localAnchorB.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * rBX + tMat.col2.x * rBY); + rBY = (tMat.col1.y * rBX + tMat.col2.y * rBY); + rBX = tX; + var mA = bA.m_invMass; + var mB = bB.m_invMass; + var iA = bA.m_invI; + var iB = bB.m_invI; + var K = new b2Mat22(); + K.col1.x = mA + mB; + K.col2.x = 0.0; + K.col1.y = 0.0; + K.col2.y = mA + mB; + K.col1.x += iA * rAY * rAY; + K.col2.x += (-iA * rAX * rAY); + K.col1.y += (-iA * rAX * rAY); + K.col2.y += iA * rAX * rAX; + K.col1.x += iB * rBY * rBY; + K.col2.x += (-iB * rBX * rBY); + K.col1.y += (-iB * rBX * rBY); + K.col2.y += iB * rBX * rBX; + K.GetInverse(this.m_linearMass); + this.m_angularMass = iA + iB; + if (this.m_angularMass > 0.0) { + this.m_angularMass = 1.0 / this.m_angularMass; + } + if (step.warmStarting) { + this.m_linearImpulse.x *= step.dtRatio; + this.m_linearImpulse.y *= step.dtRatio; + this.m_angularImpulse *= step.dtRatio; + var P = this.m_linearImpulse; + bA.m_linearVelocity.x -= mA * P.x; + bA.m_linearVelocity.y -= mA * P.y; + bA.m_angularVelocity -= iA * (rAX * P.y - rAY * P.x + this.m_angularImpulse); + bB.m_linearVelocity.x += mB * P.x; + bB.m_linearVelocity.y += mB * P.y; + bB.m_angularVelocity += iB * (rBX * P.y - rBY * P.x + this.m_angularImpulse); + } + else { + this.m_linearImpulse.SetZero(); + this.m_angularImpulse = 0.0; + } + } + b2FrictionJoint.prototype.SolveVelocityConstraints = function (step) { + var tMat; + var tX = 0; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var vA = bA.m_linearVelocity; + var wA = bA.m_angularVelocity; + var vB = bB.m_linearVelocity; + var wB = bB.m_angularVelocity; + var mA = bA.m_invMass; + var mB = bB.m_invMass; + var iA = bA.m_invI; + var iB = bB.m_invI; + tMat = bA.m_xf.R; + var rAX = this.m_localAnchorA.x - bA.m_sweep.localCenter.x; + var rAY = this.m_localAnchorA.y - bA.m_sweep.localCenter.y; + tX = (tMat.col1.x * rAX + tMat.col2.x * rAY); + rAY = (tMat.col1.y * rAX + tMat.col2.y * rAY); + rAX = tX; + tMat = bB.m_xf.R; + var rBX = this.m_localAnchorB.x - bB.m_sweep.localCenter.x; + var rBY = this.m_localAnchorB.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * rBX + tMat.col2.x * rBY); + rBY = (tMat.col1.y * rBX + tMat.col2.y * rBY); + rBX = tX; + var maxImpulse = 0; { + var Cdot = wB - wA; + var impulse = (-this.m_angularMass * Cdot); + var oldImpulse = this.m_angularImpulse; + maxImpulse = step.dt * this.m_maxTorque; + this.m_angularImpulse = b2Math.Clamp(this.m_angularImpulse + impulse, (-maxImpulse), maxImpulse); + impulse = this.m_angularImpulse - oldImpulse; + wA -= iA * impulse; + wB += iB * impulse; + } { + var CdotX = vB.x - wB * rBY - vA.x + wA * rAY; + var CdotY = vB.y + wB * rBX - vA.y - wA * rAX; + var impulseV = b2Math.MulMV(this.m_linearMass, new b2Vec2((-CdotX), (-CdotY))); + var oldImpulseV = this.m_linearImpulse.Copy(); + this.m_linearImpulse.Add(impulseV); + maxImpulse = step.dt * this.m_maxForce; + if (this.m_linearImpulse.LengthSquared() > maxImpulse * maxImpulse) { + this.m_linearImpulse.Normalize(); + this.m_linearImpulse.Multiply(maxImpulse); + } + impulseV = b2Math.SubtractVV(this.m_linearImpulse, oldImpulseV); + vA.x -= mA * impulseV.x; + vA.y -= mA * impulseV.y; + wA -= iA * (rAX * impulseV.y - rAY * impulseV.x); + vB.x += mB * impulseV.x; + vB.y += mB * impulseV.y; + wB += iB * (rBX * impulseV.y - rBY * impulseV.x); + } + bA.m_angularVelocity = wA; + bB.m_angularVelocity = wB; + } + b2FrictionJoint.prototype.SolvePositionConstraints = function (baumgarte) { + if (baumgarte === undefined) baumgarte = 0; + return true; + } + Box2D.inherit(b2FrictionJointDef, Box2D.Dynamics.Joints.b2JointDef); + b2FrictionJointDef.prototype.__super = Box2D.Dynamics.Joints.b2JointDef.prototype; + b2FrictionJointDef.b2FrictionJointDef = function () { + Box2D.Dynamics.Joints.b2JointDef.b2JointDef.apply(this, arguments); + this.localAnchorA = new b2Vec2(); + this.localAnchorB = new b2Vec2(); + }; + b2FrictionJointDef.prototype.b2FrictionJointDef = function () { + this.__super.b2JointDef.call(this); + this.type = b2Joint.e_frictionJoint; + this.maxForce = 0.0; + this.maxTorque = 0.0; + } + b2FrictionJointDef.prototype.Initialize = function (bA, bB, anchor) { + this.bodyA = bA; + this.bodyB = bB; + this.localAnchorA.SetV(this.bodyA.GetLocalPoint(anchor)); + this.localAnchorB.SetV(this.bodyB.GetLocalPoint(anchor)); + } + Box2D.inherit(b2GearJoint, Box2D.Dynamics.Joints.b2Joint); + b2GearJoint.prototype.__super = Box2D.Dynamics.Joints.b2Joint.prototype; + b2GearJoint.b2GearJoint = function () { + Box2D.Dynamics.Joints.b2Joint.b2Joint.apply(this, arguments); + this.m_groundAnchor1 = new b2Vec2(); + this.m_groundAnchor2 = new b2Vec2(); + this.m_localAnchor1 = new b2Vec2(); + this.m_localAnchor2 = new b2Vec2(); + this.m_J = new b2Jacobian(); + }; + b2GearJoint.prototype.GetAnchorA = function () { + return this.m_bodyA.GetWorldPoint(this.m_localAnchor1); + } + b2GearJoint.prototype.GetAnchorB = function () { + return this.m_bodyB.GetWorldPoint(this.m_localAnchor2); + } + b2GearJoint.prototype.GetReactionForce = function (inv_dt) { + if (inv_dt === undefined) inv_dt = 0; + return new b2Vec2(inv_dt * this.m_impulse * this.m_J.linearB.x, inv_dt * this.m_impulse * this.m_J.linearB.y); + } + b2GearJoint.prototype.GetReactionTorque = function (inv_dt) { + if (inv_dt === undefined) inv_dt = 0; + var tMat = this.m_bodyB.m_xf.R; + var rX = this.m_localAnchor1.x - this.m_bodyB.m_sweep.localCenter.x; + var rY = this.m_localAnchor1.y - this.m_bodyB.m_sweep.localCenter.y; + var tX = tMat.col1.x * rX + tMat.col2.x * rY; + rY = tMat.col1.y * rX + tMat.col2.y * rY; + rX = tX; + var PX = this.m_impulse * this.m_J.linearB.x; + var PY = this.m_impulse * this.m_J.linearB.y; + return inv_dt * (this.m_impulse * this.m_J.angularB - rX * PY + rY * PX); + } + b2GearJoint.prototype.GetRatio = function () { + return this.m_ratio; + } + b2GearJoint.prototype.SetRatio = function (ratio) { + if (ratio === undefined) ratio = 0; + this.m_ratio = ratio; + } + b2GearJoint.prototype.b2GearJoint = function (def) { + this.__super.b2Joint.call(this, def); + var type1 = parseInt(def.joint1.m_type); + var type2 = parseInt(def.joint2.m_type); + this.m_revolute1 = null; + this.m_prismatic1 = null; + this.m_revolute2 = null; + this.m_prismatic2 = null; + var coordinate1 = 0; + var coordinate2 = 0; + this.m_ground1 = def.joint1.GetBodyA(); + this.m_bodyA = def.joint1.GetBodyB(); + if (type1 == b2Joint.e_revoluteJoint) { + this.m_revolute1 = (def.joint1 instanceof b2RevoluteJoint ? def.joint1 : null); + this.m_groundAnchor1.SetV(this.m_revolute1.m_localAnchor1); + this.m_localAnchor1.SetV(this.m_revolute1.m_localAnchor2); + coordinate1 = this.m_revolute1.GetJointAngle(); + } + else { + this.m_prismatic1 = (def.joint1 instanceof b2PrismaticJoint ? def.joint1 : null); + this.m_groundAnchor1.SetV(this.m_prismatic1.m_localAnchor1); + this.m_localAnchor1.SetV(this.m_prismatic1.m_localAnchor2); + coordinate1 = this.m_prismatic1.GetJointTranslation(); + } + this.m_ground2 = def.joint2.GetBodyA(); + this.m_bodyB = def.joint2.GetBodyB(); + if (type2 == b2Joint.e_revoluteJoint) { + this.m_revolute2 = (def.joint2 instanceof b2RevoluteJoint ? def.joint2 : null); + this.m_groundAnchor2.SetV(this.m_revolute2.m_localAnchor1); + this.m_localAnchor2.SetV(this.m_revolute2.m_localAnchor2); + coordinate2 = this.m_revolute2.GetJointAngle(); + } + else { + this.m_prismatic2 = (def.joint2 instanceof b2PrismaticJoint ? def.joint2 : null); + this.m_groundAnchor2.SetV(this.m_prismatic2.m_localAnchor1); + this.m_localAnchor2.SetV(this.m_prismatic2.m_localAnchor2); + coordinate2 = this.m_prismatic2.GetJointTranslation(); + } + this.m_ratio = def.ratio; + this.m_constant = coordinate1 + this.m_ratio * coordinate2; + this.m_impulse = 0.0; + } + b2GearJoint.prototype.InitVelocityConstraints = function (step) { + var g1 = this.m_ground1; + var g2 = this.m_ground2; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var ugX = 0; + var ugY = 0; + var rX = 0; + var rY = 0; + var tMat; + var tVec; + var crug = 0; + var tX = 0; + var K = 0.0; + this.m_J.SetZero(); + if (this.m_revolute1) { + this.m_J.angularA = (-1.0); + K += bA.m_invI; + } + else { + tMat = g1.m_xf.R; + tVec = this.m_prismatic1.m_localXAxis1; + ugX = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + ugY = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + tMat = bA.m_xf.R; + rX = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + rY = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + tX = tMat.col1.x * rX + tMat.col2.x * rY; + rY = tMat.col1.y * rX + tMat.col2.y * rY; + rX = tX; + crug = rX * ugY - rY * ugX; + this.m_J.linearA.Set((-ugX), (-ugY)); + this.m_J.angularA = (-crug); + K += bA.m_invMass + bA.m_invI * crug * crug; + } + if (this.m_revolute2) { + this.m_J.angularB = (-this.m_ratio); + K += this.m_ratio * this.m_ratio * bB.m_invI; + } + else { + tMat = g2.m_xf.R; + tVec = this.m_prismatic2.m_localXAxis1; + ugX = tMat.col1.x * tVec.x + tMat.col2.x * tVec.y; + ugY = tMat.col1.y * tVec.x + tMat.col2.y * tVec.y; + tMat = bB.m_xf.R; + rX = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + rY = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = tMat.col1.x * rX + tMat.col2.x * rY; + rY = tMat.col1.y * rX + tMat.col2.y * rY; + rX = tX; + crug = rX * ugY - rY * ugX; + this.m_J.linearB.Set((-this.m_ratio * ugX), (-this.m_ratio * ugY)); + this.m_J.angularB = (-this.m_ratio * crug); + K += this.m_ratio * this.m_ratio * (bB.m_invMass + bB.m_invI * crug * crug); + } + this.m_mass = K > 0.0 ? 1.0 / K : 0.0; + if (step.warmStarting) { + bA.m_linearVelocity.x += bA.m_invMass * this.m_impulse * this.m_J.linearA.x; + bA.m_linearVelocity.y += bA.m_invMass * this.m_impulse * this.m_J.linearA.y; + bA.m_angularVelocity += bA.m_invI * this.m_impulse * this.m_J.angularA; + bB.m_linearVelocity.x += bB.m_invMass * this.m_impulse * this.m_J.linearB.x; + bB.m_linearVelocity.y += bB.m_invMass * this.m_impulse * this.m_J.linearB.y; + bB.m_angularVelocity += bB.m_invI * this.m_impulse * this.m_J.angularB; + } + else { + this.m_impulse = 0.0; + } + } + b2GearJoint.prototype.SolveVelocityConstraints = function (step) { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var Cdot = this.m_J.Compute(bA.m_linearVelocity, bA.m_angularVelocity, bB.m_linearVelocity, bB.m_angularVelocity); + var impulse = (-this.m_mass * Cdot); + this.m_impulse += impulse; + bA.m_linearVelocity.x += bA.m_invMass * impulse * this.m_J.linearA.x; + bA.m_linearVelocity.y += bA.m_invMass * impulse * this.m_J.linearA.y; + bA.m_angularVelocity += bA.m_invI * impulse * this.m_J.angularA; + bB.m_linearVelocity.x += bB.m_invMass * impulse * this.m_J.linearB.x; + bB.m_linearVelocity.y += bB.m_invMass * impulse * this.m_J.linearB.y; + bB.m_angularVelocity += bB.m_invI * impulse * this.m_J.angularB; + } + b2GearJoint.prototype.SolvePositionConstraints = function (baumgarte) { + if (baumgarte === undefined) baumgarte = 0; + var linearError = 0.0; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var coordinate1 = 0; + var coordinate2 = 0; + if (this.m_revolute1) { + coordinate1 = this.m_revolute1.GetJointAngle(); + } + else { + coordinate1 = this.m_prismatic1.GetJointTranslation(); + } + if (this.m_revolute2) { + coordinate2 = this.m_revolute2.GetJointAngle(); + } + else { + coordinate2 = this.m_prismatic2.GetJointTranslation(); + } + var C = this.m_constant - (coordinate1 + this.m_ratio * coordinate2); + var impulse = (-this.m_mass * C); + bA.m_sweep.c.x += bA.m_invMass * impulse * this.m_J.linearA.x; + bA.m_sweep.c.y += bA.m_invMass * impulse * this.m_J.linearA.y; + bA.m_sweep.a += bA.m_invI * impulse * this.m_J.angularA; + bB.m_sweep.c.x += bB.m_invMass * impulse * this.m_J.linearB.x; + bB.m_sweep.c.y += bB.m_invMass * impulse * this.m_J.linearB.y; + bB.m_sweep.a += bB.m_invI * impulse * this.m_J.angularB; + bA.SynchronizeTransform(); + bB.SynchronizeTransform(); + return linearError < b2Settings.b2_linearSlop; + } + Box2D.inherit(b2GearJointDef, Box2D.Dynamics.Joints.b2JointDef); + b2GearJointDef.prototype.__super = Box2D.Dynamics.Joints.b2JointDef.prototype; + b2GearJointDef.b2GearJointDef = function () { + Box2D.Dynamics.Joints.b2JointDef.b2JointDef.apply(this, arguments); + }; + b2GearJointDef.prototype.b2GearJointDef = function () { + this.__super.b2JointDef.call(this); + this.type = b2Joint.e_gearJoint; + this.joint1 = null; + this.joint2 = null; + this.ratio = 1.0; + } + b2Jacobian.b2Jacobian = function () { + this.linearA = new b2Vec2(); + this.linearB = new b2Vec2(); + }; + b2Jacobian.prototype.SetZero = function () { + this.linearA.SetZero(); + this.angularA = 0.0; + this.linearB.SetZero(); + this.angularB = 0.0; + } + b2Jacobian.prototype.Set = function (x1, a1, x2, a2) { + if (a1 === undefined) a1 = 0; + if (a2 === undefined) a2 = 0; + this.linearA.SetV(x1); + this.angularA = a1; + this.linearB.SetV(x2); + this.angularB = a2; + } + b2Jacobian.prototype.Compute = function (x1, a1, x2, a2) { + if (a1 === undefined) a1 = 0; + if (a2 === undefined) a2 = 0; + return (this.linearA.x * x1.x + this.linearA.y * x1.y) + this.angularA * a1 + (this.linearB.x * x2.x + this.linearB.y * x2.y) + this.angularB * a2; + } + b2Joint.b2Joint = function () { + this.m_edgeA = new b2JointEdge(); + this.m_edgeB = new b2JointEdge(); + this.m_localCenterA = new b2Vec2(); + this.m_localCenterB = new b2Vec2(); + }; + b2Joint.prototype.GetType = function () { + return this.m_type; + } + b2Joint.prototype.GetAnchorA = function () { + return null; + } + b2Joint.prototype.GetAnchorB = function () { + return null; + } + b2Joint.prototype.GetReactionForce = function (inv_dt) { + if (inv_dt === undefined) inv_dt = 0; + return null; + } + b2Joint.prototype.GetReactionTorque = function (inv_dt) { + if (inv_dt === undefined) inv_dt = 0; + return 0.0; + } + b2Joint.prototype.GetBodyA = function () { + return this.m_bodyA; + } + b2Joint.prototype.GetBodyB = function () { + return this.m_bodyB; + } + b2Joint.prototype.GetNext = function () { + return this.m_next; + } + b2Joint.prototype.GetUserData = function () { + return this.m_userData; + } + b2Joint.prototype.SetUserData = function (data) { + this.m_userData = data; + } + b2Joint.prototype.IsActive = function () { + return this.m_bodyA.IsActive() && this.m_bodyB.IsActive(); + } + b2Joint.Create = function (def, allocator) { + var joint = null; + switch (def.type) { + case b2Joint.e_distanceJoint: + { + joint = new b2DistanceJoint((def instanceof b2DistanceJointDef ? def : null)); + } + break; + case b2Joint.e_mouseJoint: + { + joint = new b2MouseJoint((def instanceof b2MouseJointDef ? def : null)); + } + break; + case b2Joint.e_prismaticJoint: + { + joint = new b2PrismaticJoint((def instanceof b2PrismaticJointDef ? def : null)); + } + break; + case b2Joint.e_revoluteJoint: + { + joint = new b2RevoluteJoint((def instanceof b2RevoluteJointDef ? def : null)); + } + break; + case b2Joint.e_pulleyJoint: + { + joint = new b2PulleyJoint((def instanceof b2PulleyJointDef ? def : null)); + } + break; + case b2Joint.e_gearJoint: + { + joint = new b2GearJoint((def instanceof b2GearJointDef ? def : null)); + } + break; + case b2Joint.e_lineJoint: + { + joint = new b2LineJoint((def instanceof b2LineJointDef ? def : null)); + } + break; + case b2Joint.e_weldJoint: + { + joint = new b2WeldJoint((def instanceof b2WeldJointDef ? def : null)); + } + break; + case b2Joint.e_frictionJoint: + { + joint = new b2FrictionJoint((def instanceof b2FrictionJointDef ? def : null)); + } + break; + default: + break; + } + return joint; + } + b2Joint.Destroy = function (joint, allocator) {} + b2Joint.prototype.b2Joint = function (def) { + b2Settings.b2Assert(def.bodyA != def.bodyB); + this.m_type = def.type; + this.m_prev = null; + this.m_next = null; + this.m_bodyA = def.bodyA; + this.m_bodyB = def.bodyB; + this.m_collideConnected = def.collideConnected; + this.m_islandFlag = false; + this.m_userData = def.userData; + } + b2Joint.prototype.InitVelocityConstraints = function (step) {} + b2Joint.prototype.SolveVelocityConstraints = function (step) {} + b2Joint.prototype.FinalizeVelocityConstraints = function () {} + b2Joint.prototype.SolvePositionConstraints = function (baumgarte) { + if (baumgarte === undefined) baumgarte = 0; + return false; + } + Box2D.postDefs.push(function () { + Box2D.Dynamics.Joints.b2Joint.e_unknownJoint = 0; + Box2D.Dynamics.Joints.b2Joint.e_revoluteJoint = 1; + Box2D.Dynamics.Joints.b2Joint.e_prismaticJoint = 2; + Box2D.Dynamics.Joints.b2Joint.e_distanceJoint = 3; + Box2D.Dynamics.Joints.b2Joint.e_pulleyJoint = 4; + Box2D.Dynamics.Joints.b2Joint.e_mouseJoint = 5; + Box2D.Dynamics.Joints.b2Joint.e_gearJoint = 6; + Box2D.Dynamics.Joints.b2Joint.e_lineJoint = 7; + Box2D.Dynamics.Joints.b2Joint.e_weldJoint = 8; + Box2D.Dynamics.Joints.b2Joint.e_frictionJoint = 9; + Box2D.Dynamics.Joints.b2Joint.e_inactiveLimit = 0; + Box2D.Dynamics.Joints.b2Joint.e_atLowerLimit = 1; + Box2D.Dynamics.Joints.b2Joint.e_atUpperLimit = 2; + Box2D.Dynamics.Joints.b2Joint.e_equalLimits = 3; + }); + b2JointDef.b2JointDef = function () {}; + b2JointDef.prototype.b2JointDef = function () { + this.type = b2Joint.e_unknownJoint; + this.userData = null; + this.bodyA = null; + this.bodyB = null; + this.collideConnected = false; + } + b2JointEdge.b2JointEdge = function () {}; + Box2D.inherit(b2LineJoint, Box2D.Dynamics.Joints.b2Joint); + b2LineJoint.prototype.__super = Box2D.Dynamics.Joints.b2Joint.prototype; + b2LineJoint.b2LineJoint = function () { + Box2D.Dynamics.Joints.b2Joint.b2Joint.apply(this, arguments); + this.m_localAnchor1 = new b2Vec2(); + this.m_localAnchor2 = new b2Vec2(); + this.m_localXAxis1 = new b2Vec2(); + this.m_localYAxis1 = new b2Vec2(); + this.m_axis = new b2Vec2(); + this.m_perp = new b2Vec2(); + this.m_K = new b2Mat22(); + this.m_impulse = new b2Vec2(); + }; + b2LineJoint.prototype.GetAnchorA = function () { + return this.m_bodyA.GetWorldPoint(this.m_localAnchor1); + } + b2LineJoint.prototype.GetAnchorB = function () { + return this.m_bodyB.GetWorldPoint(this.m_localAnchor2); + } + b2LineJoint.prototype.GetReactionForce = function (inv_dt) { + if (inv_dt === undefined) inv_dt = 0; + return new b2Vec2(inv_dt * (this.m_impulse.x * this.m_perp.x + (this.m_motorImpulse + this.m_impulse.y) * this.m_axis.x), inv_dt * (this.m_impulse.x * this.m_perp.y + (this.m_motorImpulse + this.m_impulse.y) * this.m_axis.y)); + } + b2LineJoint.prototype.GetReactionTorque = function (inv_dt) { + if (inv_dt === undefined) inv_dt = 0; + return inv_dt * this.m_impulse.y; + } + b2LineJoint.prototype.GetJointTranslation = function () { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var tMat; + var p1 = bA.GetWorldPoint(this.m_localAnchor1); + var p2 = bB.GetWorldPoint(this.m_localAnchor2); + var dX = p2.x - p1.x; + var dY = p2.y - p1.y; + var axis = bA.GetWorldVector(this.m_localXAxis1); + var translation = axis.x * dX + axis.y * dY; + return translation; + } + b2LineJoint.prototype.GetJointSpeed = function () { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var tMat; + tMat = bA.m_xf.R; + var r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + var r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + var tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + var r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + var r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var p1X = bA.m_sweep.c.x + r1X; + var p1Y = bA.m_sweep.c.y + r1Y; + var p2X = bB.m_sweep.c.x + r2X; + var p2Y = bB.m_sweep.c.y + r2Y; + var dX = p2X - p1X; + var dY = p2Y - p1Y; + var axis = bA.GetWorldVector(this.m_localXAxis1); + var v1 = bA.m_linearVelocity; + var v2 = bB.m_linearVelocity; + var w1 = bA.m_angularVelocity; + var w2 = bB.m_angularVelocity; + var speed = (dX * ((-w1 * axis.y)) + dY * (w1 * axis.x)) + (axis.x * (((v2.x + ((-w2 * r2Y))) - v1.x) - ((-w1 * r1Y))) + axis.y * (((v2.y + (w2 * r2X)) - v1.y) - (w1 * r1X))); + return speed; + } + b2LineJoint.prototype.IsLimitEnabled = function () { + return this.m_enableLimit; + } + b2LineJoint.prototype.EnableLimit = function (flag) { + this.m_bodyA.SetAwake(true); + this.m_bodyB.SetAwake(true); + this.m_enableLimit = flag; + } + b2LineJoint.prototype.GetLowerLimit = function () { + return this.m_lowerTranslation; + } + b2LineJoint.prototype.GetUpperLimit = function () { + return this.m_upperTranslation; + } + b2LineJoint.prototype.SetLimits = function (lower, upper) { + if (lower === undefined) lower = 0; + if (upper === undefined) upper = 0; + this.m_bodyA.SetAwake(true); + this.m_bodyB.SetAwake(true); + this.m_lowerTranslation = lower; + this.m_upperTranslation = upper; + } + b2LineJoint.prototype.IsMotorEnabled = function () { + return this.m_enableMotor; + } + b2LineJoint.prototype.EnableMotor = function (flag) { + this.m_bodyA.SetAwake(true); + this.m_bodyB.SetAwake(true); + this.m_enableMotor = flag; + } + b2LineJoint.prototype.SetMotorSpeed = function (speed) { + if (speed === undefined) speed = 0; + this.m_bodyA.SetAwake(true); + this.m_bodyB.SetAwake(true); + this.m_motorSpeed = speed; + } + b2LineJoint.prototype.GetMotorSpeed = function () { + return this.m_motorSpeed; + } + b2LineJoint.prototype.SetMaxMotorForce = function (force) { + if (force === undefined) force = 0; + this.m_bodyA.SetAwake(true); + this.m_bodyB.SetAwake(true); + this.m_maxMotorForce = force; + } + b2LineJoint.prototype.GetMaxMotorForce = function () { + return this.m_maxMotorForce; + } + b2LineJoint.prototype.GetMotorForce = function () { + return this.m_motorImpulse; + } + b2LineJoint.prototype.b2LineJoint = function (def) { + this.__super.b2Joint.call(this, def); + var tMat; + var tX = 0; + var tY = 0; + this.m_localAnchor1.SetV(def.localAnchorA); + this.m_localAnchor2.SetV(def.localAnchorB); + this.m_localXAxis1.SetV(def.localAxisA); + this.m_localYAxis1.x = (-this.m_localXAxis1.y); + this.m_localYAxis1.y = this.m_localXAxis1.x; + this.m_impulse.SetZero(); + this.m_motorMass = 0.0; + this.m_motorImpulse = 0.0; + this.m_lowerTranslation = def.lowerTranslation; + this.m_upperTranslation = def.upperTranslation; + this.m_maxMotorForce = def.maxMotorForce; + this.m_motorSpeed = def.motorSpeed; + this.m_enableLimit = def.enableLimit; + this.m_enableMotor = def.enableMotor; + this.m_limitState = b2Joint.e_inactiveLimit; + this.m_axis.SetZero(); + this.m_perp.SetZero(); + } + b2LineJoint.prototype.InitVelocityConstraints = function (step) { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var tMat; + var tX = 0; + this.m_localCenterA.SetV(bA.GetLocalCenter()); + this.m_localCenterB.SetV(bB.GetLocalCenter()); + var xf1 = bA.GetTransform(); + var xf2 = bB.GetTransform(); + tMat = bA.m_xf.R; + var r1X = this.m_localAnchor1.x - this.m_localCenterA.x; + var r1Y = this.m_localAnchor1.y - this.m_localCenterA.y; + tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + var r2X = this.m_localAnchor2.x - this.m_localCenterB.x; + var r2Y = this.m_localAnchor2.y - this.m_localCenterB.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var dX = bB.m_sweep.c.x + r2X - bA.m_sweep.c.x - r1X; + var dY = bB.m_sweep.c.y + r2Y - bA.m_sweep.c.y - r1Y; + this.m_invMassA = bA.m_invMass; + this.m_invMassB = bB.m_invMass; + this.m_invIA = bA.m_invI; + this.m_invIB = bB.m_invI; { + this.m_axis.SetV(b2Math.MulMV(xf1.R, this.m_localXAxis1)); + this.m_a1 = (dX + r1X) * this.m_axis.y - (dY + r1Y) * this.m_axis.x; + this.m_a2 = r2X * this.m_axis.y - r2Y * this.m_axis.x; + this.m_motorMass = this.m_invMassA + this.m_invMassB + this.m_invIA * this.m_a1 * this.m_a1 + this.m_invIB * this.m_a2 * this.m_a2; + this.m_motorMass = this.m_motorMass > Number.MIN_VALUE ? 1.0 / this.m_motorMass : 0.0; + } { + this.m_perp.SetV(b2Math.MulMV(xf1.R, this.m_localYAxis1)); + this.m_s1 = (dX + r1X) * this.m_perp.y - (dY + r1Y) * this.m_perp.x; + this.m_s2 = r2X * this.m_perp.y - r2Y * this.m_perp.x; + var m1 = this.m_invMassA; + var m2 = this.m_invMassB; + var i1 = this.m_invIA; + var i2 = this.m_invIB; + this.m_K.col1.x = m1 + m2 + i1 * this.m_s1 * this.m_s1 + i2 * this.m_s2 * this.m_s2; + this.m_K.col1.y = i1 * this.m_s1 * this.m_a1 + i2 * this.m_s2 * this.m_a2; + this.m_K.col2.x = this.m_K.col1.y; + this.m_K.col2.y = m1 + m2 + i1 * this.m_a1 * this.m_a1 + i2 * this.m_a2 * this.m_a2; + } + if (this.m_enableLimit) { + var jointTransition = this.m_axis.x * dX + this.m_axis.y * dY; + if (b2Math.Abs(this.m_upperTranslation - this.m_lowerTranslation) < 2.0 * b2Settings.b2_linearSlop) { + this.m_limitState = b2Joint.e_equalLimits; + } + else if (jointTransition <= this.m_lowerTranslation) { + if (this.m_limitState != b2Joint.e_atLowerLimit) { + this.m_limitState = b2Joint.e_atLowerLimit; + this.m_impulse.y = 0.0; + } + } + else if (jointTransition >= this.m_upperTranslation) { + if (this.m_limitState != b2Joint.e_atUpperLimit) { + this.m_limitState = b2Joint.e_atUpperLimit; + this.m_impulse.y = 0.0; + } + } + else { + this.m_limitState = b2Joint.e_inactiveLimit; + this.m_impulse.y = 0.0; + } + } + else { + this.m_limitState = b2Joint.e_inactiveLimit; + } + if (this.m_enableMotor == false) { + this.m_motorImpulse = 0.0; + } + if (step.warmStarting) { + this.m_impulse.x *= step.dtRatio; + this.m_impulse.y *= step.dtRatio; + this.m_motorImpulse *= step.dtRatio; + var PX = this.m_impulse.x * this.m_perp.x + (this.m_motorImpulse + this.m_impulse.y) * this.m_axis.x; + var PY = this.m_impulse.x * this.m_perp.y + (this.m_motorImpulse + this.m_impulse.y) * this.m_axis.y; + var L1 = this.m_impulse.x * this.m_s1 + (this.m_motorImpulse + this.m_impulse.y) * this.m_a1; + var L2 = this.m_impulse.x * this.m_s2 + (this.m_motorImpulse + this.m_impulse.y) * this.m_a2; + bA.m_linearVelocity.x -= this.m_invMassA * PX; + bA.m_linearVelocity.y -= this.m_invMassA * PY; + bA.m_angularVelocity -= this.m_invIA * L1; + bB.m_linearVelocity.x += this.m_invMassB * PX; + bB.m_linearVelocity.y += this.m_invMassB * PY; + bB.m_angularVelocity += this.m_invIB * L2; + } + else { + this.m_impulse.SetZero(); + this.m_motorImpulse = 0.0; + } + } + b2LineJoint.prototype.SolveVelocityConstraints = function (step) { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var v1 = bA.m_linearVelocity; + var w1 = bA.m_angularVelocity; + var v2 = bB.m_linearVelocity; + var w2 = bB.m_angularVelocity; + var PX = 0; + var PY = 0; + var L1 = 0; + var L2 = 0; + if (this.m_enableMotor && this.m_limitState != b2Joint.e_equalLimits) { + var Cdot = this.m_axis.x * (v2.x - v1.x) + this.m_axis.y * (v2.y - v1.y) + this.m_a2 * w2 - this.m_a1 * w1; + var impulse = this.m_motorMass * (this.m_motorSpeed - Cdot); + var oldImpulse = this.m_motorImpulse; + var maxImpulse = step.dt * this.m_maxMotorForce; + this.m_motorImpulse = b2Math.Clamp(this.m_motorImpulse + impulse, (-maxImpulse), maxImpulse); + impulse = this.m_motorImpulse - oldImpulse; + PX = impulse * this.m_axis.x; + PY = impulse * this.m_axis.y; + L1 = impulse * this.m_a1; + L2 = impulse * this.m_a2; + v1.x -= this.m_invMassA * PX; + v1.y -= this.m_invMassA * PY; + w1 -= this.m_invIA * L1; + v2.x += this.m_invMassB * PX; + v2.y += this.m_invMassB * PY; + w2 += this.m_invIB * L2; + } + var Cdot1 = this.m_perp.x * (v2.x - v1.x) + this.m_perp.y * (v2.y - v1.y) + this.m_s2 * w2 - this.m_s1 * w1; + if (this.m_enableLimit && this.m_limitState != b2Joint.e_inactiveLimit) { + var Cdot2 = this.m_axis.x * (v2.x - v1.x) + this.m_axis.y * (v2.y - v1.y) + this.m_a2 * w2 - this.m_a1 * w1; + var f1 = this.m_impulse.Copy(); + var df = this.m_K.Solve(new b2Vec2(), (-Cdot1), (-Cdot2)); + this.m_impulse.Add(df); + if (this.m_limitState == b2Joint.e_atLowerLimit) { + this.m_impulse.y = b2Math.Max(this.m_impulse.y, 0.0); + } + else if (this.m_limitState == b2Joint.e_atUpperLimit) { + this.m_impulse.y = b2Math.Min(this.m_impulse.y, 0.0); + } + var b = (-Cdot1) - (this.m_impulse.y - f1.y) * this.m_K.col2.x; + var f2r = 0; + if (this.m_K.col1.x != 0.0) { + f2r = b / this.m_K.col1.x + f1.x; + } + else { + f2r = f1.x; + } + this.m_impulse.x = f2r; + df.x = this.m_impulse.x - f1.x; + df.y = this.m_impulse.y - f1.y; + PX = df.x * this.m_perp.x + df.y * this.m_axis.x; + PY = df.x * this.m_perp.y + df.y * this.m_axis.y; + L1 = df.x * this.m_s1 + df.y * this.m_a1; + L2 = df.x * this.m_s2 + df.y * this.m_a2; + v1.x -= this.m_invMassA * PX; + v1.y -= this.m_invMassA * PY; + w1 -= this.m_invIA * L1; + v2.x += this.m_invMassB * PX; + v2.y += this.m_invMassB * PY; + w2 += this.m_invIB * L2; + } + else { + var df2 = 0; + if (this.m_K.col1.x != 0.0) { + df2 = ((-Cdot1)) / this.m_K.col1.x; + } + else { + df2 = 0.0; + } + this.m_impulse.x += df2; + PX = df2 * this.m_perp.x; + PY = df2 * this.m_perp.y; + L1 = df2 * this.m_s1; + L2 = df2 * this.m_s2; + v1.x -= this.m_invMassA * PX; + v1.y -= this.m_invMassA * PY; + w1 -= this.m_invIA * L1; + v2.x += this.m_invMassB * PX; + v2.y += this.m_invMassB * PY; + w2 += this.m_invIB * L2; + } + bA.m_linearVelocity.SetV(v1); + bA.m_angularVelocity = w1; + bB.m_linearVelocity.SetV(v2); + bB.m_angularVelocity = w2; + } + b2LineJoint.prototype.SolvePositionConstraints = function (baumgarte) { + if (baumgarte === undefined) baumgarte = 0; + var limitC = 0; + var oldLimitImpulse = 0; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var c1 = bA.m_sweep.c; + var a1 = bA.m_sweep.a; + var c2 = bB.m_sweep.c; + var a2 = bB.m_sweep.a; + var tMat; + var tX = 0; + var m1 = 0; + var m2 = 0; + var i1 = 0; + var i2 = 0; + var linearError = 0.0; + var angularError = 0.0; + var active = false; + var C2 = 0.0; + var R1 = b2Mat22.FromAngle(a1); + var R2 = b2Mat22.FromAngle(a2); + tMat = R1; + var r1X = this.m_localAnchor1.x - this.m_localCenterA.x; + var r1Y = this.m_localAnchor1.y - this.m_localCenterA.y; + tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = R2; + var r2X = this.m_localAnchor2.x - this.m_localCenterB.x; + var r2Y = this.m_localAnchor2.y - this.m_localCenterB.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var dX = c2.x + r2X - c1.x - r1X; + var dY = c2.y + r2Y - c1.y - r1Y; + if (this.m_enableLimit) { + this.m_axis = b2Math.MulMV(R1, this.m_localXAxis1); + this.m_a1 = (dX + r1X) * this.m_axis.y - (dY + r1Y) * this.m_axis.x; + this.m_a2 = r2X * this.m_axis.y - r2Y * this.m_axis.x; + var translation = this.m_axis.x * dX + this.m_axis.y * dY; + if (b2Math.Abs(this.m_upperTranslation - this.m_lowerTranslation) < 2.0 * b2Settings.b2_linearSlop) { + C2 = b2Math.Clamp(translation, (-b2Settings.b2_maxLinearCorrection), b2Settings.b2_maxLinearCorrection); + linearError = b2Math.Abs(translation); + active = true; + } + else if (translation <= this.m_lowerTranslation) { + C2 = b2Math.Clamp(translation - this.m_lowerTranslation + b2Settings.b2_linearSlop, (-b2Settings.b2_maxLinearCorrection), 0.0); + linearError = this.m_lowerTranslation - translation; + active = true; + } + else if (translation >= this.m_upperTranslation) { + C2 = b2Math.Clamp(translation - this.m_upperTranslation + b2Settings.b2_linearSlop, 0.0, b2Settings.b2_maxLinearCorrection); + linearError = translation - this.m_upperTranslation; + active = true; + } + } + this.m_perp = b2Math.MulMV(R1, this.m_localYAxis1); + this.m_s1 = (dX + r1X) * this.m_perp.y - (dY + r1Y) * this.m_perp.x; + this.m_s2 = r2X * this.m_perp.y - r2Y * this.m_perp.x; + var impulse = new b2Vec2(); + var C1 = this.m_perp.x * dX + this.m_perp.y * dY; + linearError = b2Math.Max(linearError, b2Math.Abs(C1)); + angularError = 0.0; + if (active) { + m1 = this.m_invMassA; + m2 = this.m_invMassB; + i1 = this.m_invIA; + i2 = this.m_invIB; + this.m_K.col1.x = m1 + m2 + i1 * this.m_s1 * this.m_s1 + i2 * this.m_s2 * this.m_s2; + this.m_K.col1.y = i1 * this.m_s1 * this.m_a1 + i2 * this.m_s2 * this.m_a2; + this.m_K.col2.x = this.m_K.col1.y; + this.m_K.col2.y = m1 + m2 + i1 * this.m_a1 * this.m_a1 + i2 * this.m_a2 * this.m_a2; + this.m_K.Solve(impulse, (-C1), (-C2)); + } + else { + m1 = this.m_invMassA; + m2 = this.m_invMassB; + i1 = this.m_invIA; + i2 = this.m_invIB; + var k11 = m1 + m2 + i1 * this.m_s1 * this.m_s1 + i2 * this.m_s2 * this.m_s2; + var impulse1 = 0; + if (k11 != 0.0) { + impulse1 = ((-C1)) / k11; + } + else { + impulse1 = 0.0; + } + impulse.x = impulse1; + impulse.y = 0.0; + } + var PX = impulse.x * this.m_perp.x + impulse.y * this.m_axis.x; + var PY = impulse.x * this.m_perp.y + impulse.y * this.m_axis.y; + var L1 = impulse.x * this.m_s1 + impulse.y * this.m_a1; + var L2 = impulse.x * this.m_s2 + impulse.y * this.m_a2; + c1.x -= this.m_invMassA * PX; + c1.y -= this.m_invMassA * PY; + a1 -= this.m_invIA * L1; + c2.x += this.m_invMassB * PX; + c2.y += this.m_invMassB * PY; + a2 += this.m_invIB * L2; + bA.m_sweep.a = a1; + bB.m_sweep.a = a2; + bA.SynchronizeTransform(); + bB.SynchronizeTransform(); + return linearError <= b2Settings.b2_linearSlop && angularError <= b2Settings.b2_angularSlop; + } + Box2D.inherit(b2LineJointDef, Box2D.Dynamics.Joints.b2JointDef); + b2LineJointDef.prototype.__super = Box2D.Dynamics.Joints.b2JointDef.prototype; + b2LineJointDef.b2LineJointDef = function () { + Box2D.Dynamics.Joints.b2JointDef.b2JointDef.apply(this, arguments); + this.localAnchorA = new b2Vec2(); + this.localAnchorB = new b2Vec2(); + this.localAxisA = new b2Vec2(); + }; + b2LineJointDef.prototype.b2LineJointDef = function () { + this.__super.b2JointDef.call(this); + this.type = b2Joint.e_lineJoint; + this.localAxisA.Set(1.0, 0.0); + this.enableLimit = false; + this.lowerTranslation = 0.0; + this.upperTranslation = 0.0; + this.enableMotor = false; + this.maxMotorForce = 0.0; + this.motorSpeed = 0.0; + } + b2LineJointDef.prototype.Initialize = function (bA, bB, anchor, axis) { + this.bodyA = bA; + this.bodyB = bB; + this.localAnchorA = this.bodyA.GetLocalPoint(anchor); + this.localAnchorB = this.bodyB.GetLocalPoint(anchor); + this.localAxisA = this.bodyA.GetLocalVector(axis); + } + Box2D.inherit(b2MouseJoint, Box2D.Dynamics.Joints.b2Joint); + b2MouseJoint.prototype.__super = Box2D.Dynamics.Joints.b2Joint.prototype; + b2MouseJoint.b2MouseJoint = function () { + Box2D.Dynamics.Joints.b2Joint.b2Joint.apply(this, arguments); + this.K = new b2Mat22(); + this.K1 = new b2Mat22(); + this.K2 = new b2Mat22(); + this.m_localAnchor = new b2Vec2(); + this.m_target = new b2Vec2(); + this.m_impulse = new b2Vec2(); + this.m_mass = new b2Mat22(); + this.m_C = new b2Vec2(); + }; + b2MouseJoint.prototype.GetAnchorA = function () { + return this.m_target; + } + b2MouseJoint.prototype.GetAnchorB = function () { + return this.m_bodyB.GetWorldPoint(this.m_localAnchor); + } + b2MouseJoint.prototype.GetReactionForce = function (inv_dt) { + if (inv_dt === undefined) inv_dt = 0; + return new b2Vec2(inv_dt * this.m_impulse.x, inv_dt * this.m_impulse.y); + } + b2MouseJoint.prototype.GetReactionTorque = function (inv_dt) { + if (inv_dt === undefined) inv_dt = 0; + return 0.0; + } + b2MouseJoint.prototype.GetTarget = function () { + return this.m_target; + } + b2MouseJoint.prototype.SetTarget = function (target) { + if (this.m_bodyB.IsAwake() == false) { + this.m_bodyB.SetAwake(true); + } + this.m_target = target; + } + b2MouseJoint.prototype.GetMaxForce = function () { + return this.m_maxForce; + } + b2MouseJoint.prototype.SetMaxForce = function (maxForce) { + if (maxForce === undefined) maxForce = 0; + this.m_maxForce = maxForce; + } + b2MouseJoint.prototype.GetFrequency = function () { + return this.m_frequencyHz; + } + b2MouseJoint.prototype.SetFrequency = function (hz) { + if (hz === undefined) hz = 0; + this.m_frequencyHz = hz; + } + b2MouseJoint.prototype.GetDampingRatio = function () { + return this.m_dampingRatio; + } + b2MouseJoint.prototype.SetDampingRatio = function (ratio) { + if (ratio === undefined) ratio = 0; + this.m_dampingRatio = ratio; + } + b2MouseJoint.prototype.b2MouseJoint = function (def) { + this.__super.b2Joint.call(this, def); + this.m_target.SetV(def.target); + var tX = this.m_target.x - this.m_bodyB.m_xf.position.x; + var tY = this.m_target.y - this.m_bodyB.m_xf.position.y; + var tMat = this.m_bodyB.m_xf.R; + this.m_localAnchor.x = (tX * tMat.col1.x + tY * tMat.col1.y); + this.m_localAnchor.y = (tX * tMat.col2.x + tY * tMat.col2.y); + this.m_maxForce = def.maxForce; + this.m_impulse.SetZero(); + this.m_frequencyHz = def.frequencyHz; + this.m_dampingRatio = def.dampingRatio; + this.m_beta = 0.0; + this.m_gamma = 0.0; + } + b2MouseJoint.prototype.InitVelocityConstraints = function (step) { + var b = this.m_bodyB; + var mass = b.GetMass(); + var omega = 2.0 * Math.PI * this.m_frequencyHz; + var d = 2.0 * mass * this.m_dampingRatio * omega; + var k = mass * omega * omega; + this.m_gamma = step.dt * (d + step.dt * k); + this.m_gamma = this.m_gamma != 0 ? 1 / this.m_gamma : 0.0; + this.m_beta = step.dt * k * this.m_gamma; + var tMat;tMat = b.m_xf.R; + var rX = this.m_localAnchor.x - b.m_sweep.localCenter.x; + var rY = this.m_localAnchor.y - b.m_sweep.localCenter.y; + var tX = (tMat.col1.x * rX + tMat.col2.x * rY);rY = (tMat.col1.y * rX + tMat.col2.y * rY); + rX = tX; + var invMass = b.m_invMass; + var invI = b.m_invI;this.K1.col1.x = invMass; + this.K1.col2.x = 0.0; + this.K1.col1.y = 0.0; + this.K1.col2.y = invMass; + this.K2.col1.x = invI * rY * rY; + this.K2.col2.x = (-invI * rX * rY); + this.K2.col1.y = (-invI * rX * rY); + this.K2.col2.y = invI * rX * rX; + this.K.SetM(this.K1); + this.K.AddM(this.K2); + this.K.col1.x += this.m_gamma; + this.K.col2.y += this.m_gamma; + this.K.GetInverse(this.m_mass); + this.m_C.x = b.m_sweep.c.x + rX - this.m_target.x; + this.m_C.y = b.m_sweep.c.y + rY - this.m_target.y; + b.m_angularVelocity *= 0.98; + this.m_impulse.x *= step.dtRatio; + this.m_impulse.y *= step.dtRatio; + b.m_linearVelocity.x += invMass * this.m_impulse.x; + b.m_linearVelocity.y += invMass * this.m_impulse.y; + b.m_angularVelocity += invI * (rX * this.m_impulse.y - rY * this.m_impulse.x); + } + b2MouseJoint.prototype.SolveVelocityConstraints = function (step) { + var b = this.m_bodyB; + var tMat; + var tX = 0; + var tY = 0; + tMat = b.m_xf.R; + var rX = this.m_localAnchor.x - b.m_sweep.localCenter.x; + var rY = this.m_localAnchor.y - b.m_sweep.localCenter.y; + tX = (tMat.col1.x * rX + tMat.col2.x * rY); + rY = (tMat.col1.y * rX + tMat.col2.y * rY); + rX = tX; + var CdotX = b.m_linearVelocity.x + ((-b.m_angularVelocity * rY)); + var CdotY = b.m_linearVelocity.y + (b.m_angularVelocity * rX); + tMat = this.m_mass; + tX = CdotX + this.m_beta * this.m_C.x + this.m_gamma * this.m_impulse.x; + tY = CdotY + this.m_beta * this.m_C.y + this.m_gamma * this.m_impulse.y; + var impulseX = (-(tMat.col1.x * tX + tMat.col2.x * tY)); + var impulseY = (-(tMat.col1.y * tX + tMat.col2.y * tY)); + var oldImpulseX = this.m_impulse.x; + var oldImpulseY = this.m_impulse.y; + this.m_impulse.x += impulseX; + this.m_impulse.y += impulseY; + var maxImpulse = step.dt * this.m_maxForce; + if (this.m_impulse.LengthSquared() > maxImpulse * maxImpulse) { + this.m_impulse.Multiply(maxImpulse / this.m_impulse.Length()); + } + impulseX = this.m_impulse.x - oldImpulseX; + impulseY = this.m_impulse.y - oldImpulseY; + b.m_linearVelocity.x += b.m_invMass * impulseX; + b.m_linearVelocity.y += b.m_invMass * impulseY; + b.m_angularVelocity += b.m_invI * (rX * impulseY - rY * impulseX); + } + b2MouseJoint.prototype.SolvePositionConstraints = function (baumgarte) { + if (baumgarte === undefined) baumgarte = 0; + return true; + } + Box2D.inherit(b2MouseJointDef, Box2D.Dynamics.Joints.b2JointDef); + b2MouseJointDef.prototype.__super = Box2D.Dynamics.Joints.b2JointDef.prototype; + b2MouseJointDef.b2MouseJointDef = function () { + Box2D.Dynamics.Joints.b2JointDef.b2JointDef.apply(this, arguments); + this.target = new b2Vec2(); + }; + b2MouseJointDef.prototype.b2MouseJointDef = function () { + this.__super.b2JointDef.call(this); + this.type = b2Joint.e_mouseJoint; + this.maxForce = 0.0; + this.frequencyHz = 5.0; + this.dampingRatio = 0.7; + } + Box2D.inherit(b2PrismaticJoint, Box2D.Dynamics.Joints.b2Joint); + b2PrismaticJoint.prototype.__super = Box2D.Dynamics.Joints.b2Joint.prototype; + b2PrismaticJoint.b2PrismaticJoint = function () { + Box2D.Dynamics.Joints.b2Joint.b2Joint.apply(this, arguments); + this.m_localAnchor1 = new b2Vec2(); + this.m_localAnchor2 = new b2Vec2(); + this.m_localXAxis1 = new b2Vec2(); + this.m_localYAxis1 = new b2Vec2(); + this.m_axis = new b2Vec2(); + this.m_perp = new b2Vec2(); + this.m_K = new b2Mat33(); + this.m_impulse = new b2Vec3(); + }; + b2PrismaticJoint.prototype.GetAnchorA = function () { + return this.m_bodyA.GetWorldPoint(this.m_localAnchor1); + } + b2PrismaticJoint.prototype.GetAnchorB = function () { + return this.m_bodyB.GetWorldPoint(this.m_localAnchor2); + } + b2PrismaticJoint.prototype.GetReactionForce = function (inv_dt) { + if (inv_dt === undefined) inv_dt = 0; + return new b2Vec2(inv_dt * (this.m_impulse.x * this.m_perp.x + (this.m_motorImpulse + this.m_impulse.z) * this.m_axis.x), inv_dt * (this.m_impulse.x * this.m_perp.y + (this.m_motorImpulse + this.m_impulse.z) * this.m_axis.y)); + } + b2PrismaticJoint.prototype.GetReactionTorque = function (inv_dt) { + if (inv_dt === undefined) inv_dt = 0; + return inv_dt * this.m_impulse.y; + } + b2PrismaticJoint.prototype.GetJointTranslation = function () { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var tMat; + var p1 = bA.GetWorldPoint(this.m_localAnchor1); + var p2 = bB.GetWorldPoint(this.m_localAnchor2); + var dX = p2.x - p1.x; + var dY = p2.y - p1.y; + var axis = bA.GetWorldVector(this.m_localXAxis1); + var translation = axis.x * dX + axis.y * dY; + return translation; + } + b2PrismaticJoint.prototype.GetJointSpeed = function () { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var tMat; + tMat = bA.m_xf.R; + var r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + var r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + var tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + var r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + var r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var p1X = bA.m_sweep.c.x + r1X; + var p1Y = bA.m_sweep.c.y + r1Y; + var p2X = bB.m_sweep.c.x + r2X; + var p2Y = bB.m_sweep.c.y + r2Y; + var dX = p2X - p1X; + var dY = p2Y - p1Y; + var axis = bA.GetWorldVector(this.m_localXAxis1); + var v1 = bA.m_linearVelocity; + var v2 = bB.m_linearVelocity; + var w1 = bA.m_angularVelocity; + var w2 = bB.m_angularVelocity; + var speed = (dX * ((-w1 * axis.y)) + dY * (w1 * axis.x)) + (axis.x * (((v2.x + ((-w2 * r2Y))) - v1.x) - ((-w1 * r1Y))) + axis.y * (((v2.y + (w2 * r2X)) - v1.y) - (w1 * r1X))); + return speed; + } + b2PrismaticJoint.prototype.IsLimitEnabled = function () { + return this.m_enableLimit; + } + b2PrismaticJoint.prototype.EnableLimit = function (flag) { + this.m_bodyA.SetAwake(true); + this.m_bodyB.SetAwake(true); + this.m_enableLimit = flag; + } + b2PrismaticJoint.prototype.GetLowerLimit = function () { + return this.m_lowerTranslation; + } + b2PrismaticJoint.prototype.GetUpperLimit = function () { + return this.m_upperTranslation; + } + b2PrismaticJoint.prototype.SetLimits = function (lower, upper) { + if (lower === undefined) lower = 0; + if (upper === undefined) upper = 0; + this.m_bodyA.SetAwake(true); + this.m_bodyB.SetAwake(true); + this.m_lowerTranslation = lower; + this.m_upperTranslation = upper; + } + b2PrismaticJoint.prototype.IsMotorEnabled = function () { + return this.m_enableMotor; + } + b2PrismaticJoint.prototype.EnableMotor = function (flag) { + this.m_bodyA.SetAwake(true); + this.m_bodyB.SetAwake(true); + this.m_enableMotor = flag; + } + b2PrismaticJoint.prototype.SetMotorSpeed = function (speed) { + if (speed === undefined) speed = 0; + this.m_bodyA.SetAwake(true); + this.m_bodyB.SetAwake(true); + this.m_motorSpeed = speed; + } + b2PrismaticJoint.prototype.GetMotorSpeed = function () { + return this.m_motorSpeed; + } + b2PrismaticJoint.prototype.SetMaxMotorForce = function (force) { + if (force === undefined) force = 0; + this.m_bodyA.SetAwake(true); + this.m_bodyB.SetAwake(true); + this.m_maxMotorForce = force; + } + b2PrismaticJoint.prototype.GetMotorForce = function () { + return this.m_motorImpulse; + } + b2PrismaticJoint.prototype.b2PrismaticJoint = function (def) { + this.__super.b2Joint.call(this, def); + var tMat; + var tX = 0; + var tY = 0; + this.m_localAnchor1.SetV(def.localAnchorA); + this.m_localAnchor2.SetV(def.localAnchorB); + this.m_localXAxis1.SetV(def.localAxisA); + this.m_localYAxis1.x = (-this.m_localXAxis1.y); + this.m_localYAxis1.y = this.m_localXAxis1.x; + this.m_refAngle = def.referenceAngle; + this.m_impulse.SetZero(); + this.m_motorMass = 0.0; + this.m_motorImpulse = 0.0; + this.m_lowerTranslation = def.lowerTranslation; + this.m_upperTranslation = def.upperTranslation; + this.m_maxMotorForce = def.maxMotorForce; + this.m_motorSpeed = def.motorSpeed; + this.m_enableLimit = def.enableLimit; + this.m_enableMotor = def.enableMotor; + this.m_limitState = b2Joint.e_inactiveLimit; + this.m_axis.SetZero(); + this.m_perp.SetZero(); + } + b2PrismaticJoint.prototype.InitVelocityConstraints = function (step) { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var tMat; + var tX = 0; + this.m_localCenterA.SetV(bA.GetLocalCenter()); + this.m_localCenterB.SetV(bB.GetLocalCenter()); + var xf1 = bA.GetTransform(); + var xf2 = bB.GetTransform(); + tMat = bA.m_xf.R; + var r1X = this.m_localAnchor1.x - this.m_localCenterA.x; + var r1Y = this.m_localAnchor1.y - this.m_localCenterA.y; + tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + var r2X = this.m_localAnchor2.x - this.m_localCenterB.x; + var r2Y = this.m_localAnchor2.y - this.m_localCenterB.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var dX = bB.m_sweep.c.x + r2X - bA.m_sweep.c.x - r1X; + var dY = bB.m_sweep.c.y + r2Y - bA.m_sweep.c.y - r1Y; + this.m_invMassA = bA.m_invMass; + this.m_invMassB = bB.m_invMass; + this.m_invIA = bA.m_invI; + this.m_invIB = bB.m_invI; { + this.m_axis.SetV(b2Math.MulMV(xf1.R, this.m_localXAxis1)); + this.m_a1 = (dX + r1X) * this.m_axis.y - (dY + r1Y) * this.m_axis.x; + this.m_a2 = r2X * this.m_axis.y - r2Y * this.m_axis.x; + this.m_motorMass = this.m_invMassA + this.m_invMassB + this.m_invIA * this.m_a1 * this.m_a1 + this.m_invIB * this.m_a2 * this.m_a2; + if (this.m_motorMass > Number.MIN_VALUE) this.m_motorMass = 1.0 / this.m_motorMass; + } { + this.m_perp.SetV(b2Math.MulMV(xf1.R, this.m_localYAxis1)); + this.m_s1 = (dX + r1X) * this.m_perp.y - (dY + r1Y) * this.m_perp.x; + this.m_s2 = r2X * this.m_perp.y - r2Y * this.m_perp.x; + var m1 = this.m_invMassA; + var m2 = this.m_invMassB; + var i1 = this.m_invIA; + var i2 = this.m_invIB; + this.m_K.col1.x = m1 + m2 + i1 * this.m_s1 * this.m_s1 + i2 * this.m_s2 * this.m_s2; + this.m_K.col1.y = i1 * this.m_s1 + i2 * this.m_s2; + this.m_K.col1.z = i1 * this.m_s1 * this.m_a1 + i2 * this.m_s2 * this.m_a2; + this.m_K.col2.x = this.m_K.col1.y; + this.m_K.col2.y = i1 + i2; + this.m_K.col2.z = i1 * this.m_a1 + i2 * this.m_a2; + this.m_K.col3.x = this.m_K.col1.z; + this.m_K.col3.y = this.m_K.col2.z; + this.m_K.col3.z = m1 + m2 + i1 * this.m_a1 * this.m_a1 + i2 * this.m_a2 * this.m_a2; + } + if (this.m_enableLimit) { + var jointTransition = this.m_axis.x * dX + this.m_axis.y * dY; + if (b2Math.Abs(this.m_upperTranslation - this.m_lowerTranslation) < 2.0 * b2Settings.b2_linearSlop) { + this.m_limitState = b2Joint.e_equalLimits; + } + else if (jointTransition <= this.m_lowerTranslation) { + if (this.m_limitState != b2Joint.e_atLowerLimit) { + this.m_limitState = b2Joint.e_atLowerLimit; + this.m_impulse.z = 0.0; + } + } + else if (jointTransition >= this.m_upperTranslation) { + if (this.m_limitState != b2Joint.e_atUpperLimit) { + this.m_limitState = b2Joint.e_atUpperLimit; + this.m_impulse.z = 0.0; + } + } + else { + this.m_limitState = b2Joint.e_inactiveLimit; + this.m_impulse.z = 0.0; + } + } + else { + this.m_limitState = b2Joint.e_inactiveLimit; + } + if (this.m_enableMotor == false) { + this.m_motorImpulse = 0.0; + } + if (step.warmStarting) { + this.m_impulse.x *= step.dtRatio; + this.m_impulse.y *= step.dtRatio; + this.m_motorImpulse *= step.dtRatio; + var PX = this.m_impulse.x * this.m_perp.x + (this.m_motorImpulse + this.m_impulse.z) * this.m_axis.x; + var PY = this.m_impulse.x * this.m_perp.y + (this.m_motorImpulse + this.m_impulse.z) * this.m_axis.y; + var L1 = this.m_impulse.x * this.m_s1 + this.m_impulse.y + (this.m_motorImpulse + this.m_impulse.z) * this.m_a1; + var L2 = this.m_impulse.x * this.m_s2 + this.m_impulse.y + (this.m_motorImpulse + this.m_impulse.z) * this.m_a2; + bA.m_linearVelocity.x -= this.m_invMassA * PX; + bA.m_linearVelocity.y -= this.m_invMassA * PY; + bA.m_angularVelocity -= this.m_invIA * L1; + bB.m_linearVelocity.x += this.m_invMassB * PX; + bB.m_linearVelocity.y += this.m_invMassB * PY; + bB.m_angularVelocity += this.m_invIB * L2; + } + else { + this.m_impulse.SetZero(); + this.m_motorImpulse = 0.0; + } + } + b2PrismaticJoint.prototype.SolveVelocityConstraints = function (step) { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var v1 = bA.m_linearVelocity; + var w1 = bA.m_angularVelocity; + var v2 = bB.m_linearVelocity; + var w2 = bB.m_angularVelocity; + var PX = 0; + var PY = 0; + var L1 = 0; + var L2 = 0; + if (this.m_enableMotor && this.m_limitState != b2Joint.e_equalLimits) { + var Cdot = this.m_axis.x * (v2.x - v1.x) + this.m_axis.y * (v2.y - v1.y) + this.m_a2 * w2 - this.m_a1 * w1; + var impulse = this.m_motorMass * (this.m_motorSpeed - Cdot); + var oldImpulse = this.m_motorImpulse; + var maxImpulse = step.dt * this.m_maxMotorForce; + this.m_motorImpulse = b2Math.Clamp(this.m_motorImpulse + impulse, (-maxImpulse), maxImpulse); + impulse = this.m_motorImpulse - oldImpulse; + PX = impulse * this.m_axis.x; + PY = impulse * this.m_axis.y; + L1 = impulse * this.m_a1; + L2 = impulse * this.m_a2; + v1.x -= this.m_invMassA * PX; + v1.y -= this.m_invMassA * PY; + w1 -= this.m_invIA * L1; + v2.x += this.m_invMassB * PX; + v2.y += this.m_invMassB * PY; + w2 += this.m_invIB * L2; + } + var Cdot1X = this.m_perp.x * (v2.x - v1.x) + this.m_perp.y * (v2.y - v1.y) + this.m_s2 * w2 - this.m_s1 * w1; + var Cdot1Y = w2 - w1; + if (this.m_enableLimit && this.m_limitState != b2Joint.e_inactiveLimit) { + var Cdot2 = this.m_axis.x * (v2.x - v1.x) + this.m_axis.y * (v2.y - v1.y) + this.m_a2 * w2 - this.m_a1 * w1; + var f1 = this.m_impulse.Copy(); + var df = this.m_K.Solve33(new b2Vec3(), (-Cdot1X), (-Cdot1Y), (-Cdot2)); + this.m_impulse.Add(df); + if (this.m_limitState == b2Joint.e_atLowerLimit) { + this.m_impulse.z = b2Math.Max(this.m_impulse.z, 0.0); + } + else if (this.m_limitState == b2Joint.e_atUpperLimit) { + this.m_impulse.z = b2Math.Min(this.m_impulse.z, 0.0); + } + var bX = (-Cdot1X) - (this.m_impulse.z - f1.z) * this.m_K.col3.x; + var bY = (-Cdot1Y) - (this.m_impulse.z - f1.z) * this.m_K.col3.y; + var f2r = this.m_K.Solve22(new b2Vec2(), bX, bY); + f2r.x += f1.x; + f2r.y += f1.y; + this.m_impulse.x = f2r.x; + this.m_impulse.y = f2r.y; + df.x = this.m_impulse.x - f1.x; + df.y = this.m_impulse.y - f1.y; + df.z = this.m_impulse.z - f1.z; + PX = df.x * this.m_perp.x + df.z * this.m_axis.x; + PY = df.x * this.m_perp.y + df.z * this.m_axis.y; + L1 = df.x * this.m_s1 + df.y + df.z * this.m_a1; + L2 = df.x * this.m_s2 + df.y + df.z * this.m_a2; + v1.x -= this.m_invMassA * PX; + v1.y -= this.m_invMassA * PY; + w1 -= this.m_invIA * L1; + v2.x += this.m_invMassB * PX; + v2.y += this.m_invMassB * PY; + w2 += this.m_invIB * L2; + } + else { + var df2 = this.m_K.Solve22(new b2Vec2(), (-Cdot1X), (-Cdot1Y)); + this.m_impulse.x += df2.x; + this.m_impulse.y += df2.y; + PX = df2.x * this.m_perp.x; + PY = df2.x * this.m_perp.y; + L1 = df2.x * this.m_s1 + df2.y; + L2 = df2.x * this.m_s2 + df2.y; + v1.x -= this.m_invMassA * PX; + v1.y -= this.m_invMassA * PY; + w1 -= this.m_invIA * L1; + v2.x += this.m_invMassB * PX; + v2.y += this.m_invMassB * PY; + w2 += this.m_invIB * L2; + } + bA.m_linearVelocity.SetV(v1); + bA.m_angularVelocity = w1; + bB.m_linearVelocity.SetV(v2); + bB.m_angularVelocity = w2; + } + b2PrismaticJoint.prototype.SolvePositionConstraints = function (baumgarte) { + if (baumgarte === undefined) baumgarte = 0; + var limitC = 0; + var oldLimitImpulse = 0; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var c1 = bA.m_sweep.c; + var a1 = bA.m_sweep.a; + var c2 = bB.m_sweep.c; + var a2 = bB.m_sweep.a; + var tMat; + var tX = 0; + var m1 = 0; + var m2 = 0; + var i1 = 0; + var i2 = 0; + var linearError = 0.0; + var angularError = 0.0; + var active = false; + var C2 = 0.0; + var R1 = b2Mat22.FromAngle(a1); + var R2 = b2Mat22.FromAngle(a2); + tMat = R1; + var r1X = this.m_localAnchor1.x - this.m_localCenterA.x; + var r1Y = this.m_localAnchor1.y - this.m_localCenterA.y; + tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = R2; + var r2X = this.m_localAnchor2.x - this.m_localCenterB.x; + var r2Y = this.m_localAnchor2.y - this.m_localCenterB.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var dX = c2.x + r2X - c1.x - r1X; + var dY = c2.y + r2Y - c1.y - r1Y; + if (this.m_enableLimit) { + this.m_axis = b2Math.MulMV(R1, this.m_localXAxis1); + this.m_a1 = (dX + r1X) * this.m_axis.y - (dY + r1Y) * this.m_axis.x; + this.m_a2 = r2X * this.m_axis.y - r2Y * this.m_axis.x; + var translation = this.m_axis.x * dX + this.m_axis.y * dY; + if (b2Math.Abs(this.m_upperTranslation - this.m_lowerTranslation) < 2.0 * b2Settings.b2_linearSlop) { + C2 = b2Math.Clamp(translation, (-b2Settings.b2_maxLinearCorrection), b2Settings.b2_maxLinearCorrection); + linearError = b2Math.Abs(translation); + active = true; + } + else if (translation <= this.m_lowerTranslation) { + C2 = b2Math.Clamp(translation - this.m_lowerTranslation + b2Settings.b2_linearSlop, (-b2Settings.b2_maxLinearCorrection), 0.0); + linearError = this.m_lowerTranslation - translation; + active = true; + } + else if (translation >= this.m_upperTranslation) { + C2 = b2Math.Clamp(translation - this.m_upperTranslation + b2Settings.b2_linearSlop, 0.0, b2Settings.b2_maxLinearCorrection); + linearError = translation - this.m_upperTranslation; + active = true; + } + } + this.m_perp = b2Math.MulMV(R1, this.m_localYAxis1); + this.m_s1 = (dX + r1X) * this.m_perp.y - (dY + r1Y) * this.m_perp.x; + this.m_s2 = r2X * this.m_perp.y - r2Y * this.m_perp.x; + var impulse = new b2Vec3(); + var C1X = this.m_perp.x * dX + this.m_perp.y * dY; + var C1Y = a2 - a1 - this.m_refAngle; + linearError = b2Math.Max(linearError, b2Math.Abs(C1X)); + angularError = b2Math.Abs(C1Y); + if (active) { + m1 = this.m_invMassA; + m2 = this.m_invMassB; + i1 = this.m_invIA; + i2 = this.m_invIB; + this.m_K.col1.x = m1 + m2 + i1 * this.m_s1 * this.m_s1 + i2 * this.m_s2 * this.m_s2; + this.m_K.col1.y = i1 * this.m_s1 + i2 * this.m_s2; + this.m_K.col1.z = i1 * this.m_s1 * this.m_a1 + i2 * this.m_s2 * this.m_a2; + this.m_K.col2.x = this.m_K.col1.y; + this.m_K.col2.y = i1 + i2; + this.m_K.col2.z = i1 * this.m_a1 + i2 * this.m_a2; + this.m_K.col3.x = this.m_K.col1.z; + this.m_K.col3.y = this.m_K.col2.z; + this.m_K.col3.z = m1 + m2 + i1 * this.m_a1 * this.m_a1 + i2 * this.m_a2 * this.m_a2; + this.m_K.Solve33(impulse, (-C1X), (-C1Y), (-C2)); + } + else { + m1 = this.m_invMassA; + m2 = this.m_invMassB; + i1 = this.m_invIA; + i2 = this.m_invIB; + var k11 = m1 + m2 + i1 * this.m_s1 * this.m_s1 + i2 * this.m_s2 * this.m_s2; + var k12 = i1 * this.m_s1 + i2 * this.m_s2; + var k22 = i1 + i2; + this.m_K.col1.Set(k11, k12, 0.0); + this.m_K.col2.Set(k12, k22, 0.0); + var impulse1 = this.m_K.Solve22(new b2Vec2(), (-C1X), (-C1Y)); + impulse.x = impulse1.x; + impulse.y = impulse1.y; + impulse.z = 0.0; + } + var PX = impulse.x * this.m_perp.x + impulse.z * this.m_axis.x; + var PY = impulse.x * this.m_perp.y + impulse.z * this.m_axis.y; + var L1 = impulse.x * this.m_s1 + impulse.y + impulse.z * this.m_a1; + var L2 = impulse.x * this.m_s2 + impulse.y + impulse.z * this.m_a2; + c1.x -= this.m_invMassA * PX; + c1.y -= this.m_invMassA * PY; + a1 -= this.m_invIA * L1; + c2.x += this.m_invMassB * PX; + c2.y += this.m_invMassB * PY; + a2 += this.m_invIB * L2; + bA.m_sweep.a = a1; + bB.m_sweep.a = a2; + bA.SynchronizeTransform(); + bB.SynchronizeTransform(); + return linearError <= b2Settings.b2_linearSlop && angularError <= b2Settings.b2_angularSlop; + } + Box2D.inherit(b2PrismaticJointDef, Box2D.Dynamics.Joints.b2JointDef); + b2PrismaticJointDef.prototype.__super = Box2D.Dynamics.Joints.b2JointDef.prototype; + b2PrismaticJointDef.b2PrismaticJointDef = function () { + Box2D.Dynamics.Joints.b2JointDef.b2JointDef.apply(this, arguments); + this.localAnchorA = new b2Vec2(); + this.localAnchorB = new b2Vec2(); + this.localAxisA = new b2Vec2(); + }; + b2PrismaticJointDef.prototype.b2PrismaticJointDef = function () { + this.__super.b2JointDef.call(this); + this.type = b2Joint.e_prismaticJoint; + this.localAxisA.Set(1.0, 0.0); + this.referenceAngle = 0.0; + this.enableLimit = false; + this.lowerTranslation = 0.0; + this.upperTranslation = 0.0; + this.enableMotor = false; + this.maxMotorForce = 0.0; + this.motorSpeed = 0.0; + } + b2PrismaticJointDef.prototype.Initialize = function (bA, bB, anchor, axis) { + this.bodyA = bA; + this.bodyB = bB; + this.localAnchorA = this.bodyA.GetLocalPoint(anchor); + this.localAnchorB = this.bodyB.GetLocalPoint(anchor); + this.localAxisA = this.bodyA.GetLocalVector(axis); + this.referenceAngle = this.bodyB.GetAngle() - this.bodyA.GetAngle(); + } + Box2D.inherit(b2PulleyJoint, Box2D.Dynamics.Joints.b2Joint); + b2PulleyJoint.prototype.__super = Box2D.Dynamics.Joints.b2Joint.prototype; + b2PulleyJoint.b2PulleyJoint = function () { + Box2D.Dynamics.Joints.b2Joint.b2Joint.apply(this, arguments); + this.m_groundAnchor1 = new b2Vec2(); + this.m_groundAnchor2 = new b2Vec2(); + this.m_localAnchor1 = new b2Vec2(); + this.m_localAnchor2 = new b2Vec2(); + this.m_u1 = new b2Vec2(); + this.m_u2 = new b2Vec2(); + }; + b2PulleyJoint.prototype.GetAnchorA = function () { + return this.m_bodyA.GetWorldPoint(this.m_localAnchor1); + } + b2PulleyJoint.prototype.GetAnchorB = function () { + return this.m_bodyB.GetWorldPoint(this.m_localAnchor2); + } + b2PulleyJoint.prototype.GetReactionForce = function (inv_dt) { + if (inv_dt === undefined) inv_dt = 0; + return new b2Vec2(inv_dt * this.m_impulse * this.m_u2.x, inv_dt * this.m_impulse * this.m_u2.y); + } + b2PulleyJoint.prototype.GetReactionTorque = function (inv_dt) { + if (inv_dt === undefined) inv_dt = 0; + return 0.0; + } + b2PulleyJoint.prototype.GetGroundAnchorA = function () { + var a = this.m_ground.m_xf.position.Copy(); + a.Add(this.m_groundAnchor1); + return a; + } + b2PulleyJoint.prototype.GetGroundAnchorB = function () { + var a = this.m_ground.m_xf.position.Copy(); + a.Add(this.m_groundAnchor2); + return a; + } + b2PulleyJoint.prototype.GetLength1 = function () { + var p = this.m_bodyA.GetWorldPoint(this.m_localAnchor1); + var sX = this.m_ground.m_xf.position.x + this.m_groundAnchor1.x; + var sY = this.m_ground.m_xf.position.y + this.m_groundAnchor1.y; + var dX = p.x - sX; + var dY = p.y - sY; + return Math.sqrt(dX * dX + dY * dY); + } + b2PulleyJoint.prototype.GetLength2 = function () { + var p = this.m_bodyB.GetWorldPoint(this.m_localAnchor2); + var sX = this.m_ground.m_xf.position.x + this.m_groundAnchor2.x; + var sY = this.m_ground.m_xf.position.y + this.m_groundAnchor2.y; + var dX = p.x - sX; + var dY = p.y - sY; + return Math.sqrt(dX * dX + dY * dY); + } + b2PulleyJoint.prototype.GetRatio = function () { + return this.m_ratio; + } + b2PulleyJoint.prototype.b2PulleyJoint = function (def) { + this.__super.b2Joint.call(this, def); + var tMat; + var tX = 0; + var tY = 0; + this.m_ground = this.m_bodyA.m_world.m_groundBody; + this.m_groundAnchor1.x = def.groundAnchorA.x - this.m_ground.m_xf.position.x; + this.m_groundAnchor1.y = def.groundAnchorA.y - this.m_ground.m_xf.position.y; + this.m_groundAnchor2.x = def.groundAnchorB.x - this.m_ground.m_xf.position.x; + this.m_groundAnchor2.y = def.groundAnchorB.y - this.m_ground.m_xf.position.y; + this.m_localAnchor1.SetV(def.localAnchorA); + this.m_localAnchor2.SetV(def.localAnchorB); + this.m_ratio = def.ratio; + this.m_constant = def.lengthA + this.m_ratio * def.lengthB; + this.m_maxLength1 = b2Math.Min(def.maxLengthA, this.m_constant - this.m_ratio * b2PulleyJoint.b2_minPulleyLength); + this.m_maxLength2 = b2Math.Min(def.maxLengthB, (this.m_constant - b2PulleyJoint.b2_minPulleyLength) / this.m_ratio); + this.m_impulse = 0.0; + this.m_limitImpulse1 = 0.0; + this.m_limitImpulse2 = 0.0; + } + b2PulleyJoint.prototype.InitVelocityConstraints = function (step) { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var tMat; + tMat = bA.m_xf.R; + var r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + var r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + var tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + var r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + var r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var p1X = bA.m_sweep.c.x + r1X; + var p1Y = bA.m_sweep.c.y + r1Y; + var p2X = bB.m_sweep.c.x + r2X; + var p2Y = bB.m_sweep.c.y + r2Y; + var s1X = this.m_ground.m_xf.position.x + this.m_groundAnchor1.x; + var s1Y = this.m_ground.m_xf.position.y + this.m_groundAnchor1.y; + var s2X = this.m_ground.m_xf.position.x + this.m_groundAnchor2.x; + var s2Y = this.m_ground.m_xf.position.y + this.m_groundAnchor2.y; + this.m_u1.Set(p1X - s1X, p1Y - s1Y); + this.m_u2.Set(p2X - s2X, p2Y - s2Y); + var length1 = this.m_u1.Length(); + var length2 = this.m_u2.Length(); + if (length1 > b2Settings.b2_linearSlop) { + this.m_u1.Multiply(1.0 / length1); + } + else { + this.m_u1.SetZero(); + } + if (length2 > b2Settings.b2_linearSlop) { + this.m_u2.Multiply(1.0 / length2); + } + else { + this.m_u2.SetZero(); + } + var C = this.m_constant - length1 - this.m_ratio * length2; + if (C > 0.0) { + this.m_state = b2Joint.e_inactiveLimit; + this.m_impulse = 0.0; + } + else { + this.m_state = b2Joint.e_atUpperLimit; + } + if (length1 < this.m_maxLength1) { + this.m_limitState1 = b2Joint.e_inactiveLimit; + this.m_limitImpulse1 = 0.0; + } + else { + this.m_limitState1 = b2Joint.e_atUpperLimit; + } + if (length2 < this.m_maxLength2) { + this.m_limitState2 = b2Joint.e_inactiveLimit; + this.m_limitImpulse2 = 0.0; + } + else { + this.m_limitState2 = b2Joint.e_atUpperLimit; + } + var cr1u1 = r1X * this.m_u1.y - r1Y * this.m_u1.x; + var cr2u2 = r2X * this.m_u2.y - r2Y * this.m_u2.x; + this.m_limitMass1 = bA.m_invMass + bA.m_invI * cr1u1 * cr1u1; + this.m_limitMass2 = bB.m_invMass + bB.m_invI * cr2u2 * cr2u2; + this.m_pulleyMass = this.m_limitMass1 + this.m_ratio * this.m_ratio * this.m_limitMass2; + this.m_limitMass1 = 1.0 / this.m_limitMass1; + this.m_limitMass2 = 1.0 / this.m_limitMass2; + this.m_pulleyMass = 1.0 / this.m_pulleyMass; + if (step.warmStarting) { + this.m_impulse *= step.dtRatio; + this.m_limitImpulse1 *= step.dtRatio; + this.m_limitImpulse2 *= step.dtRatio; + var P1X = ((-this.m_impulse) - this.m_limitImpulse1) * this.m_u1.x; + var P1Y = ((-this.m_impulse) - this.m_limitImpulse1) * this.m_u1.y; + var P2X = ((-this.m_ratio * this.m_impulse) - this.m_limitImpulse2) * this.m_u2.x; + var P2Y = ((-this.m_ratio * this.m_impulse) - this.m_limitImpulse2) * this.m_u2.y; + bA.m_linearVelocity.x += bA.m_invMass * P1X; + bA.m_linearVelocity.y += bA.m_invMass * P1Y; + bA.m_angularVelocity += bA.m_invI * (r1X * P1Y - r1Y * P1X); + bB.m_linearVelocity.x += bB.m_invMass * P2X; + bB.m_linearVelocity.y += bB.m_invMass * P2Y; + bB.m_angularVelocity += bB.m_invI * (r2X * P2Y - r2Y * P2X); + } + else { + this.m_impulse = 0.0; + this.m_limitImpulse1 = 0.0; + this.m_limitImpulse2 = 0.0; + } + } + b2PulleyJoint.prototype.SolveVelocityConstraints = function (step) { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var tMat; + tMat = bA.m_xf.R; + var r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + var r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + var tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + var r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + var r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var v1X = 0; + var v1Y = 0; + var v2X = 0; + var v2Y = 0; + var P1X = 0; + var P1Y = 0; + var P2X = 0; + var P2Y = 0; + var Cdot = 0; + var impulse = 0; + var oldImpulse = 0; + if (this.m_state == b2Joint.e_atUpperLimit) { + v1X = bA.m_linearVelocity.x + ((-bA.m_angularVelocity * r1Y)); + v1Y = bA.m_linearVelocity.y + (bA.m_angularVelocity * r1X); + v2X = bB.m_linearVelocity.x + ((-bB.m_angularVelocity * r2Y)); + v2Y = bB.m_linearVelocity.y + (bB.m_angularVelocity * r2X); + Cdot = (-(this.m_u1.x * v1X + this.m_u1.y * v1Y)) - this.m_ratio * (this.m_u2.x * v2X + this.m_u2.y * v2Y); + impulse = this.m_pulleyMass * ((-Cdot)); + oldImpulse = this.m_impulse; + this.m_impulse = b2Math.Max(0.0, this.m_impulse + impulse); + impulse = this.m_impulse - oldImpulse; + P1X = (-impulse * this.m_u1.x); + P1Y = (-impulse * this.m_u1.y); + P2X = (-this.m_ratio * impulse * this.m_u2.x); + P2Y = (-this.m_ratio * impulse * this.m_u2.y); + bA.m_linearVelocity.x += bA.m_invMass * P1X; + bA.m_linearVelocity.y += bA.m_invMass * P1Y; + bA.m_angularVelocity += bA.m_invI * (r1X * P1Y - r1Y * P1X); + bB.m_linearVelocity.x += bB.m_invMass * P2X; + bB.m_linearVelocity.y += bB.m_invMass * P2Y; + bB.m_angularVelocity += bB.m_invI * (r2X * P2Y - r2Y * P2X); + } + if (this.m_limitState1 == b2Joint.e_atUpperLimit) { + v1X = bA.m_linearVelocity.x + ((-bA.m_angularVelocity * r1Y)); + v1Y = bA.m_linearVelocity.y + (bA.m_angularVelocity * r1X); + Cdot = (-(this.m_u1.x * v1X + this.m_u1.y * v1Y)); + impulse = (-this.m_limitMass1 * Cdot); + oldImpulse = this.m_limitImpulse1; + this.m_limitImpulse1 = b2Math.Max(0.0, this.m_limitImpulse1 + impulse); + impulse = this.m_limitImpulse1 - oldImpulse; + P1X = (-impulse * this.m_u1.x); + P1Y = (-impulse * this.m_u1.y); + bA.m_linearVelocity.x += bA.m_invMass * P1X; + bA.m_linearVelocity.y += bA.m_invMass * P1Y; + bA.m_angularVelocity += bA.m_invI * (r1X * P1Y - r1Y * P1X); + } + if (this.m_limitState2 == b2Joint.e_atUpperLimit) { + v2X = bB.m_linearVelocity.x + ((-bB.m_angularVelocity * r2Y)); + v2Y = bB.m_linearVelocity.y + (bB.m_angularVelocity * r2X); + Cdot = (-(this.m_u2.x * v2X + this.m_u2.y * v2Y)); + impulse = (-this.m_limitMass2 * Cdot); + oldImpulse = this.m_limitImpulse2; + this.m_limitImpulse2 = b2Math.Max(0.0, this.m_limitImpulse2 + impulse); + impulse = this.m_limitImpulse2 - oldImpulse; + P2X = (-impulse * this.m_u2.x); + P2Y = (-impulse * this.m_u2.y); + bB.m_linearVelocity.x += bB.m_invMass * P2X; + bB.m_linearVelocity.y += bB.m_invMass * P2Y; + bB.m_angularVelocity += bB.m_invI * (r2X * P2Y - r2Y * P2X); + } + } + b2PulleyJoint.prototype.SolvePositionConstraints = function (baumgarte) { + if (baumgarte === undefined) baumgarte = 0; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var tMat; + var s1X = this.m_ground.m_xf.position.x + this.m_groundAnchor1.x; + var s1Y = this.m_ground.m_xf.position.y + this.m_groundAnchor1.y; + var s2X = this.m_ground.m_xf.position.x + this.m_groundAnchor2.x; + var s2Y = this.m_ground.m_xf.position.y + this.m_groundAnchor2.y; + var r1X = 0; + var r1Y = 0; + var r2X = 0; + var r2Y = 0; + var p1X = 0; + var p1Y = 0; + var p2X = 0; + var p2Y = 0; + var length1 = 0; + var length2 = 0; + var C = 0; + var impulse = 0; + var oldImpulse = 0; + var oldLimitPositionImpulse = 0; + var tX = 0; + var linearError = 0.0; + if (this.m_state == b2Joint.e_atUpperLimit) { + tMat = bA.m_xf.R; + r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + p1X = bA.m_sweep.c.x + r1X; + p1Y = bA.m_sweep.c.y + r1Y; + p2X = bB.m_sweep.c.x + r2X; + p2Y = bB.m_sweep.c.y + r2Y; + this.m_u1.Set(p1X - s1X, p1Y - s1Y); + this.m_u2.Set(p2X - s2X, p2Y - s2Y); + length1 = this.m_u1.Length(); + length2 = this.m_u2.Length(); + if (length1 > b2Settings.b2_linearSlop) { + this.m_u1.Multiply(1.0 / length1); + } + else { + this.m_u1.SetZero(); + } + if (length2 > b2Settings.b2_linearSlop) { + this.m_u2.Multiply(1.0 / length2); + } + else { + this.m_u2.SetZero(); + } + C = this.m_constant - length1 - this.m_ratio * length2; + linearError = b2Math.Max(linearError, (-C)); + C = b2Math.Clamp(C + b2Settings.b2_linearSlop, (-b2Settings.b2_maxLinearCorrection), 0.0); + impulse = (-this.m_pulleyMass * C); + p1X = (-impulse * this.m_u1.x); + p1Y = (-impulse * this.m_u1.y); + p2X = (-this.m_ratio * impulse * this.m_u2.x); + p2Y = (-this.m_ratio * impulse * this.m_u2.y); + bA.m_sweep.c.x += bA.m_invMass * p1X; + bA.m_sweep.c.y += bA.m_invMass * p1Y; + bA.m_sweep.a += bA.m_invI * (r1X * p1Y - r1Y * p1X); + bB.m_sweep.c.x += bB.m_invMass * p2X; + bB.m_sweep.c.y += bB.m_invMass * p2Y; + bB.m_sweep.a += bB.m_invI * (r2X * p2Y - r2Y * p2X); + bA.SynchronizeTransform(); + bB.SynchronizeTransform(); + } + if (this.m_limitState1 == b2Joint.e_atUpperLimit) { + tMat = bA.m_xf.R; + r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + p1X = bA.m_sweep.c.x + r1X; + p1Y = bA.m_sweep.c.y + r1Y; + this.m_u1.Set(p1X - s1X, p1Y - s1Y); + length1 = this.m_u1.Length(); + if (length1 > b2Settings.b2_linearSlop) { + this.m_u1.x *= 1.0 / length1; + this.m_u1.y *= 1.0 / length1; + } + else { + this.m_u1.SetZero(); + } + C = this.m_maxLength1 - length1; + linearError = b2Math.Max(linearError, (-C)); + C = b2Math.Clamp(C + b2Settings.b2_linearSlop, (-b2Settings.b2_maxLinearCorrection), 0.0); + impulse = (-this.m_limitMass1 * C); + p1X = (-impulse * this.m_u1.x); + p1Y = (-impulse * this.m_u1.y); + bA.m_sweep.c.x += bA.m_invMass * p1X; + bA.m_sweep.c.y += bA.m_invMass * p1Y; + bA.m_sweep.a += bA.m_invI * (r1X * p1Y - r1Y * p1X); + bA.SynchronizeTransform(); + } + if (this.m_limitState2 == b2Joint.e_atUpperLimit) { + tMat = bB.m_xf.R; + r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + p2X = bB.m_sweep.c.x + r2X; + p2Y = bB.m_sweep.c.y + r2Y; + this.m_u2.Set(p2X - s2X, p2Y - s2Y); + length2 = this.m_u2.Length(); + if (length2 > b2Settings.b2_linearSlop) { + this.m_u2.x *= 1.0 / length2; + this.m_u2.y *= 1.0 / length2; + } + else { + this.m_u2.SetZero(); + } + C = this.m_maxLength2 - length2; + linearError = b2Math.Max(linearError, (-C)); + C = b2Math.Clamp(C + b2Settings.b2_linearSlop, (-b2Settings.b2_maxLinearCorrection), 0.0); + impulse = (-this.m_limitMass2 * C); + p2X = (-impulse * this.m_u2.x); + p2Y = (-impulse * this.m_u2.y); + bB.m_sweep.c.x += bB.m_invMass * p2X; + bB.m_sweep.c.y += bB.m_invMass * p2Y; + bB.m_sweep.a += bB.m_invI * (r2X * p2Y - r2Y * p2X); + bB.SynchronizeTransform(); + } + return linearError < b2Settings.b2_linearSlop; + } + Box2D.postDefs.push(function () { + Box2D.Dynamics.Joints.b2PulleyJoint.b2_minPulleyLength = 2.0; + }); + Box2D.inherit(b2PulleyJointDef, Box2D.Dynamics.Joints.b2JointDef); + b2PulleyJointDef.prototype.__super = Box2D.Dynamics.Joints.b2JointDef.prototype; + b2PulleyJointDef.b2PulleyJointDef = function () { + Box2D.Dynamics.Joints.b2JointDef.b2JointDef.apply(this, arguments); + this.groundAnchorA = new b2Vec2(); + this.groundAnchorB = new b2Vec2(); + this.localAnchorA = new b2Vec2(); + this.localAnchorB = new b2Vec2(); + }; + b2PulleyJointDef.prototype.b2PulleyJointDef = function () { + this.__super.b2JointDef.call(this); + this.type = b2Joint.e_pulleyJoint; + this.groundAnchorA.Set((-1.0), 1.0); + this.groundAnchorB.Set(1.0, 1.0); + this.localAnchorA.Set((-1.0), 0.0); + this.localAnchorB.Set(1.0, 0.0); + this.lengthA = 0.0; + this.maxLengthA = 0.0; + this.lengthB = 0.0; + this.maxLengthB = 0.0; + this.ratio = 1.0; + this.collideConnected = true; + } + b2PulleyJointDef.prototype.Initialize = function (bA, bB, gaA, gaB, anchorA, anchorB, r) { + if (r === undefined) r = 0; + this.bodyA = bA; + this.bodyB = bB; + this.groundAnchorA.SetV(gaA); + this.groundAnchorB.SetV(gaB); + this.localAnchorA = this.bodyA.GetLocalPoint(anchorA); + this.localAnchorB = this.bodyB.GetLocalPoint(anchorB); + var d1X = anchorA.x - gaA.x; + var d1Y = anchorA.y - gaA.y; + this.lengthA = Math.sqrt(d1X * d1X + d1Y * d1Y); + var d2X = anchorB.x - gaB.x; + var d2Y = anchorB.y - gaB.y; + this.lengthB = Math.sqrt(d2X * d2X + d2Y * d2Y); + this.ratio = r; + var C = this.lengthA + this.ratio * this.lengthB; + this.maxLengthA = C - this.ratio * b2PulleyJoint.b2_minPulleyLength; + this.maxLengthB = (C - b2PulleyJoint.b2_minPulleyLength) / this.ratio; + } + Box2D.inherit(b2RevoluteJoint, Box2D.Dynamics.Joints.b2Joint); + b2RevoluteJoint.prototype.__super = Box2D.Dynamics.Joints.b2Joint.prototype; + b2RevoluteJoint.b2RevoluteJoint = function () { + Box2D.Dynamics.Joints.b2Joint.b2Joint.apply(this, arguments); + this.K = new b2Mat22(); + this.K1 = new b2Mat22(); + this.K2 = new b2Mat22(); + this.K3 = new b2Mat22(); + this.impulse3 = new b2Vec3(); + this.impulse2 = new b2Vec2(); + this.reduced = new b2Vec2(); + this.m_localAnchor1 = new b2Vec2(); + this.m_localAnchor2 = new b2Vec2(); + this.m_impulse = new b2Vec3(); + this.m_mass = new b2Mat33(); + }; + b2RevoluteJoint.prototype.GetAnchorA = function () { + return this.m_bodyA.GetWorldPoint(this.m_localAnchor1); + } + b2RevoluteJoint.prototype.GetAnchorB = function () { + return this.m_bodyB.GetWorldPoint(this.m_localAnchor2); + } + b2RevoluteJoint.prototype.GetReactionForce = function (inv_dt) { + if (inv_dt === undefined) inv_dt = 0; + return new b2Vec2(inv_dt * this.m_impulse.x, inv_dt * this.m_impulse.y); + } + b2RevoluteJoint.prototype.GetReactionTorque = function (inv_dt) { + if (inv_dt === undefined) inv_dt = 0; + return inv_dt * this.m_impulse.z; + } + b2RevoluteJoint.prototype.GetJointAngle = function () { + return this.m_bodyB.m_sweep.a - this.m_bodyA.m_sweep.a - this.m_referenceAngle; + } + b2RevoluteJoint.prototype.GetJointSpeed = function () { + return this.m_bodyB.m_angularVelocity - this.m_bodyA.m_angularVelocity; + } + b2RevoluteJoint.prototype.IsLimitEnabled = function () { + return this.m_enableLimit; + } + b2RevoluteJoint.prototype.EnableLimit = function (flag) { + this.m_enableLimit = flag; + } + b2RevoluteJoint.prototype.GetLowerLimit = function () { + return this.m_lowerAngle; + } + b2RevoluteJoint.prototype.GetUpperLimit = function () { + return this.m_upperAngle; + } + b2RevoluteJoint.prototype.SetLimits = function (lower, upper) { + if (lower === undefined) lower = 0; + if (upper === undefined) upper = 0; + this.m_lowerAngle = lower; + this.m_upperAngle = upper; + } + b2RevoluteJoint.prototype.IsMotorEnabled = function () { + this.m_bodyA.SetAwake(true); + this.m_bodyB.SetAwake(true); + return this.m_enableMotor; + } + b2RevoluteJoint.prototype.EnableMotor = function (flag) { + this.m_enableMotor = flag; + } + b2RevoluteJoint.prototype.SetMotorSpeed = function (speed) { + if (speed === undefined) speed = 0; + this.m_bodyA.SetAwake(true); + this.m_bodyB.SetAwake(true); + this.m_motorSpeed = speed; + } + b2RevoluteJoint.prototype.GetMotorSpeed = function () { + return this.m_motorSpeed; + } + b2RevoluteJoint.prototype.SetMaxMotorTorque = function (torque) { + if (torque === undefined) torque = 0; + this.m_maxMotorTorque = torque; + } + b2RevoluteJoint.prototype.GetMotorTorque = function () { + return this.m_maxMotorTorque; + } + b2RevoluteJoint.prototype.b2RevoluteJoint = function (def) { + this.__super.b2Joint.call(this, def); + this.m_localAnchor1.SetV(def.localAnchorA); + this.m_localAnchor2.SetV(def.localAnchorB); + this.m_referenceAngle = def.referenceAngle; + this.m_impulse.SetZero(); + this.m_motorImpulse = 0.0; + this.m_lowerAngle = def.lowerAngle; + this.m_upperAngle = def.upperAngle; + this.m_maxMotorTorque = def.maxMotorTorque; + this.m_motorSpeed = def.motorSpeed; + this.m_enableLimit = def.enableLimit; + this.m_enableMotor = def.enableMotor; + this.m_limitState = b2Joint.e_inactiveLimit; + } + b2RevoluteJoint.prototype.InitVelocityConstraints = function (step) { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var tMat; + var tX = 0; + if (this.m_enableMotor || this.m_enableLimit) {} + tMat = bA.m_xf.R; + var r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + var r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + var r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + var r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var m1 = bA.m_invMass; + var m2 = bB.m_invMass; + var i1 = bA.m_invI; + var i2 = bB.m_invI; + this.m_mass.col1.x = m1 + m2 + r1Y * r1Y * i1 + r2Y * r2Y * i2; + this.m_mass.col2.x = (-r1Y * r1X * i1) - r2Y * r2X * i2; + this.m_mass.col3.x = (-r1Y * i1) - r2Y * i2; + this.m_mass.col1.y = this.m_mass.col2.x; + this.m_mass.col2.y = m1 + m2 + r1X * r1X * i1 + r2X * r2X * i2; + this.m_mass.col3.y = r1X * i1 + r2X * i2; + this.m_mass.col1.z = this.m_mass.col3.x; + this.m_mass.col2.z = this.m_mass.col3.y; + this.m_mass.col3.z = i1 + i2; + this.m_motorMass = 1.0 / (i1 + i2); + if (this.m_enableMotor == false) { + this.m_motorImpulse = 0.0; + } + if (this.m_enableLimit) { + var jointAngle = bB.m_sweep.a - bA.m_sweep.a - this.m_referenceAngle; + if (b2Math.Abs(this.m_upperAngle - this.m_lowerAngle) < 2.0 * b2Settings.b2_angularSlop) { + this.m_limitState = b2Joint.e_equalLimits; + } + else if (jointAngle <= this.m_lowerAngle) { + if (this.m_limitState != b2Joint.e_atLowerLimit) { + this.m_impulse.z = 0.0; + } + this.m_limitState = b2Joint.e_atLowerLimit; + } + else if (jointAngle >= this.m_upperAngle) { + if (this.m_limitState != b2Joint.e_atUpperLimit) { + this.m_impulse.z = 0.0; + } + this.m_limitState = b2Joint.e_atUpperLimit; + } + else { + this.m_limitState = b2Joint.e_inactiveLimit; + this.m_impulse.z = 0.0; + } + } + else { + this.m_limitState = b2Joint.e_inactiveLimit; + } + if (step.warmStarting) { + this.m_impulse.x *= step.dtRatio; + this.m_impulse.y *= step.dtRatio; + this.m_motorImpulse *= step.dtRatio; + var PX = this.m_impulse.x; + var PY = this.m_impulse.y; + bA.m_linearVelocity.x -= m1 * PX; + bA.m_linearVelocity.y -= m1 * PY; + bA.m_angularVelocity -= i1 * ((r1X * PY - r1Y * PX) + this.m_motorImpulse + this.m_impulse.z); + bB.m_linearVelocity.x += m2 * PX; + bB.m_linearVelocity.y += m2 * PY; + bB.m_angularVelocity += i2 * ((r2X * PY - r2Y * PX) + this.m_motorImpulse + this.m_impulse.z); + } + else { + this.m_impulse.SetZero(); + this.m_motorImpulse = 0.0; + } + } + b2RevoluteJoint.prototype.SolveVelocityConstraints = function (step) { + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var tMat; + var tX = 0; + var newImpulse = 0; + var r1X = 0; + var r1Y = 0; + var r2X = 0; + var r2Y = 0; + var v1 = bA.m_linearVelocity; + var w1 = bA.m_angularVelocity; + var v2 = bB.m_linearVelocity; + var w2 = bB.m_angularVelocity; + var m1 = bA.m_invMass; + var m2 = bB.m_invMass; + var i1 = bA.m_invI; + var i2 = bB.m_invI; + if (this.m_enableMotor && this.m_limitState != b2Joint.e_equalLimits) { + var Cdot = w2 - w1 - this.m_motorSpeed; + var impulse = this.m_motorMass * ((-Cdot)); + var oldImpulse = this.m_motorImpulse; + var maxImpulse = step.dt * this.m_maxMotorTorque; + this.m_motorImpulse = b2Math.Clamp(this.m_motorImpulse + impulse, (-maxImpulse), maxImpulse); + impulse = this.m_motorImpulse - oldImpulse; + w1 -= i1 * impulse; + w2 += i2 * impulse; + } + if (this.m_enableLimit && this.m_limitState != b2Joint.e_inactiveLimit) { + tMat = bA.m_xf.R; + r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var Cdot1X = v2.x + ((-w2 * r2Y)) - v1.x - ((-w1 * r1Y)); + var Cdot1Y = v2.y + (w2 * r2X) - v1.y - (w1 * r1X); + var Cdot2 = w2 - w1; + this.m_mass.Solve33(this.impulse3, (-Cdot1X), (-Cdot1Y), (-Cdot2)); + if (this.m_limitState == b2Joint.e_equalLimits) { + this.m_impulse.Add(this.impulse3); + } + else if (this.m_limitState == b2Joint.e_atLowerLimit) { + newImpulse = this.m_impulse.z + this.impulse3.z; + if (newImpulse < 0.0) { + this.m_mass.Solve22(this.reduced, (-Cdot1X), (-Cdot1Y)); + this.impulse3.x = this.reduced.x; + this.impulse3.y = this.reduced.y; + this.impulse3.z = (-this.m_impulse.z); + this.m_impulse.x += this.reduced.x; + this.m_impulse.y += this.reduced.y; + this.m_impulse.z = 0.0; + } + } + else if (this.m_limitState == b2Joint.e_atUpperLimit) { + newImpulse = this.m_impulse.z + this.impulse3.z; + if (newImpulse > 0.0) { + this.m_mass.Solve22(this.reduced, (-Cdot1X), (-Cdot1Y)); + this.impulse3.x = this.reduced.x; + this.impulse3.y = this.reduced.y; + this.impulse3.z = (-this.m_impulse.z); + this.m_impulse.x += this.reduced.x; + this.m_impulse.y += this.reduced.y; + this.m_impulse.z = 0.0; + } + } + v1.x -= m1 * this.impulse3.x; + v1.y -= m1 * this.impulse3.y; + w1 -= i1 * (r1X * this.impulse3.y - r1Y * this.impulse3.x + this.impulse3.z); + v2.x += m2 * this.impulse3.x; + v2.y += m2 * this.impulse3.y; + w2 += i2 * (r2X * this.impulse3.y - r2Y * this.impulse3.x + this.impulse3.z); + } + else { + tMat = bA.m_xf.R; + r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var CdotX = v2.x + ((-w2 * r2Y)) - v1.x - ((-w1 * r1Y)); + var CdotY = v2.y + (w2 * r2X) - v1.y - (w1 * r1X); + this.m_mass.Solve22(this.impulse2, (-CdotX), (-CdotY)); + this.m_impulse.x += this.impulse2.x; + this.m_impulse.y += this.impulse2.y; + v1.x -= m1 * this.impulse2.x; + v1.y -= m1 * this.impulse2.y; + w1 -= i1 * (r1X * this.impulse2.y - r1Y * this.impulse2.x); + v2.x += m2 * this.impulse2.x; + v2.y += m2 * this.impulse2.y; + w2 += i2 * (r2X * this.impulse2.y - r2Y * this.impulse2.x); + } + bA.m_linearVelocity.SetV(v1); + bA.m_angularVelocity = w1; + bB.m_linearVelocity.SetV(v2); + bB.m_angularVelocity = w2; + } + b2RevoluteJoint.prototype.SolvePositionConstraints = function (baumgarte) { + if (baumgarte === undefined) baumgarte = 0; + var oldLimitImpulse = 0; + var C = 0; + var tMat; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var angularError = 0.0; + var positionError = 0.0; + var tX = 0; + var impulseX = 0; + var impulseY = 0; + if (this.m_enableLimit && this.m_limitState != b2Joint.e_inactiveLimit) { + var angle = bB.m_sweep.a - bA.m_sweep.a - this.m_referenceAngle; + var limitImpulse = 0.0; + if (this.m_limitState == b2Joint.e_equalLimits) { + C = b2Math.Clamp(angle - this.m_lowerAngle, (-b2Settings.b2_maxAngularCorrection), b2Settings.b2_maxAngularCorrection); + limitImpulse = (-this.m_motorMass * C); + angularError = b2Math.Abs(C); + } + else if (this.m_limitState == b2Joint.e_atLowerLimit) { + C = angle - this.m_lowerAngle; + angularError = (-C); + C = b2Math.Clamp(C + b2Settings.b2_angularSlop, (-b2Settings.b2_maxAngularCorrection), 0.0); + limitImpulse = (-this.m_motorMass * C); + } + else if (this.m_limitState == b2Joint.e_atUpperLimit) { + C = angle - this.m_upperAngle; + angularError = C; + C = b2Math.Clamp(C - b2Settings.b2_angularSlop, 0.0, b2Settings.b2_maxAngularCorrection); + limitImpulse = (-this.m_motorMass * C); + } + bA.m_sweep.a -= bA.m_invI * limitImpulse; + bB.m_sweep.a += bB.m_invI * limitImpulse; + bA.SynchronizeTransform(); + bB.SynchronizeTransform(); + } { + tMat = bA.m_xf.R; + var r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x; + var r1Y = this.m_localAnchor1.y - bA.m_sweep.localCenter.y; + tX = (tMat.col1.x * r1X + tMat.col2.x * r1Y); + r1Y = (tMat.col1.y * r1X + tMat.col2.y * r1Y); + r1X = tX; + tMat = bB.m_xf.R; + var r2X = this.m_localAnchor2.x - bB.m_sweep.localCenter.x; + var r2Y = this.m_localAnchor2.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * r2X + tMat.col2.x * r2Y); + r2Y = (tMat.col1.y * r2X + tMat.col2.y * r2Y); + r2X = tX; + var CX = bB.m_sweep.c.x + r2X - bA.m_sweep.c.x - r1X; + var CY = bB.m_sweep.c.y + r2Y - bA.m_sweep.c.y - r1Y; + var CLengthSquared = CX * CX + CY * CY; + var CLength = Math.sqrt(CLengthSquared); + positionError = CLength; + var invMass1 = bA.m_invMass; + var invMass2 = bB.m_invMass; + var invI1 = bA.m_invI; + var invI2 = bB.m_invI; + var k_allowedStretch = 10.0 * b2Settings.b2_linearSlop; + if (CLengthSquared > k_allowedStretch * k_allowedStretch) { + var uX = CX / CLength; + var uY = CY / CLength; + var k = invMass1 + invMass2; + var m = 1.0 / k; + impulseX = m * ((-CX)); + impulseY = m * ((-CY)); + var k_beta = 0.5; + bA.m_sweep.c.x -= k_beta * invMass1 * impulseX; + bA.m_sweep.c.y -= k_beta * invMass1 * impulseY; + bB.m_sweep.c.x += k_beta * invMass2 * impulseX; + bB.m_sweep.c.y += k_beta * invMass2 * impulseY; + CX = bB.m_sweep.c.x + r2X - bA.m_sweep.c.x - r1X; + CY = bB.m_sweep.c.y + r2Y - bA.m_sweep.c.y - r1Y; + } + this.K1.col1.x = invMass1 + invMass2; + this.K1.col2.x = 0.0; + this.K1.col1.y = 0.0; + this.K1.col2.y = invMass1 + invMass2; + this.K2.col1.x = invI1 * r1Y * r1Y; + this.K2.col2.x = (-invI1 * r1X * r1Y); + this.K2.col1.y = (-invI1 * r1X * r1Y); + this.K2.col2.y = invI1 * r1X * r1X; + this.K3.col1.x = invI2 * r2Y * r2Y; + this.K3.col2.x = (-invI2 * r2X * r2Y); + this.K3.col1.y = (-invI2 * r2X * r2Y); + this.K3.col2.y = invI2 * r2X * r2X; + this.K.SetM(this.K1); + this.K.AddM(this.K2); + this.K.AddM(this.K3); + this.K.Solve(b2RevoluteJoint.tImpulse, (-CX), (-CY)); + impulseX = b2RevoluteJoint.tImpulse.x; + impulseY = b2RevoluteJoint.tImpulse.y; + bA.m_sweep.c.x -= bA.m_invMass * impulseX; + bA.m_sweep.c.y -= bA.m_invMass * impulseY; + bA.m_sweep.a -= bA.m_invI * (r1X * impulseY - r1Y * impulseX); + bB.m_sweep.c.x += bB.m_invMass * impulseX; + bB.m_sweep.c.y += bB.m_invMass * impulseY; + bB.m_sweep.a += bB.m_invI * (r2X * impulseY - r2Y * impulseX); + bA.SynchronizeTransform(); + bB.SynchronizeTransform(); + } + return positionError <= b2Settings.b2_linearSlop && angularError <= b2Settings.b2_angularSlop; + } + Box2D.postDefs.push(function () { + Box2D.Dynamics.Joints.b2RevoluteJoint.tImpulse = new b2Vec2(); + }); + Box2D.inherit(b2RevoluteJointDef, Box2D.Dynamics.Joints.b2JointDef); + b2RevoluteJointDef.prototype.__super = Box2D.Dynamics.Joints.b2JointDef.prototype; + b2RevoluteJointDef.b2RevoluteJointDef = function () { + Box2D.Dynamics.Joints.b2JointDef.b2JointDef.apply(this, arguments); + this.localAnchorA = new b2Vec2(); + this.localAnchorB = new b2Vec2(); + }; + b2RevoluteJointDef.prototype.b2RevoluteJointDef = function () { + this.__super.b2JointDef.call(this); + this.type = b2Joint.e_revoluteJoint; + this.localAnchorA.Set(0.0, 0.0); + this.localAnchorB.Set(0.0, 0.0); + this.referenceAngle = 0.0; + this.lowerAngle = 0.0; + this.upperAngle = 0.0; + this.maxMotorTorque = 0.0; + this.motorSpeed = 0.0; + this.enableLimit = false; + this.enableMotor = false; + } + b2RevoluteJointDef.prototype.Initialize = function (bA, bB, anchor) { + this.bodyA = bA; + this.bodyB = bB; + this.localAnchorA = this.bodyA.GetLocalPoint(anchor); + this.localAnchorB = this.bodyB.GetLocalPoint(anchor); + this.referenceAngle = this.bodyB.GetAngle() - this.bodyA.GetAngle(); + } + Box2D.inherit(b2WeldJoint, Box2D.Dynamics.Joints.b2Joint); + b2WeldJoint.prototype.__super = Box2D.Dynamics.Joints.b2Joint.prototype; + b2WeldJoint.b2WeldJoint = function () { + Box2D.Dynamics.Joints.b2Joint.b2Joint.apply(this, arguments); + this.m_localAnchorA = new b2Vec2(); + this.m_localAnchorB = new b2Vec2(); + this.m_impulse = new b2Vec3(); + this.m_mass = new b2Mat33(); + }; + b2WeldJoint.prototype.GetAnchorA = function () { + return this.m_bodyA.GetWorldPoint(this.m_localAnchorA); + } + b2WeldJoint.prototype.GetAnchorB = function () { + return this.m_bodyB.GetWorldPoint(this.m_localAnchorB); + } + b2WeldJoint.prototype.GetReactionForce = function (inv_dt) { + if (inv_dt === undefined) inv_dt = 0; + return new b2Vec2(inv_dt * this.m_impulse.x, inv_dt * this.m_impulse.y); + } + b2WeldJoint.prototype.GetReactionTorque = function (inv_dt) { + if (inv_dt === undefined) inv_dt = 0; + return inv_dt * this.m_impulse.z; + } + b2WeldJoint.prototype.b2WeldJoint = function (def) { + this.__super.b2Joint.call(this, def); + this.m_localAnchorA.SetV(def.localAnchorA); + this.m_localAnchorB.SetV(def.localAnchorB); + this.m_referenceAngle = def.referenceAngle; + this.m_impulse.SetZero(); + this.m_mass = new b2Mat33(); + } + b2WeldJoint.prototype.InitVelocityConstraints = function (step) { + var tMat; + var tX = 0; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + tMat = bA.m_xf.R; + var rAX = this.m_localAnchorA.x - bA.m_sweep.localCenter.x; + var rAY = this.m_localAnchorA.y - bA.m_sweep.localCenter.y; + tX = (tMat.col1.x * rAX + tMat.col2.x * rAY); + rAY = (tMat.col1.y * rAX + tMat.col2.y * rAY); + rAX = tX; + tMat = bB.m_xf.R; + var rBX = this.m_localAnchorB.x - bB.m_sweep.localCenter.x; + var rBY = this.m_localAnchorB.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * rBX + tMat.col2.x * rBY); + rBY = (tMat.col1.y * rBX + tMat.col2.y * rBY); + rBX = tX; + var mA = bA.m_invMass; + var mB = bB.m_invMass; + var iA = bA.m_invI; + var iB = bB.m_invI; + this.m_mass.col1.x = mA + mB + rAY * rAY * iA + rBY * rBY * iB; + this.m_mass.col2.x = (-rAY * rAX * iA) - rBY * rBX * iB; + this.m_mass.col3.x = (-rAY * iA) - rBY * iB; + this.m_mass.col1.y = this.m_mass.col2.x; + this.m_mass.col2.y = mA + mB + rAX * rAX * iA + rBX * rBX * iB; + this.m_mass.col3.y = rAX * iA + rBX * iB; + this.m_mass.col1.z = this.m_mass.col3.x; + this.m_mass.col2.z = this.m_mass.col3.y; + this.m_mass.col3.z = iA + iB; + if (step.warmStarting) { + this.m_impulse.x *= step.dtRatio; + this.m_impulse.y *= step.dtRatio; + this.m_impulse.z *= step.dtRatio; + bA.m_linearVelocity.x -= mA * this.m_impulse.x; + bA.m_linearVelocity.y -= mA * this.m_impulse.y; + bA.m_angularVelocity -= iA * (rAX * this.m_impulse.y - rAY * this.m_impulse.x + this.m_impulse.z); + bB.m_linearVelocity.x += mB * this.m_impulse.x; + bB.m_linearVelocity.y += mB * this.m_impulse.y; + bB.m_angularVelocity += iB * (rBX * this.m_impulse.y - rBY * this.m_impulse.x + this.m_impulse.z); + } + else { + this.m_impulse.SetZero(); + } + } + b2WeldJoint.prototype.SolveVelocityConstraints = function (step) { + var tMat; + var tX = 0; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + var vA = bA.m_linearVelocity; + var wA = bA.m_angularVelocity; + var vB = bB.m_linearVelocity; + var wB = bB.m_angularVelocity; + var mA = bA.m_invMass; + var mB = bB.m_invMass; + var iA = bA.m_invI; + var iB = bB.m_invI; + tMat = bA.m_xf.R; + var rAX = this.m_localAnchorA.x - bA.m_sweep.localCenter.x; + var rAY = this.m_localAnchorA.y - bA.m_sweep.localCenter.y; + tX = (tMat.col1.x * rAX + tMat.col2.x * rAY); + rAY = (tMat.col1.y * rAX + tMat.col2.y * rAY); + rAX = tX; + tMat = bB.m_xf.R; + var rBX = this.m_localAnchorB.x - bB.m_sweep.localCenter.x; + var rBY = this.m_localAnchorB.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * rBX + tMat.col2.x * rBY); + rBY = (tMat.col1.y * rBX + tMat.col2.y * rBY); + rBX = tX; + var Cdot1X = vB.x - wB * rBY - vA.x + wA * rAY; + var Cdot1Y = vB.y + wB * rBX - vA.y - wA * rAX; + var Cdot2 = wB - wA; + var impulse = new b2Vec3(); + this.m_mass.Solve33(impulse, (-Cdot1X), (-Cdot1Y), (-Cdot2)); + this.m_impulse.Add(impulse); + vA.x -= mA * impulse.x; + vA.y -= mA * impulse.y; + wA -= iA * (rAX * impulse.y - rAY * impulse.x + impulse.z); + vB.x += mB * impulse.x; + vB.y += mB * impulse.y; + wB += iB * (rBX * impulse.y - rBY * impulse.x + impulse.z); + bA.m_angularVelocity = wA; + bB.m_angularVelocity = wB; + } + b2WeldJoint.prototype.SolvePositionConstraints = function (baumgarte) { + if (baumgarte === undefined) baumgarte = 0; + var tMat; + var tX = 0; + var bA = this.m_bodyA; + var bB = this.m_bodyB; + tMat = bA.m_xf.R; + var rAX = this.m_localAnchorA.x - bA.m_sweep.localCenter.x; + var rAY = this.m_localAnchorA.y - bA.m_sweep.localCenter.y; + tX = (tMat.col1.x * rAX + tMat.col2.x * rAY); + rAY = (tMat.col1.y * rAX + tMat.col2.y * rAY); + rAX = tX; + tMat = bB.m_xf.R; + var rBX = this.m_localAnchorB.x - bB.m_sweep.localCenter.x; + var rBY = this.m_localAnchorB.y - bB.m_sweep.localCenter.y; + tX = (tMat.col1.x * rBX + tMat.col2.x * rBY); + rBY = (tMat.col1.y * rBX + tMat.col2.y * rBY); + rBX = tX; + var mA = bA.m_invMass; + var mB = bB.m_invMass; + var iA = bA.m_invI; + var iB = bB.m_invI; + var C1X = bB.m_sweep.c.x + rBX - bA.m_sweep.c.x - rAX; + var C1Y = bB.m_sweep.c.y + rBY - bA.m_sweep.c.y - rAY; + var C2 = bB.m_sweep.a - bA.m_sweep.a - this.m_referenceAngle; + var k_allowedStretch = 10.0 * b2Settings.b2_linearSlop; + var positionError = Math.sqrt(C1X * C1X + C1Y * C1Y); + var angularError = b2Math.Abs(C2); + if (positionError > k_allowedStretch) { + iA *= 1.0; + iB *= 1.0; + } + this.m_mass.col1.x = mA + mB + rAY * rAY * iA + rBY * rBY * iB; + this.m_mass.col2.x = (-rAY * rAX * iA) - rBY * rBX * iB; + this.m_mass.col3.x = (-rAY * iA) - rBY * iB; + this.m_mass.col1.y = this.m_mass.col2.x; + this.m_mass.col2.y = mA + mB + rAX * rAX * iA + rBX * rBX * iB; + this.m_mass.col3.y = rAX * iA + rBX * iB; + this.m_mass.col1.z = this.m_mass.col3.x; + this.m_mass.col2.z = this.m_mass.col3.y; + this.m_mass.col3.z = iA + iB; + var impulse = new b2Vec3(); + this.m_mass.Solve33(impulse, (-C1X), (-C1Y), (-C2)); + bA.m_sweep.c.x -= mA * impulse.x; + bA.m_sweep.c.y -= mA * impulse.y; + bA.m_sweep.a -= iA * (rAX * impulse.y - rAY * impulse.x + impulse.z); + bB.m_sweep.c.x += mB * impulse.x; + bB.m_sweep.c.y += mB * impulse.y; + bB.m_sweep.a += iB * (rBX * impulse.y - rBY * impulse.x + impulse.z); + bA.SynchronizeTransform(); + bB.SynchronizeTransform(); + return positionError <= b2Settings.b2_linearSlop && angularError <= b2Settings.b2_angularSlop; + } + Box2D.inherit(b2WeldJointDef, Box2D.Dynamics.Joints.b2JointDef); + b2WeldJointDef.prototype.__super = Box2D.Dynamics.Joints.b2JointDef.prototype; + b2WeldJointDef.b2WeldJointDef = function () { + Box2D.Dynamics.Joints.b2JointDef.b2JointDef.apply(this, arguments); + this.localAnchorA = new b2Vec2(); + this.localAnchorB = new b2Vec2(); + }; + b2WeldJointDef.prototype.b2WeldJointDef = function () { + this.__super.b2JointDef.call(this); + this.type = b2Joint.e_weldJoint; + this.referenceAngle = 0.0; + } + b2WeldJointDef.prototype.Initialize = function (bA, bB, anchor) { + this.bodyA = bA; + this.bodyB = bB; + this.localAnchorA.SetV(this.bodyA.GetLocalPoint(anchor)); + this.localAnchorB.SetV(this.bodyB.GetLocalPoint(anchor)); + this.referenceAngle = this.bodyB.GetAngle() - this.bodyA.GetAngle(); + } +})(); +(function () { + var b2DebugDraw = Box2D.Dynamics.b2DebugDraw; + b2DebugDraw.b2DebugDraw = function () { + this.m_drawScale = 1.0; + this.m_lineThickness = 1.0; + this.m_alpha = 1.0; + this.m_fillAlpha = 1.0; + this.m_xformScale = 1.0; + var __this = this; + //#WORKAROUND + this.m_sprite = { + graphics: { + clear: function () { + __this.m_ctx.clearRect(0, 0, __this.m_ctx.canvas.width, __this.m_ctx.canvas.height) + } + } + }; + }; + b2DebugDraw.prototype._color = function (color, alpha) { + return "rgba(" + ((color & 0xFF0000) >> 16) + "," + ((color & 0xFF00) >> 8) + "," + (color & 0xFF) + "," + alpha + ")"; + }; + b2DebugDraw.prototype.b2DebugDraw = function () { + this.m_drawFlags = 0; + }; + b2DebugDraw.prototype.SetFlags = function (flags) { + if (flags === undefined) flags = 0; + this.m_drawFlags = flags; + }; + b2DebugDraw.prototype.GetFlags = function () { + return this.m_drawFlags; + }; + b2DebugDraw.prototype.AppendFlags = function (flags) { + if (flags === undefined) flags = 0; + this.m_drawFlags |= flags; + }; + b2DebugDraw.prototype.ClearFlags = function (flags) { + if (flags === undefined) flags = 0; + this.m_drawFlags &= ~flags; + }; + b2DebugDraw.prototype.SetSprite = function (sprite) { + this.m_ctx = sprite; + }; + b2DebugDraw.prototype.GetSprite = function () { + return this.m_ctx; + }; + b2DebugDraw.prototype.SetDrawScale = function (drawScale) { + if (drawScale === undefined) drawScale = 0; + this.m_drawScale = drawScale; + }; + b2DebugDraw.prototype.GetDrawScale = function () { + return this.m_drawScale; + }; + b2DebugDraw.prototype.SetLineThickness = function (lineThickness) { + if (lineThickness === undefined) lineThickness = 0; + this.m_lineThickness = lineThickness; + this.m_ctx.strokeWidth = lineThickness; + }; + b2DebugDraw.prototype.GetLineThickness = function () { + return this.m_lineThickness; + }; + b2DebugDraw.prototype.SetAlpha = function (alpha) { + if (alpha === undefined) alpha = 0; + this.m_alpha = alpha; + }; + b2DebugDraw.prototype.GetAlpha = function () { + return this.m_alpha; + }; + b2DebugDraw.prototype.SetFillAlpha = function (alpha) { + if (alpha === undefined) alpha = 0; + this.m_fillAlpha = alpha; + }; + b2DebugDraw.prototype.GetFillAlpha = function () { + return this.m_fillAlpha; + }; + b2DebugDraw.prototype.SetXFormScale = function (xformScale) { + if (xformScale === undefined) xformScale = 0; + this.m_xformScale = xformScale; + }; + b2DebugDraw.prototype.GetXFormScale = function () { + return this.m_xformScale; + }; + b2DebugDraw.prototype.DrawPolygon = function (vertices, vertexCount, color) { + if (!vertexCount) return; + var s = this.m_ctx; + var drawScale = this.m_drawScale; + s.beginPath(); + s.strokeStyle = this._color(color.color, this.m_alpha); + s.moveTo(vertices[0].x * drawScale, vertices[0].y * drawScale); + for (var i = 1; i < vertexCount; i++) { + s.lineTo(vertices[i].x * drawScale, vertices[i].y * drawScale); + } + s.lineTo(vertices[0].x * drawScale, vertices[0].y * drawScale); + s.closePath(); + s.stroke(); + }; + b2DebugDraw.prototype.DrawSolidPolygon = function (vertices, vertexCount, color) { + if (!vertexCount) return; + var s = this.m_ctx; + var drawScale = this.m_drawScale; + s.beginPath(); + s.strokeStyle = this._color(color.color, this.m_alpha); + s.fillStyle = this._color(color.color, this.m_fillAlpha); + s.moveTo(vertices[0].x * drawScale, vertices[0].y * drawScale); + for (var i = 1; i < vertexCount; i++) { + s.lineTo(vertices[i].x * drawScale, vertices[i].y * drawScale); + } + s.lineTo(vertices[0].x * drawScale, vertices[0].y * drawScale); + s.closePath(); + s.fill(); + s.stroke(); + }; + b2DebugDraw.prototype.DrawCircle = function (center, radius, color) { + if (!radius) return; + var s = this.m_ctx; + var drawScale = this.m_drawScale; + s.beginPath(); + s.strokeStyle = this._color(color.color, this.m_alpha); + s.arc(center.x * drawScale, center.y * drawScale, radius * drawScale, 0, Math.PI * 2, true); + s.closePath(); + s.stroke(); + }; + b2DebugDraw.prototype.DrawSolidCircle = function (center, radius, axis, color) { + if (!radius) return; + var s = this.m_ctx, + drawScale = this.m_drawScale, + cx = center.x * drawScale, + cy = center.y * drawScale; + s.moveTo(0, 0); + s.beginPath(); + s.strokeStyle = this._color(color.color, this.m_alpha); + s.fillStyle = this._color(color.color, this.m_fillAlpha); + s.arc(cx, cy, radius * drawScale, 0, Math.PI * 2, true); + s.moveTo(cx, cy); + s.lineTo((center.x + axis.x * radius) * drawScale, (center.y + axis.y * radius) * drawScale); + s.closePath(); + s.fill(); + s.stroke(); + }; + b2DebugDraw.prototype.DrawSegment = function (p1, p2, color) { + var s = this.m_ctx, + drawScale = this.m_drawScale; + s.strokeStyle = this._color(color.color, this.m_alpha); + s.beginPath(); + s.moveTo(p1.x * drawScale, p1.y * drawScale); + s.lineTo(p2.x * drawScale, p2.y * drawScale); + s.closePath(); + s.stroke(); + }; + b2DebugDraw.prototype.DrawTransform = function (xf) { + var s = this.m_ctx, + drawScale = this.m_drawScale; + s.beginPath(); + s.strokeStyle = this._color(0xff0000, this.m_alpha); + s.moveTo(xf.position.x * drawScale, xf.position.y * drawScale); + s.lineTo((xf.position.x + this.m_xformScale * xf.R.col1.x) * drawScale, (xf.position.y + this.m_xformScale * xf.R.col1.y) * drawScale); + + s.strokeStyle = this._color(0xff00, this.m_alpha); + s.moveTo(xf.position.x * drawScale, xf.position.y * drawScale); + s.lineTo((xf.position.x + this.m_xformScale * xf.R.col2.x) * drawScale, (xf.position.y + this.m_xformScale * xf.R.col2.y) * drawScale); + s.closePath(); + s.stroke(); + }; +})(); //post-definitions +var i; +for (i = 0; i < Box2D.postDefs.length; ++i) Box2D.postDefs[i](); +delete Box2D.postDefs; + +if (typeof require !== 'undefined' && typeof module !== 'undefined') { + module.exports = Box2D; +} diff --git a/frameworks/cocos2d-html5/external/chipmunk/chipmunk.js b/frameworks/cocos2d-html5/external/chipmunk/chipmunk.js new file mode 100644 index 0000000..0101792 --- /dev/null +++ b/frameworks/cocos2d-html5/external/chipmunk/chipmunk.js @@ -0,0 +1,6236 @@ +(function(){ +/* Copyright (c) 2007 Scott Lembcke + * + * 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. + */ + +Object.create = Object.create || function(o) { + function F() {} + F.prototype = o; + return new F(); +}; + +//var VERSION = CP_VERSION_MAJOR + "." + CP_VERSION_MINOR + "." + CP_VERSION_RELEASE; + +var cp; +if(typeof exports === 'undefined'){ + cp = {}; + + if(typeof window === 'object'){ + window["cp"] = cp; + } +} else { + cp = exports; +} + +var assert = function(value, message) +{ + if (!value) { + throw new Error('Assertion failed: ' + message); + } +}; + +var assertSoft = function(value, message) +{ + if(!value && console && console.warn) { + console.warn("ASSERTION FAILED: " + message); + if(console.trace) { + console.trace(); + } + } +}; + +var mymin = function(a, b) +{ + return a < b ? a : b; +}; +var mymax = function(a, b) +{ + return a > b ? a : b; +}; + +var min, max; +if (typeof window === 'object' && window.navigator.userAgent.indexOf('Firefox') > -1){ + // On firefox, Math.min and Math.max are really fast: + // http://jsperf.com/math-vs-greater-than/8 + min = Math.min; + max = Math.max; +} else { + // On chrome and safari, Math.min / max are slooow. The ternery operator above is faster + // than the builtins because we only have to deal with 2 arguments that are always numbers. + min = mymin; + max = mymax; +} + +/* The hashpair function takes two numbers and returns a hash code for them. + * Required that hashPair(a, b) === hashPair(b, a). + * Chipmunk's hashPair function is defined as: + * #define CP_HASH_COEF (3344921057ul) + * #define CP_HASH_PAIR(A, B) ((cpHashValue)(A)*CP_HASH_COEF ^ (cpHashValue)(B)*CP_HASH_COEF) + * But thats not suitable in javascript because multiplying by a large number will make the number + * a large float. + * + * The result of hashPair is used as the key in objects, so it returns a string. + */ +var hashPair = function(a, b) +{ + //assert(typeof(a) === 'number', "HashPair used on something not a number"); + return a < b ? a + ' ' + b : b + ' ' + a; +}; + +var deleteObjFromList = function(arr, obj) +{ + for(var i=0; i> 1; + for(var i=1; i maxx || (x == maxx && y > maxy)){ + maxx = x; + maxy = y; + end = i; + } + } + return [start, end]; +}; + +var SWAP = function(arr, idx1, idx2) +{ + var tmp = arr[idx1*2]; + arr[idx1*2] = arr[idx2*2]; + arr[idx2*2] = tmp; + + tmp = arr[idx1*2+1]; + arr[idx1*2+1] = arr[idx2*2+1]; + arr[idx2*2+1] = tmp; +}; + +var QHullPartition = function(verts, offs, count, a, b, tol) +{ + if(count === 0) return 0; + + var max = 0; + var pivot = offs; + + var delta = vsub(b, a); + var valueTol = tol * vlength(delta); + + var head = offs; + for(var tail = offs+count-1; head <= tail;){ + var v = new Vect(verts[head * 2], verts[head * 2 + 1]); + var value = vcross(delta, vsub(v, a)); + if(value > valueTol){ + if(value > max){ + max = value; + pivot = head; + } + + head++; + } else { + SWAP(verts, head, tail); + tail--; + } + } + + // move the new pivot to the front if it's not already there. + if(pivot != offs) SWAP(verts, offs, pivot); + return head - offs; +}; + +var QHullReduce = function(tol, verts, offs, count, a, pivot, b, resultPos) +{ + if(count < 0){ + return 0; + } else if(count == 0) { + verts[resultPos*2] = pivot.x; + verts[resultPos*2+1] = pivot.y; + return 1; + } else { + var left_count = QHullPartition(verts, offs, count, a, pivot, tol); + var left = new Vect(verts[offs*2], verts[offs*2+1]); + var index = QHullReduce(tol, verts, offs + 1, left_count - 1, a, left, pivot, resultPos); + + var pivotPos = resultPos + index++; + verts[pivotPos*2] = pivot.x; + verts[pivotPos*2+1] = pivot.y; + + var right_count = QHullPartition(verts, offs + left_count, count - left_count, pivot, b, tol); + var right = new Vect(verts[(offs+left_count)*2], verts[(offs+left_count)*2+1]); + return index + QHullReduce(tol, verts, offs + left_count + 1, right_count - 1, pivot, right, b, resultPos + index); + } +}; + +// QuickHull seemed like a neat algorithm, and efficient-ish for large input sets. +// My implementation performs an in place reduction using the result array as scratch space. +// +// Pass an Array into result to put the result of the calculation there. Otherwise, pass null +// and the verts list will be edited in-place. +// +// Expects the verts to be described in the same way as cpPolyShape - which is to say, it should +// be a list of [x1,y1,x2,y2,x3,y3,...]. +// +// tolerance is in world coordinates. Eg, 2. +cp.convexHull = function(verts, result, tolerance) +{ + if(result){ + // Copy the line vertexes into the empty part of the result polyline to use as a scratch buffer. + for (var i = 0; i < verts.length; i++){ + result[i] = verts[i]; + } + } else { + // If a result array was not specified, reduce the input instead. + result = verts; + } + + // Degenerate case, all points are the same. + var indexes = loopIndexes(verts); + var start = indexes[0], end = indexes[1]; + if(start == end){ + //if(first) (*first) = 0; + result.length = 2; + return result; + } + + SWAP(result, 0, start); + SWAP(result, 1, end == 0 ? start : end); + + var a = new Vect(result[0], result[1]); + var b = new Vect(result[2], result[3]); + + var count = verts.length >> 1; + //if(first) (*first) = start; + var resultCount = QHullReduce(tolerance, result, 2, count - 2, a, b, a, 1) + 1; + result.length = resultCount*2; + + assertSoft(polyValidate(result), + "Internal error: cpConvexHull() and cpPolyValidate() did not agree." + + "Please report this error with as much info as you can."); + return result; +}; + +/// Clamp @c f to be between @c min and @c max. +var clamp = function(f, minv, maxv) +{ + return min(max(f, minv), maxv); +}; + +/// Clamp @c f to be between 0 and 1. +var clamp01 = function(f) +{ + return max(0, min(f, 1)); +}; + +/// Linearly interpolate (or extrapolate) between @c f1 and @c f2 by @c t percent. +var lerp = function(f1, f2, t) +{ + return f1*(1 - t) + f2*t; +}; + +/// Linearly interpolate from @c f1 to @c f2 by no more than @c d. +var lerpconst = function(f1, f2, d) +{ + return f1 + clamp(f2 - f1, -d, d); +}; + +/* Copyright (c) 2007 Scott Lembcke + * + * 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. + */ + +// I'm using an array tuple here because (at time of writing) its about 3x faster +// than an object on firefox, and the same speed on chrome. + +//var numVects = 0; + +var Vect = cp.Vect = function(x, y) +{ + this.x = x; + this.y = y; + //numVects++; + +// var s = new Error().stack; +// traces[s] = traces[s] ? traces[s]+1 : 1; +}; + +cp.v = function (x,y) { return new Vect(x, y) }; + +var vzero = cp.vzero = new Vect(0,0); + +// The functions below *could* be rewritten to be instance methods on Vect. I don't +// know how that would effect performance. For now, I'm keeping the JS similar to +// the original C code. + +/// Vector dot product. +var vdot = cp.v.dot = function(v1, v2) +{ + return v1.x*v2.x + v1.y*v2.y; +}; + +var vdot2 = function(x1, y1, x2, y2) +{ + return x1*x2 + y1*y2; +}; + +/// Returns the length of v. +var vlength = cp.v.len = function(v) +{ + return Math.sqrt(vdot(v, v)); +}; + +var vlength2 = cp.v.len2 = function(x, y) +{ + return Math.sqrt(x*x + y*y); +}; + +/// Check if two vectors are equal. (Be careful when comparing floating point numbers!) +var veql = cp.v.eql = function(v1, v2) +{ + return (v1.x === v2.x && v1.y === v2.y); +}; + +/// Add two vectors +var vadd = cp.v.add = function(v1, v2) +{ + return new Vect(v1.x + v2.x, v1.y + v2.y); +}; + +Vect.prototype.add = function(v2) +{ + this.x += v2.x; + this.y += v2.y; + return this; +}; + +/// Subtract two vectors. +var vsub = cp.v.sub = function(v1, v2) +{ + return new Vect(v1.x - v2.x, v1.y - v2.y); +}; + +Vect.prototype.sub = function(v2) +{ + this.x -= v2.x; + this.y -= v2.y; + return this; +}; + +/// Negate a vector. +var vneg = cp.v.neg = function(v) +{ + return new Vect(-v.x, -v.y); +}; + +Vect.prototype.neg = function() +{ + this.x = -this.x; + this.y = -this.y; + return this; +}; + +/// Scalar multiplication. +var vmult = cp.v.mult = function(v, s) +{ + return new Vect(v.x*s, v.y*s); +}; + +Vect.prototype.mult = function(s) +{ + this.x *= s; + this.y *= s; + return this; +}; + +/// 2D vector cross product analog. +/// The cross product of 2D vectors results in a 3D vector with only a z component. +/// This function returns the magnitude of the z value. +var vcross = cp.v.cross = function(v1, v2) +{ + return v1.x*v2.y - v1.y*v2.x; +}; + +var vcross2 = function(x1, y1, x2, y2) +{ + return x1*y2 - y1*x2; +}; + +/// Returns a perpendicular vector. (90 degree rotation) +var vperp = cp.v.perp = function(v) +{ + return new Vect(-v.y, v.x); +}; + +/// Returns a perpendicular vector. (-90 degree rotation) +var vpvrperp = cp.v.pvrperp = function(v) +{ + return new Vect(v.y, -v.x); +}; + +/// Returns the vector projection of v1 onto v2. +var vproject = cp.v.project = function(v1, v2) +{ + return vmult(v2, vdot(v1, v2)/vlengthsq(v2)); +}; + +Vect.prototype.project = function(v2) +{ + this.mult(vdot(this, v2) / vlengthsq(v2)); + return this; +}; + +/// Uses complex number multiplication to rotate v1 by v2. Scaling will occur if v1 is not a unit vector. +var vrotate = cp.v.rotate = function(v1, v2) +{ + return new Vect(v1.x*v2.x - v1.y*v2.y, v1.x*v2.y + v1.y*v2.x); +}; + +Vect.prototype.rotate = function(v2) +{ + this.x = this.x * v2.x - this.y * v2.y; + this.y = this.x * v2.y + this.y * v2.x; + return this; +}; + +/// Inverse of vrotate(). +var vunrotate = cp.v.unrotate = function(v1, v2) +{ + return new Vect(v1.x*v2.x + v1.y*v2.y, v1.y*v2.x - v1.x*v2.y); +}; + +/// Returns the squared length of v. Faster than vlength() when you only need to compare lengths. +var vlengthsq = cp.v.lengthsq = function(v) +{ + return vdot(v, v); +}; + +var vlengthsq2 = cp.v.lengthsq2 = function(x, y) +{ + return x*x + y*y; +}; + +/// Linearly interpolate between v1 and v2. +var vlerp = cp.v.lerp = function(v1, v2, t) +{ + return vadd(vmult(v1, 1 - t), vmult(v2, t)); +}; + +/// Returns a normalized copy of v. +var vnormalize = cp.v.normalize = function(v) +{ + return vmult(v, 1/vlength(v)); +}; + +/// Returns a normalized copy of v or vzero if v was already vzero. Protects against divide by zero errors. +var vnormalize_safe = cp.v.normalize_safe = function(v) +{ + return (v.x === 0 && v.y === 0 ? vzero : vnormalize(v)); +}; + +/// Clamp v to length len. +var vclamp = cp.v.clamp = function(v, len) +{ + return (vdot(v,v) > len*len) ? vmult(vnormalize(v), len) : v; +}; + +/// Linearly interpolate between v1 towards v2 by distance d. +var vlerpconst = cp.v.lerpconst = function(v1, v2, d) +{ + return vadd(v1, vclamp(vsub(v2, v1), d)); +}; + +/// Returns the distance between v1 and v2. +var vdist = cp.v.dist = function(v1, v2) +{ + return vlength(vsub(v1, v2)); +}; + +/// Returns the squared distance between v1 and v2. Faster than vdist() when you only need to compare distances. +var vdistsq = cp.v.distsq = function(v1, v2) +{ + return vlengthsq(vsub(v1, v2)); +}; + +/// Returns true if the distance between v1 and v2 is less than dist. +var vnear = cp.v.near = function(v1, v2, dist) +{ + return vdistsq(v1, v2) < dist*dist; +}; + +/// Spherical linearly interpolate between v1 and v2. +var vslerp = cp.v.slerp = function(v1, v2, t) +{ + var omega = Math.acos(vdot(v1, v2)); + + if(omega) { + var denom = 1/Math.sin(omega); + return vadd(vmult(v1, Math.sin((1 - t)*omega)*denom), vmult(v2, Math.sin(t*omega)*denom)); + } else { + return v1; + } +}; + +/// Spherical linearly interpolate between v1 towards v2 by no more than angle a radians +var vslerpconst = cp.v.slerpconst = function(v1, v2, a) +{ + var angle = Math.acos(vdot(v1, v2)); + return vslerp(v1, v2, min(a, angle)/angle); +}; + +/// Returns the unit length vector for the given angle (in radians). +var vforangle = cp.v.forangle = function(a) +{ + return new Vect(Math.cos(a), Math.sin(a)); +}; + +/// Returns the angular direction v is pointing in (in radians). +var vtoangle = cp.v.toangle = function(v) +{ + return Math.atan2(v.y, v.x); +}; + +/// Returns a string representation of v. Intended mostly for debugging purposes and not production use. +var vstr = cp.v.str = function(v) +{ + return "(" + v.x.toFixed(3) + ", " + v.y.toFixed(3) + ")"; +}; + +/* Copyright (c) 2007 Scott Lembcke + * + * 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. + */ + +/// Chipmunk's axis-aligned 2D bounding box type along with a few handy routines. + +var numBB = 0; + +// Bounding boxes are JS objects with {l, b, r, t} = left, bottom, right, top, respectively. +var BB = cp.BB = function(l, b, r, t) +{ + this.l = l; + this.b = b; + this.r = r; + this.t = t; + + numBB++; +}; + +cp.bb = function(l, b, r, t) { return new BB(l, b, r, t); }; + +var bbNewForCircle = function(p, r) +{ + return new BB( + p.x - r, + p.y - r, + p.x + r, + p.y + r + ); +}; + +/// Returns true if @c a and @c b intersect. +var bbIntersects = function(a, b) +{ + return (a.l <= b.r && b.l <= a.r && a.b <= b.t && b.b <= a.t); +}; +var bbIntersects2 = function(bb, l, b, r, t) +{ + return (bb.l <= r && l <= bb.r && bb.b <= t && b <= bb.t); +}; + +/// Returns true if @c other lies completely within @c bb. +var bbContainsBB = function(bb, other) +{ + return (bb.l <= other.l && bb.r >= other.r && bb.b <= other.b && bb.t >= other.t); +}; + +/// Returns true if @c bb contains @c v. +var bbContainsVect = function(bb, v) +{ + return (bb.l <= v.x && bb.r >= v.x && bb.b <= v.y && bb.t >= v.y); +}; +var bbContainsVect2 = function(l, b, r, t, v) +{ + return (l <= v.x && r >= v.x && b <= v.y && t >= v.y); +}; + +/// Returns a bounding box that holds both bounding boxes. +var bbMerge = function(a, b){ + return new BB( + min(a.l, b.l), + min(a.b, b.b), + max(a.r, b.r), + max(a.t, b.t) + ); +}; + +/// Returns a bounding box that holds both @c bb and @c v. +var bbExpand = function(bb, v){ + return new BB( + min(bb.l, v.x), + min(bb.b, v.y), + max(bb.r, v.x), + max(bb.t, v.y) + ); +}; + +/// Returns the area of the bounding box. +var bbArea = function(bb) +{ + return (bb.r - bb.l)*(bb.t - bb.b); +}; + +/// Merges @c a and @c b and returns the area of the merged bounding box. +var bbMergedArea = function(a, b) +{ + return (max(a.r, b.r) - min(a.l, b.l))*(max(a.t, b.t) - min(a.b, b.b)); +}; + +var bbMergedArea2 = function(bb, l, b, r, t) +{ + return (max(bb.r, r) - min(bb.l, l))*(max(bb.t, t) - min(bb.b, b)); +}; + +/// Return true if the bounding box intersects the line segment with ends @c a and @c b. +var bbIntersectsSegment = function(bb, a, b) +{ + return (bbSegmentQuery(bb, a, b) != Infinity); +}; + +/// Clamp a vector to a bounding box. +var bbClampVect = function(bb, v) +{ + var x = min(max(bb.l, v.x), bb.r); + var y = min(max(bb.b, v.y), bb.t); + return new Vect(x, y); +}; + +// TODO edge case issue +/// Wrap a vector to a bounding box. +var bbWrapVect = function(bb, v) +{ + var ix = Math.abs(bb.r - bb.l); + var modx = (v.x - bb.l) % ix; + var x = (modx > 0) ? modx : modx + ix; + + var iy = Math.abs(bb.t - bb.b); + var mody = (v.y - bb.b) % iy; + var y = (mody > 0) ? mody : mody + iy; + + return new Vect(x + bb.l, y + bb.b); +}; +/* Copyright (c) 2007 Scott Lembcke + * + * 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. + */ + +/// Segment query info struct. +/* These are created using literals where needed. +typedef struct cpSegmentQueryInfo { + /// The shape that was hit, null if no collision occurred. + cpShape *shape; + /// The normalized distance along the query segment in the range [0, 1]. + cpFloat t; + /// The normal of the surface hit. + cpVect n; +} cpSegmentQueryInfo; +*/ + +var shapeIDCounter = 0; + +var CP_NO_GROUP = cp.NO_GROUP = 0; +var CP_ALL_LAYERS = cp.ALL_LAYERS = ~0; +var CP_ALL_CATEGORIES = cp.ALL_CATEGORIES = ~0; // Chipmunk v7.0 compatibility + +cp.resetShapeIdCounter = function() +{ + shapeIDCounter = 0; +}; + +/// The cpShape struct defines the shape of a rigid body. +// +/// Opaque collision shape struct. Do not create directly - instead use +/// PolyShape, CircleShape and SegmentShape. +var Shape = cp.Shape = function(body) { + /// The rigid body this collision shape is attached to. + this.body = body; + + /// The current bounding box of the shape. + this.bb_l = this.bb_b = this.bb_r = this.bb_t = 0; + + this.hashid = shapeIDCounter++; + + /// Sensor flag. + /// Sensor shapes call collision callbacks but don't produce collisions. + this.sensor = false; + + /// Coefficient of restitution. (elasticity) + this.e = 0; + /// Coefficient of friction. + this.u = 0; + /// Surface velocity used when solving for friction. + this.surface_v = vzero; + + /// Collision type of this shape used when picking collision handlers. + this.collision_type = 0; + /// Group of this shape. Shapes in the same group don't collide. + this.group = 0; + // Layer bitmask for this shape. Shapes only collide if the bitwise and of their layers is non-zero. + this.layers = CP_ALL_LAYERS; + + this.space = null; + + // Copy the collision code from the prototype into the actual object. This makes collision + // function lookups slightly faster. + this.collisionCode = this.collisionCode; +}; + +Shape.prototype.setElasticity = function(e) { this.e = e; }; +Shape.prototype.setFriction = function(u) { this.body.activate(); this.u = u; }; +Shape.prototype.setLayers = function(layers) { this.body.activate(); this.layers = layers; }; +Shape.prototype.setSensor = function(sensor) { this.body.activate(); this.sensor = sensor; }; +Shape.prototype.setCollisionType = function(collision_type) { this.body.activate(); this.collision_type = collision_type; }; +Shape.prototype.getBody = function() { return this.body; }; + +Shape.prototype.active = function() +{ +// return shape->prev || (shape->body && shape->body->shapeList == shape); + return this.body && this.body.shapeList.indexOf(this) !== -1; +}; + +Shape.prototype.setBody = function(body) +{ + assert(!this.active(), "You cannot change the body on an active shape. You must remove the shape from the space before changing the body."); + this.body = body; +}; + +Shape.prototype.cacheBB = function() +{ + return this.update(this.body.p, this.body.rot); +}; + +Shape.prototype.update = function(pos, rot) +{ + assert(!isNaN(rot.x), 'Rotation is NaN'); + assert(!isNaN(pos.x), 'Position is NaN'); + this.cacheData(pos, rot); +}; + +Shape.prototype.pointQuery = function(p) +{ + var info = this.nearestPointQuery(p); + if (info.d < 0) return info; +}; + +Shape.prototype.getBB = function() +{ + return new BB(this.bb_l, this.bb_b, this.bb_r, this.bb_t); +}; + +/* Not implemented - all these getters and setters. Just edit the object directly. +CP_DefineShapeStructGetter(cpBody*, body, Body); +void cpShapeSetBody(cpShape *shape, cpBody *body); + +CP_DefineShapeStructGetter(cpBB, bb, BB); +CP_DefineShapeStructProperty(cpBool, sensor, Sensor, cpTrue); +CP_DefineShapeStructProperty(cpFloat, e, Elasticity, cpFalse); +CP_DefineShapeStructProperty(cpFloat, u, Friction, cpTrue); +CP_DefineShapeStructProperty(cpVect, surface_v, SurfaceVelocity, cpTrue); +CP_DefineShapeStructProperty(cpDataPointer, data, UserData, cpFalse); +CP_DefineShapeStructProperty(cpCollisionType, collision_type, CollisionType, cpTrue); +CP_DefineShapeStructProperty(cpGroup, group, Group, cpTrue); +CP_DefineShapeStructProperty(cpLayers, layers, Layers, cpTrue); +*/ + +/// Extended point query info struct. Returned from calling pointQuery on a shape. +var PointQueryExtendedInfo = function(shape) +{ + /// Shape that was hit, NULL if no collision occurred. + this.shape = shape; + /// Depth of the point inside the shape. + this.d = Infinity; + /// Direction of minimum norm to the shape's surface. + this.n = vzero; +}; + +var NearestPointQueryInfo = function(shape, p, d) +{ + /// The nearest shape, NULL if no shape was within range. + this.shape = shape; + /// The closest point on the shape's surface. (in world space coordinates) + this.p = p; + /// The distance to the point. The distance is negative if the point is inside the shape. + this.d = d; +}; + +var SegmentQueryInfo = function(shape, t, n) +{ + /// The shape that was hit, NULL if no collision occurred. + this.shape = shape; + /// The normalized distance along the query segment in the range [0, 1]. + this.t = t; + /// The normal of the surface hit. + this.n = n; +}; + +/// Get the hit point for a segment query. +SegmentQueryInfo.prototype.hitPoint = function(start, end) +{ + return vlerp(start, end, this.t); +}; + +/// Get the hit distance for a segment query. +SegmentQueryInfo.prototype.hitDist = function(start, end) +{ + return vdist(start, end) * this.t; +}; + +// Circles. + +var CircleShape = cp.CircleShape = function(body, radius, offset) +{ + this.c = this.tc = offset; + this.r = radius; + + this.type = 'circle'; + + Shape.call(this, body); +}; + +CircleShape.prototype = Object.create(Shape.prototype); + +CircleShape.prototype.cacheData = function(p, rot) +{ + //var c = this.tc = vadd(p, vrotate(this.c, rot)); + var c = this.tc = vrotate(this.c, rot).add(p); + //this.bb = bbNewForCircle(c, this.r); + var r = this.r; + this.bb_l = c.x - r; + this.bb_b = c.y - r; + this.bb_r = c.x + r; + this.bb_t = c.y + r; +}; + +/// Test if a point lies within a shape. +/*CircleShape.prototype.pointQuery = function(p) +{ + var delta = vsub(p, this.tc); + var distsq = vlengthsq(delta); + var r = this.r; + + if(distsq < r*r){ + var info = new PointQueryExtendedInfo(this); + + var dist = Math.sqrt(distsq); + info.d = r - dist; + info.n = vmult(delta, 1/dist); + return info; + } +};*/ + +CircleShape.prototype.nearestPointQuery = function(p) +{ + var deltax = p.x - this.tc.x; + var deltay = p.y - this.tc.y; + var d = vlength2(deltax, deltay); + var r = this.r; + + var nearestp = new Vect(this.tc.x + deltax * r/d, this.tc.y + deltay * r/d); + return new NearestPointQueryInfo(this, nearestp, d - r); +}; + +var circleSegmentQuery = function(shape, center, r, a, b, info) +{ + // offset the line to be relative to the circle + a = vsub(a, center); + b = vsub(b, center); + + var qa = vdot(a, a) - 2*vdot(a, b) + vdot(b, b); + var qb = -2*vdot(a, a) + 2*vdot(a, b); + var qc = vdot(a, a) - r*r; + + var det = qb*qb - 4*qa*qc; + + if(det >= 0) + { + var t = (-qb - Math.sqrt(det))/(2*qa); + if(0 <= t && t <= 1){ + return new SegmentQueryInfo(shape, t, vnormalize(vlerp(a, b, t))); + } + } +}; + +CircleShape.prototype.segmentQuery = function(a, b) +{ + return circleSegmentQuery(this, this.tc, this.r, a, b); +}; + +// The C API has these, and also getters. Its not idiomatic to +// write getters and setters in JS. +/* +CircleShape.prototype.setRadius = function(radius) +{ + this.r = radius; +} + +CircleShape.prototype.setOffset = function(offset) +{ + this.c = offset; +}*/ + +// Segment shape + +var SegmentShape = cp.SegmentShape = function(body, a, b, r) +{ + this.a = a; + this.b = b; + this.n = vperp(vnormalize(vsub(b, a))); + + this.ta = this.tb = this.tn = null; + + this.r = r; + + this.a_tangent = vzero; + this.b_tangent = vzero; + + this.type = 'segment'; + Shape.call(this, body); +}; + +SegmentShape.prototype = Object.create(Shape.prototype); + +SegmentShape.prototype.cacheData = function(p, rot) +{ + this.ta = vadd(p, vrotate(this.a, rot)); + this.tb = vadd(p, vrotate(this.b, rot)); + this.tn = vrotate(this.n, rot); + + var l,r,b,t; + + if(this.ta.x < this.tb.x){ + l = this.ta.x; + r = this.tb.x; + } else { + l = this.tb.x; + r = this.ta.x; + } + + if(this.ta.y < this.tb.y){ + b = this.ta.y; + t = this.tb.y; + } else { + b = this.tb.y; + t = this.ta.y; + } + + var rad = this.r; + + this.bb_l = l - rad; + this.bb_b = b - rad; + this.bb_r = r + rad; + this.bb_t = t + rad; +}; + +SegmentShape.prototype.nearestPointQuery = function(p) +{ + var closest = closestPointOnSegment(p, this.ta, this.tb); + + var deltax = p.x - closest.x; + var deltay = p.y - closest.y; + var d = vlength2(deltax, deltay); + var r = this.r; + + var nearestp = (d ? vadd(closest, vmult(new Vect(deltax, deltay), r/d)) : closest); + return new NearestPointQueryInfo(this, nearestp, d - r); +}; + +SegmentShape.prototype.segmentQuery = function(a, b) +{ + var n = this.tn; + var d = vdot(vsub(this.ta, a), n); + var r = this.r; + + var flipped_n = (d > 0 ? vneg(n) : n); + var n_offset = vsub(vmult(flipped_n, r), a); + + var seg_a = vadd(this.ta, n_offset); + var seg_b = vadd(this.tb, n_offset); + var delta = vsub(b, a); + + if(vcross(delta, seg_a)*vcross(delta, seg_b) <= 0){ + var d_offset = d + (d > 0 ? -r : r); + var ad = -d_offset; + var bd = vdot(delta, n) - d_offset; + + if(ad*bd < 0){ + return new SegmentQueryInfo(this, ad/(ad - bd), flipped_n); + } + } else if(r !== 0){ + var info1 = circleSegmentQuery(this, this.ta, this.r, a, b); + var info2 = circleSegmentQuery(this, this.tb, this.r, a, b); + + if (info1){ + return info2 && info2.t < info1.t ? info2 : info1; + } else { + return info2; + } + } +}; + +SegmentShape.prototype.setNeighbors = function(prev, next) +{ + this.a_tangent = vsub(prev, this.a); + this.b_tangent = vsub(next, this.b); +}; + +SegmentShape.prototype.setEndpoints = function(a, b) +{ + this.a = a; + this.b = b; + this.n = vperp(vnormalize(vsub(b, a))); +}; + +/* +cpSegmentShapeSetRadius(cpShape *shape, cpFloat radius) +{ + this.r = radius; +}*/ + +/* +CP_DeclareShapeGetter(cpSegmentShape, cpVect, A); +CP_DeclareShapeGetter(cpSegmentShape, cpVect, B); +CP_DeclareShapeGetter(cpSegmentShape, cpVect, Normal); +CP_DeclareShapeGetter(cpSegmentShape, cpFloat, Radius); +*/ + +/* Copyright (c) 2007 Scott Lembcke + * + * 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. + */ + +/// Check that a set of vertexes is convex and has a clockwise winding. +var polyValidate = function(verts) +{ + var len = verts.length; + for(var i=0; i 0){ + if(vcross2(bx - ax, by - ay, cx - bx, cy - by) > 0){ + return false; + } + } + + return true; +}; + +/// Initialize a polygon shape. +/// The vertexes must be convex and have a clockwise winding. +var PolyShape = cp.PolyShape = function(body, verts, offset) +{ + this.setVerts(verts, offset); + this.type = 'poly'; + Shape.call(this, body); +}; + +PolyShape.prototype = Object.create(Shape.prototype); + +var SplittingPlane = function(n, d) +{ + this.n = n; + this.d = d; +}; + +SplittingPlane.prototype.compare = function(v) +{ + return vdot(this.n, v) - this.d; +}; + +PolyShape.prototype.setVerts = function(verts, offset) +{ + assert(verts.length >= 4, "Polygons require some verts"); + assert(typeof(verts[0]) === 'number', + 'Polygon verticies should be specified in a flattened list (eg [x1,y1,x2,y2,x3,y3,...])'); + + // Fail if the user attempts to pass a concave poly, or a bad winding. + assert(polyValidate(verts), "Polygon is concave or has a reversed winding. Consider using cpConvexHull()"); + + var len = verts.length; + var numVerts = len >> 1; + + // This a pretty bad way to do this in javascript. As a first pass, I want to keep + // the code similar to the C. + this.verts = new Array(len); + this.tVerts = new Array(len); + this.planes = new Array(numVerts); + this.tPlanes = new Array(numVerts); + + for(var i=0; i>1] = new SplittingPlane(n, vdot2(n.x, n.y, ax, ay)); + this.tPlanes[i>>1] = new SplittingPlane(new Vect(0,0), 0); + } +}; + +/// Initialize a box shaped polygon shape. +var BoxShape = cp.BoxShape = function(body, width, height) +{ + var hw = width/2; + var hh = height/2; + + return BoxShape2(body, new BB(-hw, -hh, hw, hh)); +}; + +/// Initialize an offset box shaped polygon shape. +var BoxShape2 = cp.BoxShape2 = function(body, box) +{ + var verts = [ + box.l, box.b, + box.l, box.t, + box.r, box.t, + box.r, box.b + ]; + + return new PolyShape(body, verts, vzero); +}; + +PolyShape.prototype.transformVerts = function(p, rot) +{ + var src = this.verts; + var dst = this.tVerts; + + var l = Infinity, r = -Infinity; + var b = Infinity, t = -Infinity; + + for(var i=0; i (' + vx + ',' + vy + ')'); + + dst[i] = vx; + dst[i+1] = vy; + + l = min(l, vx); + r = max(r, vx); + b = min(b, vy); + t = max(t, vy); + } + + this.bb_l = l; + this.bb_b = b; + this.bb_r = r; + this.bb_t = t; +}; + +PolyShape.prototype.transformAxes = function(p, rot) +{ + var src = this.planes; + var dst = this.tPlanes; + + for(var i=0; i 0) outside = true; + + var v1x = verts[i*2]; + var v1y = verts[i*2 + 1]; + var closest = closestPointOnSegment2(p.x, p.y, v0x, v0y, v1x, v1y); + + var dist = vdist(p, closest); + if(dist < minDist){ + minDist = dist; + closestPoint = closest; + } + + v0x = v1x; + v0y = v1y; + } + + return new NearestPointQueryInfo(this, closestPoint, (outside ? minDist : -minDist)); +}; + +PolyShape.prototype.segmentQuery = function(a, b) +{ + var axes = this.tPlanes; + var verts = this.tVerts; + var numVerts = axes.length; + var len = numVerts * 2; + + for(var i=0; i an) continue; + + var bn = vdot(b, n); + var t = (axes[i].d - an)/(bn - an); + if(t < 0 || 1 < t) continue; + + var point = vlerp(a, b, t); + var dt = -vcross(n, point); + var dtMin = -vcross2(n.x, n.y, verts[i*2], verts[i*2+1]); + var dtMax = -vcross2(n.x, n.y, verts[(i*2+2)%len], verts[(i*2+3)%len]); + + if(dtMin <= dt && dt <= dtMax){ + // josephg: In the original C code, this function keeps + // looping through axes after finding a match. I *think* + // this code is equivalent... + return new SegmentQueryInfo(this, t, n); + } + } +}; + +PolyShape.prototype.valueOnAxis = function(n, d) +{ + var verts = this.tVerts; + var m = vdot2(n.x, n.y, verts[0], verts[1]); + + for(var i=2; i 0) return false; + } + + return true; +}; + +PolyShape.prototype.containsVertPartial = function(vx, vy, n) +{ + var planes = this.tPlanes; + + for(var i=0; i 0) return false; + } + + return true; +}; + +// These methods are provided for API compatibility with Chipmunk. I recommend against using +// them - just access the poly.verts list directly. +PolyShape.prototype.getNumVerts = function() { return this.verts.length / 2; }; +PolyShape.prototype.getCount = PolyShape.prototype.getNumVerts; // v7.0 compatibility +PolyShape.prototype.getVert = function(i) +{ + return new Vect(this.verts[i * 2], this.verts[i * 2 + 1]); +}; + +/* Copyright (c) 2007 Scott Lembcke + * + * 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. + */ + +/// @defgroup cpBody cpBody +/// Chipmunk's rigid body type. Rigid bodies hold the physical properties of an object like +/// it's mass, and position and velocity of it's center of gravity. They don't have an shape on their own. +/// They are given a shape by creating collision shapes (cpShape) that point to the body. +/// @{ + +var Body = cp.Body = function(m, i) { + /// Mass of the body. + /// Must agree with cpBody.m_inv! Use body.setMass() when changing the mass for this reason. + //this.m; + /// Mass inverse. + //this.m_inv; + + /// Moment of inertia of the body. + /// Must agree with cpBody.i_inv! Use body.setMoment() when changing the moment for this reason. + //this.i; + /// Moment of inertia inverse. + //this.i_inv; + + /// Position of the rigid body's center of gravity. + this.p = new Vect(0,0); + /// Velocity of the rigid body's center of gravity. + this.vx = this.vy = 0; + /// Force acting on the rigid body's center of gravity. + this.f = new Vect(0,0); + + /// Rotation of the body around it's center of gravity in radians. + /// Must agree with cpBody.rot! Use cpBodySetAngle() when changing the angle for this reason. + //this.a; + /// Angular velocity of the body around it's center of gravity in radians/second. + this.w = 0; + /// Torque applied to the body around it's center of gravity. + this.t = 0; + + /// Cached unit length vector representing the angle of the body. + /// Used for fast rotations using cpvrotate(). + //cpVect rot; + + /// Maximum velocity allowed when updating the velocity. + this.v_limit = Infinity; + /// Maximum rotational rate (in radians/second) allowed when updating the angular velocity. + this.w_limit = Infinity; + + // This stuff is all private. + this.v_biasx = this.v_biasy = 0; + this.w_bias = 0; + + this.space = null; + + this.shapeList = []; + this.arbiterList = null; // These are both wacky linked lists. + this.constraintList = null; + + // This stuff is used to track information on the collision graph. + this.nodeRoot = null; + this.nodeNext = null; + this.nodeIdleTime = 0; + + // Set this.m and this.m_inv + this.setMass(m); + + // Set this.i and this.i_inv + this.setMoment(i); + + // Set this.a and this.rot + this.rot = new Vect(0,0); + this.setAngle(0); +}; + +// I wonder if this should use the constructor style like Body... +var createStaticBody = function() +{ + var body = new Body(Infinity, Infinity); + body.nodeIdleTime = Infinity; + + return body; +}; + cp.StaticBody = createStaticBody; + +if (typeof DEBUG !== 'undefined' && DEBUG) { + var v_assert_nan = function(v, message){assert(v.x == v.x && v.y == v.y, message); }; + var v_assert_infinite = function(v, message){assert(Math.abs(v.x) !== Infinity && Math.abs(v.y) !== Infinity, message);}; + var v_assert_sane = function(v, message){v_assert_nan(v, message); v_assert_infinite(v, message);}; + + Body.prototype.sanityCheck = function() + { + assert(this.m === this.m && this.m_inv === this.m_inv, "Body's mass is invalid."); + assert(this.i === this.i && this.i_inv === this.i_inv, "Body's moment is invalid."); + + v_assert_sane(this.p, "Body's position is invalid."); + v_assert_sane(this.f, "Body's force is invalid."); + assert(this.vx === this.vx && Math.abs(this.vx) !== Infinity, "Body's velocity is invalid."); + assert(this.vy === this.vy && Math.abs(this.vy) !== Infinity, "Body's velocity is invalid."); + + assert(this.a === this.a && Math.abs(this.a) !== Infinity, "Body's angle is invalid."); + assert(this.w === this.w && Math.abs(this.w) !== Infinity, "Body's angular velocity is invalid."); + assert(this.t === this.t && Math.abs(this.t) !== Infinity, "Body's torque is invalid."); + + v_assert_sane(this.rot, "Body's rotation vector is invalid."); + + assert(this.v_limit === this.v_limit, "Body's velocity limit is invalid."); + assert(this.w_limit === this.w_limit, "Body's angular velocity limit is invalid."); + }; +} else { + Body.prototype.sanityCheck = function(){}; +} + +Body.prototype.getPos = function() { return this.p; }; +Body.prototype.getVel = function() { return new Vect(this.vx, this.vy); }; +Body.prototype.getAngVel = function() { return this.w; }; +// chipmunk v7.0 compatibility +Body.prototype.getPosition = Body.prototype.getPos; +Body.prototype.getVelocity = Body.prototype.getVel; +Body.prototype.getAngularVelocity = Body.prototype.getAngVel; +Body.prototype.getCenterOfGravity = function() { + // FIXME: what is the best way to calculate the center of gravity? + // Needed for Chipmunk v7.0 compatibility + return this.p; +}; + +/// Returns true if the body is sleeping. +Body.prototype.isSleeping = function() +{ + return this.nodeRoot !== null; +}; + +/// Returns true if the body is static. +Body.prototype.isStatic = function() +{ + return this.nodeIdleTime === Infinity; +}; + +/// Returns true if the body has not been added to a space. +Body.prototype.isRogue = function() +{ + return this.space === null; +}; + +// It would be nicer to use defineProperty for this, but its about 30x slower: +// http://jsperf.com/defineproperty-vs-setter +Body.prototype.setMass = function(mass) +{ + assert(mass > 0, "Mass must be positive and non-zero."); + + //activate is defined in cpSpaceComponent + this.activate(); + this.m = mass; + this.m_inv = 1/mass; +}; + +Body.prototype.setMoment = function(moment) +{ + assert(moment > 0, "Moment of Inertia must be positive and non-zero."); + + this.activate(); + this.i = moment; + this.i_inv = 1/moment; +}; + +Body.prototype.addShape = function(shape) +{ + this.shapeList.push(shape); +}; + +Body.prototype.removeShape = function(shape) +{ + // This implementation has a linear time complexity with the number of shapes. + // The original implementation used linked lists instead, which might be faster if + // you're constantly editing the shape of a body. I expect most bodies will never + // have their shape edited, so I'm just going to use the simplest possible implemention. + deleteObjFromList(this.shapeList, shape); +}; + +var filterConstraints = function(node, body, filter) +{ + if(node === filter){ + return node.next(body); + } else if(node.a === body){ + node.next_a = filterConstraints(node.next_a, body, filter); + } else { + node.next_b = filterConstraints(node.next_b, body, filter); + } + + return node; +}; + +Body.prototype.removeConstraint = function(constraint) +{ + // The constraint must be in the constraints list when this is called. + this.constraintList = filterConstraints(this.constraintList, this, constraint); +}; + +Body.prototype.setPos = function(pos) +{ + this.activate(); + this.sanityCheck(); + // If I allow the position to be set to vzero, vzero will get changed. + if (pos === vzero) { + pos = cp.v(0,0); + } + this.p = pos; +}; + +Body.prototype.setVel = function(velocity) +{ + this.activate(); + this.vx = velocity.x; + this.vy = velocity.y; +}; + +Body.prototype.setAngVel = function(w) +{ + this.activate(); + this.w = w; +}; + +Body.prototype.setAngleInternal = function(angle) +{ + assert(!isNaN(angle), "Internal Error: Attempting to set body's angle to NaN"); + this.a = angle;//fmod(a, (cpFloat)M_PI*2.0f); + + //this.rot = vforangle(angle); + this.rot.x = Math.cos(angle); + this.rot.y = Math.sin(angle); +}; + +Body.prototype.setAngle = function(angle) +{ + this.activate(); + this.sanityCheck(); + this.setAngleInternal(angle); +}; + +Body.prototype.velocity_func = function(gravity, damping, dt) +{ + //this.v = vclamp(vadd(vmult(this.v, damping), vmult(vadd(gravity, vmult(this.f, this.m_inv)), dt)), this.v_limit); + var vx = this.vx * damping + (gravity.x + this.f.x * this.m_inv) * dt; + var vy = this.vy * damping + (gravity.y + this.f.y * this.m_inv) * dt; + + //var v = vclamp(new Vect(vx, vy), this.v_limit); + //this.vx = v.x; this.vy = v.y; + var v_limit = this.v_limit; + var lensq = vx * vx + vy * vy; + var scale = (lensq > v_limit*v_limit) ? v_limit / Math.sqrt(lensq) : 1; + this.vx = vx * scale; + this.vy = vy * scale; + + var w_limit = this.w_limit; + this.w = clamp(this.w*damping + this.t*this.i_inv*dt, -w_limit, w_limit); + + this.sanityCheck(); +}; + +Body.prototype.position_func = function(dt) +{ + //this.p = vadd(this.p, vmult(vadd(this.v, this.v_bias), dt)); + + //this.p = this.p + (this.v + this.v_bias) * dt; + this.p.x += (this.vx + this.v_biasx) * dt; + this.p.y += (this.vy + this.v_biasy) * dt; + + this.setAngleInternal(this.a + (this.w + this.w_bias)*dt); + + this.v_biasx = this.v_biasy = 0; + this.w_bias = 0; + + this.sanityCheck(); +}; + +Body.prototype.resetForces = function() +{ + this.activate(); + this.f = new Vect(0,0); + this.t = 0; +}; + +Body.prototype.applyForce = function(force, r) +{ + this.activate(); + this.f = vadd(this.f, force); + this.t += vcross(r, force); +}; + +Body.prototype.applyImpulse = function(j, r) +{ + this.activate(); + apply_impulse(this, j.x, j.y, r); +}; + +Body.prototype.getVelAtPoint = function(r) +{ + return vadd(new Vect(this.vx, this.vy), vmult(vperp(r), this.w)); +}; + +/// Get the velocity on a body (in world units) at a point on the body in world coordinates. +Body.prototype.getVelAtWorldPoint = function(point) +{ + return this.getVelAtPoint(vsub(point, this.p)); +}; + +/// Get the velocity on a body (in world units) at a point on the body in local coordinates. +Body.prototype.getVelAtLocalPoint = function(point) +{ + return this.getVelAtPoint(vrotate(point, this.rot)); +}; + +Body.prototype.eachShape = function(func) +{ + for(var i = 0, len = this.shapeList.length; i < len; i++) { + func(this.shapeList[i]); + } +}; + +Body.prototype.eachConstraint = function(func) +{ + var constraint = this.constraintList; + while(constraint) { + var next = constraint.next(this); + func(constraint); + constraint = next; + } +}; + +Body.prototype.eachArbiter = function(func) +{ + var arb = this.arbiterList; + while(arb){ + var next = arb.next(this); + + arb.swappedColl = (this === arb.body_b); + func(arb); + + arb = next; + } +}; + +/// Convert body relative/local coordinates to absolute/world coordinates. +Body.prototype.local2World = function(v) +{ + return vadd(this.p, vrotate(v, this.rot)); +}; + +/// Convert body absolute/world coordinates to relative/local coordinates. +Body.prototype.world2Local = function(v) +{ + return vunrotate(vsub(v, this.p), this.rot); +}; +// chipmunk v7.0 compatibility +Body.prototype.localToWorld = Body.prototype.local2World; +Body.prototype.worldToLocal = Body.prototype.world2Local; + +/// Get the kinetic energy of a body. +Body.prototype.kineticEnergy = function() +{ + // Need to do some fudging to avoid NaNs + var vsq = this.vx*this.vx + this.vy*this.vy; + var wsq = this.w * this.w; + return (vsq ? vsq*this.m : 0) + (wsq ? wsq*this.i : 0); +}; + +/* Copyright (c) 2010 Scott Lembcke + * + * 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. + */ + +/** + @defgroup cpSpatialIndex cpSpatialIndex + + Spatial indexes are data structures that are used to accelerate collision detection + and spatial queries. Chipmunk provides a number of spatial index algorithms to pick from + and they are programmed in a generic way so that you can use them for holding more than + just Shapes. + + It works by using pointers to the objects you add and using a callback to ask your code + for bounding boxes when it needs them. Several types of queries can be performed an index as well + as reindexing and full collision information. All communication to the spatial indexes is performed + through callback functions. + + Spatial indexes should be treated as opaque structs. + This means you shouldn't be reading any of the fields directly. + + All spatial indexes define the following methods: + + // The number of objects in the spatial index. + count = 0; + + // Iterate the objects in the spatial index. @c func will be called once for each object. + each(func); + + // Returns true if the spatial index contains the given object. + // Most spatial indexes use hashed storage, so you must provide a hash value too. + contains(obj, hashid); + + // Add an object to a spatial index. + insert(obj, hashid); + + // Remove an object from a spatial index. + remove(obj, hashid); + + // Perform a full reindex of a spatial index. + reindex(); + + // Reindex a single object in the spatial index. + reindexObject(obj, hashid); + + // Perform a point query against the spatial index, calling @c func for each potential match. + // A pointer to the point will be passed as @c obj1 of @c func. + // func(shape); + pointQuery(point, func); + + // Perform a segment query against the spatial index, calling @c func for each potential match. + // func(shape); + segmentQuery(vect a, vect b, t_exit, func); + + // Perform a rectangle query against the spatial index, calling @c func for each potential match. + // func(shape); + query(bb, func); + + // Simultaneously reindex and find all colliding objects. + // @c func will be called once for each potentially overlapping pair of objects found. + // If the spatial index was initialized with a static index, it will collide it's objects against that as well. + reindexQuery(func); +*/ + +var SpatialIndex = cp.SpatialIndex = function(staticIndex) +{ + this.staticIndex = staticIndex; + + + if(staticIndex){ + if(staticIndex.dynamicIndex){ + throw new Error("This static index is already associated with a dynamic index."); + } + staticIndex.dynamicIndex = this; + } +}; + +// Collide the objects in an index against the objects in a staticIndex using the query callback function. +SpatialIndex.prototype.collideStatic = function(staticIndex, func) +{ + if(staticIndex.count > 0){ + var query = staticIndex.query; + + this.each(function(obj) { + query(obj, new BB(obj.bb_l, obj.bb_b, obj.bb_r, obj.bb_t), func); + }); + } +}; + + +/* Copyright (c) 2009 Scott Lembcke + * + * 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. + */ + +// This file implements a modified AABB tree for collision detection. + +var BBTree = cp.BBTree = function(staticIndex) +{ + SpatialIndex.call(this, staticIndex); + + this.velocityFunc = null; + + // This is a hash from object ID -> object for the objects stored in the BBTree. + this.leaves = {}; + // A count of the number of leaves in the BBTree. + this.count = 0; + + this.root = null; + + // A linked list containing an object pool of tree nodes and pairs. + this.pooledNodes = null; + this.pooledPairs = null; + + this.stamp = 0; +}; + +BBTree.prototype = Object.create(SpatialIndex.prototype); + +var numNodes = 0; + +var Node = function(tree, a, b) +{ + this.obj = null; + this.bb_l = min(a.bb_l, b.bb_l); + this.bb_b = min(a.bb_b, b.bb_b); + this.bb_r = max(a.bb_r, b.bb_r); + this.bb_t = max(a.bb_t, b.bb_t); + this.parent = null; + + this.setA(a); + this.setB(b); +}; + +BBTree.prototype.makeNode = function(a, b) +{ + var node = this.pooledNodes; + if(node){ + this.pooledNodes = node.parent; + node.constructor(this, a, b); + return node; + } else { + numNodes++; + return new Node(this, a, b); + } +}; + +var numLeaves = 0; +var Leaf = function(tree, obj) +{ + this.obj = obj; + tree.getBB(obj, this); + + this.parent = null; + + this.stamp = 1; + this.pairs = null; + numLeaves++; +}; + +// **** Misc Functions + +BBTree.prototype.getBB = function(obj, dest) +{ + var velocityFunc = this.velocityFunc; + if(velocityFunc){ + var coef = 0.1; + var x = (obj.bb_r - obj.bb_l)*coef; + var y = (obj.bb_t - obj.bb_b)*coef; + + var v = vmult(velocityFunc(obj), 0.1); + + dest.bb_l = obj.bb_l + min(-x, v.x); + dest.bb_b = obj.bb_b + min(-y, v.y); + dest.bb_r = obj.bb_r + max( x, v.x); + dest.bb_t = obj.bb_t + max( y, v.y); + } else { + dest.bb_l = obj.bb_l; + dest.bb_b = obj.bb_b; + dest.bb_r = obj.bb_r; + dest.bb_t = obj.bb_t; + } +}; + +BBTree.prototype.getStamp = function() +{ + var dynamic = this.dynamicIndex; + return (dynamic && dynamic.stamp ? dynamic.stamp : this.stamp); +}; + +BBTree.prototype.incrementStamp = function() +{ + if(this.dynamicIndex && this.dynamicIndex.stamp){ + this.dynamicIndex.stamp++; + } else { + this.stamp++; + } +} + +// **** Pair/Thread Functions + +var numPairs = 0; +// Objects created with constructors are faster than object literals. :( +var Pair = function(leafA, nextA, leafB, nextB) +{ + this.prevA = null; + this.leafA = leafA; + this.nextA = nextA; + + this.prevB = null; + this.leafB = leafB; + this.nextB = nextB; +}; + +BBTree.prototype.makePair = function(leafA, nextA, leafB, nextB) +{ + //return new Pair(leafA, nextA, leafB, nextB); + var pair = this.pooledPairs; + if (pair) + { + this.pooledPairs = pair.prevA; + + pair.prevA = null; + pair.leafA = leafA; + pair.nextA = nextA; + + pair.prevB = null; + pair.leafB = leafB; + pair.nextB = nextB; + + //pair.constructor(leafA, nextA, leafB, nextB); + return pair; + } else { + numPairs++; + return new Pair(leafA, nextA, leafB, nextB); + } +}; + +Pair.prototype.recycle = function(tree) +{ + this.prevA = tree.pooledPairs; + tree.pooledPairs = this; +}; + +var unlinkThread = function(prev, leaf, next) +{ + if(next){ + if(next.leafA === leaf) next.prevA = prev; else next.prevB = prev; + } + + if(prev){ + if(prev.leafA === leaf) prev.nextA = next; else prev.nextB = next; + } else { + leaf.pairs = next; + } +}; + +Leaf.prototype.clearPairs = function(tree) +{ + var pair = this.pairs, + next; + + this.pairs = null; + + while(pair){ + if(pair.leafA === this){ + next = pair.nextA; + unlinkThread(pair.prevB, pair.leafB, pair.nextB); + } else { + next = pair.nextB; + unlinkThread(pair.prevA, pair.leafA, pair.nextA); + } + pair.recycle(tree); + pair = next; + } +}; + +var pairInsert = function(a, b, tree) +{ + var nextA = a.pairs, nextB = b.pairs; + var pair = tree.makePair(a, nextA, b, nextB); + a.pairs = b.pairs = pair; + + if(nextA){ + if(nextA.leafA === a) nextA.prevA = pair; else nextA.prevB = pair; + } + + if(nextB){ + if(nextB.leafA === b) nextB.prevA = pair; else nextB.prevB = pair; + } +}; + +// **** Node Functions + +Node.prototype.recycle = function(tree) +{ + this.parent = tree.pooledNodes; + tree.pooledNodes = this; +}; + +Leaf.prototype.recycle = function(tree) +{ + // Its not worth the overhead to recycle leaves. +}; + +Node.prototype.setA = function(value) +{ + this.A = value; + value.parent = this; +}; + +Node.prototype.setB = function(value) +{ + this.B = value; + value.parent = this; +}; + +Leaf.prototype.isLeaf = true; +Node.prototype.isLeaf = false; + +Node.prototype.otherChild = function(child) +{ + return (this.A == child ? this.B : this.A); +}; + +Node.prototype.replaceChild = function(child, value, tree) +{ + assertSoft(child == this.A || child == this.B, "Node is not a child of parent."); + + if(this.A == child){ + this.A.recycle(tree); + this.setA(value); + } else { + this.B.recycle(tree); + this.setB(value); + } + + for(var node=this; node; node = node.parent){ + //node.bb = bbMerge(node.A.bb, node.B.bb); + var a = node.A; + var b = node.B; + node.bb_l = min(a.bb_l, b.bb_l); + node.bb_b = min(a.bb_b, b.bb_b); + node.bb_r = max(a.bb_r, b.bb_r); + node.bb_t = max(a.bb_t, b.bb_t); + } +}; + +Node.prototype.bbArea = Leaf.prototype.bbArea = function() +{ + return (this.bb_r - this.bb_l)*(this.bb_t - this.bb_b); +}; + +var bbTreeMergedArea = function(a, b) +{ + return (max(a.bb_r, b.bb_r) - min(a.bb_l, b.bb_l))*(max(a.bb_t, b.bb_t) - min(a.bb_b, b.bb_b)); +}; + +// **** Subtree Functions + +// Would it be better to make these functions instance methods on Node and Leaf? + +var bbProximity = function(a, b) +{ + return Math.abs(a.bb_l + a.bb_r - b.bb_l - b.bb_r) + Math.abs(a.bb_b + a.bb_t - b.bb_b - b.bb_t); +}; + +var subtreeInsert = function(subtree, leaf, tree) +{ +// var s = new Error().stack; +// traces[s] = traces[s] ? traces[s]+1 : 1; + + if(subtree == null){ + return leaf; + } else if(subtree.isLeaf){ + return tree.makeNode(leaf, subtree); + } else { + var cost_a = subtree.B.bbArea() + bbTreeMergedArea(subtree.A, leaf); + var cost_b = subtree.A.bbArea() + bbTreeMergedArea(subtree.B, leaf); + + if(cost_a === cost_b){ + cost_a = bbProximity(subtree.A, leaf); + cost_b = bbProximity(subtree.B, leaf); + } + + if(cost_b < cost_a){ + subtree.setB(subtreeInsert(subtree.B, leaf, tree)); + } else { + subtree.setA(subtreeInsert(subtree.A, leaf, tree)); + } + +// subtree.bb = bbMerge(subtree.bb, leaf.bb); + subtree.bb_l = min(subtree.bb_l, leaf.bb_l); + subtree.bb_b = min(subtree.bb_b, leaf.bb_b); + subtree.bb_r = max(subtree.bb_r, leaf.bb_r); + subtree.bb_t = max(subtree.bb_t, leaf.bb_t); + + return subtree; + } +}; + +Node.prototype.intersectsBB = Leaf.prototype.intersectsBB = function(bb) +{ + return (this.bb_l <= bb.r && bb.l <= this.bb_r && this.bb_b <= bb.t && bb.b <= this.bb_t); +}; + +var subtreeQuery = function(subtree, bb, func) +{ + //if(bbIntersectsBB(subtree.bb, bb)){ + if(subtree.intersectsBB(bb)){ + if(subtree.isLeaf){ + func(subtree.obj); + } else { + subtreeQuery(subtree.A, bb, func); + subtreeQuery(subtree.B, bb, func); + } + } +}; + +/// Returns the fraction along the segment query the node hits. Returns Infinity if it doesn't hit. +var nodeSegmentQuery = function(node, a, b) +{ + var idx = 1/(b.x - a.x); + var tx1 = (node.bb_l == a.x ? -Infinity : (node.bb_l - a.x)*idx); + var tx2 = (node.bb_r == a.x ? Infinity : (node.bb_r - a.x)*idx); + var txmin = min(tx1, tx2); + var txmax = max(tx1, tx2); + + var idy = 1/(b.y - a.y); + var ty1 = (node.bb_b == a.y ? -Infinity : (node.bb_b - a.y)*idy); + var ty2 = (node.bb_t == a.y ? Infinity : (node.bb_t - a.y)*idy); + var tymin = min(ty1, ty2); + var tymax = max(ty1, ty2); + + if(tymin <= txmax && txmin <= tymax){ + var min_ = max(txmin, tymin); + var max_ = min(txmax, tymax); + + if(0.0 <= max_ && min_ <= 1.0) return max(min_, 0.0); + } + + return Infinity; +}; + +var subtreeSegmentQuery = function(subtree, a, b, t_exit, func) +{ + if(subtree.isLeaf){ + return func(subtree.obj); + } else { + var t_a = nodeSegmentQuery(subtree.A, a, b); + var t_b = nodeSegmentQuery(subtree.B, a, b); + + if(t_a < t_b){ + if(t_a < t_exit) t_exit = min(t_exit, subtreeSegmentQuery(subtree.A, a, b, t_exit, func)); + if(t_b < t_exit) t_exit = min(t_exit, subtreeSegmentQuery(subtree.B, a, b, t_exit, func)); + } else { + if(t_b < t_exit) t_exit = min(t_exit, subtreeSegmentQuery(subtree.B, a, b, t_exit, func)); + if(t_a < t_exit) t_exit = min(t_exit, subtreeSegmentQuery(subtree.A, a, b, t_exit, func)); + } + + return t_exit; + } +}; + +BBTree.prototype.subtreeRecycle = function(node) +{ + if(node.isLeaf){ + this.subtreeRecycle(node.A); + this.subtreeRecycle(node.B); + node.recycle(this); + } +}; + +var subtreeRemove = function(subtree, leaf, tree) +{ + if(leaf == subtree){ + return null; + } else { + var parent = leaf.parent; + if(parent == subtree){ + var other = subtree.otherChild(leaf); + other.parent = subtree.parent; + subtree.recycle(tree); + return other; + } else { + parent.parent.replaceChild(parent, parent.otherChild(leaf), tree); + return subtree; + } + } +}; + +// **** Marking Functions + +/* +typedef struct MarkContext { + bbTree *tree; + Node *staticRoot; + cpSpatialIndexQueryFunc func; +} MarkContext; +*/ + +var bbTreeIntersectsNode = function(a, b) +{ + return (a.bb_l <= b.bb_r && b.bb_l <= a.bb_r && a.bb_b <= b.bb_t && b.bb_b <= a.bb_t); +}; + +Leaf.prototype.markLeafQuery = function(leaf, left, tree, func) +{ + if(bbTreeIntersectsNode(leaf, this)){ + if(left){ + pairInsert(leaf, this, tree); + } else { + if(this.stamp < leaf.stamp) pairInsert(this, leaf, tree); + if(func) func(leaf.obj, this.obj); + } + } +}; + +Node.prototype.markLeafQuery = function(leaf, left, tree, func) +{ + if(bbTreeIntersectsNode(leaf, this)){ + this.A.markLeafQuery(leaf, left, tree, func); + this.B.markLeafQuery(leaf, left, tree, func); + } +}; + +Leaf.prototype.markSubtree = function(tree, staticRoot, func) +{ + if(this.stamp == tree.getStamp()){ + if(staticRoot) staticRoot.markLeafQuery(this, false, tree, func); + + for(var node = this; node.parent; node = node.parent){ + if(node == node.parent.A){ + node.parent.B.markLeafQuery(this, true, tree, func); + } else { + node.parent.A.markLeafQuery(this, false, tree, func); + } + } + } else { + var pair = this.pairs; + while(pair){ + if(this === pair.leafB){ + if(func) func(pair.leafA.obj, this.obj); + pair = pair.nextB; + } else { + pair = pair.nextA; + } + } + } +}; + +Node.prototype.markSubtree = function(tree, staticRoot, func) +{ + this.A.markSubtree(tree, staticRoot, func); + this.B.markSubtree(tree, staticRoot, func); +}; + +// **** Leaf Functions + +Leaf.prototype.containsObj = function(obj) +{ + return (this.bb_l <= obj.bb_l && this.bb_r >= obj.bb_r && this.bb_b <= obj.bb_b && this.bb_t >= obj.bb_t); +}; + +Leaf.prototype.update = function(tree) +{ + var root = tree.root; + var obj = this.obj; + + //if(!bbContainsBB(this.bb, bb)){ + if(!this.containsObj(obj)){ + tree.getBB(this.obj, this); + + root = subtreeRemove(root, this, tree); + tree.root = subtreeInsert(root, this, tree); + + this.clearPairs(tree); + this.stamp = tree.getStamp(); + + return true; + } + + return false; +}; + +Leaf.prototype.addPairs = function(tree) +{ + var dynamicIndex = tree.dynamicIndex; + if(dynamicIndex){ + var dynamicRoot = dynamicIndex.root; + if(dynamicRoot){ + dynamicRoot.markLeafQuery(this, true, dynamicIndex, null); + } + } else { + var staticRoot = tree.staticIndex.root; + this.markSubtree(tree, staticRoot, null); + } +}; + +// **** Insert/Remove + +BBTree.prototype.insert = function(obj, hashid) +{ + var leaf = new Leaf(this, obj); + + this.leaves[hashid] = leaf; + this.root = subtreeInsert(this.root, leaf, this); + this.count++; + + leaf.stamp = this.getStamp(); + leaf.addPairs(this); + this.incrementStamp(); +}; + +BBTree.prototype.remove = function(obj, hashid) +{ + var leaf = this.leaves[hashid]; + + delete this.leaves[hashid]; + this.root = subtreeRemove(this.root, leaf, this); + this.count--; + + leaf.clearPairs(this); + leaf.recycle(this); +}; + +BBTree.prototype.contains = function(obj, hashid) +{ + return this.leaves[hashid] != null; +}; + +// **** Reindex +var voidQueryFunc = function(obj1, obj2){}; + +BBTree.prototype.reindexQuery = function(func) +{ + if(!this.root) return; + + // LeafUpdate() may modify this.root. Don't cache it. + var hashid, + leaves = this.leaves; + for (hashid in leaves) + { + leaves[hashid].update(this); + } + + var staticIndex = this.staticIndex; + var staticRoot = staticIndex && staticIndex.root; + + this.root.markSubtree(this, staticRoot, func); + if(staticIndex && !staticRoot) this.collideStatic(this, staticIndex, func); + + this.incrementStamp(); +}; + +BBTree.prototype.reindex = function() +{ + this.reindexQuery(voidQueryFunc); +}; + +BBTree.prototype.reindexObject = function(obj, hashid) +{ + var leaf = this.leaves[hashid]; + if(leaf){ + if(leaf.update(this)) leaf.addPairs(this); + this.incrementStamp(); + } +}; + +// **** Query + +// This has since been removed from upstream Chipmunk - which recommends you just use query() below +// directly. +BBTree.prototype.pointQuery = function(point, func) +{ + this.query(new BB(point.x, point.y, point.x, point.y), func); +}; + +BBTree.prototype.segmentQuery = function(a, b, t_exit, func) +{ + if(this.root) subtreeSegmentQuery(this.root, a, b, t_exit, func); +}; + +BBTree.prototype.query = function(bb, func) +{ + if(this.root) subtreeQuery(this.root, bb, func); +}; + +// **** Misc + +BBTree.prototype.count = function() +{ + return this.count; +}; + +BBTree.prototype.each = function(func) +{ + var hashid; + for(hashid in this.leaves) + { + func(this.leaves[hashid].obj); + } +}; + +// **** Tree Optimization + +var bbTreeMergedArea2 = function(node, l, b, r, t) +{ + return (max(node.bb_r, r) - min(node.bb_l, l))*(max(node.bb_t, t) - min(node.bb_b, b)); +}; + +var partitionNodes = function(tree, nodes, offset, count) +{ + if(count == 1){ + return nodes[offset]; + } else if(count == 2) { + return tree.makeNode(nodes[offset], nodes[offset + 1]); + } + + // Find the AABB for these nodes + //var bb = nodes[offset].bb; + var node = nodes[offset]; + var bb_l = node.bb_l, + bb_b = node.bb_b, + bb_r = node.bb_r, + bb_t = node.bb_t; + + var end = offset + count; + for(var i=offset + 1; i bb_t - bb_b); + + // Sort the bounds and use the median as the splitting point + var bounds = new Array(count*2); + if(splitWidth){ + for(var i=offset; inext = next; + if(prev.body_a === body) { + prev.thread_a_next = next; + } else { + prev.thread_b_next = next; + } + } else { + body.arbiterList = next; + } + + if(next){ + // cpArbiterThreadForBody(next, body)->prev = prev; + if(next.body_a === body){ + next.thread_a_prev = prev; + } else { + next.thread_b_prev = prev; + } + } +}; + +Arbiter.prototype.unthread = function() +{ + unthreadHelper(this, this.body_a, this.thread_a_prev, this.thread_a_next); + unthreadHelper(this, this.body_b, this.thread_b_prev, this.thread_b_next); + this.thread_a_prev = this.thread_a_next = null; + this.thread_b_prev = this.thread_b_next = null; +}; + +//cpFloat +//cpContactsEstimateCrushingImpulse(cpContact *contacts, int numContacts) +//{ +// cpFloat fsum = 0; +// cpVect vsum = vzero; +// +// for(int i=0; i= mindist*mindist) return; + + var dist = Math.sqrt(distsq); + + // Allocate and initialize the contact. + return new Contact( + vadd(p1, vmult(delta, 0.5 + (r1 - 0.5*mindist)/(dist ? dist : Infinity))), + (dist ? vmult(delta, 1/dist) : new Vect(1, 0)), + dist - mindist, + 0 + ); +}; + +// Collide circle shapes. +var circle2circle = function(circ1, circ2) +{ + var contact = circle2circleQuery(circ1.tc, circ2.tc, circ1.r, circ2.r); + return contact ? [contact] : NONE; +}; + +var circle2segment = function(circleShape, segmentShape) +{ + var seg_a = segmentShape.ta; + var seg_b = segmentShape.tb; + var center = circleShape.tc; + + var seg_delta = vsub(seg_b, seg_a); + var closest_t = clamp01(vdot(seg_delta, vsub(center, seg_a))/vlengthsq(seg_delta)); + var closest = vadd(seg_a, vmult(seg_delta, closest_t)); + + var contact = circle2circleQuery(center, closest, circleShape.r, segmentShape.r); + if(contact){ + var n = contact.n; + + // Reject endcap collisions if tangents are provided. + return ( + (closest_t === 0 && vdot(n, segmentShape.a_tangent) < 0) || + (closest_t === 1 && vdot(n, segmentShape.b_tangent) < 0) + ) ? NONE : [contact]; + } else { + return NONE; + } +} + +// Find the minimum separating axis for the given poly and axis list. +// +// This function needs to return two values - the index of the min. separating axis and +// the value itself. Short of inlining MSA, returning values through a global like this +// is the fastest implementation. +// +// See: http://jsperf.com/return-two-values-from-function/2 +var last_MSA_min = 0; +var findMSA = function(poly, planes) +{ + var min_index = 0; + var min = poly.valueOnAxis(planes[0].n, planes[0].d); + if(min > 0) return -1; + + for(var i=1; i 0) { + return -1; + } else if(dist > min){ + min = dist; + min_index = i; + } + } + + last_MSA_min = min; + return min_index; +}; + +// Add contacts for probably penetrating vertexes. +// This handles the degenerate case where an overlap was detected, but no vertexes fall inside +// the opposing polygon. (like a star of david) +var findVertsFallback = function(poly1, poly2, n, dist) +{ + var arr = []; + + var verts1 = poly1.tVerts; + for(var i=0; i>1))); + } + } + + var verts2 = poly2.tVerts; + for(var i=0; i>1))); + } + } + + return (arr.length ? arr : findVertsFallback(poly1, poly2, n, dist)); +}; + +// Collide poly shapes together. +var poly2poly = function(poly1, poly2) +{ + var mini1 = findMSA(poly2, poly1.tPlanes); + if(mini1 == -1) return NONE; + var min1 = last_MSA_min; + + var mini2 = findMSA(poly1, poly2.tPlanes); + if(mini2 == -1) return NONE; + var min2 = last_MSA_min; + + // There is overlap, find the penetrating verts + if(min1 > min2) + return findVerts(poly1, poly2, poly1.tPlanes[mini1].n, min1); + else + return findVerts(poly1, poly2, vneg(poly2.tPlanes[mini2].n), min2); +}; + +// Like cpPolyValueOnAxis(), but for segments. +var segValueOnAxis = function(seg, n, d) +{ + var a = vdot(n, seg.ta) - seg.r; + var b = vdot(n, seg.tb) - seg.r; + return min(a, b) - d; +}; + +// Identify vertexes that have penetrated the segment. +var findPointsBehindSeg = function(arr, seg, poly, pDist, coef) +{ + var dta = vcross(seg.tn, seg.ta); + var dtb = vcross(seg.tn, seg.tb); + var n = vmult(seg.tn, coef); + + var verts = poly.tVerts; + for(var i=0; i= dt && dt >= dtb){ + arr.push(new Contact(new Vect(vx, vy), n, pDist, hashPair(poly.hashid, i))); + } + } + } +}; + +// This one is complicated and gross. Just don't go there... +// TODO: Comment me! +var seg2poly = function(seg, poly) +{ + var arr = []; + + var planes = poly.tPlanes; + var numVerts = planes.length; + + var segD = vdot(seg.tn, seg.ta); + var minNorm = poly.valueOnAxis(seg.tn, segD) - seg.r; + var minNeg = poly.valueOnAxis(vneg(seg.tn), -segD) - seg.r; + if(minNeg > 0 || minNorm > 0) return NONE; + + var mini = 0; + var poly_min = segValueOnAxis(seg, planes[0].n, planes[0].d); + if(poly_min > 0) return NONE; + for(var i=0; i 0){ + return NONE; + } else if(dist > poly_min){ + poly_min = dist; + mini = i; + } + } + + var poly_n = vneg(planes[mini].n); + + var va = vadd(seg.ta, vmult(poly_n, seg.r)); + var vb = vadd(seg.tb, vmult(poly_n, seg.r)); + if(poly.containsVert(va.x, va.y)) + arr.push(new Contact(va, poly_n, poly_min, hashPair(seg.hashid, 0))); + if(poly.containsVert(vb.x, vb.y)) + arr.push(new Contact(vb, poly_n, poly_min, hashPair(seg.hashid, 1))); + + // Floating point precision problems here. + // This will have to do for now. +// poly_min -= cp_collision_slop; // TODO is this needed anymore? + + if(minNorm >= poly_min || minNeg >= poly_min) { + if(minNorm > minNeg) + findPointsBehindSeg(arr, seg, poly, minNorm, 1); + else + findPointsBehindSeg(arr, seg, poly, minNeg, -1); + } + + // If no other collision points are found, try colliding endpoints. + if(arr.length === 0){ + var mini2 = mini * 2; + var verts = poly.tVerts; + + var poly_a = new Vect(verts[mini2], verts[mini2+1]); + + var con; + if((con = circle2circleQuery(seg.ta, poly_a, seg.r, 0, arr))) return [con]; + if((con = circle2circleQuery(seg.tb, poly_a, seg.r, 0, arr))) return [con]; + + var len = numVerts * 2; + var poly_b = new Vect(verts[(mini2+2)%len], verts[(mini2+3)%len]); + if((con = circle2circleQuery(seg.ta, poly_b, seg.r, 0, arr))) return [con]; + if((con = circle2circleQuery(seg.tb, poly_b, seg.r, 0, arr))) return [con]; + } + +// console.log(poly.tVerts, poly.tPlanes); +// console.log('seg2poly', arr); + return arr; +}; + +// This one is less gross, but still gross. +// TODO: Comment me! +var circle2poly = function(circ, poly) +{ + var planes = poly.tPlanes; + + var mini = 0; + var min = vdot(planes[0].n, circ.tc) - planes[0].d - circ.r; + for(var i=0; i 0){ + return NONE; + } else if(dist > min) { + min = dist; + mini = i; + } + } + + var n = planes[mini].n; + + var verts = poly.tVerts; + var len = verts.length; + var mini2 = mini<<1; + + //var a = poly.tVerts[mini]; + //var b = poly.tVerts[(mini + 1)%poly.tVerts.length]; + var ax = verts[mini2]; + var ay = verts[mini2+1]; + var bx = verts[(mini2+2)%len]; + var by = verts[(mini2+3)%len]; + + var dta = vcross2(n.x, n.y, ax, ay); + var dtb = vcross2(n.x, n.y, bx, by); + var dt = vcross(n, circ.tc); + + if(dt < dtb){ + var con = circle2circleQuery(circ.tc, new Vect(bx, by), circ.r, 0, con); + return con ? [con] : NONE; + } else if(dt < dta) { + return [new Contact( + vsub(circ.tc, vmult(n, circ.r + min/2)), + vneg(n), + min, + 0 + )]; + } else { + var con = circle2circleQuery(circ.tc, new Vect(ax, ay), circ.r, 0, con); + return con ? [con] : NONE; + } +}; + +// The javascripty way to do this would be either nested object or methods on the prototypes. +// +// However, the *fastest* way is the method below. +// See: http://jsperf.com/dispatch + +// These are copied from the prototypes into the actual objects in the Shape constructor. +CircleShape.prototype.collisionCode = 0; +SegmentShape.prototype.collisionCode = 1; +PolyShape.prototype.collisionCode = 2; + +CircleShape.prototype.collisionTable = [ + circle2circle, + circle2segment, + circle2poly +]; + +SegmentShape.prototype.collisionTable = [ + null, + function(segA, segB) { return NONE; }, // seg2seg + seg2poly +]; + +PolyShape.prototype.collisionTable = [ + null, + null, + poly2poly +]; + +var collideShapes = cp.collideShapes = function(a, b) +{ + assert(a.collisionCode <= b.collisionCode, 'Collided shapes must be sorted by type'); + return a.collisionTable[b.collisionCode](a, b); +}; + +/* Copyright (c) 2007 Scott Lembcke + * + * 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. + */ + +var defaultCollisionHandler = new CollisionHandler(); + +/// Basic Unit of Simulation in Chipmunk +var Space = cp.Space = function() { + this.stamp = 0; + this.curr_dt = 0; + + this.bodies = []; + this.rousedBodies = []; + this.sleepingComponents = []; + + this.staticShapes = new BBTree(null); + this.activeShapes = new BBTree(this.staticShapes); + + this.arbiters = []; + this.contactBuffersHead = null; + this.cachedArbiters = {}; + //this.pooledArbiters = []; + + this.constraints = []; + + this.locked = 0; + + this.collisionHandlers = {}; + this.defaultHandler = defaultCollisionHandler; + + this.postStepCallbacks = []; + + /// Number of iterations to use in the impulse solver to solve contacts. + this.iterations = 10; + + /// Gravity to pass to rigid bodies when integrating velocity. + this.gravity = vzero; + + /// Damping rate expressed as the fraction of velocity bodies retain each second. + /// A value of 0.9 would mean that each body's velocity will drop 10% per second. + /// The default value is 1.0, meaning no damping is applied. + /// @note This damping value is different than those of cpDampedSpring and cpDampedRotarySpring. + this.damping = 1; + + /// Speed threshold for a body to be considered idle. + /// The default value of 0 means to let the space guess a good threshold based on gravity. + this.idleSpeedThreshold = 0; + + /// Time a group of bodies must remain idle in order to fall asleep. + /// Enabling sleeping also implicitly enables the the contact graph. + /// The default value of Infinity disables the sleeping algorithm. + this.sleepTimeThreshold = Infinity; + + /// Amount of encouraged penetration between colliding shapes.. + /// Used to reduce oscillating contacts and keep the collision cache warm. + /// Defaults to 0.1. If you have poor simulation quality, + /// increase this number as much as possible without allowing visible amounts of overlap. + this.collisionSlop = 0.1; + + /// Determines how fast overlapping shapes are pushed apart. + /// Expressed as a fraction of the error remaining after each second. + /// Defaults to pow(1.0 - 0.1, 60.0) meaning that Chipmunk fixes 10% of overlap each frame at 60Hz. + this.collisionBias = Math.pow(1 - 0.1, 60); + + /// Number of frames that contact information should persist. + /// Defaults to 3. There is probably never a reason to change this value. + this.collisionPersistence = 3; + + /// Rebuild the contact graph during each step. Must be enabled to use the cpBodyEachArbiter() function. + /// Disabled by default for a small performance boost. Enabled implicitly when the sleeping feature is enabled. + this.enableContactGraph = false; + + /// The designated static body for this space. + /// You can modify this body, or replace it with your own static body. + /// By default it points to a statically allocated cpBody in the cpSpace struct. + this.staticBody = new Body(Infinity, Infinity); + this.staticBody.nodeIdleTime = Infinity; + + // Cache the collideShapes callback function for the space. + this.collideShapes = this.makeCollideShapes(); +}; + +Space.prototype.getCurrentTimeStep = function() { return this.curr_dt; }; + +Space.prototype.setIterations = function(iter) { this.iterations = iter; }; + +/// returns true from inside a callback and objects cannot be added/removed. +Space.prototype.isLocked = function() +{ + return this.locked; +}; + +var assertSpaceUnlocked = function(space) +{ + assert(!space.locked, "This addition/removal cannot be done safely during a call to cpSpaceStep() \ + or during a query. Put these calls into a post-step callback."); +}; + +// **** Collision handler function management + +/// Set a collision handler to be used whenever the two shapes with the given collision types collide. +/// You can pass null for any function you don't want to implement. +Space.prototype.addCollisionHandler = function(a, b, begin, preSolve, postSolve, separate) +{ + assertSpaceUnlocked(this); + + // Remove any old function so the new one will get added. + this.removeCollisionHandler(a, b); + + var handler = new CollisionHandler(); + handler.a = a; + handler.b = b; + if(begin) handler.begin = begin; + if(preSolve) handler.preSolve = preSolve; + if(postSolve) handler.postSolve = postSolve; + if(separate) handler.separate = separate; + + this.collisionHandlers[hashPair(a, b)] = handler; +}; + +/// Unset a collision handler. +Space.prototype.removeCollisionHandler = function(a, b) +{ + assertSpaceUnlocked(this); + + delete this.collisionHandlers[hashPair(a, b)]; +}; + +/// Set a default collision handler for this space. +/// The default collision handler is invoked for each colliding pair of shapes +/// that isn't explicitly handled by a specific collision handler. +/// You can pass null for any function you don't want to implement. +Space.prototype.setDefaultCollisionHandler = function(begin, preSolve, postSolve, separate) +{ + assertSpaceUnlocked(this); + + var handler = new CollisionHandler(); + if(begin) handler.begin = begin; + if(preSolve) handler.preSolve = preSolve; + if(postSolve) handler.postSolve = postSolve; + if(separate) handler.separate = separate; + + this.defaultHandler = handler; +}; + +Space.prototype.lookupHandler = function(a, b) +{ + return this.collisionHandlers[hashPair(a, b)] || this.defaultHandler; +}; + +// **** Body, Shape, and Joint Management + +/// Add a collision shape to the simulation. +/// If the shape is attached to a static body, it will be added as a static shape. +Space.prototype.addShape = function(shape) +{ + var body = shape.body; + if(body.isStatic()) return this.addStaticShape(shape); + + assert(!shape.space, "This shape is already added to a space and cannot be added to another."); + assertSpaceUnlocked(this); + + body.activate(); + body.addShape(shape); + + shape.update(body.p, body.rot); + this.activeShapes.insert(shape, shape.hashid); + shape.space = this; + + return shape; +}; + +/// Explicity add a shape as a static shape to the simulation. +Space.prototype.addStaticShape = function(shape) +{ + assert(!shape.space, "This shape is already added to a space and cannot be added to another."); + assertSpaceUnlocked(this); + + var body = shape.body; + body.addShape(shape); + + shape.update(body.p, body.rot); + this.staticShapes.insert(shape, shape.hashid); + shape.space = this; + + return shape; +}; + +/// Add a rigid body to the simulation. +Space.prototype.addBody = function(body) +{ + assert(!body.isStatic(), "Static bodies cannot be added to a space as they are not meant to be simulated."); + assert(!body.space, "This body is already added to a space and cannot be added to another."); + assertSpaceUnlocked(this); + + this.bodies.push(body); + body.space = this; + + return body; +}; + +/// Add a constraint to the simulation. +Space.prototype.addConstraint = function(constraint) +{ + assert(!constraint.space, "This shape is already added to a space and cannot be added to another."); + assertSpaceUnlocked(this); + + var a = constraint.a, b = constraint.b; + + a.activate(); + b.activate(); + this.constraints.push(constraint); + + // Push onto the heads of the bodies' constraint lists + constraint.next_a = a.constraintList; a.constraintList = constraint; + constraint.next_b = b.constraintList; b.constraintList = constraint; + constraint.space = this; + + return constraint; +}; + +Space.prototype.filterArbiters = function(body, filter) +{ + for (var hash in this.cachedArbiters) + { + var arb = this.cachedArbiters[hash]; + + // Match on the filter shape, or if it's null the filter body + if( + (body === arb.body_a && (filter === arb.a || filter === null)) || + (body === arb.body_b && (filter === arb.b || filter === null)) + ){ + // Call separate when removing shapes. + if(filter && arb.state !== 'cached') arb.callSeparate(this); + + arb.unthread(); + + deleteObjFromList(this.arbiters, arb); + //this.pooledArbiters.push(arb); + + delete this.cachedArbiters[hash]; + } + } +}; + +/// Remove a collision shape from the simulation. +Space.prototype.removeShape = function(shape) +{ + var body = shape.body; + if(body.isStatic()){ + this.removeStaticShape(shape); + } else { + assert(this.containsShape(shape), + "Cannot remove a shape that was not added to the space. (Removed twice maybe?)"); + assertSpaceUnlocked(this); + + body.activate(); + body.removeShape(shape); + this.filterArbiters(body, shape); + this.activeShapes.remove(shape, shape.hashid); + shape.space = null; + } +}; + +/// Remove a collision shape added using addStaticShape() from the simulation. +Space.prototype.removeStaticShape = function(shape) +{ + assert(this.containsShape(shape), + "Cannot remove a static or sleeping shape that was not added to the space. (Removed twice maybe?)"); + assertSpaceUnlocked(this); + + var body = shape.body; + if(body.isStatic()) body.activateStatic(shape); + body.removeShape(shape); + this.filterArbiters(body, shape); + this.staticShapes.remove(shape, shape.hashid); + shape.space = null; +}; + +/// Remove a rigid body from the simulation. +Space.prototype.removeBody = function(body) +{ + assert(this.containsBody(body), + "Cannot remove a body that was not added to the space. (Removed twice maybe?)"); + assertSpaceUnlocked(this); + + body.activate(); +// this.filterArbiters(body, null); + deleteObjFromList(this.bodies, body); + body.space = null; +}; + +/// Remove a constraint from the simulation. +Space.prototype.removeConstraint = function(constraint) +{ + assert(this.containsConstraint(constraint), + "Cannot remove a constraint that was not added to the space. (Removed twice maybe?)"); + assertSpaceUnlocked(this); + + constraint.a.activate(); + constraint.b.activate(); + deleteObjFromList(this.constraints, constraint); + + constraint.a.removeConstraint(constraint); + constraint.b.removeConstraint(constraint); + constraint.space = null; +}; + +/// Test if a collision shape has been added to the space. +Space.prototype.containsShape = function(shape) +{ + return (shape.space === this); +}; + +/// Test if a rigid body has been added to the space. +Space.prototype.containsBody = function(body) +{ + return (body.space == this); +}; + +/// Test if a constraint has been added to the space. +Space.prototype.containsConstraint = function(constraint) +{ + return (constraint.space == this); +}; + +Space.prototype.uncacheArbiter = function(arb) +{ + delete this.cachedArbiters[hashPair(arb.a.hashid, arb.b.hashid)]; + deleteObjFromList(this.arbiters, arb); +}; + + +// **** Iteration + +/// Call @c func for each body in the space. +Space.prototype.eachBody = function(func) +{ + this.lock(); { + var bodies = this.bodies; + + for(var i=0; i keThreshold ? 0 : body.nodeIdleTime + dt); + } + } + + // Awaken any sleeping bodies found and then push arbiters to the bodies' lists. + var arbiters = this.arbiters; + for(var i=0, count=arbiters.length; i= 0, "Internal Error: Space lock underflow."); + + if(this.locked === 0 && runPostStep){ + var waking = this.rousedBodies; + for(var i=0; i this.collisionPersistence){ + // The tail buffer is available, rotate the ring + var tail = head.next; + tail.stamp = stamp; + tail.contacts.length = 0; + this.contactBuffersHead = tail; + } else { + // Allocate a new buffer and push it into the ring + var buffer = new ContactBuffer(stamp, head); + this.contactBuffersHead = head.next = buffer; + } +}; + +cpContact * +cpContactBufferGetArray(cpSpace *space) +{ + if(space.contactBuffersHead.numContacts + CP_MAX_CONTACTS_PER_ARBITER > CP_CONTACTS_BUFFER_SIZE){ + // contact buffer could overflow on the next collision, push a fresh one. + space.pushFreshContactBuffer(); + } + + cpContactBufferHeader *head = space.contactBuffersHead; + return ((cpContactBuffer *)head)->contacts + head.numContacts; +} + +void +cpSpacePushContacts(cpSpace *space, int count) +{ + cpAssertHard(count <= CP_MAX_CONTACTS_PER_ARBITER, "Internal Error: Contact buffer overflow!"); + space.contactBuffersHead.numContacts += count; +} + +static void +cpSpacePopContacts(cpSpace *space, int count){ + space.contactBuffersHead.numContacts -= count; +} +*/ + +// **** Collision Detection Functions + +/* Use this to re-enable object pools. +static void * +cpSpaceArbiterSetTrans(cpShape **shapes, cpSpace *space) +{ + if(space.pooledArbiters.num == 0){ + // arbiter pool is exhausted, make more + int count = CP_BUFFER_BYTES/sizeof(cpArbiter); + cpAssertHard(count, "Internal Error: Buffer size too small."); + + cpArbiter *buffer = (cpArbiter *)cpcalloc(1, CP_BUFFER_BYTES); + cpArrayPush(space.allocatedBuffers, buffer); + + for(int i=0; i b.collisionCode){ + var temp = a; + a = b; + b = temp; + } + + // Narrow-phase collision detection. + //cpContact *contacts = cpContactBufferGetArray(space); + //int numContacts = cpCollideShapes(a, b, contacts); + var contacts = collideShapes(a, b); + if(contacts.length === 0) return; // Shapes are not colliding. + //cpSpacePushContacts(space, numContacts); + + // Get an arbiter from space.arbiterSet for the two shapes. + // This is where the persistent contact magic comes from. + var arbHash = hashPair(a.hashid, b.hashid); + var arb = space.cachedArbiters[arbHash]; + if (!arb){ + arb = space.cachedArbiters[arbHash] = new Arbiter(a, b); + } + + arb.update(contacts, handler, a, b); + + // Call the begin function first if it's the first step + if(arb.state == 'first coll' && !handler.begin(arb, space)){ + arb.ignore(); // permanently ignore the collision until separation + } + + if( + // Ignore the arbiter if it has been flagged + (arb.state !== 'ignore') && + // Call preSolve + handler.preSolve(arb, space) && + // Process, but don't add collisions for sensors. + !sensor + ){ + space.arbiters.push(arb); + } else { + //cpSpacePopContacts(space, numContacts); + + arb.contacts = null; + + // Normally arbiters are set as used after calling the post-solve callback. + // However, post-solve callbacks are not called for sensors or arbiters rejected from pre-solve. + if(arb.state !== 'ignore') arb.state = 'normal'; + } + + // Time stamp the arbiter so we know it was used recently. + arb.stamp = space.stamp; + }; +}; + +// Hashset filter func to throw away old arbiters. +Space.prototype.arbiterSetFilter = function(arb) +{ + var ticks = this.stamp - arb.stamp; + + var a = arb.body_a, b = arb.body_b; + + // TODO should make an arbiter state for this so it doesn't require filtering arbiters for + // dangling body pointers on body removal. + // Preserve arbiters on sensors and rejected arbiters for sleeping objects. + // This prevents errant separate callbacks from happenening. + if( + (a.isStatic() || a.isSleeping()) && + (b.isStatic() || b.isSleeping()) + ){ + return true; + } + + // Arbiter was used last frame, but not this one + if(ticks >= 1 && arb.state != 'cached'){ + arb.callSeparate(this); + arb.state = 'cached'; + } + + if(ticks >= this.collisionPersistence){ + arb.contacts = null; + + //cpArrayPush(this.pooledArbiters, arb); + return false; + } + + return true; +}; + +// **** All Important cpSpaceStep() Function + +var updateFunc = function(shape) +{ + var body = shape.body; + shape.update(body.p, body.rot); +}; + +/// Step the space forward in time by @c dt. +Space.prototype.step = function(dt) +{ + // don't step if the timestep is 0! + if(dt === 0) return; + + assert(vzero.x === 0 && vzero.y === 0, "vzero is invalid"); + + this.stamp++; + + var prev_dt = this.curr_dt; + this.curr_dt = dt; + + var i; + var j; + var hash; + var bodies = this.bodies; + var constraints = this.constraints; + var arbiters = this.arbiters; + + // Reset and empty the arbiter lists. + for(i=0; imaxForce*(dt)) + +// a and b are bodies. +var relative_velocity = function(a, b, r1, r2){ + //var v1_sum = vadd(a.v, vmult(vperp(r1), a.w)); + var v1_sumx = a.vx + (-r1.y) * a.w; + var v1_sumy = a.vy + ( r1.x) * a.w; + + //var v2_sum = vadd(b.v, vmult(vperp(r2), b.w)); + var v2_sumx = b.vx + (-r2.y) * b.w; + var v2_sumy = b.vy + ( r2.x) * b.w; + +// return vsub(v2_sum, v1_sum); + return new Vect(v2_sumx - v1_sumx, v2_sumy - v1_sumy); +}; + +var normal_relative_velocity = function(a, b, r1, r2, n){ + //return vdot(relative_velocity(a, b, r1, r2), n); + var v1_sumx = a.vx + (-r1.y) * a.w; + var v1_sumy = a.vy + ( r1.x) * a.w; + var v2_sumx = b.vx + (-r2.y) * b.w; + var v2_sumy = b.vy + ( r2.x) * b.w; + + return vdot2(v2_sumx - v1_sumx, v2_sumy - v1_sumy, n.x, n.y); +}; + +/* +var apply_impulse = function(body, j, r){ + body.v = vadd(body.v, vmult(j, body.m_inv)); + body.w += body.i_inv*vcross(r, j); +}; + +var apply_impulses = function(a, b, r1, r2, j) +{ + apply_impulse(a, vneg(j), r1); + apply_impulse(b, j, r2); +}; +*/ + +var apply_impulse = function(body, jx, jy, r){ +// body.v = body.v.add(vmult(j, body.m_inv)); + body.vx += jx * body.m_inv; + body.vy += jy * body.m_inv; +// body.w += body.i_inv*vcross(r, j); + body.w += body.i_inv*(r.x*jy - r.y*jx); +}; + +var apply_impulses = function(a, b, r1, r2, jx, jy) +{ + apply_impulse(a, -jx, -jy, r1); + apply_impulse(b, jx, jy, r2); +}; + +var apply_bias_impulse = function(body, jx, jy, r) +{ + //body.v_bias = vadd(body.v_bias, vmult(j, body.m_inv)); + body.v_biasx += jx * body.m_inv; + body.v_biasy += jy * body.m_inv; + body.w_bias += body.i_inv*vcross2(r.x, r.y, jx, jy); +}; + +/* +var apply_bias_impulses = function(a, b, r1, r2, j) +{ + apply_bias_impulse(a, vneg(j), r1); + apply_bias_impulse(b, j, r2); +};*/ + +var k_scalar_body = function(body, r, n) +{ + var rcn = vcross(r, n); + return body.m_inv + body.i_inv*rcn*rcn; +}; + +var k_scalar = function(a, b, r1, r2, n) +{ + var value = k_scalar_body(a, r1, n) + k_scalar_body(b, r2, n); + assertSoft(value !== 0, "Unsolvable collision or constraint."); + + return value; +}; + +// k1 and k2 are modified by the function to contain the outputs. +var k_tensor = function(a, b, r1, r2, k1, k2) +{ + // calculate mass matrix + // If I wasn't lazy and wrote a proper matrix class, this wouldn't be so gross... + var k11, k12, k21, k22; + var m_sum = a.m_inv + b.m_inv; + + // start with I*m_sum + k11 = m_sum; k12 = 0; + k21 = 0; k22 = m_sum; + + // add the influence from r1 + var a_i_inv = a.i_inv; + var r1xsq = r1.x * r1.x * a_i_inv; + var r1ysq = r1.y * r1.y * a_i_inv; + var r1nxy = -r1.x * r1.y * a_i_inv; + k11 += r1ysq; k12 += r1nxy; + k21 += r1nxy; k22 += r1xsq; + + // add the influnce from r2 + var b_i_inv = b.i_inv; + var r2xsq = r2.x * r2.x * b_i_inv; + var r2ysq = r2.y * r2.y * b_i_inv; + var r2nxy = -r2.x * r2.y * b_i_inv; + k11 += r2ysq; k12 += r2nxy; + k21 += r2nxy; k22 += r2xsq; + + // invert + var determinant = k11*k22 - k12*k21; + assertSoft(determinant !== 0, "Unsolvable constraint."); + + var det_inv = 1/determinant; + + k1.x = k22*det_inv; k1.y = -k12*det_inv; + k2.x = -k21*det_inv; k2.y = k11*det_inv; +}; + +var mult_k = function(vr, k1, k2) +{ + return new Vect(vdot(vr, k1), vdot(vr, k2)); +}; + +var bias_coef = function(errorBias, dt) +{ + return 1 - Math.pow(errorBias, dt); +}; + +/* Copyright (c) 2007 Scott Lembcke + * + * 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. + */ + +// TODO: Comment me! + +// a and b are bodies that the constraint applies to. +var Constraint = cp.Constraint = function(a, b) +{ + /// The first body connected to this constraint. + this.a = a; + /// The second body connected to this constraint. + this.b = b; + + this.space = null; + + this.next_a = null; + this.next_b = null; + + /// The maximum force that this constraint is allowed to use. + this.maxForce = Infinity; + /// The rate at which joint error is corrected. + /// Defaults to pow(1 - 0.1, 60) meaning that it will + /// correct 10% of the error every 1/60th of a second. + this.errorBias = Math.pow(1 - 0.1, 60); + /// The maximum rate at which joint error is corrected. + this.maxBias = Infinity; +}; + +Constraint.prototype.activateBodies = function() +{ + if(this.a) this.a.activate(); + if(this.b) this.b.activate(); +}; + +/// These methods are overridden by the constraint itself. +Constraint.prototype.preStep = function(dt) {}; +Constraint.prototype.applyCachedImpulse = function(dt_coef) {}; +Constraint.prototype.applyImpulse = function() {}; +Constraint.prototype.getImpulse = function() { return 0; }; + +/// Function called before the solver runs. This can be overridden by the user +/// to customize the constraint. +/// Animate your joint anchors, update your motor torque, etc. +Constraint.prototype.preSolve = function(space) {}; + +/// Function called after the solver runs. This can be overridden by the user +/// to customize the constraint. +/// Use the applied impulse to perform effects like breakable joints. +Constraint.prototype.postSolve = function(space) {}; + +Constraint.prototype.next = function(body) +{ + return (this.a === body ? this.next_a : this.next_b); +}; + +/* Copyright (c) 2007 Scott Lembcke + * + * 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. + */ + +var PinJoint = cp.PinJoint = function(a, b, anchr1, anchr2) +{ + Constraint.call(this, a, b); + + this.anchr1 = anchr1; + this.anchr2 = anchr2; + + // STATIC_BODY_CHECK + var p1 = (a ? vadd(a.p, vrotate(anchr1, a.rot)) : anchr1); + var p2 = (b ? vadd(b.p, vrotate(anchr2, b.rot)) : anchr2); + this.dist = vlength(vsub(p2, p1)); + + assertSoft(this.dist > 0, "You created a 0 length pin joint. A pivot joint will be much more stable."); + + this.r1 = this.r2 = null; + this.n = null; + this.nMass = 0; + + this.jnAcc = this.jnMax = 0; + this.bias = 0; +}; + +PinJoint.prototype = Object.create(Constraint.prototype); + +PinJoint.prototype.preStep = function(dt) +{ + var a = this.a; + var b = this.b; + + this.r1 = vrotate(this.anchr1, a.rot); + this.r2 = vrotate(this.anchr2, b.rot); + + var delta = vsub(vadd(b.p, this.r2), vadd(a.p, this.r1)); + var dist = vlength(delta); + this.n = vmult(delta, 1/(dist ? dist : Infinity)); + + // calculate mass normal + this.nMass = 1/k_scalar(a, b, this.r1, this.r2, this.n); + + // calculate bias velocity + var maxBias = this.maxBias; + this.bias = clamp(-bias_coef(this.errorBias, dt)*(dist - this.dist)/dt, -maxBias, maxBias); + + // compute max impulse + this.jnMax = this.maxForce * dt; +}; + +PinJoint.prototype.applyCachedImpulse = function(dt_coef) +{ + var j = vmult(this.n, this.jnAcc*dt_coef); + apply_impulses(this.a, this.b, this.r1, this.r2, j.x, j.y); +}; + +PinJoint.prototype.applyImpulse = function() +{ + var a = this.a; + var b = this.b; + var n = this.n; + + // compute relative velocity + var vrn = normal_relative_velocity(a, b, this.r1, this.r2, n); + + // compute normal impulse + var jn = (this.bias - vrn)*this.nMass; + var jnOld = this.jnAcc; + this.jnAcc = clamp(jnOld + jn, -this.jnMax, this.jnMax); + jn = this.jnAcc - jnOld; + + // apply impulse + apply_impulses(a, b, this.r1, this.r2, n.x*jn, n.y*jn); +}; + +PinJoint.prototype.getImpulse = function() +{ + return Math.abs(this.jnAcc); +}; + +/* Copyright (c) 2007 Scott Lembcke + * + * 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. + */ + +var SlideJoint = cp.SlideJoint = function(a, b, anchr1, anchr2, min, max) +{ + Constraint.call(this, a, b); + + this.anchr1 = anchr1; + this.anchr2 = anchr2; + this.min = min; + this.max = max; + + this.r1 = this.r2 = this.n = null; + this.nMass = 0; + + this.jnAcc = this.jnMax = 0; + this.bias = 0; +}; + +SlideJoint.prototype = Object.create(Constraint.prototype); + +SlideJoint.prototype.preStep = function(dt) +{ + var a = this.a; + var b = this.b; + + this.r1 = vrotate(this.anchr1, a.rot); + this.r2 = vrotate(this.anchr2, b.rot); + + var delta = vsub(vadd(b.p, this.r2), vadd(a.p, this.r1)); + var dist = vlength(delta); + var pdist = 0; + if(dist > this.max) { + pdist = dist - this.max; + this.n = vnormalize_safe(delta); + } else if(dist < this.min) { + pdist = this.min - dist; + this.n = vneg(vnormalize_safe(delta)); + } else { + this.n = vzero; + this.jnAcc = 0; + } + + // calculate mass normal + this.nMass = 1/k_scalar(a, b, this.r1, this.r2, this.n); + + // calculate bias velocity + var maxBias = this.maxBias; + this.bias = clamp(-bias_coef(this.errorBias, dt)*pdist/dt, -maxBias, maxBias); + + // compute max impulse + this.jnMax = this.maxForce * dt; +}; + +SlideJoint.prototype.applyCachedImpulse = function(dt_coef) +{ + var jn = this.jnAcc * dt_coef; + apply_impulses(this.a, this.b, this.r1, this.r2, this.n.x * jn, this.n.y * jn); +}; + +SlideJoint.prototype.applyImpulse = function() +{ + if(this.n.x === 0 && this.n.y === 0) return; // early exit + + var a = this.a; + var b = this.b; + + var n = this.n; + var r1 = this.r1; + var r2 = this.r2; + + // compute relative velocity + var vr = relative_velocity(a, b, r1, r2); + var vrn = vdot(vr, n); + + // compute normal impulse + var jn = (this.bias - vrn)*this.nMass; + var jnOld = this.jnAcc; + this.jnAcc = clamp(jnOld + jn, -this.jnMax, 0); + jn = this.jnAcc - jnOld; + + // apply impulse + apply_impulses(a, b, this.r1, this.r2, n.x * jn, n.y * jn); +}; + +SlideJoint.prototype.getImpulse = function() +{ + return Math.abs(this.jnAcc); +}; + +/* Copyright (c) 2007 Scott Lembcke + * + * 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. + */ + +// Pivot joints can also be created with (a, b, pivot); +var PivotJoint = cp.PivotJoint = function(a, b, anchr1, anchr2) +{ + Constraint.call(this, a, b); + + if(typeof anchr2 === 'undefined') { + var pivot = anchr1; + + anchr1 = (a ? a.world2Local(pivot) : pivot); + anchr2 = (b ? b.world2Local(pivot) : pivot); + } + + this.anchr1 = anchr1; + this.anchr2 = anchr2; + + this.r1 = this.r2 = vzero; + + this.k1 = new Vect(0,0); this.k2 = new Vect(0,0); + + this.jAcc = vzero; + + this.jMaxLen = 0; + this.bias = vzero; +}; + +PivotJoint.prototype = Object.create(Constraint.prototype); + +PivotJoint.prototype.preStep = function(dt) +{ + var a = this.a; + var b = this.b; + + this.r1 = vrotate(this.anchr1, a.rot); + this.r2 = vrotate(this.anchr2, b.rot); + + // Calculate mass tensor. Result is stored into this.k1 & this.k2. + k_tensor(a, b, this.r1, this.r2, this.k1, this.k2); + + // compute max impulse + this.jMaxLen = this.maxForce * dt; + + // calculate bias velocity + var delta = vsub(vadd(b.p, this.r2), vadd(a.p, this.r1)); + this.bias = vclamp(vmult(delta, -bias_coef(this.errorBias, dt)/dt), this.maxBias); +}; + +PivotJoint.prototype.applyCachedImpulse = function(dt_coef) +{ + apply_impulses(this.a, this.b, this.r1, this.r2, this.jAcc.x * dt_coef, this.jAcc.y * dt_coef); +}; + +PivotJoint.prototype.applyImpulse = function() +{ + var a = this.a; + var b = this.b; + + var r1 = this.r1; + var r2 = this.r2; + + // compute relative velocity + var vr = relative_velocity(a, b, r1, r2); + + // compute normal impulse + var j = mult_k(vsub(this.bias, vr), this.k1, this.k2); + var jOld = this.jAcc; + this.jAcc = vclamp(vadd(this.jAcc, j), this.jMaxLen); + + // apply impulse + apply_impulses(a, b, this.r1, this.r2, this.jAcc.x - jOld.x, this.jAcc.y - jOld.y); +}; + +PivotJoint.prototype.getImpulse = function() +{ + return vlength(this.jAcc); +}; + +/* Copyright (c) 2007 Scott Lembcke + * + * 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. + */ + +var GrooveJoint = cp.GrooveJoint = function(a, b, groove_a, groove_b, anchr2) +{ + Constraint.call(this, a, b); + + this.grv_a = groove_a; + this.grv_b = groove_b; + this.grv_n = vperp(vnormalize(vsub(groove_b, groove_a))); + this.anchr2 = anchr2; + + this.grv_tn = null; + this.clamp = 0; + this.r1 = this.r2 = null; + + this.k1 = new Vect(0,0); + this.k2 = new Vect(0,0); + + this.jAcc = vzero; + this.jMaxLen = 0; + this.bias = null; +}; + +GrooveJoint.prototype = Object.create(Constraint.prototype); + +GrooveJoint.prototype.preStep = function(dt) +{ + var a = this.a; + var b = this.b; + + // calculate endpoints in worldspace + var ta = a.local2World(this.grv_a); + var tb = a.local2World(this.grv_b); + + // calculate axis + var n = vrotate(this.grv_n, a.rot); + var d = vdot(ta, n); + + this.grv_tn = n; + this.r2 = vrotate(this.anchr2, b.rot); + + // calculate tangential distance along the axis of r2 + var td = vcross(vadd(b.p, this.r2), n); + // calculate clamping factor and r2 + if(td <= vcross(ta, n)){ + this.clamp = 1; + this.r1 = vsub(ta, a.p); + } else if(td >= vcross(tb, n)){ + this.clamp = -1; + this.r1 = vsub(tb, a.p); + } else { + this.clamp = 0; + this.r1 = vsub(vadd(vmult(vperp(n), -td), vmult(n, d)), a.p); + } + + // Calculate mass tensor + k_tensor(a, b, this.r1, this.r2, this.k1, this.k2); + + // compute max impulse + this.jMaxLen = this.maxForce * dt; + + // calculate bias velocity + var delta = vsub(vadd(b.p, this.r2), vadd(a.p, this.r1)); + this.bias = vclamp(vmult(delta, -bias_coef(this.errorBias, dt)/dt), this.maxBias); +}; + +GrooveJoint.prototype.applyCachedImpulse = function(dt_coef) +{ + apply_impulses(this.a, this.b, this.r1, this.r2, this.jAcc.x * dt_coef, this.jAcc.y * dt_coef); +}; + +GrooveJoint.prototype.grooveConstrain = function(j){ + var n = this.grv_tn; + var jClamp = (this.clamp*vcross(j, n) > 0) ? j : vproject(j, n); + return vclamp(jClamp, this.jMaxLen); +}; + +GrooveJoint.prototype.applyImpulse = function() +{ + var a = this.a; + var b = this.b; + + var r1 = this.r1; + var r2 = this.r2; + + // compute impulse + var vr = relative_velocity(a, b, r1, r2); + + var j = mult_k(vsub(this.bias, vr), this.k1, this.k2); + var jOld = this.jAcc; + this.jAcc = this.grooveConstrain(vadd(jOld, j)); + + // apply impulse + apply_impulses(a, b, this.r1, this.r2, this.jAcc.x - jOld.x, this.jAcc.y - jOld.y); +}; + +GrooveJoint.prototype.getImpulse = function() +{ + return vlength(this.jAcc); +}; + +GrooveJoint.prototype.setGrooveA = function(value) +{ + this.grv_a = value; + this.grv_n = vperp(vnormalize(vsub(this.grv_b, value))); + + this.activateBodies(); +}; + +GrooveJoint.prototype.setGrooveB = function(value) +{ + this.grv_b = value; + this.grv_n = vperp(vnormalize(vsub(value, this.grv_a))); + + this.activateBodies(); +}; + +/* Copyright (c) 2007 Scott Lembcke + * + * 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. + */ + +var defaultSpringForce = function(spring, dist){ + return (spring.restLength - dist)*spring.stiffness; +}; + +var DampedSpring = cp.DampedSpring = function(a, b, anchr1, anchr2, restLength, stiffness, damping) +{ + Constraint.call(this, a, b); + + this.anchr1 = anchr1; + this.anchr2 = anchr2; + + this.restLength = restLength; + this.stiffness = stiffness; + this.damping = damping; + this.springForceFunc = defaultSpringForce; + + this.target_vrn = this.v_coef = 0; + + this.r1 = this.r2 = null; + this.nMass = 0; + this.n = null; +}; + +DampedSpring.prototype = Object.create(Constraint.prototype); + +DampedSpring.prototype.preStep = function(dt) +{ + var a = this.a; + var b = this.b; + + this.r1 = vrotate(this.anchr1, a.rot); + this.r2 = vrotate(this.anchr2, b.rot); + + var delta = vsub(vadd(b.p, this.r2), vadd(a.p, this.r1)); + var dist = vlength(delta); + this.n = vmult(delta, 1/(dist ? dist : Infinity)); + + var k = k_scalar(a, b, this.r1, this.r2, this.n); + assertSoft(k !== 0, "Unsolvable this."); + this.nMass = 1/k; + + this.target_vrn = 0; + this.v_coef = 1 - Math.exp(-this.damping*dt*k); + + // apply this force + var f_spring = this.springForceFunc(this, dist); + apply_impulses(a, b, this.r1, this.r2, this.n.x * f_spring * dt, this.n.y * f_spring * dt); +}; + +DampedSpring.prototype.applyCachedImpulse = function(dt_coef){}; + +DampedSpring.prototype.applyImpulse = function() +{ + var a = this.a; + var b = this.b; + + var n = this.n; + var r1 = this.r1; + var r2 = this.r2; + + // compute relative velocity + var vrn = normal_relative_velocity(a, b, r1, r2, n); + + // compute velocity loss from drag + var v_damp = (this.target_vrn - vrn)*this.v_coef; + this.target_vrn = vrn + v_damp; + + v_damp *= this.nMass; + apply_impulses(a, b, this.r1, this.r2, this.n.x * v_damp, this.n.y * v_damp); +}; + +DampedSpring.prototype.getImpulse = function() +{ + return 0; +}; + +/* Copyright (c) 2007 Scott Lembcke + * + * 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. + */ + +var defaultSpringTorque = function(spring, relativeAngle){ + return (relativeAngle - spring.restAngle)*spring.stiffness; +} + +var DampedRotarySpring = cp.DampedRotarySpring = function(a, b, restAngle, stiffness, damping) +{ + Constraint.call(this, a, b); + + this.restAngle = restAngle; + this.stiffness = stiffness; + this.damping = damping; + this.springTorqueFunc = defaultSpringTorque; + + this.target_wrn = 0; + this.w_coef = 0; + this.iSum = 0; +}; + +DampedRotarySpring.prototype = Object.create(Constraint.prototype); + +DampedRotarySpring.prototype.preStep = function(dt) +{ + var a = this.a; + var b = this.b; + + var moment = a.i_inv + b.i_inv; + assertSoft(moment !== 0, "Unsolvable spring."); + this.iSum = 1/moment; + + this.w_coef = 1 - Math.exp(-this.damping*dt*moment); + this.target_wrn = 0; + + // apply this torque + var j_spring = this.springTorqueFunc(this, a.a - b.a)*dt; + a.w -= j_spring*a.i_inv; + b.w += j_spring*b.i_inv; +}; + +// DampedRotarySpring.prototype.applyCachedImpulse = function(dt_coef){}; + +DampedRotarySpring.prototype.applyImpulse = function() +{ + var a = this.a; + var b = this.b; + + // compute relative velocity + var wrn = a.w - b.w;//normal_relative_velocity(a, b, r1, r2, n) - this.target_vrn; + + // compute velocity loss from drag + // not 100% certain spring is derived correctly, though it makes sense + var w_damp = (this.target_wrn - wrn)*this.w_coef; + this.target_wrn = wrn + w_damp; + + //apply_impulses(a, b, this.r1, this.r2, vmult(this.n, v_damp*this.nMass)); + var j_damp = w_damp*this.iSum; + a.w += j_damp*a.i_inv; + b.w -= j_damp*b.i_inv; +}; + +// DampedRotarySpring.prototype.getImpulse = function(){ return 0; }; + +/* Copyright (c) 2007 Scott Lembcke + * + * 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. + */ + +var RotaryLimitJoint = cp.RotaryLimitJoint = function(a, b, min, max) +{ + Constraint.call(this, a, b); + + this.min = min; + this.max = max; + + this.jAcc = 0; + + this.iSum = this.bias = this.jMax = 0; +}; + +RotaryLimitJoint.prototype = Object.create(Constraint.prototype); + +RotaryLimitJoint.prototype.preStep = function(dt) +{ + var a = this.a; + var b = this.b; + + var dist = b.a - a.a; + var pdist = 0; + if(dist > this.max) { + pdist = this.max - dist; + } else if(dist < this.min) { + pdist = this.min - dist; + } + + // calculate moment of inertia coefficient. + this.iSum = 1/(1/a.i + 1/b.i); + + // calculate bias velocity + var maxBias = this.maxBias; + this.bias = clamp(-bias_coef(this.errorBias, dt)*pdist/dt, -maxBias, maxBias); + + // compute max impulse + this.jMax = this.maxForce * dt; + + // If the bias is 0, the joint is not at a limit. Reset the impulse. + if(!this.bias) this.jAcc = 0; +}; + +RotaryLimitJoint.prototype.applyCachedImpulse = function(dt_coef) +{ + var a = this.a; + var b = this.b; + + var j = this.jAcc*dt_coef; + a.w -= j*a.i_inv; + b.w += j*b.i_inv; +}; + +RotaryLimitJoint.prototype.applyImpulse = function() +{ + if(!this.bias) return; // early exit + + var a = this.a; + var b = this.b; + + // compute relative rotational velocity + var wr = b.w - a.w; + + // compute normal impulse + var j = -(this.bias + wr)*this.iSum; + var jOld = this.jAcc; + if(this.bias < 0){ + this.jAcc = clamp(jOld + j, 0, this.jMax); + } else { + this.jAcc = clamp(jOld + j, -this.jMax, 0); + } + j = this.jAcc - jOld; + + // apply impulse + a.w -= j*a.i_inv; + b.w += j*b.i_inv; +}; + +RotaryLimitJoint.prototype.getImpulse = function() +{ + return Math.abs(joint.jAcc); +}; + +/* Copyright (c) 2007 Scott Lembcke + * + * 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. + */ + +var RatchetJoint = cp.RatchetJoint = function(a, b, phase, ratchet) +{ + Constraint.call(this, a, b); + + this.angle = 0; + this.phase = phase; + this.ratchet = ratchet; + + // STATIC_BODY_CHECK + this.angle = (b ? b.a : 0) - (a ? a.a : 0); + + this.iSum = this.bias = this.jAcc = this.jMax = 0; +}; + +RatchetJoint.prototype = Object.create(Constraint.prototype); + +RatchetJoint.prototype.preStep = function(dt) +{ + var a = this.a; + var b = this.b; + + var angle = this.angle; + var phase = this.phase; + var ratchet = this.ratchet; + + var delta = b.a - a.a; + var diff = angle - delta; + var pdist = 0; + + if(diff*ratchet > 0){ + pdist = diff; + } else { + this.angle = Math.floor((delta - phase)/ratchet)*ratchet + phase; + } + + // calculate moment of inertia coefficient. + this.iSum = 1/(a.i_inv + b.i_inv); + + // calculate bias velocity + var maxBias = this.maxBias; + this.bias = clamp(-bias_coef(this.errorBias, dt)*pdist/dt, -maxBias, maxBias); + + // compute max impulse + this.jMax = this.maxForce * dt; + + // If the bias is 0, the joint is not at a limit. Reset the impulse. + if(!this.bias) this.jAcc = 0; +}; + +RatchetJoint.prototype.applyCachedImpulse = function(dt_coef) +{ + var a = this.a; + var b = this.b; + + var j = this.jAcc*dt_coef; + a.w -= j*a.i_inv; + b.w += j*b.i_inv; +}; + +RatchetJoint.prototype.applyImpulse = function() +{ + if(!this.bias) return; // early exit + + var a = this.a; + var b = this.b; + + // compute relative rotational velocity + var wr = b.w - a.w; + var ratchet = this.ratchet; + + // compute normal impulse + var j = -(this.bias + wr)*this.iSum; + var jOld = this.jAcc; + this.jAcc = clamp((jOld + j)*ratchet, 0, this.jMax*Math.abs(ratchet))/ratchet; + j = this.jAcc - jOld; + + // apply impulse + a.w -= j*a.i_inv; + b.w += j*b.i_inv; +}; + +RatchetJoint.prototype.getImpulse = function(joint) +{ + return Math.abs(joint.jAcc); +}; + +/* Copyright (c) 2007 Scott Lembcke + * + * 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. + */ + +var GearJoint = cp.GearJoint = function(a, b, phase, ratio) +{ + Constraint.call(this, a, b); + + this.phase = phase; + this.ratio = ratio; + this.ratio_inv = 1/ratio; + + this.jAcc = 0; + + this.iSum = this.bias = this.jMax = 0; +}; + +GearJoint.prototype = Object.create(Constraint.prototype); + +GearJoint.prototype.preStep = function(dt) +{ + var a = this.a; + var b = this.b; + + // calculate moment of inertia coefficient. + this.iSum = 1/(a.i_inv*this.ratio_inv + this.ratio*b.i_inv); + + // calculate bias velocity + var maxBias = this.maxBias; + this.bias = clamp(-bias_coef(this.errorBias, dt)*(b.a*this.ratio - a.a - this.phase)/dt, -maxBias, maxBias); + + // compute max impulse + this.jMax = this.maxForce * dt; +}; + +GearJoint.prototype.applyCachedImpulse = function(dt_coef) +{ + var a = this.a; + var b = this.b; + + var j = this.jAcc*dt_coef; + a.w -= j*a.i_inv*this.ratio_inv; + b.w += j*b.i_inv; +}; + +GearJoint.prototype.applyImpulse = function() +{ + var a = this.a; + var b = this.b; + + // compute relative rotational velocity + var wr = b.w*this.ratio - a.w; + + // compute normal impulse + var j = (this.bias - wr)*this.iSum; + var jOld = this.jAcc; + this.jAcc = clamp(jOld + j, -this.jMax, this.jMax); + j = this.jAcc - jOld; + + // apply impulse + a.w -= j*a.i_inv*this.ratio_inv; + b.w += j*b.i_inv; +}; + +GearJoint.prototype.getImpulse = function() +{ + return Math.abs(this.jAcc); +}; + +GearJoint.prototype.setRatio = function(value) +{ + this.ratio = value; + this.ratio_inv = 1/value; + this.activateBodies(); +}; + +/* Copyright (c) 2007 Scott Lembcke + * + * 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. + */ + +var SimpleMotor = cp.SimpleMotor = function(a, b, rate) +{ + Constraint.call(this, a, b); + + this.rate = rate; + + this.jAcc = 0; + + this.iSum = this.jMax = 0; +}; + +SimpleMotor.prototype = Object.create(Constraint.prototype); + +SimpleMotor.prototype.preStep = function(dt) +{ + // calculate moment of inertia coefficient. + this.iSum = 1/(this.a.i_inv + this.b.i_inv); + + // compute max impulse + this.jMax = this.maxForce * dt; +}; + +SimpleMotor.prototype.applyCachedImpulse = function(dt_coef) +{ + var a = this.a; + var b = this.b; + + var j = this.jAcc*dt_coef; + a.w -= j*a.i_inv; + b.w += j*b.i_inv; +}; + +SimpleMotor.prototype.applyImpulse = function() +{ + var a = this.a; + var b = this.b; + + // compute relative rotational velocity + var wr = b.w - a.w + this.rate; + + // compute normal impulse + var j = -wr*this.iSum; + var jOld = this.jAcc; + this.jAcc = clamp(jOld + j, -this.jMax, this.jMax); + j = this.jAcc - jOld; + + // apply impulse + a.w -= j*a.i_inv; + b.w += j*b.i_inv; +}; + +SimpleMotor.prototype.getImpulse = function() +{ + return Math.abs(this.jAcc); +}; + +})(); diff --git a/frameworks/cocos2d-html5/external/gaf/GAFBoot.js b/frameworks/cocos2d-html5/external/gaf/GAFBoot.js new file mode 100644 index 0000000..0baccc7 --- /dev/null +++ b/frameworks/cocos2d-html5/external/gaf/GAFBoot.js @@ -0,0 +1,24 @@ +var gaf = gaf || {}; +gaf._tmp = gaf._tmp || {}; +gaf._initialized = false; + +gaf.CCGAFLoader = function() +{ + this.load = function(realUrl, url, item, cb) + { + if(!gaf._initialized) + { + gaf._setup(); + } + var loader = new gaf.Loader(); + loader.LoadFile(realUrl, function(data){cb(null, data)}); + }; +}; + +gaf._setup = function() +{ + gaf._setupShaders(); + gaf._initialized = true; +}; + +cc.loader.register('.gaf', new gaf.CCGAFLoader()); diff --git a/frameworks/cocos2d-html5/external/gaf/GAFMacros.js b/frameworks/cocos2d-html5/external/gaf/GAFMacros.js new file mode 100644 index 0000000..ef34cc8 --- /dev/null +++ b/frameworks/cocos2d-html5/external/gaf/GAFMacros.js @@ -0,0 +1,33 @@ +var gaf = gaf || {}; + +gaf.COMPRESSION_NONE = 0x00474146; +gaf.COMPRESSION_ZIP = 0x00474143; + +gaf.IDNONE = 0xffffffff; +gaf.FIRST_FRAME_INDEX = 0; + +gaf.EFFECT_DROP_SHADOW = 0; +gaf.EFFECT_BLUR = 1; +gaf.EFFECT_GLOW = 2; +gaf.EFFECT_COLOR_MATRIX = 6; + +gaf.ACTION_STOP = 0; +gaf.ACTION_PLAY = 1; +gaf.ACTION_GO_TO_AND_STOP = 2; +gaf.ACTION_GO_TO_AND_PLAY = 3; +gaf.ACTION_DISPATCH_EVENT = 4; + +gaf.PI_FRAME = 0; +gaf.PI_EVENT_TYPE = 0; + +gaf.TYPE_TEXTURE = 0; +gaf.TYPE_TEXT_FIELD = 1; +gaf.TYPE_TIME_LINE = 2; + +gaf.UNIFORM_BLUR_TEXEL_OFFSET = "u_step"; +gaf.UNIFORM_GLOW_TEXEL_OFFSET = "u_step"; +gaf.UNIFORM_GLOW_COLOR = "u_glowColor"; +gaf.UNIFORM_ALPHA_TINT_MULT = "colorTransformMult"; +gaf.UNIFORM_ALPHA_TINT_OFFSET = "colorTransformOffsets"; +gaf.UNIFORM_ALPHA_COLOR_MATRIX_BODY = "colorMatrix"; +gaf.UNIFORM_ALPHA_COLOR_MATRIX_APPENDIX = "colorMatrix2"; diff --git a/frameworks/cocos2d-html5/external/gaf/Library/GAFAsset.js b/frameworks/cocos2d-html5/external/gaf/Library/GAFAsset.js new file mode 100644 index 0000000..23804e6 --- /dev/null +++ b/frameworks/cocos2d-html5/external/gaf/Library/GAFAsset.js @@ -0,0 +1,429 @@ +var gaf = gaf || {}; + +gaf.Asset = cc.Class.extend +({ + _className: "GAFAsset", + + // Private members + _header: null, + _timeLines: null, + _textFields: null, + _protos: null, + _objects: null, + _masks: null, + + _rootTimeLine: null, + _textureLoadDelegate: null, + _sceneFps: 60, + _sceneWidth: 0, + _sceneHeight: 0, + _sceneColor: 0, + _gafData: null, + _desiredAtlasScale: 1, + _usedAtlasScale: 0, + + _atlases: null, + _onLoadTasks: null, + _atlasScales: null, + _textureLoaded: false, // For async loading with cc.event manager + _atlasesToLoad: null, // Atlases that are not yet loaded + _gafName: null, + + /** + * @method initWithGAFFile + * @param {String} filePath - path to .gaf file + * @param {String function(String)} textureLoadDelegate - is used to change atlas path, e.g. to load `atlas.tga` instead of `atlas.png` + * @return {bool} + */ + initWithGAFFile: function (filePath, textureLoadDelegate) { + var self = this; + this._textureLoadDelegate = textureLoadDelegate; + this._gafName = filePath; + var gafData = cc.loader.getRes(filePath); + if(!gafData) + { + cc.loader.load(filePath, function(err, data){ + if(!err) + { + self._init(data[0]); + } + }); + } + else { + return this._init(gafData); + } + return false; + }, + + /** + * @method initWithGAFBundle + * @param {String} zipFilePath - path to the archive with .gaf and its textures + * @param {String} entryFile - name of the .gaf file in archive + * @param {function({path:String})} delegate - is used to change atlas path, e.g. to load `atlas.tga` instead of `atlas.png` + * @return {bool} + */ + initWithGAFBundle: function (zipFilePath, entryFile, delegate) + { + cc.assert(false, "initWithGAFBundle is not yet implemented"); + return false; + }, + + /** + * @method setRootTimelineWithName + * @param {String} name + */ + setRootTimelineWithName: function (name) + { + for(var i = 0, end = this._timeLines.length; i < end; ++i) + { + var object = this._timeLines[i]; + if (object && object.getLinkageName() === name) + { + this._setRootTimeline(object); + return; + } + } + }, + +/* addEventListener: function(name, listener) + {},*/ + + isAssetVersionPlayable: function () + { + return true; + }, + + /** + * Desired atlas scale. + * Default is 1.0f + * @returns {number} + */ + desiredAtlasScale : function(){ + return this._desiredAtlasScale; + }, + + /** + * Sets desired atlas scale. Will choose nearest atlas scale from available. + * Default is 1.0f + * @param scale + */ + setDesiredAtlasScale : function(desiredAtlasScale){ + this._desiredAtlasScale = desiredAtlasScale; + for(var currentScale in this._atlasScales)if(this._atlasScales.hasOwnProperty(currentScale)) + { + if( (this._usedAtlasScale === 0) || + (Math.abs(this._usedAtlasScale - desiredAtlasScale) > Math.abs(currentScale - desiredAtlasScale) )) + { + this._usedAtlasScale = currentScale; + } + + } + }, + + /** + * @method createObject + * @return {gaf.Object} + */ + createObject: function () + { + return this._instantiateGaf(this._gafData); + }, + + /** + * @method createObjectAndRun + * @param {boolean} arg0 - run looped + * @return {gaf.Object} + */ + createObjectAndRun: function (looped) + { + cc.assert(arguments.length === 1, "GAFAsset::createObjectAndRun should have one param"); + var object = this._instantiateGaf(this._gafData); + object.setLooped(looped, true); + object.start(); + return object; + }, + + /** + * @method setTextureLoadDelegate + * @param {function} delegate + */ + setTextureLoadDelegate: function (delegate) + { + debugger; + }, + + + /** + * @method getSceneFps + * @return {uint} + */ + getSceneFps: function () + { + return this._sceneFps; + }, + + /** + * @method getSceneWidth + * @return {uint} + */ + getSceneWidth: function () + { + debugger; + }, + + /** + * @method getSceneHeight + * @return {uint} + */ + getSceneHeight: function () + { + debugger; + }, + + /** + * @method getSceneColor + * @return {cc.color4b} + */ + getSceneColor: function () + { + debugger; + }, + + /** + * @method setSceneFps + * @param {uint} fps + */ + setSceneFps: function (fps) + { + this._sceneFps = fps; + }, + + /** + * @method setSceneWidth + * @param {uint} width + */ + setSceneWidth: function (width) + { + debugger; + }, + + /** + * @method setSceneHeight + * @param {uint} height + */ + setSceneHeight: function (height) + { + debugger; + }, + + /** + * @method setSceneColor + * @param {color4b_object} arg0 + */ + setSceneColor: function (color4B) + { + debugger; + }, + + /** + * @method getHeader + * @return {GAFHeader} + */ + getHeader: function () + { + return this._header; + }, + + getGAFFileName: function() + { + return this._gafName; + }, + + // Private + + ctor : function() + { + this._header = {}; + this._timeLines = []; + this._textFields = []; + this._objects = []; + this._masks = []; + this._protos = []; + this._atlases = {}; + this._onLoadTasks = []; + this._atlasScales = {}; + this._atlasesToLoad = {}; + + if(arguments.length > 0) + this.initWithGAFFile.apply(this, arguments); + }, + + _getProtos: function() + { + return this._protos; + }, + + _setRootTimeline : function(timeLine) + { + this._rootTimeLine = timeLine; + this._header.pivot = timeLine.getPivot(); + this._header.frameSize = timeLine.getRect(); + }, + + _setHeader : function (gafHeader) + { + for(var prop in gafHeader) + { + if(gafHeader.hasOwnProperty(prop)) + { + this._header[prop] = gafHeader[prop]; + } + } + }, + + _getMajorVerison : function() + { + return this._header.versionMajor; + }, + + _init : function(gafData) + { + var self = this; + this._gafData = gafData; + this._setHeader(gafData.header); + this._timeLinesToLink = []; + if(this._getMajorVerison() < 4) + { + this._pushTimeLine(new gaf._TimeLineProto(this, this._header.framesCount, this._header.frameSize, this._header.pivot)); + } + gaf._AssetPreload.Tags(this, gafData.tags, this._rootTimeLine); + + //Link and create + this._objects.forEach(function(item) + { + switch(item.type) + { + case gaf.TYPE_TEXTURE: + // Create gaf sprite proto if it is not yet created + if(!self._protos[item.objectId]) + { + self._protos[item.objectId] = new gaf._SpriteProto(self, self._atlasScales, item.elementAtlasIdRef); + } + break; + case gaf.TYPE_TIME_LINE: + // All time line protos are already created, just copy reference + self._protos[item.objectId] = self._timeLines[item.elementAtlasIdRef]; + break; + case gaf.TYPE_TEXT_FIELD: + // All text field protos are already created, just copy reference + self._protos[item.objectId] = self._textFields[item.elementAtlasIdRef]; + break; + default: + cc.log("Unknown object type: " + item.type); + break; + } + }); + this._masks.forEach(function(item) + { + if(self._protos[item.objectId]) + { + return; // this is continue + } + var proto = null; + switch(item.type) + { + case gaf.TYPE_TEXTURE: + // Create gaf sprite proto if it is not yet created + proto = new gaf._SpriteProto(self, self._atlasScales, item.elementAtlasIdRef); + break; + case gaf.TYPE_TIME_LINE: + // All time line protos are already created, just copy reference + proto = self._timeLines[item.elementAtlasIdRef]; + break; + case gaf.TYPE_TEXT_FIELD: + // All text field protos are already created, just copy reference + proto = self._textFields[item.elementAtlasIdRef]; + break; + } + self._protos[item.objectId] = new gaf._MaskProto(self, proto, item.elementAtlasIdRef); + }); + this.setDesiredAtlasScale(this._desiredAtlasScale); + + if(Object.keys(this._atlasesToLoad).length === 0) + { + this._textureLoaded = true; + this.dispatchEvent("load"); + } + }, + + _pushTimeLine : function(timeLine) + { + this._timeLines[timeLine.getId()] = timeLine; + + if(timeLine.getId() === 0) + { + this._setRootTimeline(timeLine); + } + }, + + _instantiateGaf : function() + { + var root = null; + root = this._rootTimeLine._gafConstruct(); + return root; + }, + + _onAtlasLoaded : function(id, atlas) + { + this._atlases[id] = atlas; + delete this._atlasesToLoad[id]; + if(Object.keys(this._atlasesToLoad).length === 0) + { + this._onLoadTasks.forEach(function(fn){fn()}); + this._onLoadTasks.length = 0; + this._textureLoaded = true; + this.dispatchEvent("load"); + } + }, + + isLoaded : function() + { + return this._textureLoaded; + }, + + _getSearchPaths: function(imageUrl) + { + var extendedPath = this.getGAFFileName().split('/'); + extendedPath[extendedPath.length-1] = imageUrl; + var alternativeUrl = extendedPath.join('/'); + + return [imageUrl, alternativeUrl]; + } +}); + +/** + * @method initWithGAFFile + * @param {String} gafFilePath - path to .gaf file + * @param {function({path:String})} delegate - is used to change atlas path, e.g. to load `atlas.tga` instead of `atlas.png` + * @return {gaf.Asset} + */ +gaf.Asset.create = function (gafFilePath, delegate) +{ + return new gaf.Asset(gafFilePath, delegate); +}; + +/** + * @method createWithBundle + * @param {String} zipFilePath - path to the archive with .gaf and its textures + * @param {String} entryFile - name of the .gaf file in archive + * @param {function({path:String})} delegate - is used to change atlas path, e.g. to load `atlas.tga` instead of `atlas.png` + * @return {gaf.Asset} + */ +gaf.Asset.createWithBundle = function (zipFilePath, entryFile, delegate) +{ + var asset = new gaf.Asset(); + asset.initWithGAFBundle(zipFilePath, entryFile, delegate); + return asset; +}; + +cc.EventHelper.prototype.apply(gaf.Asset.prototype); diff --git a/frameworks/cocos2d-html5/external/gaf/Library/GAFAssetPreload.js b/frameworks/cocos2d-html5/external/gaf/Library/GAFAssetPreload.js new file mode 100644 index 0000000..8514e09 --- /dev/null +++ b/frameworks/cocos2d-html5/external/gaf/Library/GAFAssetPreload.js @@ -0,0 +1,270 @@ + +gaf.CGAffineTransformCocosFormatFromFlashFormat = function(transform) +{ + var t = {}; + t.a = transform.a; + t.b = -transform.b; + t.c = -transform.c; + t.d = transform.d; + t.tx = transform.tx; + t.ty = -transform.ty; + return t; +}; + +gaf._AssetPreload = function() +{ + this["0"] = this.End; + this["1"] = this.Atlases; + this["2"] = this.AnimationMasks; + this["3"] = this.AnimationObjects; + this["4"] = this.AnimationFrames; + this["5"] = this.NamedParts; + this["6"] = this.Sequences; + this["7"] = this.TextFields; + this["8"] = this.Atlases; // 2 + this["9"] = this.Stage; + this["10"] = this.AnimationObjects; //2 + this["11"] = this.AnimationMasks; // 2 + this["12"] = this.AnimationFrames; // 2 + this["13"] = this.TimeLine; +}; + +gaf._AssetPreload.prototype.End = function(asset, content, timeLine){ + if(timeLine) + { + timeLine.getFps = function() + { + return asset.getSceneFps(); + }; + } +}; + +gaf._AssetPreload.prototype.Tag = function(asset, tag, timeLine) +{ + (this[tag.tagId]).call(this, asset, tag.content, timeLine); +}; + +gaf._AssetPreload.prototype.Tags = function(asset, tags, timeLine) +{ + var self = this; + tags.forEach(function(tag) + { + self.Tag(asset, tag, timeLine); + }); +}; + +gaf._AssetPreload.prototype.AtlasCreateFrames = function(elements, asset, spriteFrames) +{ + elements.forEach(function (item) { + var texture = asset._atlases[item.atlasId]; + var rect = cc.rect(item.origin.x, item.origin.y, item.size.x, item.size.y); + var frame = new cc.SpriteFrame(texture, rect); + frame._gafAnchor = + { + x: (0 - (0 - (item.pivot.x / item.size.x))), + y: (0 + (1 - (item.pivot.y / item.size.y))) + }; + spriteFrames[item.elementAtlasId] = frame; + // 9 grid + }); +}; + + + +gaf._AssetPreload.prototype.Atlases = function(asset, content, timeLine) +{ + var spriteFrames = asset._atlasScales[content.scale] = asset._atlasScales[content.scale] || []; + var csf = cc.Director._getInstance().getContentScaleFactor(); + + content.atlases.forEach(function(item) + { + var atlasId = item.id; + var finalizeLoading = function() + { + gaf._AssetPreload.AtlasCreateFrames(content.elements, asset, spriteFrames); + }; + + var atlasPath = ""; + item.sources.forEach(function(atlasSource) + { + if(atlasSource.csf === csf) + { + atlasPath = atlasSource.source; + } + }); + cc.assert(atlasPath, "GAF Error. Texture for current CSF not found. Reconvert animation with correct parameters."); + + if(asset._textureLoadDelegate) + { + atlasPath = asset._textureLoadDelegate(atlasPath); + } + + var loaded = false; + var paths = asset._getSearchPaths(atlasPath); + for(var i = 0, len = paths.length; i < len; ++i){ + var path = paths[i]; + var atlas = cc.textureCache.getTextureForKey(path); + if(atlas && atlas.isLoaded()) + { + atlas.handleLoadedTexture(true); + loaded = true; + asset._atlases[atlasId] = atlas; + finalizeLoading(); + break; + } + } + // Need to load atlases async + if(!loaded) + { + var success = function (atlas) { + atlas.handleLoadedTexture(true); + asset._onAtlasLoaded(atlasId, atlas); + }; + + var fail = function () { + cc.log("GAF Error. Couldn't find `" + atlasPath + "` required by `" + asset.getGAFFileName() + "`"); + }; + + if(!asset._atlasesToLoad.hasOwnProperty(atlasId)) + { + gaf._AtlasLoader.loadArray(paths, success, fail); + asset._atlasesToLoad[atlasId] = {}; + } + asset._onLoadTasks.push(finalizeLoading); + } + }); +}; + +gaf._AssetPreload.prototype.AnimationObjects = function(asset, content, timeLine) +{ + content.forEach(function(item) + { + item.type = (item.type === undefined) ? gaf.TYPE_TEXTURE : item.type; + timeLine._objects.push(item.objectId); + asset._objects[item.objectId] = item; + }); +}; + +gaf._AssetPreload.prototype.convertTint = function(mat, alpha) +{ + if(!mat) + return null; + return { + mult: + { + r: mat.redMultiplier * 255, + g: mat.greenMultiplier * 255, + b: mat.blueMultiplier * 255, + a: alpha * 255 + }, + offset: + { + r: mat.redOffset * 255, + g: mat.greenOffset * 255, + b: mat.blueOffset * 255, + a: mat.alphaOffset * 255 + } + }; +}; + +gaf._AssetPreload.prototype.convertState = function(state) +{ + return { + hasColorTransform: state.hasColorTransform, + hasMask: state.hasMask, + hasEffect: state.hasEffect, + objectIdRef: state.objectIdRef, + depth: state.depth, + alpha: state.alpha * 255, + matrix: gaf.CGAffineTransformCocosFormatFromFlashFormat(state.matrix), + colorTransform: this.convertTint(state.colorTransform, state.alpha), + effect: state.effect, + maskObjectIdRef: state.maskObjectIdRef + }; +}; + +gaf._AssetPreload.prototype.AnimationFrames = function(asset, content, timeLine) +{ + var self = this; + cc.assert(timeLine, "Error. Time Line should not be null."); + var statesForId = {}; + var frames = []; + var lastFrame = {}; + for(var i = 0, len = content.length; i < len; ++i) + { + var frame = content[i]; + if(frame.state) + { + frame.state.forEach(function (state) + { + if (state.alpha !== 0) + { + statesForId[state.objectIdRef] = self.convertState(state); + } + else + { + statesForId[state.objectIdRef] = null; + } + }); + } + var stateArray = []; + for(var obj in statesForId){ if(statesForId.hasOwnProperty(obj) && statesForId[obj]) + { + stateArray.push(statesForId[obj]); + }} + lastFrame = frame; + frames[frame.frame - 1] = {states: stateArray, actions: frame.actions || null}; + } + timeLine.getFrames = function(){return frames}; +}; + +gaf._AssetPreload.prototype.NamedParts = function(asset, content, timeLine) +{ + var parts = {}; + content.forEach(function(item) + { + parts[item.name] = item.objectId; + }); + timeLine.getNamedParts = function(){return parts}; +}; + +gaf._AssetPreload.prototype.Sequences = function(asset, content, timeLine) +{ + var sequences = {}; + content.forEach(function(item){ + sequences[item.id] = {start: item.start - 1, end: item.end}; + }); + timeLine.getSequences = function(){return sequences}; +}; + +gaf._AssetPreload.prototype.TextFields = function(asset, content, timeLine) +{ + debugger; +}; + +gaf._AssetPreload.prototype.Stage = function(asset, content, timeLine) +{ + asset._sceneFps = content.fps; + asset._sceneColor = content.color; + asset._sceneWidth = content.width; + asset._sceneHeight = content.height; +}; + +gaf._AssetPreload.prototype.AnimationMasks = function(asset, content, timeLine) +{ + content.forEach(function(item) + { + item.type = (item.type === undefined) ? gaf.TYPE_TEXTURE : item.type; + timeLine._objects.push(item.objectId); + asset._masks[item.objectId] = item; + }); +}; + +gaf._AssetPreload.prototype.TimeLine = function(asset, content, timeLine) +{ + var result = new gaf._TimeLineProto(asset, content.animationFrameCount, content.boundingBox, content.pivotPoint, content.id, content.linkageName); + asset._pushTimeLine(result); + this.Tags(asset, content.tags, result); +}; + +gaf._AssetPreload = new gaf._AssetPreload(); diff --git a/frameworks/cocos2d-html5/external/gaf/Library/GAFAtlasLoader.js b/frameworks/cocos2d-html5/external/gaf/Library/GAFAtlasLoader.js new file mode 100644 index 0000000..152bba0 --- /dev/null +++ b/frameworks/cocos2d-html5/external/gaf/Library/GAFAtlasLoader.js @@ -0,0 +1,50 @@ +/** + * Created by admiral on 19.02.2015. + */ + +gaf._AtlasLoader = {}; +gaf._AtlasLoader.execute = function(condition, success, fail) +{ + condition() ? success() : fail(); +}; + +gaf._AtlasLoader.checkAtlas = function(atlas) // curried function +{ + return function(){return atlas && typeof atlas !== "string" && atlas.isLoaded()}; +}; + +gaf._AtlasLoader.load = function(path, success, fail) +{ + cc.textureCache.addImage(path, function(atlas){ + gaf._AtlasLoader.execute( + gaf._AtlasLoader.checkAtlas(atlas), + function(){success(atlas)}, + fail + ); + }); +}; + +gaf._AtlasLoader.loadFront = function(arr, success, fail) +{ + // Call recursively this function for each element starting from the first + // stops on first success, or fails after last element + return function() + { + if (arr.length > 0){ + gaf._AtlasLoader.load( + arr[0], + success, + gaf._AtlasLoader.loadFront( + arr.slice(1), + success, + fail + ));} + else + fail(); + } +}; + +gaf._AtlasLoader.loadArray = function(array, success, fail) +{ + gaf._AtlasLoader.loadFront(array, success, fail)(); +}; diff --git a/frameworks/cocos2d-html5/external/gaf/Library/GAFDataReader.js b/frameworks/cocos2d-html5/external/gaf/Library/GAFDataReader.js new file mode 100644 index 0000000..3affbe1 --- /dev/null +++ b/frameworks/cocos2d-html5/external/gaf/Library/GAFDataReader.js @@ -0,0 +1,229 @@ +gaf.DataReader = function(data) { + this.dataRaw = data; + this.buf = new DataView(data); + this.offset = [0]; +}; + +gaf.DataReader.prototype.constructor = gaf.DataReader; + +gaf.DataReader.prototype.newOffset = function(size){ + this.offset[this.offset.length - 1] += size; + if(this.getOffset() > this.maxOffset()){ + throw new Error("GAF format error"); + } + return this.offset[this.offset.length - 1] - size; +}; + +gaf.DataReader.prototype.maxOffset = function(){ + if(this.offset.length == 1){ + return this.buf.byteLength; + } + else{ + return this.offset[this.offset.length - 2]; + } +}; + +gaf.DataReader.prototype.getOffset = function(size){ + return this.offset[this.offset.length - 1]; +}; + +gaf.DataReader.prototype.Ubyte = function() { + return this.buf.getUint8(this.newOffset(1)); +}; + +gaf.DataReader.prototype.Boolean = function() { + var result = this.buf.getUint8(this.newOffset(1)); + if(result > 1){ + throw new Error("GAF format error"); + } + return result; +}; + +gaf.DataReader.prototype.Uint = function() { + return this.buf.getUint32(this.newOffset(4), true); +}; + +gaf.DataReader.prototype.Int = function() { + return this.buf.getInt32(this.newOffset(4), true); +}; + +gaf.DataReader.prototype.color = function() { + return { + b: this.Ubyte(), + g: this.Ubyte(), + r: this.Ubyte(), + a: this.Ubyte() + }; +}; + +gaf.DataReader.prototype.Ushort = function() { + return this.buf.getUint16(this.newOffset(2), true); +}; + +gaf.DataReader.prototype.Float = function() { + return this.buf.getFloat32(this.newOffset(4), true); +}; + +gaf.DataReader.prototype.String = function() { + var strLen = this.Ushort(); + var from = this.newOffset(strLen); + var to = this.getOffset(); + + try + { + var str = this.dataRaw.slice(from, to); + } + catch(e) + { + // Internet Explorer 10 T.T + if(e.message == "Object doesn't support property or method 'slice'") + { + str = []; + for(var i = from; i < to; ++i) + str.push(this.buf.getUint8(i)); + } + else + { + throw(e); + } + } + return decodeURIComponent(escape(String.fromCharCode.apply(null, new Uint8Array(str)))); + +}; + +gaf.DataReader.prototype.startNestedBuffer = function(length) { + this.offset.push(this.offset[this.offset.length-1]); + this.offset[this.offset.length-2] += length; +}; + +gaf.DataReader.prototype.endNestedBuffer = function() { + if (this.offset.length == 1) throw new Error('No nested buffer available'); + this.offset.pop(); +}; + +gaf.DataReader.prototype.Point = function(){ + return { + x: this.Float(), + y: this.Float() + }; +}; + +gaf.DataReader.prototype.Rect = function(){ + return { + x: this.Float(), + y: this.Float(), + width: this.Float(), + height: this.Float() + }; +}; + +gaf.DataReader.prototype.Matrix = function(){ + return { + a: this.Float(), + b: this.Float(), + c: this.Float(), + d: this.Float(), + tx: this.Float(), + ty: this.Float() + }; +}; + +gaf.DataReader.prototype.seek = function(pos){ + this.offset[this.offset.length-1] = pos; +}; + +gaf.DataReader.prototype.tell = function(){ + return this.offset[this.offset.length-1]; +}; + +/* Creates a fields parsing function +* @ returns a function that will read from DataReader `field` of type `type` +* @`key` - key for read data to be stored +* @`data` - data to store. Can be DataReader function name or a function that will return a value +* Note. Parameters pair `key` and `data` can be repeated any number of times*/ + +gaf.DataReader.prototype.fields = function(){ + var self = this; + var arguments_ = arguments; + return function(){ + arguments.callee.result = {}; + var i = 0; + if(arguments_.length % 2){ + throw new Error('Number of arguments is not even'); + } + while(i < arguments_.length){ + var field = arguments_[i++]; + var func = arguments_[i++]; + if(typeof func === 'function'){ + arguments.callee.result[field] = func(); + } + else if (func in self && typeof self[func] === 'function'){ + arguments.callee.result[field] = self[func].call(self); + } + else{ + throw new Error('Object DataReader has no function `' + func + '`'); + } + } + return arguments.callee.result; + } +}; + +/* +* Creates a parsing function +* @ returns function that will execute expression if caller's `result` field has `key` equal to `value` parameter +* @ `key` - key in caller's `result` element +* @ `value` - expected value of the `key` or a comparator function +* @ `func` - function to execute if condition is true +* */ + +gaf.DataReader.prototype.condition = function(key, value, func){ + var arguments_ = arguments; + return function() { + if(arguments_.length != 3){ + throw new Error('Condition function'); + } + var parent = arguments.callee.caller; + if(!('result' in parent)){ + throw new Error('Condition function caller has no key `result`'); + } + var container = parent.result; + var field = arguments_[0]; + var value = arguments_[1]; + var exec = arguments_[2]; + + var evaluate = null; + if(typeof value === 'function'){ + evaluate = function(){return value(container[field]);}; + } + else{ + evaluate = function(){return value == container[field];}; + } + if(evaluate()){ + return exec(); + } + else{ + return null; + } + } +}; + +/* +* Creates an array parsing function +* @ returns function that will execute `func` number of times read from DataReader +* @ `type` - type of count number +* @ `func` - function to be executed +* */ + +gaf.DataReader.prototype.array = function(){ + var self = this; + var arguments_ = arguments; + return function() { + arguments.callee.result = []; + var length = self[arguments_[0]].call(self); + for (var i = 0; i < length; ++i) { + var r = arguments_[1].call(); + arguments.callee.result.push(r); + } + return arguments.callee.result; + } +}; diff --git a/frameworks/cocos2d-html5/external/gaf/Library/GAFLoader.js b/frameworks/cocos2d-html5/external/gaf/Library/GAFLoader.js new file mode 100644 index 0000000..3e40c33 --- /dev/null +++ b/frameworks/cocos2d-html5/external/gaf/Library/GAFLoader.js @@ -0,0 +1,75 @@ +var gaf = gaf || {}; + +//@Private class +gaf.Loader = function(){ + + var readHeaderBegin = function(stream, header){ + header.compression = stream.Uint(); + header.versionMajor = stream.Ubyte(); + header.versionMinor = stream.Ubyte(); + header.fileLength = stream.Uint(); + }; + + var readHeaderEndV3 = function(stream, header) { + header.framesCount = stream.Ushort(); + header.frameSize = stream.Rect(); + header.pivot = stream.Point(); + }; + + var readHeaderEndV4 = function(stream, header){ + var scaleCount = stream.Uint(); + header.scaleValues = []; + for(var i = 0; i < scaleCount; ++i){ + header.scaleValues.push(stream.Float()); + } + var csfCount = stream.Uint(); + header.csfValues = []; + for(var i = 0; i < csfCount; ++i){ + header.csfValues.push(stream.Float()); + } + }; + + this.LoadFile = function(filePath, onLoaded){ + var oReq = new XMLHttpRequest(); + oReq.open("GET", filePath, true); + var self = this; + oReq.responseType = "arraybuffer"; + oReq.onload = function(oEvent) { + var gaf_data = new gaf.DataReader(oReq.response); + var gafFile = self.LoadStream(gaf_data); + if(onLoaded) + onLoaded(gafFile); + }; + oReq.send(); + }; + + this.LoadStream = function(stream){ + var header = {}; + readHeaderBegin(stream, header); + if(header.compression == gaf.COMPRESSION_NONE) { // GAF + } + else if(header.compression == gaf.COMPRESSION_ZIP){ // GAC + var compressed = stream.dataRaw.slice(stream.tell()); + + var inflate = new window.Zlib.Inflate(new Uint8Array(compressed)); + var decompressed = inflate.decompress(); + stream = new gaf.DataReader(decompressed.buffer); + } + else{ + throw new Error("GAF syntax error."); + } + + if(header.versionMajor < 4){ + readHeaderEndV3(stream, header); + } + else{ + readHeaderEndV4(stream, header); + } + + var tags = gaf.ReadTags(stream); + return { + header: header, + tags: tags + }; + }; +}; diff --git a/frameworks/cocos2d-html5/external/gaf/Library/GAFMask.js b/frameworks/cocos2d-html5/external/gaf/Library/GAFMask.js new file mode 100644 index 0000000..34ca206 --- /dev/null +++ b/frameworks/cocos2d-html5/external/gaf/Library/GAFMask.js @@ -0,0 +1,36 @@ + +gaf.Mask = gaf.Object.extend +({ + _className: "GAFMask", + _clippingNode: null, + + ctor : function(gafSpriteProto) + { + this._super(); + cc.assert(gafSpriteProto, "Error! Missing mandatory parameter."); + this._gafproto = gafSpriteProto; + }, + + _init : function() + { + var maskNodeProto = this._gafproto.getMaskNodeProto(); + cc.assert(maskNodeProto, "Error. Mask node for id ref " + this._gafproto.getIdRef() + " not found."); + this._maskNode = maskNodeProto._gafConstruct(); + this._clippingNode = cc.ClippingNode.create(this._maskNode); + this._clippingNode.setAlphaThreshold(0.5); + this.addChild(this._clippingNode); + }, + + setExternalTransform : function(affineTransform) + { + if(!cc.affineTransformEqualToTransform(this._maskNode._additionalTransform, affineTransform)) + { + this._maskNode.setAdditionalTransform(affineTransform); + } + }, + + _getNode : function() + { + return this._clippingNode; + } +}); \ No newline at end of file diff --git a/frameworks/cocos2d-html5/external/gaf/Library/GAFMaskProto.js b/frameworks/cocos2d-html5/external/gaf/Library/GAFMaskProto.js new file mode 100644 index 0000000..6074fd1 --- /dev/null +++ b/frameworks/cocos2d-html5/external/gaf/Library/GAFMaskProto.js @@ -0,0 +1,16 @@ + +gaf._MaskProto = function(asset, mask, idRef) +{ + this.getIdRef = function(){return idRef}; + this.getMaskNodeProto = function() {return mask}; + + /* + * Will construct GAFMask + */ + this._gafConstruct = function() + { + var ret = new gaf.Mask(this); + ret._init(); + return ret; + }; +}; diff --git a/frameworks/cocos2d-html5/external/gaf/Library/GAFObject.js b/frameworks/cocos2d-html5/external/gaf/Library/GAFObject.js new file mode 100644 index 0000000..7d5375a --- /dev/null +++ b/frameworks/cocos2d-html5/external/gaf/Library/GAFObject.js @@ -0,0 +1,426 @@ +var gaf = gaf || {}; + +gaf._stateHasCtx = function(state) +{ + // Check for tint color offset + if( state.hasColorTransform && + (state.colorTransform.offset.r > 0 || + state.colorTransform.offset.g > 0 || + state.colorTransform.offset.b > 0 || + state.colorTransform.offset.a > 0) + ) + { + return true; + } + + // Check for color transform filter + if(state.hasEffect) + { + for(var i = 0, total = state.effect.length; i < total; ++i) + { + if(state.effect[i].type === gaf.EFFECT_COLOR_MATRIX) + return true; + } + } + return false; +}; + +gaf.Object = cc.Node.extend +({ + _asset : null, + _className : "GAFObject", + _id : gaf.IDNONE, + _gafproto : null, + _parentTimeLine : null, + _lastVisibleInFrame : 0, + _filterStack : null, + _cascadeColorMult : null, + _cascadeColorOffset : null, + _needsCtx : false, + _usedAtlasScale: 1, + + // Public methods + ctor: function(scale) + { + if(arguments.length == 1) + { + this._usedAtlasScale = scale; + } + this._super(); + this._cascadeColorMult = cc.color(255, 255, 255, 255); + this._cascadeColorOffset = cc.color(0, 0, 0, 0); + this._filterStack = []; + }, + + /** + * @method setAnimationStartedNextLoopDelegate + * @param {function(Object)} delegate + */ + setAnimationStartedNextLoopDelegate : function (delegate) {}, + + /** + * @method setAnimationFinishedPlayDelegate + * @param {function(Object)} delegate + */ + setAnimationFinishedPlayDelegate : function (delegate) {}, + + /** + * @method setLooped + * @param {bool} looped + */ + setLooped : function (looped) {}, + + /** + * @method getBoundingBoxForCurrentFrame + * @return {cc.Rect} + */ + getBoundingBoxForCurrentFrame : function () {return null;}, + + /** + * @method setFps + * @param {uint} fps + */ + setFps : function (fps) {}, + + /** + * @method getObjectByName + * @param {String} name - name of the object to find + * @return {gaf.Object} + */ + getObjectByName : function (name) {return null;}, + + /** + * @method clearSequence + */ + clearSequence : function () {}, + + /** + * @method getIsAnimationRunning + * @return {bool} + */ + getIsAnimationRunning : function () {return false;}, + + /** + * @method getSequences + * @return [string] - list of sequences if has any + */ + getSequences : function(){return [];}, + + + /** + * @method gotoAndStop + * @param {uint|String} value - label ot frame number + * @return {bool} + */ + gotoAndStop : function (value) {}, + + /** + * @method getStartFrame + * @param {String} frameLabel + * @return {uint} + */ + getStartFrame : function (frameLabel) {return gaf.IDNONE;}, + + /** + * @method setFramePlayedDelegate + * @param {function(Object, frame)} delegate + */ + setFramePlayedDelegate : function (delegate) {}, + + /** + * @method getCurrentFrameIndex + * @return {uint} + */ + getCurrentFrameIndex : function () { + return gaf.IDNONE; + }, + + /** + * @method getTotalFrameCount + * @return {uint} + */ + getTotalFrameCount : function () {return 0;}, + + /** + * @method start + */ + start : function () {}, + + /** + * @method stop + */ + stop : function () {}, + + /** + * @method isVisibleInCurrentFrame + * @return {bool} + */ + isVisibleInCurrentFrame : function () + { + /*if (this._parentTimeLine && + ((this._parentTimeLine.getCurrentFrameIndex() + 1) != this._lastVisibleInFrame)) + { + return false; + } + else + { + return true; + }*/ + return !(this._parentTimeLine && ((this._parentTimeLine.getCurrentFrameIndex() + 1) != this._lastVisibleInFrame)); + }, + + /** + * @method isDone + * @return {bool} + */ + isDone : function () {return true;}, + + /** + * @method playSequence + * @param {String} name - name of the sequence to play + * @param {bool} looped - play looped + * @param {bool} resume - whether to resume animation if stopped. True by default + * @return {bool} + */ + playSequence : function (name, looped, resume) {return false;}, + + /** + * @method isReversed + * @return {bool} + */ + isReversed : function () {return false;}, + + /** + * @method setSequenceDelegate + * @param {function(Object, sequenceName)} delegate + */ + setSequenceDelegate : function (delegate) {}, + + /** + * @method setFrame + * @param {uint} index + * @return {bool} + */ + setFrame : function (index) {return false;}, + + /** + * @method setControlDelegate + * @param {function} func + */ + setControlDelegate : function (func) {}, + + /** + * @method getEndFrame + * @param {String} frameLabel + * @return {uint} + */ + getEndFrame : function (frameLabel) {return gaf.IDNONE;}, + + /** + * @method pauseAnimation + */ + pauseAnimation : function () {}, + + /** + * @method gotoAndPlay + * @param {uint|String} value - label ot frame number + * @return {bool} + */ + gotoAndPlay : function (value) {}, + + /** + * @method isLooped + * @return {bool} + */ + isLooped : function () {return false;}, + + /** + * @method resumeAnimation + */ + resumeAnimation : function () {}, + + /** + * @method setReversed + * @param {bool} reversed + */ + setReversed : function (reversed) {}, + + /** + * @method hasSequences + * @return {bool} + */ + hasSequences : function () {return false;}, + + /** + * @method getFps + * @return {uint} + */ + getFps : function () {return 60;}, + + /** + * @method setLocator + * @param {bool} locator + * Locator object will not draw itself, but its children will be drawn + */ + setLocator : function (locator){}, + + setExternalTransform : function(affineTransform) + { + if(!cc.affineTransformEqualToTransform(this._additionalTransform, affineTransform)) + { + this.setAdditionalTransform(affineTransform); + } + }, + + getExternalTransform : function() + { + return this._additionalTransform; + }, + + setAnimationRunning: function () {}, + + //////////////// + // Private + //////////////// + _enableTick: function(val){}, + + _resetState : function() + {}, + + _updateVisibility : function(state, parent) + { + var alphaOffset = state.hasColorTransform ? state.colorTransform.offset.a : 0; + this.setOpacity(state.alpha + alphaOffset); + //return this.isVisible(); + }, + + // @Override + isVisible : function() + { + return this.getOpacity() > 0; + }, + + // @Override + visit: function(parentCmd) + { + if(this.isVisibleInCurrentFrame()) + { + this._super(parentCmd); + } + }, + + _getFilters : function(){return null}, + + _processAnimation : function(){}, + + + _applyState : function(state, parent) + { + this._applyStateSuper(state, parent); + }, + + _applyStateSuper : function(state, parent) + { + this._needsCtx = parent._needsCtx; + this._filterStack.length = 0; // clear + this._parentTimeLine = parent; // only gaf time line can call applyState. Assign it as parent + if(this._usedAtlasScale != 1) + { + var newMat = cc.clone(state.matrix); + newMat.tx *= this._usedAtlasScale; + newMat.ty *= this._usedAtlasScale; + this.setExternalTransform(newMat); // apply transformations of the state + } + else + { + this.setExternalTransform(state.matrix); // apply transformations of the state + } + // Cascade filters + // TODO: apply more than one filter + if (state.hasEffect) { + this._filterStack = this._filterStack.concat(state.effect); + this._needsCtx = true; + } + if (parent._filterStack && parent._filterStack.length > 0) { + this._filterStack = this._filterStack.concat(parent._filterStack); + } + + if(this._filterStack.length > 0 && this._filterStack[0].type === gaf.EFFECT_COLOR_MATRIX) + { + this._needsCtx = true; + } + + // Cascade color transformations + + // If state has a tint, then we should process it + if (state.hasColorTransform) + { + this._cascadeColorMult.r = state.colorTransform.mult.r * parent._cascadeColorMult.r / 255; + this._cascadeColorMult.g = state.colorTransform.mult.g * parent._cascadeColorMult.g / 255; + this._cascadeColorMult.b = state.colorTransform.mult.b * parent._cascadeColorMult.b / 255; + this._cascadeColorMult.a = state.colorTransform.mult.a * parent._cascadeColorMult.a / 255; + + this._cascadeColorOffset.r = state.colorTransform.offset.r + parent._cascadeColorOffset.r; + this._cascadeColorOffset.g = state.colorTransform.offset.g + parent._cascadeColorOffset.g; + this._cascadeColorOffset.b = state.colorTransform.offset.b + parent._cascadeColorOffset.b; + this._cascadeColorOffset.a = state.colorTransform.offset.a + parent._cascadeColorOffset.a; + } + else + { + this._cascadeColorMult.r = parent._cascadeColorMult.r; + this._cascadeColorMult.g = parent._cascadeColorMult.g; + this._cascadeColorMult.b = parent._cascadeColorMult.b; + this._cascadeColorMult.a = state.alpha * (parent._cascadeColorMult.a / 255); + + this._cascadeColorOffset.r = parent._cascadeColorOffset.r; + this._cascadeColorOffset.g = parent._cascadeColorOffset.g; + this._cascadeColorOffset.b = parent._cascadeColorOffset.b; + this._cascadeColorOffset.a = parent._cascadeColorOffset.a; + } + + if (this._cascadeColorOffset.r > 0 || + this._cascadeColorOffset.g > 0 || + this._cascadeColorOffset.b > 0 || + this._cascadeColorOffset.a > 0) + { + this._needsCtx = true; + } + }, + + _initRendererCmd: function() + { + this._renderCmd = cc.renderer.getRenderCmd(this); + this._renderCmd._visit = this._renderCmd.visit; + var self = this; + this._renderCmd.visit = function(parentCmd) { + if(self.isVisibleInCurrentFrame()){ + this._visit(parentCmd); + } + } + }, + + _getNode : function() + { + return this; + }, + + setAnchorPoint : function(point, y) + { + if (y === undefined) + { + this._super(point.x, point.y - 1); + } + else + { + this._super(point, y - 1); + } + } + +}); + +gaf.Object._createNullObject = function() +{ + var ret = new gaf.Object(); + ret.isVisible = function(){return true}; + return ret; +}; diff --git a/frameworks/cocos2d-html5/external/gaf/Library/GAFShaderManager.js b/frameworks/cocos2d-html5/external/gaf/Library/GAFShaderManager.js new file mode 100644 index 0000000..49b7ffb --- /dev/null +++ b/frameworks/cocos2d-html5/external/gaf/Library/GAFShaderManager.js @@ -0,0 +1,63 @@ + +gaf._glShaderInit = function() { + gaf._Uniforms = { + ColorTransformMult: -1, + ColorTransformOffset: -1, + ColorMatrixBody: -1, + ColorMatrixAppendix: -1, + BlurTexelOffset: -1, + GlowTexelOffset: -1, + GlowColor: -1 + }; + + gaf._shaderCreate = function (fs, vs) { + var program = new cc.GLProgram(); + var result = program.initWithVertexShaderByteArray(vs, fs); + cc.assert(result, "Shader init error"); + program.addAttribute(cc.ATTRIBUTE_NAME_POSITION, cc.VERTEX_ATTRIB_POSITION); + program.addAttribute(cc.ATTRIBUTE_NAME_COLOR, cc.VERTEX_ATTRIB_COLOR); + program.addAttribute(cc.ATTRIBUTE_NAME_TEX_COORD, cc.VERTEX_ATTRIB_TEX_COORDS); + result = program.link(); + cc.assert(result, "Shader linking error"); + program.updateUniforms(); + return program; + }; + + gaf._shaderCreateAlpha = function () { + var program = gaf._shaderCreate(gaf.SHADER_COLOR_MATRIX_FRAG, cc.SHADER_POSITION_TEXTURE_COLOR_VERT); + gaf._Uniforms.ColorTransformMult = program.getUniformLocationForName(gaf.UNIFORM_ALPHA_TINT_MULT); + gaf._Uniforms.ColorTransformOffset = program.getUniformLocationForName(gaf.UNIFORM_ALPHA_TINT_OFFSET); + gaf._Uniforms.ColorMatrixBody = program.getUniformLocationForName(gaf.UNIFORM_ALPHA_COLOR_MATRIX_BODY); + gaf._Uniforms.ColorMatrixAppendix = program.getUniformLocationForName(gaf.UNIFORM_ALPHA_COLOR_MATRIX_APPENDIX); + return program; + }; + + gaf._shaderCreateBlur = function () { + var program = gaf._shaderCreate(gaf.SHADER_GAUSSIAN_BLUR_FRAG, cc.SHADER_POSITION_TEXTURE_COLOR_VERT); + gaf._Uniforms.BlurTexelOffset = program._glContext.getUniformLocation(program._programObj, gaf.UNIFORM_BLUR_TEXEL_OFFSET); + + return program; + }; + + gaf._shaderCreateGlow = function () { + var program = gaf._shaderCreate(gaf.SHADER_GLOW_FRAG, cc.SHADER_POSITION_TEXTURE_COLOR_VERT); + gaf._Uniforms.GlowTexelOffset = program._glContext.getUniformLocation(program._programObj, gaf.UNIFORM_GLOW_TEXEL_OFFSET); + gaf._Uniforms.GlowColor = program._glContext.getUniformLocation(program._programObj, gaf.UNIFORM_GLOW_COLOR); + return program; + }; + + gaf._Shaders = { + Alpha: gaf._shaderCreateAlpha(), + Blur: gaf._shaderCreateBlur(), + Glow: gaf._shaderCreateGlow() + }; +}; + +gaf._setupShaders = function() { + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + gaf._glShaderInit(); + } + else { + delete gaf._glShaderInit; + } +}; diff --git a/frameworks/cocos2d-html5/external/gaf/Library/GAFShaders.js b/frameworks/cocos2d-html5/external/gaf/Library/GAFShaders.js new file mode 100644 index 0000000..7d91537 --- /dev/null +++ b/frameworks/cocos2d-html5/external/gaf/Library/GAFShaders.js @@ -0,0 +1,58 @@ +gaf.SHADER_GAUSSIAN_BLUR_FRAG = + "varying mediump vec2 v_texCoord;\n" + + "uniform mediump vec2 u_step;\n" + + "void main()\n" + + "{ \n" + + " mediump vec4 sum = vec4(0.0); \n" + + " sum += texture2D(CC_Texture0, v_texCoord - u_step * 4.0) * 0.05; \n" + + " sum += texture2D(CC_Texture0, v_texCoord - u_step * 3.0) * 0.09; \n" + + " sum += texture2D(CC_Texture0, v_texCoord - u_step * 2.0) * 0.12; \n" + + " sum += texture2D(CC_Texture0, v_texCoord - u_step * 1.0) * 0.15; \n" + + " sum += texture2D(CC_Texture0, v_texCoord + u_step * 0.0) * 0.18; \n" + + " sum += texture2D(CC_Texture0, v_texCoord + u_step * 1.0) * 0.15; \n" + + " sum += texture2D(CC_Texture0, v_texCoord + u_step * 2.0) * 0.12; \n" + + " sum += texture2D(CC_Texture0, v_texCoord + u_step * 3.0) * 0.09; \n" + + " sum += texture2D(CC_Texture0, v_texCoord + u_step * 4.0) * 0.05; \n" + + " gl_FragColor = sum; \n" + + "} \n"; + +gaf.SHADER_GLOW_FRAG = + "varying mediump vec2 v_texCoord;\n" + + "uniform mediump vec2 u_step;\n" + + "uniform mediump vec4 u_glowColor;\n" + + "void main()\n" + + "{ \n" + + " mediump vec4 sum = vec4(0.0); \n" + + " sum += texture2D(CC_Texture0, v_texCoord - u_step * 4.0) * 0.05; \n" + + " sum += texture2D(CC_Texture0, v_texCoord - u_step * 3.0) * 0.09; \n" + + " sum += texture2D(CC_Texture0, v_texCoord - u_step * 2.0) * 0.12; \n" + + " sum += texture2D(CC_Texture0, v_texCoord - u_step * 1.0) * 0.15; \n" + + " sum += texture2D(CC_Texture0, v_texCoord + u_step * 0.0) * 0.18; \n" + + " sum += texture2D(CC_Texture0, v_texCoord + u_step * 1.0) * 0.15; \n" + + " sum += texture2D(CC_Texture0, v_texCoord + u_step * 2.0) * 0.12; \n" + + " sum += texture2D(CC_Texture0, v_texCoord + u_step * 3.0) * 0.09; \n" + + " sum += texture2D(CC_Texture0, v_texCoord + u_step * 4.0) * 0.05; \n" + + " gl_FragColor = sum * u_glowColor; \n" + + "} \n"; + +gaf.SHADER_COLOR_MATRIX_FRAG = + "varying mediump vec2 v_texCoord;\n" + + "varying mediump vec4 v_fragmentColor;\n" + + "uniform mediump vec4 colorTransformMult;\n" + + "uniform mediump vec4 colorTransformOffsets;\n" + + "uniform mediump mat4 colorMatrix;\n" + + "uniform mediump vec4 colorMatrix2;\n" + + "void main()\n" + + "{ \n" + + " vec4 texColor = texture2D(CC_Texture0, v_texCoord); \n" + + " const float kMinimalAlphaAllowed = 1.0e-8; \n" + + " if (texColor.a > kMinimalAlphaAllowed) \n" + + " { \n" + + " texColor = vec4(texColor.rgb / texColor.a, texColor.a); \n" + + " vec4 ctxColor = texColor * colorTransformMult + colorTransformOffsets; \n" + + " vec4 adjustColor = colorMatrix * ctxColor + colorMatrix2; \n" + + " adjustColor *= v_fragmentColor; \n" + + " texColor = vec4(adjustColor.rgb * adjustColor.a, adjustColor.a); \n" + + " } \n" + + " gl_FragColor = texColor; \n" + + "}\n"; diff --git a/frameworks/cocos2d-html5/external/gaf/Library/GAFSprite.js b/frameworks/cocos2d-html5/external/gaf/Library/GAFSprite.js new file mode 100644 index 0000000..2387bf9 --- /dev/null +++ b/frameworks/cocos2d-html5/external/gaf/Library/GAFSprite.js @@ -0,0 +1,100 @@ + +gaf.Sprite = gaf.Object.extend +({ + _className: "GAFSprite", + + _hasCtx: false, + _hasFilter: false, + + ctor : function(gafSpriteProto, usedScale) + { + this._super(usedScale); + cc.assert(gafSpriteProto, "Error! Missing mandatory parameter."); + this._gafproto = gafSpriteProto; + }, + + // Private + + _init : function() + { + var frame = this._gafproto.getFrame(); + cc.assert(frame instanceof cc.SpriteFrame, "Error. Wrong object type."); + + // Create sprite with custom render command from frame + this._sprite = new cc.Sprite(); + this._sprite._renderCmd = this._gafCreateRenderCmd(this._sprite); + this._sprite.initWithSpriteFrame(frame); + + this._sprite.setAnchorPoint(this._gafproto.getAnchor()); + this.addChild(this._sprite); + //this._sprite.setCascadeColorEnabled(true); + //this._sprite.setCascadeOpacityEnabled(true); + this._sprite.setOpacityModifyRGB(true); + + if(cc._renderType === cc.game.RENDER_TYPE_WEBGL) + this._sprite.setBlendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); + }, + + _applyState : function(state, parent) + { + this._applyStateSuper(state, parent); + if(this._needsCtx) + { + // Enable ctx state if wasn't enabled + if(!this._hasCtx) + { + this._enableCtx(); + this._hasCtx = true; + } + // Set ctx shader + this._applyCtxState(state); + } + else + { + // Disable ctx state if was enabled + if(this._hasCtx) + { + this._disableCtx(); + this._hasCtx = false; + } + // Apply color + if(!cc.colorEqual(this._sprite._realColor, this._cascadeColorMult)) + { + this._sprite.setColor(this._cascadeColorMult); + } + // Apply opacity + if(this._sprite.getOpacity() != this._cascadeColorMult.a) + { + this._sprite.setOpacity(this._cascadeColorMult.a); + } + + } + }, + + _enableCtx: function() + { + this._sprite._renderCmd._enableCtx(); + }, + + _disableCtx: function() + { + this._sprite._renderCmd._disableCtx(); + }, + + _applyCtxState: function(state){ + this._sprite._renderCmd._applyCtxState(this); + }, + + getBoundingBoxForCurrentFrame: function () + { + var result = this._sprite.getBoundingBox(); + return cc._rectApplyAffineTransformIn(result, this.getNodeToParentTransform()); + }, + + _gafCreateRenderCmd: function(item){ + if(cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return new gaf.Sprite.CanvasRenderCmd(item); + else + return new gaf.Sprite.WebGLRenderCmd(item); + } +}); diff --git a/frameworks/cocos2d-html5/external/gaf/Library/GAFSpriteCanvasRenderCmd.js b/frameworks/cocos2d-html5/external/gaf/Library/GAFSpriteCanvasRenderCmd.js new file mode 100644 index 0000000..bb807cc --- /dev/null +++ b/frameworks/cocos2d-html5/external/gaf/Library/GAFSpriteCanvasRenderCmd.js @@ -0,0 +1,233 @@ + +(function() { + gaf.Sprite.CanvasRenderCmd = function (renderable) { + cc.Sprite.CanvasRenderCmd.call(this, renderable); + this._hasTintMult = false; + this._hasTintOffset = false; + this._hasCtx = false; + this._tintMult = cc.color(255,255,255,255); + this._tintOffset = cc.color(0,0,0,0); + this._textureDirty = false; + }; + var proto = gaf.Sprite.CanvasRenderCmd.prototype = Object.create(cc.Sprite.CanvasRenderCmd.prototype); + proto.constructor = gaf.Sprite.CanvasRenderCmd; + + proto._disableCtx = function(){ + this._hasTintOffset = false; + this._hasCtx = false; + this._textureDirty = true; + this.setDirtyFlag(cc.Node._dirtyFlags.colorDirty); + this._tintMult = cc.color(255,255,255,255); + this._tintOffset = cc.color(0,0,0,0); + }; + + proto._enableCtx = function(){ + + }; + + proto._applyCtxState = function(gafObject){ + + var tintMult = gafObject._cascadeColorMult; + var tintOffset = gafObject._cascadeColorOffset; + var opacity = tintMult.a; + + // Apply opacity + if(this._node.getOpacity() != opacity) + { + this._node.setOpacity(opacity); + } + + // Check Tint multiplicator + var multDirty = !cc.colorEqual(this._tintMult, tintMult); + if(multDirty) + { + this._node.setColor(tintMult); + this._tintMult = tintMult; + this._hasTintMult = + (tintMult.r !== 255 || + tintMult.g !== 255 || + tintMult.b !== 255 ); + } + + // Check Tint offset + var offfsetDirty = + (this._tintOffset.r != tintOffset.r) || + (this._tintOffset.g != tintOffset.g) || + (this._tintOffset.b != tintOffset.b) || + (this._tintOffset.a != tintOffset.a); + + if(offfsetDirty) + { + this._tintOffset = tintOffset; + this._hasTintOffset = + (tintOffset.r !== 0 || + tintOffset.g !== 0 || + tintOffset.b !== 0 || + tintOffset.a !== 0 ); + } + + // Update dirty flag + this._textureDirty = multDirty || offfsetDirty; + if(this._textureDirty) + { + this.setDirtyFlag(cc.Node._dirtyFlags.colorDirty); + } + + + this._hasCtx = gafObject._filterStack.length > 0 && gafObject._filterStack[0].type === gaf.EFFECT_COLOR_MATRIX; + + }; + + proto.rendering = function(ctx, scaleX, scaleY) + { + var node = this._node; + var locTextureCoord = this._textureCoord, + alpha = (this._displayedOpacity / 255); + + if ((node._texture && ((locTextureCoord.width === 0 || locTextureCoord.height === 0) //set texture but the texture isn't loaded. + || !node._texture._textureLoaded)) || alpha === 0) + return; + + var wrapper = ctx || cc._renderContext, + context = wrapper.getContext(); + var locX = node._offsetPosition.x, + locHeight = node._rect.height, + locWidth = node._rect.width, + locY = -node._offsetPosition.y - locHeight, + image; + + wrapper.setTransform(this._worldTransform, scaleX, scaleY); + wrapper.setCompositeOperation(this._blendFuncStr); + wrapper.setGlobalAlpha(alpha); + + if(node._flippedX || node._flippedY) + wrapper.save(); + if (node._flippedX) { + locX = -locX - locWidth; + context.scale(-1, 1); + } + if (node._flippedY) { + locY = node._offsetPosition.y; + context.scale(1, -1); + } + + image = node._texture._htmlElementObj; + + if (this._colorized) { + context.drawImage(image, + 0, 0, locTextureCoord.width,locTextureCoord.height, + locX * scaleX,locY * scaleY, locWidth * scaleX, locHeight * scaleY); + } else { + context.drawImage(image, + locTextureCoord.renderX, locTextureCoord.renderY, locTextureCoord.width, locTextureCoord.height, + locX * scaleX, locY * scaleY, locWidth * scaleX, locHeight * scaleY); + } + + if(node._flippedX || node._flippedY) + wrapper.restore(); + cc.g_NumberOfDraws++; + }; + + if(cc.sys._supportCanvasNewBlendModes){ + proto._updateColor = function () { + var displayedColor = this._displayedColor, node = this._node; + this._hasTintMult |= (displayedColor.r !== 255 || displayedColor.g !== 255 || displayedColor.b !== 255); + + // If no color changes + if(this._textureDirty) + { + this._textureDirty = false; + if (this._colorized) { + this._colorized = false; + node.texture = this._originalTexture; + } + } + else + { + return; + } + + var locElement, locTexture = node._texture, locRect = this._textureCoord; + if(this._hasTintMult) + { + if (locTexture && locRect.validRect && this._originalTexture) { + locElement = locTexture.getHtmlElementObj(); + if (!locElement) + return; + + this._colorized = true; + if (this._hasTintOffset || this._hasCtx) displayedColor = this._tintMult; + + locElement = cc.Sprite.CanvasRenderCmd._generateTintImageWithMultiply(this._originalTexture._htmlElementObj, displayedColor, locRect); + locTexture = new cc.Texture2D(); + locTexture.initWithElement(locElement); + locTexture.handleLoadedTexture(); + node.texture = locTexture; + } + } + + locTexture = node._texture; + if(this._hasTintOffset) + { + var cacheTextureForColor = cc.textureCache.getTextureColors(this._originalTexture.getHtmlElementObj()); + if (locTexture && locRect.validRect && this._originalTexture) { + locElement = locTexture.getHtmlElementObj(); + if (!locElement) + return; + if(this._colorized) + var texRect = cc.rect(0,0,locRect.width, locRect.height); + else + texRect = locRect; + locElement = this._gafGenerateTintImage(node.texture._htmlElementObj, texRect, cacheTextureForColor, this._tintOffset, locRect); + locTexture = new cc.Texture2D(); + locTexture.initWithElement(locElement); + locTexture.handleLoadedTexture(); + node.texture = locTexture; + this._colorized = true; + } + } + + + }; + + proto._gafGenerateTintImage = function(texture, texRect, tintedImgCache, color, rect, renderCanvas){ + if (!rect) + rect = cc.rect(0, 0, texture.width, texture.height); + + // Create a new buffer if required + var w = Math.min(rect.width, tintedImgCache[0].width); + var h = Math.min(rect.height, tintedImgCache[0].height); + var buff = renderCanvas, ctx; + if (!buff) { + buff = document.createElement("canvas"); + buff.width = w; + buff.height = h; + ctx = buff.getContext("2d"); + } else { + ctx = buff.getContext("2d"); + ctx.clearRect(0, 0, w, h); + } + ctx.save(); + + // draw a channel with alpha of the original image + ctx.globalCompositeOperation = 'source-over'; + //ctx.globalAlpha = 1; + ctx.drawImage(tintedImgCache[2], rect.x, rect.y, w, h, 0, 0, w, h); + + // draw a rect of specified color + ctx.globalCompositeOperation = 'source-in'; + ctx.fillStyle = 'rgba(' + Math.round(color.r) + ',' + Math.round(color.g) + ',' + Math.round(color.b) + ',1)'; + ctx.fillRect(0, 0, w, h); + + // add the desired image to the drawn + ctx.globalCompositeOperation = 'lighter'; + ctx.drawImage(texture, texRect.x, texRect.y, w, h, 0, 0, w, h); + + + ctx.restore(); + return buff; + + }; + } + +})(); diff --git a/frameworks/cocos2d-html5/external/gaf/Library/GAFSpriteProto.js b/frameworks/cocos2d-html5/external/gaf/Library/GAFSpriteProto.js new file mode 100644 index 0000000..6e33fa7 --- /dev/null +++ b/frameworks/cocos2d-html5/external/gaf/Library/GAFSpriteProto.js @@ -0,0 +1,36 @@ + +gaf._SpriteProto = function(asset, atlasFrames, elementAtlasIdRef) +{ + //this._anchor = atlasFrame._gafAnchor; + //delete atlasFrame._gafAnchor; + + this.getFrames = function(){return atlasFrames}; + this.getIdRef = function(){return elementAtlasIdRef}; + //this.getAnchor = function() {return this._anchor}; + this.getAsset = function() {return asset}; + + /* + * Will construct GAFSprite + */ + this._gafConstruct = function() + { + var usedScale = this.getAsset()._usedAtlasScale; + var ret = new gaf.Sprite(this, usedScale); + ret._init(); + return ret; + }; +}; + +gaf._SpriteProto.prototype.getFrame = function() +{ + var usedScale = this.getAsset()._usedAtlasScale; + cc.assert(usedScale, "Error. Atlas scale zero."); + var frames = this.getFrames()[usedScale]; + cc.assert(frames, "Error. No frames found for used scale `"+usedScale+"`"); + return frames[this.getIdRef()]; +}; + +gaf._SpriteProto.prototype.getAnchor = function() +{ + return this.getFrame()._gafAnchor; +}; diff --git a/frameworks/cocos2d-html5/external/gaf/Library/GAFSpriteWebGLRenderCmd.js b/frameworks/cocos2d-html5/external/gaf/Library/GAFSpriteWebGLRenderCmd.js new file mode 100644 index 0000000..eabe25b --- /dev/null +++ b/frameworks/cocos2d-html5/external/gaf/Library/GAFSpriteWebGLRenderCmd.js @@ -0,0 +1,132 @@ + +(function(){ + gaf.Sprite.WebGLRenderCmd = function (renderable) { + cc.Sprite.WebGLRenderCmd.call(this, renderable); + this._defualtShader = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR); + this._customShader = gaf._Shaders.Alpha; + + //this._shaderProgram = this._defualtShader; + + this._tintMult = null; + this._tintOffset = null; + this._ctxMatrixBody = null; + this._ctxMatrixAppendix = null; + }; + + var proto = gaf.Sprite.WebGLRenderCmd.prototype = Object.create(cc.Sprite.WebGLRenderCmd.prototype); + proto.constructor = gaf.Sprite.WebGLRenderCmd; + + proto._identityVec = [1.0, 1.0, 1.0, 1.0]; + proto._zeroVec = [0.0, 0.0, 0.0, 0.0]; + proto._identityMat = [ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ]; + + proto._disableCtx = function(){ + this.setShaderProgram(this._defualtShader); + }; + + proto._enableCtx = function(){ + this.setShaderProgram(this._customShader); + }; + + proto._applyCtxState = function(gafObject){ + var tintMult = gafObject._cascadeColorMult; + this._tintMult = [ + tintMult.r / 255, + tintMult.g / 255, + tintMult.b / 255, + tintMult.a / 255 + ]; + + var tintOffset = gafObject._cascadeColorOffset; + this._tintOffset = [ + tintOffset.r / 255, + tintOffset.g / 255, + tintOffset.b / 255, + tintOffset.a / 255 + ]; + + var filterStack = gafObject._filterStack; + if(filterStack && filterStack.length > 0 && filterStack[0].type === gaf.EFFECT_COLOR_MATRIX) + { + var m = filterStack[0].colorMatrix; + this._ctxMatrixBody = [ + m.rr, m.rg, m.rb, m.ra, + m.gr, m.gg, m.gb, m.ga, + m.br, m.bg, m.bb, m.ba, + m.ar, m.ag, m.ab, m.aa + ]; + this._ctxMatrixAppendix = [ + m.r / 255, + m.g / 255, + m.b / 255, + m.a / 255 + ]; + } + else + { + this._ctxMatrixBody = null; + this._ctxMatrixAppendix = null; + } + }; + + proto._setUniforms = function() + { + if(this._shaderProgram === this._customShader) + { + this._shaderProgram.use(); + { + this._shaderProgram.setUniformLocationWith4fv( + gaf._Uniforms.ColorTransformMult, + this._tintMult, + 1 + ); + this._shaderProgram.setUniformLocationWith4fv( + gaf._Uniforms.ColorTransformOffset, + this._tintOffset, + 1 + ); + } + + if(this._ctxMatrixBody && this._ctxMatrixAppendix) + { + this._shaderProgram.setUniformLocationWithMatrix4fv( + gaf._Uniforms.ColorMatrixBody, + this._ctxMatrixBody, + 1 + ); + this._shaderProgram.setUniformLocationWith4fv( + gaf._Uniforms.ColorMatrixAppendix, + this._ctxMatrixAppendix, + 1 + ); + } + else + { + this._shaderProgram.setUniformLocationWithMatrix4fv( + gaf._Uniforms.ColorMatrixBody, + this._identityMat, + 1 + ); + this._shaderProgram.setUniformLocationWith4fv( + gaf._Uniforms.ColorMatrixAppendix, + this._zeroVec, + 1 + ); + } + } + }; + + proto.rendering = function(ctx) + { + this._setUniforms(); + + // Super call + cc.Sprite.WebGLRenderCmd.prototype.rendering.call(this, ctx); + }; + +})(); diff --git a/frameworks/cocos2d-html5/external/gaf/Library/GAFTags.js b/frameworks/cocos2d-html5/external/gaf/Library/GAFTags.js new file mode 100644 index 0000000..09f8186 --- /dev/null +++ b/frameworks/cocos2d-html5/external/gaf/Library/GAFTags.js @@ -0,0 +1,378 @@ + +gaf.ReadSingleTag = function(stream){ + var tagId = stream.Ushort(); + var tag = gaf.Tags[tagId]; + var result = {}; + if(typeof tag === "undefined"){ + console.log("GAF. Non implemented tag detected."); + gaf.Tags.Default.parse(stream, tagId); + } + else{ + //console.log("tag " + tag.tagName); + result = tag.parse(stream, tagId); + } + return result; +}; + +gaf.ReadTags = function(stream){ + var tags = []; + try { + do { + var tag = gaf.ReadSingleTag(stream); + tags.push(tag); + } while (tag.tagId != 0); + } + catch (e){ + if (e instanceof Error && e.message == "GAF format error"){ + console.log("GAF format error:\n" + e.stack); + // Tag will be closed and parser will continue from where it should. + } + else{ + console.log(e.stack); + throw e; + } + } + return tags; +}; + + +gaf.Tag = function(){ + this.Default = Object.create(gaf.Tag.base); + this["0"] = Object.create(gaf.Tag.End); + this["1"] = Object.create(gaf.Tag.DefineAtlas); + this["2"] = Object.create(gaf.Tag.DefineAnimationMasks); + this["3"] = Object.create(gaf.Tag.DefineAnimationObjects); + this["4"] = Object.create(gaf.Tag.DefineAnimationFrames); + this["5"] = Object.create(gaf.Tag.DefineNamedParts); + this["6"] = Object.create(gaf.Tag.DefineSequences); + this["7"] = Object.create(gaf.Tag.DefineTextFields); + this["8"] = Object.create(gaf.Tag.DefineAtlas2); + this["9"] = Object.create(gaf.Tag.DefineStage); + this["10"] = Object.create(gaf.Tag.DefineAnimationObjects2); + this["11"] = Object.create(gaf.Tag.DefineAnimationMasks2); + this["12"] = Object.create(gaf.Tag.DefineAnimationFrames2); + this["13"] = Object.create(gaf.Tag.DefineTimeline); +}; + +gaf.Tag.base = function() {}; +gaf.Tag.base.parse = function(stream, tagId){ + var size = stream.Uint(); + + stream.startNestedBuffer(size); + var result = this.doParse(stream); + stream.endNestedBuffer(); + + result.tagName = this.tagName; + result.tagId = tagId; + return result; +}; +gaf.Tag.base.doParse = function(stream){ + return {}; + }; + +gaf.Tag.End = Object.create(gaf.Tag.base); +gaf.Tag.End.tagName = "TagEnd"; + +gaf.Tag.DefineAtlas = Object.create(gaf.Tag.base); +gaf.Tag.DefineAtlas.tagName = "TagDefineAtlas"; +gaf.Tag.DefineAtlas.doParse = function (s) { + var exec = s.fields( + 'scale', 'Float', + 'atlases', s.array('Ubyte', s.fields( + 'id', 'Uint', + 'sources', s.array('Ubyte', s.fields( + 'source', 'String', + 'csf', 'Float' + )) + )), + 'elements', s.array('Uint', s.fields( + 'pivot', 'Point', + 'origin', 'Point', + 'scale', 'Float', + 'size', 'Point', + 'atlasId', 'Uint', + 'elementAtlasId', 'Uint' + )) + ); + return {'content': exec()}; +}; + +gaf.Tag.DefineAnimationMasks = Object.create(gaf.Tag.base); +gaf.Tag.DefineAnimationMasks.tagName = "TagDefineAnimationMasks"; +gaf.Tag.DefineAnimationMasks.doParse = function (s) { + var exec = s.array('Uint', s.fields( + 'objectId', 'Uint', + 'elementAtlasIdRef', 'Uint' + )); + var result = {'content': exec()}; + debugger; + return result; +}; + +gaf.Tag.DefineAnimationObjects = Object.create(gaf.Tag.base); +gaf.Tag.DefineAnimationObjects.tagName = "TagDefineAnimationObjects"; +gaf.Tag.DefineAnimationObjects.doParse = function (s) { + var exec = s.array('Uint', s.fields( + 'objectId', 'Uint', + 'elementAtlasIdRef', 'Uint' + )); + return {'content': exec()}; +}; + +gaf.Tag.DefineAnimationFrames = Object.create(gaf.Tag.base); +gaf.Tag.DefineAnimationFrames.tagName = "TagDefineAnimationFrames"; +gaf.Tag.DefineAnimationFrames.doParse = function(s){ + var exec = s.array('Uint', s.fields( + 'frame', 'Uint', + 'state', s.array('Uint', s.fields( + 'hasColorTransform', 'Ubyte', + 'hasMask', 'Ubyte', + 'hasEffect', 'Ubyte', + 'objectIdRef', 'Uint', + 'depth', 'Int', + 'alpha', 'Float', + 'matrix', 'Matrix', + 'colorTransform', s.condition('hasColorTransform', 1, s.fields( + 'alphaOffset', 'Float', + 'redMultiplier', 'Float', + 'redOffset', 'Float', + 'greenMultiplier', 'Float', + 'greenOffset', 'Float', + 'blueMultiplier', 'Float', + 'blueOffset', 'Float' + )), + 'effect', s.condition('hasEffect', 1, s.array('Ubyte', gaf.Tag._readFilter(s))), + 'maskObjectIdRef', s.condition('hasMask', 1, s.fields( + 'maskObjectIdRef', 'Uint' + )) + )) + )); + return {'content': exec()}; +}; + +gaf.Tag.DefineNamedParts = Object.create(gaf.Tag.base); +gaf.Tag.DefineNamedParts.tagName = "TagDefineNamedParts"; +gaf.Tag.DefineNamedParts.doParse = function(s) { + var exec = s.array('Uint', s.fields( + 'objectId', 'Uint', + 'name', 'String' + )); + return {'content': exec()}; +}; + +gaf.Tag.DefineSequences = Object.create(gaf.Tag.base); +gaf.Tag.DefineSequences.tagName = "TagDefineSequences"; +gaf.Tag.DefineSequences.doParse = function(s) { + var exec = s.array('Uint', s.fields( + 'id', 'String', + 'start', 'Ushort', + 'end', 'Ushort' + )); + return {'content': exec()}; +}; + +gaf.Tag.DefineTextFields = Object.create(gaf.Tag.base); +gaf.Tag.DefineTextFields.tagName = "TagDefineTextFields"; +gaf.Tag.DefineTextFields.doParse = function(s) { + var exec = s.array('Uint', s.fields( + 'id', 'Uint', + 'pivot', 'Point', + 'end', 'Ushort', + 'width', 'Float', + 'height', 'Float', + 'text', 'String', + 'embedFonts', 'Boolean', + 'multiline', 'Boolean', + 'wordWrap', 'Boolean', + 'hasRestrict', 'Boolean', + 'restrict', s.condition('hasRestrict', 1, function (){return s['String'];}), + 'editable', 'Boolean', + 'selectable', 'Boolean', + 'displayAsPassword', 'Boolean', + 'maxChars', 'Uint', + 'align', 'Uint', + 'blockIndent', 'Uint', + 'bold', 'Boolean', + 'bullet', 'Boolean', + 'color', 'color', + 'font', 'String', + 'indent', 'Uint', + 'italic', 'Boolean', + 'kerning', 'Boolean', + 'leading', 'Uint', + 'leftMargin', 'Uint', + 'letterSpacing', 'Float', + 'rightMargin', 'Uint', + 'size', 'Uint', + 'tabStops', s.array('Uint', s.fields( + 'value', 'Uint' + )), + 'target', 'string', + 'underline', 'Boolean', + 'url', 'String' + )); + return {'content': exec()}; +}; + +gaf.Tag.DefineAtlas2 = Object.create(gaf.Tag.base); +gaf.Tag.DefineAtlas2.tagName = "TagDefineAtlas2"; +gaf.Tag.DefineAtlas2.doParse = function(s) { + var exec = s.fields( + 'scale', 'Float', + 'atlases', s.array('Ubyte', s.fields( + 'id', 'Uint', + 'sources', s.array('Ubyte', s.fields( + 'source', 'String', + 'csf', 'Float' + )) + )), + 'elements', s.array('Uint', s.fields( + 'pivot', 'Point', + 'origin', 'Point', + 'scale', 'Float', + 'size', 'Point', + 'atlasId', 'Uint', + 'elementAtlasId', 'Uint', + 'hasScale9Grid', 'Boolean', + 'scale9GridRect', s.condition('hasScale9Grid', 1, function(){return s.Rect();}) + )) + ); + return {'content': exec()}; +}; + +gaf.Tag.DefineStage = Object.create(gaf.Tag.base); +gaf.Tag.DefineStage.tagName = "TagDefineStage"; +gaf.Tag.DefineStage.doParse = function(s) { + var exec = s.fields( + 'fps', 'Ubyte', + 'color', 'color', + 'width', 'Ushort', + 'height', 'Ushort' + ); + return {'content': exec()}; +}; + +gaf.Tag.DefineAnimationObjects2 = Object.create(gaf.Tag.base); +gaf.Tag.DefineAnimationObjects2.tagName = "TagDefineAnimationObjects2"; +gaf.Tag.DefineAnimationObjects2.doParse = function(s) { + var exec = s.array('Uint', s.fields( + 'objectId', 'Uint', + 'elementAtlasIdRef', 'Uint', + 'type', 'Ushort' + )); + return {'content': exec()}; +}; + +gaf.Tag.DefineAnimationMasks2 = Object.create(gaf.Tag.base); +gaf.Tag.DefineAnimationMasks2.tagName = "TagDefineAnimationMasks2"; +gaf.Tag.DefineAnimationMasks2.doParse = function(s) { + var exec = s.array('Uint', s.fields( + 'objectId', 'Uint', + 'elementAtlasIdRef', 'Uint', + 'type', 'Ushort' + )); + return {'content': exec()}; +}; + +gaf.Tag.DefineAnimationFrames2 = Object.create(gaf.Tag.base); +gaf.Tag.DefineAnimationFrames2.tagName = "TagDefineAnimationFrames2"; +gaf.Tag.DefineAnimationFrames2.doParse = function(s) { + var exec = s.array('Uint', s.fields( + 'frame', 'Uint', + 'hasChangesInDisplayList', 'Boolean', + 'hasActions', 'Boolean', + 'state', s.condition('hasChangesInDisplayList', 1, s.array('Uint', s.fields( + 'hasColorTransform', 'Boolean', + 'hasMask', 'Boolean', + 'hasEffect', 'Boolean', + 'objectIdRef', 'Uint', + 'depth', 'Int', + 'alpha', 'Float', + 'matrix', 'Matrix', + 'colorTransform', s.condition('hasColorTransform', 1, s.fields( + 'alphaOffset', 'Float', + 'redMultiplier', 'Float', + 'redOffset', 'Float', + 'greenMultiplier', 'Float', + 'greenOffset', 'Float', + 'blueMultiplier', 'Float', + 'blueOffset', 'Float' + )), + 'effect', s.condition('hasEffect', 1, s.array('Ubyte', gaf.Tag._readFilter(s))), + 'maskObjectIdRef', s.condition('hasMask', 1, function(){return s.Uint()}) + ))), + 'actions', s.condition('hasActions', 1, s.array('Uint', s.fields( + 'type', 'Uint', + 'scope', 'String', + 'params', gaf.Tag._readActionArguments(s) + ))) + )); + return {'content': exec()}; +}; + +gaf.Tag.DefineTimeline = Object.create(gaf.Tag.base); +gaf.Tag.DefineTimeline.tagName = "TagDefineTimeline"; +gaf.Tag.DefineTimeline.doParse = function(s) { + var exec = s.fields( + 'id', 'Uint', + 'animationFrameCount', 'Uint', + 'boundingBox', 'Rect', + 'pivotPoint', 'Point', + 'hasLinkage', 'Boolean', + 'linkageName', s.condition('hasLinkage', 1, function () { + return s.String(); + }) + ); + var result = {'content': exec()}; + result.content.tags = gaf.ReadTags(s); + return result; +}; + +gaf.Tag._readActionArguments = function(s){ + return function(){ + var size = s.Uint(); + var ret = []; + s.startNestedBuffer(size); + while(s.maxOffset() < s.tell()){ + ret.push(s.String()); + } + s.endNestedBuffer(); + return ret; + }; +}; + +gaf.Tag._readFilter = function(s){ + return s.fields( + 'type', 'Uint', + 'dropShadow', s.condition('type', gaf.EFFECT_DROP_SHADOW, s.fields( // DropShadow + 'color', 'color', + 'blurX', 'Float', + 'blurY', 'Float', + 'angle', 'Float', + 'distance', 'Float', + 'strength', 'Float', + 'inner', 'Boolean', + 'knockout', 'Boolean' + )), + 'blur', s.condition('type', gaf.EFFECT_BLUR, s.fields( // Blur + 'blurX', 'Float', + 'blurY', 'Float' + )), + 'glow', s.condition('type', gaf.EFFECT_GLOW, s.fields( // Glow + 'color', 'color', + 'blurX', 'Float', + 'blurY', 'Float', + 'strength', 'Float', + 'inner', 'Boolean', + 'knockout', 'Boolean' + )), + 'colorMatrix', s.condition('type', gaf.EFFECT_COLOR_MATRIX, s.fields( // ColorMatrix + 'rr', 'Float', 'gr', 'Float', 'br', 'Float', 'ar', 'Float', 'r', 'Float', + 'rg', 'Float', 'gg', 'Float', 'bg', 'Float', 'ag', 'Float', 'g', 'Float', + 'rb', 'Float', 'gb', 'Float', 'bb', 'Float', 'ab', 'Float', 'b', 'Float', + 'ra', 'Float', 'ga', 'Float', 'ba', 'Float', 'aa', 'Float', 'a', 'Float' + )) + ) +}; + +gaf.Tags = new gaf.Tag(); diff --git a/frameworks/cocos2d-html5/external/gaf/Library/GAFTextField.js b/frameworks/cocos2d-html5/external/gaf/Library/GAFTextField.js new file mode 100644 index 0000000..04f9744 --- /dev/null +++ b/frameworks/cocos2d-html5/external/gaf/Library/GAFTextField.js @@ -0,0 +1,6 @@ + +gaf.TextField = gaf.Object.extend +({ + _className: "GAFTextField" + +}); \ No newline at end of file diff --git a/frameworks/cocos2d-html5/external/gaf/Library/GAFTimeLine.js b/frameworks/cocos2d-html5/external/gaf/Library/GAFTimeLine.js new file mode 100644 index 0000000..ef9387a --- /dev/null +++ b/frameworks/cocos2d-html5/external/gaf/Library/GAFTimeLine.js @@ -0,0 +1,547 @@ + +gaf.TimeLine = gaf.Object.extend +({ + _className: "GAFTimeLine", + _objects: null, + _container: null, + _animationStartedNextLoopDelegate: null, + _animationFinishedPlayDelegate: null, + _framePlayedDelegate: null, + _sequenceDelegate: null, + _fps: 60, + _frameTime: 1/60, + _currentSequenceStart: gaf.FIRST_FRAME_INDEX, + _currentSequenceEnd: gaf.FIRST_FRAME_INDEX, + _totalFrameCount: 0, + _isRunning: false, + _isLooped: false, + _isReversed: false, + _timeDelta: 0, + _animationsSelectorScheduled: false, + _currentFrame: gaf.FIRST_FRAME_INDEX, + + + setAnimationStartedNextLoopDelegate: function (delegate) + { + this._animationStartedNextLoopDelegate = delegate; + }, + setAnimationFinishedPlayDelegate: function (delegate) + { + this._animationFinishedPlayDelegate = delegate; + }, + setLooped: function (looped, recursively) + { + this._isLooped = looped; + if (recursively) + { + this._objects.forEach(function (item) + { + item.setLooped(looped, recursively); + }); + } + }, + getBoundingBoxForCurrentFrame: function () + { + var result = null;//cc.rect(); + var isFirstObj = true; + this._objects.forEach(function (item) { + if(item.isVisibleInCurrentFrame() && item.isVisible()) + { + var bb = item.getBoundingBoxForCurrentFrame(); + if(!bb) + { + bb = item.getBoundingBox(); + } + if (isFirstObj) + { + isFirstObj = false; + result = bb; + } + else + { + result = cc.rectUnion(result, bb); + } + } + }); + return cc._rectApplyAffineTransformIn(result, this._container.getNodeToParentTransform()); + }, + setFps: function (fps) + { + cc.assert(fps !== 0, 'Error! Fps is set to zero.'); + this._fps = fps; + this._frameTime = 1/fps; + }, + getObjectByName: function (name) + { + var elements = name.split('.'); + var result = null; + var retId = -1; + var timeLine = this; + var BreakException = {}; + try + { + elements.forEach(function(element) + { + var parts = timeLine._gafproto.getNamedParts(); + if(parts.hasOwnProperty(element)) + { + retId = parts[element]; + } + else + { + // Sequence is incorrect + BreakException.lastElement = element; + throw BreakException; + } + result = timeLine._objects[retId]; + timeLine = result; + }); + } + catch (e) + { + if (e!==BreakException) + { + throw e; + } + cc.log("Sequence incorrect: `" + name + "` At: `" + BreakException.lastElement + "`"); + return null; + } + return result; + }, + clearSequence: function () + { + this._currentSequenceStart = gaf.FIRST_FRAME_INDEX; + this._currentSequenceEnd = this._gafproto.getTotalFrames(); + }, + getIsAnimationRunning: function () + { + return this._isRunning; + }, + gotoAndStop: function (value) + { + var frame = 0; + if (typeof value === 'string') + { + frame = this.getStartFrame(value); + } + else + { + frame = value; + } + if (this.setFrame(frame)) + { + this.setAnimationRunning(false, false); + return true; + } + return false; + }, + gotoAndPlay: function (value) + { + var frame = 0; + if (typeof value === 'string') + { + frame = this.getStartFrame(value); + } + else + { + frame = value; + } + if (this.setFrame(frame)) + { + this.setAnimationRunning(true, false); + return true; + } + return false; + }, + getStartFrame: function (frameLabel) + { + var seq = this._gafproto.getSequences()[frameLabel]; + if (seq) + { + return seq.start; + } + return gaf.IDNONE; + }, + getEndFrame: function (frameLabel) + { + var seq = this._gafproto.getSequences()[frameLabel]; + if (seq) + { + return seq.end; + } + return gaf.IDNONE; + }, + setFramePlayedDelegate: function (delegate) + { + this._framePlayedDelegate = delegate; + }, + getCurrentFrameIndex: function () + { + return this._showingFrame; + }, + getTotalFrameCount: function () + { + return this._gafproto.getTotalFrames(); + }, + start: function () + { + this._enableTick(true); + if (!this._isRunning) + { + this._currentFrame = gaf.FIRST_FRAME_INDEX; + this.setAnimationRunning(true, true); + } + }, + stop: function () + { + this._enableTick(false); + if (this._isRunning) + { + this._currentFrame = gaf.FIRST_FRAME_INDEX; + this.setAnimationRunning(false, true); + } + }, + isDone: function () + { + if (this._isLooped) + { + return false; + } + else + { + if (!this._isReversed) + { + return this._currentFrame > this._totalFrameCount; + } + else + { + return this._currentFrame < gaf.FIRST_FRAME_INDEX - 1; + } + } + }, + getSequences: function() + { + return this._gafproto.getSequences(); + }, + playSequence: function (name, looped) + { + var s = this.getStartFrame(name); + var e = this.getEndFrame(name); + if (gaf.IDNONE === s || gaf.IDNONE === e) + { + return false; + } + this._currentSequenceStart = s; + this._currentSequenceEnd = e; + if (this._currentFrame < this._currentSequenceStart || this._currentFrame > this._currentSequenceEnd) + { + this._currentFrame = this._currentSequenceStart; + } + else + { + this._currentFrame = this._currentSequenceStart; + } + this.setLooped(looped, false); + this.resumeAnimation(); + return true; + }, + isReversed: function () + { + return this._isReversed; + }, + setSequenceDelegate: function (delegate) + { + this._sequenceDelegate = delegate; + }, + setFrame: function (index) + { + if (index >= gaf.FIRST_FRAME_INDEX && index < this._totalFrameCount) + { + this._showingFrame = index; + this._currentFrame = index; + this._processAnimation(); + return true; + } + return false; + }, + + pauseAnimation: function () + { + if (this._isRunning) + { + this.setAnimationRunning(false, false); + } + }, + isLooped: function () + { + return this._isLooped; + }, + resumeAnimation: function () + { + if (!this._isRunning) + { + this.setAnimationRunning(true, false); + } + }, + setReversed: function (reversed) + { + this._isReversed = reversed; + }, + hasSequences: function () + { + return this._gafproto.getSequences().length > 0; + }, + getFps: function () + { + return this._fps; + }, + + + // Private + + ctor: function(gafTimeLineProto, scale) + { + this._super(scale); + this._objects = []; + cc.assert(gafTimeLineProto, "Error! Missing mandatory parameter."); + this._gafproto = gafTimeLineProto; + }, + + setExternalTransform: function(affineTransform) + { + if(!cc.affineTransformEqualToTransform(this._container._additionalTransform, affineTransform)) + { + this._container.setAdditionalTransform(affineTransform); + } + }, + + _init: function() + { + this.setContentSize(this._gafproto.getBoundingBox()); + this._currentSequenceEnd = this._gafproto.getTotalFrames(); + this._totalFrameCount = this._currentSequenceEnd; + this.setFps(this._gafproto.getFps()); + this._container = new cc.Node(); + this.addChild(this._container); + + var self = this; + var asset = this._gafproto.getAsset(); + + // Construct objects for current time line + this._gafproto.getObjects().forEach(function(object) + { + var objectProto = asset._getProtos()[object]; + cc.assert(objectProto, "Error. GAF proto for type: " + object.type + " and reference id: " + object + " not found."); + self._objects[object] = objectProto._gafConstruct(); + }); + }, + + _enableTick: function(val) + { + if (!this._animationsSelectorScheduled && val) + { + this.schedule(this._processAnimations); + this._animationsSelectorScheduled = true; + } + else if (this._animationsSelectorScheduled && !val) + { + this.unschedule(this._processAnimations); + this._animationsSelectorScheduled = false; + } + }, + + _processAnimations: function (dt) + { + this._timeDelta += dt; + while (this._timeDelta >= this._frameTime) + { + this._timeDelta -= this._frameTime; + this._step(); + } + }, + + _step: function () + { + this._showingFrame = this._currentFrame; + + if(!this.getIsAnimationRunning()) + { + this._processAnimation(); + return; + } + + if(this._sequenceDelegate) + { + var seq; + if(!this._isReversed) + { + seq = this._getSequenceByLastFrame(this._currentFrame); + } + else + { + seq = this._getSequenceByFirstFrame(this._currentFrame + 1); + } + + if (seq) + { + this._sequenceDelegate(this, seq); + } + } + if (this._isCurrentFrameLastInSequence()) + { + if(this._isLooped) + { + if(this._animationStartedNextLoopDelegate) + this._animationStartedNextLoopDelegate(this); + } + else + { + this.setAnimationRunning(false, false); + if(this._animationFinishedPlayDelegate) + this._animationFinishedPlayDelegate(this); + } + } + this._processAnimation(); + this._currentFrame = this._nextFrame(); + }, + + _isCurrentFrameLastInSequence: function() + { + if (this._isReversed) + return this._currentFrame == this._currentSequenceStart; + return this._currentFrame == this._currentSequenceEnd - 1; + }, + + _nextFrame: function() + { + if (this._isCurrentFrameLastInSequence()) + { + if (!this._isLooped) + return this._currentFrame; + + if (this._isReversed) + return this._currentSequenceEnd - 1; + else + return this._currentSequenceStart; + } + + return this._currentFrame + (this._isReversed ? -1 : 1); + }, + + _processAnimation: function () + { + //var id = this._gafproto.getId(); + this._realizeFrame(this._container, this._currentFrame); + if (this._framePlayedDelegate) + { + this._framePlayedDelegate(this, this._currentFrame); + } + }, + _realizeFrame: function(out, frameIndex) + { + var self = this; + var objects = self._objects; + var frames = self._gafproto.getFrames(); + if(frameIndex > frames.length) + { + return; + } + var currentFrame = frames[frameIndex]; + if(!currentFrame) + { + return; + } + var states = currentFrame.states; + for(var stateIdx = 0, total = states.length; stateIdx < total; ++stateIdx) + { + var state = states[stateIdx]; + var object = objects[state.objectIdRef]; + if(!object) + { + return; + } + if(state.alpha < 0) + { + object._resetState(); + } + object._updateVisibility(state, self); + if(!object.isVisible()) + { + continue; + } + object._applyState(state, self); + var parent = out; + if(state.hasMask) + { + parent = objects[state.maskObjectIdRef]._getNode(); + cc.assert(parent, "Error! Mask not found."); + } + object._lastVisibleInFrame = 1 + frameIndex; + gaf.TimeLine.rearrangeSubobject(parent, object, state.depth); + if(object._step) + { + object._step(); + } + } + }, + setAnimationRunning: function (value, recursively) + { + this._isRunning = value; + if(recursively) + { + this._objects.forEach(function (obj) + { + if (obj && obj.setAnimationRunning) + { + obj.setAnimationRunning(value, recursively); + } + }); + } + }, + + _getSequenceByLastFrame: function(){ + var sequences = this._gafproto.getSequences(); + for(var item in sequences){ + if(sequences.hasOwnProperty(item)){ + if(sequences[item].end === frame + 1) + { + return item; + } + } + } + return ""; + }, + + _resetState : function() + { + this._super(); + this._currentFrame = this._currentSequenceStart; + }, + + _getSequenceByFirstFrame: function(){ + var sequences = this._gafproto.getSequences(); + for(var item in sequences){ + if(sequences.hasOwnProperty(item)){ + if(sequences[item].start === frame) + { + return item; + } + } + } + return ""; + } +}); + +gaf.TimeLine.rearrangeSubobject = function(out, object, depth) +{ + var parent = object.getParent(); + if (parent !== out) + { + object.removeFromParent(false); + out.addChild(object, depth); + } + else + { + object.setLocalZOrder(depth); + } +}; diff --git a/frameworks/cocos2d-html5/external/gaf/Library/GAFTimeLineProto.js b/frameworks/cocos2d-html5/external/gaf/Library/GAFTimeLineProto.js new file mode 100644 index 0000000..9d78b21 --- /dev/null +++ b/frameworks/cocos2d-html5/external/gaf/Library/GAFTimeLineProto.js @@ -0,0 +1,32 @@ + +gaf._TimeLineProto = function(asset, animationFrameCount, boundingBox, pivotPoint, id, linkageName) +{ + id = typeof id != 'undefined' ? id : 0; + linkageName = linkageName || ""; + + this._objects = []; + + this.getTotalFrames = function(){return animationFrameCount}; + this.getBoundingBox = function() {return boundingBox}; + this.getId = function() {return id}; + this.getLinkageName = function() {return linkageName}; + this.getPivot = function(){return pivotPoint}; + this.getRect = function(){return boundingBox}; + this.getNamedParts = function() {return {}}; // Map name -> id + this.getSequences = function() {return {}}; // Map name -> {start, end} + this.getFrames = function(){return []}; // Array {states, actions} + this.getFps = function(){return 60}; + this.getObjects = function(){return this._objects}; + this.getAsset = function(){return asset}; + + /* + * Will construct GAFTimeLine + */ + this._gafConstruct = function() + { + var usedScale = this.getAsset()._usedAtlasScale; + var ret = new gaf.TimeLine(this, usedScale); + ret._init(); + return ret; + }; +}; diff --git a/frameworks/cocos2d-html5/external/gaf/gaf_viewer.css b/frameworks/cocos2d-html5/external/gaf/gaf_viewer.css new file mode 100644 index 0000000..6093316 --- /dev/null +++ b/frameworks/cocos2d-html5/external/gaf/gaf_viewer.css @@ -0,0 +1,42 @@ +#drop_zone { + border: 2px dashed #bbb; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; + padding: 25px; + text-align: center; + font: 20pt bold 'Vollkorn'; + color: #bbb; + +} +.renderjson a { + text-decoration: none; +} +.renderjson .disclosure { + color: crimson; + font-size: 150%; +} +.renderjson .syntax { + color: grey; +} +.renderjson .string { + color: darkred; +} +.renderjson .number { + color: darkcyan; +} +.renderjson .boolean { + color: blueviolet; +} +.renderjson .key { + color: darkblue; +} +.renderjson .keyword { + color: blue; +} +.renderjson .object.syntax { + color: lightseagreen; +} +.renderjson .array.syntax { + color: orange; +} diff --git a/frameworks/cocos2d-html5/external/gaf/gaf_viewer.html b/frameworks/cocos2d-html5/external/gaf/gaf_viewer.html new file mode 100644 index 0000000..2c66aeb --- /dev/null +++ b/frameworks/cocos2d-html5/external/gaf/gaf_viewer.html @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + +
Drop GAF here
+ + + + + + \ No newline at end of file diff --git a/frameworks/cocos2d-html5/external/gaf/gaf_viewer.js b/frameworks/cocos2d-html5/external/gaf/gaf_viewer.js new file mode 100644 index 0000000..98d247c --- /dev/null +++ b/frameworks/cocos2d-html5/external/gaf/gaf_viewer.js @@ -0,0 +1,219 @@ + /* + * Created by Teivaz on 29.11.2014. + * Thanks to David Caldwell for `renderjson` + */ +function handleFileSelect(evt) { + evt.stopPropagation(); + evt.preventDefault(); + + var files = evt.dataTransfer.files; + + var output = []; + for (var i = 0, f; f = files[i]; i++) { + var name = escape(f.name); + var ext = name.split('.').pop(); + if (ext == 'gaf') { + var reader = new FileReader(); + reader.onload = (function(theFile) { + return function(req) { + var arrayBuffer = new gaf.DataReader(req.target.result); + var loader = new gaf.Loader(); + var data = loader.LoadStream(arrayBuffer); + document.getElementById('list').appendChild(renderjson(data)); + }; + })(f); + reader.readAsArrayBuffer(f); + } + } +} + +function handleDragOver(evt) { + evt.stopPropagation(); + evt.preventDefault(); + evt.dataTransfer.dropEffect = 'copy'; +} + +var dropZone = document.getElementById('drop_zone'); +dropZone.addEventListener('dragover', handleDragOver, false); +dropZone.addEventListener('drop', handleFileSelect, false); + +var module; +(module || {}).exports = renderjson = (function() { + var themetext = function( /* [class, text]+ */ ) { + var spans = []; + while (arguments.length) + spans.push(append(span(Array.prototype.shift.call(arguments)), + text(Array.prototype.shift.call(arguments)))); + return spans; + }; + var append = function( /* el, ... */ ) { + var el = Array.prototype.shift.call(arguments); + for (var a = 0; a < arguments.length; a++) + if (arguments[a].constructor == Array) + append.apply(this, [el].concat(arguments[a])); + else + el.appendChild(arguments[a]); + return el; + }; + var prepend = function(el, child) { + el.insertBefore(child, el.firstChild); + return el; + }; + var isempty = function(obj) { + for (var k in obj) + if (obj.hasOwnProperty(k)) return false; + return true; + }; + var text = function(txt) { + return document.createTextNode(txt) + }; + var div = function() { + return document.createElement("div") + }; + var span = function(classname) { + var s = document.createElement("span"); + if (classname) s.className = classname; + return s; + }; + var A = function A(txt, classname, callback) { + var a = document.createElement("a"); + if (classname) a.className = classname; + a.appendChild(text(txt)); + a.href = '#'; + a.onclick = function() { + callback(); + return false; + }; + return a; + }; + + function _renderjson(json, indent, dont_indent, show_level, sort_objects) { + var my_indent = dont_indent ? "" : indent; + + if (json === null) return themetext(null, my_indent, "keyword", "null"); + if (json === void 0) return themetext(null, my_indent, "keyword", "undefined"); + if (typeof(json) != "object") // Strings, numbers and bools + return themetext(null, my_indent, typeof(json), JSON.stringify(json)); + + var disclosure = function(open, close, type, builder) { + var content; + var empty = span(type); + var show = function() { + if (!content) append(empty.parentNode, + content = prepend(builder(), + A(renderjson.hide, "disclosure", + function() { + content.style.display = "none"; + empty.style.display = "inline"; + }))); + content.style.display = "inline"; + empty.style.display = "none"; + }; + + function isColor(a){ + return a.hasOwnProperty('a') && a.hasOwnProperty('r') && a.hasOwnProperty('g') && a.hasOwnProperty('b'); + } + + var color_rect = span(); + if (json.hasOwnProperty("tagName")) + var placeholder = json.tagName; + else if (json.hasOwnProperty("header")) + placeholder = " GAF v" + json.header.versionMajor + "." + json.header.versionMinor + " "; + else if (json.constructor == Array) + placeholder = " " + json.length + " "; + else if (json.hasOwnProperty("id")) + placeholder = " id:" + json.id + " ... "; + else if (json.hasOwnProperty("objectId")) + placeholder = " id:" + json.objectId + " ... "; + else if (json.hasOwnProperty("frame")) + placeholder = " frame:" + json.frame + " ... "; + else if(isColor(json)){ + color_rect.style.backgroundColor = "rgba("+json.r+","+json.g+","+json.b+","+json.a / 255.0+")";// parseInt(json.r).toString(16) + parseInt(json.g).toString(16) + parseInt(json.b).toString(16); + color_rect.style.height = '10px'; + color_rect.style.width = '10px'; + color_rect.style.display = 'inline-block'; + color_rect.style.margin = '0 4px'; + color_rect.style.border = '1px solid #7f7f7f'; + } + + placeholder = placeholder || ' ... '; + append(empty, + A(renderjson.show, "disclosure", show), + color_rect, + themetext(type + " syntax", open), + A(placeholder, null, show), + themetext(type + " syntax", close)); + + var el = append(span(), text(my_indent.slice(0, -1)), empty); + if (show_level > 0) + show(); + return el; + }; + + if (json.constructor == Array) { + if (json.length == 0) return themetext(null, my_indent, "array syntax", "[]"); + + return disclosure("[", "]", "array", function() { + var as = append(span("array"), themetext("array syntax", "[", null, "\n")); + for (var i = 0; i < json.length; i++) + append(as, + _renderjson(json[i], indent + " ", false, show_level - 1, sort_objects), + i != json.length - 1 ? themetext("syntax", ",") : [], + text("\n")); + append(as, themetext(null, indent, "array syntax", "]")); + return as; + }); + } + + // object + if (isempty(json)) + return themetext(null, my_indent, "object syntax", "{}"); + + + return disclosure("{", "}", "object", function() { + var os = append(span("object"), themetext("object syntax", "{", null, "\n")); + for (var k in json) var last = k; + var keys = Object.keys(json); + if (sort_objects) + keys = keys.sort(); + for (var i in keys) { + var k = keys[i]; + append(os, themetext(null, indent + " ", "key", '"' + k + '"', "object syntax", ': '), + _renderjson(json[k], indent + " ", true, show_level - 1, sort_objects), + k != last ? themetext("syntax", ",") : [], + text("\n")); + } + append(os, themetext(null, indent, "object syntax", "}")); + return os; + }); + } + + var renderjson = function renderjson(json) { + var pre = append(document.createElement("pre"), _renderjson(json, "", false, renderjson.show_to_level, renderjson.sort_objects)); + pre.className = "renderjson"; + return pre; + }; + renderjson.set_icons = function(show, hide) { + renderjson.show = show; + renderjson.hide = hide; + return renderjson; + }; + renderjson.set_show_to_level = function(level) { + renderjson.show_to_level = typeof level == "string" && + level.toLowerCase() === "all" ? Number.MAX_VALUE : level; + return renderjson; + }; + renderjson.set_sort_objects = function(sort_bool) { + renderjson.sort_objects = sort_bool; + return renderjson; + }; + // Backwards compatibility. Use set_show_to_level() for new code. + renderjson.set_show_by_default = function(show) { + renderjson.show_to_level = show ? Number.MAX_VALUE : 0; + return renderjson; + }; + renderjson.set_icons('⊕', '⊖'); + renderjson.set_show_by_default(false); + renderjson.set_sort_objects(false); + return renderjson; +})(); \ No newline at end of file diff --git a/frameworks/cocos2d-html5/external/pluginx/Plugin.js b/frameworks/cocos2d-html5/external/pluginx/Plugin.js new file mode 100644 index 0000000..e2bc145 --- /dev/null +++ b/frameworks/cocos2d-html5/external/pluginx/Plugin.js @@ -0,0 +1,253 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * plugin manager + * @class + * + */ +(function(){ + + if(cc === undefined){ + return; + } + + //Native plugin usage + var PluginManager = function(){}; + + PluginManager.prototype = { + constructor: PluginManager, + + /** + * @returns {PluginManager} + * @expose + */ + getInstance: function(){ + return this; + }, + + /** + * @param {String} pluginName + * @expose + */ + loadPlugin: function(pluginName){ + + }, + + /** + * + * @param pluginName + * @expose + */ + unloadPlugin: function(pluginName){ + + } + }; + + var PluginAssembly = function(){}; + + PluginAssembly.prototype = { + constructor: PluginAssembly, + + /** + * @param {Boolean} debug + * @expose + */ + setDebugMode: function(debug){}, + + /** + * @param {String} appKey + * @expose + */ + startSession: function(appKey){}, + + /** + * @param {Boolean} Capture + * @expose + */ + setCaptureUncaughtException: function(Capture){}, + + /** + * @param {String} funName + * @param {All} Params + * @expose + */ + callFuncWithParam: function(funName){ + if(typeof this[funName] === 'function'){ + return this[funName].apply(this, Array.prototype.splice.call(arguments, 1)); + }else{ + cc.log("function is not define"); + } + }, + + /** + * @param {String} funName + * @param {All} Params + * @expose + */ + callStringFuncWithParam: function(funName){ + this.callFuncWithParam.apply(arguments); + }, + + /** + * @returns {String} + * @expose + */ + getPluginName: function(){ + return this._name; + }, + + /** + * @returns {String} + * @expose + */ + getPluginVersion: function(){ + return this._version; + } + }; + + /** @expose */ + PluginAssembly.extend = function(name, porp){ + var p, prototype = {}; + for(p in PluginAssembly.prototype){ + prototype[p] = PluginAssembly.prototype[p]; + } + for(p in porp){ + prototype[p] = porp[p]; + } + var tmp = eval("(function " + name + "Plugin(){})"); + prototype.constructor = tmp; + tmp.prototype = prototype; + return tmp; + }; + + //Param + var Param = function(type, value){ + var paramType = plugin.PluginParam.ParamType,tmpValue; + switch(type){ + case paramType.TypeInt: + tmpValue = parseInt(value); + break; + case paramType.TypeFloat: + tmpValue = parseFloat(value); + break; + case paramType.TypeBool: + tmpValue = Boolean(value); + break; + case paramType.TypeString: + tmpValue = String(value); + break; + case paramType.TypeStringMap: + tmpValue = value//JSON.stringify(value); + break; + default: + tmpValue = value; + } + return tmpValue + }; + + /** @expose */ + Param.ParamType = { + /** @expose */ + TypeInt:1, + /** @expose */ + TypeFloat:2, + /** @expose */ + TypeBool:3, + /** @expose */ + TypeString:4, + /** @expose */ + TypeStringMap:5 + }; + + /** @expose */ + Param.AdsResultCode = { + /** @expose */ + AdsReceived:0, + /** @expose */ + FullScreenViewShown:1, + /** @expose */ + FullScreenViewDismissed:2, + /** @expose */ + PointsSpendSucceed:3, + /** @expose */ + PointsSpendFailed:4, + /** @expose */ + NetworkError:5, + /** @expose */ + UnknownError:6 + }; + + /** @expose */ + Param.PayResultCode = { + /** @expose */ + PaySuccess:0, + /** @expose */ + PayFail:1, + /** @expose */ + PayCancel:2, + /** @expose */ + PayTimeOut:3 + }; + + /** @expose */ + Param.ShareResultCode = { + /** @expose */ + ShareSuccess:0, + /** @expose */ + ShareFail:1, + /** @expose */ + ShareCancel:2, + /** @expose */ + ShareTimeOut:3 + }; + + /** @expose */ + var PluginList = {}; + + /** @expose */ + var Plugin = { + + /** @expose */ + extend: function(name, extend){ + var config = (cc.game.config && cc.game.config.plugin) || {}; + PluginList[name] = new (PluginAssembly.extend(name, extend)); + typeof PluginList[name].ctor === "function" && PluginList[name].ctor(config[name]); + }, + + /** @expose */ + PluginList: PluginList, + + /** @expose */ + PluginParam: Param, + + /** @expose */ + PluginManager: new PluginManager() + + }; + + /** @expose */ + window.plugin = Plugin; + +})(); \ No newline at end of file diff --git a/frameworks/cocos2d-html5/external/pluginx/platform/facebook.js b/frameworks/cocos2d-html5/external/pluginx/platform/facebook.js new file mode 100644 index 0000000..1901159 --- /dev/null +++ b/frameworks/cocos2d-html5/external/pluginx/platform/facebook.js @@ -0,0 +1,557 @@ +/**************************************************************************** + Copyright (c) 2013-2014 Chukong Technologies Inc. + + http://www.cocos2d-x.org + + 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. + ****************************************************************************/ + +/** + * Facebook SDK for Web Platform
+ * FacebookAgent... + * + * @property {String} name - plugin name + * @property {String} version - API version + */ +plugin.extend('facebook', { + name: "", + version: "", + _userInfo: null, + _isLoggedIn: false, + + /** + * HTTP methods constants + * @constant + * @type {Object} + */ + HttpMethod: { + 'GET': 'get', + 'POST': 'post', + 'DELETE': 'delete' + }, + + /** + * Succeed code returned in callbacks + * @constant + * @type {Number} + */ + CODE_SUCCEED: 0, + + /** + * App event names constants + * @constant + * @type {Object} + */ + AppEvent: { + 'ACTIVATED_APP': FB.AppEvents.EventNames.ACTIVATED_APP, + 'COMPLETED_REGISTRATION': FB.AppEvents.EventNames.COMPLETED_REGISTRATION, + 'VIEWED_CONTENT': FB.AppEvents.EventNames.VIEWED_CONTENT, + 'SEARCHED': FB.AppEvents.EventNames.SEARCHED, + 'RATED': FB.AppEvents.EventNames.RATED, + 'COMPLETED_TUTORIAL': FB.AppEvents.EventNames.COMPLETED_TUTORIAL, + 'ADDED_TO_CART': FB.AppEvents.EventNames.ADDED_TO_CART, + 'ADDED_TO_WISHLIST': FB.AppEvents.EventNames.ADDED_TO_WISHLIST, + 'INITIATED_CHECKOUT': FB.AppEvents.EventNames.INITIATED_CHECKOUT, + 'ADDED_PAYMENT_INFO': FB.AppEvents.EventNames.ADDED_PAYMENT_INFO, + 'PURCHASED': FB.AppEvents.EventNames.PURCHASED, + 'ACHIEVED_LEVEL': FB.AppEvents.EventNames.ACHIEVED_LEVEL, + 'UNLOCKED_ACHIEVEMENT': FB.AppEvents.EventNames.UNLOCKED_ACHIEVEMENT, + 'SPENT_CREDITS': FB.AppEvents.EventNames.SPENT_CREDITS + }, + + /** + * App event parameter names constants + * @constant + * @type {Object} + */ + AppEventParam: { + 'CURRENCY': FB.AppEvents.ParameterNames.CURRENCY, + 'REGISTRATION_METHOD': FB.AppEvents.ParameterNames.REGISTRATION_METHOD, + 'CONTENT_TYPE': FB.AppEvents.ParameterNames.CONTENT_TYPE, + 'CONTENT_ID': FB.AppEvents.ParameterNames.CONTENT_ID, + 'SEARCH_STRING': FB.AppEvents.ParameterNames.SEARCH_STRING, + 'SUCCESS': FB.AppEvents.ParameterNames.SUCCESS, + 'MAX_RATING_VALUE': FB.AppEvents.ParameterNames.MAX_RATING_VALUE, + 'PAYMENT_INFO_AVAILABLE': FB.AppEvents.ParameterNames.PAYMENT_INFO_AVAILABLE, + 'NUM_ITEMS': FB.AppEvents.ParameterNames.NUM_ITEMS, + 'LEVEL': FB.AppEvents.ParameterNames.LEVEL, + 'DESCRIPTION': FB.AppEvents.ParameterNames.DESCRIPTION + }, + + /** + * App event parameter values constants + * @constant + * @type {Object} + */ + AppEventParamValue: { + 'VALUE_YES': "1", + 'VALUE_NO': "0" + }, + + _checkLoginStatus: function() { + var self = this; + FB.getLoginStatus(function (response) { + if (response && response.status === 'connected') { + //login + self._isLoggedIn = true; + //save user info + self._userInfo = response['authResponse']; + } else { + // Reset cached status + self._isLoggedIn = false; + self._userInfo = {}; + } + }); + }, + + ctor: function (config) { + this.name = "facebook"; + this.version = "1.0"; + this._userInfo = {}; + this._isLoggedIn = false; + + if (!FB) { + return; + } + + //This configuration will be read from the project.json. + FB.init(config); + this._checkLoginStatus(); + + plugin.FacebookAgent = this; + }, + /** + * Gets the current object + * @returns {FacebookAgent} + */ + getInstance: function () { + return this; + }, + /** + * Login to facebook + * @param {Function} callback + * @param {Array} permissions + * @example + * //example + * plugin.FacebookAgent.login(); + */ + login: function (permissions, callback) { + var self = this; + if (typeof permissions == 'function') { + callback = permissions; + permissions = []; + } + if (permissions.every(function (item) { + if (item != 'public_profile') + return true; + })) { + permissions.push("public_profile"); + } + var permissionsStr = permissions.join(','); + FB.login(function (response) { + if (response['authResponse']) { + //save user info + self._isLoggedIn = true; + self._userInfo = response['authResponse']; + var permissList = response['authResponse']['grantedScopes'].split(","); + typeof callback === 'function' && callback(0, { + accessToken: response['authResponse']['accessToken'], + permissions: permissList + }); + } else { + self._isLoggedIn = false; + self._userInfo = {}; + typeof callback === 'function' && callback(response['error_code'] || 1, { + error_message: response['error_message'] || "Unknown error" + }); + } + }, { + scope: permissionsStr, + return_scopes: true + }); + }, + /** + * Checking login status + * @return {Bool} Whether user is logged in + * @example + * //example + * plugin.FacebookAgent.isLoggedIn(type, msg); + */ + isLoggedIn: function () { + //this._checkLoginStatus(); + return this._isLoggedIn; + }, + + /** + * Logout of facebook + * @param {Function} callback + * @example + * //example + * plugin.FacebookAgent.logout(callback); + */ + logout: function (callback) { + var self = this; + FB.logout(function (response) { + if (response['authResponse']) { + // user is now logged out + self._isLoggedIn = false; + self._userInfo = {}; + typeof callback === 'function' && callback(0, {"isLoggedIn": false}); + } else { + typeof callback === 'function' && callback(response['error_code'] || 1, { + error_message: response['error_message'] || "Unknown error" + }); + } + }); + }, + + /** + * Acquiring new permissions + * @deprecated since v3.0 + * @param permissions + * @param callback + * @example + * //example + * plugin.FacebookAgent.requestPermissions(["manage_pages"], callback); + */ + _requestPermissions: function (permissions, callback) { + var permissionsStr = permissions.join(','); + var self = this; + FB.login(function (response) { + if (response['authResponse']) { + var permissList = response['authResponse']['grantedScopes'].split(","); + //save user info + self._isLoggedIn = true; + self._userInfo = response['authResponse']; + typeof callback === 'function' && callback(0, { + permissions: permissList + }); + } else { + self._isLoggedIn = false; + self._userInfo = {}; + typeof callback === 'function' && callback(response['error_code'] || 1, { + error_message: response['error_message'] || "Unknown error" + }); + } + }, { + scope: permissionsStr, + return_scopes: true + }); + }, + + /** + * Acquiring AccessToken + * @return {String} + * @example + * //example + * var accessToken = plugin.FacebookAgent.getAccessToken(); + */ + getAccessToken: function () { + return this._userInfo ? this._userInfo['accessToken'] : null; + }, + + /** + * Acquiring User ID + * @return {String} + * @example + * //example + * var userID = plugin.FacebookAgent.getUserID(); + */ + getUserID: function () { + return this._userInfo ? this._userInfo['userID'] : null; + }, + + _share: function (info, callback) { + FB.ui({ + method: 'share', + name: info['title'], + caption: info['caption'], + description: info['text'], + href: info['link'], + picture: info['imageUrl'] + }, + function (response) { + if (response) { + if (response['post_id']) + typeof callback === 'function' && callback(0, { + didComplete: true, + post_id: response['post_id'] + }); + else + typeof callback === 'function' && callback(response['error_code'] || 1, { + error_message: response['error_message'] || "Unknown error" + }); + } else { + typeof callback === 'function' && callback(1, { + error_message: "Unknown error" + }); + } + }); + }, + + /** + * Request a web dialog for Facebook sharing + * @param info + * @param callback + */ + dialog: function (info, callback) { + if (!info) { + typeof callback === 'function' && callback(1, { + error_message: "No info parameter provided" + }); + return; + } + if (!this.canPresentDialog(info)) { + typeof callback === 'function' && callback(1, { + error_message: "The requested dialog: " + info['dialog'] + " can not be presented on Web" + }); + return; + } + + // Preprocess properties + info['name'] = info['name'] || info['site']; + delete info['site']; + + info['href'] = info['href'] || info['link'] || info['siteUrl']; + delete info['siteUrl']; + delete info['link']; + + info['picture'] = info['picture'] || info['image'] || info['photo'] || info['imageUrl'] || info['imagePath']; + delete info['imageUrl']; + delete info['imagePath']; + delete info['photo']; + delete info['image']; + + info['caption'] = info['title'] || info['caption']; + delete info['title']; + + info['description'] = info['text'] || info['description']; + delete info['text']; + + var method = info['dialog']; + delete info['dialog']; + + if (method === 'shareLink' || method == 'feedDialog') { + info['method'] = 'share'; + } else if (method == 'messageLink') { + info['method'] = 'send'; + info['link'] = info['href']; + } else if (method == 'shareOpenGraph') { + info['method'] = 'share_open_graph'; + + if (info['url']) { + var obj = {}; + if (info["preview_property_name"]) + obj[info["preview_property_name"]] = info["url"]; + else + obj["object"] = info["url"]; + + for (var p in info) { + if (p != "method" && p != "action_type" && p != "action_properties") { + info[p] && (obj[p] = info[p]); + delete info[p]; + } + } + + info['action_properties'] = JSON.stringify(obj); + } + } + + FB.ui(info, + function (response) { + if (response && typeof callback === 'function') { + if (response['post_id'] || response['success']) { + callback(0, { + didComplete: true, + post_id: response['post_id'] || "" + }); + } + else { + if (response['error_code']) { + callback(response['error_code'], { + error_message : response['error_message'] || 'Unknown error' + }); + } + else callback(0, response); + } + } else if (response == undefined && typeof callback === 'function') { + callback(1, { + error_message: "Unknown error" + }); + } + }); + }, + + /** + * Check whether the share request can be achieved + * @param info + */ + canPresentDialog: function (info) { + if (info && info['dialog'] && ( + info['dialog'] === 'shareLink' || + info['dialog'] === 'feedDialog' || + info['dialog'] === 'shareOpenGraph' || + info['dialog'] === 'messageLink')) + return true; + else + return false; + }, + /** + * FB.api + * @param {String} path + * @param {Number} httpmethod + * @param {Object} params + * @param {Function} callback + */ + api: function (path, httpmethod, params, callback) { + if (typeof params === 'function') { + callback = params; + params = {}; + } + FB.api(path, httpmethod, params, function (response) { + if (response.error) { + typeof callback === 'function' && callback(response['error']['code'], { + error_message: response['error']['message'] || 'Unknown error' + }) + } else { + typeof callback === 'function' && callback(0, response); + } + }); + }, + + _getPermissionList: function (callback) { + FB.api("/me/permissions", function (response) { + if (response['data']) { + var permissionList = []; + for (var i = 0; i < response['data'].length; i++) { + if (response['data'][i]["status"] == "granted") { + permissionList.push(response['data'][i]['permission']); + } + } + typeof callback == 'function' && callback(0, { + permissions: permissionList + }); + } else { + if (!response['error']) + response['error'] = {}; + typeof callback == 'function' && callback(response['error']['code'] || 1, { + error_message: response['error']['message'] || 'Unknown error' + }); + } + }) + }, + + destroyInstance: function () { + }, + canvas:{ + /** + * Payment request + * @param {Object} info + * @param {Function} callback + */ + pay: function (info, callback) { + /* + * Reference document + * https://developers.facebook.com/docs/payments/reference/paydialog + */ + info['method'] = 'pay'; + info['action'] = 'purchaseitem'; + + FB.ui(info, function (response) { + if (response['error_code']) { + callback(response['error_code'] || 1, { + error_message: response['error_message'] || response['error_msg'] || 'Unknown error' + }); + } else { + callback(0, response); + } + }) + } + }, + + /** + * Send an app requests to friends + * @param {Object} info + * @param {Function} callback + */ + appRequest: function (info, callback) { + if (!info) { + typeof callback === 'function' && callback(1, { + error_message: "No info parameter provided" + }); + return; + } + + info['method'] = "apprequests"; + + FB.ui(info, + function (response) { + if (response) { + if (response['error_code']) { + typeof callback === 'function' && callback(response['error_code'], { + error_message : response['error_message'] || 'Unknown error' + }); + } + else { + typeof callback === 'function' && callback(0, response); + } + } else { + typeof callback === 'function' && callback(1, { + error_message: "Unknown error" + }); + } + }); + }, + + /** + * Log an event + * @param {String} eventName + * @param {Number} valueToSum + * @param {Object} parameters + */ + logEvent: function (eventName, valueToSum, parameters) { + if (eventName == undefined) return; + if (valueToSum === undefined && parameters === undefined) { + FB.AppEvents.logEvent(eventName, null, null); + } else if (typeof valueToSum === "number" && parameters === undefined) { + FB.AppEvents.logEvent(eventName, valueToSum); + } else if (typeof valueToSum === "object" && parameters === undefined) { + FB.AppEvents.logEvent(eventName, null, valueToSum); + } else { + FB.AppEvents.logEvent(eventName, valueToSum, parameters); + } + }, + + /** + * Activate App + */ + activateApp: function () { + FB.AppEvents.activateApp(); + }, + + /** + * Log a purchase + * @param {Number} amount Amount of the purchase + * @param {String} currency The currency + * @param {Object} param Supplemental parameters + */ + logPurchase:function(amount, currency, param){ + FB.AppEvents.logPurchase(amount, currency, param); + } +}); diff --git a/frameworks/cocos2d-html5/external/pluginx/platform/facebook_sdk.js b/frameworks/cocos2d-html5/external/pluginx/platform/facebook_sdk.js new file mode 100644 index 0000000..bc05068 --- /dev/null +++ b/frameworks/cocos2d-html5/external/pluginx/platform/facebook_sdk.js @@ -0,0 +1,151 @@ + +/*1411456395,,JIT Construction: v1425205,zh_CN*/ + +/** + * Copyright Facebook Inc. + * + * Licensed under the Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + */ +try {window.FB || (function(window) { + var self = window, document = window.document; + var setTimeout = window.setTimeout, setInterval = window.setInterval,clearTimeout = window.clearTimeout,clearInterval = window.clearInterval;var __DEV__ = 0; + function emptyFunction() {}; + var __w, __t; + __t=function(a){return a[0];};__w=function(a){return a;}; + var require,__d;(function(a){var b={},c={},d=['global','require','requireDynamic','requireLazy','module','exports'];require=function(e,f){if(c.hasOwnProperty(e))return c[e];if(!b.hasOwnProperty(e)){if(f)return null;throw new Error('Module '+e+' has not been defined');}var g=b[e],h=g.deps,i=g.factory.length,j,k=[];for(var l=0;l1?Number(arguments[1]):0;if(isNaN(j))j=0;var k=Math.min(Math.max(j,0),i.length);return i.indexOf(String(h),j)==k;};g.endsWith=function(h){var i=String(this);if(this==null)throw new TypeError('String.prototype.endsWith called on null or undefined');var j=i.length,k=String(h),l=arguments.length>1?Number(arguments[1]):j;if(isNaN(l))l=0;var m=Math.min(Math.max(l,0),j),n=m-k.length;if(n<0)return false;return i.lastIndexOf(k,n)==n;};g.contains=function(h){if(this==null)throw new TypeError('String.prototype.contains called on null or undefined');var i=String(this),j=arguments.length>1?Number(arguments[1]):0;if(isNaN(j))j=0;return i.indexOf(String(h),j)!=-1;};g.repeat=function(h){if(this==null)throw new TypeError('String.prototype.repeat called on null or undefined');var i=String(this),j=h?Number(h):0;if(isNaN(j))j=0;if(j<0||j===Infinity)throw RangeError();if(j===1)return i;if(j===0)return '';var k='';while(j){if(j&1)k+=i;if((j>>=1))i+=i;}return k;};e.exports=g;},null); + __d("ES5Array",[],function(a,b,c,d,e,f){var g={};g.isArray=function(h){return Object.prototype.toString.call(h)=='[object Array]';};e.exports=g;},null); + __d("ie8DontEnum",[],function(a,b,c,d,e,f){var g=['toString','toLocaleString','valueOf','hasOwnProperty','isPrototypeOf','prototypeIsEnumerable','constructor'],h=({}).hasOwnProperty,i=function(){};if(({toString:true}).propertyIsEnumerable('toString'))i=function(j,k){for(var l=0;l1)))/4)-ca((ga-1901+ha)/100)+ca((ga-1601+ha)/400);};}if(typeof JSON=="object"&&JSON){k.stringify=JSON.stringify;k.parse=JSON.parse;}if((m=typeof k.stringify=="function"&&!ea)){(ba=function(){return 1;}).toJSON=ba;try{m=k.stringify(0)==="0"&&k.stringify(new Number())==="0"&&k.stringify(new String())=='""'&&k.stringify(g)===j&&k.stringify(j)===j&&k.stringify()===j&&k.stringify(ba)==="1"&&k.stringify([ba])=="[1]"&&k.stringify([j])=="[null]"&&k.stringify(null)=="null"&&k.stringify([j,g,null])=="[null,null,null]"&&k.stringify({result:[ba,true,false,null,"\0\b\n\f\r\t"]})==l&&k.stringify(null,ba)==="1"&&k.stringify([1,2],null,1)=="[\n 1,\n 2\n]"&&k.stringify(new Date(-8.64e+15))=='"-271821-04-20T00:00:00.000Z"'&&k.stringify(new Date(8.64e+15))=='"+275760-09-13T00:00:00.000Z"'&&k.stringify(new Date(-62198755200000))=='"-000001-01-01T00:00:00.000Z"'&&k.stringify(new Date(-1))=='"1969-12-31T23:59:59.999Z"';}catch(fa){m=false;}}if(typeof k.parse=="function")try{if(k.parse("0")===0&&!k.parse(false)){ba=k.parse(l);if((r=ba.A.length==5&&ba.A[0]==1)){try{r=!k.parse('"\t"');}catch(fa){}if(r)try{r=k.parse("01")!=1;}catch(fa){}}}}catch(fa){r=false;}ba=l=null;if(!m||!r){if(!(h={}.hasOwnProperty))h=function(ga){var ha={},ia;if((ha.__proto__=null,ha.__proto__={toString:1},ha).toString!=g){h=function(ja){var ka=this.__proto__,la=ja in (this.__proto__=null,this);this.__proto__=ka;return la;};}else{ia=ha.constructor;h=function(ja){var ka=(this.constructor||ia).prototype;return ja in this&&!(ja in ka&&this[ja]===ka[ja]);};}ha=null;return h.call(this,ga);};i=function(ga,ha){var ia=0,ja,ka,la,ma;(ja=function(){this.valueOf=0;}).prototype.valueOf=0;ka=new ja();for(la in ka)if(h.call(ka,la))ia++;ja=ka=null;if(!ia){ka=["valueOf","toString","toLocaleString","propertyIsEnumerable","isPrototypeOf","hasOwnProperty","constructor"];ma=function(na,oa){var pa=g.call(na)=="[object Function]",qa,ra;for(qa in na)if(!(pa&&qa=="prototype")&&h.call(na,qa))oa(qa);for(ra=ka.length;qa=ka[--ra];h.call(na,qa)&&oa(qa));};}else if(ia==2){ma=function(na,oa){var pa={},qa=g.call(na)=="[object Function]",ra;for(ra in na)if(!(qa&&ra=="prototype")&&!h.call(pa,ra)&&(pa[ra]=1)&&h.call(na,ra))oa(ra);};}else ma=function(na,oa){var pa=g.call(na)=="[object Function]",qa,ra;for(qa in na)if(!(pa&&qa=="prototype")&&h.call(na,qa)&&!(ra=qa==="constructor"))oa(qa);if(ra||h.call(na,(qa="constructor")))oa(qa);};return ma(ga,ha);};if(!m){n={"\\":"\\\\",'"':'\\"',"\b":"\\b","\f":"\\f","\n":"\\n","\r":"\\r","\t":"\\t"};o=function(ga,ha){return ("000000"+(ha||0)).slice(-ga);};p=function(ga){var ha='"',ia=0,ja;for(;ja=ga.charAt(ia);ia++)ha+='\\"\b\f\n\r\t'.indexOf(ja)>-1?n[ja]:ja<" "?"\\u00"+o(2,ja.charCodeAt(0).toString(16)):ja;return ha+'"';};q=function(ga,ha,ia,ja,ka,la,ma){var na=ha[ga],oa,pa,qa,ra,sa,ta,ua,va,wa,xa,ya,za,ab,bb,cb;if(typeof na=="object"&&na){oa=g.call(na);if(oa=="[object Date]"&&!h.call(na,"toJSON")){if(na>-1/0&&na<1/0){if(ea){ra=ca(na/86400000);for(pa=ca(ra/365.2425)+1970-1;ea(pa+1,0)<=ra;pa++);for(qa=ca((ra-ea(pa,0))/30.42);ea(pa,qa+1)<=ra;qa++);ra=1+ra-ea(pa,qa);sa=(na%86400000+86400000)%86400000;ta=ca(sa/3600000)%24;ua=ca(sa/60000)%60;va=ca(sa/1000)%60;wa=sa%1000;}else{pa=na.getUTCFullYear();qa=na.getUTCMonth();ra=na.getUTCDate();ta=na.getUTCHours();ua=na.getUTCMinutes();va=na.getUTCSeconds();wa=na.getUTCMilliseconds();}na=(pa<=0||pa>=10000?(pa<0?"-":"+")+o(6,pa<0?-pa:pa):o(4,pa))+"-"+o(2,qa+1)+"-"+o(2,ra)+"T"+o(2,ta)+":"+o(2,ua)+":"+o(2,va)+"."+o(3,wa)+"Z";}else na=null;}else if(typeof na.toJSON=="function"&&((oa!="[object Number]"&&oa!="[object String]"&&oa!="[object Array]")||h.call(na,"toJSON")))na=na.toJSON(ga);}if(ia)na=ia.call(ha,ga,na);if(na===null)return "null";oa=g.call(na);if(oa=="[object Boolean]"){return ""+na;}else if(oa=="[object Number]"){return na>-1/0&&na<1/0?""+na:"null";}else if(oa=="[object String]")return p(na);if(typeof na=="object"){for(ab=ma.length;ab--;)if(ma[ab]===na)throw TypeError();ma.push(na);xa=[];bb=la;la+=ka;if(oa=="[object Array]"){for(za=0,ab=na.length;za0)for(ja="",ia>10&&(ia=10);ja.length-1){z++;}else if("{}[]:,".indexOf(ia)>-1){z++;return ia;}else if(ia=='"'){for(ja="@",z++;z-1){ja+=t[ia];z++;}else if(ia=="u"){ka=++z;for(la=z+4;z="0"&&ia<="9"||ia>="a"&&ia<="f"||ia>="A"&&ia<="F"))u();}ja+=s("0x"+ga.slice(ka,z));}else u();}else{if(ia=='"')break;ja+=ia;z++;}}if(ga.charAt(z)=='"'){z++;return ja;}u();}else{ka=z;if(ia=="-"){ma=true;ia=ga.charAt(++z);}if(ia>="0"&&ia<="9"){if(ia=="0"&&(ia=ga.charAt(z+1),ia>="0"&&ia<="9"))u();ma=false;for(;z="0"&&ia<="9");z++);if(ga.charAt(z)=="."){la=++z;for(;la="0"&&ia<="9");la++);if(la==z)u();z=la;}ia=ga.charAt(z);if(ia=="e"||ia=="E"){ia=ga.charAt(++z);if(ia=="+"||ia=="-")z++;for(la=z;la="0"&&ia<="9");la++);if(la==z)u();z=la;}return +ga.slice(ka,z);}if(ma)u();if(ga.slice(z,z+4)=="true"){z+=4;return true;}else if(ga.slice(z,z+5)=="false"){z+=5;return false;}else if(ga.slice(z,z+4)=="null"){z+=4;return null;}u();}}return "$";};w=function(ga){var ha,ia,ja;if(ga=="$")u();if(typeof ga=="string"){if(ga.charAt(0)=="@")return ga.slice(1);if(ga=="["){ha=[];for(;;ia||(ia=true)){ga=v();if(ga=="]")break;if(ia)if(ga==","){ga=v();if(ga=="]")u();}else u();if(ga==",")u();ha.push(w(ga));}return ha;}else if(ga=="{"){ha={};for(;;ia||(ia=true)){ga=v();if(ga=="}")break;if(ia)if(ga==","){ga=v();if(ga=="}")u();}else u();if(ga==","||typeof ga!="string"||ga.charAt(0)!="@"||v()!=":")u();ha[ga.slice(1)]=w(v());}return ha;}u();}return ga;};y=function(ga,ha,ia){var ja=x(ga,ha,ia);if(ja===j){delete ga[ha];}else ga[ha]=ja;};x=function(ga,ha,ia){var ja=ga[ha],ka;if(typeof ja=="object"&&ja)if(g.call(ja)=="[object Array]"){for(ka=ja.length;ka--;)y(ja,ka,ia);}else i(ja,function(la){y(ja,la,ia);});return ia.call(ga,ha,ja);};k.parse=function(ga,ha){z=0;aa=ga;var ia=w(v());if(v()!="$")u();z=aa=null;return ha&&g.call(ha)=="[object Function]"?x((ba={},ba[""]=ia,ba),"",ha):ia;};}}}).call(this);},null); + __d("ES6Object",["ie8DontEnum"],function(a,b,c,d,e,f,g){var h=({}).hasOwnProperty,i={assign:function(j){var k=Array.prototype.slice.call(arguments,1);if(j==null)throw new TypeError('Object.assign target cannot be null or undefined');j=Object(j);for(var l=0;l>>0;for(var l=0;l9999?'+':''))+('00000'+Math.abs(i)).slice(0<=i&&i<=9999?-4:-6);return i+'-'+g(this.getUTCMonth()+1)+'-'+g(this.getUTCDate())+'T'+g(this.getUTCHours())+':'+g(this.getUTCMinutes())+':'+g(this.getUTCSeconds())+'.'+(this.getUTCMilliseconds()/1000).toFixed(3).slice(2,5)+'Z';}};e.exports=h;},null); + __d("ES6Number",[],function(a,b,c,d,e,f){var g={isFinite:function(h){return (typeof h=='number')&&isFinite(h);},isNaN:function(h){return (typeof h=='number')&&isNaN(h);}};e.exports=g;},null); + __d("ES",["ES5ArrayPrototype","ES5FunctionPrototype","ES5StringPrototype","ES5Array","ES5Object","ES5Date","JSON3","ES6Object","ES6ArrayPrototype","ES6DatePrototype","ES6Number"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q){var r=({}).toString,s={'JSON.stringify':m.stringify,'JSON.parse':m.parse},t={'Array.prototype':g,'Function.prototype':h,'String.prototype':i,Object:k,Array:j,Date:l},u={Object:n,'Array.prototype':o,'Date.prototype':p,Number:q};function v(x){for(var y in x){if(!x.hasOwnProperty(y))continue;var z=x[y],aa=y.split('.'),ba=aa.length==2?window[aa[0]][aa[1]]:window[y];for(var ca in z){if(!z.hasOwnProperty(ca))continue;var da=ba[ca];s[y+'.'+ca]=da&&/\{\s+\[native code\]\s\}/.test(da)?da:z[ca];}}}v(t);v(u);function w(x,y,z){var aa=Array.prototype.slice.call(arguments,3),ba=z?r.call(x).slice(8,-1)+'.prototype':x,ca=s[ba+'.'+y]||x[y];if(typeof ca==='function')return ca.apply(x,aa);}e.exports=w;},null); + var ES = require('ES'); + __d("JSSDKRuntimeConfig",[],{"locale":"zh_CN","rtl":false,"revision":"1425205"});__d("JSSDKConfig",[],{"bustCache":true,"tagCountLogRate":0.01,"errorHandling":{"rate":4},"usePluginPipe":true,"features":{"allow_non_canvas_app_events":false,"event_subscriptions_log":{"rate":0.01,"value":10000},"kill_fragment":true,"xfbml_profile_pic_server":true,"error_handling":{"rate":4},"e2e_ping_tracking":{"rate":1.0e-6},"xd_timeout":{"rate":4,"value":30000},"use_bundle":true,"launch_payment_dialog_via_pac":{"rate":100}},"api":{"mode":"warn","whitelist":["Canvas","Canvas.Prefetcher","Canvas.Prefetcher.addStaticResource","Canvas.Prefetcher.setCollectionMode","Canvas.getPageInfo","Canvas.hideFlashElement","Canvas.scrollTo","Canvas.setAutoGrow","Canvas.setDoneLoading","Canvas.setSize","Canvas.setUrlHandler","Canvas.showFlashElement","Canvas.startTimer","Canvas.stopTimer","Data","Data.process","Data.query","Data.query:wait","Data.waitOn","Data.waitOn:wait","Event","Event.subscribe","Event.unsubscribe","Music.flashCallback","Music.init","Music.send","Payment","Payment.cancelFlow","Payment.continueFlow","Payment.init","Payment.lockForProcessing","Payment.unlockForProcessing","Payment.parse","Payment.setSize","ThirdPartyProvider","ThirdPartyProvider.init","ThirdPartyProvider.sendData","UA","UA.nativeApp","XFBML","XFBML.RecommendationsBar","XFBML.RecommendationsBar.markRead","XFBML.parse","addFriend","api","getAccessToken","getAuthResponse","getLoginStatus","getUserID","init","login","logout","publish","share","ui","ui:subscribe","AppEvents","AppEvents.activateApp","AppEvents.logEvent","AppEvents.logPurchase","AppEvents.EventNames","AppEvents.ParameterNames"]},"initSitevars":{"enableMobileComments":1,"iframePermissions":{"read_stream":false,"manage_mailbox":false,"manage_friendlists":false,"read_mailbox":false,"publish_checkins":true,"status_update":true,"photo_upload":true,"video_upload":true,"sms":false,"create_event":true,"rsvp_event":true,"offline_access":true,"email":true,"xmpp_login":false,"create_note":true,"share_item":true,"export_stream":false,"publish_stream":true,"publish_likes":true,"ads_management":false,"contact_email":true,"access_private_data":false,"read_insights":false,"read_requests":false,"read_friendlists":true,"manage_pages":false,"physical_login":false,"manage_groups":false,"read_deals":false}}});__d("UrlMapConfig",[],{"www":"www.facebook.com","m":"m.facebook.com","connect":"connect.facebook.net","business":"business.facebook.com","api_https":"api.facebook.com","api_read_https":"api-read.facebook.com","graph_https":"graph.facebook.com","fbcdn_http":"static.ak.fbcdn.net","fbcdn_https":"fbstatic-a.akamaihd.net","cdn_http":"static.ak.facebook.com","cdn_https":"s-static.ak.facebook.com"});__d("JSSDKXDConfig",[],{"XdUrl":"\/connect\/xd_arbiter.php?version=41","XdBundleUrl":"\/connect\/xd_arbiter\/ZEbdHPQfV3x.js?version=41","Flash":{"path":"https:\/\/connect.facebook.net\/rsrc.php\/v1\/yR\/r\/ks_9ZXiQ0GL.swf"},"useCdn":true});__d("JSSDKCssConfig",[],{"rules":".fb_hidden{position:absolute;top:-10000px;z-index:10001}.fb_invisible{display:none}.fb_reset{background:none;border:0;border-spacing:0;color:#000;cursor:auto;direction:ltr;font-family:\"lucida grande\", tahoma, verdana, arial, sans-serif;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:1;margin:0;overflow:visible;padding:0;text-align:left;text-decoration:none;text-indent:0;text-shadow:none;text-transform:none;visibility:visible;white-space:normal;word-spacing:normal}.fb_reset>div{overflow:hidden}.fb_link img{border:none}\n.fb_dialog{background:rgba(82, 82, 82, .7);position:absolute;top:-10000px;z-index:10001}.fb_reset .fb_dialog_legacy{overflow:visible}.fb_dialog_advanced{padding:10px;-moz-border-radius:8px;-webkit-border-radius:8px;border-radius:8px}.fb_dialog_content{background:#fff;color:#333}.fb_dialog_close_icon{background:url(http:\/\/static.ak.fbcdn.net\/rsrc.php\/v2\/yq\/r\/IE9JII6Z1Ys.png) no-repeat scroll 0 0 transparent;_background-image:url(http:\/\/static.ak.fbcdn.net\/rsrc.php\/v2\/yL\/r\/s816eWC-2sl.gif);cursor:pointer;display:block;height:15px;position:absolute;right:18px;top:17px;width:15px}.fb_dialog_mobile .fb_dialog_close_icon{top:5px;left:5px;right:auto}.fb_dialog_padding{background-color:transparent;position:absolute;width:1px;z-index:-1}.fb_dialog_close_icon:hover{background:url(http:\/\/static.ak.fbcdn.net\/rsrc.php\/v2\/yq\/r\/IE9JII6Z1Ys.png) no-repeat scroll 0 -15px transparent;_background-image:url(http:\/\/static.ak.fbcdn.net\/rsrc.php\/v2\/yL\/r\/s816eWC-2sl.gif)}.fb_dialog_close_icon:active{background:url(http:\/\/static.ak.fbcdn.net\/rsrc.php\/v2\/yq\/r\/IE9JII6Z1Ys.png) no-repeat scroll 0 -30px transparent;_background-image:url(http:\/\/static.ak.fbcdn.net\/rsrc.php\/v2\/yL\/r\/s816eWC-2sl.gif)}.fb_dialog_loader{background-color:#f2f2f2;border:1px solid #606060;font-size:25px;padding:20px}.fb_dialog_top_left,.fb_dialog_top_right,.fb_dialog_bottom_left,.fb_dialog_bottom_right{height:10px;width:10px;overflow:hidden;position:absolute}.fb_dialog_top_left{background:url(http:\/\/static.ak.fbcdn.net\/rsrc.php\/v2\/ye\/r\/8YeTNIlTZjm.png) no-repeat 0 0;left:-10px;top:-10px}.fb_dialog_top_right{background:url(http:\/\/static.ak.fbcdn.net\/rsrc.php\/v2\/ye\/r\/8YeTNIlTZjm.png) no-repeat 0 -10px;right:-10px;top:-10px}.fb_dialog_bottom_left{background:url(http:\/\/static.ak.fbcdn.net\/rsrc.php\/v2\/ye\/r\/8YeTNIlTZjm.png) no-repeat 0 -20px;bottom:-10px;left:-10px}.fb_dialog_bottom_right{background:url(http:\/\/static.ak.fbcdn.net\/rsrc.php\/v2\/ye\/r\/8YeTNIlTZjm.png) no-repeat 0 -30px;right:-10px;bottom:-10px}.fb_dialog_vert_left,.fb_dialog_vert_right,.fb_dialog_horiz_top,.fb_dialog_horiz_bottom{position:absolute;background:#525252;filter:alpha(opacity=70);opacity:.7}.fb_dialog_vert_left,.fb_dialog_vert_right{width:10px;height:100\u0025}.fb_dialog_vert_left{margin-left:-10px}.fb_dialog_vert_right{right:0;margin-right:-10px}.fb_dialog_horiz_top,.fb_dialog_horiz_bottom{width:100\u0025;height:10px}.fb_dialog_horiz_top{margin-top:-10px}.fb_dialog_horiz_bottom{bottom:0;margin-bottom:-10px}.fb_dialog_iframe{line-height:0}.fb_dialog_content .dialog_title{background:#6d84b4;border:1px solid #3b5998;color:#fff;font-size:15px;font-weight:bold;margin:0}.fb_dialog_content .dialog_title>span{background:url(http:\/\/static.ak.fbcdn.net\/rsrc.php\/v2\/yd\/r\/Cou7n-nqK52.gif) no-repeat 5px 50\u0025;float:left;padding:5px 0 7px 26px}body.fb_hidden{-webkit-transform:none;height:100\u0025;margin:0;overflow:visible;position:absolute;top:-10000px;left:0;width:100\u0025}.fb_dialog.fb_dialog_mobile.loading{background:url(http:\/\/static.ak.fbcdn.net\/rsrc.php\/v2\/ya\/r\/3rhSv5V8j3o.gif) white no-repeat 50\u0025 50\u0025;min-height:100\u0025;min-width:100\u0025;overflow:hidden;position:absolute;top:0;z-index:10001}.fb_dialog.fb_dialog_mobile.loading.centered{max-height:590px;min-height:590px;max-width:500px;min-width:500px}#fb-root #fb_dialog_ipad_overlay{background:rgba(0, 0, 0, .45);position:absolute;left:0;top:0;width:100\u0025;min-height:100\u0025;z-index:10000}#fb-root #fb_dialog_ipad_overlay.hidden{display:none}.fb_dialog.fb_dialog_mobile.loading iframe{visibility:hidden}.fb_dialog_content .dialog_header{-webkit-box-shadow:white 0 1px 1px -1px inset;background:-webkit-gradient(linear, 0\u0025 0\u0025, 0\u0025 100\u0025, from(#738ABA), to(#2C4987));border-bottom:1px solid;border-color:#1d4088;color:#fff;font:14px Helvetica, sans-serif;font-weight:bold;text-overflow:ellipsis;text-shadow:rgba(0, 30, 84, .296875) 0 -1px 0;vertical-align:middle;white-space:nowrap}.fb_dialog_content .dialog_header table{-webkit-font-smoothing:subpixel-antialiased;height:43px;width:100\u0025}.fb_dialog_content .dialog_header td.header_left{font-size:13px;padding-left:5px;vertical-align:middle;width:60px}.fb_dialog_content .dialog_header td.header_right{font-size:13px;padding-right:5px;vertical-align:middle;width:60px}.fb_dialog_content .touchable_button{background:-webkit-gradient(linear, 0\u0025 0\u0025, 0\u0025 100\u0025, from(#4966A6), color-stop(.5, #355492), to(#2A4887));border:1px solid #29447e;-webkit-background-clip:padding-box;-webkit-border-radius:3px;-webkit-box-shadow:rgba(0, 0, 0, .117188) 0 1px 1px inset, rgba(255, 255, 255, .167969) 0 1px 0;display:inline-block;margin-top:3px;max-width:85px;line-height:18px;padding:4px 12px;position:relative}.fb_dialog_content .dialog_header .touchable_button input{border:none;background:none;color:#fff;font:12px Helvetica, sans-serif;font-weight:bold;margin:2px -12px;padding:2px 6px 3px 6px;text-shadow:rgba(0, 30, 84, .296875) 0 -1px 0}.fb_dialog_content .dialog_header .header_center{color:#fff;font-size:17px;font-weight:bold;line-height:18px;text-align:center;vertical-align:middle}.fb_dialog_content .dialog_content{background:url(http:\/\/static.ak.fbcdn.net\/rsrc.php\/v2\/y9\/r\/jKEcVPZFk-2.gif) no-repeat 50\u0025 50\u0025;border:1px solid #555;border-bottom:0;border-top:0;height:150px}.fb_dialog_content .dialog_footer{background:#f2f2f2;border:1px solid #555;border-top-color:#ccc;height:40px}#fb_dialog_loader_close{float:left}.fb_dialog.fb_dialog_mobile .fb_dialog_close_button{text-shadow:rgba(0, 30, 84, .296875) 0 -1px 0}.fb_dialog.fb_dialog_mobile .fb_dialog_close_icon{visibility:hidden}\n.fb_iframe_widget{display:inline-block;position:relative}.fb_iframe_widget span{display:inline-block;position:relative;text-align:justify}.fb_iframe_widget iframe{position:absolute}.fb_iframe_widget_lift{z-index:1}.fb_hide_iframes iframe{position:relative;left:-10000px}.fb_iframe_widget_loader{position:relative;display:inline-block}.fb_iframe_widget_fluid{display:inline}.fb_iframe_widget_fluid span{width:100\u0025}.fb_iframe_widget_loader iframe{min-height:32px;z-index:2;zoom:1}.fb_iframe_widget_loader .FB_Loader{background:url(http:\/\/static.ak.fbcdn.net\/rsrc.php\/v2\/y9\/r\/jKEcVPZFk-2.gif) no-repeat;height:32px;width:32px;margin-left:-16px;position:absolute;left:50\u0025;z-index:4}\n.fbpluginrecommendationsbarleft,.fbpluginrecommendationsbarright{position:fixed !important;bottom:0;z-index:999}.fbpluginrecommendationsbarleft{left:10px}.fbpluginrecommendationsbarright{right:10px}","components":["css:fb.css.base","css:fb.css.dialog","css:fb.css.iframewidget","css:fb.css.plugin.recommendationsbar"]});__d("ApiClientConfig",[],{"FlashRequest":{"swfUrl":"https:\/\/connect.facebook.net\/rsrc.php\/v1\/yW\/r\/PvklbuW2Ycn.swf"}});__d("JSSDKCanvasPrefetcherConfig",[],{"blacklist":[144959615576466],"sampleRate":500});__d("JSSDKPluginPipeConfig",[],{"threshold":0,"enabledApps":{"209753825810663":1,"187288694643718":1}}); + __d("QueryString",[],function(a,b,c,d,e,f){function g(k){var l=[];ES(ES('Object','keys',false,k).sort(),'forEach',true,function(m){var n=k[m];if(typeof n==='undefined')return;if(n===null){l.push(m);return;}l.push(encodeURIComponent(m)+'='+encodeURIComponent(n));});return l.join('&');}function h(k,l){var m={};if(k==='')return m;var n=k.split('&');for(var o=0;oh);},ie64:function(){return x.ie()&&r;},firefox:function(){return w()||i;},opera:function(){return w()||j;},webkit:function(){return w()||k;},safari:function(){return x.webkit();},chrome:function(){return w()||l;},windows:function(){return w()||o;},osx:function(){return w()||n;},linux:function(){return w()||p;},iphone:function(){return w()||s;},mobile:function(){return w()||(s||t||q||v);},nativeApp:function(){return w()||u;},android:function(){return w()||q;},ipad:function(){return w()||t;}};e.exports=x;},null); + __d("hasNamePropertyBug",["guid","UserAgent_DEPRECATED"],function(a,b,c,d,e,f,g,h){var i=h.ie()?undefined:false;function j(){var l=document.createElement("form"),m=l.appendChild(document.createElement("input"));m.name=g();i=m!==l.elements[m.name];l=m=null;return i;}function k(){return typeof i==='undefined'?j():i;}e.exports=k;},null); + __d("wrapFunction",[],function(a,b,c,d,e,f){var g={};function h(i,j,k){j=j||'default';return function(){var l=j in g?g[j](i,k):i;return l.apply(this,arguments);};}h.setWrapper=function(i,j){j=j||'default';g[j]=i;};e.exports=h;},null); + __d("DOMEventListener",["wrapFunction"],function(a,b,c,d,e,f,g){var h,i;if(window.addEventListener){h=function(k,l,m){m.wrapper=g(m,'entry','DOMEventListener.add '+l);k.addEventListener(l,m.wrapper,false);};i=function(k,l,m){k.removeEventListener(l,m.wrapper,false);};}else if(window.attachEvent){h=function(k,l,m){m.wrapper=g(m,'entry','DOMEventListener.add '+l);k.attachEvent('on'+l,m.wrapper);};i=function(k,l,m){k.detachEvent('on'+l,m.wrapper);};}else i=h=function(){};var j={add:function(k,l,m){h(k,l,m);return {remove:function(){i(k,l,m);k=null;}};},remove:i};e.exports=j;},null); + __d("sdk.createIframe",["guid","hasNamePropertyBug","DOMEventListener"],function(a,b,c,d,e,f,g,h,i){function j(k){k=ES('Object','assign',false,{},k);var l,m=k.name||g(),n=k.root,o=k.style||{border:'none'},p=k.url,q=k.onload,r=k.onerror;if(h()){l=document.createElement('');j.root.innerHTML=('');k=true;setTimeout(function(){j.root.innerHTML=o;j.root.firstChild.src=j.url;j.onInsert&&j.onInsert(j.root.firstChild);},0);}else{var p=document.createElement('iframe');p.id=j.id;p.name=j.name;p.onload=m;p.scrolling='no';p.style.border='none';p.style.overflow='hidden';if(j.title)p.title=j.title;if(j.className)p.className=j.className;if(j.height!==undefined)p.style.height=j.height+'px';if(j.width!==undefined)if(j.width=='100%'){p.style.width=j.width;}else p.style.width=j.width+'px';j.root.appendChild(p);k=true;p.src=j.url;j.onInsert&&j.onInsert(p);}}e.exports=i;},null); + __d("Miny",[],function(a,b,c,d,e,f){var g='Miny1',h={encode:[],decode:{}},i='wxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_'.split('');function j(n){for(var o=h.encode.length;o2000)if(p.payload&&typeof p.payload==='string'){var t=j.encode(p.payload);if(t&&t.length>>18),g.charCodeAt((l>>>12)&63),g.charCodeAt((l>>>6)&63),g.charCodeAt(l&63));}var i='>___?456789:;<=_______'+'\0\1\2\3\4\5\6\7\b\t\n\13\f\r\16\17\20\21\22\23\24\25\26\27\30\31'+'______\32\33\34\35\36\37 !"#$%&\'()*+,-./0123';function j(l){l=(i.charCodeAt(l.charCodeAt(0)-43)<<18)|(i.charCodeAt(l.charCodeAt(1)-43)<<12)|(i.charCodeAt(l.charCodeAt(2)-43)<<6)|i.charCodeAt(l.charCodeAt(3)-43);return String.fromCharCode(l>>>16,(l>>>8)&255,l&255);}var k={encode:function(l){l=unescape(encodeURI(l));var m=(l.length+2)%3;l=(l+'\0\0'.slice(m)).replace(/[\s\S]{3}/g,h);return l.slice(0,l.length+m-2)+'=='.slice(m);},decode:function(l){l=l.replace(/[^A-Za-z0-9+\/]/g,'');var m=(l.length+3)&3;l=(l+'AAA'.slice(m)).replace(/..../g,j);l=l.slice(0,l.length+m-3);try{return decodeURIComponent(escape(l));}catch(n){throw new Error('Not valid UTF-8');}},encodeObject:function(l){return k.encode(ES('JSON','stringify',false,l));},decodeObject:function(l){return ES('JSON','parse',false,k.decode(l));},encodeNums:function(l){return String.fromCharCode.apply(String,ES(l,'map',true,function(m){return g.charCodeAt((m|-(m>63))&-(m>0)&63);}));}};e.exports=k;},null); + __d("sdk.SignedRequest",["Base64"],function(a,b,c,d,e,f,g){function h(j){if(!j)return null;var k=j.split('.',2)[1].replace(/\-/g,'+').replace(/\_/g,'/');return g.decodeObject(k);}var i={parse:h};e.exports=i;},null); + __d("URIRFC3986",[],function(a,b,c,d,e,f){var g=new RegExp('^'+'([^:/?#]+:)?'+'(//'+'([^\\\\/?#@]*@)?'+'('+'\\[[A-Fa-f0-9:.]+\\]|'+'[^\\/?#:]*'+')'+'(:[0-9]*)?'+')?'+'([^?#]*)'+'(\\?[^#]*)?'+'(#.*)?'),h={parse:function(i){if(ES(i,'trim',true)==='')return null;var j=i.match(g),k={};k.uri=j[0]?j[0]:null;k.scheme=j[1]?j[1].substr(0,j[1].length-1):null;k.authority=j[2]?j[2].substr(2):null;k.userinfo=j[3]?j[3].substr(0,j[3].length-1):null;k.host=j[2]?j[4]:null;k.port=j[5]?(j[5].substr(1)?parseInt(j[5].substr(1),10):null):null;k.path=j[6]?j[6]:null;k.query=j[7]?j[7].substr(1):null;k.fragment=j[8]?j[8].substr(1):null;k.isGenericURI=k.authority===null&&!!k.scheme;return k;}};e.exports=h;},null); + __d("createObjectFrom",[],function(a,b,c,d,e,f){function g(h,i){var j={},k=ES('Array','isArray',false,i);if(typeof i=='undefined')i=true;for(var l=h.length;l--;)j[h[l]]=k?i[l]:i;return j;}e.exports=g;},null); + __d("URISchemes",["createObjectFrom"],function(a,b,c,d,e,f,g){var h=g(['fb','fbcf','fbconnect','fb-messenger','fbrpc','file','ftp','http','https','mailto','ms-app','itms','itms-apps','itms-services','market','svn+ssh','fbstaging','tel','sms']),i={isAllowed:function(j){if(!j)return true;return h.hasOwnProperty(j.toLowerCase());}};e.exports=i;},null); + __d("copyProperties",[],function(a,b,c,d,e,f){function g(h,i,j,k,l,m,n){h=h||{};var o=[i,j,k,l,m],p=0,q;while(o[p]){q=o[p++];for(var r in q)h[r]=q[r];if(q.hasOwnProperty&&q.hasOwnProperty('toString')&&(typeof q.toString!='undefined')&&(h.toString!==q.toString))h.toString=q.toString;}return h;}e.exports=g;},null); + __d("eprintf",[],function(a,b,c,d,e,f){var g=function(h){var i=ES(Array.prototype.slice.call(arguments),'map',true,function(l){return String(l);}),j=h.split('%s').length-1;if(j!==i.length-1)return g('eprintf args number mismatch: %s',ES('JSON','stringify',false,i));var k=1;return h.replace(/%s/g,function(l){return String(i[k++]);});};e.exports=g;},null); + __d("ex",["eprintf"],function(a,b,c,d,e,f,g){var h=function(){var i=Array.prototype.slice.call(arguments,0);i=ES(i,'map',true,function(j){return String(j);});if(i[0].split('%s').length!==i.length)return h('ex args number mismatch: %s',ES('JSON','stringify',false,i));return h._prefix+ES('JSON','stringify',false,i)+h._suffix;};h._prefix='';e.exports=h;},null); + __d("invariant",[],function(a,b,c,d,e,f){"use strict";var g=function(h,i,j,k,l,m,n,o){if(!h){var p;if(i===undefined){p=new Error('Minified exception occurred; use the non-minified dev environment '+'for the full error message and additional helpful warnings.');}else{var q=[j,k,l,m,n,o],r=0;p=new Error('Invariant Violation: '+i.replace(/%s/g,function(){return q[r++];}));}p.framesToPop=1;throw p;}};e.exports=g;},null); + __d("URIBase",["URIRFC3986","URISchemes","copyProperties","ex","invariant"],function(a,b,c,d,e,f,g,h,i,j,k){var l=new RegExp('[\\x00-\\x2c\\x2f\\x3b-\\x40\\x5c\\x5e\\x60\\x7b-\\x7f'+'\\uFDD0-\\uFDEF\\uFFF0-\\uFFFF'+'\\u2047\\u2048\\uFE56\\uFE5F\\uFF03\\uFF0F\\uFF1F]'),m=new RegExp('^(?:[^/]*:|'+'[\\x00-\\x1f]*/[\\x00-\\x1f]*/)');function n(p,q,r,s){if(!q)return true;if(q instanceof o){p.setProtocol(q.getProtocol());p.setDomain(q.getDomain());p.setPort(q.getPort());p.setPath(q.getPath());p.setQueryData(s.deserialize(s.serialize(q.getQueryData())));p.setFragment(q.getFragment());p.setForceFragmentSeparator(q.getForceFragmentSeparator());return true;}q=ES(q.toString(),'trim',true);var t=g.parse(q)||{};if(!r&&!h.isAllowed(t.scheme))return false;p.setProtocol(t.scheme||'');if(!r&&l.test(t.host))return false;p.setDomain(t.host||'');p.setPort(t.port||'');p.setPath(t.path||'');if(r){p.setQueryData(s.deserialize(t.query)||{});}else try{p.setQueryData(s.deserialize(t.query)||{});}catch(u){return false;}p.setFragment(t.fragment||'');if(t.fragment==='')p.setForceFragmentSeparator(true);if(t.userinfo!==null)if(r){throw new Error(j('URI.parse: invalid URI (userinfo is not allowed in a URI): %s',p.toString()));}else return false;if(!p.getDomain()&&ES(p.getPath(),'indexOf',true,'\\')!==-1)if(r){throw new Error(j('URI.parse: invalid URI (no domain but multiple back-slashes): %s',p.toString()));}else return false;if(!p.getProtocol()&&m.test(q))if(r){throw new Error(j('URI.parse: invalid URI (unsafe protocol-relative URLs): %s',p.toString()));}else return false;return true;}function o(p,q){"use strict";k(q);this.$URIBase0=q;this.$URIBase1='';this.$URIBase2='';this.$URIBase3='';this.$URIBase4='';this.$URIBase5='';this.$URIBase6={};this.$URIBase7=false;n(this,p,true,q);}o.prototype.setProtocol=function(p){"use strict";k(h.isAllowed(p));this.$URIBase1=p;return this;};o.prototype.getProtocol=function(p){"use strict";return this.$URIBase1;};o.prototype.setSecure=function(p){"use strict";return this.setProtocol(p?'https':'http');};o.prototype.isSecure=function(){"use strict";return this.getProtocol()==='https';};o.prototype.setDomain=function(p){"use strict";if(l.test(p))throw new Error(j('URI.setDomain: unsafe domain specified: %s for url %s',p,this.toString()));this.$URIBase2=p;return this;};o.prototype.getDomain=function(){"use strict";return this.$URIBase2;};o.prototype.setPort=function(p){"use strict";this.$URIBase3=p;return this;};o.prototype.getPort=function(){"use strict";return this.$URIBase3;};o.prototype.setPath=function(p){"use strict";this.$URIBase4=p;return this;};o.prototype.getPath=function(){"use strict";return this.$URIBase4;};o.prototype.addQueryData=function(p,q){"use strict";if(Object.prototype.toString.call(p)==='[object Object]'){i(this.$URIBase6,p);}else this.$URIBase6[p]=q;return this;};o.prototype.setQueryData=function(p){"use strict";this.$URIBase6=p;return this;};o.prototype.getQueryData=function(){"use strict";return this.$URIBase6;};o.prototype.removeQueryData=function(p){"use strict";if(!ES('Array','isArray',false,p))p=[p];for(var q=0,r=p.length;q0||this.getFragment());};o.prototype.toString=function(){"use strict";var p='';if(this.$URIBase1)p+=this.$URIBase1+'://';if(this.$URIBase2)p+=this.$URIBase2;if(this.$URIBase3)p+=':'+this.$URIBase3;if(this.$URIBase4){p+=this.$URIBase4;}else if(p)p+='/';var q=this.$URIBase0.serialize(this.$URIBase6);if(q)p+='?'+q;if(this.$URIBase5){p+='#'+this.$URIBase5;}else if(this.$URIBase7)p+='#';return p;};o.prototype.getOrigin=function(){"use strict";return this.$URIBase1+'://'+this.$URIBase2+(this.$URIBase3?':'+this.$URIBase3:'');};o.isValidURI=function(p,q){return n(new o(null,q),p,false,q);};e.exports=o;},null); + __d("sdk.URI",["Assert","QueryString","URIBase"],function(a,b,c,d,e,f,g,h,i){var j=/\.facebook\.com$/,k={serialize:function(o){return o?h.encode(o):'';},deserialize:function(o){return o?h.decode(o):{};}};for(var l in i)if(i.hasOwnProperty(l))n[l]=i[l];var m=i===null?null:i.prototype;n.prototype=ES('Object','create',false,m);n.prototype.constructor=n;n.__superConstructor__=i;function n(o){"use strict";g.isString(o,'The passed argument was of invalid type.');if(!(this instanceof n))return new n(o);i.call(this,o,k);}n.prototype.isFacebookURI=function(){"use strict";return j.test(this.getDomain());};n.prototype.valueOf=function(){"use strict";return this.toString();};e.exports=n;},null); + __d("sdk.Event",[],function(a,b,c,d,e,f){var g={SUBSCRIBE:'event.subscribe',UNSUBSCRIBE:'event.unsubscribe',subscribers:function(){if(!this._subscribersMap)this._subscribersMap={};return this._subscribersMap;},subscribe:function(h,i){var j=this.subscribers();if(!j[h]){j[h]=[i];}else if(ES(j[h],'indexOf',true,i)==-1)j[h].push(i);if(h!=this.SUBSCRIBE&&h!=this.UNSUBSCRIBE)this.fire(this.SUBSCRIBE,h,j[h]);},unsubscribe:function(h,i){var j=this.subscribers()[h];if(j)ES(j,'forEach',true,function(k,l){if(k==i)j.splice(l,1);});if(h!=this.SUBSCRIBE&&h!=this.UNSUBSCRIBE)this.fire(this.UNSUBSCRIBE,h,j);},monitor:function(h,i){if(!i()){var j=this,k=function(){if(i.apply(i,arguments))j.unsubscribe(h,k);};this.subscribe(h,k);}},clear:function(h){delete this.subscribers()[h];},fire:function(h){var i=Array.prototype.slice.call(arguments,1),j=this.subscribers()[h];if(j)ES(j,'forEach',true,function(k){if(k)k.apply(this,i);});}};e.exports=g;},null); + __d("Queue",["copyProperties"],function(a,b,c,d,e,f,g){var h={};function i(j){"use strict";this._opts=g({interval:0,processor:null},j);this._queue=[];this._stopped=true;}i.prototype._dispatch=function(j){"use strict";if(this._stopped||this._queue.length===0)return;if(!this._opts.processor){this._stopped=true;throw new Error('No processor available');}if(this._opts.interval){this._opts.processor.call(this,this._queue.shift());this._timeout=setTimeout(ES(this._dispatch,'bind',true,this),this._opts.interval);}else while(this._queue.length)this._opts.processor.call(this,this._queue.shift());};i.prototype.enqueue=function(j){"use strict";if(this._opts.processor&&!this._stopped){this._opts.processor.call(this,j);}else this._queue.push(j);return this;};i.prototype.start=function(j){"use strict";if(j)this._opts.processor=j;this._stopped=false;this._dispatch();return this;};i.prototype.isStarted=function(){"use strict";return !this._stopped;};i.prototype.dispatch=function(){"use strict";this._dispatch(true);};i.prototype.stop=function(j){"use strict";this._stopped=true;if(j)clearTimeout(this._timeout);return this;};i.prototype.merge=function(j,k){"use strict";this._queue[k?'unshift':'push'].apply(this._queue,j._queue);j._queue=[];this._dispatch();return this;};i.prototype.getLength=function(){"use strict";return this._queue.length;};i.get=function(j,k){"use strict";var l;if(j in h){l=h[j];}else l=h[j]=new i(k);return l;};i.exists=function(j){"use strict";return j in h;};i.remove=function(j){"use strict";return delete h[j];};e.exports=i;},null); + __d("JSONRPC",["Log"],function(a,b,c,d,e,f,g){function h(i){"use strict";this.$JSONRPC0=0;this.$JSONRPC1={};this.remote=ES(function(j){this.$JSONRPC2=j;return this.remote;},'bind',true,this);this.local={};this.$JSONRPC3=i;}h.prototype.stub=function(i){"use strict";this.remote[i]=ES(function(){var j=Array.prototype.slice.call(arguments,0),k={jsonrpc:'2.0',method:i};if(typeof j[j.length-1]=='function'){k.id=++this.$JSONRPC0;this.$JSONRPC1[k.id]=j.pop();}k.params=j;this.$JSONRPC3(ES('JSON','stringify',false,k),this.$JSONRPC2||{method:i});},'bind',true,this);};h.prototype.read=function(i,j){"use strict";var k=ES('JSON','parse',false,i),l=k.id;if(!k.method){if(!this.$JSONRPC1[l]){g.warn('Could not find callback %s',l);return;}var m=this.$JSONRPC1[l];delete this.$JSONRPC1[l];delete k.id;delete k.jsonrpc;m(k);return;}var n=this,o=this.local[k.method],p;if(l){p=function(s,t){var u={jsonrpc:'2.0',id:l};u[s]=t;setTimeout(function(){n.$JSONRPC3(ES('JSON','stringify',false,u),j);},0);};}else p=function(){};if(!o){g.error('Method "%s" has not been defined',k.method);p('error',{code:-32601,message:'Method not found',data:k.method});return;}k.params.push(ES(p,'bind',true,null,'result'));k.params.push(ES(p,'bind',true,null,'error'));try{var r=o.apply(j||null,k.params);if(typeof r!=='undefined')p('result',r);}catch(q){g.error('Invokation of RPC method %s resulted in the error: %s',k.method,q.message);p('error',{code:-32603,message:'Internal error',data:q.message});}};e.exports=h;},null); + __d("sdk.RPC",["Assert","JSONRPC","Queue"],function(a,b,c,d,e,f,g,h,i){var j=new i(),k=new h(function(m){j.enqueue(m);}),l={local:k.local,remote:k.remote,stub:ES(k.stub,'bind',true,k),setInQueue:function(m){g.isInstanceOf(i,m);m.start(function(n){k.read(n);});},getOutQueue:function(){return j;}};e.exports=l;},null); + __d("sdk.Scribe",["QueryString","sdk.Runtime","UrlMap"],function(a,b,c,d,e,f,g,h,i){function j(l,m){if(typeof m.extra=='object')m.extra.revision=h.getRevision();(new Image()).src=g.appendToUrl(i.resolve('www',true)+'/common/scribe_endpoint.php',{c:l,m:ES('JSON','stringify',false,m)});}var k={log:j};e.exports=k;},null); + __d("emptyFunction",["copyProperties"],function(a,b,c,d,e,f,g){function h(j){return function(){return j;};}function i(){}g(i,{thatReturns:h,thatReturnsFalse:h(false),thatReturnsTrue:h(true),thatReturnsNull:h(null),thatReturnsThis:function(){return this;},thatReturnsArgument:function(j){return j;}});e.exports=i;},null); + __d("htmlSpecialChars",[],function(a,b,c,d,e,f){var g=/&/g,h=//g,j=/"/g,k=/'/g;function l(m){if(typeof m=='undefined'||m===null||!m.toString)return '';if(m===false){return '0';}else if(m===true)return '1';return m.toString().replace(g,'&').replace(j,'"').replace(k,''').replace(h,'<').replace(i,'>');}e.exports=l;},null); + __d("Flash",["DOMEventListener","DOMWrapper","QueryString","UserAgent_DEPRECATED","copyProperties","guid","htmlSpecialChars"],function(a,b,c,d,e,f,g,h,i,j,k,l,m){var n={},o,p=h.getWindow().document;function q(v){var w=p.getElementById(v);if(w)w.parentNode.removeChild(w);delete n[v];}function r(){for(var v in n)if(n.hasOwnProperty(v))q(v);}function s(v){return v.replace(/\d+/g,function(w){return '000'.substring(w.length)+w;});}function t(v){if(!o){if(j.ie()>=9)g.add(window,'unload',r);o=true;}n[v]=v;}var u={embed:function(v,w,x,y){var z=l();v=m(v).replace(/&/g,'&');x=k({allowscriptaccess:'always',flashvars:y,movie:v},x||{});if(typeof x.flashvars=='object')x.flashvars=i.encode(x.flashvars);var aa=[];for(var ba in x)if(x.hasOwnProperty(ba)&&x[ba])aa.push('');var ca=w.appendChild(p.createElement('span')),da=''+aa.join('')+'';ca.innerHTML=da;var ea=ca.firstChild;t(z);return ea;},remove:q,getVersion:function(){var v='Shockwave Flash',w='application/x-shockwave-flash',x='ShockwaveFlash.ShockwaveFlash',y;if(navigator.plugins&&typeof navigator.plugins[v]=='object'){var z=navigator.plugins[v].description;if(z&&navigator.mimeTypes&&navigator.mimeTypes[w]&&navigator.mimeTypes[w].enabledPlugin)y=z.match(/\d+/g);}if(!y)try{y=(new ActiveXObject(x)).GetVariable('$version').match(/(\d+),(\d+),(\d+),(\d+)/);y=Array.prototype.slice.call(y,1);}catch(aa){}return y;},checkMinVersion:function(v){var w=u.getVersion();if(!w)return false;return s(w.join('.'))>=s(v);},isAvailable:function(){return !!u.getVersion();}};e.exports=u;},null); + __d("XDM",["DOMEventListener","DOMWrapper","emptyFunction","Flash","GlobalCallback","guid","Log","UserAgent_DEPRECATED","wrapFunction"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o){var p={},q={transports:[]},r=h.getWindow();function s(u){var v={},w=u.length,x=q.transports;while(w--)v[u[w]]=1;w=x.length;while(w--){var y=x[w],z=p[y];if(!v[y]&&z.isAvailable())return y;}}var t={register:function(u,v){m.debug('Registering %s as XDM provider',u);q.transports.push(u);p[u]=v;},create:function(u){if(!u.whenReady&&!u.onMessage){m.error('An instance without whenReady or onMessage makes no sense');throw new Error('An instance without whenReady or '+'onMessage makes no sense');}if(!u.channel){m.warn('Missing channel name, selecting at random');u.channel=l();}if(!u.whenReady)u.whenReady=i;if(!u.onMessage)u.onMessage=i;var v=u.transport||s(u.blacklist||[]),w=p[v];if(w&&w.isAvailable()){m.debug('%s is available',v);w.init(u);return v;}}};t.register('flash',(function(){var u=false,v,w=false,x=15000,y;return {isAvailable:function(){return j.checkMinVersion('8.0.24');},init:function(z){m.debug('init flash: '+z.channel);var aa={send:function(da,ea,fa,ga){m.debug('sending to: %s (%s)',ea,ga);v.postMessage(da,ea,ga);}};if(u){z.whenReady(aa);return;}var ba=z.root.appendChild(r.document.createElement('div')),ca=k.create(function(){k.remove(ca);clearTimeout(y);m.info('xdm.swf called the callback');var da=k.create(function(ea,fa){ea=decodeURIComponent(ea);fa=decodeURIComponent(fa);m.debug('received message %s from %s',ea,fa);z.onMessage(ea,fa);},'xdm.swf:onMessage');v.init(z.channel,da);z.whenReady(aa);},'xdm.swf:load');v=j.embed(z.flashUrl,ba,null,{protocol:location.protocol.replace(':',''),host:location.host,callback:ca,log:w});y=setTimeout(function(){m.warn('The Flash component did not load within %s ms - '+'verify that the container is not set to hidden or invisible '+'using CSS as this will cause some browsers to not load '+'the components',x);},x);u=true;}};})());t.register('postmessage',(function(){var u=false;return {isAvailable:function(){return !!r.postMessage;},init:function(v){m.debug('init postMessage: '+v.channel);var w='_FB_'+v.channel,x={send:function(y,z,aa,ba){if(r===aa){m.error('Invalid windowref, equal to window (self)');throw new Error();}m.debug('sending to: %s (%s)',z,ba);var ca=function(){aa.postMessage('_FB_'+ba+y,z);};if(n.ie()==8||n.ieCompatibilityMode()){setTimeout(ca,0);}else ca();}};if(u){v.whenReady(x);return;}g.add(r,'message',o(function(event){var y=event.data,z=event.origin||'native';if(!/^(https?:\/\/|native$)/.test(z)){m.debug('Received message from invalid origin type: %s',z);return;}if(typeof y!='string'){m.warn('Received message of type %s from %s, expected a string',typeof y,z);return;}m.debug('received message %s from %s',y,z);if(y.substring(0,w.length)==w)y=y.substring(w.length);v.onMessage(y,z);},'entry','onMessage'));v.whenReady(x);u=true;}};})());e.exports=t;},null); + __d("isFacebookURI",[],function(a,b,c,d,e,f){var g=null,h=['http','https'];function i(j){if(!g)g=new RegExp('(^|\\.)facebook\\.com$','i');if(j.isEmpty())return false;if(!j.getDomain()&&!j.getProtocol())return true;return (ES(h,'indexOf',true,j.getProtocol())!==-1&&g.test(j.getDomain()));}e.exports=i;},null); + __d("sdk.XD",["sdk.Content","sdk.Event","Log","QueryString","Queue","sdk.RPC","sdk.Runtime","sdk.Scribe","sdk.URI","UrlMap","JSSDKXDConfig","XDM","isFacebookURI","sdk.createIframe","sdk.feature","guid"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v){var w=new k(),x=new k(),y=new k(),z,aa,ba=v(),ca=q.useCdn?'cdn':'www',da=u('use_bundle')?q.XdBundleUrl:q.XdUrl,ea=p.resolve(ca,false)+da,fa=p.resolve(ca,true)+da,ga=v(),ha=location.protocol+'//'+location.host,ia,ja=false,ka='Facebook Cross Domain Communication Frame',la={},ma=new k();l.setInQueue(ma);function na(ta){i.info('Remote XD can talk to facebook.com (%s)',ta);m.setEnvironment(ta==='canvas'?m.ENVIRONMENTS.CANVAS:m.ENVIRONMENTS.PAGETAB);}function oa(ta,ua){if(!ua){i.error('No senderOrigin');throw new Error();}var va=/^https?/.exec(ua)[0];switch(ta.xd_action){case 'proxy_ready':var wa,xa;if(va=='https'){wa=y;xa=aa;}else{wa=x;xa=z;}if(ta.registered){na(ta.registered);w=wa.merge(w);}i.info('Proxy ready, starting queue %s containing %s messages',va+'ProxyQueue',wa.getLength());wa.start(function(za){ia.send(typeof za==='string'?za:j.encode(za),ua,xa.contentWindow,ga+'_'+va);});break;case 'plugin_ready':i.info('Plugin %s ready, protocol: %s',ta.name,va);la[ta.name]={protocol:va};if(k.exists(ta.name)){var ya=k.get(ta.name);i.debug('Enqueuing %s messages for %s in %s',ya.getLength(),ta.name,va+'ProxyQueue');(va=='https'?y:x).merge(ya);}break;}if(ta.data)pa(ta.data,ua);}function pa(ta,ua){if(ua&&ua!=='native'&&!s(o(ua)))return;if(typeof ta=='string'){if(/^FB_RPC:/.test(ta)){ma.enqueue(ta.substring(7));return;}if(ta.substring(0,1)=='{'){try{ta=ES('JSON','parse',false,ta);}catch(va){i.warn('Failed to decode %s as JSON',ta);return;}}else ta=j.decode(ta);}if(!ua)if(ta.xd_sig==ba)ua=ta.xd_origin;if(ta.xd_action){oa(ta,ua);return;}if(ta.access_token)m.setSecure(/^https/.test(ha));if(ta.cb){var wa=sa._callbacks[ta.cb];if(!sa._forever[ta.cb])delete sa._callbacks[ta.cb];if(wa)wa(ta);}}function qa(ta,ua){if(ta=='facebook'){ua.relation='parent.parent';w.enqueue(ua);}else{ua.relation='parent.frames["'+ta+'"]';var va=la[ta];if(va){i.debug('Enqueuing message for plugin %s in %s',ta,va.protocol+'ProxyQueue');(va.protocol=='https'?y:x).enqueue(ua);}else{i.debug('Buffering message for plugin %s',ta);k.get(ta).enqueue(ua);}}}l.getOutQueue().start(function(ta){qa('facebook','FB_RPC:'+ta);});function ra(ta){if(ja)return;var ua=g.appendHidden(document.createElement('div')),va=r.create({blacklist:null,root:ua,channel:ga,flashUrl:q.Flash.path,whenReady:function(wa){ia=wa;var xa={channel:ga,origin:location.protocol+'//'+location.host,transport:va,xd_name:ta},ya='#'+j.encode(xa);if(m.getSecure()!==true)z=t({url:ea+ya,name:'fb_xdm_frame_http',id:'fb_xdm_frame_http',root:ua,'aria-hidden':true,title:ka,tabindex:-1});aa=t({url:fa+ya,name:'fb_xdm_frame_https',id:'fb_xdm_frame_https',root:ua,'aria-hidden':true,title:ka,tabindex:-1});},onMessage:pa});if(!va)n.log('jssdk_error',{appId:m.getClientID(),error:'XD_TRANSPORT',extra:{message:'Failed to create a valid transport'}});ja=true;}var sa={rpc:l,_callbacks:{},_forever:{},_channel:ga,_origin:ha,onMessage:pa,recv:pa,init:ra,sendToFacebook:qa,inform:function(ta,ua,va,wa){qa('facebook',{method:ta,params:ES('JSON','stringify',false,ua||{}),behavior:wa||'p',relation:va});},handler:function(ta,ua,va,wa){var xa='#'+j.encode({cb:this.registerCallback(ta,va,wa),origin:ha+'/'+ga,domain:location.hostname,relation:ua||'opener'});return (location.protocol=='https:'?fa:ea)+xa;},registerCallback:function(ta,ua,va){va=va||v();if(ua)sa._forever[va]=true;sa._callbacks[va]=ta;return va;}};h.subscribe('init:post',function(ta){ra(ta.xdProxyName);var ua=u('xd_timeout');if(ua)setTimeout(function(){var va=aa&&(!!z==x.isStarted()&&!!aa==y.isStarted());if(!va)n.log('jssdk_error',{appId:m.getClientID(),error:'XD_INITIALIZATION',extra:{message:'Failed to initialize in '+ua+'ms'}});},ua);});e.exports=sa;},null); + __d("sdk.Auth",["sdk.Cookie","sdk.createIframe","DOMWrapper","sdk.feature","sdk.getContextType","guid","sdk.Impressions","Log","ObservableMixin","sdk.Runtime","sdk.SignedRequest","UrlMap","sdk.URI","sdk.XD"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t){var u,v,w=new o();function x(da,ea){var fa=p.getUserID(),ga='';if(da)if(da.userID){ga=da.userID;}else if(da.signedRequest){var ha=q.parse(da.signedRequest);if(ha&&ha.user_id)ga=ha.user_id;}var ia=p.getLoginStatus(),ja=(ia==='unknown'&&da)||(p.getUseCookie()&&p.getCookieUserID()!==ga),ka=fa&&!da,la=da&&fa&&fa!=ga,ma=da!=u,na=ea!=(ia||'unknown');p.setLoginStatus(ea);p.setAccessToken(da&&da.accessToken||null);p.setUserID(ga);u=da;var oa={authResponse:da,status:ea};if(ka||la)w.inform('logout',oa);if(ja||la)w.inform('login',oa);if(ma)w.inform('authresponse.change',oa);if(na)w.inform('status.change',oa);return oa;}function y(){return u;}function z(da,ea,fa){return function(ga){var ha;if(ga&&ga.access_token){var ia=q.parse(ga.signed_request);ea={accessToken:ga.access_token,userID:ia.user_id,expiresIn:parseInt(ga.expires_in,10),signedRequest:ga.signed_request};if(ga.granted_scopes)ea.grantedScopes=ga.granted_scopes;if(p.getUseCookie()){var ja=ea.expiresIn===0?0:ES('Date','now',false)+ea.expiresIn*1000,ka=g.getDomain();if(!ka&&ga.base_domain)g.setDomain('.'+ga.base_domain);g.setSignedRequestCookie(ga.signed_request,ja);}ha='connected';x(ea,ha);}else if(fa==='logout'||fa==='login_status'){if(ga.error&&ga.error==='not_authorized'){ha='not_authorized';}else ha='unknown';x(null,ha);if(p.getUseCookie())g.clearSignedRequestCookie();}if(ga&&ga.https==1)p.setSecure(true);if(da)da({authResponse:ea,status:p.getLoginStatus()});return ea;};}function aa(da){var ea,fa=ES('Date','now',false);if(v){clearTimeout(v);v=null;}var ga=z(da,u,'login_status'),ha=s(r.resolve('www',true)+'/connect/ping').setQueryData({client_id:p.getClientID(),response_type:'token,signed_request,code',domain:location.hostname,origin:k(),redirect_uri:t.handler(function(ia){if(j('e2e_ping_tracking',true)){var ja={init:fa,close:ES('Date','now',false),method:'ping'};n.debug('e2e: %s',ES('JSON','stringify',false,ja));m.log(114,{payload:ja});}ea.parentNode.removeChild(ea);if(ga(ia))v=setTimeout(function(){aa(function(){});},1200000);},'parent'),sdk:'joey',kid_directed_site:p.getKidDirectedSite()});ea=h({root:i.getRoot(),name:l(),url:ha.toString(),style:{display:'none'}});}var ba;function ca(da,ea){if(!p.getClientID()){n.warn('FB.getLoginStatus() called before calling FB.init().');return;}if(da)if(!ea&&ba=='loaded'){da({status:p.getLoginStatus(),authResponse:y()});return;}else w.subscribe('FB.loginStatus',da);if(!ea&&ba=='loading')return;ba='loading';var fa=function(ga){ba='loaded';w.inform('FB.loginStatus',ga);w.clearSubscribers('FB.loginStatus');};aa(fa);}ES('Object','assign',false,w,{getLoginStatus:ca,fetchLoginStatus:aa,setAuthResponse:x,getAuthResponse:y,parseSignedRequest:q.parse,xdResponseWrapper:z});e.exports=w;},null); + __d("toArray",["invariant"],function(a,b,c,d,e,f,g){function h(i){var j=i.length;g(!ES('Array','isArray',false,i)&&(typeof i==='object'||typeof i==='function'));g(typeof j==='number');g(j===0||(j-1) in i);if(i.hasOwnProperty)try{return Array.prototype.slice.call(i);}catch(k){}var l=Array(j);for(var m=0;m=0;}function q(z,aa){g.isTruthy(z,'element not specified');g.isString(aa);if(!p(z,aa))z.className=n(z,'className')+' '+aa;}function r(z,aa){g.isTruthy(z,'element not specified');g.isString(aa);var ba=new RegExp('\\s*'+aa,'g');z.className=ES(n(z,'className').replace(ba,''),'trim',true);}function s(z,aa,ba){g.isString(z);aa=aa||document.body;ba=ba||'*';if(aa.querySelectorAll)return h(aa.querySelectorAll(ba+'.'+z));var ca=aa.getElementsByTagName(ba),da=[];for(var ea=0,fa=ca.length;ea2000){h.remove(n.callback);return false;}p.onerror=function(){q({error:{type:'http',message:'unknown error'}});};var r=function(){setTimeout(function(){q({error:{type:'http',message:'unknown error'}});},0);};if(p.addEventListener){p.addEventListener('load',r,false);}else p.onreadystatechange=function(){if(/loaded|complete/.test(this.readyState))r();};p.src=l;g.getRoot().appendChild(p);return true;}var k={execute:j};e.exports=k;},null); + __d("ApiClient",["ArgumentError","Assert","CORSRequest","FlashRequest","flattenObject","JSONPRequest","Log","ObservableMixin","sprintf","sdk.URI","UrlMap","ApiClientConfig","invariant"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s){var t,u,v,w={get:true,post:true,'delete':true,put:true},x={fql_query:true,fql_multiquery:true,friends_get:true,notifications_get:true,stream_get:true,users_getinfo:true},y=[],z=[],aa=null,ba=50,ca=105440539523;function da(la,ma,na,oa){if(v)na=ES('Object','assign',false,{},v,na);na.access_token=na.access_token||t;na.pretty=na.pretty||0;na=k(na);var pa={jsonp:l,cors:i,flash:j},qa;if(na.transport){qa=[na.transport];delete na.transport;}else qa=['jsonp','cors','flash'];for(var ra=0;ra0);var la=y,ma=z;y=[];z=[];aa=null;ga('/','POST',{batch:ES('JSON','stringify',false,la),include_headers:false,batch_app_id:u||ca},function(na){if(ES('Array','isArray',false,na)){ES(na,'forEach',true,function(oa,pa){ma[pa](ES('JSON','parse',false,oa.body));});}else ES(ma,'forEach',true,function(oa){return oa({error:{message:'Fatal: batch call failed.'}});});});}function ja(la,ma){h.isObject(la);h.isString(la.method,'method missing');if(!ma)m.warn('No callback passed to the ApiClient');var na=la.method.toLowerCase().replace('.','_');la.format='json-strings';la.api_key=u;var oa=na in x?'api_read':'api',pa=q.resolve(oa)+'/restserver.php',qa=ES(ea,'bind',true,null,ma,'/restserver.php','get',la);da(pa,'get',la,qa);}var ka=ES('Object','assign',false,new n(),{setAccessToken:function(la){t=la;},setClientID:function(la){u=la;},setDefaultParams:function(la){v=la;},rest:ja,graph:ga,scheduleBatchCall:ha});j.setSwfUrl(r.FlashRequest.swfUrl);e.exports=ka;},null); + __d("sdk.PlatformVersioning",["sdk.Runtime","ManagedError"],function(a,b,c,d,e,f,g,h){var i=/^v\d+\.\d\d?$/,j={REGEX:i,assertVersionIsSet:function(){if(!g.getVersion())throw new h('init not called with valid version');},assertValidVersion:function(k){if(!i.test(k))throw new h('invalid version specified');}};e.exports=j;},null); + __d("sdk.api",["ApiClient","sdk.PlatformVersioning","sdk.Runtime","sdk.URI"],function(a,b,c,d,e,f,g,h,i,j){var k;i.subscribe('ClientID.change',function(m){g.setClientID(m);});i.subscribe('AccessToken.change',function(m){k=m;g.setAccessToken(m);});g.setDefaultParams({sdk:'joey'});g.subscribe('request.complete',function(m,n,o,p){var q=false;if(p&&typeof p=='object')if(p.error){if(p.error=='invalid_token'||(p.error.type=='OAuthException'&&p.error.code==190))q=true;}else if(p.error_code)if(p.error_code=='190')q=true;if(q&&k===i.getAccessToken())i.setAccessToken(null);});g.subscribe('request.complete',function(m,n,o,p){if(((m=='/me/permissions'&&n==='delete')||(m=='/restserver.php'&&o.method=='Auth.revokeAuthorization'))&&p===true)i.setAccessToken(null);});function l(m){if(typeof m==='string'){if(i.getIsVersioned()){h.assertVersionIsSet();if(!/https?/.test(m)&&m.charAt(0)!=='/')m='/'+m;m=j(m).setDomain(null).setProtocol(null).toString();if(!h.REGEX.test(m.substring(1,ES(m,'indexOf',true,'/',1))))m='/'+i.getVersion()+m;var n=[m].concat(Array.prototype.slice.call(arguments,1));g.graph.apply(g,n);}else g.graph.apply(g,arguments);}else g.rest.apply(g,arguments);}e.exports=l;},null); + __d("legacy:fb.api",["FB","sdk.api"],function(a,b,c,d,e,f,g,h){g.provide('',{api:h});},3); + __d("merge",[],function(a,b,c,d,e,f){"use strict";var g=function(h,i){return ES('Object','assign',false,{},h,i);};e.exports=g;},null); + __d("sdk.AppEvents",["Assert","sdk.Impressions","merge","sdk.Runtime"],function(a,b,c,d,e,f,g,h,i,j){var k={COMPLETED_REGISTRATION:'fb_mobile_complete_registration',VIEWED_CONTENT:'fb_mobile_content_view',SEARCHED:'fb_mobile_search',RATED:'fb_mobile_rate',COMPLETED_TUTORIAL:'fb_mobile_tutorial_completion',ADDED_TO_CART:'fb_mobile_add_to_cart',ADDED_TO_WISHLIST:'fb_mobile_add_to_wishlist',INITIATED_CHECKOUT:'fb_mobile_initiated_checkout',ADDED_PAYMENT_INFO:'fb_mobile_add_payment_info',ACHIEVED_LEVEL:'fb_mobile_level_achieved',UNLOCKED_ACHIEVEMENT:'fb_mobile_achievement_unlocked',SPENT_CREDITS:'fb_mobile_spent_credits'},l={ACTIVATED_APP:'fb_mobile_activate_app',PURCHASED:'fb_mobile_purchase'},m={CURRENCY:'fb_currency',REGISTRATION_METHOD:'fb_registration_method',CONTENT_TYPE:'fb_content_type',CONTENT_ID:'fb_content_id',SEARCH_STRING:'fb_search_string',SUCCESS:'fb_success',MAX_RATING_VALUE:'fb_max_rating_value',PAYMENT_INFO_AVAILABLE:'fb_payment_info_available',NUM_ITEMS:'fb_num_items',LEVEL:'fb_level',DESCRIPTION:'fb_description'},n=40,o='^[0-9a-zA-Z_]+[0-9a-zA-Z _-]*$';function p(t,u,v,w){g.isTrue(q(u),'Invalid event name: '+u+'. '+'It must be between 1 and '+n+' characters, '+'and must be contain only alphanumerics, _, - or spaces, '+'starting with alphanumeric or _.');var x={ae:1,ev:u,vts:v,canvas:j.isCanvasEnvironment()?1:0};if(w)x.cd=w;h.impression({api_key:t,payload:ES('JSON','stringify',false,x)});}function q(t){if(t===null||t.length===0||t.length>n||!(new RegExp(o)).test(t))return false;return true;}function r(t,u,v,w){var x={};x[m.CURRENCY]=v;p(t,l.PURCHASED,u,i(w,x));}function s(t){p(t,l.ACTIVATED_APP);}e.exports={activateApp:s,logEvent:p,logPurchase:r,isValidEventName:q,EventNames:k,ParameterNames:m};},null); + __d("legacy:fb.appevents",["Assert","sdk.AppEvents","FB","sdk.feature","sdk.Runtime"],function(a,b,c,d,e,f,g,h,i,j,k){i.provide('AppEvents',{logEvent:function(l,m,n){g.isTrue(j('allow_non_canvas_app_events')||k.isCanvasEnvironment(),'You can only use this function in Facebook Canvas environment');g.isString(l,'Invalid eventName');g.maybeNumber(m,'Invalid valueToSum');g.maybeObject(n,'Invalid params');var o=k.getClientID();g.isTrue(o!==null&&o.length>0,'You need to call FB.init() with App ID first.');h.logEvent(o,l,m,n);},logPurchase:function(l,m,n){g.isTrue(j('allow_non_canvas_app_events')||k.isCanvasEnvironment(),'You can only use this function in Facebook Canvas environment');g.isNumber(l,'Invalid purchaseAmount');g.isString(m,'Invalid currency');g.maybeObject(n,'Invalid params');var o=k.getClientID();g.isTrue(o!==null&&o.length>0,'You need to call FB.init() with App ID first.');h.logPurchase(o,l,m,n);},activateApp:function(){g.isTrue(j('allow_non_canvas_app_events')||k.isCanvasEnvironment(),'You can only use this function in Facebook Canvas environment');var l=k.getClientID();g.isTrue(l!==null&&l.length>0,'You need to call FB.init() with App ID first.');h.activateApp(l);},EventNames:h.EventNames,ParameterNames:h.ParameterNames});},3); + __d("sdk.Canvas.Environment",["sdk.RPC"],function(a,b,c,d,e,f,g){function h(k){g.remote.getPageInfo(function(l){k(l.result);});}function i(k,l){g.remote.scrollTo({x:k||0,y:l||0});}g.stub('getPageInfo');g.stub('scrollTo');var j={getPageInfo:h,scrollTo:i};e.exports=j;},null); + __d("sdk.Intl",["Log"],function(a,b,c,d,e,f,g){var h=('['+'.!?'+'\u3002'+'\uFF01'+'\uFF1F'+'\u0964'+'\u2026'+'\u0EAF'+'\u1801'+'\u0E2F'+'\uFF0E'+']');function i(l){if(typeof l!='string')return false;return !!l.match(new RegExp(h+'['+')"'+"'"+'\u00BB'+'\u0F3B'+'\u0F3D'+'\u2019'+'\u201D'+'\u203A'+'\u3009'+'\u300B'+'\u300D'+'\u300F'+'\u3011'+'\u3015'+'\u3017'+'\u3019'+'\u301B'+'\u301E'+'\u301F'+'\uFD3F'+'\uFF07'+'\uFF09'+'\uFF3D'+'\\s'+']*$'));}function j(l,m){if(m!==undefined)if(typeof m!='object'){g.error('The second arg to FB.Intl.tx() must be an Object for '+'FB.Intl.tx('+l+', ...)');}else{var n;for(var o in m)if(m.hasOwnProperty(o)){if(i(m[o])){n=new RegExp('\\{'+o+'\\}'+h+'*','g');}else n=new RegExp('\\{'+o+'\\}','g');l=l.replace(n,m[o]);}}return l;}function k(){throw new Error('Placeholder function');}k._=j;e.exports={tx:k};},null); + __d("sdk.Dialog",["sdk.Canvas.Environment","sdk.Content","sdk.DOM","DOMEventListener","sdk.Intl","ObservableMixin","sdk.Runtime","Type","UserAgent_DEPRECATED","sdk.feature"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p){var q=590,r=500,s=240,t=575,u=function(){var y;if(p('dialog_resize_refactor')){var z=v();y=z&&(z.height>=q||z.width>=r);}else y=!!o.ipad();u=function(){return y;};return y;};function v(){if(p('dialog_resize_refactor')){var y=i.getViewportInfo();if(y.height&&y.width)return {width:Math.min(y.width,q),height:Math.min(y.height,r)};}return null;}var w=n.extend({constructor:function y(z,aa){this.parent();this.id=z;this.display=aa;this._e2e={};if(!x._dialogs){x._dialogs={};x._addOrientationHandler();}x._dialogs[z]=this;this.trackEvent('init');},trackEvent:function(y,z){if(this._e2e[y])return this;this._e2e[y]=z||ES('Date','now',false);if(y=='close')this.inform('e2e:end',this._e2e);return this;},trackEvents:function(y){if(typeof y==='string')y=ES('JSON','parse',false,y);for(var z in y)if(y.hasOwnProperty(z))this.trackEvent(z,y[z]);return this;}},l),x={newInstance:function(y,z){return new w(y,z);},_dialogs:null,_lastYOffset:0,_loaderEl:null,_overlayEl:null,_stack:[],_active:null,get:function(y){return x._dialogs[y];},_findRoot:function(y){while(y){if(i.containsCss(y,'fb_dialog'))return y;y=y.parentNode;}},_createWWWLoader:function(y){y=y?y:460;return x.create({content:('
'+' '+'
'+'
'+' Facebook'+'
'+'
'+'
'+''),width:y});},_createMobileLoader:function(){var y=o.nativeApp()?'':(''+' '+' '+' '+' '+' '+' '+' '+'
'+' '+' '+'
'+k.tx._("\u52a0\u8f7d\u4e2d...")+'
'+'
'+'
');return x.create({classes:'loading'+(u()?' centered':''),content:('
'+y+'
')});},_restoreBodyPosition:function(){if(!u()){var y=document.getElementsByTagName('body')[0];i.removeCss(y,'fb_hidden');}},_showTabletOverlay:function(){if(!u())return;if(!x._overlayEl){x._overlayEl=document.createElement('div');x._overlayEl.setAttribute('id','fb_dialog_ipad_overlay');h.append(x._overlayEl,null);}x._overlayEl.className='';},_hideTabletOverlay:function(){if(u())x._overlayEl.className='hidden';},showLoader:function(y,z){x._showTabletOverlay();if(!x._loaderEl)x._loaderEl=x._findRoot(o.mobile()?x._createMobileLoader():x._createWWWLoader(z));if(!y)y=function(){};var aa=document.getElementById('fb_dialog_loader_close');i.removeCss(aa,'fb_hidden');aa.onclick=function(){x._hideLoader();x._restoreBodyPosition();x._hideTabletOverlay();y();};var ba=document.getElementById('fb_dialog_ipad_overlay');if(ba)ba.ontouchstart=aa.onclick;x._makeActive(x._loaderEl);},_hideLoader:function(){if(x._loaderEl&&x._loaderEl==x._active)x._loaderEl.style.top='-10000px';},_makeActive:function(y){x._setDialogSizes();x._lowerActive();x._active=y;if(m.isEnvironment(m.ENVIRONMENTS.CANVAS))g.getPageInfo(function(z){x._centerActive(z);});x._centerActive();},_lowerActive:function(){if(!x._active)return;x._active.style.top='-10000px';x._active=null;},_removeStacked:function(y){x._stack=ES(x._stack,'filter',true,function(z){return z!=y;});},_centerActive:function(y){var z=x._active;if(!z)return;var aa=i.getViewportInfo(),ba=parseInt(z.offsetWidth,10),ca=parseInt(z.offsetHeight,10),da=aa.scrollLeft+(aa.width-ba)/2,ea=(aa.height-ca)/2.5;if(dafa)ga=fa;ga+=aa.scrollTop;if(o.mobile()){var ha=100;if(u()){ha+=(aa.height-ca)/2;}else{var ia=document.getElementsByTagName('body')[0];i.addCss(ia,'fb_hidden');if(p('dialog_resize_refactor'))ia.style.width='auto';ga=10000;}var ja=i.getByClass('fb_dialog_padding',z);if(ja.length)ja[0].style.height=ha+'px';}z.style.left=(da>0?da:0)+'px';z.style.top=(ga>0?ga:0)+'px';},_setDialogSizes:function(){if(!o.mobile()||u())return;for(var y in x._dialogs)if(x._dialogs.hasOwnProperty(y)){var z=document.getElementById(y);if(z){z.style.width=x.getDefaultSize().width+'px';z.style.height=x.getDefaultSize().height+'px';}}},getDefaultSize:function(){if(o.mobile()){var y=v();if(y)return y;if(o.ipad())return {width:r,height:q};if(o.android()){return {width:screen.availWidth,height:screen.availHeight};}else{var z=window.innerWidth,aa=window.innerHeight,ba=z/aa>1.2;return {width:z,height:Math.max(aa,(ba?screen.width:screen.height))};}}return {width:t,height:s};},_handleOrientationChange:function(y){var z=p('dialog_resize_refactor',false)?i.getViewportInfo().width:screen.availWidth;if(o.android()&&z==x._availScreenWidth){setTimeout(x._handleOrientationChange,50);return;}x._availScreenWidth=z;if(u()){x._centerActive();}else{var aa=x.getDefaultSize().width;for(var ba in x._dialogs)if(x._dialogs.hasOwnProperty(ba)){var ca=document.getElementById(ba);if(ca)ca.style.width=aa+'px';}}},_addOrientationHandler:function(){if(!o.mobile())return;var y="onorientationchange" in window?'orientationchange':'resize';x._availScreenWidth=p('dialog_resize_refactor',false)?i.getViewportInfo().width:screen.availWidth;j.add(window,y,x._handleOrientationChange);},create:function(y){y=y||{};var z=document.createElement('div'),aa=document.createElement('div'),ba='fb_dialog';if(y.closeIcon&&y.onClose){var ca=document.createElement('a');ca.className='fb_dialog_close_icon';ca.onclick=y.onClose;z.appendChild(ca);}ba+=' '+(y.classes||'');if(o.ie()){ba+=' fb_dialog_legacy';ES(['vert_left','vert_right','horiz_top','horiz_bottom','top_left','top_right','bottom_left','bottom_right'],'forEach',true,function(fa){var ga=document.createElement('span');ga.className='fb_dialog_'+fa;z.appendChild(ga);});}else ba+=o.mobile()?' fb_dialog_mobile':' fb_dialog_advanced';if(y.content)h.append(y.content,aa);z.className=ba;var da=parseInt(y.width,10);if(!isNaN(da))z.style.width=da+'px';aa.className='fb_dialog_content';z.appendChild(aa);if(o.mobile()){var ea=document.createElement('div');ea.className='fb_dialog_padding';z.appendChild(ea);}h.append(z);if(y.visible)x.show(z);return aa;},show:function(y){var z=x._findRoot(y);if(z){x._removeStacked(z);x._hideLoader();x._makeActive(z);x._stack.push(z);if('fbCallID' in y)x.get(y.fbCallID).inform('iframe_show').trackEvent('show');}},hide:function(y){var z=x._findRoot(y);x._hideLoader();if(z==x._active){x._lowerActive();x._restoreBodyPosition();x._hideTabletOverlay();if('fbCallID' in y)x.get(y.fbCallID).inform('iframe_hide').trackEvent('hide');}},remove:function(y){y=x._findRoot(y);if(y){var z=x._active==y;x._removeStacked(y);if(z){x._hideLoader();if(x._stack.length>0){x.show(x._stack.pop());}else{x._lowerActive();x._restoreBodyPosition();x._hideTabletOverlay();}}else if(x._active===null&&x._stack.length>0)x.show(x._stack.pop());setTimeout(function(){y.parentNode.removeChild(y);},3000);}},isActive:function(y){var z=x._findRoot(y);return z&&z===x._active;}};e.exports=x;},null); + __d("sdk.Frictionless",["sdk.Auth","sdk.api","sdk.Event","sdk.Dialog"],function(a,b,c,d,e,f,g,h,i,j){var k={_allowedRecipients:{},_useFrictionless:false,_updateRecipients:function(){k._allowedRecipients={};h('/me/apprequestformerrecipients',function(l){if(!l||l.error)return;ES(l.data,'forEach',true,function(m){k._allowedRecipients[m.recipient_id]=true;});});},init:function(){k._useFrictionless=true;g.getLoginStatus(function(l){if(l.status=='connected')k._updateRecipients();});i.subscribe('auth.login',function(l){if(l.authResponse)k._updateRecipients();});},_processRequestResponse:function(l,m){return function(n){var o=n&&n.updated_frictionless;if(k._useFrictionless&&o)k._updateRecipients();if(n){if(!m&&n.frictionless){j._hideLoader();j._restoreBodyPosition();j._hideIPadOverlay();}delete n.frictionless;delete n.updated_frictionless;}l&&l(n);};},isAllowed:function(l){if(!l)return false;if(typeof l==='number')return l in k._allowedRecipients;if(typeof l==='string')l=l.split(',');l=ES(l,'map',true,function(o){return ES(String(o),'trim',true);});var m=true,n=false;ES(l,'forEach',true,function(o){m=m&&o in k._allowedRecipients;n=true;});return m&&n;}};i.subscribe('init:post',function(l){if(l.frictionlessRequests)k.init();});e.exports=k;},null); + __d("sdk.Native",["Log","UserAgent_DEPRECATED"],function(a,b,c,d,e,f,g,h){var i='fbNativeReady',j={onready:function(k){if(!h.nativeApp()){g.error('FB.Native.onready only works when the page is rendered '+'in a WebView of the native Facebook app. Test if this is the '+'case calling FB.UA.nativeApp()');return;}if(window.__fbNative&&!this.nativeReady)ES('Object','assign',false,this,window.__fbNative);if(this.nativeReady){k();}else{var l=function(m){window.removeEventListener(i,l);this.onready(k);};window.addEventListener(i,l,false);}}};e.exports=j;},null); + __d("resolveURI",[],function(a,b,c,d,e,f){function g(h){if(!h)return window.location.href;h=h.replace(/&/g,'&').replace(/"/g,'"');var i=document.createElement('div');i.innerHTML='';return i.firstChild.href;}e.exports=g;},null); + __d("sdk.UIServer",["sdk.Auth","sdk.Content","createObjectFrom","sdk.Dialog","sdk.DOM","sdk.Event","flattenObject","sdk.Frictionless","sdk.getContextType","guid","insertIframe","Log","sdk.Native","QueryString","resolveURI","sdk.RPC","sdk.Runtime","JSSDKConfig","UrlMap","UserAgent_DEPRECATED","sdk.XD"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,aa){var ba={transform:function(ea){if(ea.params.display==='touch'&&ea.params.access_token&&window.postMessage){ea.params.channel=da._xdChannelHandler(ea.id,'parent');if(!z.nativeApp())ea.params.in_iframe=1;return ea;}else return da.genericTransform(ea);},getXdRelation:function(ea){var fa=ea.display;if(fa==='touch'&&window.postMessage&&ea.in_iframe)return 'parent';return da.getXdRelation(ea);}},ca={'stream.share':{size:{width:670,height:340},url:'sharer.php',transform:function(ea){if(!ea.params.u)ea.params.u=window.location.toString();ea.params.display='popup';return ea;}},apprequests:{transform:function(ea){ea=ba.transform(ea);ea.params.frictionless=n&&n._useFrictionless;if(ea.params.frictionless){if(n.isAllowed(ea.params.to)){ea.params.display='iframe';ea.params.in_iframe=true;ea.hideLoader=true;}ea.cb=n._processRequestResponse(ea.cb,ea.hideLoader);}ea.closeIcon=false;return ea;},getXdRelation:ba.getXdRelation},feed:ba,'permissions.oauth':{url:'dialog/oauth',size:{width:(z.mobile()?null:475),height:(z.mobile()?null:183)},transform:function(ea){if(!w.getClientID()){r.error('FB.login() called before FB.init().');return;}if(g.getAuthResponse()&&!ea.params.scope&&!ea.params.auth_type){r.error('FB.login() called when user is already connected.');ea.cb&&ea.cb({status:w.getLoginStatus(),authResponse:g.getAuthResponse()});return;}var fa=ea.cb,ga=ea.id;delete ea.cb;var ha=ES('Object','keys',false,ES('Object','assign',false,ea.params.response_type?i(ea.params.response_type.split(',')):{},{token:true,signed_request:true})).join(',');if(ea.params.display==='async'){ES('Object','assign',false,ea.params,{client_id:w.getClientID(),origin:o(),response_type:ha,domain:location.hostname});ea.cb=g.xdResponseWrapper(fa,g.getAuthResponse(),'permissions.oauth');}else ES('Object','assign',false,ea.params,{client_id:w.getClientID(),redirect_uri:u(da.xdHandler(fa,ga,'opener',g.getAuthResponse(),'permissions.oauth')),origin:o(),response_type:ha,domain:location.hostname});return ea;}},'auth.logout':{url:'logout.php',transform:function(ea){if(!w.getClientID()){r.error('FB.logout() called before calling FB.init().');}else if(!g.getAuthResponse()){r.error('FB.logout() called without an access token.');}else{ea.params.next=da.xdHandler(ea.cb,ea.id,'parent',g.getAuthResponse(),'logout');return ea;}}},'login.status':{url:'dialog/oauth',transform:function(ea){var fa=ea.cb,ga=ea.id;delete ea.cb;ES('Object','assign',false,ea.params,{client_id:w.getClientID(),redirect_uri:da.xdHandler(fa,ga,'parent',g.getAuthResponse(),'login_status'),origin:o(),response_type:'token,signed_request,code',domain:location.hostname});return ea;}}},da={Methods:ca,_loadedNodes:{},_defaultCb:{},_resultToken:'"xxRESULTTOKENxx"',genericTransform:function(ea){if(ea.params.display=='dialog'||ea.params.display=='iframe')ES('Object','assign',false,ea.params,{display:'iframe',channel:da._xdChannelHandler(ea.id,'parent.parent')},true);return ea;},checkOauthDisplay:function(ea){var fa=ea.scope||ea.perms||w.getScope();if(!fa)return ea.display;var ga=fa.split(/\s|,/g);for(var ha=0;ha2000;},getDisplayMode:function(ea,fa){if(fa.display==='hidden'||fa.display==='none')return fa.display;var ga=w.isEnvironment(w.ENVIRONMENTS.CANVAS)||w.isEnvironment(w.ENVIRONMENTS.PAGETAB);if(ga&&!fa.display)return 'async';if(z.mobile()||fa.display==='touch')return 'touch';if(!w.getAccessToken()&&(fa.display=='iframe'||fa.display=='dialog')&&!ea.loggedOutIframe){r.error('"dialog" mode can only be used when the user is connected.');return 'popup';}if(ea.connectDisplay&&!ga)return ea.connectDisplay;return fa.display||(w.getAccessToken()?'dialog':'popup');},getXdRelation:function(ea){var fa=ea.display;if(fa==='popup'||fa==='touch')return 'opener';if(fa==='dialog'||fa==='iframe'||fa==='hidden'||fa==='none')return 'parent';if(fa==='async')return 'parent.frames['+window.name+']';},popup:function(ea){var fa=typeof window.screenX!='undefined'?window.screenX:window.screenLeft,ga=typeof window.screenY!='undefined'?window.screenY:window.screenTop,ha=typeof window.outerWidth!='undefined'?window.outerWidth:document.documentElement.clientWidth,ia=typeof window.outerHeight!='undefined'?window.outerHeight:(document.documentElement.clientHeight-22),ja=z.mobile()?null:ea.size.width,ka=z.mobile()?null:ea.size.height,la=(fa<0)?window.screen.width+fa:fa,ma=parseInt(la+((ha-ja)/2),10),na=parseInt(ga+((ia-ka)/2.5),10),oa=[];if(ja!==null)oa.push('width='+ja);if(ka!==null)oa.push('height='+ka);oa.push('left='+ma);oa.push('top='+na);oa.push('scrollbars=1');if(ea.name=='permissions.request'||ea.name=='permissions.oauth')oa.push('location=1,toolbar=0');oa=oa.join(',');var pa;if(ea.post){pa=window.open('about:blank',ea.id,oa);if(pa){da.setLoadedNode(ea,pa,'popup');h.submitToTarget({url:ea.url,target:ea.id,params:ea.params});}}else{pa=window.open(ea.url,ea.id,oa);if(pa)da.setLoadedNode(ea,pa,'popup');}if(!pa)return;if(ea.id in da._defaultCb)da._popupMonitor();},setLoadedNode:function(ea,fa,ga){if(ea.params&&ea.params.display!='popup')fa.fbCallID=ea.id;fa={node:fa,type:ga,fbCallID:ea.id};da._loadedNodes[ea.id]=fa;},getLoadedNode:function(ea){var fa=typeof ea=='object'?ea.id:ea,ga=da._loadedNodes[fa];return ga?ga.node:null;},hidden:function(ea){ea.className='FB_UI_Hidden';ea.root=h.appendHidden('');da._insertIframe(ea);},iframe:function(ea){ea.className='FB_UI_Dialog';var fa=function(){da._triggerDefault(ea.id);};ea.root=j.create({onClose:fa,closeIcon:ea.closeIcon===undefined?true:ea.closeIcon,classes:(z.ipad()?'centered':'')});if(!ea.hideLoader)j.showLoader(fa,ea.size.width);k.addCss(ea.root,'fb_dialog_iframe');da._insertIframe(ea);},touch:function(ea){if(ea.params&&ea.params.in_iframe){if(ea.ui_created){j.showLoader(function(){da._triggerDefault(ea.id);},0);}else da.iframe(ea);}else if(z.nativeApp()&&!ea.ui_created){ea.frame=ea.id;s.onready(function(){da.setLoadedNode(ea,s.open(ea.url+'#cb='+ea.frameName),'native');});da._popupMonitor();}else if(!ea.ui_created)da.popup(ea);},async:function(ea){ea.params.redirect_uri=location.protocol+'//'+location.host+location.pathname;delete ea.params.access_token;v.remote.showDialog(ea.params,function(fa){var ga=fa.result;if(ga&&ga.e2e){var ha=j.get(ea.id);ha.trackEvents(ga.e2e);ha.trackEvent('close');delete ga.e2e;}ea.cb(ga);});},getDefaultSize:function(){return j.getDefaultSize();},_insertIframe:function(ea){da._loadedNodes[ea.id]=false;var fa=function(ga){if(ea.id in da._loadedNodes)da.setLoadedNode(ea,ga,'iframe');};if(ea.post){q({url:'about:blank',root:ea.root,className:ea.className,width:ea.size.width,height:ea.size.height,id:ea.id,onInsert:fa,onload:function(ga){h.submitToTarget({url:ea.url,target:ga.name,params:ea.params});}});}else q({url:ea.url,root:ea.root,className:ea.className,width:ea.size.width,height:ea.size.height,id:ea.id,name:ea.frameName,onInsert:fa});},_handleResizeMessage:function(ea,fa){var ga=da.getLoadedNode(ea);if(!ga)return;if(fa.height)ga.style.height=fa.height+'px';if(fa.width)ga.style.width=fa.width+'px';aa.inform('resize.ack',fa||{},'parent.frames['+ga.name+']');if(!j.isActive(ga))j.show(ga);},_triggerDefault:function(ea){da._xdRecv({frame:ea},da._defaultCb[ea]||function(){});},_popupMonitor:function(){var ea;for(var fa in da._loadedNodes)if(da._loadedNodes.hasOwnProperty(fa)&&fa in da._defaultCb){var ga=da._loadedNodes[fa];if(ga.type!='popup'&&ga.type!='native')continue;var ha=ga.node;try{if(ha.closed){da._triggerDefault(fa);}else ea=true;}catch(ia){}}if(ea&&!da._popupInterval){da._popupInterval=setInterval(da._popupMonitor,100);}else if(!ea&&da._popupInterval){clearInterval(da._popupInterval);da._popupInterval=null;}},_xdChannelHandler:function(ea,fa){return aa.handler(function(ga){var ha=da.getLoadedNode(ea);if(!ha)return;if(ga.type=='resize'){da._handleResizeMessage(ea,ga);}else if(ga.type=='hide'){j.hide(ha);}else if(ga.type=='rendered'){var ia=j._findRoot(ha);j.show(ia);}else if(ga.type=='fireevent')l.fire(ga.event);},fa,true,null);},_xdNextHandler:function(ea,fa,ga,ha){if(ha)da._defaultCb[fa]=ea;return aa.handler(function(ia){da._xdRecv(ia,ea);},ga)+'&frame='+fa;},_xdRecv:function(ea,fa){var ga=da.getLoadedNode(ea.frame);if(ga)if(ga.close){try{ga.close();if(/iPhone.*Version\/(5|6)/.test(navigator.userAgent)&&RegExp.$1!=='5')window.focus();da._popupCount--;}catch(ha){}}else if(k.containsCss(ga,'FB_UI_Hidden')){setTimeout(function(){ga.parentNode.parentNode.removeChild(ga.parentNode);},3000);}else if(k.containsCss(ga,'FB_UI_Dialog'))j.remove(ga);delete da._loadedNodes[ea.frame];delete da._defaultCb[ea.frame];if(ea.e2e){var ia=j.get(ea.frame);ia.trackEvents(ea.e2e);ia.trackEvent('close');delete ea.e2e;}fa(ea);},_xdResult:function(ea,fa,ga,ha){return (da._xdNextHandler(function(ia){ea&&ea(ia.result&&ia.result!=da._resultToken&&ES('JSON','parse',false,ia.result));},fa,ga,ha)+'&result='+encodeURIComponent(da._resultToken));},xdHandler:function(ea,fa,ga,ha,ia){return da._xdNextHandler(g.xdResponseWrapper(ea,ha,ia),fa,ga,true);}};v.stub('showDialog');e.exports=da;},null); + __d("sdk.ui",["Assert","sdk.Impressions","Log","sdk.PlatformVersioning","sdk.Runtime","sdk.UIServer","sdk.feature"],function(a,b,c,d,e,f,g,h,i,j,k,l,m){function n(o,p){g.isObject(o);g.maybeFunction(p);if(k.getIsVersioned()){j.assertVersionIsSet();if(o.version){j.assertValidVersion(o.version);}else o.version=k.getVersion();}o=ES('Object','assign',false,{},o);if(!o.method){i.error('"method" is a required parameter for FB.ui().');return null;}if(o.method=='pay.prompt')o.method='pay';var q=o.method;if(o.redirect_uri){i.warn('When using FB.ui, you should not specify a redirect_uri.');delete o.redirect_uri;}if((q=='permissions.request'||q=='permissions.oauth')&&(o.display=='iframe'||o.display=='dialog'))o.display=l.checkOauthDisplay(o);var r=m('e2e_tracking',true);if(r)o.e2e={};var s=l.prepareCall(o,p||function(){});if(!s)return null;var t=s.params.display;if(t==='dialog'){t='iframe';}else if(t==='none')t='hidden';var u=l[t];if(!u){i.error('"display" must be one of "popup", '+'"dialog", "iframe", "touch", "async", "hidden", or "none"');return null;}if(r)s.dialog.subscribe('e2e:end',function(v){v.method=q;v.display=t;i.debug('e2e: %s',ES('JSON','stringify',false,v));h.log(114,{payload:v});});u(s);return s.dialog;}e.exports=n;},null); + __d("legacy:fb.auth",["sdk.Auth","sdk.Cookie","copyProperties","sdk.Event","FB","Log","sdk.Runtime","sdk.SignedRequest","sdk.ui"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o){k.provide('',{getLoginStatus:function(){return g.getLoginStatus.apply(g,arguments);},getAuthResponse:function(){return g.getAuthResponse();},getAccessToken:function(){return m.getAccessToken()||null;},getUserID:function(){return m.getUserID()||m.getCookieUserID();},login:function(p,q){if(q&&q.perms&&!q.scope){q.scope=q.perms;delete q.perms;l.warn('OAuth2 specification states that \'perms\' '+'should now be called \'scope\'. Please update.');}var r=m.isEnvironment(m.ENVIRONMENTS.CANVAS)||m.isEnvironment(m.ENVIRONMENTS.PAGETAB);o(i({method:'permissions.oauth',display:r?'async':'popup',domain:location.hostname},q||{}),p);},logout:function(p){o({method:'auth.logout',display:'hidden'},p);}});g.subscribe('logout',ES(j.fire,'bind',true,j,'auth.logout'));g.subscribe('login',ES(j.fire,'bind',true,j,'auth.login'));g.subscribe('authresponse.change',ES(j.fire,'bind',true,j,'auth.authResponseChange'));g.subscribe('status.change',ES(j.fire,'bind',true,j,'auth.statusChange'));j.subscribe('init:post',function(p){if(p.status)g.getLoginStatus();if(m.getClientID())if(p.authResponse){g.setAuthResponse(p.authResponse,'connected');}else if(m.getUseCookie()){var q=h.loadSignedRequest(),r;if(q){try{r=n.parse(q);}catch(s){h.clearSignedRequestCookie();}if(r&&r.user_id)m.setCookieUserID(r.user_id);}h.loadMeta();}});},3); + __d("sdk.Canvas.IframeHandling",["DOMWrapper","sdk.RPC"],function(a,b,c,d,e,f,g,h){var i=null,j;function k(){var o=g.getWindow().document,p=o.body,q=o.documentElement,r=Math.max(p.offsetTop,0),s=Math.max(q.offsetTop,0),t=p.scrollHeight+r,u=p.offsetHeight+r,v=q.scrollHeight+s,w=q.offsetHeight+s;return Math.max(t,u,v,w);}function l(o){if(typeof o!='object')o={};var p=0,q=0;if(!o.height){o.height=k();p=16;q=4;}if(!o.frame)o.frame=window.name||'iframe_canvas';if(j){var r=j.height,s=o.height-r;if(s<=q&&s>=-p)return false;}j=o;h.remote.setSize(o);return true;}function m(o,p){if(p===undefined&&typeof o==='number'){p=o;o=true;}if(o||o===undefined){if(i===null)i=setInterval(function(){l();},p||100);l();}else if(i!==null){clearInterval(i);i=null;}}h.stub('setSize');var n={setSize:l,setAutoGrow:m};e.exports=n;},null); + __d("sdk.Canvas.Navigation",["sdk.RPC"],function(a,b,c,d,e,f,g){function h(j){g.local.navigate=function(k){j({path:k});};g.remote.setNavigationEnabled(true);}g.stub('setNavigationEnabled');var i={setUrlHandler:h};e.exports=i;},null); + __d("sdk.Canvas.Plugin",["sdk.api","sdk.RPC","Log","UserAgent_DEPRECATED","sdk.Runtime","createArrayFrom"],function(a,b,c,d,e,f,g,h,i,j,k,l){var m='CLSID:D27CDB6E-AE6D-11CF-96B8-444553540000',n='CLSID:444785F1-DE89-4295-863A-D46C3A781394',o=null,p=!(j.osx()>=10.9&&(j.chrome()>=31||j.webkit()>=537.71||j.firefox()>=25));function q(aa){aa._hideunity_savedstyle={};aa._hideunity_savedstyle.left=aa.style.left;aa._hideunity_savedstyle.position=aa.style.position;aa._hideunity_savedstyle.width=aa.style.width;aa._hideunity_savedstyle.height=aa.style.height;aa.style.left='-10000px';aa.style.position='absolute';aa.style.width='1px';aa.style.height='1px';}function r(aa){if(aa._hideunity_savedstyle){aa.style.left=aa._hideunity_savedstyle.left;aa.style.position=aa._hideunity_savedstyle.position;aa.style.width=aa._hideunity_savedstyle.width;aa.style.height=aa._hideunity_savedstyle.height;}}function s(aa){aa._old_visibility=aa.style.visibility;aa.style.visibility='hidden';}function t(aa){aa.style.visibility=aa._old_visibility||'';delete aa._old_visibility;}function u(aa){var ba=aa.type?aa.type.toLowerCase():null,ca=ba==='application/x-shockwave-flash'||(aa.classid&&aa.classid.toUpperCase()==m);if(!ca)return false;var da=/opaque|transparent/i;if(da.test(aa.getAttribute('wmode')))return false;for(var ea=0;ea1/l||m=='*'||~ES(m,'indexOf',true,j.getClientID()))return;setTimeout(p,30000);}function r(u){n=u;}function s(u){o.push(u);}var t={COLLECT_AUTOMATIC:k.AUTOMATIC,COLLECT_MANUAL:k.MANUAL,addStaticResource:s,setCollectionMode:r,_maybeSample:q};e.exports=t;},null); + __d("legacy:fb.canvas.prefetcher",["FB","sdk.Canvas.Prefetcher","sdk.Event","sdk.Runtime"],function(a,b,c,d,e,f,g,h,i,j){g.provide('Canvas.Prefetcher',h);i.subscribe('init:post',function(k){if(j.isEnvironment(j.ENVIRONMENTS.CANVAS))h._maybeSample();});},3); + __d("legacy:fb.canvas.presence",["sdk.RPC","sdk.Event"],function(a,b,c,d,e,f,g,h){h.subscribe(h.SUBSCRIBE,i);h.subscribe(h.UNSUBSCRIBE,j);g.stub('useFriendsOnline');function i(k,l){if(k!='canvas.friendsOnlineUpdated')return;if(l.length===1)g.remote.useFriendsOnline(true);}function j(k,l){if(k!='canvas.friendsOnlineUpdated')return;if(l.length===0)g.remote.useFriendsOnline(false);}},3); + __d("legacy:fb.event",["FB","sdk.Event","sdk.Runtime","sdk.Scribe","sdk.feature"],function(a,b,c,d,e,f,g,h,i,j,k){var l=[],m=null,n=k('event_subscriptions_log',false);g.provide('Event',{subscribe:function(o,p){if(n){l.push(o);if(!m)m=setTimeout(function(){j.log('jssdk_error',{appId:i.getClientID(),error:'EVENT_SUBSCRIPTIONS_LOG',extra:{line:0,name:'EVENT_SUBSCRIPTIONS_LOG',script:'N/A',stack:'N/A',message:l.sort().join(',')}});l.length=0;m=null;},n);}return h.subscribe(o,p);},unsubscribe:ES(h.unsubscribe,'bind',true,h)});},3); + __d("legacy:fb.frictionless",["FB","sdk.Frictionless"],function(a,b,c,d,e,f,g,h){g.provide('Frictionless',h);},3); + __d("sdk.init",["sdk.Cookie","sdk.ErrorHandling","sdk.Event","Log","ManagedError","sdk.PlatformVersioning","QueryString","sdk.Runtime","sdk.URI","createArrayFrom"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p){function q(s){var t=(typeof s=='number'&&s>0)||(typeof s=='string'&&/^[0-9a-f]{21,}$|^[0-9]{1,21}$/.test(s));if(t)return s.toString();j.warn('Invalid App Id: Must be a number or numeric string representing '+'the application id.');return null;}function r(s){if(n.getInitialized())j.warn('FB.init has already been called - this could indicate a problem');if(n.getIsVersioned()){if(Object.prototype.toString.call(s)!=='[object Object]')throw new k('Invalid argument');if(s.authResponse)j.warn('Setting authResponse is not supported');if(!s.version)s.version=o(location.href).getQueryData().sdk_version;l.assertValidVersion(s.version);n.setVersion(s.version);}else{if(/number|string/.test(typeof s)){j.warn('FB.init called with invalid parameters');s={apiKey:s};}s=ES('Object','assign',false,{status:true},s||{});}var t=q(s.appId||s.apiKey);if(t!==null)n.setClientID(t);if('scope' in s)n.setScope(s.scope);if(s.cookie){n.setUseCookie(true);if(typeof s.cookie==='string')g.setDomain(s.cookie);}if(s.kidDirectedSite)n.setKidDirectedSite(true);n.setInitialized(true);i.fire('init:post',s);}setTimeout(function(){var s=/(connect\.facebook\.net|\.facebook\.com\/assets.php).*?#(.*)/;ES(p(document.getElementsByTagName('script')),'forEach',true,function(t){if(t.src){var u=s.exec(t.src);if(u){var v=m.decode(u[2]);for(var w in v)if(v.hasOwnProperty(w)){var x=v[w];if(x=='0')v[w]=0;}r(v);}}});if(window.fbAsyncInit&&!window.fbAsyncInit.hasRun){window.fbAsyncInit.hasRun=true;h.unguard(window.fbAsyncInit)();}},0);e.exports=r;},null); + __d("legacy:fb.init",["FB","sdk.init"],function(a,b,c,d,e,f,g,h){g.provide('',{init:h});},3); + __d("legacy:fb.pay",["copyProperties","sdk.Runtime","sdk.UIServer","sdk.XD","sdk.feature","FB"],function(a,b,c,d,e,f,g,h,i,j,k){b('FB');var l={error_code:1383001,error_message:'An unknown error caused the dialog to be closed'},m=function(n){return function(o){if(o&&typeof o.response==='string'){n(ES('JSON','parse',false,o.response));}else if(typeof o==='object'){n(o);}else n(l);};};g(i.Methods,{pay:{size:{width:555,height:120},connectDisplay:'popup',transform:function(n){if(k('launch_payment_dialog_via_pac')){n.cb=m(n.cb);return n;}else{n.cb=m(n.cb);if(!h.isEnvironment(h.ENVIRONMENTS.CANVAS)){n.params.order_info=ES('JSON','stringify',false,n.params.order_info);return n;}var o=j.handler(n.cb,'parent.frames['+(window.name||'iframe_canvas')+']');n.params.channel=o;n.params.uiserver=true;j.inform('Pay.Prompt',n.params);}}}});},3); + __d("legacy:fb.ui",["FB","sdk.ui"],function(a,b,c,d,e,f,g,h){g.provide('',{ui:h});},3); + __d("runOnce",[],function(a,b,c,d,e,f){function g(h){var i,j;return function(){if(!i){i=true;j=h();}return j;};}e.exports=g;},null); + __d("XFBML",["Assert","createArrayFrom","sdk.DOM","sdk.feature","sdk.Impressions","Log","ObservableMixin","runOnce","UserAgent_DEPRECATED"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o){var p={},q={},r=0,s=new m();function t(ba,ca){return ba[ca]+'';}function u(ba){return ba.scopeName?(ba.scopeName+':'+ba.nodeName):'';}function v(ba){return p[t(ba,'nodeName').toLowerCase()]||p[u(ba).toLowerCase()];}function w(ba){var ca=ES(ES(t(ba,'className'),'trim',true).split(/\s+/),'filter',true,function(da){return q.hasOwnProperty(da);});if(ca.length===0)return undefined;if(ba.getAttribute('fb-xfbml-state')||!ba.childNodes||ba.childNodes.length===0||(ba.childNodes.length===1&&ba.childNodes[0].nodeType===3)||(ba.children.length===1&&t(ba.children[0],'className')==='fb-xfbml-parse-ignore'))return q[ca[0]];}function x(ba){var ca={};ES(h(ba.attributes),'forEach',true,function(da){ca[t(da,'name')]=t(da,'value');});return ca;}function y(ba,ca,da){var ea=document.createElement('div');i.addCss(ba,ca+'-'+da);ES(h(ba.childNodes),'forEach',true,function(fa){ea.appendChild(fa);});ES(h(ba.attributes),'forEach',true,function(fa){ea.setAttribute(fa.name,fa.value);});ba.parentNode.replaceChild(ea,ba);return ea;}function z(ba,ca,da){g.isTrue(ba&&ba.nodeType&&ba.nodeType===1&&!!ba.getElementsByTagName,'Invalid DOM node passed to FB.XFBML.parse()');g.isFunction(ca,'Invalid callback passed to FB.XFBML.parse()');var ea=++r;l.info('XFBML Parsing Start %s',ea);var fa=1,ga=0,ha=function(){fa--;if(fa===0){l.info('XFBML Parsing Finish %s, %s tags found',ea,ga);ca();s.inform('render',ea,ga);}g.isTrue(fa>=0,'onrender() has been called too many times');};ES(h(ba.getElementsByTagName('*')),'forEach',true,function(ja){if(!da&&ja.getAttribute('fb-xfbml-state'))return;if(ja.nodeType!==1)return;var ka=v(ja)||w(ja);if(!ka)return;if(o.ie()<9&&ja.scopeName)ja=y(ja,ka.xmlns,ka.localName);fa++;ga++;var la=new ka.ctor(ja,ka.xmlns,ka.localName,x(ja));la.subscribe('render',n(function(){ja.setAttribute('fb-xfbml-state','rendered');ha();}));var ma=function(){if(ja.getAttribute('fb-xfbml-state')=='parsed'){s.subscribe('render.queue',ma);}else{ja.setAttribute('fb-xfbml-state','parsed');la.process();}};ma();});s.inform('parse',ea,ga);var ia=30000;setTimeout(function(){if(fa>0)l.warn('%s tags failed to render in %s ms',fa,ia);},ia);ha();}s.subscribe('render',function(){var ba=s.getSubscribers('render.queue');s.clearSubscribers('render.queue');ES(ba,'forEach',true,function(ca){ca();});});ES('Object','assign',false,s,{registerTag:function(ba){var ca=ba.xmlns+':'+ba.localName;g.isUndefined(p[ca],ca+' already registered');p[ca]=ba;q[ba.xmlns+'-'+ba.localName]=ba;},parse:function(ba,ca){z(ba||document.body,ca||function(){},true);},parseNew:function(){z(document.body,function(){},false);}});if(j('log_tag_count')){var aa=function(ba,ca){s.unsubscribe('parse',aa);setTimeout(ES(k.log,'bind',true,null,102,{tag_count:ca}),5000);};s.subscribe('parse',aa);}e.exports=s;},null); + __d("PluginPipe",["sdk.Content","sdk.feature","guid","insertIframe","Miny","ObservableMixin","JSSDKPluginPipeConfig","sdk.Runtime","UrlMap","UserAgent_DEPRECATED","XFBML"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q){var r=new l(),s=m.threshold,t=[];function u(){return !!(h('plugin_pipe')&&n.getSecure()!==undefined&&(p.chrome()||p.firefox())&&m.enabledApps[n.getClientID()]);}function v(){var x=t;t=[];if(x.length<=s){ES(x,'forEach',true,function(aa){j(aa.config);});return;}var y=x.length+1;function z(){y--;if(y===0)w(x);}ES(x,'forEach',true,function(aa){var ba={};for(var ca in aa.config)ba[ca]=aa.config[ca];ba.url=o.resolve('www',n.getSecure())+'/plugins/plugin_pipe_shell.php';ba.onload=z;j(ba);});z();}q.subscribe('parse',v);function w(x){var y=document.createElement('span');g.appendHidden(y);var z={};ES(x,'forEach',true,function(ea){z[ea.config.name]={plugin:ea.tag,params:ea.params};});var aa=ES('JSON','stringify',false,z),ba=k.encode(aa);ES(x,'forEach',true,function(ea){var fa=document.getElementsByName(ea.config.name)[0];fa.onload=ea.config.onload;});var ca=o.resolve('www',n.getSecure())+'/plugins/pipe.php',da=i();j({url:'about:blank',root:y,name:da,className:'fb_hidden fb_invisible',onload:function(){g.submitToTarget({url:ca,target:da,params:{plugins:ba.length-1)?n:l;});},isValid:function(){for(var k=this.dom;k;k=k.parentNode)if(k==document.body)return true;},clear:function(){g.html(this.dom,'');}},i);e.exports=j;},null); + __d("sdk.XFBML.IframeWidget",["sdk.Arbiter","sdk.Auth","sdk.Content","sdk.DOM","sdk.Event","sdk.XFBML.Element","guid","insertIframe","QueryString","sdk.Runtime","sdk.ui","UrlMap","sdk.XD"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s){var t=l.extend({_iframeName:null,_showLoader:true,_refreshOnAuthChange:false,_allowReProcess:false,_fetchPreCachedLoader:false,_visibleAfter:'load',_widgetPipeEnabled:false,_borderReset:false,_repositioned:false,getUrlBits:function(){throw new Error('Inheriting class needs to implement getUrlBits().');},setupAndValidate:function(){return true;},oneTimeSetup:function(){},getSize:function(){},getIframeName:function(){return this._iframeName;},getIframeTitle:function(){return 'Facebook Social Plugin';},getChannelUrl:function(){if(!this._channelUrl){var x=this;this._channelUrl=s.handler(function(y){x.fire('xd.'+y.type,y);},'parent.parent',true);}return this._channelUrl;},getIframeNode:function(){return this.dom.getElementsByTagName('iframe')[0];},arbiterInform:function(event,x,y){s.sendToFacebook(this.getIframeName(),{method:event,params:ES('JSON','stringify',false,x||{}),behavior:y||g.BEHAVIOR_PERSISTENT});},_arbiterInform:function(event,x,y){var z='parent.frames["'+this.getIframeNode().name+'"]';s.inform(event,x,z,y);},getDefaultWebDomain:function(){return r.resolve('www');},process:function(x){if(this._done){if(!this._allowReProcess&&!x)return;this.clear();}else this._oneTimeSetup();this._done=true;this._iframeName=this.getIframeName()||this._iframeName||m();if(!this.setupAndValidate()){this.fire('render');return;}if(this._showLoader)this._addLoader();j.addCss(this.dom,'fb_iframe_widget');if(this._visibleAfter!='immediate'){j.addCss(this.dom,'fb_hide_iframes');}else this.subscribe('iframe.onload',ES(this.fire,'bind',true,this,'render'));var y=this.getSize()||{},z=this.getFullyQualifiedURL();if(y.width=='100%')j.addCss(this.dom,'fb_iframe_widget_fluid');this.clear();n({url:z,root:this.dom.appendChild(document.createElement('span')),name:this._iframeName,title:this.getIframeTitle(),className:p.getRtl()?'fb_rtl':'fb_ltr',height:y.height,width:y.width,onload:ES(this.fire,'bind',true,this,'iframe.onload')});this._resizeFlow(y);this.loaded=false;this.subscribe('iframe.onload',ES(function(){this.loaded=true;if(!this._isResizeHandled)j.addCss(this.dom,'fb_hide_iframes');},'bind',true,this));},generateWidgetPipeIframeName:function(){u++;return 'fb_iframe_'+u;},getFullyQualifiedURL:function(){var x=this._getURL();x+='?'+o.encode(this._getQS());if(x.length>2000){x='about:blank';var y=ES(function(){this._postRequest();this.unsubscribe('iframe.onload',y);},'bind',true,this);this.subscribe('iframe.onload',y);}return x;},_getWidgetPipeShell:function(){return r.resolve('www')+'/common/widget_pipe_shell.php';},_oneTimeSetup:function(){this.subscribe('xd.resize',ES(this._handleResizeMsg,'bind',true,this));this.subscribe('xd.resize',ES(this._bubbleResizeEvent,'bind',true,this));this.subscribe('xd.resize.iframe',ES(this._resizeIframe,'bind',true,this));this.subscribe('xd.resize.flow',ES(this._resizeFlow,'bind',true,this));this.subscribe('xd.resize.flow',ES(this._bubbleResizeEvent,'bind',true,this));this.subscribe('xd.refreshLoginStatus',function(){h.getLoginStatus(function(){},true);});this.subscribe('xd.logout',function(){q({method:'auth.logout',display:'hidden'},function(){});});if(this._refreshOnAuthChange)this._setupAuthRefresh();if(this._visibleAfter=='load')this.subscribe('iframe.onload',ES(this._makeVisible,'bind',true,this));this.subscribe('xd.verify',ES(function(x){this.arbiterInform('xd/verify',x.token);},'bind',true,this));this.oneTimeSetup();},_makeVisible:function(){this._removeLoader();j.removeCss(this.dom,'fb_hide_iframes');this.fire('render');},_setupAuthRefresh:function(){h.getLoginStatus(ES(function(x){var y=x.status;k.subscribe('auth.statusChange',ES(function(z){if(!this.isValid())return;if(y=='unknown'||z.status=='unknown')this.process(true);y=z.status;},'bind',true,this));},'bind',true,this));},_handleResizeMsg:function(x){if(!this.isValid())return;this._resizeIframe(x);this._resizeFlow(x);if(!this._borderReset){this.getIframeNode().style.border='none';this._borderReset=true;}this._isResizeHandled=true;this._makeVisible();},_bubbleResizeEvent:function(x){var y={height:x.height,width:x.width,pluginID:this.getAttribute('plugin-id')};k.fire('xfbml.resize',y);},_resizeIframe:function(x){var y=this.getIframeNode();if(x.reposition==="true")this._repositionIframe(x);x.height&&(y.style.height=x.height+'px');x.width&&(y.style.width=x.width+'px');this._updateIframeZIndex();},_resizeFlow:function(x){var y=this.dom.getElementsByTagName('span')[0];x.height&&(y.style.height=x.height+'px');x.width&&(y.style.width=x.width+'px');this._updateIframeZIndex();},_updateIframeZIndex:function(){var x=this.dom.getElementsByTagName('span')[0],y=this.getIframeNode(),z=y.style.height===x.style.height&&y.style.width===x.style.width,aa=z?'removeCss':'addCss';j[aa](y,'fb_iframe_widget_lift');},_repositionIframe:function(x){var y=this.getIframeNode(),z=parseInt(j.getStyle(y,'width'),10),aa=j.getPosition(y).x,ba=j.getViewportInfo().width,ca=parseInt(x.width,10);if(aa+ca>ba&&aa>ca){y.style.left=z-ca+'px';this.arbiterInform('xd/reposition',{type:'horizontal'});this._repositioned=true;}else if(this._repositioned){y.style.left='0px';this.arbiterInform('xd/reposition',{type:'restore'});this._repositioned=false;}},_addLoader:function(){if(!this._loaderDiv){j.addCss(this.dom,'fb_iframe_widget_loader');this._loaderDiv=document.createElement('div');this._loaderDiv.className='FB_Loader';this.dom.appendChild(this._loaderDiv);}},_removeLoader:function(){if(this._loaderDiv){j.removeCss(this.dom,'fb_iframe_widget_loader');if(this._loaderDiv.parentNode)this._loaderDiv.parentNode.removeChild(this._loaderDiv);this._loaderDiv=null;}},_getQS:function(){return ES('Object','assign',false,{api_key:p.getClientID(),locale:p.getLocale(),sdk:'joey',kid_directed_site:p.getKidDirectedSite(),ref:this.getAttribute('ref')},this.getUrlBits().params);},_getURL:function(){var x=this.getDefaultWebDomain(),y='';return x+'/plugins/'+y+this.getUrlBits().name+'.php';},_postRequest:function(){i.submitToTarget({url:this._getURL(),target:this.getIframeNode().name,params:this._getQS()});}}),u=0,v={};function w(){var x={};for(var y in v){var z=v[y];x[y]={widget:z.getUrlBits().name,params:z._getQS()};}return x;}e.exports=t;},null); + __d("sdk.XFBML.Comments",["sdk.Event","sdk.XFBML.IframeWidget","QueryString","sdk.Runtime","JSSDKConfig","UrlMap","UserAgent_DEPRECATED"],function(a,b,c,d,e,f,g,h,i,j,k,l,m){var n=h.extend({_visibleAfter:'immediate',_refreshOnAuthChange:true,setupAndValidate:function(){var o={channel_url:this.getChannelUrl(),colorscheme:this.getAttribute('colorscheme'),skin:this.getAttribute('skin'),numposts:this.getAttribute('num-posts',10),width:this._getLengthAttribute('width'),href:this.getAttribute('href'),permalink:this.getAttribute('permalink'),publish_feed:this.getAttribute('publish_feed'),order_by:this.getAttribute('order_by'),mobile:this._getBoolAttribute('mobile')};if(!o.width&&!o.permalink)o.width=550;if(k.initSitevars.enableMobileComments&&m.mobile()&&o.mobile!==false){o.mobile=true;delete o.width;}if(!o.skin)o.skin=o.colorscheme;if(!o.href){o.migrated=this.getAttribute('migrated');o.xid=this.getAttribute('xid');o.title=this.getAttribute('title',document.title);o.url=this.getAttribute('url',document.URL);o.quiet=this.getAttribute('quiet');o.reverse=this.getAttribute('reverse');o.simple=this.getAttribute('simple');o.css=this.getAttribute('css');o.notify=this.getAttribute('notify');if(!o.xid){var p=ES(document.URL,'indexOf',true,'#');if(p>0){o.xid=encodeURIComponent(document.URL.substring(0,p));}else o.xid=encodeURIComponent(document.URL);}if(o.migrated)o.href=l.resolve('www')+'/plugins/comments_v1.php?'+'app_id='+j.getClientID()+'&xid='+encodeURIComponent(o.xid)+'&url='+encodeURIComponent(o.url);}else{var q=this.getAttribute('fb_comment_id');if(!q){q=i.decode(document.URL.substring(ES(document.URL,'indexOf',true,'?')+1)).fb_comment_id;if(q&&ES(q,'indexOf',true,'#')>0)q=q.substring(0,ES(q,'indexOf',true,'#'));}if(q){o.fb_comment_id=q;this.subscribe('render',ES(function(){if(!window.location.hash)window.location.hash=this.getIframeNode().id;},'bind',true,this));}}this._attr=o;return true;},oneTimeSetup:function(){this.subscribe('xd.addComment',ES(this._handleCommentMsg,'bind',true,this));this.subscribe('xd.commentCreated',ES(this._handleCommentCreatedMsg,'bind',true,this));this.subscribe('xd.commentRemoved',ES(this._handleCommentRemovedMsg,'bind',true,this));},getSize:function(){if(!this._attr.permalink)return {width:this._attr.mobile?'100%':this._attr.width,height:160};},getUrlBits:function(){return {name:'comments',params:this._attr};},getDefaultWebDomain:function(){return l.resolve(this._attr.mobile?'m':'www',true);},_handleCommentMsg:function(o){if(!this.isValid())return;g.fire('comments.add',{post:o.post,user:o.user,widget:this});},_handleCommentCreatedMsg:function(o){if(!this.isValid())return;var p={href:o.href,commentID:o.commentID,parentCommentID:o.parentCommentID,message:o.message};g.fire('comment.create',p);},_handleCommentRemovedMsg:function(o){if(!this.isValid())return;var p={href:o.href,commentID:o.commentID};g.fire('comment.remove',p);}});e.exports=n;},null); + __d("sdk.XFBML.CommentsCount",["ApiClient","sdk.DOM","sdk.XFBML.Element","sprintf"],function(a,b,c,d,e,f,g,h,i,j){var k=i.extend({process:function(){h.addCss(this.dom,'fb_comments_count_zero');var l=this.getAttribute('href',window.location.href);g.scheduleBatchCall('/v2.1/'+encodeURIComponent(l),{fields:'share'},ES(function(m){var n=(m.share&&m.share.comment_count)||0;h.html(this.dom,j('%s',n));if(n>0)h.removeCss(this.dom,'fb_comments_count_zero');this.fire('render');},'bind',true,this));}});e.exports=k;},null); + __d("safeEval",[],function(a,b,c,d,e,f){function g(h,i){if(h===null||typeof h==='undefined')return;if(typeof h!=='string')return h;if(/^\w+$/.test(h)&&typeof window[h]==='function')return window[h].apply(null,i||[]);return Function('return eval("'+h.replace(/"/g,'\\"')+'");').apply(null,i||[]);}e.exports=g;},null); + __d("sdk.Helper",["sdk.ErrorHandling","sdk.Event","UrlMap","safeEval","sprintf"],function(a,b,c,d,e,f,g,h,i,j,k){var l={isUser:function(m){return m<2.2e+09||(m>=1e+14&&m<=100099999989999)||(m>=8.9e+13&&m<=89999999999999);},upperCaseFirstChar:function(m){if(m.length>0){return m.substr(0,1).toUpperCase()+m.substr(1);}else return m;},getProfileLink:function(m,n,o){if(!o&&m)o=k('%s/profile.php?id=%s',i.resolve('www'),m.uid||m.id);if(o)n=k('%s',o,n);return n;},invokeHandler:function(m,n,o){if(m)if(typeof m==='string'){g.unguard(j)(m,o);}else if(m.apply)g.unguard(m).apply(n,o||[]);},fireEvent:function(m,n){var o=n._attr.href;n.fire(m,o);h.fire(m,o,n);},executeFunctionByName:function(m){var n=Array.prototype.slice.call(arguments,1),o=m.split("."),p=o.pop(),q=window;for(var r=0;r"'\/]/g,h={'&':'&','<':'<','>':'>','"':'"',"'":''','/':'/'};function i(j){return j.replace(g,function(k){return h[k];});}e.exports=i;},null); + __d("sdk.XFBML.Name",["ApiClient","escapeHTML","sdk.Event","sdk.XFBML.Element","sdk.Helper","Log","sdk.Runtime"],function(a,b,c,d,e,f,g,h,i,j,k,l,m){var n=({}).hasOwnProperty,o=j.extend({process:function(){ES('Object','assign',false,this,{_uid:this.getAttribute('uid'),_firstnameonly:this._getBoolAttribute('first-name-only'),_lastnameonly:this._getBoolAttribute('last-name-only'),_possessive:this._getBoolAttribute('possessive'),_reflexive:this._getBoolAttribute('reflexive'),_objective:this._getBoolAttribute('objective'),_linked:this._getBoolAttribute('linked',true),_subjectId:this.getAttribute('subject-id')});if(!this._uid){l.error('"uid" is a required attribute for ');this.fire('render');return;}var p=[];if(this._firstnameonly){p.push('first_name');}else if(this._lastnameonly){p.push('last_name');}else p.push('name');if(this._subjectId){p.push('gender');if(this._subjectId==m.getUserID())this._reflexive=true;}i.monitor('auth.statusChange',ES(function(){if(!this.isValid()){this.fire('render');return true;}if(!this._uid||this._uid=='loggedinuser')this._uid=m.getUserID();if(!this._uid)return;g.scheduleBatchCall('/v1.0/'+this._uid,{fields:p.join(',')},ES(function(q){if(n.call(q,'error')){l.warn('The name is not found for ID: '+this._uid);return;}if(this._subjectId==this._uid){this._renderPronoun(q);}else this._renderOther(q);this.fire('render');},'bind',true,this));},'bind',true,this));},_renderPronoun:function(p){var q='',r=this._objective;if(this._subjectId){r=true;if(this._subjectId===this._uid)this._reflexive=true;}if(this._uid==m.getUserID()&&this._getBoolAttribute('use-you',true)){if(this._possessive){if(this._reflexive){q='your own';}else q='your';}else if(this._reflexive){q='yourself';}else q='you';}else switch(p.gender){case 'male':if(this._possessive){q=this._reflexive?'his own':'his';}else if(this._reflexive){q='himself';}else if(r){q='him';}else q='he';break;case 'female':if(this._possessive){q=this._reflexive?'her own':'her';}else if(this._reflexive){q='herself';}else if(r){q='her';}else q='she';break;default:if(this._getBoolAttribute('use-they',true)){if(this._possessive){if(this._reflexive){q='their own';}else q='their';}else if(this._reflexive){q='themselves';}else if(r){q='them';}else q='they';}else if(this._possessive){if(this._reflexive){q='his/her own';}else q='his/her';}else if(this._reflexive){q='himself/herself';}else if(r){q='him/her';}else q='he/she';break;}if(this._getBoolAttribute('capitalize',false))q=k.upperCaseFirstChar(q);this.dom.innerHTML=q;},_renderOther:function(p){var q='',r='';if(this._uid==m.getUserID()&&this._getBoolAttribute('use-you',true)){if(this._reflexive){if(this._possessive){q='your own';}else q='yourself';}else if(this._possessive){q='your';}else q='you';}else if(p){if(null===p.first_name)p.first_name='';if(null===p.last_name)p.last_name='';if(this._firstnameonly&&p.first_name!==undefined){q=h(p.first_name);}else if(this._lastnameonly&&p.last_name!==undefined)q=h(p.last_name);if(!q)q=h(p.name);if(q!==''&&this._possessive)q+='\'s';}if(!q)q=h(this.getAttribute('if-cant-see','Facebook User'));if(q){if(this._getBoolAttribute('capitalize',false))q=k.upperCaseFirstChar(q);if(p&&this._linked){r=k.getProfileLink(p,q,this.getAttribute('href',null));}else r=q;}this.dom.innerHTML=r;}});e.exports=o;},null); + __d("sdk.XFBML.RecommendationsBar",["sdk.Arbiter","DOMEventListener","sdk.Event","sdk.XFBML.IframeWidget","resolveURI","sdk.Runtime"],function(a,b,c,d,e,f,g,h,i,j,k,l){var m=j.extend({getUrlBits:function(){return {name:'recommendations_bar',params:this._attr};},setupAndValidate:function(){function n(w,x){var y=0,z=null;function aa(){x();z=null;y=ES('Date','now',false);}return function(){if(!z){var ba=ES('Date','now',false);if(ba-y=this._attr.trigger;}}});e.exports=m;},null); + __d("sdk.XFBML.Registration",["sdk.Auth","sdk.Helper","sdk.XFBML.IframeWidget","sdk.Runtime","UrlMap"],function(a,b,c,d,e,f,g,h,i,j,k){var l=i.extend({_visibleAfter:'immediate',_baseHeight:167,_fieldHeight:28,_skinnyWidth:520,_skinnyBaseHeight:173,_skinnyFieldHeight:52,setupAndValidate:function(){this._attr={action:this.getAttribute('action'),border_color:this.getAttribute('border-color'),channel_url:this.getChannelUrl(),client_id:j.getClientID(),fb_only:this._getBoolAttribute('fb-only',false),fb_register:this._getBoolAttribute('fb-register',false),fields:this.getAttribute('fields'),height:this._getPxAttribute('height'),redirect_uri:this.getAttribute('redirect-uri',window.location.href),no_footer:this._getBoolAttribute('no-footer'),no_header:this._getBoolAttribute('no-header'),onvalidate:this.getAttribute('onvalidate'),width:this._getPxAttribute('width',600),target:this.getAttribute('target')};if(this._attr.onvalidate)this.subscribe('xd.validate',ES(function(m){var n=ES('JSON','parse',false,m.value),o=ES(function(q){this.arbiterInform('Registration.Validation',{errors:q,id:m.id});},'bind',true,this),p=h.executeFunctionByName(this._attr.onvalidate,n,o);if(p)o(p);},'bind',true,this));this.subscribe('xd.authLogin',ES(this._onAuthLogin,'bind',true,this));this.subscribe('xd.authLogout',ES(this._onAuthLogout,'bind',true,this));return true;},getSize:function(){return {width:this._attr.width,height:this._getHeight()};},_getHeight:function(){if(this._attr.height)return this._attr.height;var m;if(!this._attr.fields){m=['name'];}else try{m=ES('JSON','parse',false,this._attr.fields);}catch(n){m=this._attr.fields.split(/,/);}if(this._attr.width 0 && !this.encoding) { + var pack = this.packetBuffer.shift(); + this.packet(pack); + } + }; + + /** + * Clean up transport subscriptions and packet buffer. + * + * @api private + */ + + Manager.prototype.cleanup = function(){ + var sub; + while (sub = this.subs.shift()) sub.destroy(); + + this.packetBuffer = []; + this.encoding = false; + + this.decoder.destroy(); + }; + + /** + * Close the current socket. + * + * @api private + */ + + Manager.prototype.close = + Manager.prototype.disconnect = function(){ + this.skipReconnect = true; + this.backoff.reset(); + this.readyState = 'closed'; + this.engine && this.engine.close(); + }; + + /** + * Called upon engine close. + * + * @api private + */ + + Manager.prototype.onclose = function(reason){ + debug('close'); + this.cleanup(); + this.backoff.reset(); + this.readyState = 'closed'; + this.emit('close', reason); + if (this._reconnection && !this.skipReconnect) { + this.reconnect(); + } + }; + + /** + * Attempt a reconnection. + * + * @api private + */ + + Manager.prototype.reconnect = function(){ + if (this.reconnecting || this.skipReconnect) return this; + + var self = this; + + if (this.backoff.attempts >= this._reconnectionAttempts) { + debug('reconnect failed'); + this.backoff.reset(); + this.emitAll('reconnect_failed'); + this.reconnecting = false; + } else { + var delay = this.backoff.duration(); + debug('will wait %dms before reconnect attempt', delay); + + this.reconnecting = true; + var timer = setTimeout(function(){ + if (self.skipReconnect) return; + + debug('attempting reconnect'); + self.emitAll('reconnect_attempt', self.backoff.attempts); + self.emitAll('reconnecting', self.backoff.attempts); + + // check again for the case socket closed in above events + if (self.skipReconnect) return; + + self.open(function(err){ + if (err) { + debug('reconnect attempt error'); + self.reconnecting = false; + self.reconnect(); + self.emitAll('reconnect_error', err.data); + } else { + debug('reconnect success'); + self.onreconnect(); + } + }); + }, delay); + + this.subs.push({ + destroy: function(){ + clearTimeout(timer); + } + }); + } + }; + + /** + * Called upon successful reconnect. + * + * @api private + */ + + Manager.prototype.onreconnect = function(){ + var attempt = this.backoff.attempts; + this.reconnecting = false; + this.backoff.reset(); + this.updateSocketIds(); + this.emitAll('reconnect', attempt); + }; + +},{"./on":4,"./socket":5,"./url":6,"backo2":7,"component-bind":8,"component-emitter":9,"debug":10,"engine.io-client":11,"indexof":42,"object-component":43,"socket.io-parser":46}],4:[function(_dereq_,module,exports){ + + /** + * Module exports. + */ + + module.exports = on; + + /** + * Helper for subscriptions. + * + * @param {Object|EventEmitter} obj with `Emitter` mixin or `EventEmitter` + * @param {String} event name + * @param {Function} callback + * @api public + */ + + function on(obj, ev, fn) { + obj.on(ev, fn); + return { + destroy: function(){ + obj.removeListener(ev, fn); + } + }; + } + +},{}],5:[function(_dereq_,module,exports){ + + /** + * Module dependencies. + */ + + var parser = _dereq_('socket.io-parser'); + var Emitter = _dereq_('component-emitter'); + var toArray = _dereq_('to-array'); + var on = _dereq_('./on'); + var bind = _dereq_('component-bind'); + var debug = _dereq_('debug')('socket.io-client:socket'); + var hasBin = _dereq_('has-binary'); + + /** + * Module exports. + */ + + module.exports = exports = Socket; + + /** + * Internal events (blacklisted). + * These events can't be emitted by the user. + * + * @api private + */ + + var events = { + connect: 1, + connect_error: 1, + connect_timeout: 1, + disconnect: 1, + error: 1, + reconnect: 1, + reconnect_attempt: 1, + reconnect_failed: 1, + reconnect_error: 1, + reconnecting: 1 + }; + + /** + * Shortcut to `Emitter#emit`. + */ + + var emit = Emitter.prototype.emit; + + /** + * `Socket` constructor. + * + * @api public + */ + + function Socket(io, nsp){ + this.io = io; + this.nsp = nsp; + this.json = this; // compat + this.ids = 0; + this.acks = {}; + if (this.io.autoConnect) this.open(); + this.receiveBuffer = []; + this.sendBuffer = []; + this.connected = false; + this.disconnected = true; + } + + /** + * Mix in `Emitter`. + */ + + Emitter(Socket.prototype); + + /** + * Subscribe to open, close and packet events + * + * @api private + */ + + Socket.prototype.subEvents = function() { + if (this.subs) return; + + var io = this.io; + this.subs = [ + on(io, 'open', bind(this, 'onopen')), + on(io, 'packet', bind(this, 'onpacket')), + on(io, 'close', bind(this, 'onclose')) + ]; + }; + + /** + * "Opens" the socket. + * + * @api public + */ + + Socket.prototype.open = + Socket.prototype.connect = function(){ + if (this.connected) return this; + + this.subEvents(); + this.io.open(); // ensure open + if ('open' == this.io.readyState) this.onopen(); + return this; + }; + + /** + * Sends a `message` event. + * + * @return {Socket} self + * @api public + */ + + Socket.prototype.send = function(){ + var args = toArray(arguments); + args.unshift('message'); + this.emit.apply(this, args); + return this; + }; + + /** + * Override `emit`. + * If the event is in `events`, it's emitted normally. + * + * @param {String} event name + * @return {Socket} self + * @api public + */ + + Socket.prototype.emit = function(ev){ + if (events.hasOwnProperty(ev)) { + emit.apply(this, arguments); + return this; + } + + var args = toArray(arguments); + var parserType = parser.EVENT; // default + if (hasBin(args)) { parserType = parser.BINARY_EVENT; } // binary + var packet = { type: parserType, data: args }; + + // event ack callback + if ('function' == typeof args[args.length - 1]) { + debug('emitting packet with ack id %d', this.ids); + this.acks[this.ids] = args.pop(); + packet.id = this.ids++; + } + + if (this.connected) { + this.packet(packet); + } else { + this.sendBuffer.push(packet); + } + + return this; + }; + + /** + * Sends a packet. + * + * @param {Object} packet + * @api private + */ + + Socket.prototype.packet = function(packet){ + packet.nsp = this.nsp; + this.io.packet(packet); + }; + + /** + * Called upon engine `open`. + * + * @api private + */ + + Socket.prototype.onopen = function(){ + debug('transport is open - connecting'); + + // write connect packet if necessary + if ('/' != this.nsp) { + this.packet({ type: parser.CONNECT }); + } + }; + + /** + * Called upon engine `close`. + * + * @param {String} reason + * @api private + */ + + Socket.prototype.onclose = function(reason){ + debug('close (%s)', reason); + this.connected = false; + this.disconnected = true; + delete this.id; + this.emit('disconnect', reason); + }; + + /** + * Called with socket packet. + * + * @param {Object} packet + * @api private + */ + + Socket.prototype.onpacket = function(packet){ + if (packet.nsp != this.nsp) return; + + switch (packet.type) { + case parser.CONNECT: + this.onconnect(); + break; + + case parser.EVENT: + this.onevent(packet); + break; + + case parser.BINARY_EVENT: + this.onevent(packet); + break; + + case parser.ACK: + this.onack(packet); + break; + + case parser.BINARY_ACK: + this.onack(packet); + break; + + case parser.DISCONNECT: + this.ondisconnect(); + break; + + case parser.ERROR: + this.emit('error', packet.data); + break; + } + }; + + /** + * Called upon a server event. + * + * @param {Object} packet + * @api private + */ + + Socket.prototype.onevent = function(packet){ + var args = packet.data || []; + debug('emitting event %j', args); + + if (null != packet.id) { + debug('attaching ack callback to event'); + args.push(this.ack(packet.id)); + } + + if (this.connected) { + emit.apply(this, args); + } else { + this.receiveBuffer.push(args); + } + }; + + /** + * Produces an ack callback to emit with an event. + * + * @api private + */ + + Socket.prototype.ack = function(id){ + var self = this; + var sent = false; + return function(){ + // prevent double callbacks + if (sent) return; + sent = true; + var args = toArray(arguments); + debug('sending ack %j', args); + + var type = hasBin(args) ? parser.BINARY_ACK : parser.ACK; + self.packet({ + type: type, + id: id, + data: args + }); + }; + }; + + /** + * Called upon a server acknowlegement. + * + * @param {Object} packet + * @api private + */ + + Socket.prototype.onack = function(packet){ + debug('calling ack %s with %j', packet.id, packet.data); + var fn = this.acks[packet.id]; + fn.apply(this, packet.data); + delete this.acks[packet.id]; + }; + + /** + * Called upon server connect. + * + * @api private + */ + + Socket.prototype.onconnect = function(){ + this.connected = true; + this.disconnected = false; + this.emit('connect'); + this.emitBuffered(); + }; + + /** + * Emit buffered events (received and emitted). + * + * @api private + */ + + Socket.prototype.emitBuffered = function(){ + var i; + for (i = 0; i < this.receiveBuffer.length; i++) { + emit.apply(this, this.receiveBuffer[i]); + } + this.receiveBuffer = []; + + for (i = 0; i < this.sendBuffer.length; i++) { + this.packet(this.sendBuffer[i]); + } + this.sendBuffer = []; + }; + + /** + * Called upon server disconnect. + * + * @api private + */ + + Socket.prototype.ondisconnect = function(){ + debug('server disconnect (%s)', this.nsp); + this.destroy(); + this.onclose('io server disconnect'); + }; + + /** + * Called upon forced client/server side disconnections, + * this method ensures the manager stops tracking us and + * that reconnections don't get triggered for this. + * + * @api private. + */ + + Socket.prototype.destroy = function(){ + if (this.subs) { + // clean subscriptions to avoid reconnections + for (var i = 0; i < this.subs.length; i++) { + this.subs[i].destroy(); + } + this.subs = null; + } + + this.io.destroy(this); + }; + + /** + * Disconnects the socket manually. + * + * @return {Socket} self + * @api public + */ + + Socket.prototype.close = + Socket.prototype.disconnect = function(){ + if (this.connected) { + debug('performing disconnect (%s)', this.nsp); + this.packet({ type: parser.DISCONNECT }); + } + + // remove socket from pool + this.destroy(); + + if (this.connected) { + // fire events + this.onclose('io client disconnect'); + } + return this; + }; + +},{"./on":4,"component-bind":8,"component-emitter":9,"debug":10,"has-binary":38,"socket.io-parser":46,"to-array":50}],6:[function(_dereq_,module,exports){ + (function (global){ + + /** + * Module dependencies. + */ + + var parseuri = _dereq_('parseuri'); + var debug = _dereq_('debug')('socket.io-client:url'); + + /** + * Module exports. + */ + + module.exports = url; + + /** + * URL parser. + * + * @param {String} url + * @param {Object} An object meant to mimic window.location. + * Defaults to window.location. + * @api public + */ + + function url(uri, loc){ + var obj = uri; + + // default to window.location + var loc = loc || global.location; + if (null == uri) uri = loc.protocol + '//' + loc.host; + + // relative path support + if ('string' == typeof uri) { + if ('/' == uri.charAt(0)) { + if ('/' == uri.charAt(1)) { + uri = loc.protocol + uri; + } else { + uri = loc.hostname + uri; + } + } + + if (!/^(https?|wss?):\/\//.test(uri)) { + debug('protocol-less url %s', uri); + if ('undefined' != typeof loc) { + uri = loc.protocol + '//' + uri; + } else { + uri = 'https://' + uri; + } + } + + // parse + debug('parse %s', uri); + obj = parseuri(uri); + } + + // make sure we treat `localhost:80` and `localhost` equally + if (!obj.port) { + if (/^(http|ws)$/.test(obj.protocol)) { + obj.port = '80'; + } + else if (/^(http|ws)s$/.test(obj.protocol)) { + obj.port = '443'; + } + } + + obj.path = obj.path || '/'; + + // define unique id + obj.id = obj.protocol + '://' + obj.host + ':' + obj.port; + // define href + obj.href = obj.protocol + '://' + obj.host + (loc && loc.port == obj.port ? '' : (':' + obj.port)); + + return obj; + } + + }).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"debug":10,"parseuri":44}],7:[function(_dereq_,module,exports){ + + /** + * Expose `Backoff`. + */ + + module.exports = Backoff; + + /** + * Initialize backoff timer with `opts`. + * + * - `min` initial timeout in milliseconds [100] + * - `max` max timeout [10000] + * - `jitter` [0] + * - `factor` [2] + * + * @param {Object} opts + * @api public + */ + + function Backoff(opts) { + opts = opts || {}; + this.ms = opts.min || 100; + this.max = opts.max || 10000; + this.factor = opts.factor || 2; + this.jitter = opts.jitter > 0 && opts.jitter <= 1 ? opts.jitter : 0; + this.attempts = 0; + } + + /** + * Return the backoff duration. + * + * @return {Number} + * @api public + */ + + Backoff.prototype.duration = function(){ + var ms = this.ms * Math.pow(this.factor, this.attempts++); + if (this.jitter) { + var rand = Math.random(); + var deviation = Math.floor(rand * this.jitter * ms); + ms = (Math.floor(rand * 10) & 1) == 0 ? ms - deviation : ms + deviation; + } + return Math.min(ms, this.max) | 0; + }; + + /** + * Reset the number of attempts. + * + * @api public + */ + + Backoff.prototype.reset = function(){ + this.attempts = 0; + }; + + /** + * Set the minimum duration + * + * @api public + */ + + Backoff.prototype.setMin = function(min){ + this.ms = min; + }; + + /** + * Set the maximum duration + * + * @api public + */ + + Backoff.prototype.setMax = function(max){ + this.max = max; + }; + + /** + * Set the jitter + * + * @api public + */ + + Backoff.prototype.setJitter = function(jitter){ + this.jitter = jitter; + }; + + +},{}],8:[function(_dereq_,module,exports){ + /** + * Slice reference. + */ + + var slice = [].slice; + + /** + * Bind `obj` to `fn`. + * + * @param {Object} obj + * @param {Function|String} fn or string + * @return {Function} + * @api public + */ + + module.exports = function(obj, fn){ + if ('string' == typeof fn) fn = obj[fn]; + if ('function' != typeof fn) throw new Error('bind() requires a function'); + var args = slice.call(arguments, 2); + return function(){ + return fn.apply(obj, args.concat(slice.call(arguments))); + } + }; + +},{}],9:[function(_dereq_,module,exports){ + + /** + * Expose `Emitter`. + */ + + module.exports = Emitter; + + /** + * Initialize a new `Emitter`. + * + * @api public + */ + + function Emitter(obj) { + if (obj) return mixin(obj); + }; + + /** + * Mixin the emitter properties. + * + * @param {Object} obj + * @return {Object} + * @api private + */ + + function mixin(obj) { + for (var key in Emitter.prototype) { + obj[key] = Emitter.prototype[key]; + } + return obj; + } + + /** + * Listen on the given `event` with `fn`. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + + Emitter.prototype.on = + Emitter.prototype.addEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; + (this._callbacks[event] = this._callbacks[event] || []) + .push(fn); + return this; + }; + + /** + * Adds an `event` listener that will be invoked a single + * time then automatically removed. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + + Emitter.prototype.once = function(event, fn){ + var self = this; + this._callbacks = this._callbacks || {}; + + function on() { + self.off(event, on); + fn.apply(this, arguments); + } + + on.fn = fn; + this.on(event, on); + return this; + }; + + /** + * Remove the given callback for `event` or all + * registered callbacks. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + + Emitter.prototype.off = + Emitter.prototype.removeListener = + Emitter.prototype.removeAllListeners = + Emitter.prototype.removeEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; + + // all + if (0 == arguments.length) { + this._callbacks = {}; + return this; + } + + // specific event + var callbacks = this._callbacks[event]; + if (!callbacks) return this; + + // remove all handlers + if (1 == arguments.length) { + delete this._callbacks[event]; + return this; + } + + // remove specific handler + var cb; + for (var i = 0; i < callbacks.length; i++) { + cb = callbacks[i]; + if (cb === fn || cb.fn === fn) { + callbacks.splice(i, 1); + break; + } + } + return this; + }; + + /** + * Emit `event` with the given args. + * + * @param {String} event + * @param {Mixed} ... + * @return {Emitter} + */ + + Emitter.prototype.emit = function(event){ + this._callbacks = this._callbacks || {}; + var args = [].slice.call(arguments, 1) + , callbacks = this._callbacks[event]; + + if (callbacks) { + callbacks = callbacks.slice(0); + for (var i = 0, len = callbacks.length; i < len; ++i) { + callbacks[i].apply(this, args); + } + } + + return this; + }; + + /** + * Return array of callbacks for `event`. + * + * @param {String} event + * @return {Array} + * @api public + */ + + Emitter.prototype.listeners = function(event){ + this._callbacks = this._callbacks || {}; + return this._callbacks[event] || []; + }; + + /** + * Check if this emitter has `event` handlers. + * + * @param {String} event + * @return {Boolean} + * @api public + */ + + Emitter.prototype.hasListeners = function(event){ + return !! this.listeners(event).length; + }; + +},{}],10:[function(_dereq_,module,exports){ + + /** + * Expose `debug()` as the module. + */ + + module.exports = debug; + + /** + * Create a debugger with the given `name`. + * + * @param {String} name + * @return {Type} + * @api public + */ + + function debug(name) { + if (!debug.enabled(name)) return function(){}; + + return function(fmt){ + fmt = coerce(fmt); + + var curr = new Date; + var ms = curr - (debug[name] || curr); + debug[name] = curr; + + fmt = name + + ' ' + + fmt + + ' +' + debug.humanize(ms); + + // This hackery is required for IE8 + // where `console.log` doesn't have 'apply' + window.console + && console.log + && Function.prototype.apply.call(console.log, console, arguments); + } + } + + /** + * The currently active debug mode names. + */ + + debug.names = []; + debug.skips = []; + + /** + * Enables a debug mode by name. This can include modes + * separated by a colon and wildcards. + * + * @param {String} name + * @api public + */ + + debug.enable = function(name) { + try { + localStorage.debug = name; + } catch(e){} + + var split = (name || '').split(/[\s,]+/) + , len = split.length; + + for (var i = 0; i < len; i++) { + name = split[i].replace('*', '.*?'); + if (name[0] === '-') { + debug.skips.push(new RegExp('^' + name.substr(1) + '$')); + } + else { + debug.names.push(new RegExp('^' + name + '$')); + } + } + }; + + /** + * Disable debug output. + * + * @api public + */ + + debug.disable = function(){ + debug.enable(''); + }; + + /** + * Humanize the given `ms`. + * + * @param {Number} m + * @return {String} + * @api private + */ + + debug.humanize = function(ms) { + var sec = 1000 + , min = 60 * 1000 + , hour = 60 * min; + + if (ms >= hour) return (ms / hour).toFixed(1) + 'h'; + if (ms >= min) return (ms / min).toFixed(1) + 'm'; + if (ms >= sec) return (ms / sec | 0) + 's'; + return ms + 'ms'; + }; + + /** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + + debug.enabled = function(name) { + for (var i = 0, len = debug.skips.length; i < len; i++) { + if (debug.skips[i].test(name)) { + return false; + } + } + for (var i = 0, len = debug.names.length; i < len; i++) { + if (debug.names[i].test(name)) { + return true; + } + } + return false; + }; + + /** + * Coerce `val`. + */ + + function coerce(val) { + if (val instanceof Error) return val.stack || val.message; + return val; + } + +// persist + + try { + if (window.localStorage) debug.enable(localStorage.debug); + } catch(e){} + +},{}],11:[function(_dereq_,module,exports){ + + module.exports = _dereq_('./lib/'); + +},{"./lib/":12}],12:[function(_dereq_,module,exports){ + + module.exports = _dereq_('./socket'); + + /** + * Exports parser + * + * @api public + * + */ + module.exports.parser = _dereq_('engine.io-parser'); + +},{"./socket":13,"engine.io-parser":25}],13:[function(_dereq_,module,exports){ + (function (global){ + /** + * Module dependencies. + */ + + var transports = _dereq_('./transports'); + var Emitter = _dereq_('component-emitter'); + var debug = _dereq_('debug')('engine.io-client:socket'); + var index = _dereq_('indexof'); + var parser = _dereq_('engine.io-parser'); + var parseuri = _dereq_('parseuri'); + var parsejson = _dereq_('parsejson'); + var parseqs = _dereq_('parseqs'); + + /** + * Module exports. + */ + + module.exports = Socket; + + /** + * Noop function. + * + * @api private + */ + + function noop(){} + + /** + * Socket constructor. + * + * @param {String|Object} uri or options + * @param {Object} options + * @api public + */ + + function Socket(uri, opts){ + if (!(this instanceof Socket)) return new Socket(uri, opts); + + opts = opts || {}; + + if (uri && 'object' == typeof uri) { + opts = uri; + uri = null; + } + + if (uri) { + uri = parseuri(uri); + opts.host = uri.host; + opts.secure = uri.protocol == 'https' || uri.protocol == 'wss'; + opts.port = uri.port; + if (uri.query) opts.query = uri.query; + } + + this.secure = null != opts.secure ? opts.secure : + (global.location && 'https:' == location.protocol); + + if (opts.host) { + var pieces = opts.host.split(':'); + opts.hostname = pieces.shift(); + if (pieces.length) { + opts.port = pieces.pop(); + } else if (!opts.port) { + // if no port is specified manually, use the protocol default + opts.port = this.secure ? '443' : '80'; + } + } + + this.agent = opts.agent || false; + this.hostname = opts.hostname || + (global.location ? location.hostname : 'localhost'); + this.port = opts.port || (global.location && location.port ? + location.port : + (this.secure ? 443 : 80)); + this.query = opts.query || {}; + if ('string' == typeof this.query) this.query = parseqs.decode(this.query); + this.upgrade = false !== opts.upgrade; + this.path = (opts.path || '/engine.io').replace(/\/$/, '') + '/'; + this.forceJSONP = !!opts.forceJSONP; + this.jsonp = false !== opts.jsonp; + this.forceBase64 = !!opts.forceBase64; + this.enablesXDR = !!opts.enablesXDR; + this.timestampParam = opts.timestampParam || 't'; + this.timestampRequests = opts.timestampRequests; + this.transports = opts.transports || ['polling', 'websocket']; + this.readyState = ''; + this.writeBuffer = []; + this.callbackBuffer = []; + this.policyPort = opts.policyPort || 843; + this.rememberUpgrade = opts.rememberUpgrade || false; + this.binaryType = null; + this.onlyBinaryUpgrades = opts.onlyBinaryUpgrades; + + // SSL options for Node.js client + this.pfx = opts.pfx || null; + this.key = opts.key || null; + this.passphrase = opts.passphrase || null; + this.cert = opts.cert || null; + this.ca = opts.ca || null; + this.ciphers = opts.ciphers || null; + this.rejectUnauthorized = opts.rejectUnauthorized || null; + + this.open(); + } + + Socket.priorWebsocketSuccess = false; + + /** + * Mix in `Emitter`. + */ + + Emitter(Socket.prototype); + + /** + * Protocol version. + * + * @api public + */ + + Socket.protocol = parser.protocol; // this is an int + + /** + * Expose deps for legacy compatibility + * and standalone browser access. + */ + + Socket.Socket = Socket; + Socket.Transport = _dereq_('./transport'); + Socket.transports = _dereq_('./transports'); + Socket.parser = _dereq_('engine.io-parser'); + + /** + * Creates transport of the given type. + * + * @param {String} transport name + * @return {Transport} + * @api private + */ + + Socket.prototype.createTransport = function (name) { + debug('creating transport "%s"', name); + var query = clone(this.query); + + // append engine.io protocol identifier + query.EIO = parser.protocol; + + // transport name + query.transport = name; + + // session id if we already have one + if (this.id) query.sid = this.id; + + var transport = new transports[name]({ + agent: this.agent, + hostname: this.hostname, + port: this.port, + secure: this.secure, + path: this.path, + query: query, + forceJSONP: this.forceJSONP, + jsonp: this.jsonp, + forceBase64: this.forceBase64, + enablesXDR: this.enablesXDR, + timestampRequests: this.timestampRequests, + timestampParam: this.timestampParam, + policyPort: this.policyPort, + socket: this, + pfx: this.pfx, + key: this.key, + passphrase: this.passphrase, + cert: this.cert, + ca: this.ca, + ciphers: this.ciphers, + rejectUnauthorized: this.rejectUnauthorized + }); + + return transport; + }; + + function clone (obj) { + var o = {}; + for (var i in obj) { + if (obj.hasOwnProperty(i)) { + o[i] = obj[i]; + } + } + return o; + } + + /** + * Initializes transport to use and starts probe. + * + * @api private + */ + Socket.prototype.open = function () { + var transport; + if (this.rememberUpgrade && Socket.priorWebsocketSuccess && this.transports.indexOf('websocket') != -1) { + transport = 'websocket'; + } else if (0 == this.transports.length) { + // Emit error on next tick so it can be listened to + var self = this; + setTimeout(function() { + self.emit('error', 'No transports available'); + }, 0); + return; + } else { + transport = this.transports[0]; + } + this.readyState = 'opening'; + + // Retry with the next transport if the transport is disabled (jsonp: false) + var transport; + try { + transport = this.createTransport(transport); + } catch (e) { + this.transports.shift(); + this.open(); + return; + } + + transport.open(); + this.setTransport(transport); + }; + + /** + * Sets the current transport. Disables the existing one (if any). + * + * @api private + */ + + Socket.prototype.setTransport = function(transport){ + debug('setting transport %s', transport.name); + var self = this; + + if (this.transport) { + debug('clearing existing transport %s', this.transport.name); + this.transport.removeAllListeners(); + } + + // set up transport + this.transport = transport; + + // set up transport listeners + transport + .on('drain', function(){ + self.onDrain(); + }) + .on('packet', function(packet){ + self.onPacket(packet); + }) + .on('error', function(e){ + self.onError(e); + }) + .on('close', function(){ + self.onClose('transport close'); + }); + }; + + /** + * Probes a transport. + * + * @param {String} transport name + * @api private + */ + + Socket.prototype.probe = function (name) { + debug('probing transport "%s"', name); + var transport = this.createTransport(name, { probe: 1 }) + , failed = false + , self = this; + + Socket.priorWebsocketSuccess = false; + + function onTransportOpen(){ + if (self.onlyBinaryUpgrades) { + var upgradeLosesBinary = !this.supportsBinary && self.transport.supportsBinary; + failed = failed || upgradeLosesBinary; + } + if (failed) return; + + debug('probe transport "%s" opened', name); + transport.send([{ type: 'ping', data: 'probe' }]); + transport.once('packet', function (msg) { + if (failed) return; + if ('pong' == msg.type && 'probe' == msg.data) { + debug('probe transport "%s" pong', name); + self.upgrading = true; + self.emit('upgrading', transport); + if (!transport) return; + Socket.priorWebsocketSuccess = 'websocket' == transport.name; + + debug('pausing current transport "%s"', self.transport.name); + self.transport.pause(function () { + if (failed) return; + if ('closed' == self.readyState) return; + debug('changing transport and sending upgrade packet'); + + cleanup(); + + self.setTransport(transport); + transport.send([{ type: 'upgrade' }]); + self.emit('upgrade', transport); + transport = null; + self.upgrading = false; + self.flush(); + }); + } else { + debug('probe transport "%s" failed', name); + var err = new Error('probe error'); + err.transport = transport.name; + self.emit('upgradeError', err); + } + }); + } + + function freezeTransport() { + if (failed) return; + + // Any callback called by transport should be ignored since now + failed = true; + + cleanup(); + + transport.close(); + transport = null; + } + + //Handle any error that happens while probing + function onerror(err) { + var error = new Error('probe error: ' + err); + error.transport = transport.name; + + freezeTransport(); + + debug('probe transport "%s" failed because of error: %s', name, err); + + self.emit('upgradeError', error); + } + + function onTransportClose(){ + onerror("transport closed"); + } + + //When the socket is closed while we're probing + function onclose(){ + onerror("socket closed"); + } + + //When the socket is upgraded while we're probing + function onupgrade(to){ + if (transport && to.name != transport.name) { + debug('"%s" works - aborting "%s"', to.name, transport.name); + freezeTransport(); + } + } + + //Remove all listeners on the transport and on self + function cleanup(){ + transport.removeListener('open', onTransportOpen); + transport.removeListener('error', onerror); + transport.removeListener('close', onTransportClose); + self.removeListener('close', onclose); + self.removeListener('upgrading', onupgrade); + } + + transport.once('open', onTransportOpen); + transport.once('error', onerror); + transport.once('close', onTransportClose); + + this.once('close', onclose); + this.once('upgrading', onupgrade); + + transport.open(); + + }; + + /** + * Called when connection is deemed open. + * + * @api public + */ + + Socket.prototype.onOpen = function () { + debug('socket open'); + this.readyState = 'open'; + Socket.priorWebsocketSuccess = 'websocket' == this.transport.name; + this.emit('open'); + this.flush(); + + // we check for `readyState` in case an `open` + // listener already closed the socket + if ('open' == this.readyState && this.upgrade && this.transport.pause) { + debug('starting upgrade probes'); + for (var i = 0, l = this.upgrades.length; i < l; i++) { + this.probe(this.upgrades[i]); + } + } + }; + + /** + * Handles a packet. + * + * @api private + */ + + Socket.prototype.onPacket = function (packet) { + if ('opening' == this.readyState || 'open' == this.readyState) { + debug('socket receive: type "%s", data "%s"', packet.type, packet.data); + + this.emit('packet', packet); + + // Socket is live - any packet counts + this.emit('heartbeat'); + + switch (packet.type) { + case 'open': + this.onHandshake(parsejson(packet.data)); + break; + + case 'pong': + this.setPing(); + break; + + case 'error': + var err = new Error('server error'); + err.code = packet.data; + this.emit('error', err); + break; + + case 'message': + this.emit('data', packet.data); + this.emit('message', packet.data); + break; + } + } else { + debug('packet received with socket readyState "%s"', this.readyState); + } + }; + + /** + * Called upon handshake completion. + * + * @param {Object} handshake obj + * @api private + */ + + Socket.prototype.onHandshake = function (data) { + this.emit('handshake', data); + this.id = data.sid; + this.transport.query.sid = data.sid; + this.upgrades = this.filterUpgrades(data.upgrades); + this.pingInterval = data.pingInterval; + this.pingTimeout = data.pingTimeout; + this.onOpen(); + // In case open handler closes socket + if ('closed' == this.readyState) return; + this.setPing(); + + // Prolong liveness of socket on heartbeat + this.removeListener('heartbeat', this.onHeartbeat); + this.on('heartbeat', this.onHeartbeat); + }; + + /** + * Resets ping timeout. + * + * @api private + */ + + Socket.prototype.onHeartbeat = function (timeout) { + clearTimeout(this.pingTimeoutTimer); + var self = this; + self.pingTimeoutTimer = setTimeout(function () { + if ('closed' == self.readyState) return; + self.onClose('ping timeout'); + }, timeout || (self.pingInterval + self.pingTimeout)); + }; + + /** + * Pings server every `this.pingInterval` and expects response + * within `this.pingTimeout` or closes connection. + * + * @api private + */ + + Socket.prototype.setPing = function () { + var self = this; + clearTimeout(self.pingIntervalTimer); + self.pingIntervalTimer = setTimeout(function () { + debug('writing ping packet - expecting pong within %sms', self.pingTimeout); + self.ping(); + self.onHeartbeat(self.pingTimeout); + }, self.pingInterval); + }; + + /** + * Sends a ping packet. + * + * @api public + */ + + Socket.prototype.ping = function () { + this.sendPacket('ping'); + }; + + /** + * Called on `drain` event + * + * @api private + */ + + Socket.prototype.onDrain = function() { + for (var i = 0; i < this.prevBufferLen; i++) { + if (this.callbackBuffer[i]) { + this.callbackBuffer[i](); + } + } + + this.writeBuffer.splice(0, this.prevBufferLen); + this.callbackBuffer.splice(0, this.prevBufferLen); + + // setting prevBufferLen = 0 is very important + // for example, when upgrading, upgrade packet is sent over, + // and a nonzero prevBufferLen could cause problems on `drain` + this.prevBufferLen = 0; + + if (this.writeBuffer.length == 0) { + this.emit('drain'); + } else { + this.flush(); + } + }; + + /** + * Flush write buffers. + * + * @api private + */ + + Socket.prototype.flush = function () { + if ('closed' != this.readyState && this.transport.writable && + !this.upgrading && this.writeBuffer.length) { + debug('flushing %d packets in socket', this.writeBuffer.length); + this.transport.send(this.writeBuffer); + // keep track of current length of writeBuffer + // splice writeBuffer and callbackBuffer on `drain` + this.prevBufferLen = this.writeBuffer.length; + this.emit('flush'); + } + }; + + /** + * Sends a message. + * + * @param {String} message. + * @param {Function} callback function. + * @return {Socket} for chaining. + * @api public + */ + + Socket.prototype.write = + Socket.prototype.send = function (msg, fn) { + this.sendPacket('message', msg, fn); + return this; + }; + + /** + * Sends a packet. + * + * @param {String} packet type. + * @param {String} data. + * @param {Function} callback function. + * @api private + */ + + Socket.prototype.sendPacket = function (type, data, fn) { + if ('closing' == this.readyState || 'closed' == this.readyState) { + return; + } + + var packet = { type: type, data: data }; + this.emit('packetCreate', packet); + this.writeBuffer.push(packet); + this.callbackBuffer.push(fn); + this.flush(); + }; + + /** + * Closes the connection. + * + * @api private + */ + + Socket.prototype.close = function () { + if ('opening' == this.readyState || 'open' == this.readyState) { + this.readyState = 'closing'; + + var self = this; + + function close() { + self.onClose('forced close'); + debug('socket closing - telling transport to close'); + self.transport.close(); + } + + function cleanupAndClose() { + self.removeListener('upgrade', cleanupAndClose); + self.removeListener('upgradeError', cleanupAndClose); + close(); + } + + function waitForUpgrade() { + // wait for upgrade to finish since we can't send packets while pausing a transport + self.once('upgrade', cleanupAndClose); + self.once('upgradeError', cleanupAndClose); + } + + if (this.writeBuffer.length) { + this.once('drain', function() { + if (this.upgrading) { + waitForUpgrade(); + } else { + close(); + } + }); + } else if (this.upgrading) { + waitForUpgrade(); + } else { + close(); + } + } + + return this; + }; + + /** + * Called upon transport error + * + * @api private + */ + + Socket.prototype.onError = function (err) { + debug('socket error %j', err); + Socket.priorWebsocketSuccess = false; + this.emit('error', err); + this.onClose('transport error', err); + }; + + /** + * Called upon transport close. + * + * @api private + */ + + Socket.prototype.onClose = function (reason, desc) { + if ('opening' == this.readyState || 'open' == this.readyState || 'closing' == this.readyState) { + debug('socket close with reason: "%s"', reason); + var self = this; + + // clear timers + clearTimeout(this.pingIntervalTimer); + clearTimeout(this.pingTimeoutTimer); + + // clean buffers in next tick, so developers can still + // grab the buffers on `close` event + setTimeout(function() { + self.writeBuffer = []; + self.callbackBuffer = []; + self.prevBufferLen = 0; + }, 0); + + // stop event from firing again for transport + this.transport.removeAllListeners('close'); + + // ensure transport won't stay open + this.transport.close(); + + // ignore further transport communication + this.transport.removeAllListeners(); + + // set ready state + this.readyState = 'closed'; + + // clear session id + this.id = null; + + // emit close event + this.emit('close', reason, desc); + } + }; + + /** + * Filters upgrades, returning only those matching client transports. + * + * @param {Array} server upgrades + * @api private + * + */ + + Socket.prototype.filterUpgrades = function (upgrades) { + var filteredUpgrades = []; + for (var i = 0, j = upgrades.length; i