From c0f59c15987c9aceb104e3a3eabf95e2035770d1 Mon Sep 17 00:00:00 2001 From: Nan Wang Date: Sat, 5 Dec 2015 12:22:41 +0800 Subject: [PATCH] synchronized to cocos2d-html5#6700f8bb94b2. starting from fresh history. --- .gitignore | 12 + AUTHORS.txt | 288 + Base64Images.js | 32 + CCBoot.js | 343 + CCDebugger.js | 459 + CHANGELOG.txt | 952 ++ README.mdown | 52 + bower.json | 36 + cocos2d/ToMerge/qunit/test-event-system.js | 360 + cocos2d/ToMerge/qunit/test-ticker.js | 39 + cocos2d/ToMerge/qunit/test-time.js | 40 + cocos2d/ToMerge/wrappers/bitmap-font.js | 177 + cocos2d/ToMerge/wrappers/draw-node.js | 17 + cocos2d/ToMerge/wrappers/edit-box.js | 250 + cocos2d/ToMerge/wrappers/index.js | 27 + cocos2d/ToMerge/wrappers/label-ttf.js | 202 + cocos2d/ToMerge/wrappers/layer-color.js | 17 + cocos2d/ToMerge/wrappers/layer.js | 44 + cocos2d/ToMerge/wrappers/progress-timer.js | 167 + cocos2d/ToMerge/wrappers/sprite-batch-node.js | 55 + cocos2d/ToMerge/wrappers/tiled-map.js | 103 + cocos2d/ToMerge/wrappers/ui/button.js | 182 + cocos2d/ToMerge/wrappers/ui/check-box.js | 140 + cocos2d/ToMerge/wrappers/ui/layout.js | 110 + cocos2d/ToMerge/wrappers/ui/list-view.js | 17 + cocos2d/ToMerge/wrappers/ui/loading-bar.js | 85 + cocos2d/ToMerge/wrappers/ui/page-view.js | 44 + cocos2d/ToMerge/wrappers/ui/scale9-sprite.js | 58 + cocos2d/ToMerge/wrappers/ui/scale9.js | 171 + cocos2d/ToMerge/wrappers/ui/scroll-view.js | 99 + cocos2d/ToMerge/wrappers/ui/slider.js | 139 + cocos2d/ToMerge/wrappers/ui/text-atlas.js | 79 + .../ToMerge/wrappers/ui/text-bitmap-font.js | 70 + cocos2d/ToMerge/wrappers/ui/text-field.js | 214 + cocos2d/ToMerge/wrappers/ui/text.js | 178 + cocos2d/ToMerge/wrappers/ui/widget.js | 245 + cocos2d/ToMerge/wrappers/utils.js | 16 + cocos2d/actions/CCAction.js | 694 + cocos2d/actions/CCActionCamera.js | 293 + cocos2d/actions/CCActionCatmullRom.js | 605 + cocos2d/actions/CCActionEase.js | 3681 ++++++ cocos2d/actions/CCActionInstant.js | 749 ++ cocos2d/actions/CCActionInterval.js | 3567 +++++ cocos2d/actions/CCActionTween.js | 166 + cocos2d/actions3d/CCActionGrid.js | 427 + cocos2d/actions3d/CCActionGrid3D.js | 1257 ++ cocos2d/actions3d/CCActionPageTurn3D.js | 130 + cocos2d/actions3d/CCActionTiledGrid.js | 1300 ++ cocos2d/animation/animation-animator.js | 315 + cocos2d/animation/animation-clip.js | 134 + cocos2d/animation/animation-curves.js | 397 + cocos2d/animation/animation-manager.js | 51 + cocos2d/animation/animation-state.js | 71 + cocos2d/animation/animators.js | 197 + cocos2d/animation/bezier.js | 196 + cocos2d/animation/binary-search.js | 28 + cocos2d/animation/easing.js | 257 + cocos2d/animation/index.js | 10 + cocos2d/animation/motion-path-helper.js | 327 + cocos2d/animation/playable.js | 131 + cocos2d/animation/types.js | 364 + cocos2d/audio/CCAudio.js | 1033 ++ cocos2d/clipping-nodes/CCClippingNode.js | 224 + .../CCClippingNodeCanvasRenderCmd.js | 215 + .../CCClippingNodeWebGLRenderCmd.js | 238 + cocos2d/compression/ZipUtils.js | 82 + cocos2d/compression/base64.js | 95 + cocos2d/compression/gzip.js | 731 ++ cocos2d/compression/zlib.min.js | 55 + cocos2d/core/CCActionManager.js | 383 + cocos2d/core/CCCamera.js | 282 + cocos2d/core/CCConfiguration.js | 294 + cocos2d/core/CCDirector.js | 1173 ++ cocos2d/core/CCDirectorCanvas.js | 85 + cocos2d/core/CCDirectorWebGL.js | 270 + cocos2d/core/CCDrawingPrimitivesCanvas.js | 438 + cocos2d/core/CCDrawingPrimitivesWebGL.js | 464 + cocos2d/core/CCGame.js | 603 + cocos2d/core/CCNode.js | 546 + cocos2d/core/CCScene.js | 136 + cocos2d/core/CCScheduler.js | 1065 ++ cocos2d/core/assets/CCAsset.js | 154 + cocos2d/core/assets/CCAudioClip.js | 13 + cocos2d/core/assets/CCBitmapFont.js | 13 + cocos2d/core/assets/CCPrefab.js | 43 + cocos2d/core/assets/CCRawAsset.js | 30 + cocos2d/core/assets/CCSceneAsset.js | 17 + cocos2d/core/assets/CCScripts.js | 38 + cocos2d/core/assets/CCSpriteAnimation.js | 86 + cocos2d/core/assets/CCSpriteAtlas.js | 13 + cocos2d/core/assets/CCTTFFont.js | 19 + cocos2d/core/assets/CCTiledMapAsset.js | 45 + cocos2d/core/assets/index.js | 39 + .../base-nodes/BaseNodesPropertyDefine.js | 127 + cocos2d/core/base-nodes/CCAtlasNode.js | 297 + .../base-nodes/CCAtlasNodeCanvasRenderCmd.js | 91 + .../base-nodes/CCAtlasNodeWebGLRenderCmd.js | 145 + cocos2d/core/base-nodes/CCNode.js | 2251 ++++ .../core/base-nodes/CCNodeCanvasRenderCmd.js | 501 + .../core/base-nodes/CCNodeWebGLRenderCmd.js | 104 + cocos2d/core/base-ui/CCWidgetManager.js | 141 + cocos2d/core/cocos2d_externs.js | 66 + cocos2d/core/components/CCAnimation.js | 433 + cocos2d/core/components/CCAudioSource.js | 219 + cocos2d/core/components/CCButton.js | 496 + cocos2d/core/components/CCCanvas.js | 178 + cocos2d/core/components/CCComponent.js | 716 + cocos2d/core/components/CCComponentInSG.js | 86 + cocos2d/core/components/CCELabel.js | 175 + cocos2d/core/components/CCSpriteRenderer.js | 531 + cocos2d/core/components/CCWidget.js | 423 + cocos2d/core/components/index.js | 12 + cocos2d/core/event-manager/CCEventListener.js | 598 + cocos2d/core/event-manager/CCEventManager.js | 965 ++ cocos2d/core/event-manager/CCSystemEvent.js | 428 + cocos2d/core/event-manager/CCTouch.js | 186 + cocos2d/core/event/event-listeners.js | 55 + cocos2d/core/event/event-target.js | 343 + cocos2d/core/event/event.js | 279 + cocos2d/core/event/index.js | 3 + cocos2d/core/index.js | 11 + cocos2d/core/labelttf/CCLabelTTF.js | 869 ++ .../labelttf/CCLabelTTFCanvasRenderCmd.js | 535 + .../core/labelttf/CCLabelTTFWebGLRenderCmd.js | 39 + .../core/labelttf/LabelTTFPropertyDefine.js | 88 + cocos2d/core/layers/CCLayer.js | 721 + cocos2d/core/layers/CCLayerCanvasRenderCmd.js | 474 + cocos2d/core/layers/CCLayerWebGLRenderCmd.js | 311 + cocos2d/core/platform/CCAssetLibrary.js | 519 + cocos2d/core/platform/CCClass.js | 1036 ++ cocos2d/core/platform/CCCommon.js | 295 + cocos2d/core/platform/CCConfig.js | 312 + cocos2d/core/platform/CCEGLView.js | 1387 ++ cocos2d/core/platform/CCInputExtension.js | 133 + cocos2d/core/platform/CCInputManager.js | 615 + cocos2d/core/platform/CCLoader.js | 583 + cocos2d/core/platform/CCLoaders.js | 156 + cocos2d/core/platform/CCMacro.js | 742 ++ cocos2d/core/platform/CCObject.js | 362 + cocos2d/core/platform/CCSAXParser.js | 167 + cocos2d/core/platform/CCScreen.js | 164 + cocos2d/core/platform/CCSys.js | 675 + cocos2d/core/platform/CCVisibleRect.js | 154 + cocos2d/core/platform/_CCClass.js | 217 + cocos2d/core/platform/attribute.js | 301 + cocos2d/core/platform/callbacks-invoker.js | 245 + cocos2d/core/platform/deserialize.js | 517 + cocos2d/core/platform/index.js | 43 + cocos2d/core/platform/instantiate.js | 243 + cocos2d/core/platform/js.js | 629 + cocos2d/core/platform/load-manager.js | 206 + cocos2d/core/platform/miniFramework.js | 260 + cocos2d/core/platform/prefab-info.js | 28 + cocos2d/core/platform/preprocess-attrs.js | 179 + cocos2d/core/platform/requiring-frame.js | 40 + cocos2d/core/platform/url.js | 32 + cocos2d/core/platform/utils.js | 77 + cocos2d/core/renderer/RendererCanvas.js | 289 + cocos2d/core/renderer/RendererWebGL.js | 154 + cocos2d/core/scenes/CCLoaderScene.js | 167 + cocos2d/core/scenes/CCScene.js | 62 + cocos2d/core/sprites/CCAnimation.js | 477 + cocos2d/core/sprites/CCAnimationCache.js | 213 + cocos2d/core/sprites/CCBakeSprite.js | 78 + cocos2d/core/sprites/CCSprite.js | 1032 ++ cocos2d/core/sprites/CCSpriteBatchNode.js | 676 + .../CCSpriteBatchNodeCanvasRenderCmd.js | 100 + .../CCSpriteBatchNodeWebGLRenderCmd.js | 243 + .../core/sprites/CCSpriteCanvasRenderCmd.js | 313 + cocos2d/core/sprites/CCSpriteFrame.js | 603 + cocos2d/core/sprites/CCSpriteFrameCache.js | 361 + .../core/sprites/CCSpriteWebGLRenderCmd.js | 503 + cocos2d/core/sprites/SpritesPropertyDefine.js | 67 + cocos2d/core/support/CCPointExtension.js | 561 + cocos2d/core/support/CCVertex.js | 170 + cocos2d/core/support/TransformUtils.js | 62 + cocos2d/core/textures/CCTexture2D.js | 1158 ++ cocos2d/core/textures/CCTextureAtlas.js | 733 ++ cocos2d/core/textures/CCTextureCache.js | 393 + cocos2d/core/textures/index.js | 3 + cocos2d/core/utils/Async.js | 181 + cocos2d/core/utils/BinaryLoader.js | 159 + cocos2d/core/utils/CCPath.js | 162 + cocos2d/core/utils/CCProfiler.js | 160 + cocos2d/core/utils/base-node.js | 1365 ++ cocos2d/core/utils/find.js | 66 + cocos2d/core/utils/index.js | 4 + cocos2d/core/utils/misc.js | 47 + cocos2d/core/utils/scene-graph-helper.js | 63 + cocos2d/core/value-types/CCAffineTransform.js | 288 + cocos2d/core/value-types/CCColor.js | 491 + cocos2d/core/value-types/CCEnum.js | 132 + cocos2d/core/value-types/CCRect.js | 416 + cocos2d/core/value-types/CCSize.js | 104 + cocos2d/core/value-types/CCTypes.js | 282 + cocos2d/core/value-types/CCTypesWebGL.js | 662 + cocos2d/core/value-types/CCValueType.js | 57 + cocos2d/core/value-types/CCVec2.js | 483 + cocos2d/core/value-types/index.js | 9 + cocos2d/deprecated.js | 503 + cocos2d/effects/CCGrabber.js | 110 + cocos2d/effects/CCGrid.js | 846 ++ cocos2d/kazmath/SIMDPolyfill.js | 78 + cocos2d/kazmath/aabb.js | 78 + cocos2d/kazmath/gl/mat4stack.js | 98 + cocos2d/kazmath/gl/matrix.js | 167 + cocos2d/kazmath/mat3.js | 344 + cocos2d/kazmath/mat4.js | 1014 ++ cocos2d/kazmath/mat4SIMD.js | 428 + cocos2d/kazmath/plane.js | 137 + cocos2d/kazmath/quaternion.js | 459 + cocos2d/kazmath/ray2.js | 140 + cocos2d/kazmath/simd_benchmark/base.js | 139 + cocos2d/kazmath/simd_benchmark/index.html | 44 + .../kazmath/simd_benchmark/kernel-template.js | 64 + .../kazmath/simd_benchmark/kmMat4AreEqual.js | 77 + .../kazmath/simd_benchmark/kmMat4Assign.js | 68 + .../kazmath/simd_benchmark/kmMat4Inverse.js | 116 + .../simd_benchmark/kmMat4IsIdentity.js | 65 + .../kazmath/simd_benchmark/kmMat4LookAt.js | 94 + .../kazmath/simd_benchmark/kmMat4Multiply.js | 74 + .../kazmath/simd_benchmark/kmMat4Transpose.js | 82 + .../simd_benchmark/kmVec3TransformCoord.js | 65 + cocos2d/kazmath/simd_benchmark/run.js | 40 + cocos2d/kazmath/simd_benchmark/run_browser.js | 80 + cocos2d/kazmath/utility.js | 54 + cocos2d/kazmath/vec2.js | 113 + cocos2d/kazmath/vec3.js | 201 + cocos2d/kazmath/vec3SIMD.js | 51 + cocos2d/kazmath/vec4.js | 158 + cocos2d/labels/CCLabel.js | 251 + cocos2d/labels/CCLabelAtlas.js | 240 + cocos2d/labels/CCLabelAtlasCanvasRenderCmd.js | 110 + cocos2d/labels/CCLabelAtlasWebGLRenderCmd.js | 153 + cocos2d/labels/CCLabelBMFont.js | 971 ++ .../labels/CCLabelBMFontCanvasRenderCmd.js | 107 + cocos2d/labels/CCLabelBMFontWebGLRenderCmd.js | 84 + cocos2d/labels/CCLabelCanvasRenderCmd.js | 338 + cocos2d/labels/CCLabelWebGLRenderCmd.js | 78 + cocos2d/menus/CCMenu.js | 600 + cocos2d/menus/CCMenuItem.js | 1349 ++ cocos2d/motion-streak/CCMotionStreak.js | 533 + .../CCMotionStreakWebGLRenderCmd.js | 66 + cocos2d/node-grid/CCNodeGrid.js | 150 + cocos2d/node-grid/CCNodeGridWebGLRenderCmd.js | 98 + cocos2d/parallax/CCParallaxNode.js | 252 + cocos2d/parallax/CCParallaxNodeRenderCmd.js | 69 + cocos2d/particle/CCEParticleSystem.js | 830 ++ cocos2d/particle/CCPNGReader.js | 330 + cocos2d/particle/CCParticleAsset.js | 12 + cocos2d/particle/CCParticleBatchNode.js | 527 + .../CCParticleBatchNodeCanvasRenderCmd.js | 38 + .../CCParticleBatchNodeWebGLRenderCmd.js | 74 + cocos2d/particle/CCParticleExamples.js | 1006 ++ cocos2d/particle/CCParticleSystem.js | 2241 ++++ .../CCParticleSystemCanvasRenderCmd.js | 199 + .../CCParticleSystemWebGLRenderCmd.js | 401 + cocos2d/particle/CCTIFFReader.js | 692 + cocos2d/physics/CCPhysicsDebugNode.js | 212 + .../CCPhysicsDebugNodeCanvasRenderCmd.js | 52 + .../CCPhysicsDebugNodeWebGLRenderCmd.js | 53 + cocos2d/physics/CCPhysicsSprite.js | 447 + .../physics/CCPhysicsSpriteCanvasRenderCmd.js | 93 + .../physics/CCPhysicsSpriteWebGLRenderCmd.js | 92 + .../progress-timer/CCActionProgressTimer.js | 227 + cocos2d/progress-timer/CCProgressTimer.js | 344 + .../CCProgressTimerCanvasRenderCmd.js | 270 + .../CCProgressTimerWebGLRenderCmd.js | 486 + cocos2d/render-texture/CCRenderTexture.js | 406 + .../CCRenderTextureCanvasRenderCmd.js | 107 + .../CCRenderTextureWebGLRenderCmd.js | 387 + cocos2d/shaders/CCGLProgram.js | 767 ++ cocos2d/shaders/CCGLStateCache.js | 340 + cocos2d/shaders/CCShaderCache.js | 301 + cocos2d/shaders/CCShaders.js | 280 + cocos2d/shape-nodes/CCDrawNode.js | 905 ++ .../shape-nodes/CCDrawNodeCanvasRenderCmd.js | 131 + .../shape-nodes/CCDrawNodeWebGLRenderCmd.js | 41 + cocos2d/text-input/CCIMEDispatcher.js | 534 + cocos2d/text-input/CCTextFieldTTF.js | 500 + cocos2d/tilemap/CCTGAlib.js | 437 + cocos2d/tilemap/CCTMXLayer.js | 914 ++ cocos2d/tilemap/CCTMXLayerCanvasRenderCmd.js | 220 + cocos2d/tilemap/CCTMXLayerWebGLRenderCmd.js | 67 + cocos2d/tilemap/CCTMXObjectGroup.js | 157 + cocos2d/tilemap/CCTMXTiledMap.js | 479 + cocos2d/tilemap/CCTMXXMLParser.js | 950 ++ cocos2d/transitions/CCTransition.js | 1966 +++ cocos2d/transitions/CCTransitionPageTurn.js | 152 + cocos2d/transitions/CCTransitionProgress.js | 458 + .../core/CCActionManager/ActionManager.js | 2 + docs/cocos2d/core/CCScheduler/schedule.js | 3 + .../CCScheduler/scheduleCallbackForTarget.js | 3 + .../CCScheduler/scheduleUpdateForTarget.js | 3 + .../unscheduleCallbackForTarget.js | 3 + .../CCScheduler/unscheduleUpdateForTarget.js | 3 + .../initWithSpriteFrameName.js | 3 + .../event-manager/CCEventListener/create.js | 10 + .../core/event/_getCapturingTargets.js | 8 + docs/cocos2d/core/platform/CCCommon/KEY.js | 9 + docs/cocos2d/core/platform/CCMacro/lerp.js | 4 + docs/cocos2d/core/platform/attribute/attr.js | 10 + docs/cocos2d/core/platform/url/raw.js | 3 + docs/cocos2d/core/sprites/SpriteFrame.js | 9 + docs/cocos2d/core/sprites/addSpriteFrames.js | 4 + docs/cocos2d/core/sprites/getSpriteFrame.js | 3 + docs/cocos2d/core/support/pAdd.js | 4 + docs/cocos2d/core/support/pCompOp.js | 3 + docs/cocos2d/core/textures/TextureAtlas.js | 7 + docs/cocos2d/core/textures/addImage.js | 2 + docs/cocos2d/core/textures/getKeyByTexture.js | 2 + .../cocos2d/core/textures/getTextureColors.js | 2 + .../cocos2d/core/textures/getTextureForKey.js | 2 + docs/cocos2d/core/textures/initWithFile.js | 3 + docs/cocos2d/core/textures/initWithTexture.js | 4 + .../core/textures/removeAllTextures.js | 2 + docs/cocos2d/core/textures/removeTexture.js | 2 + .../core/textures/removeTextureForKey.js | 2 + docs/cocos2d/core/textures/textureForKey.js | 2 + docs/cocos2d/core/utils/CCPath/basename.js | 6 + .../core/utils/CCPath/changeBasename.js | 6 + .../core/utils/CCPath/changeExtname.js | 3 + docs/cocos2d/core/utils/CCPath/dirname.js | 9 + docs/cocos2d/core/utils/CCPath/extname.js | 5 + docs/cocos2d/core/utils/CCPath/join.js | 6 + .../core/utils/node-wrapper/setPosition.js | 3 + .../cocos2d/core/value-types/CCColor/color.js | 7 + docs/cocos2d/core/value-types/CCSize/size.js | 5 + docs/extensions/ccpool/putInPool.js | 5 + editor/dashboard/banner.jpg | Bin 0 -> 4915 bytes editor/dashboard/banner.png | Bin 0 -> 47339 bytes editor/dashboard/logo.png | Bin 0 -> 9390 bytes editor/template/new-scene.fire | 49 + editor/template/new-script.coffee | 16 + editor/template/new-script.js | 25 + extends.js | 43 + extensions/ccb-reader/CCBAnimationManager.js | 768 ++ extensions/ccb-reader/CCBKeyframe.js | 60 + extensions/ccb-reader/CCBReader.js | 1126 ++ extensions/ccb-reader/CCBReaderUtil.js | 61 + .../ccb-reader/CCBRelativePositioning.js | 90 + extensions/ccb-reader/CCBSequence.js | 114 + extensions/ccb-reader/CCBValue.js | 81 + extensions/ccb-reader/CCControlLoader.js | 318 + extensions/ccb-reader/CCNodeLoader.js | 904 ++ extensions/ccb-reader/CCNodeLoaderLibrary.js | 101 + extensions/ccb-reader/CCSpriteLoader.js | 544 + extensions/ccpool/CCPool.js | 141 + .../ccui/base-classes/CCProtectedNode.js | 305 + .../CCProtectedNodeCanvasRenderCmd.js | 240 + .../CCProtectedNodeWebGLRenderCmd.js | 135 + .../ccui/base-classes/UIScale9Sprite.js | 1118 ++ .../UIScale9SpriteCanvasRenderCmd.js | 141 + .../UIScale9SpriteWebGLRenderCmd.js | 180 + extensions/ccui/base-classes/UIWidget.js | 2069 +++ .../ccui/base-classes/UIWidgetRenderCmd.js | 97 + extensions/ccui/layouts/UIHBox.js | 80 + extensions/ccui/layouts/UILayout.js | 1555 +++ .../ccui/layouts/UILayoutCanvasRenderCmd.js | 179 + extensions/ccui/layouts/UILayoutComponent.js | 594 + extensions/ccui/layouts/UILayoutManager.js | 457 + extensions/ccui/layouts/UILayoutParameter.js | 539 + .../ccui/layouts/UILayoutWebGLRenderCmd.js | 242 + extensions/ccui/layouts/UIRelativeBox.js | 79 + extensions/ccui/layouts/UIVBox.js | 79 + extensions/ccui/system/CocosGUI.js | 62 + extensions/ccui/system/UIHelper.js | 164 + extensions/ccui/uiwidgets/UIButton.js | 973 ++ extensions/ccui/uiwidgets/UICheckBox.js | 740 ++ extensions/ccui/uiwidgets/UIImageView.js | 329 + extensions/ccui/uiwidgets/UILoadingBar.js | 443 + extensions/ccui/uiwidgets/UIRichText.js | 742 ++ extensions/ccui/uiwidgets/UISlider.js | 762 ++ extensions/ccui/uiwidgets/UIText.js | 515 + extensions/ccui/uiwidgets/UITextAtlas.js | 229 + extensions/ccui/uiwidgets/UITextBMFont.js | 216 + extensions/ccui/uiwidgets/UITextField.js | 915 ++ extensions/ccui/uiwidgets/UIVideoPlayer.js | 543 + extensions/ccui/uiwidgets/UIWebView.js | 389 + .../uiwidgets/scroll-widget/UIListView.js | 606 + .../uiwidgets/scroll-widget/UIPageView.js | 663 + .../uiwidgets/scroll-widget/UIScrollView.js | 1921 +++ .../UIScrollViewCanvasRenderCmd.js | 43 + .../UIScrollViewWebGLRenderCmd.js | 44 + extensions/cocostudio/CocoStudio.js | 68 + extensions/cocostudio/action/CCActionFrame.js | 530 + .../cocostudio/action/CCActionManager.js | 112 + extensions/cocostudio/action/CCActionNode.js | 421 + .../cocostudio/action/CCActionObject.js | 263 + extensions/cocostudio/armature/CCArmature.js | 578 + .../armature/CCArmatureCanvasRenderCmd.js | 190 + .../armature/CCArmatureWebGLRenderCmd.js | 167 + extensions/cocostudio/armature/CCBone.js | 713 + .../armature/animation/CCArmatureAnimation.js | 669 + .../armature/animation/CCProcessBase.js | 365 + .../cocostudio/armature/animation/CCTween.js | 448 + .../cocostudio/armature/datas/CCDatas.js | 807 ++ .../armature/display/CCBatchNode.js | 114 + .../armature/display/CCDecorativeDisplay.js | 118 + .../armature/display/CCDisplayFactory.js | 217 + .../armature/display/CCDisplayManager.js | 465 + .../cocostudio/armature/display/CCSkin.js | 215 + .../armature/display/CCSkinCanvasRenderCmd.js | 58 + .../armature/display/CCSkinWebGLRenderCmd.js | 146 + .../armature/physics/CCColliderDetector.js | 397 + .../armature/utils/CCArmatureDataManager.js | 325 + .../armature/utils/CCArmatureDefine.js | 45 + .../armature/utils/CCDataReaderHelper.js | 1223 ++ .../utils/CCSpriteFrameCacheHelper.js | 68 + .../armature/utils/CCTransformHelp.js | 183 + .../armature/utils/CCTweenFunction.js | 501 + .../cocostudio/armature/utils/CCUtilMath.js | 69 + .../cocostudio/components/CCComAttribute.js | 210 + .../cocostudio/components/CCComAudio.js | 285 + .../cocostudio/components/CCComController.js | 76 + .../cocostudio/components/CCComRender.js | 91 + .../cocostudio/components/CCComponent.js | 136 + .../components/CCComponentContainer.js | 159 + extensions/cocostudio/loader/load.js | 274 + .../cocostudio/loader/parsers/action-1.x.js | 232 + .../cocostudio/loader/parsers/action-2.x.js | 309 + .../cocostudio/loader/parsers/compatible.js | 251 + .../cocostudio/loader/parsers/scene-1.x.js | 262 + .../loader/parsers/timelineParser-1.x.js | 292 + .../loader/parsers/timelineParser-2.x.js | 1401 ++ .../cocostudio/loader/parsers/uiParser-1.x.js | 698 + .../cocostudio/timeline/ActionTimeline.js | 541 + extensions/cocostudio/timeline/CCBoneNode.js | 589 + .../cocostudio/timeline/CCSkeletonNode.js | 262 + extensions/cocostudio/timeline/CCSkinNode.js | 14 + extensions/cocostudio/timeline/Frame.js | 1427 ++ extensions/cocostudio/timeline/Timeline.js | 336 + .../cocostudio/trigger/ObjectFactory.js | 99 + extensions/cocostudio/trigger/TriggerBase.js | 49 + extensions/cocostudio/trigger/TriggerMng.js | 301 + extensions/cocostudio/trigger/TriggerObj.js | 263 + extensions/editbox/CCEditBox.js | 688 + extensions/editbox/CCdomNode.js | 657 + extensions/gui/control-extension/CCControl.js | 381 + .../gui/control-extension/CCControlButton.js | 688 + .../CCControlColourPicker.js | 187 + .../control-extension/CCControlHuePicker.js | 218 + .../CCControlPotentiometer.js | 300 + .../CCControlSaturationBrightnessPicker.js | 257 + .../gui/control-extension/CCControlSlider.js | 308 + .../gui/control-extension/CCControlStepper.js | 390 + .../gui/control-extension/CCControlSwitch.js | 425 + .../gui/control-extension/CCControlUtils.js | 177 + .../gui/control-extension/CCInvocation.js | 65 + .../gui/control-extension/CCMenuPassive.js | 415 + extensions/gui/scrollview/CCScrollView.js | 826 ++ .../scrollview/CCScrollViewCanvasRenderCmd.js | 81 + .../scrollview/CCScrollViewWebGLRenderCmd.js | 109 + extensions/gui/scrollview/CCSorting.js | 239 + extensions/gui/scrollview/CCTableView.js | 720 + extensions/runtime/CCLoaderLayer.js | 974 ++ extensions/spine/CCSkeleton.js | 407 + extensions/spine/CCSkeletonAnimation.js | 348 + extensions/spine/CCSkeletonCanvasRenderCmd.js | 213 + extensions/spine/CCSkeletonWebGLRenderCmd.js | 260 + extensions/spine/Spine.js | 2636 ++++ external/box2d/box2d.js | 10882 ++++++++++++++++ external/chipmunk/chipmunk.js | 6196 +++++++++ external/gaf/GAFBoot.js | 24 + external/gaf/GAFMacros.js | 33 + external/gaf/Library/GAFAsset.js | 428 + external/gaf/Library/GAFAssetPreload.js | 270 + external/gaf/Library/GAFAtlasLoader.js | 50 + external/gaf/Library/GAFDataReader.js | 229 + external/gaf/Library/GAFLoader.js | 75 + external/gaf/Library/GAFMask.js | 36 + external/gaf/Library/GAFMaskProto.js | 16 + external/gaf/Library/GAFObject.js | 426 + external/gaf/Library/GAFShaderManager.js | 63 + external/gaf/Library/GAFShaders.js | 58 + external/gaf/Library/GAFSprite.js | 100 + .../gaf/Library/GAFSpriteCanvasRenderCmd.js | 233 + external/gaf/Library/GAFSpriteProto.js | 36 + .../gaf/Library/GAFSpriteWebGLRenderCmd.js | 132 + external/gaf/Library/GAFTags.js | 378 + external/gaf/Library/GAFTextField.js | 6 + external/gaf/Library/GAFTimeLine.js | 547 + external/gaf/Library/GAFTimeLineProto.js | 32 + external/gaf/gaf_viewer.css | 42 + external/gaf/gaf_viewer.html | 21 + external/gaf/gaf_viewer.js | 219 + external/pluginx/Plugin.js | 254 + external/pluginx/platform/facebook.js | 557 + external/pluginx/platform/facebook_sdk.js | 151 + external/socketio/socket.io.js | 7000 ++++++++++ external/socketio/socket.io.min.js | 3 + gulp/tasks/build-cocos2d.js | 318 + gulp/tasks/build.js | 210 + gulp/tasks/clean.js | 8 + gulp/tasks/test.js | 36 + gulp/util/handleErrors.js | 27 + gulpfile.js | 43 + index.js | 76 + jsb_apis.js | 618 + jsb_predefine.js | 41 + licenses/LICENSE_cocos2d-html5.txt | 23 + licenses/LICENSE_cocos2d-x.txt | 23 + licenses/LICENSE_zlib.js.txt | 28 + moduleConfig.json | 478 + package.json | 36 + polyfill/bind.js | 29 + polyfill/index.js | 2 + polyfill/string.js | 17 + predefine.js | 69 + test/core.js | 26 + test/fixtures/assets/atlas.plist | 92 + test/fixtures/assets/atlas.png | Bin 0 -> 11766 bytes test/fixtures/assets/bitmap-font.fnt | 206 + test/fixtures/assets/bitmap-font.png | Bin 0 -> 178317 bytes test/fixtures/assets/button.png | Bin 0 -> 11604 bytes test/fixtures/assets/particle.plist | 110 + test/fixtures/assets/sprite.jpg | Bin 0 -> 4915 bytes test/fixtures/library/22/223456/1.ttf | Bin 0 -> 29948 bytes test/fixtures/library/22/223459.json | 5 + test/index.js | 12 + test/lib/init.js | 30 + test/lib/runner.html | 39 + test/lib/spawn-runner.js | 69 + test/page.html | 32 + test/page.js | 31 + test/page/asset.js | 32 + test/page/bitmap-font.js | 120 + test/page/button.js | 103 + test/page/engine.js | 104 + test/page/index.js | 75 + test/page/label-ttf.js | 93 + test/page/node.js | 322 + test/page/particle.js | 287 + test/page/scale9-sprite.js | 62 + test/page/scene.js | 128 + test/page/sprite.js | 98 + test/qunit/assets/background.mp3 | Bin 0 -> 128313 bytes test/qunit/assets/button.png | Bin 0 -> 11604 bytes test/qunit/assets/library/12/123200.json | 16 + test/qunit/assets/library/12/1232218.json | 16 + test/qunit/assets/library/19/19851210.json | 71 + test/qunit/assets/library/65/65341123490 | 16 + test/qunit/assets/library/65/6545543 | 6 + test/qunit/assets/library/65/6545543.png | Bin 0 -> 265815 bytes test/qunit/assets/library/74/748321.json | 6 + test/qunit/assets/library/74/748321.json.host | Bin 0 -> 1521 bytes .../library/deferred-loading/12/1232218.json | 17 + .../library/deferred-loading/74/748321.json | 7 + test/qunit/lib/assert-deep-close.js | 28 + test/qunit/lib/qunit-assert-callback.js | 170 + test/qunit/lib/qunit-assert-close.js | 106 + test/qunit/lib/qunit-large-module.js | 8 + test/qunit/lib/qunit-runner.html | 46 + test/qunit/lib/qunit.css | 244 + test/qunit/lib/qunit.js | 2212 ++++ test/qunit/run-helper.sh | 3 + test/qunit/run.sh | 6 + test/qunit/server.js | 39 + test/qunit/unit/_callback-tester.js | 147 + test/qunit/unit/_init.js | 231 + test/qunit/unit/_polyfill-for-phantom.js | 18 + test/qunit/unit/test-animation.js | 890 ++ test/qunit/unit/test-asset-library.js | 84 + test/qunit/unit/test-attribute.js | 78 + test/qunit/unit/test-audioSource.js | 25 + test/qunit/unit/test-callbacks-invoker.js | 170 + test/qunit/unit/test-class.js | 485 + test/qunit/unit/test-color.js | 20 + test/qunit/unit/test-component.js | 174 + test/qunit/unit/test-deserialize.js | 320 + test/qunit/unit/test-instantiate.js | 244 + test/qunit/unit/test-js.js | 36 + test/qunit/unit/test-load-manager.js | 68 + test/qunit/unit/test-load-scene.js | 49 + test/qunit/unit/test-node-serialization.js | 145 + test/qunit/unit/test-node.js | 394 + test/qunit/unit/test-object.js | 167 + test/qunit/unit/test-prefab.js | 183 + test/qunit/unit/test-record-object.js | 17 + .../unit/test-serialize-missing-script.js | 51 + test/qunit/unit/test-serialize.js | 458 + test/qunit/unit/test-spriteRenderer.js | 29 + test/qunit/unit/test-utils.js | 42 + test/qunit/unit/test-vec2.js | 36 + test/test-env-page.js | 13 + test/test-env.js | 17 + test/visual-tests/.cocos-project.json | 58 + test/visual-tests/index.html | 22 + test/visual-tests/main.js | 215 + test/visual-tests/res/.gitignore | 2 + test/visual-tests/res/Hello.png | Bin 0 -> 138777 bytes .../visual-tests/res/Images/BoilingFoam.plist | 96 + test/visual-tests/res/Images/Comet.png | Bin 0 -> 2248 bytes test/visual-tests/res/Images/CyanSquare.png | Bin 0 -> 6576 bytes test/visual-tests/res/Images/CyanTriangle.png | Bin 0 -> 7250 bytes test/visual-tests/res/Images/ETC1.pkm | Bin 0 -> 32784 bytes test/visual-tests/res/Images/Fog.png | Bin 0 -> 48840 bytes test/visual-tests/res/Images/HelloWorld.png | Bin 0 -> 137663 bytes test/visual-tests/res/Images/Icon.png | Bin 0 -> 6259 bytes .../visual-tests/res/Images/MagentaSquare.png | Bin 0 -> 6714 bytes test/visual-tests/res/Images/Pea.png | Bin 0 -> 3062 bytes .../res/Images/SendScoreButton.png | Bin 0 -> 3876 bytes .../res/Images/SendScoreButtonPressed.png | Bin 0 -> 4460 bytes test/visual-tests/res/Images/SpinningPeas.png | Bin 0 -> 2495 bytes test/visual-tests/res/Images/SpookyPeas.png | Bin 0 -> 2495 bytes test/visual-tests/res/Images/YellowSquare.png | Bin 0 -> 4104 bytes .../res/Images/YellowTriangle.png | Bin 0 -> 6222 bytes test/visual-tests/res/Images/arrows-hd.png | Bin 0 -> 3319 bytes test/visual-tests/res/Images/arrows.png | Bin 0 -> 315 bytes test/visual-tests/res/Images/arrowsBar-hd.png | Bin 0 -> 2844 bytes test/visual-tests/res/Images/arrowsBar.png | Bin 0 -> 121 bytes .../res/Images/assetMgrBackground1.jpg | Bin 0 -> 33193 bytes .../res/Images/assetMgrBackground2.png | Bin 0 -> 18080 bytes .../res/Images/assetMgrBackground3.png | Bin 0 -> 796 bytes test/visual-tests/res/Images/atlastest.png | Bin 0 -> 168995 bytes test/visual-tests/res/Images/b1.png | Bin 0 -> 3845 bytes test/visual-tests/res/Images/b2.png | Bin 0 -> 4038 bytes test/visual-tests/res/Images/background.png | Bin 0 -> 18080 bytes test/visual-tests/res/Images/background1.jpg | Bin 0 -> 33193 bytes test/visual-tests/res/Images/background1.png | Bin 0 -> 125988 bytes test/visual-tests/res/Images/background2.jpg | Bin 0 -> 113801 bytes test/visual-tests/res/Images/background2.png | Bin 0 -> 125210 bytes test/visual-tests/res/Images/background3.jpg | Bin 0 -> 7582 bytes test/visual-tests/res/Images/background3.png | Bin 0 -> 796 bytes test/visual-tests/res/Images/ball.png | Bin 0 -> 776 bytes .../res/Images/bitmapFontTest3.fnt | 102 + .../res/Images/bitmapFontTest3.png | Bin 0 -> 66059 bytes test/visual-tests/res/Images/blocks.png | Bin 0 -> 891 bytes test/visual-tests/res/Images/blocks9.png | Bin 0 -> 470 bytes test/visual-tests/res/Images/blocks9r.png | Bin 0 -> 518 bytes test/visual-tests/res/Images/blocks9ss.plist | 113 + test/visual-tests/res/Images/blocks9ss.png | Bin 0 -> 2188 bytes test/visual-tests/res/Images/blocks9ss.tps | Bin 0 -> 2269 bytes .../res/Images/btn-about-normal-vertical.png | Bin 0 -> 3091 bytes .../res/Images/btn-about-normal.png | Bin 0 -> 3069 bytes .../res/Images/btn-about-selected.png | Bin 0 -> 2982 bytes .../res/Images/btn-highscores-normal.png | Bin 0 -> 4228 bytes .../res/Images/btn-highscores-selected.png | Bin 0 -> 4162 bytes .../res/Images/btn-play-normal.png | Bin 0 -> 2703 bytes .../res/Images/btn-play-selected.png | Bin 0 -> 2653 bytes .../res/Images/bug12847_sprite.png | Bin 0 -> 4282 bytes .../res/Images/bug12847_spriteframe.plist | 35 + .../res/Images/bug12847_spriteframe.png | Bin 0 -> 4305 bytes .../res/Images/bugs/RetinaDisplay.jpg | Bin 0 -> 63922 bytes test/visual-tests/res/Images/bugs/bug886.jpg | Bin 0 -> 31737 bytes test/visual-tests/res/Images/bugs/bug886.png | Bin 0 -> 220924 bytes .../visual-tests/res/Images/bugs/circle.plist | 31 + test/visual-tests/res/Images/bugs/circle.png | Bin 0 -> 1398 bytes test/visual-tests/res/Images/bugs/corner.png | Bin 0 -> 462 bytes test/visual-tests/res/Images/bugs/edge.png | Bin 0 -> 108 bytes test/visual-tests/res/Images/bugs/fill.png | Bin 0 -> 97 bytes test/visual-tests/res/Images/bugs/picture.png | Bin 0 -> 2137 bytes test/visual-tests/res/Images/close.png | Bin 0 -> 2676 bytes test/visual-tests/res/Images/cocos-html5.png | Bin 0 -> 54970 bytes .../visual-tests/res/Images/cocos2dbanner.png | Bin 0 -> 19837 bytes test/visual-tests/res/Images/dot.png | Bin 0 -> 2276 bytes .../res/Images/elephant1_Diffuse.png | Bin 0 -> 2278 bytes .../res/Images/elephant1_Normal.png | Bin 0 -> 19736 bytes .../res/Images/encryptedAtlas.plist | 35 + .../res/Images/encryptedAtlas.pvr.ccz | Bin 0 -> 40197 bytes .../res/Images/encryptedAtlas.tps | 162 + test/visual-tests/res/Images/f1.png | Bin 0 -> 3891 bytes test/visual-tests/res/Images/f2.png | Bin 0 -> 4050 bytes test/visual-tests/res/Images/favicon.ico | Bin 0 -> 1029 bytes .../res/Images/fire-grayscale.png | Bin 0 -> 800 bytes test/visual-tests/res/Images/fire.png | Bin 0 -> 622 bytes .../visual-tests/res/Images/fire_rgba8888.pvr | Bin 0 -> 4148 bytes test/visual-tests/res/Images/fps_images.png | Bin 0 -> 6203 bytes test/visual-tests/res/Images/grossini.png | Bin 0 -> 758 bytes .../res/Images/grossini_128x256_mipmap.pvr | Bin 0 -> 87434 bytes .../res/Images/grossini_dance_01.png | Bin 0 -> 463 bytes .../res/Images/grossini_dance_02.png | Bin 0 -> 490 bytes .../res/Images/grossini_dance_03.png | Bin 0 -> 500 bytes .../res/Images/grossini_dance_04.png | Bin 0 -> 510 bytes .../res/Images/grossini_dance_05.png | Bin 0 -> 777 bytes .../res/Images/grossini_dance_06.png | Bin 0 -> 766 bytes .../res/Images/grossini_dance_07.png | Bin 0 -> 786 bytes .../res/Images/grossini_dance_08.png | Bin 0 -> 758 bytes .../res/Images/grossini_dance_09.png | Bin 0 -> 777 bytes .../res/Images/grossini_dance_10.png | Bin 0 -> 762 bytes .../res/Images/grossini_dance_11.png | Bin 0 -> 480 bytes .../res/Images/grossini_dance_12.png | Bin 0 -> 749 bytes .../res/Images/grossini_dance_13.png | Bin 0 -> 750 bytes .../res/Images/grossini_dance_14.png | Bin 0 -> 751 bytes .../res/Images/grossini_dance_atlas-mono.png | Bin 0 -> 166948 bytes .../res/Images/grossini_dance_atlas.png | Bin 0 -> 4676 bytes .../Images/grossini_dance_atlas_nomipmap.png | Bin 0 -> 4676 bytes .../res/Images/grossini_pvr_rgba4444.pvr | Bin 0 -> 20622 bytes .../res/Images/grossini_pvr_rgba8888.pvr | Bin 0 -> 41192 bytes .../Images/grossinis_sister1-testalpha.png | Bin 0 -> 2688 bytes .../Images/grossinis_sister1-testalpha.ppng | Bin 0 -> 3501 bytes .../grossinis_sister1-testalpha_nopremult.pvr | Bin 0 -> 41652 bytes .../grossinis_sister1-testalpha_premult.pvr | Bin 0 -> 41652 bytes .../res/Images/grossinis_sister1.png | Bin 0 -> 1207 bytes .../res/Images/grossinis_sister2.png | Bin 0 -> 873 bytes .../res/Images/heightfield64x64.raw | Bin 0 -> 4096 bytes test/visual-tests/res/Images/hole_effect.png | Bin 0 -> 2605 bytes test/visual-tests/res/Images/hole_stencil.png | Bin 0 -> 254 bytes test/visual-tests/res/Images/labelatlas.png | Bin 0 -> 6308 bytes test/visual-tests/res/Images/logo-mipmap.pvr | Bin 0 -> 43828 bytes .../visual-tests/res/Images/logo-nomipmap.pvr | Bin 0 -> 32820 bytes .../res/Images/lookup-desktop.plist | 16 + .../res/Images/lookup-html5.plist | 16 + .../res/Images/lookup-mobile.plist | 16 + .../res/Images/menuitemsprite.png | Bin 0 -> 9662 bytes test/visual-tests/res/Images/movement.png | Bin 0 -> 13369 bytes test/visual-tests/res/Images/noise.png | Bin 0 -> 262848 bytes .../res/Images/nonencryptedAtlas.plist | 35 + .../res/Images/nonencryptedAtlas.pvr.ccz | Bin 0 -> 7232 bytes .../res/Images/nonencryptedAtlas.tps | 162 + test/visual-tests/res/Images/paddle.png | Bin 0 -> 1548 bytes test/visual-tests/res/Images/particles-hd.png | Bin 0 -> 4527 bytes test/visual-tests/res/Images/particles.png | Bin 0 -> 3269 bytes test/visual-tests/res/Images/pattern1.png | Bin 0 -> 9957 bytes test/visual-tests/res/Images/piece.png | Bin 0 -> 9529 bytes test/visual-tests/res/Images/powered.png | Bin 0 -> 42732 bytes test/visual-tests/res/Images/r1.png | Bin 0 -> 1939 bytes test/visual-tests/res/Images/r2.png | Bin 0 -> 1808 bytes test/visual-tests/res/Images/shapemode.png | Bin 0 -> 8304 bytes test/visual-tests/res/Images/snow.png | Bin 0 -> 1534 bytes .../res/Images/sprites_test/sprite-0-0.png | Bin 0 -> 2131 bytes .../res/Images/sprites_test/sprite-0-1.png | Bin 0 -> 1771 bytes .../res/Images/sprites_test/sprite-0-2.png | Bin 0 -> 2640 bytes .../res/Images/sprites_test/sprite-0-3.png | Bin 0 -> 2698 bytes .../res/Images/sprites_test/sprite-0-4.png | Bin 0 -> 3048 bytes .../res/Images/sprites_test/sprite-0-5.png | Bin 0 -> 2707 bytes .../res/Images/sprites_test/sprite-0-6.png | Bin 0 -> 2967 bytes .../res/Images/sprites_test/sprite-0-7.png | Bin 0 -> 3019 bytes .../res/Images/sprites_test/sprite-1-0.png | Bin 0 -> 2240 bytes .../res/Images/sprites_test/sprite-1-1.png | Bin 0 -> 1823 bytes .../res/Images/sprites_test/sprite-1-2.png | Bin 0 -> 3243 bytes .../res/Images/sprites_test/sprite-1-3.png | Bin 0 -> 3252 bytes .../res/Images/sprites_test/sprite-1-4.png | Bin 0 -> 1976 bytes .../res/Images/sprites_test/sprite-1-5.png | Bin 0 -> 2866 bytes .../res/Images/sprites_test/sprite-1-6.png | Bin 0 -> 1984 bytes .../res/Images/sprites_test/sprite-1-7.png | Bin 0 -> 2705 bytes .../res/Images/sprites_test/sprite-2-0.png | Bin 0 -> 3379 bytes .../res/Images/sprites_test/sprite-2-1.png | Bin 0 -> 3325 bytes .../res/Images/sprites_test/sprite-2-2.png | Bin 0 -> 2476 bytes .../res/Images/sprites_test/sprite-2-3.png | Bin 0 -> 2084 bytes .../res/Images/sprites_test/sprite-2-4.png | Bin 0 -> 2883 bytes .../res/Images/sprites_test/sprite-2-5.png | Bin 0 -> 2331 bytes .../res/Images/sprites_test/sprite-2-6.png | Bin 0 -> 3008 bytes .../res/Images/sprites_test/sprite-2-7.png | Bin 0 -> 2724 bytes .../res/Images/sprites_test/sprite-3-0.png | Bin 0 -> 2485 bytes .../res/Images/sprites_test/sprite-3-1.png | Bin 0 -> 2067 bytes .../res/Images/sprites_test/sprite-3-2.png | Bin 0 -> 2879 bytes .../res/Images/sprites_test/sprite-3-3.png | Bin 0 -> 2774 bytes .../res/Images/sprites_test/sprite-3-4.png | Bin 0 -> 2477 bytes .../res/Images/sprites_test/sprite-3-5.png | Bin 0 -> 2825 bytes .../res/Images/sprites_test/sprite-3-6.png | Bin 0 -> 2941 bytes .../res/Images/sprites_test/sprite-3-7.png | Bin 0 -> 2400 bytes .../res/Images/sprites_test/sprite-4-0.png | Bin 0 -> 2893 bytes .../res/Images/sprites_test/sprite-4-1.png | Bin 0 -> 2835 bytes .../res/Images/sprites_test/sprite-4-2.png | Bin 0 -> 1983 bytes .../res/Images/sprites_test/sprite-4-3.png | Bin 0 -> 2444 bytes .../res/Images/sprites_test/sprite-4-4.png | Bin 0 -> 3203 bytes .../res/Images/sprites_test/sprite-4-5.png | Bin 0 -> 3398 bytes .../res/Images/sprites_test/sprite-4-6.png | Bin 0 -> 2879 bytes .../res/Images/sprites_test/sprite-4-7.png | Bin 0 -> 3214 bytes .../res/Images/sprites_test/sprite-5-0.png | Bin 0 -> 3351 bytes .../res/Images/sprites_test/sprite-5-1.png | Bin 0 -> 3129 bytes .../res/Images/sprites_test/sprite-5-2.png | Bin 0 -> 2540 bytes .../res/Images/sprites_test/sprite-5-3.png | Bin 0 -> 3239 bytes .../res/Images/sprites_test/sprite-5-4.png | Bin 0 -> 2616 bytes .../res/Images/sprites_test/sprite-5-5.png | Bin 0 -> 2363 bytes .../res/Images/sprites_test/sprite-5-6.png | Bin 0 -> 3385 bytes .../res/Images/sprites_test/sprite-5-7.png | Bin 0 -> 2230 bytes .../res/Images/sprites_test/sprite-6-0.png | Bin 0 -> 2183 bytes .../res/Images/sprites_test/sprite-6-1.png | Bin 0 -> 2093 bytes .../res/Images/sprites_test/sprite-6-2.png | Bin 0 -> 3258 bytes .../res/Images/sprites_test/sprite-6-3.png | Bin 0 -> 1799 bytes .../res/Images/sprites_test/sprite-6-4.png | Bin 0 -> 2999 bytes .../res/Images/sprites_test/sprite-6-5.png | Bin 0 -> 3138 bytes .../res/Images/sprites_test/sprite-6-6.png | Bin 0 -> 2525 bytes .../res/Images/sprites_test/sprite-6-7.png | Bin 0 -> 3413 bytes .../res/Images/sprites_test/sprite-7-0.png | Bin 0 -> 2867 bytes .../res/Images/sprites_test/sprite-7-1.png | Bin 0 -> 2951 bytes .../res/Images/sprites_test/sprite-7-2.png | Bin 0 -> 2158 bytes .../res/Images/sprites_test/sprite-7-3.png | Bin 0 -> 2471 bytes .../res/Images/sprites_test/sprite-7-4.png | Bin 0 -> 2290 bytes .../res/Images/sprites_test/sprite-7-5.png | Bin 0 -> 3211 bytes .../res/Images/sprites_test/sprite-7-6.png | Bin 0 -> 3513 bytes .../res/Images/sprites_test/sprite-7-7.png | Bin 0 -> 2973 bytes .../res/Images/stars-grayscale.png | Bin 0 -> 1080 bytes test/visual-tests/res/Images/stars.png | Bin 0 -> 1080 bytes .../res/Images/stars2-grayscale.png | Bin 0 -> 2066 bytes test/visual-tests/res/Images/stars2.png | Bin 0 -> 2066 bytes test/visual-tests/res/Images/stone.png | Bin 0 -> 105975 bytes test/visual-tests/res/Images/streak.png | Bin 0 -> 1357 bytes test/visual-tests/res/Images/test-rgba1.png | Bin 0 -> 23848 bytes .../res/Images/test_1021x1024_a8.pvr.gz | Bin 0 -> 86117 bytes ...test_256x256_ATC_RGBA_Explicit_mipmaps.ktx | Bin 0 -> 87508 bytes ..._256x256_ATC_RGBA_Interpolated_mipmaps.ktx | Bin 0 -> 87508 bytes .../Images/test_256x256_ATC_RGB_mipmaps.ktx | Bin 0 -> 43804 bytes .../Images/test_256x256_s3tc_dxt1_mipmaps.dds | Bin 0 -> 43832 bytes .../Images/test_256x256_s3tc_dxt3_mipmaps.dds | Bin 0 -> 87536 bytes .../Images/test_256x256_s3tc_dxt5_mipmaps.dds | Bin 0 -> 87536 bytes ...test_512x512_s3tc_dxt5_with_no_mipmaps.dds | Bin 0 -> 262272 bytes test/visual-tests/res/Images/test_blend.png | Bin 0 -> 4261 bytes .../res/Images/test_image-bad_encoding.pvr | Bin 0 -> 65588 bytes test/visual-tests/res/Images/test_image.jpeg | Bin 0 -> 7544 bytes test/visual-tests/res/Images/test_image.png | Bin 0 -> 10114 bytes test/visual-tests/res/Images/test_image.pvr | Bin 0 -> 8244 bytes .../visual-tests/res/Images/test_image.pvrraw | Bin 0 -> 8192 bytes test/visual-tests/res/Images/test_image.tiff | Bin 0 -> 65906 bytes test/visual-tests/res/Images/test_image.webp | Bin 0 -> 6720 bytes .../visual-tests/res/Images/test_image_a8.pvr | Bin 0 -> 16436 bytes .../res/Images/test_image_a8_v3.pvr | Bin 0 -> 16436 bytes .../res/Images/test_image_ai88.png | Bin 0 -> 126 bytes .../res/Images/test_image_ai88.pvr | Bin 0 -> 32820 bytes .../res/Images/test_image_ai88_v3.pvr | Bin 0 -> 32820 bytes .../res/Images/test_image_bgra8888.pvr | Bin 0 -> 65588 bytes .../res/Images/test_image_bgra8888_v3.pvr | Bin 0 -> 65588 bytes .../visual-tests/res/Images/test_image_i8.png | Bin 0 -> 730 bytes .../visual-tests/res/Images/test_image_i8.pvr | Bin 0 -> 16436 bytes .../res/Images/test_image_i8_v3.pvr | Bin 0 -> 16436 bytes .../res/Images/test_image_pvrtc2bpp.pvr | Bin 0 -> 4148 bytes .../res/Images/test_image_pvrtc2bpp_v3.pvr | Bin 0 -> 4148 bytes .../res/Images/test_image_pvrtc4bpp.pvr | Bin 0 -> 8244 bytes .../res/Images/test_image_pvrtc4bpp_v3.pvr | Bin 0 -> 8244 bytes .../res/Images/test_image_pvrtcii2bpp_v3.pvr | Bin 0 -> 5532 bytes .../res/Images/test_image_pvrtcii4bpp_v3.pvr | Bin 0 -> 10988 bytes .../res/Images/test_image_rgb565.pvr | Bin 0 -> 32820 bytes .../res/Images/test_image_rgb565_v3.pvr | Bin 0 -> 43742 bytes .../res/Images/test_image_rgb888.png | Bin 0 -> 145 bytes .../res/Images/test_image_rgb888.pvr | Bin 0 -> 49204 bytes .../res/Images/test_image_rgb888_v3.pvr | Bin 0 -> 65587 bytes .../res/Images/test_image_rgba4444.pvr | Bin 0 -> 32820 bytes .../res/Images/test_image_rgba4444.pvr.ccz | Bin 0 -> 23792 bytes .../res/Images/test_image_rgba4444.pvr.gz | Bin 0 -> 23805 bytes .../res/Images/test_image_rgba4444_mipmap.pvr | Bin 0 -> 43742 bytes .../res/Images/test_image_rgba4444_v3.pvr | Bin 0 -> 43742 bytes .../res/Images/test_image_rgba5551.pvr | Bin 0 -> 32820 bytes .../res/Images/test_image_rgba5551_v3.pvr | Bin 0 -> 43742 bytes .../res/Images/test_image_rgba8888.png | Bin 0 -> 182 bytes .../res/Images/test_image_rgba8888.pvr | Bin 0 -> 65588 bytes .../res/Images/test_image_rgba8888_v3.pvr | Bin 0 -> 65588 bytes .../res/Images/texture1024x1024.png | Bin 0 -> 222 bytes .../res/Images/texture2048x2048.png | Bin 0 -> 605 bytes .../res/Images/texture512x512.png | Bin 0 -> 126 bytes test/visual-tests/res/Images/texturemode.png | Bin 0 -> 8002 bytes test/visual-tests/res/Images/ui.plist | 139 + test/visual-tests/res/Images/ui.png | Bin 0 -> 7292 bytes test/visual-tests/res/Images/water_2_dxt1.dds | Bin 0 -> 22024 bytes test/visual-tests/res/Images/water_2_dxt3.dds | Bin 0 -> 43920 bytes test/visual-tests/res/Images/water_2_dxt5.dds | Bin 0 -> 43920 bytes .../visual-tests/res/Images/white-512x512.png | Bin 0 -> 126 bytes test/visual-tests/res/Images/wood.jpg | Bin 0 -> 2138 bytes .../Manifests/AMTestScene1/project.manifest | 16 + .../Manifests/AMTestScene2/project.manifest | 15 + .../Manifests/AMTestScene3/project.manifest | 16 + .../Manifests/AMTestScene4/project.manifest | 16 + .../res/Manifests/ScriptTest/project.manifest | 13 + .../res/Particles/BoilingFoam.plist | 102 + .../res/Particles/BoilingFoamStar.plist | 110 + .../res/Particles/BurstPipe.plist | 102 + .../res/Particles/ButterFly.plist | 120 + .../res/Particles/ButterFlyYFlipped.plist | 120 + test/visual-tests/res/Particles/Comet.plist | 102 + .../res/Particles/ExplodingRing.plist | 102 + test/visual-tests/res/Particles/Flower.plist | 102 + test/visual-tests/res/Particles/Galaxy.plist | 102 + .../visual-tests/res/Particles/LavaFlow.plist | 102 + test/visual-tests/res/Particles/Phoenix.plist | 102 + .../visual-tests/res/Particles/SmallSun.plist | 110 + .../res/Particles/SpinningPeas.plist | 110 + test/visual-tests/res/Particles/Spiral.plist | 102 + .../res/Particles/SpookyPeas.plist | 102 + .../Particles/TestPremultipliedAlpha.plist | 110 + .../res/Particles/Upsidedown.plist | 102 + test/visual-tests/res/Particles/debian.plist | 104 + test/visual-tests/res/Particles/lines.plist | 118 + .../res/Shaders/example_3D_PositionTex.fsh | 12 + .../res/Shaders/example_3D_PositionTex.vsh | 11 + .../res/Shaders/example_Bloom.fsh | 54 + .../visual-tests/res/Shaders/example_Blur.fsh | 46 + .../res/Shaders/example_Blur_winrt.fsh | 36 + .../res/Shaders/example_CelShading.fsh | 63 + .../res/Shaders/example_ColorBars.fsh | 43 + .../res/Shaders/example_ColorBars.vsh | 16 + .../res/Shaders/example_EdgeDetection.fsh | 41 + .../res/Shaders/example_Flower.fsh | 34 + .../res/Shaders/example_Flower.vsh | 8 + .../res/Shaders/example_GreyScale.fsh | 13 + .../res/Shaders/example_Heart.fsh | 33 + .../res/Shaders/example_Heart.vsh | 8 + .../res/Shaders/example_HorizontalColor.fsh | 27 + .../res/Shaders/example_Julia.fsh | 29 + .../res/Shaders/example_Julia.vsh | 8 + .../res/Shaders/example_LensFlare.fsh | 100 + .../res/Shaders/example_Mandelbrot.fsh | 41 + .../res/Shaders/example_Mandelbrot.vsh | 8 + .../res/Shaders/example_Monjori.fsh | 35 + .../res/Shaders/example_Monjori.vsh | 8 + .../res/Shaders/example_MultiTexture.fsh | 17 + .../res/Shaders/example_MultiTexture.vsh | 19 + .../res/Shaders/example_Noisy.fsh | 26 + .../res/Shaders/example_Normal.fsh | 11 + .../res/Shaders/example_Outline.fsh | 27 + .../res/Shaders/example_Outline.vsh | 13 + .../res/Shaders/example_Outline_noMVP.vsh | 13 + .../res/Shaders/example_Plasma.fsh | 22 + .../res/Shaders/example_Plasma.vsh | 8 + .../res/Shaders/example_Sepia.fsh | 17 + .../res/Shaders/example_Simple.vsh | 18 + .../res/Shaders/example_Twist.fsh | 26 + .../res/Shaders/example_Twist.vsh | 8 + .../res/Shaders/shadertoy_FireBall.fsh | 55 + .../res/Shaders/shadertoy_Glow.fsh | 108 + .../res/Shaders/shadertoy_LensFlare.fsh | 100 + test/visual-tests/res/Test.html | 5 + .../res/TileMaps/fixed-ortho-test2.png | Bin 0 -> 50336 bytes test/visual-tests/res/TileMaps/grass.png | Bin 0 -> 14449 bytes test/visual-tests/res/TileMaps/grass01.png | Bin 0 -> 64329 bytes test/visual-tests/res/TileMaps/hexa-test.tmx | 12 + test/visual-tests/res/TileMaps/hexa-tiles.png | Bin 0 -> 271978 bytes .../res/TileMaps/iso-test-bug787.tmx | 17 + .../res/TileMaps/iso-test-movelayer.tmx | 27 + .../res/TileMaps/iso-test-objectgroup.tmx | 28 + .../res/TileMaps/iso-test-vertexz.tmx | 23 + .../res/TileMaps/iso-test-zorder.tmx | 26 + test/visual-tests/res/TileMaps/iso-test.png | Bin 0 -> 14572 bytes test/visual-tests/res/TileMaps/iso-test.tmx | 16 + test/visual-tests/res/TileMaps/iso-test1.tmx | 21 + .../res/TileMaps/iso-test2-uncompressed.tmx | 17 + test/visual-tests/res/TileMaps/iso-test2.png | Bin 0 -> 3478 bytes test/visual-tests/res/TileMaps/iso-test2.tmx | 17 + test/visual-tests/res/TileMaps/iso.png | Bin 0 -> 1158 bytes test/visual-tests/res/TileMaps/levelmap.tga | Bin 0 -> 5808 bytes .../res/TileMaps/ortho-objects.tmx | 28 + .../res/TileMaps/ortho-rotation-test.tmx | 23 + .../visual-tests/res/TileMaps/ortho-test1.png | Bin 0 -> 459304 bytes .../res/TileMaps/ortho-test1_bw.png | Bin 0 -> 119558 bytes .../visual-tests/res/TileMaps/ortho-test2.png | Bin 0 -> 46116 bytes .../res/TileMaps/ortho-tile-property.tmx | 46 + .../TileMaps/orthogonal-test-movelayer.tmx | 27 + .../res/TileMaps/orthogonal-test-vertexz.tmx | 21 + .../res/TileMaps/orthogonal-test-zorder.tmx | 26 + .../res/TileMaps/orthogonal-test1.tmx | 9 + .../res/TileMaps/orthogonal-test1.tsx | 4 + .../res/TileMaps/orthogonal-test2.tmx | 11 + .../res/TileMaps/orthogonal-test3.tmx | 13 + .../res/TileMaps/orthogonal-test4-hd.tmx | 11 + .../res/TileMaps/orthogonal-test4.tmx | 11 + .../res/TileMaps/orthogonal-test5.tmx | 24 + .../res/TileMaps/orthogonal-test6-hd.tmx | 11 + .../res/TileMaps/orthogonal-test6.tmx | 11 + .../res/TileMaps/test-object-layer.tmx | 18 + .../res/TileMaps/test-staggered.tmx | 19 + .../res/TileMaps/tile_iso_offset.png | Bin 0 -> 34465 bytes .../res/TileMaps/tile_iso_offset.tmx | 37 + test/visual-tests/res/TileMaps/tiles-hd.png | Bin 0 -> 23618 bytes test/visual-tests/res/TileMaps/tiles.png | Bin 0 -> 28567 bytes .../res/TileMaps/tmw_desert_spacing-hd.png | Bin 0 -> 47941 bytes .../res/TileMaps/tmw_desert_spacing.png | Bin 0 -> 46318 bytes test/visual-tests/res/TileMaps/xml-test.tmx | 152 + test/visual-tests/res/TileMaps/xml-test.tsx | 19 + .../res/animations/animations-2.plist | 133 + .../res/animations/animations.plist | 65 + .../res/animations/crystals.plist | 174 + test/visual-tests/res/animations/crystals.png | Bin 0 -> 26684 bytes .../res/animations/dragon_animation-hd.png | Bin 0 -> 26685 bytes .../res/animations/dragon_animation.png | Bin 0 -> 12208 bytes test/visual-tests/res/animations/ghosts.plist | 92 + test/visual-tests/res/animations/ghosts.png | Bin 0 -> 11766 bytes .../res/animations/grossini-aliases.plist | 254 + .../res/animations/grossini-aliases.png | Bin 0 -> 14188 bytes .../res/animations/grossini.plist | 282 + .../res/animations/grossini.plist.xml | 282 + test/visual-tests/res/animations/grossini.png | Bin 0 -> 6493 bytes .../res/animations/grossini.pvr.gz | Bin 0 -> 5704 bytes test/visual-tests/res/animations/grossini.zss | Bin 0 -> 29755 bytes test/visual-tests/res/animations/grossini.ztp | Bin 0 -> 17283 bytes .../res/animations/grossini_blue.plist | 92 + .../res/animations/grossini_blue.png | Bin 0 -> 5381 bytes .../res/animations/grossini_family.plist | 73 + .../res/animations/grossini_family.png | Bin 0 -> 7423 bytes .../res/animations/grossini_gray.plist | 282 + .../res/animations/grossini_gray.png | Bin 0 -> 11001 bytes .../res/animations/tcc_issue_1.plist | 61 + .../res/animations/tcc_issue_1.png | Bin 0 -> 3129 bytes .../res/animations/tcc_issue_2.plist | 61 + .../res/animations/tcc_issue_2.png | Bin 0 -> 5225 bytes test/visual-tests/res/audio/LuckyDay.mp3 | Bin 0 -> 270974 bytes .../res/audio/SoundEffectsFX009/FX081.mp3 | Bin 0 -> 8515 bytes .../res/audio/SoundEffectsFX009/FX082.mp3 | Bin 0 -> 11440 bytes .../res/audio/SoundEffectsFX009/FX083.mp3 | Bin 0 -> 11440 bytes .../res/audio/SoundEffectsFX009/FX084.mp3 | Bin 0 -> 11440 bytes .../res/audio/SoundEffectsFX009/FX085.mp3 | Bin 0 -> 16665 bytes .../res/audio/SoundEffectsFX009/FX086.mp3 | Bin 0 -> 16665 bytes .../res/audio/SoundEffectsFX009/FX087.mp3 | Bin 0 -> 11231 bytes .../res/audio/SoundEffectsFX009/FX088.mp3 | Bin 0 -> 13530 bytes .../res/audio/SoundEffectsFX009/FX089.mp3 | Bin 0 -> 13530 bytes .../res/audio/SoundEffectsFX009/FX090.mp3 | Bin 0 -> 13530 bytes .../res/audio/SoundEffectsFX009/README.txt | 4 + .../visual-tests/res/background-music-aac.mp3 | Bin 0 -> 299291 bytes test/visual-tests/res/background.caf | Bin 0 -> 163584 bytes test/visual-tests/res/background.mp3 | Bin 0 -> 128313 bytes test/visual-tests/res/background.ogg | Bin 0 -> 89234 bytes test/visual-tests/res/ccs-res/XueJ2312F.ttf | Bin 0 -> 453216 bytes .../res/ccs-res/cocosui/100/100.ExportJson | 283 + .../res/ccs-res/cocosui/100/100.csb | Bin 0 -> 1834 bytes .../res/ccs-res/cocosui/100/1000.plist | 65 + .../res/ccs-res/cocosui/100/1000.png | Bin 0 -> 1411 bytes .../res/ccs-res/cocosui/CloseNormal.png | Bin 0 -> 6311 bytes .../res/ccs-res/cocosui/CloseSelected.png | Bin 0 -> 5499 bytes .../res/ccs-res/cocosui/CocoGUISample.json | 397 + .../cocosui/CocoGUI_PageView_Sample.csb | Bin 0 -> 805 bytes .../cocosui/CocoGUI_PageView_Sample.json | 49 + .../CustomImageViewTest/NewProject_20.plist | 46 + .../CustomImageViewTest/NewProject_20.png | Bin 0 -> 13472 bytes .../NewProject_2_1.ExportJson | 139 + .../CustomImageViewTest/NewProject_2_1.csb | Bin 0 -> 2181 bytes .../CustomWidgetCallbackBindTest.csb | Bin 0 -> 3360 bytes .../Default/Button_Disable.png | Bin 0 -> 1111 bytes .../Default/Button_Normal.png | Bin 0 -> 1113 bytes .../Default/Button_Press.png | Bin 0 -> 1153 bytes .../Default/CheckBoxNode_Disable.png | Bin 0 -> 1231 bytes .../Default/CheckBoxNode_Normal.png | Bin 0 -> 1241 bytes .../Default/CheckBox_Disable.png | Bin 0 -> 1204 bytes .../Default/CheckBox_Normal.png | Bin 0 -> 1204 bytes .../Default/CheckBox_Press.png | Bin 0 -> 1243 bytes .../CustomWidgetCallbackBindTest/Layer.csb | Bin 0 -> 1768 bytes .../PageViewBugScene.csb | Bin 0 -> 676 bytes .../res/ccs-res/cocosui/Hello.png | Bin 0 -> 138777 bytes .../res/ccs-res/cocosui/Marker Felt.ttf | Bin 0 -> 25776 bytes .../res/ccs-res/cocosui/UITest/UITest.csb | Bin 0 -> 3726 bytes .../res/ccs-res/cocosui/UITest/UITest.json | 446 + .../res/ccs-res/cocosui/UITest/b1.png | Bin 0 -> 3877 bytes .../res/ccs-res/cocosui/UITest/b2.png | Bin 0 -> 4027 bytes .../res/ccs-res/cocosui/UITest/background.png | Bin 0 -> 103086 bytes .../cocosui/UITest/buttonBackground.png | Bin 0 -> 498 bytes .../res/ccs-res/cocosui/UITest/f1.png | Bin 0 -> 3915 bytes .../res/ccs-res/cocosui/UITest/f2.png | Bin 0 -> 4046 bytes .../res/ccs-res/cocosui/UITest/r1.png | Bin 0 -> 1956 bytes .../res/ccs-res/cocosui/UITest/r2.png | Bin 0 -> 2209 bytes .../res/ccs-res/cocosui/UITest/ribbon.png | Bin 0 -> 983 bytes .../res/ccs-res/cocosui/android9patch.plist | 100 + .../res/ccs-res/cocosui/android9patch.png | Bin 0 -> 27140 bytes .../ccs-res/cocosui/animationbuttonnormal.png | Bin 0 -> 8046 bytes .../cocosui/animationbuttonpressed.png | Bin 0 -> 8152 bytes .../res/ccs-res/cocosui/arrow.png | Bin 0 -> 3437 bytes test/visual-tests/res/ccs-res/cocosui/b11.png | Bin 0 -> 163116 bytes .../res/ccs-res/cocosui/backtotopnormal.png | Bin 0 -> 8445 bytes .../res/ccs-res/cocosui/backtotoppressed.png | Bin 0 -> 8509 bytes .../res/ccs-res/cocosui/bitmapFontTest2.fnt | 188 + .../res/ccs-res/cocosui/bitmapFontTest2.png | Bin 0 -> 126215 bytes .../res/ccs-res/cocosui/btn_exercise01_n.png | Bin 0 -> 8420 bytes .../res/ccs-res/cocosui/btn_exercise01_p.png | Bin 0 -> 8144 bytes .../res/ccs-res/cocosui/btn_exercise02_n.png | Bin 0 -> 7033 bytes .../res/ccs-res/cocosui/btn_exercise02_p.png | Bin 0 -> 6903 bytes .../res/ccs-res/cocosui/btn_exercise03_n.png | Bin 0 -> 7658 bytes .../res/ccs-res/cocosui/btn_exercise03_p.png | Bin 0 -> 7416 bytes .../res/ccs-res/cocosui/button.png | Bin 0 -> 840 bytes .../res/ccs-res/cocosui/buttonHighlighted.png | Bin 0 -> 896 bytes .../res/ccs-res/cocosui/ccicon.png | Bin 0 -> 10831 bytes .../res/ccs-res/cocosui/check_box_active.png | Bin 0 -> 4621 bytes .../cocosui/check_box_active_disable.png | Bin 0 -> 4576 bytes .../cocosui/check_box_active_press.png | Bin 0 -> 4650 bytes .../res/ccs-res/cocosui/check_box_normal.png | Bin 0 -> 4522 bytes .../cocosui/check_box_normal_disable.png | Bin 0 -> 4426 bytes .../cocosui/check_box_normal_press.png | Bin 0 -> 4533 bytes .../Button/button_country_n.png | Bin 0 -> 8345 bytes .../Button/button_country_p.png | Bin 0 -> 10246 bytes .../Button/button_country_un.png | Bin 0 -> 4412 bytes .../Test/UIResForEditor/Button/symbol_1B.png | Bin 0 -> 6550 bytes .../Test/UIResForEditor/Button/symbol_1a.png | Bin 0 -> 5393 bytes .../Test/UIResForEditor/Button/symbol_1c.png | Bin 0 -> 3027 bytes .../ccs-res/cocosui/examples/equip/111.png | Bin 0 -> 1707 bytes .../res/ccs-res/cocosui/examples/equip/12.png | Bin 0 -> 623 bytes .../res/ccs-res/cocosui/examples/equip/13.png | Bin 0 -> 563 bytes .../res/ccs-res/cocosui/examples/equip/14.png | Bin 0 -> 452 bytes .../res/ccs-res/cocosui/examples/equip/15.png | Bin 0 -> 465 bytes .../res/ccs-res/cocosui/examples/equip/2.png | Bin 0 -> 12493 bytes .../cocosui/examples/equip/button_end_01.png | Bin 0 -> 1062 bytes .../cocosui/examples/equip/button_end_02.png | Bin 0 -> 1901 bytes .../examples/equip/button_green_n2.png | Bin 0 -> 1553 bytes .../examples/equip/button_green_p2.png | Bin 0 -> 1315 bytes .../examples/equip/button_green_un2.png | Bin 0 -> 1605 bytes .../ccs-res/cocosui/examples/equip/eg/1.png | Bin 0 -> 11743 bytes .../ccs-res/cocosui/examples/equip/eg/10.png | Bin 0 -> 4964 bytes .../ccs-res/cocosui/examples/equip/eg/11.png | Bin 0 -> 5237 bytes .../ccs-res/cocosui/examples/equip/eg/3.png | Bin 0 -> 4364 bytes .../ccs-res/cocosui/examples/equip/eg/4.png | Bin 0 -> 4834 bytes .../ccs-res/cocosui/examples/equip/eg/5.png | Bin 0 -> 4774 bytes .../ccs-res/cocosui/examples/equip/eg/6.png | Bin 0 -> 4758 bytes .../ccs-res/cocosui/examples/equip/eg/7.png | Bin 0 -> 4862 bytes .../ccs-res/cocosui/examples/equip/eg/8.png | Bin 0 -> 4491 bytes .../ccs-res/cocosui/examples/equip/eg/9.png | Bin 0 -> 4713 bytes .../cocosui/examples/equip/eg/crab.png | Bin 0 -> 3062 bytes .../cocosui/examples/equip/eg/research.png | Bin 0 -> 3458 bytes .../cocosui/examples/equip/eg/sell.png | Bin 0 -> 3205 bytes .../examples/equip/eg/shop_shield_1.png | Bin 0 -> 3374 bytes .../examples/equip/eg/shop_shield_2.png | Bin 0 -> 3382 bytes .../examples/equip/eg/shop_shield_3.png | Bin 0 -> 3786 bytes .../cocosui/examples/equip/eg/train.png | Bin 0 -> 3174 bytes .../cocosui/examples/equip/eg/upgrade.png | Bin 0 -> 3609 bytes .../res/ccs-res/cocosui/examples/examples.csb | Bin 0 -> 18546 bytes .../ccs-res/cocosui/examples/examples.json | 3898 ++++++ .../cocosui/examples/weapon_introduce/4.png | Bin 0 -> 3555 bytes .../cocosui/examples/weapon_introduce/5.png | Bin 0 -> 3180 bytes .../cocosui/examples/weapon_introduce/6.png | Bin 0 -> 804 bytes .../weapon_introduce/button_end_01.png | Bin 0 -> 6719 bytes .../weapon_introduce/button_end_02.png | Bin 0 -> 6507 bytes .../weapon_item_1/weapon_item/1.png | Bin 0 -> 3244 bytes .../weapon_item_1/weapon_item/2.png | Bin 0 -> 3276 bytes .../weapon_item_1/weapon_item/7.png | Bin 0 -> 6174 bytes .../weapon_item_1/weapon_item_1.csb | Bin 0 -> 2182 bytes .../weapon_item_1/weapon_item_1.json | 210 + .../res/ccs-res/cocosui/green_edit.png | Bin 0 -> 6749 bytes .../res/ccs-res/cocosui/grossini-aliases.png | Bin 0 -> 14188 bytes .../UIAction_1/CocoStudio_UIEditor.png | Bin 0 -> 14593 bytes .../gui_examples/UIAction_1/UIAction_1.csb | Bin 0 -> 2599 bytes .../gui_examples/UIAction_1/UIAction_1.json | 260 + .../ccs-res/cocosui/gui_examples/buy_1/5.png | Bin 0 -> 3180 bytes .../ccs-res/cocosui/gui_examples/buy_1/7.png | Bin 0 -> 6174 bytes .../cocosui/gui_examples/buy_1/button.png | Bin 0 -> 4617 bytes .../gui_examples/buy_1/buttonHighlighted.png | Bin 0 -> 4617 bytes .../cocosui/gui_examples/buy_1/buy_1.csb | Bin 0 -> 3547 bytes .../cocosui/gui_examples/buy_1/buy_1.json | 337 + .../gui_examples/equip_1/equip/111.png | Bin 0 -> 1170 bytes .../cocosui/gui_examples/equip_1/equip/12.png | Bin 0 -> 623 bytes .../cocosui/gui_examples/equip_1/equip/13.png | Bin 0 -> 563 bytes .../cocosui/gui_examples/equip_1/equip/14.png | Bin 0 -> 452 bytes .../cocosui/gui_examples/equip_1/equip/15.png | Bin 0 -> 465 bytes .../cocosui/gui_examples/equip_1/equip/2.png | Bin 0 -> 12493 bytes .../equip_1/equip/button_end_01.png | Bin 0 -> 1062 bytes .../equip_1/equip/button_end_02.png | Bin 0 -> 1901 bytes .../equip_1/equip/button_green_n2.png | Bin 0 -> 1553 bytes .../equip_1/equip/button_green_p2.png | Bin 0 -> 1315 bytes .../equip_1/equip/button_green_un2.png | Bin 0 -> 1605 bytes .../gui_examples/equip_1/equip/eg/1.png | Bin 0 -> 11743 bytes .../gui_examples/equip_1/equip/eg/10.png | Bin 0 -> 4964 bytes .../gui_examples/equip_1/equip/eg/11.png | Bin 0 -> 5237 bytes .../gui_examples/equip_1/equip/eg/3.png | Bin 0 -> 4364 bytes .../gui_examples/equip_1/equip/eg/4.png | Bin 0 -> 4834 bytes .../gui_examples/equip_1/equip/eg/5.png | Bin 0 -> 4774 bytes .../gui_examples/equip_1/equip/eg/6.png | Bin 0 -> 4758 bytes .../gui_examples/equip_1/equip/eg/7.png | Bin 0 -> 4862 bytes .../gui_examples/equip_1/equip/eg/8.png | Bin 0 -> 4491 bytes .../gui_examples/equip_1/equip/eg/9.png | Bin 0 -> 4713 bytes .../gui_examples/equip_1/equip/eg/crab.png | Bin 0 -> 3062 bytes .../equip_1/equip/eg/research.png | Bin 0 -> 3458 bytes .../gui_examples/equip_1/equip/eg/sell.png | Bin 0 -> 3205 bytes .../equip_1/equip/eg/shop_shield_1.png | Bin 0 -> 3374 bytes .../equip_1/equip/eg/shop_shield_2.png | Bin 0 -> 3382 bytes .../equip_1/equip/eg/shop_shield_3.png | Bin 0 -> 3786 bytes .../gui_examples/equip_1/equip/eg/train.png | Bin 0 -> 3174 bytes .../gui_examples/equip_1/equip/eg/upgrade.png | Bin 0 -> 3609 bytes .../cocosui/gui_examples/equip_1/equip_1.csb | Bin 0 -> 14880 bytes .../cocosui/gui_examples/equip_1/equip_1.json | 3234 +++++ .../gui_examples/map_1/image_castle.png | Bin 0 -> 17482 bytes .../cocosui/gui_examples/map_1/map_1.csb | Bin 0 -> 4242 bytes .../cocosui/gui_examples/map_1/map_1.json | 530 + .../cocosui/gui_examples/map_1/map_pve.png | Bin 0 -> 354187 bytes .../cocosui/gui_examples/map_alert_1/5.png | Bin 0 -> 3180 bytes .../cocosui/gui_examples/map_alert_1/7.png | Bin 0 -> 6174 bytes .../gui_examples/map_alert_1/close_02.png | Bin 0 -> 1758 bytes .../gui_examples/map_alert_1/close_03.png | Bin 0 -> 1758 bytes .../gui_examples/map_alert_1/close_04.png | Bin 0 -> 1758 bytes .../gui_examples/map_alert_1/map_alert_1.csb | Bin 0 -> 3041 bytes .../gui_examples/map_alert_1/map_alert_1.json | 264 + .../gui_examples/page_1/background.png | Bin 0 -> 103086 bytes .../gui_examples/page_1/buttonBackground.png | Bin 0 -> 498 bytes .../cocosui/gui_examples/page_1/page_1.csb | Bin 0 -> 5041 bytes .../cocosui/gui_examples/page_1/page_1.json | 706 + .../CocoStudio_AnimationEditor.png | Bin 0 -> 15654 bytes .../page_content/CocoStudio_DataEditor.png | Bin 0 -> 14930 bytes .../page_content/CocoStudio_SceneEditor.png | Bin 0 -> 14604 bytes .../page_content/CocoStudio_UIEditor.png | Bin 0 -> 14593 bytes .../cocosui/gui_examples/page_1/ribbon.png | Bin 0 -> 983 bytes .../teehanlax - iOS 6 - iPhone_check.png | Bin 0 -> 11484 bytes .../teehanlax - iOS 6 - iPhone_check01.png | Bin 0 -> 11114 bytes .../gui_examples/register_1/128_128.png | Bin 0 -> 10249 bytes .../register_1/Rosewood stdloadingH.fnt | 116 + .../register_1/Rosewood stdloadingH.png | Bin 0 -> 43935 bytes .../gui_examples/register_1/button_d.png | Bin 0 -> 6639 bytes .../gui_examples/register_1/button_n.png | Bin 0 -> 6991 bytes .../gui_examples/register_1/e-mail.png | Bin 0 -> 18555 bytes .../gui_examples/register_1/register_1.csb | Bin 0 -> 5789 bytes .../gui_examples/register_1/register_1.json | 719 + .../register_1/ui_shop_005-hd.png | Bin 0 -> 6035 bytes .../weapon_introduce_1/weapon_introduce/4.png | Bin 0 -> 3555 bytes .../weapon_introduce_1/weapon_introduce/5.png | Bin 0 -> 3180 bytes .../weapon_introduce_1/weapon_introduce/6.png | Bin 0 -> 804 bytes .../weapon_introduce/button_end_01.png | Bin 0 -> 6719 bytes .../weapon_introduce/button_end_02.png | Bin 0 -> 6507 bytes .../weapon_introduce_1/weapon_introduce_1.csb | Bin 0 -> 3927 bytes .../weapon_introduce_1.json | 447 + .../weapon_item_1/weapon_item/1.png | Bin 0 -> 3244 bytes .../weapon_item_1/weapon_item/2.png | Bin 0 -> 3276 bytes .../weapon_item_1/weapon_item/7.png | Bin 0 -> 6174 bytes .../weapon_item_1/weapon_item_1.csb | Bin 0 -> 2180 bytes .../weapon_item_1/weapon_item_1.json | 210 + .../weapon_item_1/weapons/weapons_1.png | Bin 0 -> 6369 bytes .../weapon_item_1/weapons/weapons_10.png | Bin 0 -> 6037 bytes .../weapon_item_1/weapons/weapons_11.png | Bin 0 -> 6164 bytes .../weapon_item_1/weapons/weapons_12.png | Bin 0 -> 7639 bytes .../weapon_item_1/weapons/weapons_13.png | Bin 0 -> 4314 bytes .../weapon_item_1/weapons/weapons_14.png | Bin 0 -> 5703 bytes .../weapon_item_1/weapons/weapons_15.png | Bin 0 -> 6137 bytes .../weapon_item_1/weapons/weapons_16.png | Bin 0 -> 4581 bytes .../weapon_item_1/weapons/weapons_17.png | Bin 0 -> 4871 bytes .../weapon_item_1/weapons/weapons_18.png | Bin 0 -> 4799 bytes .../weapon_item_1/weapons/weapons_19.png | Bin 0 -> 6302 bytes .../weapon_item_1/weapons/weapons_2.png | Bin 0 -> 6620 bytes .../weapon_item_1/weapons/weapons_20.png | Bin 0 -> 5594 bytes .../weapon_item_1/weapons/weapons_21.png | Bin 0 -> 7449 bytes .../weapon_item_1/weapons/weapons_22.png | Bin 0 -> 5326 bytes .../weapon_item_1/weapons/weapons_23.png | Bin 0 -> 5797 bytes .../weapon_item_1/weapons/weapons_24.png | Bin 0 -> 6223 bytes .../weapon_item_1/weapons/weapons_25.png | Bin 0 -> 7198 bytes .../weapon_item_1/weapons/weapons_26.png | Bin 0 -> 6275 bytes .../weapon_item_1/weapons/weapons_27.png | Bin 0 -> 11880 bytes .../weapon_item_1/weapons/weapons_28.png | Bin 0 -> 9365 bytes .../weapon_item_1/weapons/weapons_29.png | Bin 0 -> 12507 bytes .../weapon_item_1/weapons/weapons_3.png | Bin 0 -> 5615 bytes .../weapon_item_1/weapons/weapons_30.png | Bin 0 -> 6196 bytes .../weapon_item_1/weapons/weapons_31.png | Bin 0 -> 6929 bytes .../weapon_item_1/weapons/weapons_4.png | Bin 0 -> 7118 bytes .../weapon_item_1/weapons/weapons_5.png | Bin 0 -> 6339 bytes .../weapon_item_1/weapons/weapons_6.png | Bin 0 -> 9766 bytes .../weapon_item_1/weapons/weapons_7.png | Bin 0 -> 7647 bytes .../weapon_item_1/weapons/weapons_8.png | Bin 0 -> 6352 bytes .../weapon_item_1/weapons/weapons_9.png | Bin 0 -> 8498 bytes .../res/ccs-res/cocosui/labelatlas.png | Bin 0 -> 11830 bytes .../res/ccs-res/cocosui/loadingbar.png | Bin 0 -> 2794 bytes .../res/ccs-res/cocosui/monster.9.png | Bin 0 -> 1083 bytes .../res/ccs-res/cocosui/player.9.png | Bin 0 -> 2150 bytes .../res/ccs-res/cocosui/radio_button_off.png | Bin 0 -> 2062 bytes .../res/ccs-res/cocosui/radio_button_on.png | Bin 0 -> 2515 bytes .../res/ccs-res/cocosui/scrollviewbg.png | Bin 0 -> 13454 bytes .../res/ccs-res/cocosui/slidbar.png | Bin 0 -> 2409 bytes .../res/ccs-res/cocosui/sliderProgress.png | Bin 0 -> 396 bytes .../res/ccs-res/cocosui/sliderProgress2.png | Bin 0 -> 3024 bytes .../res/ccs-res/cocosui/sliderThumb.png | Bin 0 -> 1441 bytes .../res/ccs-res/cocosui/sliderTrack.png | Bin 0 -> 539 bytes .../res/ccs-res/cocosui/sliderTrack2.png | Bin 0 -> 3199 bytes .../cocosui/slider_bar_active_9patch.png | Bin 0 -> 3290 bytes .../cocosui/slider_bar_active_9patch2.png | Bin 0 -> 1002 bytes .../res/ccs-res/cocosui/sliderballnormal.png | Bin 0 -> 4138 bytes .../res/ccs-res/cocosui/sliderballpressed.png | Bin 0 -> 4116 bytes .../res/ccs-res/cocosui/switch-mask.png | Bin 0 -> 10704 bytes .../res/configs/config-example.plist | 34 + .../res/configs/config-test-invalid.plist | 282 + .../res/configs/config-test-ok.plist | 34 + test/visual-tests/res/effect1.raw | Bin 0 -> 8000 bytes test/visual-tests/res/effect1.wav | Bin 0 -> 10026 bytes test/visual-tests/res/effect2.mp3 | Bin 0 -> 13407 bytes test/visual-tests/res/effect2.ogg | Bin 0 -> 4278 bytes .../CCControlColourPickerSpriteSheet.plist | 113 + .../CCControlColourPickerSpriteSheet.png | Bin 0 -> 38149 bytes .../res/extensions/background.png | Bin 0 -> 103086 bytes test/visual-tests/res/extensions/button.png | Bin 0 -> 840 bytes .../res/extensions/buttonBackground.png | Bin 0 -> 498 bytes .../res/extensions/buttonHighlighted.png | Bin 0 -> 896 bytes .../res/extensions/green_edit.png | Bin 0 -> 6749 bytes .../res/extensions/orange_edit.png | Bin 0 -> 3758 bytes .../res/extensions/potentiometerButton.png | Bin 0 -> 5504 bytes .../res/extensions/potentiometerProgress.png | Bin 0 -> 10441 bytes .../res/extensions/potentiometerTrack.png | Bin 0 -> 4057 bytes test/visual-tests/res/extensions/ribbon.png | Bin 0 -> 983 bytes .../res/extensions/sliderProgress.png | Bin 0 -> 396 bytes .../res/extensions/sliderProgress2.png | Bin 0 -> 3024 bytes .../res/extensions/sliderThumb.png | Bin 0 -> 1441 bytes .../res/extensions/sliderTrack.png | Bin 0 -> 539 bytes .../res/extensions/sliderTrack2.png | Bin 0 -> 3199 bytes .../res/extensions/stepper-minus.png | Bin 0 -> 545 bytes .../res/extensions/stepper-plus.png | Bin 0 -> 522 bytes .../res/extensions/switch-mask.png | Bin 0 -> 2996 bytes .../res/extensions/switch-off.png | Bin 0 -> 1169 bytes .../visual-tests/res/extensions/switch-on.png | Bin 0 -> 1080 bytes .../res/extensions/switch-thumb.png | Bin 0 -> 2292 bytes .../res/extensions/yellow_edit.png | Bin 0 -> 6394 bytes test/visual-tests/res/fileLookup.plist | 13 + test/visual-tests/res/fonts/A Damn Mess.ttf | Bin 0 -> 35428 bytes test/visual-tests/res/fonts/Abberancy.ttf | Bin 0 -> 29948 bytes test/visual-tests/res/fonts/Abduction.ttf | Bin 0 -> 50868 bytes .../res/fonts/American Typewriter.ttf | Bin 0 -> 64088 bytes test/visual-tests/res/fonts/Courier New.ttf | Bin 0 -> 709600 bytes test/visual-tests/res/fonts/Fingerpop.ttf | Bin 0 -> 11240 bytes test/visual-tests/res/fonts/HKYuanMini.ttf | Bin 0 -> 155300 bytes test/visual-tests/res/fonts/Japanese.ttf | Bin 0 -> 49748 bytes test/visual-tests/res/fonts/Marker Felt.ttf | Bin 0 -> 25776 bytes test/visual-tests/res/fonts/Paint Boy.ttf | Bin 0 -> 87396 bytes test/visual-tests/res/fonts/Roboto.bmf.fnt | Bin 0 -> 2112 bytes test/visual-tests/res/fonts/Roboto.bmf_0.png | Bin 0 -> 8652 bytes .../res/fonts/Schwarzwald Regular.ttf | Bin 0 -> 49088 bytes test/visual-tests/res/fonts/Schwarzwald.ttf | Bin 0 -> 49088 bytes .../res/fonts/Schwarzwald_Regular.eot | Bin 0 -> 49376 bytes test/visual-tests/res/fonts/Scissor Cuts.ttf | Bin 0 -> 29328 bytes test/visual-tests/res/fonts/Thonburi.ttf | Bin 0 -> 223404 bytes test/visual-tests/res/fonts/ThonburiBold.ttf | Bin 0 -> 255412 bytes .../visual-tests/res/fonts/arial-26-en-ru.fnt | 638 + .../res/fonts/arial-26-en-ru_0.png | Bin 0 -> 162616 bytes .../res/fonts/arial-unicode-26.GlyphProject | Bin 0 -> 3462 bytes .../res/fonts/arial-unicode-26.fnt | 206 + .../res/fonts/arial-unicode-26.png | Bin 0 -> 178317 bytes test/visual-tests/res/fonts/arial.ttf | Bin 0 -> 778552 bytes test/visual-tests/res/fonts/arial16.fnt | 192 + test/visual-tests/res/fonts/arial16.png | Bin 0 -> 18410 bytes .../res/fonts/bitmapFontChinese.fnt | 210 + .../res/fonts/bitmapFontChinese.png | Bin 0 -> 110840 bytes .../visual-tests/res/fonts/bitmapFontTest.fnt | 100 + .../visual-tests/res/fonts/bitmapFontTest.png | Bin 0 -> 143693 bytes .../res/fonts/bitmapFontTest2.bmp | Bin 0 -> 2097206 bytes .../res/fonts/bitmapFontTest2.fnt | 188 + .../res/fonts/bitmapFontTest2.png | Bin 0 -> 126215 bytes .../res/fonts/bitmapFontTest3.fnt | 102 + .../res/fonts/bitmapFontTest3.png | Bin 0 -> 40153 bytes .../res/fonts/bitmapFontTest4.fnt | 99 + .../res/fonts/bitmapFontTest4.png | Bin 0 -> 28312 bytes .../res/fonts/bitmapFontTest5.fnt | 99 + .../res/fonts/bitmapFontTest5.png | Bin 0 -> 46741 bytes .../visual-tests/res/fonts/boundsTestFont.fnt | 343 + .../visual-tests/res/fonts/boundsTestFont.png | Bin 0 -> 249811 bytes test/visual-tests/res/fonts/cyril.ttf | Bin 0 -> 27900 bytes test/visual-tests/res/fonts/cyrillic.ttf | Bin 0 -> 36452 bytes .../res/fonts/font-issue1343-hd.fnt | 75 + .../res/fonts/font-issue1343-hd.png | Bin 0 -> 238653 bytes .../visual-tests/res/fonts/font-issue1343.fnt | 75 + .../visual-tests/res/fonts/font-issue1343.png | Bin 0 -> 53555 bytes test/visual-tests/res/fonts/futura-48.fnt | 182 + test/visual-tests/res/fonts/futura-48.png | Bin 0 -> 178532 bytes test/visual-tests/res/fonts/geneva-32.fnt | 99 + test/visual-tests/res/fonts/helvetica-32.fnt | 99 + .../res/fonts/helvetica-geneva-32.png | Bin 0 -> 202706 bytes test/visual-tests/res/fonts/konqa32-hd.fnt | 96 + test/visual-tests/res/fonts/konqa32-hd.png | Bin 0 -> 39380 bytes test/visual-tests/res/fonts/konqa32.fnt | 96 + test/visual-tests/res/fonts/konqa32.png | Bin 0 -> 19460 bytes test/visual-tests/res/fonts/labelatlas.png | Bin 0 -> 6203 bytes .../res/fonts/larabie-16-hd.plist | 16 + test/visual-tests/res/fonts/larabie-16-hd.png | Bin 0 -> 4128 bytes test/visual-tests/res/fonts/larabie-16.plist | 16 + test/visual-tests/res/fonts/larabie-16.png | Bin 0 -> 4775 bytes test/visual-tests/res/fonts/markerFelt-hd.fnt | 3770 ++++++ test/visual-tests/res/fonts/markerFelt-hd.png | Bin 0 -> 106771 bytes test/visual-tests/res/fonts/markerFelt.fnt | 3769 ++++++ test/visual-tests/res/fonts/markerFelt.png | Bin 0 -> 52520 bytes test/visual-tests/res/fonts/strings.xml | 14 + test/visual-tests/res/fonts/tahoma.ttf | Bin 0 -> 700180 bytes .../fonts/tuffy_bold_italic-charmap-hd.plist | 16 + .../fonts/tuffy_bold_italic-charmap-hd.png | Bin 0 -> 614681 bytes .../res/fonts/tuffy_bold_italic-charmap.plist | 16 + .../res/fonts/tuffy_bold_italic-charmap.png | Bin 0 -> 22986 bytes .../res/fonts/west_england-64.fnt | 99 + .../res/fonts/west_england-64.png | Bin 0 -> 35632 bytes .../res/fonts/xingkai-incomplete.ttf | Bin 0 -> 25856 bytes test/visual-tests/res/fps_images.png | Bin 0 -> 6953 bytes test/visual-tests/res/music.mid | Bin 0 -> 46465 bytes .../res/resjs/Shaders/example_ColorBars.fsh | 43 + .../res/resjs/Shaders/example_ColorBars.vsh | 16 + .../res/resjs/Shaders/example_Flower.fsh | 34 + .../res/resjs/Shaders/example_Flower.vsh | 8 + .../res/resjs/Shaders/example_Heart.fsh | 33 + .../res/resjs/Shaders/example_Heart.vsh | 8 + .../res/resjs/Shaders/example_Julia.fsh | 29 + .../res/resjs/Shaders/example_Julia.vsh | 8 + .../res/resjs/Shaders/example_Mandelbrot.fsh | 41 + .../res/resjs/Shaders/example_Mandelbrot.vsh | 8 + .../res/resjs/Shaders/example_Monjori.fsh | 35 + .../res/resjs/Shaders/example_Monjori.vsh | 8 + .../res/resjs/Shaders/example_Outline.fsh | 27 + .../res/resjs/Shaders/example_Outline.vsh | 13 + .../resjs/Shaders/example_Outline_noMVP.vsh | 13 + .../res/resjs/Shaders/example_Plasma.fsh | 22 + .../res/resjs/Shaders/example_Plasma.vsh | 8 + .../res/resjs/Shaders/example_Twist.fsh | 26 + .../res/resjs/Shaders/example_Twist.vsh | 8 + test/visual-tests/res/spine/goblins-ffd.atlas | 292 + test/visual-tests/res/spine/goblins-ffd.json | 1081 ++ test/visual-tests/res/spine/goblins-ffd.png | Bin 0 -> 163618 bytes test/visual-tests/res/spine/goblins.atlas | 285 + test/visual-tests/res/spine/goblins.json | 499 + test/visual-tests/res/spine/goblins.png | Bin 0 -> 147696 bytes test/visual-tests/res/spine/raptor.atlas | 251 + test/visual-tests/res/spine/raptor.json | 1333 ++ test/visual-tests/res/spine/raptor.png | Bin 0 -> 563690 bytes test/visual-tests/res/spine/spineboy.atlas | 194 + test/visual-tests/res/spine/spineboy.json | 2412 ++++ test/visual-tests/res/spine/spineboy.png | Bin 0 -> 244976 bytes test/visual-tests/res/spine/sprite.png | Bin 0 -> 3774 bytes test/visual-tests/run-helper.sh | 3 + test/visual-tests/run.sh | 6 + test/visual-tests/server.js | 39 + .../ActionManagerTest/ActionManagerTest.js | 428 + .../src/ActionsTest/ActionsTest.js | 2994 +++++ .../src/BakeLayerTest/BakeLayerTest.js | 239 + .../src/BaseTestLayer/BaseTestLayer.js | 328 + .../src/ClippingNodeTest/ClippingNodeTest.js | 768 ++ .../CocosDenshionTest/CocosDenshionTest.js | 328 + .../src/CocosNodeTest/CocosNodeTest.js | 846 ++ .../DrawPrimitivesTest/DrawPrimitivesTest.js | 246 + .../src/EaseActionsTest/EaseActionsTest.js | 1223 ++ .../AssetsManagerTest/AssetsManagerTest.js | 231 + .../ExtensionsTest/CCPoolTest/CCPoolTest.js | 190 + .../src/ExtensionsTest/ExtensionsTest.js | 101 + .../NetworkTest/SocketIOTest.js | 278 + .../NetworkTest/WebSocketTest.js | 264 + .../S9SpriteTest/S9SpriteTest.js | 993 ++ .../TableViewTest/TableViewTestScene.js | 137 + .../src/GUITest/UIButtonTest/UIButtonTest.js | 581 + .../GUITest/UICheckBoxTest/UICheckBoxTest.js | 120 + .../src/GUITest/UIFocusTest/UIFocusTest.js | 550 + .../UIImageViewTest/UIImageViewTest.js | 171 + .../UILabelAtlasTest/UILabelAtlasTest.js | 45 + .../UILabelBMFontTest/UILabelBMFontTest.js | 46 + .../src/GUITest/UILabelTest/UILabelTest.js | 102 + .../src/GUITest/UILayoutTest/UILayoutTest.js | 536 + .../GUITest/UIListViewTest/UIListViewTest.js | 334 + .../UILoadingBarTest/UILoadingBarTest.js | 170 + .../UINodeContainerTest.js | 49 + .../GUITest/UIPageViewTest/UIPageViewTest.js | 570 + .../GUITest/UIRichTextTest/UIRichTextTest.js | 91 + .../UIS9NinePatchTest/UIS9NinePatchTest.js | 73 + test/visual-tests/src/GUITest/UIScene.js | 102 + .../src/GUITest/UISceneManager.js | 709 + .../UIScrollViewTest/UIScrollViewTest.js | 598 + .../src/GUITest/UISliderTest/UISliderTest.js | 175 + .../UITextFieldTest/UITextFieldTest.js | 339 + .../src/GUITest/UITextTest/UITextTest.js | 182 + .../GUITest/UIWebViewTest/UIWebViewTest.js | 107 + .../src/GUITest/UIWebViewTest/webview.html | 21 + .../src/GUITest/UIWebViewTest/webview2.html | 21 + .../src/IntervalTest/IntervalTest.js | 156 + test/visual-tests/src/LabelTest/LabelTest.js | 2162 +++ test/visual-tests/src/LayerTest/LayerTest.js | 579 + .../visual-tests/src/LoaderTest/LoaderTest.js | 221 + .../src/NativeTest/AudioEngineTest.js | 463 + .../src/NativeTest/FileUtils/FileUtilsTest.js | 398 + .../src/NativeTest/JSBExtendTest.js | 167 + .../visual-tests/src/NativeTest/NativeTest.js | 82 + .../NewEventManagerTest.js | 1294 ++ .../visual-tests/src/OpenGLTest/OpenGLTest.js | 1368 ++ .../src/ParallaxTest/ParallaxTest.js | 265 + .../src/ParticleTest/ParticleTest.js | 1232 ++ test/visual-tests/src/PathTest/PathTest.js | 132 + .../ProgressActionsTest.js | 413 + .../src/ReflectionTest/ReflectionTest.js | 84 + .../RenderTextureTest/RenderTextureTest.js | 698 + .../src/SchedulerTest/SchedulerTest.js | 750 ++ test/visual-tests/src/SpineTest/SpineTest.js | 301 + .../SpritePolygonTest/SpritePolygonTest.js | 497 + .../visual-tests/src/SpriteTest/SpriteTest.js | 5503 ++++++++ .../src/SysTest/ScriptTestTempFile.js | 38 + test/visual-tests/src/SysTest/SysTest.js | 370 + .../src/TextureCacheTest/TextureCacheTest.js | 341 + .../src/TileMapTest/TileMapTest.js | 1741 +++ test/visual-tests/src/TouchesTest/Ball.js | 98 + test/visual-tests/src/TouchesTest/Paddle.js | 106 + .../src/TouchesTest/TouchesTest.js | 123 + .../src/XHRTest/XHRArrayBufferTest.js | 121 + test/visual-tests/src/XHRTest/XHRTest.js | 188 + test/visual-tests/src/tests-main.js | 512 + test/visual-tests/src/tests_resources.js | 1044 ++ tools/XmlCheck.js | 143 + tools/compiler/compiler.jar | Bin 0 -> 6243182 bytes tools/core4cc.js | 245 + tools/genBuildXml.js | 89 + tools/publish.js | 72 + tools/readme for tools.txt | 8 + 1463 files changed, 265287 insertions(+) create mode 100644 .gitignore create mode 100644 AUTHORS.txt create mode 100644 Base64Images.js create mode 100644 CCBoot.js create mode 100644 CCDebugger.js create mode 100644 CHANGELOG.txt create mode 100644 README.mdown create mode 100644 bower.json create mode 100644 cocos2d/ToMerge/qunit/test-event-system.js create mode 100644 cocos2d/ToMerge/qunit/test-ticker.js create mode 100644 cocos2d/ToMerge/qunit/test-time.js create mode 100644 cocos2d/ToMerge/wrappers/bitmap-font.js create mode 100644 cocos2d/ToMerge/wrappers/draw-node.js create mode 100644 cocos2d/ToMerge/wrappers/edit-box.js create mode 100644 cocos2d/ToMerge/wrappers/index.js create mode 100644 cocos2d/ToMerge/wrappers/label-ttf.js create mode 100644 cocos2d/ToMerge/wrappers/layer-color.js create mode 100644 cocos2d/ToMerge/wrappers/layer.js create mode 100644 cocos2d/ToMerge/wrappers/progress-timer.js create mode 100644 cocos2d/ToMerge/wrappers/sprite-batch-node.js create mode 100644 cocos2d/ToMerge/wrappers/tiled-map.js create mode 100644 cocos2d/ToMerge/wrappers/ui/button.js create mode 100644 cocos2d/ToMerge/wrappers/ui/check-box.js create mode 100644 cocos2d/ToMerge/wrappers/ui/layout.js create mode 100644 cocos2d/ToMerge/wrappers/ui/list-view.js create mode 100644 cocos2d/ToMerge/wrappers/ui/loading-bar.js create mode 100644 cocos2d/ToMerge/wrappers/ui/page-view.js create mode 100644 cocos2d/ToMerge/wrappers/ui/scale9-sprite.js create mode 100644 cocos2d/ToMerge/wrappers/ui/scale9.js create mode 100644 cocos2d/ToMerge/wrappers/ui/scroll-view.js create mode 100644 cocos2d/ToMerge/wrappers/ui/slider.js create mode 100644 cocos2d/ToMerge/wrappers/ui/text-atlas.js create mode 100644 cocos2d/ToMerge/wrappers/ui/text-bitmap-font.js create mode 100644 cocos2d/ToMerge/wrappers/ui/text-field.js create mode 100644 cocos2d/ToMerge/wrappers/ui/text.js create mode 100644 cocos2d/ToMerge/wrappers/ui/widget.js create mode 100644 cocos2d/ToMerge/wrappers/utils.js create mode 100644 cocos2d/actions/CCAction.js create mode 100644 cocos2d/actions/CCActionCamera.js create mode 100644 cocos2d/actions/CCActionCatmullRom.js create mode 100644 cocos2d/actions/CCActionEase.js create mode 100644 cocos2d/actions/CCActionInstant.js create mode 100644 cocos2d/actions/CCActionInterval.js create mode 100644 cocos2d/actions/CCActionTween.js create mode 100644 cocos2d/actions3d/CCActionGrid.js create mode 100644 cocos2d/actions3d/CCActionGrid3D.js create mode 100644 cocos2d/actions3d/CCActionPageTurn3D.js create mode 100644 cocos2d/actions3d/CCActionTiledGrid.js create mode 100644 cocos2d/animation/animation-animator.js create mode 100644 cocos2d/animation/animation-clip.js create mode 100644 cocos2d/animation/animation-curves.js create mode 100644 cocos2d/animation/animation-manager.js create mode 100644 cocos2d/animation/animation-state.js create mode 100644 cocos2d/animation/animators.js create mode 100644 cocos2d/animation/bezier.js create mode 100644 cocos2d/animation/binary-search.js create mode 100644 cocos2d/animation/easing.js create mode 100644 cocos2d/animation/index.js create mode 100644 cocos2d/animation/motion-path-helper.js create mode 100644 cocos2d/animation/playable.js create mode 100644 cocos2d/animation/types.js create mode 100644 cocos2d/audio/CCAudio.js create mode 100644 cocos2d/clipping-nodes/CCClippingNode.js create mode 100644 cocos2d/clipping-nodes/CCClippingNodeCanvasRenderCmd.js create mode 100644 cocos2d/clipping-nodes/CCClippingNodeWebGLRenderCmd.js create mode 100644 cocos2d/compression/ZipUtils.js create mode 100644 cocos2d/compression/base64.js create mode 100644 cocos2d/compression/gzip.js create mode 100644 cocos2d/compression/zlib.min.js create mode 100644 cocos2d/core/CCActionManager.js create mode 100644 cocos2d/core/CCCamera.js create mode 100644 cocos2d/core/CCConfiguration.js create mode 100644 cocos2d/core/CCDirector.js create mode 100644 cocos2d/core/CCDirectorCanvas.js create mode 100644 cocos2d/core/CCDirectorWebGL.js create mode 100644 cocos2d/core/CCDrawingPrimitivesCanvas.js create mode 100644 cocos2d/core/CCDrawingPrimitivesWebGL.js create mode 100644 cocos2d/core/CCGame.js create mode 100644 cocos2d/core/CCNode.js create mode 100644 cocos2d/core/CCScene.js create mode 100644 cocos2d/core/CCScheduler.js create mode 100644 cocos2d/core/assets/CCAsset.js create mode 100644 cocos2d/core/assets/CCAudioClip.js create mode 100644 cocos2d/core/assets/CCBitmapFont.js create mode 100644 cocos2d/core/assets/CCPrefab.js create mode 100644 cocos2d/core/assets/CCRawAsset.js create mode 100644 cocos2d/core/assets/CCSceneAsset.js create mode 100644 cocos2d/core/assets/CCScripts.js create mode 100644 cocos2d/core/assets/CCSpriteAnimation.js create mode 100644 cocos2d/core/assets/CCSpriteAtlas.js create mode 100644 cocos2d/core/assets/CCTTFFont.js create mode 100644 cocos2d/core/assets/CCTiledMapAsset.js create mode 100644 cocos2d/core/assets/index.js create mode 100644 cocos2d/core/base-nodes/BaseNodesPropertyDefine.js create mode 100644 cocos2d/core/base-nodes/CCAtlasNode.js create mode 100644 cocos2d/core/base-nodes/CCAtlasNodeCanvasRenderCmd.js create mode 100644 cocos2d/core/base-nodes/CCAtlasNodeWebGLRenderCmd.js create mode 100644 cocos2d/core/base-nodes/CCNode.js create mode 100644 cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js create mode 100644 cocos2d/core/base-nodes/CCNodeWebGLRenderCmd.js create mode 100644 cocos2d/core/base-ui/CCWidgetManager.js create mode 100644 cocos2d/core/cocos2d_externs.js create mode 100644 cocos2d/core/components/CCAnimation.js create mode 100644 cocos2d/core/components/CCAudioSource.js create mode 100644 cocos2d/core/components/CCButton.js create mode 100644 cocos2d/core/components/CCCanvas.js create mode 100644 cocos2d/core/components/CCComponent.js create mode 100644 cocos2d/core/components/CCComponentInSG.js create mode 100644 cocos2d/core/components/CCELabel.js create mode 100644 cocos2d/core/components/CCSpriteRenderer.js create mode 100644 cocos2d/core/components/CCWidget.js create mode 100644 cocos2d/core/components/index.js create mode 100644 cocos2d/core/event-manager/CCEventListener.js create mode 100644 cocos2d/core/event-manager/CCEventManager.js create mode 100644 cocos2d/core/event-manager/CCSystemEvent.js create mode 100644 cocos2d/core/event-manager/CCTouch.js create mode 100644 cocos2d/core/event/event-listeners.js create mode 100644 cocos2d/core/event/event-target.js create mode 100644 cocos2d/core/event/event.js create mode 100644 cocos2d/core/event/index.js create mode 100644 cocos2d/core/index.js create mode 100644 cocos2d/core/labelttf/CCLabelTTF.js create mode 100644 cocos2d/core/labelttf/CCLabelTTFCanvasRenderCmd.js create mode 100644 cocos2d/core/labelttf/CCLabelTTFWebGLRenderCmd.js create mode 100644 cocos2d/core/labelttf/LabelTTFPropertyDefine.js create mode 100644 cocos2d/core/layers/CCLayer.js create mode 100644 cocos2d/core/layers/CCLayerCanvasRenderCmd.js create mode 100644 cocos2d/core/layers/CCLayerWebGLRenderCmd.js create mode 100644 cocos2d/core/platform/CCAssetLibrary.js create mode 100644 cocos2d/core/platform/CCClass.js create mode 100644 cocos2d/core/platform/CCCommon.js create mode 100644 cocos2d/core/platform/CCConfig.js create mode 100644 cocos2d/core/platform/CCEGLView.js create mode 100644 cocos2d/core/platform/CCInputExtension.js create mode 100644 cocos2d/core/platform/CCInputManager.js create mode 100644 cocos2d/core/platform/CCLoader.js create mode 100644 cocos2d/core/platform/CCLoaders.js create mode 100644 cocos2d/core/platform/CCMacro.js create mode 100644 cocos2d/core/platform/CCObject.js create mode 100644 cocos2d/core/platform/CCSAXParser.js create mode 100644 cocos2d/core/platform/CCScreen.js create mode 100644 cocos2d/core/platform/CCSys.js create mode 100644 cocos2d/core/platform/CCVisibleRect.js create mode 100644 cocos2d/core/platform/_CCClass.js create mode 100644 cocos2d/core/platform/attribute.js create mode 100644 cocos2d/core/platform/callbacks-invoker.js create mode 100644 cocos2d/core/platform/deserialize.js create mode 100644 cocos2d/core/platform/index.js create mode 100644 cocos2d/core/platform/instantiate.js create mode 100644 cocos2d/core/platform/js.js create mode 100644 cocos2d/core/platform/load-manager.js create mode 100644 cocos2d/core/platform/miniFramework.js create mode 100644 cocos2d/core/platform/prefab-info.js create mode 100644 cocos2d/core/platform/preprocess-attrs.js create mode 100644 cocos2d/core/platform/requiring-frame.js create mode 100644 cocos2d/core/platform/url.js create mode 100644 cocos2d/core/platform/utils.js create mode 100644 cocos2d/core/renderer/RendererCanvas.js create mode 100644 cocos2d/core/renderer/RendererWebGL.js create mode 100644 cocos2d/core/scenes/CCLoaderScene.js create mode 100644 cocos2d/core/scenes/CCScene.js create mode 100644 cocos2d/core/sprites/CCAnimation.js create mode 100644 cocos2d/core/sprites/CCAnimationCache.js create mode 100644 cocos2d/core/sprites/CCBakeSprite.js create mode 100644 cocos2d/core/sprites/CCSprite.js create mode 100644 cocos2d/core/sprites/CCSpriteBatchNode.js create mode 100644 cocos2d/core/sprites/CCSpriteBatchNodeCanvasRenderCmd.js create mode 100644 cocos2d/core/sprites/CCSpriteBatchNodeWebGLRenderCmd.js create mode 100644 cocos2d/core/sprites/CCSpriteCanvasRenderCmd.js create mode 100644 cocos2d/core/sprites/CCSpriteFrame.js create mode 100644 cocos2d/core/sprites/CCSpriteFrameCache.js create mode 100644 cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js create mode 100644 cocos2d/core/sprites/SpritesPropertyDefine.js create mode 100644 cocos2d/core/support/CCPointExtension.js create mode 100644 cocos2d/core/support/CCVertex.js create mode 100644 cocos2d/core/support/TransformUtils.js create mode 100644 cocos2d/core/textures/CCTexture2D.js create mode 100644 cocos2d/core/textures/CCTextureAtlas.js create mode 100644 cocos2d/core/textures/CCTextureCache.js create mode 100644 cocos2d/core/textures/index.js create mode 100644 cocos2d/core/utils/Async.js create mode 100644 cocos2d/core/utils/BinaryLoader.js create mode 100644 cocos2d/core/utils/CCPath.js create mode 100644 cocos2d/core/utils/CCProfiler.js create mode 100644 cocos2d/core/utils/base-node.js create mode 100644 cocos2d/core/utils/find.js create mode 100644 cocos2d/core/utils/index.js create mode 100644 cocos2d/core/utils/misc.js create mode 100644 cocos2d/core/utils/scene-graph-helper.js create mode 100644 cocos2d/core/value-types/CCAffineTransform.js create mode 100644 cocos2d/core/value-types/CCColor.js create mode 100644 cocos2d/core/value-types/CCEnum.js create mode 100644 cocos2d/core/value-types/CCRect.js create mode 100644 cocos2d/core/value-types/CCSize.js create mode 100644 cocos2d/core/value-types/CCTypes.js create mode 100644 cocos2d/core/value-types/CCTypesWebGL.js create mode 100644 cocos2d/core/value-types/CCValueType.js create mode 100644 cocos2d/core/value-types/CCVec2.js create mode 100644 cocos2d/core/value-types/index.js create mode 100644 cocos2d/deprecated.js create mode 100644 cocos2d/effects/CCGrabber.js create mode 100644 cocos2d/effects/CCGrid.js create mode 100644 cocos2d/kazmath/SIMDPolyfill.js create mode 100644 cocos2d/kazmath/aabb.js create mode 100644 cocos2d/kazmath/gl/mat4stack.js create mode 100644 cocos2d/kazmath/gl/matrix.js create mode 100644 cocos2d/kazmath/mat3.js create mode 100644 cocos2d/kazmath/mat4.js create mode 100644 cocos2d/kazmath/mat4SIMD.js create mode 100644 cocos2d/kazmath/plane.js create mode 100644 cocos2d/kazmath/quaternion.js create mode 100644 cocos2d/kazmath/ray2.js create mode 100644 cocos2d/kazmath/simd_benchmark/base.js create mode 100644 cocos2d/kazmath/simd_benchmark/index.html create mode 100644 cocos2d/kazmath/simd_benchmark/kernel-template.js create mode 100644 cocos2d/kazmath/simd_benchmark/kmMat4AreEqual.js create mode 100644 cocos2d/kazmath/simd_benchmark/kmMat4Assign.js create mode 100644 cocos2d/kazmath/simd_benchmark/kmMat4Inverse.js create mode 100644 cocos2d/kazmath/simd_benchmark/kmMat4IsIdentity.js create mode 100644 cocos2d/kazmath/simd_benchmark/kmMat4LookAt.js create mode 100644 cocos2d/kazmath/simd_benchmark/kmMat4Multiply.js create mode 100644 cocos2d/kazmath/simd_benchmark/kmMat4Transpose.js create mode 100644 cocos2d/kazmath/simd_benchmark/kmVec3TransformCoord.js create mode 100644 cocos2d/kazmath/simd_benchmark/run.js create mode 100644 cocos2d/kazmath/simd_benchmark/run_browser.js create mode 100644 cocos2d/kazmath/utility.js create mode 100644 cocos2d/kazmath/vec2.js create mode 100644 cocos2d/kazmath/vec3.js create mode 100644 cocos2d/kazmath/vec3SIMD.js create mode 100644 cocos2d/kazmath/vec4.js create mode 100644 cocos2d/labels/CCLabel.js create mode 100644 cocos2d/labels/CCLabelAtlas.js create mode 100644 cocos2d/labels/CCLabelAtlasCanvasRenderCmd.js create mode 100644 cocos2d/labels/CCLabelAtlasWebGLRenderCmd.js create mode 100644 cocos2d/labels/CCLabelBMFont.js create mode 100644 cocos2d/labels/CCLabelBMFontCanvasRenderCmd.js create mode 100644 cocos2d/labels/CCLabelBMFontWebGLRenderCmd.js create mode 100644 cocos2d/labels/CCLabelCanvasRenderCmd.js create mode 100644 cocos2d/labels/CCLabelWebGLRenderCmd.js create mode 100644 cocos2d/menus/CCMenu.js create mode 100644 cocos2d/menus/CCMenuItem.js create mode 100644 cocos2d/motion-streak/CCMotionStreak.js create mode 100644 cocos2d/motion-streak/CCMotionStreakWebGLRenderCmd.js create mode 100644 cocos2d/node-grid/CCNodeGrid.js create mode 100644 cocos2d/node-grid/CCNodeGridWebGLRenderCmd.js create mode 100644 cocos2d/parallax/CCParallaxNode.js create mode 100644 cocos2d/parallax/CCParallaxNodeRenderCmd.js create mode 100644 cocos2d/particle/CCEParticleSystem.js create mode 100644 cocos2d/particle/CCPNGReader.js create mode 100644 cocos2d/particle/CCParticleAsset.js create mode 100644 cocos2d/particle/CCParticleBatchNode.js create mode 100644 cocos2d/particle/CCParticleBatchNodeCanvasRenderCmd.js create mode 100644 cocos2d/particle/CCParticleBatchNodeWebGLRenderCmd.js create mode 100644 cocos2d/particle/CCParticleExamples.js create mode 100644 cocos2d/particle/CCParticleSystem.js create mode 100644 cocos2d/particle/CCParticleSystemCanvasRenderCmd.js create mode 100644 cocos2d/particle/CCParticleSystemWebGLRenderCmd.js create mode 100644 cocos2d/particle/CCTIFFReader.js create mode 100644 cocos2d/physics/CCPhysicsDebugNode.js create mode 100644 cocos2d/physics/CCPhysicsDebugNodeCanvasRenderCmd.js create mode 100644 cocos2d/physics/CCPhysicsDebugNodeWebGLRenderCmd.js create mode 100644 cocos2d/physics/CCPhysicsSprite.js create mode 100644 cocos2d/physics/CCPhysicsSpriteCanvasRenderCmd.js create mode 100644 cocos2d/physics/CCPhysicsSpriteWebGLRenderCmd.js create mode 100644 cocos2d/progress-timer/CCActionProgressTimer.js create mode 100644 cocos2d/progress-timer/CCProgressTimer.js create mode 100644 cocos2d/progress-timer/CCProgressTimerCanvasRenderCmd.js create mode 100644 cocos2d/progress-timer/CCProgressTimerWebGLRenderCmd.js create mode 100644 cocos2d/render-texture/CCRenderTexture.js create mode 100644 cocos2d/render-texture/CCRenderTextureCanvasRenderCmd.js create mode 100644 cocos2d/render-texture/CCRenderTextureWebGLRenderCmd.js create mode 100644 cocos2d/shaders/CCGLProgram.js create mode 100644 cocos2d/shaders/CCGLStateCache.js create mode 100644 cocos2d/shaders/CCShaderCache.js create mode 100644 cocos2d/shaders/CCShaders.js create mode 100644 cocos2d/shape-nodes/CCDrawNode.js create mode 100644 cocos2d/shape-nodes/CCDrawNodeCanvasRenderCmd.js create mode 100644 cocos2d/shape-nodes/CCDrawNodeWebGLRenderCmd.js create mode 100644 cocos2d/text-input/CCIMEDispatcher.js create mode 100644 cocos2d/text-input/CCTextFieldTTF.js create mode 100644 cocos2d/tilemap/CCTGAlib.js create mode 100644 cocos2d/tilemap/CCTMXLayer.js create mode 100644 cocos2d/tilemap/CCTMXLayerCanvasRenderCmd.js create mode 100644 cocos2d/tilemap/CCTMXLayerWebGLRenderCmd.js create mode 100644 cocos2d/tilemap/CCTMXObjectGroup.js create mode 100644 cocos2d/tilemap/CCTMXTiledMap.js create mode 100644 cocos2d/tilemap/CCTMXXMLParser.js create mode 100644 cocos2d/transitions/CCTransition.js create mode 100644 cocos2d/transitions/CCTransitionPageTurn.js create mode 100644 cocos2d/transitions/CCTransitionProgress.js create mode 100644 docs/cocos2d/core/CCActionManager/ActionManager.js create mode 100644 docs/cocos2d/core/CCScheduler/schedule.js create mode 100644 docs/cocos2d/core/CCScheduler/scheduleCallbackForTarget.js create mode 100644 docs/cocos2d/core/CCScheduler/scheduleUpdateForTarget.js create mode 100644 docs/cocos2d/core/CCScheduler/unscheduleCallbackForTarget.js create mode 100644 docs/cocos2d/core/CCScheduler/unscheduleUpdateForTarget.js create mode 100644 docs/cocos2d/core/components/CCSpriteRenerer/initWithSpriteFrameName.js create mode 100644 docs/cocos2d/core/event-manager/CCEventListener/create.js create mode 100644 docs/cocos2d/core/event/_getCapturingTargets.js create mode 100644 docs/cocos2d/core/platform/CCCommon/KEY.js create mode 100644 docs/cocos2d/core/platform/CCMacro/lerp.js create mode 100644 docs/cocos2d/core/platform/attribute/attr.js create mode 100644 docs/cocos2d/core/platform/url/raw.js create mode 100644 docs/cocos2d/core/sprites/SpriteFrame.js create mode 100644 docs/cocos2d/core/sprites/addSpriteFrames.js create mode 100644 docs/cocos2d/core/sprites/getSpriteFrame.js create mode 100644 docs/cocos2d/core/support/pAdd.js create mode 100644 docs/cocos2d/core/support/pCompOp.js create mode 100644 docs/cocos2d/core/textures/TextureAtlas.js create mode 100644 docs/cocos2d/core/textures/addImage.js create mode 100644 docs/cocos2d/core/textures/getKeyByTexture.js create mode 100644 docs/cocos2d/core/textures/getTextureColors.js create mode 100644 docs/cocos2d/core/textures/getTextureForKey.js create mode 100644 docs/cocos2d/core/textures/initWithFile.js create mode 100644 docs/cocos2d/core/textures/initWithTexture.js create mode 100644 docs/cocos2d/core/textures/removeAllTextures.js create mode 100644 docs/cocos2d/core/textures/removeTexture.js create mode 100644 docs/cocos2d/core/textures/removeTextureForKey.js create mode 100644 docs/cocos2d/core/textures/textureForKey.js create mode 100644 docs/cocos2d/core/utils/CCPath/basename.js create mode 100644 docs/cocos2d/core/utils/CCPath/changeBasename.js create mode 100644 docs/cocos2d/core/utils/CCPath/changeExtname.js create mode 100644 docs/cocos2d/core/utils/CCPath/dirname.js create mode 100644 docs/cocos2d/core/utils/CCPath/extname.js create mode 100644 docs/cocos2d/core/utils/CCPath/join.js create mode 100644 docs/cocos2d/core/utils/node-wrapper/setPosition.js create mode 100644 docs/cocos2d/core/value-types/CCColor/color.js create mode 100644 docs/cocos2d/core/value-types/CCSize/size.js create mode 100644 docs/extensions/ccpool/putInPool.js create mode 100644 editor/dashboard/banner.jpg create mode 100644 editor/dashboard/banner.png create mode 100644 editor/dashboard/logo.png create mode 100644 editor/template/new-scene.fire create mode 100644 editor/template/new-script.coffee create mode 100644 editor/template/new-script.js create mode 100644 extends.js create mode 100644 extensions/ccb-reader/CCBAnimationManager.js create mode 100644 extensions/ccb-reader/CCBKeyframe.js create mode 100644 extensions/ccb-reader/CCBReader.js create mode 100644 extensions/ccb-reader/CCBReaderUtil.js create mode 100644 extensions/ccb-reader/CCBRelativePositioning.js create mode 100644 extensions/ccb-reader/CCBSequence.js create mode 100644 extensions/ccb-reader/CCBValue.js create mode 100644 extensions/ccb-reader/CCControlLoader.js create mode 100644 extensions/ccb-reader/CCNodeLoader.js create mode 100644 extensions/ccb-reader/CCNodeLoaderLibrary.js create mode 100644 extensions/ccb-reader/CCSpriteLoader.js create mode 100644 extensions/ccpool/CCPool.js create mode 100644 extensions/ccui/base-classes/CCProtectedNode.js create mode 100644 extensions/ccui/base-classes/CCProtectedNodeCanvasRenderCmd.js create mode 100644 extensions/ccui/base-classes/CCProtectedNodeWebGLRenderCmd.js create mode 100644 extensions/ccui/base-classes/UIScale9Sprite.js create mode 100644 extensions/ccui/base-classes/UIScale9SpriteCanvasRenderCmd.js create mode 100644 extensions/ccui/base-classes/UIScale9SpriteWebGLRenderCmd.js create mode 100644 extensions/ccui/base-classes/UIWidget.js create mode 100644 extensions/ccui/base-classes/UIWidgetRenderCmd.js create mode 100644 extensions/ccui/layouts/UIHBox.js create mode 100644 extensions/ccui/layouts/UILayout.js create mode 100644 extensions/ccui/layouts/UILayoutCanvasRenderCmd.js create mode 100644 extensions/ccui/layouts/UILayoutComponent.js create mode 100644 extensions/ccui/layouts/UILayoutManager.js create mode 100644 extensions/ccui/layouts/UILayoutParameter.js create mode 100644 extensions/ccui/layouts/UILayoutWebGLRenderCmd.js create mode 100644 extensions/ccui/layouts/UIRelativeBox.js create mode 100644 extensions/ccui/layouts/UIVBox.js create mode 100644 extensions/ccui/system/CocosGUI.js create mode 100644 extensions/ccui/system/UIHelper.js create mode 100644 extensions/ccui/uiwidgets/UIButton.js create mode 100644 extensions/ccui/uiwidgets/UICheckBox.js create mode 100644 extensions/ccui/uiwidgets/UIImageView.js create mode 100644 extensions/ccui/uiwidgets/UILoadingBar.js create mode 100644 extensions/ccui/uiwidgets/UIRichText.js create mode 100644 extensions/ccui/uiwidgets/UISlider.js create mode 100644 extensions/ccui/uiwidgets/UIText.js create mode 100644 extensions/ccui/uiwidgets/UITextAtlas.js create mode 100644 extensions/ccui/uiwidgets/UITextBMFont.js create mode 100644 extensions/ccui/uiwidgets/UITextField.js create mode 100644 extensions/ccui/uiwidgets/UIVideoPlayer.js create mode 100644 extensions/ccui/uiwidgets/UIWebView.js create mode 100644 extensions/ccui/uiwidgets/scroll-widget/UIListView.js create mode 100644 extensions/ccui/uiwidgets/scroll-widget/UIPageView.js create mode 100644 extensions/ccui/uiwidgets/scroll-widget/UIScrollView.js create mode 100644 extensions/ccui/uiwidgets/scroll-widget/UIScrollViewCanvasRenderCmd.js create mode 100644 extensions/ccui/uiwidgets/scroll-widget/UIScrollViewWebGLRenderCmd.js create mode 100644 extensions/cocostudio/CocoStudio.js create mode 100644 extensions/cocostudio/action/CCActionFrame.js create mode 100644 extensions/cocostudio/action/CCActionManager.js create mode 100644 extensions/cocostudio/action/CCActionNode.js create mode 100644 extensions/cocostudio/action/CCActionObject.js create mode 100644 extensions/cocostudio/armature/CCArmature.js create mode 100644 extensions/cocostudio/armature/CCArmatureCanvasRenderCmd.js create mode 100644 extensions/cocostudio/armature/CCArmatureWebGLRenderCmd.js create mode 100644 extensions/cocostudio/armature/CCBone.js create mode 100644 extensions/cocostudio/armature/animation/CCArmatureAnimation.js create mode 100644 extensions/cocostudio/armature/animation/CCProcessBase.js create mode 100644 extensions/cocostudio/armature/animation/CCTween.js create mode 100644 extensions/cocostudio/armature/datas/CCDatas.js create mode 100644 extensions/cocostudio/armature/display/CCBatchNode.js create mode 100644 extensions/cocostudio/armature/display/CCDecorativeDisplay.js create mode 100644 extensions/cocostudio/armature/display/CCDisplayFactory.js create mode 100644 extensions/cocostudio/armature/display/CCDisplayManager.js create mode 100644 extensions/cocostudio/armature/display/CCSkin.js create mode 100644 extensions/cocostudio/armature/display/CCSkinCanvasRenderCmd.js create mode 100644 extensions/cocostudio/armature/display/CCSkinWebGLRenderCmd.js create mode 100644 extensions/cocostudio/armature/physics/CCColliderDetector.js create mode 100644 extensions/cocostudio/armature/utils/CCArmatureDataManager.js create mode 100644 extensions/cocostudio/armature/utils/CCArmatureDefine.js create mode 100644 extensions/cocostudio/armature/utils/CCDataReaderHelper.js create mode 100644 extensions/cocostudio/armature/utils/CCSpriteFrameCacheHelper.js create mode 100644 extensions/cocostudio/armature/utils/CCTransformHelp.js create mode 100644 extensions/cocostudio/armature/utils/CCTweenFunction.js create mode 100644 extensions/cocostudio/armature/utils/CCUtilMath.js create mode 100644 extensions/cocostudio/components/CCComAttribute.js create mode 100644 extensions/cocostudio/components/CCComAudio.js create mode 100644 extensions/cocostudio/components/CCComController.js create mode 100644 extensions/cocostudio/components/CCComRender.js create mode 100644 extensions/cocostudio/components/CCComponent.js create mode 100644 extensions/cocostudio/components/CCComponentContainer.js create mode 100644 extensions/cocostudio/loader/load.js create mode 100644 extensions/cocostudio/loader/parsers/action-1.x.js create mode 100644 extensions/cocostudio/loader/parsers/action-2.x.js create mode 100644 extensions/cocostudio/loader/parsers/compatible.js create mode 100644 extensions/cocostudio/loader/parsers/scene-1.x.js create mode 100644 extensions/cocostudio/loader/parsers/timelineParser-1.x.js create mode 100644 extensions/cocostudio/loader/parsers/timelineParser-2.x.js create mode 100644 extensions/cocostudio/loader/parsers/uiParser-1.x.js create mode 100644 extensions/cocostudio/timeline/ActionTimeline.js create mode 100644 extensions/cocostudio/timeline/CCBoneNode.js create mode 100644 extensions/cocostudio/timeline/CCSkeletonNode.js create mode 100644 extensions/cocostudio/timeline/CCSkinNode.js create mode 100644 extensions/cocostudio/timeline/Frame.js create mode 100644 extensions/cocostudio/timeline/Timeline.js create mode 100644 extensions/cocostudio/trigger/ObjectFactory.js create mode 100644 extensions/cocostudio/trigger/TriggerBase.js create mode 100644 extensions/cocostudio/trigger/TriggerMng.js create mode 100644 extensions/cocostudio/trigger/TriggerObj.js create mode 100644 extensions/editbox/CCEditBox.js create mode 100644 extensions/editbox/CCdomNode.js create mode 100644 extensions/gui/control-extension/CCControl.js create mode 100644 extensions/gui/control-extension/CCControlButton.js create mode 100644 extensions/gui/control-extension/CCControlColourPicker.js create mode 100644 extensions/gui/control-extension/CCControlHuePicker.js create mode 100644 extensions/gui/control-extension/CCControlPotentiometer.js create mode 100644 extensions/gui/control-extension/CCControlSaturationBrightnessPicker.js create mode 100644 extensions/gui/control-extension/CCControlSlider.js create mode 100644 extensions/gui/control-extension/CCControlStepper.js create mode 100644 extensions/gui/control-extension/CCControlSwitch.js create mode 100644 extensions/gui/control-extension/CCControlUtils.js create mode 100644 extensions/gui/control-extension/CCInvocation.js create mode 100644 extensions/gui/control-extension/CCMenuPassive.js create mode 100644 extensions/gui/scrollview/CCScrollView.js create mode 100644 extensions/gui/scrollview/CCScrollViewCanvasRenderCmd.js create mode 100644 extensions/gui/scrollview/CCScrollViewWebGLRenderCmd.js create mode 100644 extensions/gui/scrollview/CCSorting.js create mode 100644 extensions/gui/scrollview/CCTableView.js create mode 100644 extensions/runtime/CCLoaderLayer.js create mode 100644 extensions/spine/CCSkeleton.js create mode 100644 extensions/spine/CCSkeletonAnimation.js create mode 100644 extensions/spine/CCSkeletonCanvasRenderCmd.js create mode 100644 extensions/spine/CCSkeletonWebGLRenderCmd.js create mode 100644 extensions/spine/Spine.js create mode 100644 external/box2d/box2d.js create mode 100644 external/chipmunk/chipmunk.js create mode 100644 external/gaf/GAFBoot.js create mode 100644 external/gaf/GAFMacros.js create mode 100644 external/gaf/Library/GAFAsset.js create mode 100644 external/gaf/Library/GAFAssetPreload.js create mode 100644 external/gaf/Library/GAFAtlasLoader.js create mode 100644 external/gaf/Library/GAFDataReader.js create mode 100644 external/gaf/Library/GAFLoader.js create mode 100644 external/gaf/Library/GAFMask.js create mode 100644 external/gaf/Library/GAFMaskProto.js create mode 100644 external/gaf/Library/GAFObject.js create mode 100644 external/gaf/Library/GAFShaderManager.js create mode 100644 external/gaf/Library/GAFShaders.js create mode 100644 external/gaf/Library/GAFSprite.js create mode 100644 external/gaf/Library/GAFSpriteCanvasRenderCmd.js create mode 100644 external/gaf/Library/GAFSpriteProto.js create mode 100644 external/gaf/Library/GAFSpriteWebGLRenderCmd.js create mode 100644 external/gaf/Library/GAFTags.js create mode 100644 external/gaf/Library/GAFTextField.js create mode 100644 external/gaf/Library/GAFTimeLine.js create mode 100644 external/gaf/Library/GAFTimeLineProto.js create mode 100644 external/gaf/gaf_viewer.css create mode 100644 external/gaf/gaf_viewer.html create mode 100644 external/gaf/gaf_viewer.js create mode 100644 external/pluginx/Plugin.js create mode 100644 external/pluginx/platform/facebook.js create mode 100644 external/pluginx/platform/facebook_sdk.js create mode 100644 external/socketio/socket.io.js create mode 100644 external/socketio/socket.io.min.js create mode 100644 gulp/tasks/build-cocos2d.js create mode 100644 gulp/tasks/build.js create mode 100644 gulp/tasks/clean.js create mode 100644 gulp/tasks/test.js create mode 100644 gulp/util/handleErrors.js create mode 100644 gulpfile.js create mode 100644 index.js create mode 100644 jsb_apis.js create mode 100644 jsb_predefine.js create mode 100644 licenses/LICENSE_cocos2d-html5.txt create mode 100644 licenses/LICENSE_cocos2d-x.txt create mode 100644 licenses/LICENSE_zlib.js.txt create mode 100644 moduleConfig.json create mode 100644 package.json create mode 100644 polyfill/bind.js create mode 100644 polyfill/index.js create mode 100644 polyfill/string.js create mode 100644 predefine.js create mode 100644 test/core.js create mode 100644 test/fixtures/assets/atlas.plist create mode 100644 test/fixtures/assets/atlas.png create mode 100644 test/fixtures/assets/bitmap-font.fnt create mode 100644 test/fixtures/assets/bitmap-font.png create mode 100644 test/fixtures/assets/button.png create mode 100644 test/fixtures/assets/particle.plist create mode 100644 test/fixtures/assets/sprite.jpg create mode 100644 test/fixtures/library/22/223456/1.ttf create mode 100644 test/fixtures/library/22/223459.json create mode 100644 test/index.js create mode 100644 test/lib/init.js create mode 100644 test/lib/runner.html create mode 100644 test/lib/spawn-runner.js create mode 100644 test/page.html create mode 100644 test/page.js create mode 100644 test/page/asset.js create mode 100644 test/page/bitmap-font.js create mode 100644 test/page/button.js create mode 100644 test/page/engine.js create mode 100644 test/page/index.js create mode 100644 test/page/label-ttf.js create mode 100644 test/page/node.js create mode 100644 test/page/particle.js create mode 100644 test/page/scale9-sprite.js create mode 100644 test/page/scene.js create mode 100644 test/page/sprite.js create mode 100644 test/qunit/assets/background.mp3 create mode 100644 test/qunit/assets/button.png create mode 100644 test/qunit/assets/library/12/123200.json create mode 100644 test/qunit/assets/library/12/1232218.json create mode 100644 test/qunit/assets/library/19/19851210.json create mode 100644 test/qunit/assets/library/65/65341123490 create mode 100644 test/qunit/assets/library/65/6545543 create mode 100644 test/qunit/assets/library/65/6545543.png create mode 100644 test/qunit/assets/library/74/748321.json create mode 100644 test/qunit/assets/library/74/748321.json.host create mode 100644 test/qunit/assets/library/deferred-loading/12/1232218.json create mode 100644 test/qunit/assets/library/deferred-loading/74/748321.json create mode 100644 test/qunit/lib/assert-deep-close.js create mode 100644 test/qunit/lib/qunit-assert-callback.js create mode 100644 test/qunit/lib/qunit-assert-close.js create mode 100644 test/qunit/lib/qunit-large-module.js create mode 100644 test/qunit/lib/qunit-runner.html create mode 100644 test/qunit/lib/qunit.css create mode 100644 test/qunit/lib/qunit.js create mode 100644 test/qunit/run-helper.sh create mode 100644 test/qunit/run.sh create mode 100644 test/qunit/server.js create mode 100644 test/qunit/unit/_callback-tester.js create mode 100644 test/qunit/unit/_init.js create mode 100644 test/qunit/unit/_polyfill-for-phantom.js create mode 100644 test/qunit/unit/test-animation.js create mode 100644 test/qunit/unit/test-asset-library.js create mode 100644 test/qunit/unit/test-attribute.js create mode 100644 test/qunit/unit/test-audioSource.js create mode 100644 test/qunit/unit/test-callbacks-invoker.js create mode 100644 test/qunit/unit/test-class.js create mode 100644 test/qunit/unit/test-color.js create mode 100644 test/qunit/unit/test-component.js create mode 100644 test/qunit/unit/test-deserialize.js create mode 100644 test/qunit/unit/test-instantiate.js create mode 100644 test/qunit/unit/test-js.js create mode 100644 test/qunit/unit/test-load-manager.js create mode 100644 test/qunit/unit/test-load-scene.js create mode 100644 test/qunit/unit/test-node-serialization.js create mode 100644 test/qunit/unit/test-node.js create mode 100644 test/qunit/unit/test-object.js create mode 100644 test/qunit/unit/test-prefab.js create mode 100644 test/qunit/unit/test-record-object.js create mode 100644 test/qunit/unit/test-serialize-missing-script.js create mode 100644 test/qunit/unit/test-serialize.js create mode 100644 test/qunit/unit/test-spriteRenderer.js create mode 100644 test/qunit/unit/test-utils.js create mode 100644 test/qunit/unit/test-vec2.js create mode 100644 test/test-env-page.js create mode 100644 test/test-env.js create mode 100644 test/visual-tests/.cocos-project.json create mode 100644 test/visual-tests/index.html create mode 100644 test/visual-tests/main.js create mode 100755 test/visual-tests/res/.gitignore create mode 100755 test/visual-tests/res/Hello.png create mode 100644 test/visual-tests/res/Images/BoilingFoam.plist create mode 100755 test/visual-tests/res/Images/Comet.png create mode 100755 test/visual-tests/res/Images/CyanSquare.png create mode 100755 test/visual-tests/res/Images/CyanTriangle.png create mode 100755 test/visual-tests/res/Images/ETC1.pkm create mode 100755 test/visual-tests/res/Images/Fog.png create mode 100755 test/visual-tests/res/Images/HelloWorld.png create mode 100755 test/visual-tests/res/Images/Icon.png create mode 100755 test/visual-tests/res/Images/MagentaSquare.png create mode 100755 test/visual-tests/res/Images/Pea.png create mode 100755 test/visual-tests/res/Images/SendScoreButton.png create mode 100755 test/visual-tests/res/Images/SendScoreButtonPressed.png create mode 100755 test/visual-tests/res/Images/SpinningPeas.png create mode 100755 test/visual-tests/res/Images/SpookyPeas.png create mode 100755 test/visual-tests/res/Images/YellowSquare.png create mode 100755 test/visual-tests/res/Images/YellowTriangle.png create mode 100644 test/visual-tests/res/Images/arrows-hd.png create mode 100755 test/visual-tests/res/Images/arrows.png create mode 100644 test/visual-tests/res/Images/arrowsBar-hd.png create mode 100755 test/visual-tests/res/Images/arrowsBar.png create mode 100755 test/visual-tests/res/Images/assetMgrBackground1.jpg create mode 100755 test/visual-tests/res/Images/assetMgrBackground2.png create mode 100755 test/visual-tests/res/Images/assetMgrBackground3.png create mode 100755 test/visual-tests/res/Images/atlastest.png create mode 100755 test/visual-tests/res/Images/b1.png create mode 100755 test/visual-tests/res/Images/b2.png create mode 100755 test/visual-tests/res/Images/background.png create mode 100755 test/visual-tests/res/Images/background1.jpg create mode 100755 test/visual-tests/res/Images/background1.png create mode 100755 test/visual-tests/res/Images/background2.jpg create mode 100755 test/visual-tests/res/Images/background2.png create mode 100755 test/visual-tests/res/Images/background3.jpg create mode 100755 test/visual-tests/res/Images/background3.png create mode 100755 test/visual-tests/res/Images/ball.png create mode 100755 test/visual-tests/res/Images/bitmapFontTest3.fnt create mode 100755 test/visual-tests/res/Images/bitmapFontTest3.png create mode 100755 test/visual-tests/res/Images/blocks.png create mode 100755 test/visual-tests/res/Images/blocks9.png create mode 100755 test/visual-tests/res/Images/blocks9r.png create mode 100755 test/visual-tests/res/Images/blocks9ss.plist create mode 100755 test/visual-tests/res/Images/blocks9ss.png create mode 100755 test/visual-tests/res/Images/blocks9ss.tps create mode 100755 test/visual-tests/res/Images/btn-about-normal-vertical.png create mode 100755 test/visual-tests/res/Images/btn-about-normal.png create mode 100755 test/visual-tests/res/Images/btn-about-selected.png create mode 100755 test/visual-tests/res/Images/btn-highscores-normal.png create mode 100755 test/visual-tests/res/Images/btn-highscores-selected.png create mode 100755 test/visual-tests/res/Images/btn-play-normal.png create mode 100755 test/visual-tests/res/Images/btn-play-selected.png create mode 100644 test/visual-tests/res/Images/bug12847_sprite.png create mode 100644 test/visual-tests/res/Images/bug12847_spriteframe.plist create mode 100644 test/visual-tests/res/Images/bug12847_spriteframe.png create mode 100755 test/visual-tests/res/Images/bugs/RetinaDisplay.jpg create mode 100755 test/visual-tests/res/Images/bugs/bug886.jpg create mode 100755 test/visual-tests/res/Images/bugs/bug886.png create mode 100755 test/visual-tests/res/Images/bugs/circle.plist create mode 100755 test/visual-tests/res/Images/bugs/circle.png create mode 100755 test/visual-tests/res/Images/bugs/corner.png create mode 100755 test/visual-tests/res/Images/bugs/edge.png create mode 100755 test/visual-tests/res/Images/bugs/fill.png create mode 100755 test/visual-tests/res/Images/bugs/picture.png create mode 100755 test/visual-tests/res/Images/close.png create mode 100644 test/visual-tests/res/Images/cocos-html5.png create mode 100644 test/visual-tests/res/Images/cocos2dbanner.png create mode 100644 test/visual-tests/res/Images/dot.png create mode 100755 test/visual-tests/res/Images/elephant1_Diffuse.png create mode 100755 test/visual-tests/res/Images/elephant1_Normal.png create mode 100755 test/visual-tests/res/Images/encryptedAtlas.plist create mode 100755 test/visual-tests/res/Images/encryptedAtlas.pvr.ccz create mode 100755 test/visual-tests/res/Images/encryptedAtlas.tps create mode 100755 test/visual-tests/res/Images/f1.png create mode 100755 test/visual-tests/res/Images/f2.png create mode 100644 test/visual-tests/res/Images/favicon.ico create mode 100755 test/visual-tests/res/Images/fire-grayscale.png create mode 100755 test/visual-tests/res/Images/fire.png create mode 100755 test/visual-tests/res/Images/fire_rgba8888.pvr create mode 100644 test/visual-tests/res/Images/fps_images.png create mode 100755 test/visual-tests/res/Images/grossini.png create mode 100755 test/visual-tests/res/Images/grossini_128x256_mipmap.pvr create mode 100755 test/visual-tests/res/Images/grossini_dance_01.png create mode 100755 test/visual-tests/res/Images/grossini_dance_02.png create mode 100755 test/visual-tests/res/Images/grossini_dance_03.png create mode 100755 test/visual-tests/res/Images/grossini_dance_04.png create mode 100755 test/visual-tests/res/Images/grossini_dance_05.png create mode 100755 test/visual-tests/res/Images/grossini_dance_06.png create mode 100755 test/visual-tests/res/Images/grossini_dance_07.png create mode 100755 test/visual-tests/res/Images/grossini_dance_08.png create mode 100755 test/visual-tests/res/Images/grossini_dance_09.png create mode 100755 test/visual-tests/res/Images/grossini_dance_10.png create mode 100755 test/visual-tests/res/Images/grossini_dance_11.png create mode 100755 test/visual-tests/res/Images/grossini_dance_12.png create mode 100755 test/visual-tests/res/Images/grossini_dance_13.png create mode 100755 test/visual-tests/res/Images/grossini_dance_14.png create mode 100755 test/visual-tests/res/Images/grossini_dance_atlas-mono.png create mode 100755 test/visual-tests/res/Images/grossini_dance_atlas.png create mode 100755 test/visual-tests/res/Images/grossini_dance_atlas_nomipmap.png create mode 100755 test/visual-tests/res/Images/grossini_pvr_rgba4444.pvr create mode 100755 test/visual-tests/res/Images/grossini_pvr_rgba8888.pvr create mode 100755 test/visual-tests/res/Images/grossinis_sister1-testalpha.png create mode 100755 test/visual-tests/res/Images/grossinis_sister1-testalpha.ppng create mode 100755 test/visual-tests/res/Images/grossinis_sister1-testalpha_nopremult.pvr create mode 100755 test/visual-tests/res/Images/grossinis_sister1-testalpha_premult.pvr create mode 100755 test/visual-tests/res/Images/grossinis_sister1.png create mode 100755 test/visual-tests/res/Images/grossinis_sister2.png create mode 100644 test/visual-tests/res/Images/heightfield64x64.raw create mode 100755 test/visual-tests/res/Images/hole_effect.png create mode 100755 test/visual-tests/res/Images/hole_stencil.png create mode 100755 test/visual-tests/res/Images/labelatlas.png create mode 100755 test/visual-tests/res/Images/logo-mipmap.pvr create mode 100755 test/visual-tests/res/Images/logo-nomipmap.pvr create mode 100644 test/visual-tests/res/Images/lookup-desktop.plist create mode 100644 test/visual-tests/res/Images/lookup-html5.plist create mode 100644 test/visual-tests/res/Images/lookup-mobile.plist create mode 100755 test/visual-tests/res/Images/menuitemsprite.png create mode 100644 test/visual-tests/res/Images/movement.png create mode 100755 test/visual-tests/res/Images/noise.png create mode 100755 test/visual-tests/res/Images/nonencryptedAtlas.plist create mode 100755 test/visual-tests/res/Images/nonencryptedAtlas.pvr.ccz create mode 100755 test/visual-tests/res/Images/nonencryptedAtlas.tps create mode 100755 test/visual-tests/res/Images/paddle.png create mode 100644 test/visual-tests/res/Images/particles-hd.png create mode 100755 test/visual-tests/res/Images/particles.png create mode 100755 test/visual-tests/res/Images/pattern1.png create mode 100755 test/visual-tests/res/Images/piece.png create mode 100755 test/visual-tests/res/Images/powered.png create mode 100755 test/visual-tests/res/Images/r1.png create mode 100755 test/visual-tests/res/Images/r2.png create mode 100644 test/visual-tests/res/Images/shapemode.png create mode 100755 test/visual-tests/res/Images/snow.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-0-0.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-0-1.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-0-2.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-0-3.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-0-4.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-0-5.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-0-6.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-0-7.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-1-0.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-1-1.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-1-2.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-1-3.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-1-4.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-1-5.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-1-6.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-1-7.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-2-0.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-2-1.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-2-2.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-2-3.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-2-4.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-2-5.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-2-6.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-2-7.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-3-0.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-3-1.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-3-2.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-3-3.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-3-4.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-3-5.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-3-6.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-3-7.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-4-0.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-4-1.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-4-2.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-4-3.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-4-4.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-4-5.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-4-6.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-4-7.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-5-0.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-5-1.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-5-2.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-5-3.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-5-4.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-5-5.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-5-6.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-5-7.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-6-0.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-6-1.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-6-2.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-6-3.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-6-4.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-6-5.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-6-6.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-6-7.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-7-0.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-7-1.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-7-2.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-7-3.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-7-4.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-7-5.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-7-6.png create mode 100755 test/visual-tests/res/Images/sprites_test/sprite-7-7.png create mode 100755 test/visual-tests/res/Images/stars-grayscale.png create mode 100755 test/visual-tests/res/Images/stars.png create mode 100755 test/visual-tests/res/Images/stars2-grayscale.png create mode 100755 test/visual-tests/res/Images/stars2.png create mode 100755 test/visual-tests/res/Images/stone.png create mode 100755 test/visual-tests/res/Images/streak.png create mode 100755 test/visual-tests/res/Images/test-rgba1.png create mode 100755 test/visual-tests/res/Images/test_1021x1024_a8.pvr.gz create mode 100755 test/visual-tests/res/Images/test_256x256_ATC_RGBA_Explicit_mipmaps.ktx create mode 100755 test/visual-tests/res/Images/test_256x256_ATC_RGBA_Interpolated_mipmaps.ktx create mode 100755 test/visual-tests/res/Images/test_256x256_ATC_RGB_mipmaps.ktx create mode 100755 test/visual-tests/res/Images/test_256x256_s3tc_dxt1_mipmaps.dds create mode 100755 test/visual-tests/res/Images/test_256x256_s3tc_dxt3_mipmaps.dds create mode 100755 test/visual-tests/res/Images/test_256x256_s3tc_dxt5_mipmaps.dds create mode 100755 test/visual-tests/res/Images/test_512x512_s3tc_dxt5_with_no_mipmaps.dds create mode 100755 test/visual-tests/res/Images/test_blend.png create mode 100755 test/visual-tests/res/Images/test_image-bad_encoding.pvr create mode 100755 test/visual-tests/res/Images/test_image.jpeg create mode 100755 test/visual-tests/res/Images/test_image.png create mode 100755 test/visual-tests/res/Images/test_image.pvr create mode 100755 test/visual-tests/res/Images/test_image.pvrraw create mode 100755 test/visual-tests/res/Images/test_image.tiff create mode 100755 test/visual-tests/res/Images/test_image.webp create mode 100644 test/visual-tests/res/Images/test_image_a8.pvr create mode 100755 test/visual-tests/res/Images/test_image_a8_v3.pvr create mode 100755 test/visual-tests/res/Images/test_image_ai88.png create mode 100755 test/visual-tests/res/Images/test_image_ai88.pvr create mode 100755 test/visual-tests/res/Images/test_image_ai88_v3.pvr create mode 100755 test/visual-tests/res/Images/test_image_bgra8888.pvr create mode 100755 test/visual-tests/res/Images/test_image_bgra8888_v3.pvr create mode 100755 test/visual-tests/res/Images/test_image_i8.png create mode 100755 test/visual-tests/res/Images/test_image_i8.pvr create mode 100755 test/visual-tests/res/Images/test_image_i8_v3.pvr create mode 100755 test/visual-tests/res/Images/test_image_pvrtc2bpp.pvr create mode 100755 test/visual-tests/res/Images/test_image_pvrtc2bpp_v3.pvr create mode 100755 test/visual-tests/res/Images/test_image_pvrtc4bpp.pvr create mode 100755 test/visual-tests/res/Images/test_image_pvrtc4bpp_v3.pvr create mode 100755 test/visual-tests/res/Images/test_image_pvrtcii2bpp_v3.pvr create mode 100755 test/visual-tests/res/Images/test_image_pvrtcii4bpp_v3.pvr create mode 100755 test/visual-tests/res/Images/test_image_rgb565.pvr create mode 100755 test/visual-tests/res/Images/test_image_rgb565_v3.pvr create mode 100755 test/visual-tests/res/Images/test_image_rgb888.png create mode 100755 test/visual-tests/res/Images/test_image_rgb888.pvr create mode 100755 test/visual-tests/res/Images/test_image_rgb888_v3.pvr create mode 100755 test/visual-tests/res/Images/test_image_rgba4444.pvr create mode 100755 test/visual-tests/res/Images/test_image_rgba4444.pvr.ccz create mode 100755 test/visual-tests/res/Images/test_image_rgba4444.pvr.gz create mode 100755 test/visual-tests/res/Images/test_image_rgba4444_mipmap.pvr create mode 100755 test/visual-tests/res/Images/test_image_rgba4444_v3.pvr create mode 100755 test/visual-tests/res/Images/test_image_rgba5551.pvr create mode 100755 test/visual-tests/res/Images/test_image_rgba5551_v3.pvr create mode 100755 test/visual-tests/res/Images/test_image_rgba8888.png create mode 100755 test/visual-tests/res/Images/test_image_rgba8888.pvr create mode 100755 test/visual-tests/res/Images/test_image_rgba8888_v3.pvr create mode 100755 test/visual-tests/res/Images/texture1024x1024.png create mode 100755 test/visual-tests/res/Images/texture2048x2048.png create mode 100755 test/visual-tests/res/Images/texture512x512.png create mode 100644 test/visual-tests/res/Images/texturemode.png create mode 100755 test/visual-tests/res/Images/ui.plist create mode 100755 test/visual-tests/res/Images/ui.png create mode 100755 test/visual-tests/res/Images/water_2_dxt1.dds create mode 100755 test/visual-tests/res/Images/water_2_dxt3.dds create mode 100755 test/visual-tests/res/Images/water_2_dxt5.dds create mode 100755 test/visual-tests/res/Images/white-512x512.png create mode 100644 test/visual-tests/res/Images/wood.jpg create mode 100644 test/visual-tests/res/Manifests/AMTestScene1/project.manifest create mode 100644 test/visual-tests/res/Manifests/AMTestScene2/project.manifest create mode 100644 test/visual-tests/res/Manifests/AMTestScene3/project.manifest create mode 100755 test/visual-tests/res/Manifests/AMTestScene4/project.manifest create mode 100644 test/visual-tests/res/Manifests/ScriptTest/project.manifest create mode 100644 test/visual-tests/res/Particles/BoilingFoam.plist create mode 100644 test/visual-tests/res/Particles/BoilingFoamStar.plist create mode 100644 test/visual-tests/res/Particles/BurstPipe.plist create mode 100644 test/visual-tests/res/Particles/ButterFly.plist create mode 100644 test/visual-tests/res/Particles/ButterFlyYFlipped.plist create mode 100644 test/visual-tests/res/Particles/Comet.plist create mode 100644 test/visual-tests/res/Particles/ExplodingRing.plist create mode 100644 test/visual-tests/res/Particles/Flower.plist create mode 100644 test/visual-tests/res/Particles/Galaxy.plist create mode 100644 test/visual-tests/res/Particles/LavaFlow.plist create mode 100644 test/visual-tests/res/Particles/Phoenix.plist create mode 100644 test/visual-tests/res/Particles/SmallSun.plist create mode 100644 test/visual-tests/res/Particles/SpinningPeas.plist create mode 100644 test/visual-tests/res/Particles/Spiral.plist create mode 100644 test/visual-tests/res/Particles/SpookyPeas.plist create mode 100644 test/visual-tests/res/Particles/TestPremultipliedAlpha.plist create mode 100644 test/visual-tests/res/Particles/Upsidedown.plist create mode 100644 test/visual-tests/res/Particles/debian.plist create mode 100644 test/visual-tests/res/Particles/lines.plist create mode 100644 test/visual-tests/res/Shaders/example_3D_PositionTex.fsh create mode 100644 test/visual-tests/res/Shaders/example_3D_PositionTex.vsh create mode 100644 test/visual-tests/res/Shaders/example_Bloom.fsh create mode 100644 test/visual-tests/res/Shaders/example_Blur.fsh create mode 100644 test/visual-tests/res/Shaders/example_Blur_winrt.fsh create mode 100644 test/visual-tests/res/Shaders/example_CelShading.fsh create mode 100644 test/visual-tests/res/Shaders/example_ColorBars.fsh create mode 100644 test/visual-tests/res/Shaders/example_ColorBars.vsh create mode 100644 test/visual-tests/res/Shaders/example_EdgeDetection.fsh create mode 100644 test/visual-tests/res/Shaders/example_Flower.fsh create mode 100644 test/visual-tests/res/Shaders/example_Flower.vsh create mode 100644 test/visual-tests/res/Shaders/example_GreyScale.fsh create mode 100644 test/visual-tests/res/Shaders/example_Heart.fsh create mode 100644 test/visual-tests/res/Shaders/example_Heart.vsh create mode 100644 test/visual-tests/res/Shaders/example_HorizontalColor.fsh create mode 100644 test/visual-tests/res/Shaders/example_Julia.fsh create mode 100644 test/visual-tests/res/Shaders/example_Julia.vsh create mode 100644 test/visual-tests/res/Shaders/example_LensFlare.fsh create mode 100644 test/visual-tests/res/Shaders/example_Mandelbrot.fsh create mode 100644 test/visual-tests/res/Shaders/example_Mandelbrot.vsh create mode 100644 test/visual-tests/res/Shaders/example_Monjori.fsh create mode 100644 test/visual-tests/res/Shaders/example_Monjori.vsh create mode 100644 test/visual-tests/res/Shaders/example_MultiTexture.fsh create mode 100644 test/visual-tests/res/Shaders/example_MultiTexture.vsh create mode 100644 test/visual-tests/res/Shaders/example_Noisy.fsh create mode 100644 test/visual-tests/res/Shaders/example_Normal.fsh create mode 100644 test/visual-tests/res/Shaders/example_Outline.fsh create mode 100644 test/visual-tests/res/Shaders/example_Outline.vsh create mode 100644 test/visual-tests/res/Shaders/example_Outline_noMVP.vsh create mode 100644 test/visual-tests/res/Shaders/example_Plasma.fsh create mode 100644 test/visual-tests/res/Shaders/example_Plasma.vsh create mode 100644 test/visual-tests/res/Shaders/example_Sepia.fsh create mode 100644 test/visual-tests/res/Shaders/example_Simple.vsh create mode 100644 test/visual-tests/res/Shaders/example_Twist.fsh create mode 100644 test/visual-tests/res/Shaders/example_Twist.vsh create mode 100644 test/visual-tests/res/Shaders/shadertoy_FireBall.fsh create mode 100644 test/visual-tests/res/Shaders/shadertoy_Glow.fsh create mode 100644 test/visual-tests/res/Shaders/shadertoy_LensFlare.fsh create mode 100644 test/visual-tests/res/Test.html create mode 100755 test/visual-tests/res/TileMaps/fixed-ortho-test2.png create mode 100755 test/visual-tests/res/TileMaps/grass.png create mode 100755 test/visual-tests/res/TileMaps/grass01.png create mode 100755 test/visual-tests/res/TileMaps/hexa-test.tmx create mode 100755 test/visual-tests/res/TileMaps/hexa-tiles.png create mode 100755 test/visual-tests/res/TileMaps/iso-test-bug787.tmx create mode 100755 test/visual-tests/res/TileMaps/iso-test-movelayer.tmx create mode 100755 test/visual-tests/res/TileMaps/iso-test-objectgroup.tmx create mode 100755 test/visual-tests/res/TileMaps/iso-test-vertexz.tmx create mode 100755 test/visual-tests/res/TileMaps/iso-test-zorder.tmx create mode 100755 test/visual-tests/res/TileMaps/iso-test.png create mode 100755 test/visual-tests/res/TileMaps/iso-test.tmx create mode 100755 test/visual-tests/res/TileMaps/iso-test1.tmx create mode 100755 test/visual-tests/res/TileMaps/iso-test2-uncompressed.tmx create mode 100755 test/visual-tests/res/TileMaps/iso-test2.png create mode 100755 test/visual-tests/res/TileMaps/iso-test2.tmx create mode 100755 test/visual-tests/res/TileMaps/iso.png create mode 100755 test/visual-tests/res/TileMaps/levelmap.tga create mode 100755 test/visual-tests/res/TileMaps/ortho-objects.tmx create mode 100755 test/visual-tests/res/TileMaps/ortho-rotation-test.tmx create mode 100755 test/visual-tests/res/TileMaps/ortho-test1.png create mode 100755 test/visual-tests/res/TileMaps/ortho-test1_bw.png create mode 100755 test/visual-tests/res/TileMaps/ortho-test2.png create mode 100755 test/visual-tests/res/TileMaps/ortho-tile-property.tmx create mode 100755 test/visual-tests/res/TileMaps/orthogonal-test-movelayer.tmx create mode 100755 test/visual-tests/res/TileMaps/orthogonal-test-vertexz.tmx create mode 100755 test/visual-tests/res/TileMaps/orthogonal-test-zorder.tmx create mode 100755 test/visual-tests/res/TileMaps/orthogonal-test1.tmx create mode 100755 test/visual-tests/res/TileMaps/orthogonal-test1.tsx create mode 100755 test/visual-tests/res/TileMaps/orthogonal-test2.tmx create mode 100755 test/visual-tests/res/TileMaps/orthogonal-test3.tmx create mode 100644 test/visual-tests/res/TileMaps/orthogonal-test4-hd.tmx create mode 100755 test/visual-tests/res/TileMaps/orthogonal-test4.tmx create mode 100755 test/visual-tests/res/TileMaps/orthogonal-test5.tmx create mode 100644 test/visual-tests/res/TileMaps/orthogonal-test6-hd.tmx create mode 100755 test/visual-tests/res/TileMaps/orthogonal-test6.tmx create mode 100755 test/visual-tests/res/TileMaps/test-object-layer.tmx create mode 100755 test/visual-tests/res/TileMaps/test-staggered.tmx create mode 100644 test/visual-tests/res/TileMaps/tile_iso_offset.png create mode 100644 test/visual-tests/res/TileMaps/tile_iso_offset.tmx create mode 100644 test/visual-tests/res/TileMaps/tiles-hd.png create mode 100755 test/visual-tests/res/TileMaps/tiles.png create mode 100644 test/visual-tests/res/TileMaps/tmw_desert_spacing-hd.png create mode 100755 test/visual-tests/res/TileMaps/tmw_desert_spacing.png create mode 100755 test/visual-tests/res/TileMaps/xml-test.tmx create mode 100755 test/visual-tests/res/TileMaps/xml-test.tsx create mode 100755 test/visual-tests/res/animations/animations-2.plist create mode 100755 test/visual-tests/res/animations/animations.plist create mode 100644 test/visual-tests/res/animations/crystals.plist create mode 100644 test/visual-tests/res/animations/crystals.png create mode 100644 test/visual-tests/res/animations/dragon_animation-hd.png create mode 100755 test/visual-tests/res/animations/dragon_animation.png create mode 100755 test/visual-tests/res/animations/ghosts.plist create mode 100755 test/visual-tests/res/animations/ghosts.png create mode 100755 test/visual-tests/res/animations/grossini-aliases.plist create mode 100755 test/visual-tests/res/animations/grossini-aliases.png create mode 100755 test/visual-tests/res/animations/grossini.plist create mode 100755 test/visual-tests/res/animations/grossini.plist.xml create mode 100755 test/visual-tests/res/animations/grossini.png create mode 100755 test/visual-tests/res/animations/grossini.pvr.gz create mode 100755 test/visual-tests/res/animations/grossini.zss create mode 100755 test/visual-tests/res/animations/grossini.ztp create mode 100755 test/visual-tests/res/animations/grossini_blue.plist create mode 100755 test/visual-tests/res/animations/grossini_blue.png create mode 100755 test/visual-tests/res/animations/grossini_family.plist create mode 100755 test/visual-tests/res/animations/grossini_family.png create mode 100755 test/visual-tests/res/animations/grossini_gray.plist create mode 100755 test/visual-tests/res/animations/grossini_gray.png create mode 100644 test/visual-tests/res/animations/tcc_issue_1.plist create mode 100644 test/visual-tests/res/animations/tcc_issue_1.png create mode 100644 test/visual-tests/res/animations/tcc_issue_2.plist create mode 100644 test/visual-tests/res/animations/tcc_issue_2.png create mode 100644 test/visual-tests/res/audio/LuckyDay.mp3 create mode 100644 test/visual-tests/res/audio/SoundEffectsFX009/FX081.mp3 create mode 100644 test/visual-tests/res/audio/SoundEffectsFX009/FX082.mp3 create mode 100644 test/visual-tests/res/audio/SoundEffectsFX009/FX083.mp3 create mode 100644 test/visual-tests/res/audio/SoundEffectsFX009/FX084.mp3 create mode 100644 test/visual-tests/res/audio/SoundEffectsFX009/FX085.mp3 create mode 100644 test/visual-tests/res/audio/SoundEffectsFX009/FX086.mp3 create mode 100644 test/visual-tests/res/audio/SoundEffectsFX009/FX087.mp3 create mode 100644 test/visual-tests/res/audio/SoundEffectsFX009/FX088.mp3 create mode 100644 test/visual-tests/res/audio/SoundEffectsFX009/FX089.mp3 create mode 100644 test/visual-tests/res/audio/SoundEffectsFX009/FX090.mp3 create mode 100644 test/visual-tests/res/audio/SoundEffectsFX009/README.txt create mode 100644 test/visual-tests/res/background-music-aac.mp3 create mode 100755 test/visual-tests/res/background.caf create mode 100755 test/visual-tests/res/background.mp3 create mode 100755 test/visual-tests/res/background.ogg create mode 100644 test/visual-tests/res/ccs-res/XueJ2312F.ttf create mode 100644 test/visual-tests/res/ccs-res/cocosui/100/100.ExportJson create mode 100644 test/visual-tests/res/ccs-res/cocosui/100/100.csb create mode 100644 test/visual-tests/res/ccs-res/cocosui/100/1000.plist create mode 100644 test/visual-tests/res/ccs-res/cocosui/100/1000.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/CloseNormal.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/CloseSelected.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/CocoGUISample.json create mode 100644 test/visual-tests/res/ccs-res/cocosui/CocoGUI_PageView_Sample.csb create mode 100644 test/visual-tests/res/ccs-res/cocosui/CocoGUI_PageView_Sample.json create mode 100644 test/visual-tests/res/ccs-res/cocosui/CustomImageViewTest/NewProject_20.plist create mode 100644 test/visual-tests/res/ccs-res/cocosui/CustomImageViewTest/NewProject_20.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/CustomImageViewTest/NewProject_2_1.ExportJson create mode 100644 test/visual-tests/res/ccs-res/cocosui/CustomImageViewTest/NewProject_2_1.csb create mode 100755 test/visual-tests/res/ccs-res/cocosui/CustomTest/CustomWidgetCallbackBindTest/CustomWidgetCallbackBindTest.csb create mode 100755 test/visual-tests/res/ccs-res/cocosui/CustomTest/CustomWidgetCallbackBindTest/Default/Button_Disable.png create mode 100755 test/visual-tests/res/ccs-res/cocosui/CustomTest/CustomWidgetCallbackBindTest/Default/Button_Normal.png create mode 100755 test/visual-tests/res/ccs-res/cocosui/CustomTest/CustomWidgetCallbackBindTest/Default/Button_Press.png create mode 100755 test/visual-tests/res/ccs-res/cocosui/CustomTest/CustomWidgetCallbackBindTest/Default/CheckBoxNode_Disable.png create mode 100755 test/visual-tests/res/ccs-res/cocosui/CustomTest/CustomWidgetCallbackBindTest/Default/CheckBoxNode_Normal.png create mode 100755 test/visual-tests/res/ccs-res/cocosui/CustomTest/CustomWidgetCallbackBindTest/Default/CheckBox_Disable.png create mode 100755 test/visual-tests/res/ccs-res/cocosui/CustomTest/CustomWidgetCallbackBindTest/Default/CheckBox_Normal.png create mode 100755 test/visual-tests/res/ccs-res/cocosui/CustomTest/CustomWidgetCallbackBindTest/Default/CheckBox_Press.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/CustomTest/CustomWidgetCallbackBindTest/Layer.csb create mode 100644 test/visual-tests/res/ccs-res/cocosui/CustomTest/CustomWidgetCallbackBindTest/PageViewBugScene.csb create mode 100644 test/visual-tests/res/ccs-res/cocosui/Hello.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/Marker Felt.ttf create mode 100644 test/visual-tests/res/ccs-res/cocosui/UITest/UITest.csb create mode 100644 test/visual-tests/res/ccs-res/cocosui/UITest/UITest.json create mode 100644 test/visual-tests/res/ccs-res/cocosui/UITest/b1.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/UITest/b2.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/UITest/background.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/UITest/buttonBackground.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/UITest/f1.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/UITest/f2.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/UITest/r1.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/UITest/r2.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/UITest/ribbon.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/android9patch.plist create mode 100644 test/visual-tests/res/ccs-res/cocosui/android9patch.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/animationbuttonnormal.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/animationbuttonpressed.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/arrow.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/b11.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/backtotopnormal.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/backtotoppressed.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/bitmapFontTest2.fnt create mode 100644 test/visual-tests/res/ccs-res/cocosui/bitmapFontTest2.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/btn_exercise01_n.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/btn_exercise01_p.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/btn_exercise02_n.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/btn_exercise02_p.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/btn_exercise03_n.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/btn_exercise03_p.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/button.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/buttonHighlighted.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/ccicon.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/check_box_active.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/check_box_active_disable.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/check_box_active_press.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/check_box_normal.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/check_box_normal_disable.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/check_box_normal_press.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/Test/UIResForEditor/Button/button_country_n.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/Test/UIResForEditor/Button/button_country_p.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/Test/UIResForEditor/Button/button_country_un.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/Test/UIResForEditor/Button/symbol_1B.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/Test/UIResForEditor/Button/symbol_1a.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/Test/UIResForEditor/Button/symbol_1c.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/111.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/12.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/13.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/14.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/15.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/2.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/button_end_01.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/button_end_02.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/button_green_n2.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/button_green_p2.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/button_green_un2.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/eg/1.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/eg/10.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/eg/11.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/eg/3.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/eg/4.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/eg/5.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/eg/6.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/eg/7.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/eg/8.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/eg/9.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/eg/crab.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/eg/research.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/eg/sell.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/eg/shop_shield_1.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/eg/shop_shield_2.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/eg/shop_shield_3.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/eg/train.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/equip/eg/upgrade.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/examples.csb create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/examples.json create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/weapon_introduce/4.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/weapon_introduce/5.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/weapon_introduce/6.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/weapon_introduce/button_end_01.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/weapon_introduce/button_end_02.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/weapon_introduce/weapon_item_1/weapon_item/1.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/weapon_introduce/weapon_item_1/weapon_item/2.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/weapon_introduce/weapon_item_1/weapon_item/7.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/weapon_introduce/weapon_item_1/weapon_item_1.csb create mode 100644 test/visual-tests/res/ccs-res/cocosui/examples/weapon_introduce/weapon_item_1/weapon_item_1.json create mode 100644 test/visual-tests/res/ccs-res/cocosui/green_edit.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/grossini-aliases.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/UIAction_1/CocoStudio_UIEditor.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/UIAction_1/UIAction_1.csb create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/UIAction_1/UIAction_1.json create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/buy_1/5.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/buy_1/7.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/buy_1/button.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/buy_1/buttonHighlighted.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/buy_1/buy_1.csb create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/buy_1/buy_1.json create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/111.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/12.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/13.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/14.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/15.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/2.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/button_end_01.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/button_end_02.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/button_green_n2.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/button_green_p2.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/button_green_un2.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/eg/1.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/eg/10.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/eg/11.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/eg/3.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/eg/4.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/eg/5.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/eg/6.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/eg/7.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/eg/8.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/eg/9.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/eg/crab.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/eg/research.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/eg/sell.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/eg/shop_shield_1.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/eg/shop_shield_2.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/eg/shop_shield_3.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/eg/train.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip/eg/upgrade.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip_1.csb create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/equip_1/equip_1.json create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/map_1/image_castle.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/map_1/map_1.csb create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/map_1/map_1.json create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/map_1/map_pve.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/map_alert_1/5.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/map_alert_1/7.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/map_alert_1/close_02.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/map_alert_1/close_03.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/map_alert_1/close_04.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/map_alert_1/map_alert_1.csb create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/map_alert_1/map_alert_1.json create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/page_1/background.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/page_1/buttonBackground.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/page_1/page_1.csb create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/page_1/page_1.json create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/page_1/page_content/CocoStudio_AnimationEditor.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/page_1/page_content/CocoStudio_DataEditor.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/page_1/page_content/CocoStudio_SceneEditor.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/page_1/page_content/CocoStudio_UIEditor.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/page_1/ribbon.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/page_1/teehanlax - iOS 6 - iPhone_check.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/page_1/teehanlax - iOS 6 - iPhone_check01.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/register_1/128_128.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/register_1/Rosewood stdloadingH.fnt create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/register_1/Rosewood stdloadingH.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/register_1/button_d.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/register_1/button_n.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/register_1/e-mail.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/register_1/register_1.csb create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/register_1/register_1.json create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/register_1/ui_shop_005-hd.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_introduce/4.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_introduce/5.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_introduce/6.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_introduce/button_end_01.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_introduce/button_end_02.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_introduce_1.csb create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_introduce_1.json create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapon_item/1.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapon_item/2.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapon_item/7.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapon_item_1.csb create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapon_item_1.json create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_1.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_10.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_11.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_12.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_13.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_14.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_15.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_16.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_17.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_18.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_19.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_2.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_20.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_21.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_22.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_23.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_24.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_25.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_26.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_27.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_28.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_29.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_3.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_30.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_31.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_4.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_5.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_6.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_7.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_8.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/gui_examples/weapon_introduce_1/weapon_item_1/weapons/weapons_9.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/labelatlas.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/loadingbar.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/monster.9.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/player.9.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/radio_button_off.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/radio_button_on.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/scrollviewbg.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/slidbar.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/sliderProgress.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/sliderProgress2.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/sliderThumb.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/sliderTrack.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/sliderTrack2.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/slider_bar_active_9patch.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/slider_bar_active_9patch2.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/sliderballnormal.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/sliderballpressed.png create mode 100644 test/visual-tests/res/ccs-res/cocosui/switch-mask.png create mode 100755 test/visual-tests/res/configs/config-example.plist create mode 100755 test/visual-tests/res/configs/config-test-invalid.plist create mode 100755 test/visual-tests/res/configs/config-test-ok.plist create mode 100755 test/visual-tests/res/effect1.raw create mode 100755 test/visual-tests/res/effect1.wav create mode 100644 test/visual-tests/res/effect2.mp3 create mode 100755 test/visual-tests/res/effect2.ogg create mode 100755 test/visual-tests/res/extensions/CCControlColourPickerSpriteSheet.plist create mode 100755 test/visual-tests/res/extensions/CCControlColourPickerSpriteSheet.png create mode 100755 test/visual-tests/res/extensions/background.png create mode 100755 test/visual-tests/res/extensions/button.png create mode 100755 test/visual-tests/res/extensions/buttonBackground.png create mode 100755 test/visual-tests/res/extensions/buttonHighlighted.png create mode 100755 test/visual-tests/res/extensions/green_edit.png create mode 100755 test/visual-tests/res/extensions/orange_edit.png create mode 100755 test/visual-tests/res/extensions/potentiometerButton.png create mode 100755 test/visual-tests/res/extensions/potentiometerProgress.png create mode 100755 test/visual-tests/res/extensions/potentiometerTrack.png create mode 100755 test/visual-tests/res/extensions/ribbon.png create mode 100755 test/visual-tests/res/extensions/sliderProgress.png create mode 100755 test/visual-tests/res/extensions/sliderProgress2.png create mode 100755 test/visual-tests/res/extensions/sliderThumb.png create mode 100755 test/visual-tests/res/extensions/sliderTrack.png create mode 100755 test/visual-tests/res/extensions/sliderTrack2.png create mode 100755 test/visual-tests/res/extensions/stepper-minus.png create mode 100755 test/visual-tests/res/extensions/stepper-plus.png create mode 100755 test/visual-tests/res/extensions/switch-mask.png create mode 100755 test/visual-tests/res/extensions/switch-off.png create mode 100755 test/visual-tests/res/extensions/switch-on.png create mode 100755 test/visual-tests/res/extensions/switch-thumb.png create mode 100755 test/visual-tests/res/extensions/yellow_edit.png create mode 100755 test/visual-tests/res/fileLookup.plist create mode 100755 test/visual-tests/res/fonts/A Damn Mess.ttf create mode 100755 test/visual-tests/res/fonts/Abberancy.ttf create mode 100755 test/visual-tests/res/fonts/Abduction.ttf create mode 100755 test/visual-tests/res/fonts/American Typewriter.ttf create mode 100755 test/visual-tests/res/fonts/Courier New.ttf create mode 100644 test/visual-tests/res/fonts/Fingerpop.ttf create mode 100644 test/visual-tests/res/fonts/HKYuanMini.ttf create mode 100755 test/visual-tests/res/fonts/Japanese.ttf create mode 100755 test/visual-tests/res/fonts/Marker Felt.ttf create mode 100755 test/visual-tests/res/fonts/Paint Boy.ttf create mode 100755 test/visual-tests/res/fonts/Roboto.bmf.fnt create mode 100755 test/visual-tests/res/fonts/Roboto.bmf_0.png create mode 100644 test/visual-tests/res/fonts/Schwarzwald Regular.ttf create mode 100755 test/visual-tests/res/fonts/Schwarzwald.ttf create mode 100644 test/visual-tests/res/fonts/Schwarzwald_Regular.eot create mode 100755 test/visual-tests/res/fonts/Scissor Cuts.ttf create mode 100755 test/visual-tests/res/fonts/Thonburi.ttf create mode 100755 test/visual-tests/res/fonts/ThonburiBold.ttf create mode 100755 test/visual-tests/res/fonts/arial-26-en-ru.fnt create mode 100755 test/visual-tests/res/fonts/arial-26-en-ru_0.png create mode 100755 test/visual-tests/res/fonts/arial-unicode-26.GlyphProject create mode 100755 test/visual-tests/res/fonts/arial-unicode-26.fnt create mode 100755 test/visual-tests/res/fonts/arial-unicode-26.png create mode 100755 test/visual-tests/res/fonts/arial.ttf create mode 100755 test/visual-tests/res/fonts/arial16.fnt create mode 100755 test/visual-tests/res/fonts/arial16.png create mode 100755 test/visual-tests/res/fonts/bitmapFontChinese.fnt create mode 100755 test/visual-tests/res/fonts/bitmapFontChinese.png create mode 100755 test/visual-tests/res/fonts/bitmapFontTest.fnt create mode 100755 test/visual-tests/res/fonts/bitmapFontTest.png create mode 100755 test/visual-tests/res/fonts/bitmapFontTest2.bmp create mode 100755 test/visual-tests/res/fonts/bitmapFontTest2.fnt create mode 100755 test/visual-tests/res/fonts/bitmapFontTest2.png create mode 100755 test/visual-tests/res/fonts/bitmapFontTest3.fnt create mode 100755 test/visual-tests/res/fonts/bitmapFontTest3.png create mode 100755 test/visual-tests/res/fonts/bitmapFontTest4.fnt create mode 100755 test/visual-tests/res/fonts/bitmapFontTest4.png create mode 100755 test/visual-tests/res/fonts/bitmapFontTest5.fnt create mode 100755 test/visual-tests/res/fonts/bitmapFontTest5.png create mode 100755 test/visual-tests/res/fonts/boundsTestFont.fnt create mode 100755 test/visual-tests/res/fonts/boundsTestFont.png create mode 100755 test/visual-tests/res/fonts/cyril.ttf create mode 100755 test/visual-tests/res/fonts/cyrillic.ttf create mode 100755 test/visual-tests/res/fonts/font-issue1343-hd.fnt create mode 100755 test/visual-tests/res/fonts/font-issue1343-hd.png create mode 100755 test/visual-tests/res/fonts/font-issue1343.fnt create mode 100755 test/visual-tests/res/fonts/font-issue1343.png create mode 100755 test/visual-tests/res/fonts/futura-48.fnt create mode 100755 test/visual-tests/res/fonts/futura-48.png create mode 100755 test/visual-tests/res/fonts/geneva-32.fnt create mode 100755 test/visual-tests/res/fonts/helvetica-32.fnt create mode 100755 test/visual-tests/res/fonts/helvetica-geneva-32.png create mode 100644 test/visual-tests/res/fonts/konqa32-hd.fnt create mode 100644 test/visual-tests/res/fonts/konqa32-hd.png create mode 100755 test/visual-tests/res/fonts/konqa32.fnt create mode 100755 test/visual-tests/res/fonts/konqa32.png create mode 100755 test/visual-tests/res/fonts/labelatlas.png create mode 100644 test/visual-tests/res/fonts/larabie-16-hd.plist create mode 100644 test/visual-tests/res/fonts/larabie-16-hd.png create mode 100755 test/visual-tests/res/fonts/larabie-16.plist create mode 100755 test/visual-tests/res/fonts/larabie-16.png create mode 100644 test/visual-tests/res/fonts/markerFelt-hd.fnt create mode 100644 test/visual-tests/res/fonts/markerFelt-hd.png create mode 100755 test/visual-tests/res/fonts/markerFelt.fnt create mode 100755 test/visual-tests/res/fonts/markerFelt.png create mode 100755 test/visual-tests/res/fonts/strings.xml create mode 100755 test/visual-tests/res/fonts/tahoma.ttf create mode 100644 test/visual-tests/res/fonts/tuffy_bold_italic-charmap-hd.plist create mode 100644 test/visual-tests/res/fonts/tuffy_bold_italic-charmap-hd.png create mode 100755 test/visual-tests/res/fonts/tuffy_bold_italic-charmap.plist create mode 100755 test/visual-tests/res/fonts/tuffy_bold_italic-charmap.png create mode 100755 test/visual-tests/res/fonts/west_england-64.fnt create mode 100755 test/visual-tests/res/fonts/west_england-64.png create mode 100644 test/visual-tests/res/fonts/xingkai-incomplete.ttf create mode 100644 test/visual-tests/res/fps_images.png create mode 100755 test/visual-tests/res/music.mid create mode 100644 test/visual-tests/res/resjs/Shaders/example_ColorBars.fsh create mode 100644 test/visual-tests/res/resjs/Shaders/example_ColorBars.vsh create mode 100644 test/visual-tests/res/resjs/Shaders/example_Flower.fsh create mode 100644 test/visual-tests/res/resjs/Shaders/example_Flower.vsh create mode 100644 test/visual-tests/res/resjs/Shaders/example_Heart.fsh create mode 100644 test/visual-tests/res/resjs/Shaders/example_Heart.vsh create mode 100644 test/visual-tests/res/resjs/Shaders/example_Julia.fsh create mode 100644 test/visual-tests/res/resjs/Shaders/example_Julia.vsh create mode 100644 test/visual-tests/res/resjs/Shaders/example_Mandelbrot.fsh create mode 100644 test/visual-tests/res/resjs/Shaders/example_Mandelbrot.vsh create mode 100644 test/visual-tests/res/resjs/Shaders/example_Monjori.fsh create mode 100644 test/visual-tests/res/resjs/Shaders/example_Monjori.vsh create mode 100644 test/visual-tests/res/resjs/Shaders/example_Outline.fsh create mode 100644 test/visual-tests/res/resjs/Shaders/example_Outline.vsh create mode 100644 test/visual-tests/res/resjs/Shaders/example_Outline_noMVP.vsh create mode 100644 test/visual-tests/res/resjs/Shaders/example_Plasma.fsh create mode 100644 test/visual-tests/res/resjs/Shaders/example_Plasma.vsh create mode 100644 test/visual-tests/res/resjs/Shaders/example_Twist.fsh create mode 100644 test/visual-tests/res/resjs/Shaders/example_Twist.vsh create mode 100644 test/visual-tests/res/spine/goblins-ffd.atlas create mode 100644 test/visual-tests/res/spine/goblins-ffd.json create mode 100644 test/visual-tests/res/spine/goblins-ffd.png create mode 100755 test/visual-tests/res/spine/goblins.atlas create mode 100755 test/visual-tests/res/spine/goblins.json create mode 100755 test/visual-tests/res/spine/goblins.png create mode 100644 test/visual-tests/res/spine/raptor.atlas create mode 100644 test/visual-tests/res/spine/raptor.json create mode 100644 test/visual-tests/res/spine/raptor.png create mode 100644 test/visual-tests/res/spine/spineboy.atlas create mode 100644 test/visual-tests/res/spine/spineboy.json create mode 100644 test/visual-tests/res/spine/spineboy.png create mode 100644 test/visual-tests/res/spine/sprite.png create mode 100644 test/visual-tests/run-helper.sh create mode 100644 test/visual-tests/run.sh create mode 100644 test/visual-tests/server.js create mode 100644 test/visual-tests/src/ActionManagerTest/ActionManagerTest.js create mode 100644 test/visual-tests/src/ActionsTest/ActionsTest.js create mode 100644 test/visual-tests/src/BakeLayerTest/BakeLayerTest.js create mode 100644 test/visual-tests/src/BaseTestLayer/BaseTestLayer.js create mode 100644 test/visual-tests/src/ClippingNodeTest/ClippingNodeTest.js create mode 100644 test/visual-tests/src/CocosDenshionTest/CocosDenshionTest.js create mode 100644 test/visual-tests/src/CocosNodeTest/CocosNodeTest.js create mode 100644 test/visual-tests/src/DrawPrimitivesTest/DrawPrimitivesTest.js create mode 100644 test/visual-tests/src/EaseActionsTest/EaseActionsTest.js create mode 100644 test/visual-tests/src/ExtensionsTest/AssetsManagerTest/AssetsManagerTest.js create mode 100644 test/visual-tests/src/ExtensionsTest/CCPoolTest/CCPoolTest.js create mode 100644 test/visual-tests/src/ExtensionsTest/ExtensionsTest.js create mode 100644 test/visual-tests/src/ExtensionsTest/NetworkTest/SocketIOTest.js create mode 100644 test/visual-tests/src/ExtensionsTest/NetworkTest/WebSocketTest.js create mode 100644 test/visual-tests/src/ExtensionsTest/S9SpriteTest/S9SpriteTest.js create mode 100644 test/visual-tests/src/ExtensionsTest/TableViewTest/TableViewTestScene.js create mode 100644 test/visual-tests/src/GUITest/UIButtonTest/UIButtonTest.js create mode 100644 test/visual-tests/src/GUITest/UICheckBoxTest/UICheckBoxTest.js create mode 100644 test/visual-tests/src/GUITest/UIFocusTest/UIFocusTest.js create mode 100644 test/visual-tests/src/GUITest/UIImageViewTest/UIImageViewTest.js create mode 100644 test/visual-tests/src/GUITest/UILabelAtlasTest/UILabelAtlasTest.js create mode 100644 test/visual-tests/src/GUITest/UILabelBMFontTest/UILabelBMFontTest.js create mode 100644 test/visual-tests/src/GUITest/UILabelTest/UILabelTest.js create mode 100644 test/visual-tests/src/GUITest/UILayoutTest/UILayoutTest.js create mode 100644 test/visual-tests/src/GUITest/UIListViewTest/UIListViewTest.js create mode 100644 test/visual-tests/src/GUITest/UILoadingBarTest/UILoadingBarTest.js create mode 100644 test/visual-tests/src/GUITest/UINodeContainerTest/UINodeContainerTest.js create mode 100644 test/visual-tests/src/GUITest/UIPageViewTest/UIPageViewTest.js create mode 100644 test/visual-tests/src/GUITest/UIRichTextTest/UIRichTextTest.js create mode 100644 test/visual-tests/src/GUITest/UIS9NinePatchTest/UIS9NinePatchTest.js create mode 100644 test/visual-tests/src/GUITest/UIScene.js create mode 100644 test/visual-tests/src/GUITest/UISceneManager.js create mode 100644 test/visual-tests/src/GUITest/UIScrollViewTest/UIScrollViewTest.js create mode 100644 test/visual-tests/src/GUITest/UISliderTest/UISliderTest.js create mode 100644 test/visual-tests/src/GUITest/UITextFieldTest/UITextFieldTest.js create mode 100644 test/visual-tests/src/GUITest/UITextTest/UITextTest.js create mode 100644 test/visual-tests/src/GUITest/UIWebViewTest/UIWebViewTest.js create mode 100644 test/visual-tests/src/GUITest/UIWebViewTest/webview.html create mode 100644 test/visual-tests/src/GUITest/UIWebViewTest/webview2.html create mode 100644 test/visual-tests/src/IntervalTest/IntervalTest.js create mode 100644 test/visual-tests/src/LabelTest/LabelTest.js create mode 100644 test/visual-tests/src/LayerTest/LayerTest.js create mode 100644 test/visual-tests/src/LoaderTest/LoaderTest.js create mode 100644 test/visual-tests/src/NativeTest/AudioEngineTest.js create mode 100644 test/visual-tests/src/NativeTest/FileUtils/FileUtilsTest.js create mode 100644 test/visual-tests/src/NativeTest/JSBExtendTest.js create mode 100644 test/visual-tests/src/NativeTest/NativeTest.js create mode 100644 test/visual-tests/src/NewEventManagerTest/NewEventManagerTest.js create mode 100644 test/visual-tests/src/OpenGLTest/OpenGLTest.js create mode 100644 test/visual-tests/src/ParallaxTest/ParallaxTest.js create mode 100644 test/visual-tests/src/ParticleTest/ParticleTest.js create mode 100644 test/visual-tests/src/PathTest/PathTest.js create mode 100644 test/visual-tests/src/ProgressActionsTest/ProgressActionsTest.js create mode 100644 test/visual-tests/src/ReflectionTest/ReflectionTest.js create mode 100644 test/visual-tests/src/RenderTextureTest/RenderTextureTest.js create mode 100644 test/visual-tests/src/SchedulerTest/SchedulerTest.js create mode 100644 test/visual-tests/src/SpineTest/SpineTest.js create mode 100644 test/visual-tests/src/SpritePolygonTest/SpritePolygonTest.js create mode 100644 test/visual-tests/src/SpriteTest/SpriteTest.js create mode 100644 test/visual-tests/src/SysTest/ScriptTestTempFile.js create mode 100644 test/visual-tests/src/SysTest/SysTest.js create mode 100644 test/visual-tests/src/TextureCacheTest/TextureCacheTest.js create mode 100644 test/visual-tests/src/TileMapTest/TileMapTest.js create mode 100644 test/visual-tests/src/TouchesTest/Ball.js create mode 100644 test/visual-tests/src/TouchesTest/Paddle.js create mode 100644 test/visual-tests/src/TouchesTest/TouchesTest.js create mode 100644 test/visual-tests/src/XHRTest/XHRArrayBufferTest.js create mode 100644 test/visual-tests/src/XHRTest/XHRTest.js create mode 100644 test/visual-tests/src/tests-main.js create mode 100644 test/visual-tests/src/tests_resources.js create mode 100644 tools/XmlCheck.js create mode 100644 tools/compiler/compiler.jar create mode 100644 tools/core4cc.js create mode 100644 tools/genBuildXml.js create mode 100644 tools/publish.js create mode 100644 tools/readme for tools.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..8ba66418219 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +/lib +/bin +/web.config +.idea +.DS_Store +build +aspnet_client +node_modules +/tools/jsdoc_toolkit-2.4.0 +/package +/tools/jsdoc_toolkit/jsdoc_toolkit-2.4.0 +/.project diff --git a/AUTHORS.txt b/AUTHORS.txt new file mode 100644 index 00000000000..5a20dc7535b --- /dev/null +++ b/AUTHORS.txt @@ -0,0 +1,288 @@ +Cocos2d-html5 authors + +(Ordered by join time) + +Core Developers: + + Shun Lin (Sean Lin) + + Ricardo Quesada + + Huabin LING (@pandamicro) + + Sijie Wang (@VisualSJ) + + Long Jiang (@jianglong0156) + + Menghe Zhang (@ZhangMenghe) + +Contributors: +Name GithubID Main contribution + +Dali Kilani @dadilcool added instruction to read me + +Chris @hannon235 added node.js api for box2d + added SocketIO and SocketIO tests + +Jason Aeschliman @jaeschliman fixed cc.Node setposition + +Zhuoshi Sun(Intel) @sunzhuoshi engine loader fixed + +Alejandro Reyero @KaTXi fixed error message + +Long Xiang @seanlong resource loader count + +Timm Drevensek(Zynga) @timmjd cc.Class fixed + add CCScale9Sprite test + +Lzzy Chen @czizzy audio engine fixed + cleanup comments + +Mcscooter @mcscooter tilempapxml fixed + +Roed @roed Mouse rightclick api + Mouse scroll fix for firefox + cc.Screen bug fix + +Surith Thekkiam(Zynga) @folecr match Cocos2dx api + +Robert Boyd @rboyd fixed stackable actions + +Ivo Wetzel(Zynga Germany) @BonsaiDen Fixes for Audio Support Detection, + Texture Support in Particle System, + and Performance improvements + +Mario Adrian @many20 Code review, multi touch improvements, + ccbi bugs fixed + +keisuke hata(Square) @Seasons7 Code review, bug fix + +Marat Yakupov @moadib Various bug fixes + +Liang Wu @akira-cn Touch location fix for designResolution + ScrollView on paused bugs fix + +Jimmy Sambuo @jsambuo AudioEngine improvements + +Jose Antonio Andujar Clavell Scale9Sprite improvements + +TadeuszWlodarkiewic @TadeuszWlodarkiewic Tizen Compatibility + +smshuja @smshuja Parallax node bug fix + +rickms @rickms EGLView Optimizations + +Szymon Piłkowski @ardcore CCTexture Bug fix + +Tomasz Tunik @tomasztunik CCNode Memory leak fix + +Xuankang Lin(Intel) @AndriyLin cc.WebAudioEngine implements + +Kang-Hao Lu(Opera/Oupeng) @kennyluck Optimize John Resig's inheritance pattern + cc.clone improvements + +Mark Henderson @MarkEHenderson Code review, LabelTTF and Scale9Sprite bug fix + +Jing Wang @06wj CCScheduler improvements + Js file loading image add + cc.RectApplyAffineTransform improvements + Fixed a bug of cc.Node.setPosition that parameter check is incorrect + +Ze Wang @WanderWang Fix crash when BrowserTypes match nothing from navigator.userAgent + LabelTTF improvements + cc.TextureCache.dumpCachedTextureInfo 's bug fix + CCTween bug fix + CCScale9Sprite bug fix + sys.localStorage bug fix + CCArmature refactoring + cc.SpriteFrame and cc.SpriteFrameCache refactoring + +Christian Schwartz @cschwartz SpriteFrame.initWithTextureFilename converted fix + +XiaoJun Zheng @SmallAiTT _getResType error fix + cc.ScrollView bug fix + cc.LabelTTF bug fix + rewrite functions in CCNS.js with regex + refactor CCScheduler.js and CCCommon.js + cc.BMFontConfiguration bug fix + refactor cc.Application to cc.game + refactor cc.loader + refactor cc.Textur2D + refactor some functions about array operation + move sys.xxx to cc.sys.xxx + refactor some public functions in cc to private + add node.js scripts for publishing game + refactor cc.CCBReader + cc.view bug fix + multiple property object supports in extend function + +Guozhu Cheng @bengol cc.SimpleAudioEngine bug fix + +Jing Xiao @xbruce cc.SAXParser bug fix + cc.ActionManager refactoring + cc.Scheduler refactoring + cc.LabelTTF refactoring + +JiaHui He @garfield_ho cc.FileUtils bug fix + cc.Builder and SpriteLoader bugs fix + CCBAnimation bug fix + +Luis Parravicini @luisparravicini cc.Director refactoring + cc.LabelTTF documentation + typo fix + +Jose Antonio @jandujar cc.ParticleSystem bug fix for CocoStudio animations + SceneReader bug fix + +BoHao Tang @btspoony cc.TableView bug fix + +Claudiu @csaftoiu cc.LabelTTF bug fix + cc.DrawNode bugs fix + cc.NodeRGBA bug fix + cc.Texture2D bug fix + +Pei Wu @rablwupei cc.ProgressTimer bug fix + cc.ParticleBatchNode bug fix + +kuaipao @kuaipao CocoStudio GUIReader bug fix + CocoStudio UIScrollView bug fix + CocoStudio UIWidget bug fix + +XieDaijin @NijiadeIX CCControl bug fix + +samael @samael65535 CCPhysicsSprite bug fix + +NatWeiss @NatWeiss Add analytics plugin protocol ,Flurry plugin and ProtocolAds.js plugin protocol + cc.FileUtils refactoring + cc.Audio bugs fix + cc.Texture2D bug fix + +Andor Salga @asalga typo fix + +erykwalder @erykwalder Function.prototype.bind bug fix + +ZippoLag @ZippoLag cc.Application.getCurrentLanguage bug fix + typo fix + Fixed `cc.TMXObjectGroup#objectNamed` not returning the result bug + +Asano @LaercioAsano cc.Node bug fix + +Bruno Assarisse @bassarisse cc.LabelBMFont bug fix + +Mykyta Usikov @musikov cc.ClippingNode bugs fix + cc.fontLoader bug fix + Inverted ClippingNode with DrawNode as stencil bug fix under canvas render mode + JumpTo bug with wrong _delta position bug fix + cc.ProgressTimer bugs fix + cc.Scale9Sprite bugs fix + cc.RenderTexture bug fix + cc.ParticleSystem bug fix + Made CCProgressTimerCanvasRenderCmd to properly show colorized sprites + cc.ScrollView and cc.TableView: added check for parent visibility in onTouchBegan method + +Han XiaoLong @kpkhxlgy0 cc.ParticleSytem bug fix + +AaronRZH @AaronRZH Creation of a sequence objcet or a spawn object by using new method bug fix + +Xiaodong Liu @tianxing113 cc.Spawn.create bug fix + ccui.LoadingBar.setPercent crash bug fix + +Park Hyun Chen @sincntx Touch anywhere of screen to finish input when using cc.EditBox + ccui.TextBMFont bug fix + cc.game bug fix + Fixed an issue of cc.ArmatureAnimation's setMovementEventCallFunc + +Ninja Lau @mutoo A typo bug in UILayout fix + One-loop CCArmatureAnimation can't finish when setSpeedScale is less than 1.0 bug fix + A transform error in CCTransformHelp.js fix + ccs.DisplayManager bug fix + Fix child armature lost _parentBone issue + cc.eventManager bug fix + ccs.Bone bug fix + ccs.ActionFrame bug fix + ccui.Widget bug fix + ccui.LoadingBar bug fix + +Taras Tovchenko @tovchenko cc.Skin bounding box calculation bug fix under canvas render mode + +Minh Quy @MQuy cc.MenuItemSprite bug fix + Check empty string for textureData + Adds type check functions + +Michael Yin @layerssss cc.game refactored + +Yang Yuchen @yycdef cc.sys bug fix + +K @kiwigrc cc.ParticleSystem bug fix + +Claudio Freitas @claudiofreitas ccui.TextField typo fix. + +nopakos @nopakos cc.Texture2D bug fix + +Robert Rouhani @Robmaister cc.TMXMapInfo bug fix + cc.TMXLayer bug fix + +Igor Mats @IgorMats cc.Scale9Sprite bug fix + Spine runtime update + Add getStroke and setStroke method to cc.MotionStreak + +Tim @duhaibo0404 ccs.csLoader bug fix + +Hermanto @man2 cc.loader bug fix + +Long Jiang @jianglong0156 cc.LabelBMFont bug fix + KeyCode bug fix + ccui.ListView bug fix + +Joe Lafiosca @lafiosca Added Javascript file loader + +galapagosit @galapagosit ccs.actionManager bug fix + +Dany Ellement @DEllement cc.FontDefinition & ccui.RichText improvements + cc.LayerGradient improvements + +IShm @IShm cc.Screen bug fix + cc.ParticleSystem bug fix + ccui.PageView bug fix + Fixed crash when character not found into BMP font + Fixed restoring of sprite's color issue + +Thomas Jablonski @thomas-jablonski cc.audioEngine bug fix + Cocostudio typo fix + +WingGao @WingGao cc.TMXLayer bug fix + +Skliar Ihor @igogo5yo Add Bower support + +feijing566 @feijing566 cc.Audio bug fix + +RackovychV @RackovychV Fixed a bug of `cc.Scheduler`'s `pauseAllTargetsWithMinPriority` + +giuseppelt @giuseppelt Fixed TransitionSlideX callback sequence issue + +YShumov @pixmaster Fixed issue in music end event + +SPACELAN @spacelan Fixed `inverse` function bug of `cc.math.Matrix4` + +patriciog @patriciog Allowed timeline animations with only one frame + +Ningxin Hu @huningxin SIMD.js optimization for kazmath functions + +Zachary Lester @ZLester Fix typo in AUTHORS.txt + +Juan Carlos @Ruluk Fixed a bug where not resetting cc.Audio._ignoreEnded when replaying a sound caused it to stay in a "playing" state + +Maxim Litvinov @metalim Throw new Error object instead of error message string + +Retired Core Developers: + Shengxiang Chen (Nero Chan) + Xingsen Ma + Jialong Zhai (@JoshuaAstray) + Hao Wu (WuHao) + Dingping Lv (David Lv) + +Cocos2d-x and cocos2d-html5 can not grow so fast without the active community. + +Thanks to all developers who report & trace bugs, discuss the engine usage in forum & QQ groups! + +Special thanks to Ricardo Quesada for giving us lots of guidances & suggestions. diff --git a/Base64Images.js b/Base64Images.js new file mode 100644 index 00000000000..0d983506be8 --- /dev/null +++ b/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/CCBoot.js b/CCBoot.js new file mode 100644 index 00000000000..e9eb020bc1b --- /dev/null +++ b/CCBoot.js @@ -0,0 +1,343 @@ +/**************************************************************************** + 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 = 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; + +/** + * Device oriented vertically, home button on the bottom + * @constant + * @type {Number} + */ +cc.ORIENTATION_PORTRAIT = 0; + +/** + * Device oriented vertically, home button on the top + * @constant + * @type {Number} + */ +cc.ORIENTATION_PORTRAIT_UPSIDE_DOWN = 1; + +/** + * Device oriented horizontally, home button on the right + * @constant + * @type {Number} + */ +cc.ORIENTATION_LANDSCAPE_LEFT = 2; + +/** + * Device oriented horizontally, home button on the left + * @constant + * @type {Number} + */ +cc.ORIENTATION_LANDSCAPE_RIGHT = 3; + +/** + * 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; + +cc.isEditor = typeof Editor !== 'undefined'; + +/** + * 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; + } + } +}; + +/** + * 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; +}; + +/** + * 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; +}; + +require('../cocos2d/core/utils'); +require('../cocos2d/core/platform/CCLoader'); +require('../cocos2d/core/platform/CCSys'); + +require('../cocos2d/core/CCGame'); + + +//+++++++++++++++++++++++++Engine initialization function begin+++++++++++++++++++++++++++ +(function () { + +var _jsAddedCache = {}, //cache for js and module that has added into jsList to be loaded. + _engineInitCalled = false, + _engineLoadedCallback = null; + +cc._engineLoaded = false; + +function _determineRenderType(config) { + var CONFIG_KEY = cc.game.CONFIG_KEY, + userRenderMode = parseInt(config[CONFIG_KEY.renderMode]) || 0, + shieldOs = [cc.sys.OS_ANDROID], + shieldBrowser = []; + + // 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 = true; + + if ( userRenderMode === 2 || + ( userRenderMode === 0 && + shieldOs.indexOf(cc.sys.os) === -1 && + shieldBrowser.indexOf(cc.sys.browserType) === -1 )) { + if (cc.sys.capabilities["opengl"]) { + cc._renderType = cc.game.RENDER_TYPE_WEBGL; + cc._supportRender = true; + } + else { + cc._supportRender = false; + } + } + if (userRenderMode === 1 + || (userRenderMode === 0 && !cc._supportRender)) { + if (cc.sys.capabilities["canvas"]) { + cc._renderType = cc.game.RENDER_TYPE_CANVAS; + cc._supportRender = true; + } + else { + cc._supportRender = false; + } + } +} + +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) { + cc._initDebugSetting(config[cc.game.CONFIG_KEY.debugMode]); + cc._engineLoaded = true; + cc.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() { + window.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) : window.addEventListener('load', _windowLoaded, false); + _engineInitCalled = true; +} + +})(); +//+++++++++++++++++++++++++Engine initialization function end+++++++++++++++++++++++++++++ diff --git a/CCDebugger.js b/CCDebugger.js new file mode 100644 index 00000000000..ee1b2d273a2 --- /dev/null +++ b/CCDebugger.js @@ -0,0 +1,459 @@ +/**************************************************************************** + 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", + removeAction: "cocos2d: removeAction: Target not found", + removeActionByTag: "cc.ActionManager.removeActionByTag(): an invalid tag", + removeActionByTag_2: "cc.ActionManager.removeActionByTag(): target must be non-null", + getActionByTag: "cc.ActionManager.getActionByTag(): an invalid tag", + 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)", + loadConfigFile: "Expected 'data' dict, but not found. Config file: %s", + loadConfigFile_2: "Please load the resource first : %s", + }, + + Director: { + resume: "cocos2d: Director: Error in gettimeofday", + setProjection: "cocos2d: Director: unrecognized projection", + popToSceneStackLevel: "cocos2d: Director: unrecognized projection", + popToSceneStackLevel_2: "cocos2d: Director: Error in gettimeofday", + popScene: "running scene should not null", + pushScene: "the scene should not null", + }, + + Array: { + verifyType: "element type is wrong!", + }, + + deprecated: '"%s" is deprecated, please use "%s" instead.', + + Scheduler: { + scheduleCallbackForTarget: "CCSheduler#scheduleCallback. Callback already scheduled. Updating interval from:%s to %s", + scheduleCallbackForTarget_2: "cc.scheduler.scheduleCallbackForTarget(): callback_fn should be non-null.", + scheduleCallbackForTarget_3: "cc.scheduler.scheduleCallbackForTarget(): target should be non-null.", + pauseTarget: "cc.Scheduler.pauseTarget():target should be non-null", + resumeTarget: "cc.Scheduler.resumeTarget():target should be non-null", + isTargetPaused: "cc.Scheduler.isTargetPaused():target should be non-null", + }, + + Node: { + getZOrder: "getZOrder is deprecated. Please use getLocalZOrder instead.", + setZOrder: "setZOrder is deprecated. Please use setLocalZOrder instead.", + getRotation: "RotationX != RotationY. Don't know which one to return", + getScale: "ScaleX != ScaleY. Don't know which one to return", + addChild: "An Node can't be added as a child of itself.", + addChild_2: "child already added. It can't be added again", + addChild_3: "child must be non-null", + removeFromParentAndCleanup: "removeFromParentAndCleanup is deprecated. Use removeFromParent instead", + boundingBox: "boundingBox is deprecated. Use getBoundingBox instead", + removeChildByTag: "argument tag is an invalid tag", + removeChildByTag_2: "cocos2d: removeChildByTag(tag = %s): child not found!", + removeAllChildrenWithCleanup: "removeAllChildrenWithCleanup is deprecated. Use removeAllChildren instead", + stopActionByTag: "cc.Node.stopActionBy(): argument tag an invalid tag", + getActionByTag: "cc.Node.getActionByTag(): argument tag is an invalid tag", + reumeSchedulerAndActions: "resumeSchedulerAndActions is deprecated, please use resume instead.", + pauseSchedulerAndActions: "pauseSchedulerAndActions is deprecated, please use pause instead.", + _arrayMakeObjectsPerformSelector: "Unknown callback function", + reorderChild: "child must be non-null", + runAction: "cc.Node.runAction(): action must be non-null", + schedule: "callback function must be non-null", + schedule_2: "interval must be positive", + initWithTexture: "cocos2d: Could not initialize cc.AtlasNode. Invalid Texture." + }, + + AtlasNode: { + _updateAtlasValues: "cc.AtlasNode.updateAtlasValues(): Shall be overridden in subclasses", + _initWithTileFile: "", + _initWithTexture: "cocos2d: Could not initialize cc.AtlasNode. Invalid Texture.", + }, + + _checkEventListenerAvailable: { + keyboard: "cc._EventListenerKeyboard.checkAvailable(): Invalid EventListenerKeyboard!", + touchOneByOne: "cc._EventListenerTouchOneByOne.checkAvailable(): Invalid EventListenerTouchOneByOne!", + touchAllAtOnce: "cc._EventListenerTouchAllAtOnce.checkAvailable(): Invalid EventListenerTouchAllAtOnce!", + acceleration: "cc._EventListenerAcceleration.checkAvailable(): _onAccelerationEvent must be non-nil", + }, + + EventListener: { + create: "Invalid parameter.", + }, + + __getListenerID: "Don't call this method if the event is for touch.", + + LayerMultiplex: { + initWithLayers: "parameters should not be ending with null in Javascript", + switchTo: "Invalid index in MultiplexLayer switchTo message", + switchToAndReleaseMe: "Invalid index in MultiplexLayer switchTo message", + addLayer: "cc.Layer.addLayer(): layer should be non-null", + }, + + view: { + setDesignResolutionSize: "Resolution not valid", + 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.", + _addAnimationsWithDictionary_2: "cc.AnimationCache. Invalid animation format", + addAnimations: "cc.AnimationCache.addAnimations(): File could not be found", + _parseVersion1: "cocos2d: cc.AnimationCache: Animation '%s' found in dictionary without any frames - cannot add to animation cache.", + _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.", + _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.", + _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.", + _parseVersion2: "cocos2d: CCAnimationCache: Animation '%s' found in dictionary without any frames - cannot add to animation cache.", + _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.", + addAnimations_2: "cc.AnimationCache.addAnimations(): Invalid texture file name", + }, + + Sprite: { + reorderChild: "cc.Sprite.reorderChild(): this child is not in children list", + ignoreAnchorPointForPosition: "cc.Sprite.ignoreAnchorPointForPosition(): it is invalid in cc.Sprite when using SpriteBatchNode", + setDisplayFrameWithAnimationName: "cc.Sprite.setDisplayFrameWithAnimationName(): Frame not found", + setDisplayFrameWithAnimationName_2: "cc.Sprite.setDisplayFrameWithAnimationName(): Invalid frame index", + setDisplayFrame: "setDisplayFrame is deprecated, please use setSpriteFrame instead.", + _updateBlendFunc: "cc.Sprite._updateBlendFunc(): _updateBlendFunc doesn't work when the sprite is rendered using a cc.CCSpriteBatchNode", initWithSpriteFrame: "cc.Sprite.initWithSpriteFrame(): spriteFrame should be non-null", + initWithSpriteFrameName: "cc.Sprite.initWithSpriteFrameName(): spriteFrameName should be non-null", + initWithSpriteFrameName1: " is null, please check.", + initWithFile: "cc.Sprite.initWithFile(): filename should be non-null", + setDisplayFrameWithAnimationName_3: "cc.Sprite.setDisplayFrameWithAnimationName(): animationName must be non-null", + reorderChild_2: "cc.Sprite.reorderChild(): child should be non-null", + addChild: "cc.Sprite.addChild(): cc.Sprite only supports cc.Sprites as children when using cc.SpriteBatchNode", + addChild_2: "cc.Sprite.addChild(): cc.Sprite only supports a sprite using same texture as children when using cc.SpriteBatchNode", + addChild_3: "cc.Sprite.addChild(): child should be non-null", + setTexture: "cc.Sprite.texture setter: Batched sprites should use the same texture as the batchnode", + updateQuadFromSprite: "cc.SpriteBatchNode.updateQuadFromSprite(): cc.SpriteBatchNode only supports cc.Sprites as children", insertQuadFromSprite: "cc.SpriteBatchNode.insertQuadFromSprite(): cc.SpriteBatchNode only supports cc.Sprites as children", + addChild_4: "cc.SpriteBatchNode.addChild(): cc.SpriteBatchNode only supports cc.Sprites as children", + addChild_5: "cc.SpriteBatchNode.addChild(): cc.Sprite is not using the same texture", + initWithTexture: "Sprite.initWithTexture(): Argument must be non-nil ", + setSpriteFrame: "Invalid spriteFrameName", + setTexture_2: "Invalid argument: cc.Sprite.texture setter expects a CCTexture2D.", + updateQuadFromSprite_2: "cc.SpriteBatchNode.updateQuadFromSprite(): sprite should be non-null", + insertQuadFromSprite_2: "cc.SpriteBatchNode.insertQuadFromSprite(): sprite should be non-null", + }, + + SpriteBatchNode: { + addSpriteWithoutQuad: "cc.SpriteBatchNode.addQuadFromSprite(): SpriteBatchNode only supports cc.Sprites as children", + increaseAtlasCapacity: "cocos2d: CCSpriteBatchNode: resizing TextureAtlas capacity from %s to %s.", + increaseAtlasCapacity_2: "cocos2d: WARNING: Not enough memory to resize the atlas", + reorderChild: "cc.SpriteBatchNode.addChild(): Child doesn't belong to Sprite", + removeChild: "cc.SpriteBatchNode.addChild(): sprite batch node should contain the child", + addSpriteWithoutQuad_2: "cc.SpriteBatchNode.addQuadFromSprite(): child should be non-null", + reorderChild_2: "cc.SpriteBatchNode.addChild(): child should be non-null", + updateQuadFromSprite: "cc.SpriteBatchNode.updateQuadFromSprite(): cc.SpriteBatchNode only supports cc.Sprites as children", + insertQuadFromSprite: "cc.SpriteBatchNode.insertQuadFromSprite(): cc.SpriteBatchNode only supports cc.Sprites as children", + addChild: "cc.SpriteBatchNode.addChild(): cc.SpriteBatchNode only supports cc.Sprites as children", + initWithTexture: "Sprite.initWithTexture(): Argument must be non-nil ", + addChild_2: "cc.Sprite.addChild(): child should be non-null", + setSpriteFrame: "Invalid spriteFrameName", + setTexture: "Invalid argument: cc.Sprite texture setter expects a CCTexture2D.", + updateQuadFromSprite_2: "cc.SpriteBatchNode.updateQuadFromSprite(): sprite should be non-null", + insertQuadFromSprite_2: "cc.SpriteBatchNode.insertQuadFromSprite(): sprite should be non-null", + addChild_3: "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", + addSpriteFrames: "cocos2d: WARNING: an alias with name %s already exists", + _checkConflict: "cocos2d: WARNING: Sprite frame: %s has already been added by another source, please fix name conflit", + getSpriteFrame: "cocos2d: cc.SpriteFrameCahce: Frame %s not found", + _getFrameConfig_2: "Please load the resource first : %s", + addSpriteFrames_2: "cc.SpriteFrameCache.addSpriteFrames(): plist should be non-null", + addSpriteFrames_3: "Argument must be non-nil", + }, + + TextureAtlas: { + initWithFile: "cocos2d: Could not open file: %s", + insertQuad: "cc.TextureAtlas.insertQuad(): invalid totalQuads", + initWithTexture: "cc.TextureAtlas.initWithTexture():texture should be non-null", + updateQuad: "cc.TextureAtlas.updateQuad(): quad should be non-null", + updateQuad_2: "cc.TextureAtlas.updateQuad(): Invalid index", + insertQuad_2: "cc.TextureAtlas.insertQuad(): Invalid index", + insertQuads: "cc.TextureAtlas.insertQuad(): Invalid index + amount", + insertQuadFromIndex: "cc.TextureAtlas.insertQuadFromIndex(): Invalid newIndex", + insertQuadFromIndex_2: "cc.TextureAtlas.insertQuadFromIndex(): Invalid fromIndex", + removeQuadAtIndex: "cc.TextureAtlas.removeQuadAtIndex(): Invalid index", + removeQuadsAtIndex: "cc.TextureAtlas.removeQuadsAtIndex(): index + amount out of bounds", + moveQuadsFromIndex: "cc.TextureAtlas.moveQuadsFromIndex(): move is out of bounds", + moveQuadsFromIndex_2: "cc.TextureAtlas.moveQuadsFromIndex(): Invalid newIndex", + moveQuadsFromIndex_3: "cc.TextureAtlas.moveQuadsFromIndex(): Invalid oldIndex", + }, + + textureCache: { + addPVRTCImage: "TextureCache:addPVRTCImage does not support on HTML5", + addETCImage: "TextureCache:addPVRTCImage does not support on HTML5", + textureForKey: "textureForKey is deprecated. Please use getTextureForKey instead.", + addPVRImage: "addPVRImage does not support on HTML5", + addUIImage: "cocos2d: Couldn't add UIImage in TextureCache", + dumpCachedTextureInfo: "cocos2d: '%s' id=%s %s x %s", + dumpCachedTextureInfo_2: "cocos2d: '%s' id= HTMLCanvasElement %s x %s", + dumpCachedTextureInfo_3: "cocos2d: TextureCache dumpDebugInfo: %s textures, HTMLCanvasElement for %s KB (%s MB)", + addUIImage_2: "cc.Texture.addUIImage(): image should be non-null", + }, + + Texture2D: { + initWithETCFile: "initWithETCFile does not support on HTML5", + initWithPVRFile: "initWithPVRFile does not support on HTML5", + initWithPVRTCData: "initWithPVRTCData does not support on HTML5", + addImage: "cc.Texture.addImage(): path should be non-null", + initWithImage: "cocos2d: cc.Texture2D. Can't create Texture. UIImage is nil", + initWithImage_2: "cocos2d: WARNING: Image (%s x %s) is bigger than the supported %s x %s", + initWithString: "initWithString isn't supported on cocos2d-html5", + initWithETCFile_2: "initWithETCFile does not support on HTML5", + initWithPVRFile_2: "initWithPVRFile does not support on HTML5", + initWithPVRTCData_2: "initWithPVRTCData does not support on HTML5", + bitsPerPixelForFormat: "bitsPerPixelForFormat: %s, cannot give useful result, it's a illegal pixel format", + _initPremultipliedATextureWithImage: "cocos2d: cc.Texture2D: Using RGB565 texture since image has no alpha", + addImage_2: "cc.Texture.addImage(): path should be non-null", + 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: { + addListener: "0 priority is forbidden for fixed priority since it's used for scene graph based priority.", + removeListeners: "Invalid listener type!", + setPriority: "Can't set fixed priority with scene graph based listener.", + addListener_2: "Invalid parameters.", + addListener_3: "listener must be a cc.EventListener object when adding a fixed priority listener", + addListener_4: "The listener has been registered, please don't register it again.", + _forceAddEventListener: "Invalid scene graph priority!", + _updateListeners: "If program goes here, there should be event in dispatch.", + _updateListeners_2: "_inDispatch should be 1 here." + } +}; + +if (CC_EDITOR || CC_TEST) { + cc._LogInfos.Editor = { + Class: { + callSuperCtor: "cc.Class will automatically call super constructor of %s, you should not call it manually." + } + }; +} + +//+++++++++++++++++++++++++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.js.isObject(arg)) { + try { + return JSON.stringify(arg); + } catch (err) { + return ""; + } + } else + return arg; +}; + +/** + * Enum for debug modes. + * @readOnly + * @enum DebugMode + */ +cc.DebugMode = cc.Enum({ + /** + * @property {number} NONE - The debug mode none. + */ + NONE: 0, + /** + * @property {number} INFO - The debug mode info. + */ + INFO: 1, + /** + * @property {number} WARN - The debug mode warn. + */ + WARN: 2, + /** + * @property {number} ERROR - The debug mode error. + */ + ERROR: 3, + /** + * @property {number} INFO_FOR_WEB_PAGE - The debug mode info for web page. + */ + INFO_FOR_WEB_PAGE: 4, + /** + * @property {number} WARN_FOR_WEB_PAGE - The debug mode warn for web page. + */ + WARN_FOR_WEB_PAGE: 5, + /** + * @property {number} ERROR_FOR_WEB_PAGE - The debug mode error for web page. + */ + ERROR_FOR_WEB_PAGE: 6 +}); + +/** + * Init Debug setting. + * @function + * @param {cc.DebugMode} mode + */ +cc._initDebugSetting = function (mode) { + if(mode === cc.DebugMode.NONE) + return; + + var locLog; + if(mode > cc.DebugMode.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) { + 'use strict'; + 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 !== cc.DebugMode.ERROR_FOR_WEB_PAGE){ + cc.warn = function(){ + locLog("WARN : " + cc.formatStr.apply(cc, arguments)); + }; + } + if(mode === cc.DebugMode.INFO_FOR_WEB_PAGE){ + cc.log = cc.info = 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 + + /** + * Outputs an error message to the Fireball Console (editor) or Web Console (runtime). + * - In Fireball, error is red. + * - In Chrome, error have a red icon along with red message text. + * @param {any|string} obj - A JavaScript string containing zero or more substitution strings. + * @param {any} ...subst - JavaScript objects with which to replace substitution strings within msg. This gives you additional control over the format of the output. + */ + if (console.error.bind) { + // use bind to avoid pollute call stacks + cc.error = console.error.bind(console); + } + else { + cc.error = function(){ + return console.error.apply(console, 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])); + throw new Error(msg); + } + }; + if(mode !== cc.DebugMode.ERROR) + /** + * Outputs a warning message to the Fireball Console (editor) or Web Console (runtime). + * - In Fireball, warning is yellow. + * - In Chrome, warning have a yellow warning icon with the message text. + * @param {any|string} obj - A JavaScript string containing zero or more substitution strings. + * @param {any} ...subst - JavaScript objects with which to replace substitution strings within msg. This gives you additional control over the format of the output. + */ + cc.warn = function(){ + return console.warn.apply(console, arguments); + }; + if(mode === cc.DebugMode.INFO) { + /** + * Outputs a message to the Fireball Console (editor) or Web Console (runtime). + * @param {any|string} obj - A JavaScript string containing zero or more substitution strings. + * @param {any} ...subst - JavaScript objects with which to replace substitution strings within msg. This gives you additional control over the format of the output. + */ + cc.log = function () { + return console.log.apply(console, arguments); + }; + /** + * Outputs an informational message to the Fireball Console (editor) or Web Console (runtime). + * - In Fireball, info is blue. + * - In Firefox and Chrome, a small "i" icon is displayed next to these items in the Web Console's log. + * @param {any|string} obj - A JavaScript string containing zero or more substitution strings. + * @param {any} ...subst - JavaScript objects with which to replace substitution strings within msg. This gives you additional control over the format of the output. + */ + cc.info = function () { + (console.info || console.log).apply(console, arguments); + }; + } + } + cc._throw = function (error) { + cc.error(error.stack || error); + }; +}; +//+++++++++++++++++++++++++something about log end+++++++++++++++++++++++++++++ diff --git a/CHANGELOG.txt b/CHANGELOG.txt new file mode 100644 index 00000000000..14124a4cdcb --- /dev/null +++ b/CHANGELOG.txt @@ -0,0 +1,952 @@ +ChangeLog: + +Cocos2d-x v3.8 @ September.6 2015 + + [NEW] spine: Supported Spine runtime 2.3 (Both native and web engine) + [NEW] Animate: Added Animate's getCurrentFrameIndex function + [NEW] network: Upgrade SocketIO support to v1.x + + [REFINE] web: Avoid re-bake the content when the parent node's position get changed + [REFINE] web: Added GameNodeObjectData and GameLayerObjectData in JSON parser + [REFINE] web: Updated skeleton animation to the latest version + [REFINE] web: Optimized resources automatic loading in JSON parser + [REFINE] web: Avoid cc.loader resource loading being terminated while encounter errors + [REFINE] web: Throw new Error object instead of error message string + [REFINE] web: Move setDepthTest to renderer + [REFINE] web: Added BlendFuncFrame parser + [REFINE] web: Permitted webp image loading on Chrome + [REFINE] web: Suspended the video player when the browser is minimized + + [FIX] web: Fixed a bug that VideoPlayer remove event throw error + [FIX] web: Fixed Armature position error in studio JSON parser + [FIX] web: Fixed default clearColor error in director + [FIX] web: Fixed rotation value parsing error in the timeline parser + [FIX] web: Fixed a bug that nested animation may be affected by outer animation + [FIX] web: Made LabelAtlas ignoring invalid characters and updating correctly the content size + [FIX] web: Fixed a bug that VideoPlayer remove event throw error + [FIX] web: Fixed a bug that cc.director.setNotificationNode(null) doesn't take effect + [FIX] web: Fixed texture rect update issue while changing sprite frame + [FIX] web: Fixed effect issue in ActionGrid and NodeGrid + [FIX] web: Fixed logic issue in Menu's _onTouchCancelled function + [FIX] web: Fixed MenuItem crash when normal image is null + [FIX] web: Fixed incomplete fadeout effects + [FIX] web: Fixed issue that return value of cc.screen.fullScreen is not boolean + [FIX] web: Fixed a bug that SkeletonNode is not drawing children + + [TEST] web: Rewrote testcase for stencil depth mask in RenderTextureTest + [TEST] web: Improved renderTexture stencilDepth test + [TEST] web: Fixed abnormal effects in effectsTest + [TEST] web: Fixed invisiable testcase of effects + +Cocos2d-x v3.7.1 @ August.12 2015 + [HIGHLIGHT] studio: Added new skeleton animation support and JSON parser for cocos v2.3.2 beta + + [NEW] Node: Added getNodeToParentTransform with selected ancestor + [NEW] web: Added cc.director.setClearColor and support transparent background + [NEW] web: Added Animate's getCurrentFrameIndex function + + [REFINE] studio: Optimized JSON parser's performance by removing audio play + [REFINE] studio: Optimized editor related extension data to a component instead of hosting in _userObject + [REFINE] web: Improved color/opacity manipulations in MenuItems + + [FIX] studio: Fixed ccs.Skin construction issue in JSON parser + [FIX] web: Fixed an issue that loading process won't trigger callback problem + [FIX] web: Fixed a bug where not resetting cc.Audio._ignoreEnded when replaying a sound caused it to stay in a "playing" state + [FIX] web: cc.ScrollView and cc.TableView: added check for parent visibility in onTouchBegan method + [FIX] web: Fixed TurnPageDown effect + [FIX] web: Fixed Cocos Studio parser issue that all elements are missing while the timeline action contains rotation + +Cocos2d-x v3.7 Final @ July 20 2015 + [REFINE] web: Add compatible Uint16Array defintion + + [FIX] web: Fixed url check regular expression not supporting localhost issue + [FIX] web: Fixed issue that sprite doesn't update texture rect correctly + +Cocos2d-x v3.7 RC1 @ July 14 2015 + [REFINE] Improved localStorage warning when disabled + + [FIX] Fixed a bug that SingleNode's color isn't set + [FIX] studio: Fixed a bug of JSON parser that texture address is wrong + [FIX] Fixed MenuItems' color/opacity setter issue with child nodes + [FIX] Fixed page view's layout issue for JSON parser + [FIX] Add ttc loader and prevent the pure digital fonts is invalid + [FIX] Fixed Float32Array initialization + [FIX] Fixed a bug that layout background is missing + [FIX] Fixed a bug that ObjectExtensionData miss setCustomProperty and getCustomProperty function + +Cocos2d-x v3.7 RC0 @ July 1 2015 + +* The json loader of Cocos Studio will automatically load dependencies resources +* SIMD.js optimization for kazmath functions (from Intel) +* Deleted the redundant variables defined and log informations in ccui.RichText +* Allowed timeline animations with only one frame +* Improved property declaration of cc.Texture2D + +* Bug fixes: + 1. Fixed positionType error of particle system in timeline parser + 2. Fixed setAnimationName issue while the property is undefined in timeline parser + 3. Fixed `cc.TMXObjectGroup#objectNamed` not returning the result bug + 4. Fixed TransitionSlideX callback sequence issue + 5. Fixed issue in music end event + 6. Fixed bug that LayerColor's color will disappear when update transform after being baked + 7. Fixed `inverse` function bug of `cc.math.Matrix4` + 8. Fixed the webaudio's invalid loop attribute bug for chrome 42 + 9. Fixed crash when character not found into BMP font + 10. Fixed spine's js parser issue by avoid NaN duration + 11. Fixed LabelTTF multiline detection + 12. Fixed issue in ccui.Widget#getScale + 13. Fixed texture is not updated in some cases + 14. PlayMusic should not use the search path (timeline 2.x) + 15. Fixed bug of loading path of resources + 16. Premultiply texture's alpha for png by default to fix Cocos Studio render issues + 17. Fixed cache update issue of Layout after bake + 18. Fixed isBaked returning undefined issue + 19. Made CCProgressTimerCanvasRenderCmd to properly show colorized sprites + 20. Fixed attributes being reset issue while baked cache canvas' size changed + 21. Fixed texture does not rotate bug of ccui.LoadingBar + 22. Fixed color not being set issue in timeline parser + 23. Fixed custom easing animation bug + 24. Fixed return empty texture2d bug when adding image with same url multiple times + 25. Fixed actiontimeline can not step to last frame issue when loop play + 26. Fixed the prompt can not be used in iOS wechat 6.2 + 27. Fixed restoring of sprite's color issue + 28. Fixed Uint8Array initialize issue + 29. Fixed cc.TextFieldTTF Delegate memory leaks + 30. Fixed sorted result is wrong in cc.eventManager (_sortEventListenersOfSceneGraphPriorityDes) + 31. Fixed BinaryLoader issue on IE11 + 32. Fixed the sprite's texture bug when frequently change the color + 33. Fixed an issue that action will result in automatic termination + 34. Fixed ScrollView initWithViewSize issue + +Cocos2d-JS v3.6 @ April 29 2015 + +* Added GAF web runtime to the web engine, the native support will be merged in future version. +* Synchronised Cocos2d-x v3.6. + +* Bug fixes: + 1. Fixed a bug of Cocos Studio parser that it doesn't parse correctly the outline of text widget and button widget. + 2. Fixed a bug of Cocos Studio parser that it doesn't support inner action correctly. + 3. Fixed a bug of Cocos Studio parser that `ccui.Text`'s content size is set incorrectly. + 4. Fixed a bug of Cocos Studio parser that `ccui.Layout`'s background color is set incorrectly. + 5. Fixed a bug of `cc.Node`'s `removeAllChildren` that it doesn't notify the renderer to update. + 6. Fixed a bug of audio system that the resume of music may start from the beginning. + 7. Fixed a bug that sprite's `setTexture` fails to update its content size. + 8. Fixed a bug that Scale9Sprite's children doesn't get transformed recursively. + 9. Fixed constant naming issue of `ccs.FrameEaseType`. + 10. Fixed `cc.LoaderScene.preload` API inconsistency between web engine and native engine. + 11. Fixed a bug that `ccui.Slider` doesn't act correctly when it's scaled. + 12. Fixed a bug that `ccui.Button` renders incorrectly when scale9sprite option enabled. + 13. Fixed circular invocation issue in `cc.Sprite`'s canvas render command. + +Cocos2d-JS v3.6 Beta @ April 22 2015 + +* Improved TMX transform to support RotationX and RotationY. +* Refactored Spine skeleton render command. +* Added checks to prevent issues when `cc.Node.WebGLRenderCmd` is not exist. +* Improved iOS browsers detection. +* Added getter setter function for `cc.MotionStreak`'s stroke property. +* Improved the detection of render mode. +* Upgraded Action Timeline and parser for the latest version of Cocos editor. +* Added `enumerateChildren` function for `cc.Node`. +* Make `cc.Scale9Sprite` support unpreloaded texture. +* Added `cc.sys.isObjectValid` to detect whether an object is still valid (in web and native engine). + +* Bug fixes: + 1. Fixed a bug that `cc.Scheduler`'s `scheduleOnce` runs multiply times. + 2. Fixed a bug of `cc.Scheduler`'s `pauseAllTargetsWithMinPriority`. + 3. Fixed a bug of `cc.eventManager` that its event listeners' order is incorrect when some nodes haven't been added to the scene graph or have been removed from parent without cleanup. + 4. Fixed a bug of `cc.LabelTTF` that `enableShadow` doesn't work. + 5. Fixed a bug of `cc.LabelTTF` that `setColor` doesn't set shadow color under Canvas render mode. + 6. Fixed a bug that stopped audios can be resume after invoking pause on them. + 7. Fixed a bug that `ccui.LoadingBar`'s texture renders incorrectly without preload. + 8. Fixed a bug that cocos builder's callback doesn't get invoked. + 9. Fixed a bug that TMX objects' position is incorrect when content scale factor is modified. + 10. Fixed a mistaken usage of `cc.isObject` in `cc.Sprite` implementation. + 11. Fixed a bug that position type haven't been copied in `cc.ParticleSystem`'s `clone` function. + 12. Fixed some undefined parameter check issues in `cc.Node`. + 13. Fixed a bug that setter for `scaleY` of `cc.EditBox` is incorrect. + 14. Fixed a bug of `cc.SkeletonAnimation` that its canvas render command doesn't work correctly. + 15. Fixed a parsing issue for the width of `cc.LabelBMFont`. + 16. Fixed `ccs.TweenType`'s constants naming issue. + 17. Fixed a bug that the spine skeleton may be rendered under the unsupported mode. + 18. Fixed a bug when setting `cc.ParticleSystem`'s blend function in the ActionTimeline parser. + 19. Added check to prevent issues that functions may not exist in the ActionTimeline parser. + 20. Fixed a typo of `ccs.displayFactory`. + 21. Fixed a bug of `cc.Node.setPosition` that parameter check is incorrect. + +Cocos2d-JS v3.5 @ April 1 2015 + +* Upgraded Cocos Studio parser to support Cocos Studio v2.2. +* Upgraded Spine support to v2.1, added spine test case with FFD. FFD is supported in native but not in web, both engine can parse the new version file correctly, but the web engine will ignore FFD informations. +* Replaced '==' with '===' for better performance. +* Added `path` parameter in `ccs.load` to support modifying cocostudio project resource path. +* Added animationList to Cocostudio ActionTimeline to support playing animation by name. +* Made ParticleSystem support creation from an map object. +* Added missing functions to `cc.Grid3D` and `cc.PageTurn3D`. +* Added tip message functions to `cc.TextFieldTTF` for mobile browser. +* Added a function `cc.sys.openURL`. +* Disabled retina display by default for better performance. +* Added Bower support. +* Updated `cc.sys.OS_XXX` informations for supported systems. + +* Bug fixes: + 1. Fixed a bug of chipmunk.js that it doesn't work under closure compiler advanced mode. + 2. Fixed a bug of Cocos Studio parser that widget didn't set its layout component. + 3. Fixed grammatical mistakes in cocostudio parser logs. + 4. Fixed memory leak issue in `cc.LabelBMFont`. + 5. Fixed a bug of `cc.Scale9Sprite` that its `updateDisplayColor` doesn't take effect. + 6. Fixed a bug of Cocos Studio parser that `cc.Scale9Sprite` doesn't display correctly if its texture isn't preloaded. + 7. Fixed a bug of `cc.MenuItemSprite` that the construction will fail when parameter `selectedSprite` is a Scale9Sprite instance. + 8. Fixed a bug of Cocos Studio parser that the background color of `ccui.Layout` can't be parsed correctly. + 9. Fixed a bug of `cc.ClippingNode` that it doesn't work when set `inverted` to true in Canvas Mode. + 10. Fixed a bug of `ccs.Armature` that its name was modified to animation name when loading from json files. + 11. Fixed a bug of `ccui.PageView` that it cancel child touch during movment of page view. + 12. Fixed a bug of `cc.Scheduler` that its parameter `repeat` is invalid in schedule function. + 13. Fixed a bug of `cc.Scheduler` that `unschedule` function may fail. + +Cocos2d-JS v3.4 Beta0 @ March 19 2015 + +* Added Windows Phone 8.0 platform support. +* Upgraded SpiderMonkey to v33, greatly improved JS object garbage collection and performance. +* Bound 3D modules including camera, light, sprite 3d, animation 3d, billboard, etc. +* Improved `cc.FontDefinition` & `ccui.RichText` in the web engine. +* Added gradient stops feature to `cc.LayerGradient` [Web exclusive]. +* Upgraded `cc.Scheduler` in the web engine with Cocos2d-x v3.4 implementation. +* Added a loading screen when scripts are loading. +* Improved performance by replacing `Object.defineProperties` with `cc.defineGetterSetter`. +* Supported loading sprite frames from json object. +* Refactored math library to improve web engine performance. +* Removed some variables from `cc` namespace to improve web engine performance. +* Added the Firefox OS Web manifest to support Firefox OS apps. +* Added `cocos` attr to the script element in templates. +* Moved loading.js to res folder for Cocos Console release mode. + +* Bug fixes: + 1. Added `getSpriteFrame` to `cc.Sprite` to fix API inconsistency. + 2. Added `getObejct` to `cc.TMXObjectGroup` to fix API inconsistency. + 3. Added `addImageAsync` to `cc.textureCache` to fix API inconsistency. + 4. Fixed a bug of `cc.text` that its default font name is incorrect. + 5. Fixed a bug of `ccui.PageView` that its `getPage` doesn't work. + 6. Fixed a bug of `ccui.ImageView` that its `loadTexture` doesn't work while it's invoked multiple times at the same frame. + 7. Fixed a bug of `ccui` that its load event callbacks have some mistakes. + 8. Fixed a bug of `cc.Layer` that its bake function doesn't work when the layer has a parent node. + 9. Fixed typos in `cc.ClippingNode.WebGLRenderCmd` and `cc.ParticleSystem.WebGLRenderCmd` creation. + 10. Fixed a bug of `cc.Sprite` in `setTextureRect`. + 11. Fixed a bug of `cc.Screen`. + 12. Fixed a bug of `cc.view` that it doesn't work on iOS 8.1.2. + 13. Fixed a bug of cc.DrawNode that its lineWidth is always to default value when set linewidth to zero. + 14. Fixed a bug in hack for particles performance on canvas. + 15. Fixed a bug of `cc.audioEngine` that it doesn't work after minified/compiled. + 16. Fixed a bug in `CCBoot.js` that WebGL is not activated in web view of iOS 8. + 17. Fixed a bug of `cc.CheckBox` that its position is incorrect when its texture isn't preloaded. + 18. Fixed a bug of `cc.TMXLayer` that it stops to work after `setTileGID` called. + 19. Fixed a bug of Cocos parser 2.x that it doesn't set widget's LayoutComponent. + 20. Fixed a bug of `cc.isObject` that it considered function as an object. + +Cocos2d-JS v3.3 @ Feb.9, 2015 + +* Upgraded spine runtime to support the latest version and updated its test case. +* Added an option "noCache" for debugging on browsers. +* Set the default value of `cc.ParticleSystem`'s draw mode to texture mode. +* Added message to `ccs.load` when loading armature json file. +* Improved particle system test case. + +* Bug fixes: + 1. Fixed a bug of `cc.Sprite` that its `setSpriteFrame` doesn't work when sprite frame's `rotated` property is true. + 2. Fixed a bug of `cc.ClippingNode` when its stencil is `cc.Node` object in canvas mode. + 3. Fixed a ccui bug that the position of widgets is incorrect after loaded v2.x json file with `ccs.load`. + 4. Fixed a bug of `cc.PhysicsSprite` that `setIgnoreBodyRotation` function doesn't work. + 5. Fixed a bug of `ccui.Button` that setting pressed texture doesn't work when scale9 enabled. + 6. Fixed a bug of `ccui.ScrollView` that its `dir` property is null when passing `DIR_NONE` as `direction` in `_endRecordSlidAction` function. + +Cocos2d-JS v3.3 RC0 @ Feb.1, 2015 + +* Added web exclusive functions: `_getFontStyle`, `_setFontStyle`, `_getFontWeight` and `_setFontWeight` APIs to `cc.LabelTTF`. +* Observed orientation change event on mobile for resolution policy adaptation. + +* Bug fixes: + 1. Fixed Cocos Studio JSON parser's issues for parsing nested animation. + 2. Fixed Cocos Studio JSON parser's parameters parsing issues. + 3. Fixed Cocos Studio JSON parser's issue for parsing layer. + 4. Fixed Cocos Studio JSON action parser's issues. + 5. Fixed Cocos Studio JSON parser's issue for parsing Scale9Sprite. + 6. Fixed Cocos Studio JSON parser's issues caused by parsing process order. + 7. Fixed Cocos Studio JSON parser's issue for parsing loading bar's direction. + 8. Fixed UI layout system issues. + 9. Fixed `cc.EditBox`'s position issue under certain resolution policies. + 10. Fixed `ccui.ListView`'s issue for setting direction. + 11. Fixed an issue of `cc.Tween` that its `_currentPercent` is incorrect in `updateHandler` function. + 12. Fixed an issue of `ccui.Button` that its state is incorrect in `_onPressStateChangedToNormal`. + 13. Fixed an issue of `cc.ArmatureAnimation`'s `setMovementEventCallFunc`. + 14. Fixed an issue of `cc.Sequence` action when it's repeated. + 15. Fixed `_anchorPointInPoints` usage issue. + 16. Fixed an issue of `cc.GLProgram` that it doesn't work on some devices which didn't support highp float precision. + 17. Fixed an issue of fade actions that they don't work when duration is 0. + 18. Fixed `onended` callback issue of audio engine on iOS. + 19. Fixed Cocos Builder's parser issue for auto playing animations. + 20. Added a message to `ccs.Armature` that it doesn't support adding widget as its child. + 21. Improved test cases for stability. + +Cocos2d-JS v3.3 Beta @ Jan.24, 2015 + +* Added Cocos Studio v2.x parser and refactored 1.x parser. +* Upgraded new flow layout UI system in web engine. +* Refactored `load` events of texture2d, sprite and so on to be more intuitive. +* Added JavaScript file loader. +* Allowed set texture to null in `cc.Sprite`. +* Added full test cases for Cocos Studio v2.x parser and the new flow layout UI system. +* Upgraded MoonWarriors sample's UI and graphic design. + +* Bug fixes: + 1. Fixed a bug of Cocos2d UI, their focus event has been supported. + 2. Fixed a buf of `ccui.Widget` that its percent position doesn't work. + 3. Fixed a bug of `ccs.Armature` that its position doesn't update in visit on WebGL render mode. + 4. Fixed a bug of `cc.Sprite` that its `setTextureRect` function doesn't work when `setColor` invoked. + 5. Fixed a bug of `cc.PhysicsSprite` that its position is incorrect. + 6. Fixed a bug of `ccs.Bone` that its `setOpacity` and `setColor` doesn't work. + 7. Fixed a bug of `cc.LabelBMFont` that its word wrap doesn't work. + 8. Fixed a bug of `cc.sys` that it gets the incorrect OS type when system is Linux. + 9. Fixed a bug of `cc.audioEngine` that its loading path is incorrect. + 10. Fixed a bug of `ccui.Widget` that it can't touch when it's reused. + 11. Fixed a bug of UI system that the `setNormalizedPosition` doesn't work. + 12. Fixed a bug of `cc.ActionInterval` that its `_times` conflict with `cc.Blink`. + 13. Fixed release texture issue in canvas mode. + 14. Fixed a bug of `ccs.actionManager` that its `getActionByName` doesn't work. + 15. Fixed a bug of `cc.Sprite` that it can't draw without texture on WebGL mode. + 16. Fixed a bug of `cc.audioEngine` that it doesn't work on baidu browser. + 17. Fixed a bug of `cc.EditBox` that its position is incorrect on Canvas Mode and its string value is wrong when PlaceHolder is showing. + 18. Fixed a bug of `cc.loader` that its `loadImg` function doesn't work when image is accessed cross origin. + 19. Fixed a bug of `ccui.TextField` that its `contentSize` is incorrect in text field event. + +Cocos2d-JS v3.2 @ Dec.29, 2014 + +* Replaced `transform` function with `setTransform` function under canvas render mode for better performance. +* Added a timer in `cc.audioEngine` to check audio element loading event, prevent the loading process being stucked when load audio file failed. +* Added some new browser types to `cc.sys`. +* Added some audio resource loading codes to ensure compatibility with Wechat browser. +* Added check for WebAudio support to ensure compatibility. + +* Bug fixes: + 1. Fixed an issue that `cc.InputManager` doesn't trigger touch event on chrome mobile emulator. + 2. Fixed an issue that `cc.game.setFrameRate` doesn't work. + 3. Fixed an issue that `cc.view` can't remove resize event listener. + 4. Fixed an issue that `cc.EventManager` didn't set register flag to false when a listener is removed. + 5. Fixed an issue that `cc.audioEngine` doesn't play some audios on some iOS devices. + 6. Fixed an issue of ccui controls that their `setColor` doesn't work when cascade color is enabled. + 7. Fixed an issue that `ccs.Armature`'s `setColor` doesn't work in canvas render mode. + 8. Fixed an issue that `ccs.Armature` crashes when adding a child to it. + 9. Fixed an issue that `cc.SpriteBatchNode`'s status is incorrect in WebGL render mode. + 10. Fixed an issue of `cc.Layer` that its position is incorrect under bake mode. + 11. Fixed an issue of `ccui.RichText` that its `setContentSize` doesn't work. + 12. Fixed an issue of `cc.LabelTTF` that its `setColor` doesn't work when cascade color is enabled. + 13. Fixed an issue of spine that its skeletons position is incorrect when scaleX equals to -1 and scaleY equals to 1. + 14. Fixed `sp.Skeleton`'s API inconsistence by renaming `boundingBox` to `getBoundingBox`. + 15. Removed all usages of deprecated create functions in the test cases. + +Cocos2d-JS v3.2 RC0 @ Dec.11, 2014 + +* Refactoration of web engine by separating the render logic, the arthictecture level refactoration is now completed and brounght great performance improvement. +* Refactoration of web engine's resolution adaptation and audio engine with polyfilled adaptation logics for different devices and browsers. This ensures better compatibility and better extensibility for future needs. +* Added `setRotation` method to `ccui.ImageView`. +* Added a function that fill sprite with repeated texture in Canvas mode. +* Added `setLineHeight` method to `cc.LabelTTF`. +* Added `dumpAudioInfo` to `cc.audioEngine` for debugging purpose on mobile browser. +* Removed Cocos Studio's Protobuffer support from the framework. +* Added an outline shader sample. + +* Bug fixes: + 1. Fixed an issue of `cc.Sprite` that its rendering is incorrect without texture. + 2. Fixed an issue of `cc.ClippingNode` that its stencil drawing is incorrect on Canvas Mode. + 3. Fixed an issue of `TextFieldReader` that it will throw an error when 'areaWidth' and 'areaHeight' equal to zero. + 4. Fixed an issue of `ccui.CheckBox` that its getSelectedState doesn't return its state. + 5. Fixed an issue of `cc.LabelTTF` that it doesn't update the string when its string become to empty string. + 6. Fixed an issue of `cc.ParticleSystem` that it can't change its texture mode and shape type in Canvas mode. + 7. Fixed an issue of `cc.Layer`'s bake function that its position is incorrect when cc.view's scale isn't 1. + 8. Fixed an issue of `ccs.ArmatureAnimation`'s `setMovementEventCallFunc` and `setFrameEventCallFunc`. + 9. Fixed an issue of `console.log` that it isn't a funtion on IE9. + 10. Fixed an issue of `CSLoader` that it will add duplicate resources to sprite frame cache. + 11. Fixed an issue of `cc.ProgressTimer` that its setColor is not taking effect. + 12. Fixed an issue of `cc.loader` that it will throw an error when loading a remote texture. + 13. Upgrade html5 version chipmunk to the latest release. + +Cocos2d-JS-v3.1 @ Oct.22, 2014 + +* Released Facebook Integration for Cocos2d-JS v1.0, all APIs have been significantly polished and stabilized. Improved test cases for Facebook with more features demonstrated. +* Upgraded Cocos2d-x to v3.3 rc0 +* Supported Cocos Studio v2.0 including Timeline animation support and proto buffers format support for both web engine and JSB engine. +* Refactored load event of texture, sprite frame and sprite for better maintainability. +* Refactored `cc.rendererCanvas` for improving performance. +* Moved the `CC_Texture0` definition of fragment shader to cc.GLProgram to ensure compatibility with JSB. +* Added normalized position functions to cc.Node. +* Refactored the constructor of Cocos Studio's classes and deprecated all create functions. +* Refactored Cocos Studio reader for better maintainability. +* Improved Facebook SDK. +* Modified `cc.ProgressTo`'s behavior, its progression didn't reset to zero when the progression is 100. +* Changed `ccui.Widget`'s default anchor point to (0, 0) in widget reader. +* Removed all deprecated create function usage in engine and in the test cases. + +* Bug fixes: + 1. Fixed an issue of `cc.UILayout` that its scissor mode didn't work. + 2. Fixed an issue of `ccui.TextBMFont` that its 'string' property setting was incorrect. + 3. Fixed an issue of `cc.DrawNode` that its element's position was incorrect in Canvas mode. + 4. Fixed an issue of `cc.Layer` that its bake function didn't work in new renderer. + 5. Fixed an issue of `cc.Scale9Sprite` that its cached canvas size was incorrect. + 6. Fixed an issue of `cc.Director` that its position was incorrect when calling `setProjection` in new renderer. + 7. Fixed an issue of `cc.view` that the reinitialization logic of frame size was incorrect. + 8. Fixed incorrect usage of `cc.progressTo` in progress action test. + 9. Fixed an issue of CocosNodeTest for the new renderer. + 10. Fixed minor issues in test cases. + +* Known Issues: + 1. `jsb.AssetsManager` doesn't work on windows due to a bug in libcurl + +Cocos2d-JS v3.1 beta @ Oct.13, 2014 + +* Refactoration of the web engine with new renderer on the architecture level, optimization is under going. +* Released Facebook SDK for Cocos2d-JS beta2, its API have been significantly improved and stablized. +* Upgraded MoonWarriors sample with new set of graphical assets. +* Automatically enabled WebGL on iOS 8 safari. +* Upgraded chipmunk.js to the newest version. +* Supported setting color of shadow for `cc.LabelTTF`. +* Added `getTitleRenderer` function to ccui.Button. +* Supported Coco Studio timeline animation. +* Set the default value of LabelAtlas's `cascadeOpacityEnabled` and `cascadeColorEnabled` to true. +* Added a listener of texture to `cc.Sprite#setTexture` when the texture hasn't loaded. +* Activated `cc.pool` for all kind of objects. +* Added query test for chipmunk and added necessary JavaScript bindings. + +* Bugs fix: + 1. Fixed a bug of `cc.ComponentContainer` that a 'if' statement behavior is incorrect. + 2. Fixed a bug of `cc.Scale9Sprite` that the behavior of Canvas and WebGL is different. + 3. Fixed a bug of `cc.EventListener` that its pause state should set to true. + 4. Fixed a bug of `cc.ParticleSystem` that it should apply canvas scaling on canvas rendering mode. + 5. Fixed a bug of CCBoot.js that `cc.loader` should add a condition to check whether `crossOrign` property is undefined on IE9 and IE10. + 6. Fixed a bug of `ccui.Widget` that its `setPosition` function's behavior is incorrect. + 7. Fixed a bug of `ccui.LoadingBar` that its `barRenderer` should add to protected children array. + 8. Fixed a bug of `cc.Texture2D` that its `TEXTURE_MAG_FILTER` should set to LINEAR. + 9. Fixed a bug of `cc.TMXMapInfo` that its doesn't parse `rotation` property. + +Cocos2d-JS-v3.0 Final @ Sep.10, 2014 + +* Facebook SDK Beta2: Added `appRequest` API. +* Facebook SDK Beta2: Added permission request in `login` API, removed `requestPermission` API. +* Facebook SDK Beta2: Renamed `request` API to `api`. +* Facebook SDK Beta2: Renamed `publishInstall` API to `activateApp`. +* Added getter and setter function for browser's density dpi: `cc.view.setTargetDensityDPI`, `cc.view.getTargetDensityDPI`. +* Added some type check functions. +* Added audio support for wechat browser. +* Added setPlaceHolderColor and setTextColor to ccui.TextField. +* Added API reference for Cocos Studio extension. + +* Bugs fix: + 1. Fixed an issue of `cc.Menu` that its item's touch priority is different than cc.eventManager. + 2. Fixed an issue of `cc.view` that its NO_BORDER mode doesn't work correctly. + 3. Fixed an issue of `cc.LabelBMFont` that its content size is different than JSB. + 4. Fixed an issue of `cc.LabelBMFont` that its `setColor` is invalid on some mobile devices. + 5. Fixed an issue of `cc.PageView` that it can't receive TOUCH_CANCEL event. + 6. Fixed an issue of `cc.loader` that it can't load cross origin textures. + 7. Fixed an issue that Facebook SDK Web's `appRequest` wraps info parameter incorrectly. + 8. Fixed an issue of ccui widgets' `addEventListener` that it doesn't accept function's target as parameter. + +Cocos2d-JS-v3.0 RC3 @ Aug.29, 2014 + +* Facebook SDK Beta: Unified the callback parameters for different platform. +* Facebook SDK Beta: Added payment API on Web platform. +* Facebook SDK Beta: Supported app request and share open graph API on Web platform. +* Facebook SDK Beta: Remove plugin configuration for Facebook SDK to simplify the usage. +* Facebook SDK Beta: Added test case for new features and improve all test cases. +* Cocos Console: Improved web compile with `--advanced` tag. +* Improved Cocos2d-JS inline docs to provide a better API reference document. +* Refactored cc.game for maintainability. +* Refactored cc.async to simplify and improve the usage. +* Added `cc.formatStr` for string formatting, for example: `cc.formatStr("a: %d, b: %b", a, b)`. +* Refactored cc.log to support formatted string. +* Refactored cc.pool's `hasObj` to `hasObject` and `removeObj` to `removeObject`. +* Added some state check to cc.audioEngine. +* Refactored sprite's blend function to support more features on Canvas. +* Refactored `cc.textureCache.textureForKey` to `cc.textureCache.getTextureForKey`, `cc.TMXTilemap#propertiesForGID` to `cc.TMXTilemap#getPropertiesForGID` to follow the standard API naming style. +* Detected mouse event on touch screen tablets. +* Support new construction for cc.PhysicsDebugNode and deprecated `cc.PhysicsDebugNode.create` +* Made cc.Texture2D's setTexParameters supports two types of parameters. +* Added test case for remote image loading. + +* Bugs fix: + 1. Fixed an issue of tilemap that it can't runAction in canvas render mode. + 2. Fixed an issue of cc.eventManager that its removeListeners' codes are unreachable. + 3. Fixed an issue of cc.EditBox that its position is incorrect. + 4. Fixed an issue of cc.WebAudio that its stopped state is incorrect. + 5. Fixed an issue of cc.audioEngine that it doesn't work on firefox after it compiled with advanced mode. + 6. Fixed an issue of ccs.Bone that it doesn't update color and opacity correctly. + 7. Fixed an issue of ccs.Armature that its setShaderProgram doesn't work. + 8. Fixed cc.Sprite and cc.Scale9Sprite's issue so that their texture loads incorrectly. + 9. Fixed an issue of ccui.LoadingBar that its setPercent is invalid. + 10. Fixed an issue of Armature reader that it can't parse isTween property. + 11. Fixed an issue of ccui.PageView that its getTouchBeganPosition returns incorrect value. + 12. Fixed an issue of ccui.ImageView that its setColor doesn't work. + 13. Fixed an issue of cc.RenderTexture that it doesn't support parameter depthStencilFormat. + 14. Fixed an issue of ccs.ArmatureAnimation.setSpeedScale. + 15. Fixed an issue of cc.Scale9Sprite that it has a line on iOS device. + 16. Fixed CCProgressTimer draw on canvas with colorized sprite + 17. Fixed an issue of cc.game that its frameRate setter is invalid. + 18. Fixed an issue of cc.loader that its callback state is incorrect. + +Cocos2d-html5-v3.0 RC2 @ Aug.8, 2014 + +* Refactored Cocos UI for more stable and friendly user experience. +* Upgraded Cocostudio reader to support version 1.2 - 1.5.x. +* Upgraded Cocostudio Armature animation from Cocos2d-x v3.2. +* Added back 2.x createWithXXX functions and deprecate all create/createWithXXX functions. +* Merged cc.NodeRGBA and cc.LayerRGBA to cc.Node. +* Fixed ctor functions bugs to support new construction. +* Refactored cc.Sprite's setColor to improve its performance. +* Renamed CCAffineTransform.js's functions to lowercase started functions. +* Upgraded cc.Scale9Sprite from Cocos2d-x 3.2. +* Improved cc.LabelTTF's line break algorithms to support multi-languages. +* Made cc.RenderTexture's beginWithClear accept color value from 0-255. +* Improved implementation of all Actions lower case alias creation functions. +* Added lower case creation functions for 3d actions and progress actions. +* Added cc.sys.platform API for detecting platform. +* Upgraded HelloWorld project with v3.0 APIs. + +* Bugs fix: + 1. Fixed a bug of cc.WebAudio that sourceNode's playbackState is invalid on some browsers. + 2. Fixed a bug of cc.MenuItemToggle that callback is not correctly initialized when using new construction. + 3. Fixed a bug of ccui.Layout that its clipping area is incorrect. + 4. Fixed a bug of requestAnimFrame that it doesn't work after re-focus WeChat browser on Samsung mobile. + 5. Fixed a bug of CCBoot.js that bind function is undefined in Safari for iOS 5.1. + 6. Fixed a bug in cc.layer's bake function that its position is incorrect when cc.view is scaled. + 7. Fixed a bug of cc.LayerMultiplex. + 8. Fixed a bug of cc.TMXLayer that it can't display all map image when its type is hexagonal. + 9. Fixed a transform error in ccs.TransformHelp. + 10. Fixed a bug of cc.ControlSwitch. + 11. Fixed image format constant inconsistence. + 12. Fixed a bug of ccui.Widget that it is invisible after popScene. + 13. Correct behavior of cc.TransitionSlideInB and cc.TransitionSlideInT. + 14. Fixed bugs of ccui.Widget and ccui.Text's clone functions. + + +Cocos2d-html5-v3.0 RC0 @ July.3, 2014 +* Added Facebook SDK plugin into Pluginx extension. +* Refactoration of gui system `ccui` for better performance, usage and maintainbility. +* Added `bake` function to `cc.Layer` to support layer baking. +* Added object pool extension: `cc.pool`. +* Added new easing functions: bezier action, quadratic actions, quartic actions, quintic actions, circle actions, cubic actions. +* Made `cc.loader` continue the counter process even if a resource failed to be loaded. +* Supported multiple property objects in `cc.Class.extend` function. +* Refactored `ccui.Widget`'s `getLeftInParent`, `getBottomInParent`, `getRightInParent`, `getTopInParent` to `getLeftBoundary`, `getBottomBoundary`, `getRightBoundary`, `getTopBoundary`. +* Refactored `cc.FadeIn.create(duration, toOpacity)` to `cc.FadeIn.create(duration)`. +* Refactroed all string access functions in `ccui` extension to `setString` and `getString`. +* Added `getContentSize` and `setContentSize` in `ccui` extension. +* Changed the default alpha value of `cc.Color` from `undefined` to 255. +* Made `cc.log` support formatted string. + +* Bugs fix: + 1. Fix bugs on creating sequence objcet or spawn object using new method. + 2. Fix a bug that `ccui.LoadingBar`'s `setPercent` function will crash when its texture is in a plist file and scale9Enabled is true. + 3. Fixed a bug of `cc.audioEngine` that it crashs when audio isn't correctly loaded and its duration is infinity. + 4. Correction of the calculation of `cc.visibleRect`. + 5. Fix `cc.Skin`'s bounding box calculation for canvas rendering. + 6. Fix an issue that `cc.TextureCache` doesn't handle loaded texture in some case. + 7. Fix an issue that texture rect could be zero sized in `initWithFile` function of `cc.Sprite`. + 8. Fix a bug on inverted ClippingNode with DrawNode as stencil in Canvas render mode. + 9. Fix a bug that `cc.SpriteFrame` didn't support initialization with texture name parameter. + 10. Fix a bug on `ccs.ArmatureAnimation`'s loop parameter. + 11. Fix a bug that `cc.JumpTo`'s `_delta` position calculation is incorrect. + 12. Fix a bug of `cc._audioLoader` that it doesn't work when it failed to load an audio file. + +Cocos2d-html5-v3.0 beta @ May.23, 2014 + +* Refactored actions to make it more friendly and easy-to-use. +* Integrated Spine skeleton animation feature. +* Renamed constants of ProgressTimer, Scale9Sprite, TMXLayerInfo, Node, ParticleSystem for maintainability. +* Modified mouseMove event behavior of cc.inputManager to compatible with Cocos2d-x +* Modified cc.game.run to receive a canvas id as parameter. +* Added local audio file playing from 'file://' origin. +* Added local images file displaying from 'file://' origin. +* Refactored cc.TMXLayer's setTileAt etc functions to support point or x,y as their parameters. +* Added a check to cc.Sprite and cc.SpriteFrame to avoid its texture rect out of bounds. +* Added a check to cc.SpriteFrame to avoid cc.loader release invalid sprite frame file. +* Made cc.Touch return copies of point. +* Made the default of cc.Color alpha value is 255 to avoid cc.Sprite's setColor is invalid. +* Optimized cc.Node.sortAllChildren for better performance. +* Added warning of cc.Texture2D if it has an invalid texture. + +* Bugs fix: + 1. Fixed a bug of cc.winSize that it returns incorrect value when using setDesignResolution. + 2. Added a check to cc._setup to avoid double invocation. + 3. Fixed a bug of cc.TMXMapInfo that its tile's property id is incorrect. + 4. Fixed a bug of cc.Scale9Sprite that its CascadeColor and CascadeOpacity are invalid. + 5. Fixed a bug of ccs.UILoadingBar which its barRendererScaleChangedWithSize is incorrect. + 6. Added some forgotten files to build.xml for minimize core. + 7. Corrected a mistake of renderMode default value in CCBoot.js. + 8. Fixed a bug of ccui.Layout's draw function that its scaleX, scaleY value is incorrect. + 9. Fixed a bug of cc.Audio's stopMusic function. + 10. Fixed a bug of TextureCache that it can't remove image's event handler. + 11. Fixed ClippingNode's DrawNode stencil bug on Canvas. + 12. Fixed a typo 'cc.radiansToDegress' function to 'cc.radiansToDegrees'. + 13. Fixed a bug of ccui.ImageView that its setSize is invalid when the picture without pre-load. + 14. Fixed a bug of cc.ParticleSystem that it throws a error when create from CocosBuilder. + 15. Fixed a bug of cc.LabelAtlas that it can't display its children. + 16. Fixed a bug of cc.fontLoader that it can't load custom font. + 17. Fixed a bug of ccui.Widget that its setOpacity is invalid. + 18. Fixed a bug of cc.Node that it transform value is incorrect when a node skew to a special value. + +Cocos2d-html5-v3.0 alpha2 @ April.14, 2014 + +* Minimized the size of core from 254k to 113k after google closure advanced compiling +* Made cc.DrawNode support some DrawingPrimitive's drawing function on WebGL mode +* Added undefined checking in cc.loader for better performance. +* cc.Sprite supports creating a sprite through external URL. +* Added the warning information to notice developers that their project.json cannot be loaded or parsed. +* Added retina display support to cc.Editbox. +* cc.Node's pauseSchedulerAndActions and resumeSchedulerAndActions are deprecated, please use pause and resume instead. +* Added render mode checking to 3D action classes. +* Added SocketIO +* Sync cc.eventManager to the latest version of Cocos2d-x v3.0 Stable. +* ccui.Layout's doLayout function has been set to private function "_doLayout" +* Made actions extendable directly via ctor +* Added null callback check in cc.textureCache.addImage +* Fixed the API inconsistence of ccs.ArmatureAnimation.play +* Fixed compatibility and performance for ctor +* Renamed all Uppercase functions to lowercase in CCMacro +* Added necessary GL constants in H5 +* Fixed CONSTANTS inconsistence between h5 and JSB + +* Bugs fix: + 1. Fixed ccs.comAttribute API incompatible issue + 2. Fixed a bug of Cocostudio's data reader that getting isTween value is incorrect when the attribute value is false. + 3. Fixed a bug of Sprite that it doesn't work when its texture doesn't preload and its parent is a SpriteBatchNode + 4. Fixed a bug in CCBoot.js that console.error is invalid on firefox. + 5. Fixed some comment errors of framework. + 6. Fixed a bug of cc.LabelBMFont that it's multiline works incorrectly. + 7. Fixed a bug that Touches event doesn't work in release mode on IE browser. + 8. Fixed a bug that cc.winSize has not been reset after cc.view.setDesignResolutionSize. + 9. Fixed typo in ccui.Widget.TOUCH_BEGAN + 10. Fixed a bug of cc.MenuItemSprite.create that + 11. Fixed a bug of cc.loader that it need to set value before call the callback. + 12. Fixed a bug of cc.log that it doesn't work in IE9 + 13. Fixed IE incompatible issue with __lookupGetter__ + 14. Fixed a mistake of cc.Node that it return a reference of _position in getPosition + 15. Fixed a bug of cc.ClippingNode that its _super is undefined + 16. Fixed a bug of inputManager's touch event in IE browser + +* Known Issues: + 1. EventListener is not extendable. + + +Cocos2d-html5-v3.0 alpha @ March.15, 2014 + +* Refactor some properties of all rendering classes with getter setter for providing javascript user friendly APIs. +* Provide `attr` function for cc.Node and its descendants to permit modify multiple properties at the same time with a key-value object. +* Refactor foundational data structures for better maintainability. +* Add event manager to cocos2d-html5, all events are dispatched via cc.eventManager to event listener. +* Refactor cc.Application to cc.game. +* Refactor singleton Classes to javascript object. +* Refactor all createWithXXX functions into unified create function with different parameters. +* Use `moduleConfig.json` to config the paths of engine scripts. +* `cocos2d.js` is replaced with `project.json`. +* Refactoring cc.loader. +* CocoStudio GUI updated to 3.0, and ccs prefix of UI widgets have been changed to ccui. +* CocoStudio v1.3.0 has been supported in v3.0. +* richText has been supported in v3.0. +* Use `cc.BuilderReader.registerController` to register controller of ccb. +* Add `cc.path` to handle operations of file path. +* Add `cc.async` to handle async operations. +* Add cc.NodeGrid in v3.0. +* Replace `replaceWithScene` and `runWithScene` with `runScene`. +* move sys.xxx to cc.sys.xxx. +* Refactor CCEGLView.js for better maintainability. +* Refactor CCScheduler.js for better maintainability. +* Remove arguments.callee which is forbidden in ECMAScript strict mode. +* Refactor Array clean function for better performance. +* Refactor some functions about array operation. +* Refactor FadeIn/FadeOut to fix a bug that it always start from/to 255. +* Rewrite functions in CCNS.js with regex. +* Move CCFormatHelper and CCNS content into CCCommon.js. +* Refactor cc.Screen to support all browsers. +* Add retina display support for Apple devices to cc.view. +* Add "allLayers" function to cc.TMXTiledMap. +* Make cc.p and cc.size support two types of parameters. +* cc.DrawNode supports all functions of cc.DrawingPrimitive on Canvas mode. +* WebAudioEngine is supported on iOS now. +* Use event on cc.canvas to make full screen. +* Add a browser white list that support multiple audio playback at the same time. +* Removed in/hasOwnProperty usage in engine for better performance. +* Refactoring CCCommon.js, delete some unused functions, rename some functions for better maintainability. +* Add analytics plugin protocol ,Flurry plugin and ProtocolAds.js plugin protocol. +* Arguments length check replaced by undefined check for better performance. +* Fix legacy Function.prototype.bind support. + +* Bugs fix: + 1. Avoid CCLabelTTF enter in infinite loop while character's width larger than the dimension width + 2. Add jsDoc Flags to cc.NodeRGBA and cc.LayerRGBA + 3. Fixed a bug that Schedule doesn't restart when widget is re-added after being removed + 4. Correction of split logic in CCLabelTTF + 5. Fixed a bug that armature animation does not display correctly on canvas mode + 6. Correct gui widget clone functions + 7. Fixed a bug of cc.SpriteFrameCache that filePath is needed in `loadedFileNames` + 8. Add a condition check to avoid texture out of range bug + 9. Fixed a bug of cc.Editbox that its position is incorrect when its parent node isn't root node. + 10. Fixed a SimpleAudioEngine's state error. + 11. Fixed a bug of cc.TMXTileMap that its `_tileProperties` should be a dictionary object + 12. Fixed a bug of cc.DrawNode that it need to deep-copy verts in `drawPoly` + 13. Fixed a bug of UILabelBMFont that variable `_strStringValue` should be `_stringValue` + 14. Fixed a bug in SceneReader's `setPropertyFromJsonDict` function + 15. Fixed a bug when margin not set in ccs.Margin + 16. Fixed a bug of cc.TMXLayer that its `removeChild` works incorrectly. + +* Known Issues: + +Cocos2d-html5-v2.2.2 @ Dec.31, 2013 +* Resolution policy now act as a combination of cc.ContainerStrategy and cc.ContentStrategy so that user can beautifully customize its behavior. +* cc.LabelTTF's now support perfectly automatic line break with occidental and Chinese characters. +* cc.ClippingNode for canvas render mode is implemented. +* Refactored cc.Node and cc.Sprite by adding cc._PointConst and cc._SizeConst for better Performance. Now the performance of setPosition and getPosition is faster 65% than before. +* CCNode's setContentSize and setAnchorPoint support two types of parameters, more friendly and more efficient. setAnchorPoint(x,y) is faster 35% than setAnchorPoint(cc.p(x,y)). +* Added NPM support and adjusted folder structure. It supports modules customization, the mini HelloWorld is just 185KB when package all files into single file.Please visit NPM Guide for more details. +* Added SpriteFrameCache JSON format support. +* Added source map generating of Closure Compiler advance mode , please make sure your JDK version is 7.0 and up. +* Improved audio compatibility for mobile browser, added playing queue to solve the one audio restriction of some mobile browser. +* Refactoring TMXLayer for better performance. +* set cc.Rect's origin and size from public to private for compatibility with JSB. +* CocoStudio supports async image loading. +* cc.log supports printing object content to console +* Refactoring indexing of actionManager and Scheduler for better performance. +* ClippingNode supports some features on Canvas Mode. +* Migrated Armature to v2.2.2. +* Add callback function to CocoStudio action completion and refactoring it for better performance. +* CCBReader supports that CCControl can send action by all types of event. +* Add create function to cc.NodeRGBA +* Add jsdoc document to CocoStudio classes + +* Bugs fix: + 1. Fixed a bug of TMXLayer that it has thin lines at tile's border when EGLView's scale doesn't equal 1. + 2. Fixed bugs of LabelBMFont about updateDisplayedOpacity and multi-line is incorrect. + 3. Fixed a bug of LabelTTF that enter an infinite loop when setting special string and fontSize to it. + 4. Fixed a bug of NodeRGBA and LayerRGBA about updateDisplayedColor and updateDisplayedOpacity. + 5. Fixed a bug of ProgressTimer that it can't change color and opacity when calling setColor and setOpacity directly. + 6. Fixed a bug of cc.ProgressTimer that it has a blink when its reverseDirection equals false and type equals cc.PROGRESS_TIMER_TYPE_RADIAL. + 7. Some Loaders need modify their default value to adapt CocosBuilder that CocosBuilder ignores some two properties object like cc.Point when all the properties equals to zero. + 8. Fixed a bug of Fixed a bug of TMXTileMap that its getProperty doesn't work. + 9. Fixed a bug of ActionInterval that it throws error when its target doesn't have RGBAProtocol property. + 10. Fixed a bug of MenuItemSprite that it throws an error when create a MenuItemSprite object with cc.Node. + 11. Fixed a bug of UIWidget that its container intercept touch event while they can't. + 12. Fixed a bug of ccs.UILayout about relative positioning. + 13. Fixed a bug of Armature that its nodeToParentTransformCanvas correct. + +* Known Issues: + 1. Effect Advanced Lens3D doesn't work + 2. ClipNodeTest effects varies in different browsers + 3. Stencil of cc.ClippingNode doesn't work well with WEBGL render mode, the stencil have the right size and shape but it masks the content with a monochrome mask. + +Cocos2d-html5-v2.2.1 @ Nov.19, 2013 +* CocoStudio is now supported on Cocos2d-html5. The GUI, scene and component modules have been added to it. +* cc.EGLView and most render classes have been re-written to adapt multiple resolution resources in-order to optimize performance on mobile browsers. +* Refactored cc.LabelTTF, now its contentSize and position is correct for labels which has defined stroke and shadow. +* Corrected the behavior of "CascadeColorEnabled" and "CascadeOpacityEnabled" for cc.NodeRGBA and cc.LayerRGBA. +* All cc.Assert has being replaced, and more arguments checking and log information have added to engine's function. +* Added cc.Screen to engine, it uses to enter/exit FullScreen mode. +* Added cc.VisibleRect to engine, it provides nine points of game view for positioning. +* cc.WebAudioEngine now works perfectly on chrome. +* CocoStudio's namespace changes to 'ccs' now, and the other module's namespace will be renamed and support NPM in next version. +* cc.rect now accepts more types of parameters on JSB and HTML5 now, for example: cc.rect(1,1,1,1) and cc.rect(aRect), and cc.rect(cc.p(1,1),cc.size(10,10)); +* Optimized cc.Node's getBoundingBoxToWorld for better Performance. +* Modified the _sequenceCompleted method in CCBAnimation, it can set the next sequence in callback now. +* Improved the maintainability for _drawSceneForWebGL and _drawSceneForCanvas. +* ParticleExamples has been refactored for JSB. +* HelloHTML5World's CircleSprite has been removed, because it doesn't work on JSB. + +* Bugs fix: + 1. Fixed a Scale9Sprite's bug that setCapInsets is invalid. + 2. Fixed a bug that prevents the game to run on Chrome 31 WebGL mode. + 3. Fixed a bug of LabelTTF that doesn't work on Baidu browser. + 4. Fixed a bug of Sprite that it shouldn't to set transform dirty when setting color or opacity. + 5. Fixed a bug that cc.EditBox's setFontSize is invalid. + 6. Fixed a bug that Particles doesn't work when search path in FileUtils was set. + 7. Fixed a bug of Scale9Sprite that it throws an error when _scale9Image is null. + 8. Fixed a bug of LayerGradient that it shows wrong size when setting content size. + 9. It should listen to the method "onLoad" in cc.FileUtils when the browser isn't IE. + +* Known Issues: + 1. Effect Advanced Lens3D doesn't work + 2. ClipNodeTest effects varies in different browsers + +Cocos2d-html5-v2.2 @ Sep.19, 2013 +* Improved Sprite, Node, LabelTTF class define from separated code to combined code for maintainability, now it is clean and clear +* added a new sample game "Fruit attack" which works great on PC browsers, mobile browsers, and can even be run natively as an android and iOS app with JSB +* cc.Sprite and its subClasses's texture has been replaced from DOM element to cc.Texture2D on Canvas mode +* Improved cc.Texture2d for direct using without pre-loading image resources, you don't need to wait resources loading when create a new scene or layers +* Migrated CCBReader and GUI to Cocos2d-x 2.1.4 +* Improved update function of Action, and avoid using temporary object, it is good for GC and performance +* Modified LabelTTF's rendering from direct drawing to pre-rendering for performance, 100% faster than before on mobile browser +* Fixed APIs of HTML5 according to JSB for compatibility, e.g. cc.ParticleSystemQuad has merged into cc.ParticleSystem, please check it on upgrade guide v2.1.5 to v2.2(http://www.cocos2d-x.org/wiki/Upgrade_Guide_from_Cocos2d-html5_v215_to_v22) +* Added Hiding url address bar for mobile browser, please check the template and hello world +* Re-writed Canvas Mode of RenderTexture to adapt WebGL interface +* Added frame event, collider and blend type supporting for Armature. Now Armature supports two tools:1.CocoStudio(windows,http://www.cocostudio.org),2.DragonBones(flash, https://github.com/2youyouo2/SkeletonAnimationDesignPanel) +* Set auto render mode default value to canvas in mobile browsers and WebGL in desktop browsers + +* Bug fix: +1. Fixed cc.Sprite's displayFrame returns wrong value on Canvas mode. +2. Fixed cc.LabelBMFont is very slow when calling setString +3. Fixed a bug of CCBReader that cc.ControlButton doesn't work when its controller is _jsControlled +4. Fixed a bug of cc.TextureCache that the status of texture is wrong in callback +5. Fixed a bug of cc.Scale9Sprite that its contentSize is wrong when call setCapInsets +6. Fixed a bug of cc.TableView's that contentSize is wrong when change datasource +7. Fixed a bug of cc.Sprite that its children also follow fliped when it was fliped +8. Fixed cc.Node's nodeToWorldTransform returns wrong value on Canvas Mode +9. Fixed a bug of cc.LayerColor that represent incorrect opacity passed into init method +10. Stop listening and remove the event for HtmlImageElement object onload +11. Fixed cc.ProgressTimer display wrong when its sprite was flipped +12. Fixed some bugs for actions that set their object property through reference when initiating actions. + +* Known Issues: + 1. Effect Advanced Lens3D doesn't work + 2. ClipNodeTest effects varies in different browsers + +Cocos2d-html5-v2.1.5 @ July.24, 2013 +* Ported engine API to keep the same as Cocos2d-x v2.1.4 API +* Optimized John Resig's inheritance pattern (cc.Class.extend) with advanced property initialization. +* Implemented the rest of extensions features according to Cocos2d-x v2.1.4 +* Integrated Armature module +* Rewrote CCGrid, CCMotionStreak , CCProgressTimer with TypeArray +* Optimized performance for actions +* Optimized performance for MoonWarriors and CocosDragonJS + +* Bug fix: + 1. Fixed cc.EditBox Dom Element position issue when EGLView is set + 2. Fixed cc.EGLView adjustSize bug + 3. Fixed the bug of cc.ParticleBatchNode that it doesn't hide particles after particle life has expired when calling stopSystem() + 4. Fixed a bug that LabelTTF dimension behavior doesn't support height=0 + 5. Fixed line height for multiline LabelTTF and overlapping pixels in Scale9Sprite on Canvas browsers + 6. Fixed a bug of cc.SimpleAudioEngine that unloading effect doesn't work +* Known Issues: + 1. Effect Advanced Lens3D doesn't work + 2. ClipNodeTest effects varies in different browsers + 3. nodeToParentTransform in cc.Node returns wrong value on Canvas mode + +Cocos2d-html5-v2.1.4 @ Jun.12, 2013 +* Added support for multiple resources loading. This mechanic is the same as cocos2d-x now +* Optimised "Performance Tests -> Sprites Test", and increased its benchmark to 220%! +* Migrated audio (CocosDenshion) API to keep the same as Cocos2d JS API +* Added auto test for NodeTests and TilemapTests +* Changed CCTextureCache member functions such as addImage(path), addImageAysnc(path), removeTextureForKey(key) from using relative path to absolute path +* Added support for particle batch node + +* Bug fix: + 1. Fixed preLoading issue on iOS 5.1.1 + 2. Fixed cc.Menu / cc.MenuItemImage remaining touchable issue after replaceScene + 3. Fixed Box2d and chipmunk path error for single engine file mode + 4. Fixed cc.EditBox Dom Element position issue when cc.EditBox skewed + 5. Fixed cc.ScrollView position issue when it's parent node moved + 6. Fixed cc.TouchDispatcher can't touch issue when WebPage has been scrolled on Firefox or IE +* Known Issues: + 1. Effect Advanced Lens3D doesn't work + 2. ClipNodeTest effects varies in different browsers + +Cocos2d-html5-v2.1.3 @ May.1, 2013 +* CCEditbox now implemented for WebGL and JSB +* Updated CCBReader to latest version +* Performance optimization on Firefox 20% +* Added render mode flag to switch between WebGL and canvas2d +* Added support for Tizen +* Now able to load embedded texture file in a plist +* EGLView now works if canvas is placed inside another DOM element +* Added a Simulator which can be found in MoonWarriors Directory + +* Bug fix: + 1. Preloading on some mobile browsers + 2. CCLoader for WebGL + 3. ccNode memory leak +* Known Issues: + 1. Effect Advanced Lens3D doesn't work + 2. Particle System has some weird behavior when load from CCBReader + 3. RenderTextureTest RenderTextureIssue937 & Issue1464 doesn't work properly + 4. ClipNodeTest effects varies in different browsers + + +Cocos2d-html5-v2.1.2-beta @ Mar.20, 2013 +* WebGL rendering mode implemented - blazing fast on supported browsers +* Added many WegGL test to testbed +* cc.Loader now supports multiple stage preloading - all tests now preload by multi resource groups +* Now warns the user if their browser does not support html5 +* cc.Node now uses transform matrix - better performance +* Accelerometer implemented - Also works on Javascript binding (JSB) for Cocos2d-x & Cocos2d-iPhone +* Supports MP4 and M4a Audio format now +* Designer resolution for multi resolution support - Also works on Javascript binding for Cocos2d-x +* Faster Particle - thanks to Ivo Wetzel +* Bug fixes: + 1. File utility fixed + 2. Audio support bug fixed + 3. Removed some trailing coma which prevents blocks closure compiler + 4. Local storage bug fixed + 5. cc.MenuItemImage and cc.MenuItemToggle bugs fixed + 6. Fixed compatibility with some UIWebView + 7. Fixed rounding errors on ease actions +* Known Issues: + 1. Effect Advanced Lens3D doesn't work + 2. particle system can't load texture from plist + 3. EditBox doesn't work on WebGL mode + 4. Particle System has some weird behavior when load from CCBReader + 5. RenderTextureTest RenderTextureIssue937 & Issue1464 doesn't work properly + 6. ClipNodeTest effects varies in different browsers + +Cocos2d-html5-v2.1.1 @ Jan.28, 2013 +* Fixed bugs +* Added mouse button to MouseDispatcher, supports right-click +* Changed preload audio type from "bgm" and "effect" to "sound" +* Added "Sys" class for system capabilities +* Improved cc.BuilderReader to support .ccbi extension auto-completion +* Improved TMXXMLParser to support XML, CSV and zlib compression +* Changed cc.Time.gettimeofdayCocos2d to Date.now which is more javascript friendly. +* Added support for stackable actions + +Cocos2d-html5-v2.1.0 @ Dec.4, 2012 +* Improved cc.Class and add Release Mode +* All tests and games can now be run on Cocos2d-html5, Cocos2d-iPhone and Cocos2d-x +* Added support for google.base +* Added support for CocosBuilder and Bone Animation +* Updated API for Javascript Binding +* Integrated Chipmunk physical engine and chipmunk tests +* Added physicsDebugNode, physicsSprite, drawNode +* Built cocos2d-js-tests repo for tests +* Fixed support for mouse/touch/keyboard +* Fixed bugs +* Added WaterMelon with me and CocosDragon games for sample +* Added Edit Box for input + +Cocos2d-html5-v2.0.0 @ Aug.28, 2012 +* Updated API to Cocos2d-x V2.0 +* Updated template and directory name +* Improved JS files loader +* Added support for Dom rendering +* Updated JSDoc comments and shell +* Added TileMap property process and flip +* Improved BMFont +* Added Actions spline paths and cc.AnimationFrame +* Added support for multi-touch +* Added mini-framework for Dom manipulation +* Changed cc.Animation, cc.AudioEngine, cc.LableTTF and cc.Sprite API + + +Cocos2d-html5-v0.5.0-alpha2 @ Jun.18, 2012 +* Changed API, use "create" to construct all objects +* Fixed naming of variables +* Added JSDoc comments and shell +* Fixed Dom Menu flicker bug +* Changed code for closure compiler Advance optional +* Added version control + +Cocos2d-html5-v0.5.0-alpha @ May.28, 2012 +* supports canvas and Dom Menu +* part of test cases were added and tested in chrome +* porting from cocos2d-x is not finished +* files must load from http server + +Cocos2d-html5-v0.1.0 @ Jan.29, 2012 +* Build the directory structure of Engine +* cocos2d-html5 first version +* more details: http://www.cocos2d-x.org/ + + + + diff --git a/README.mdown b/README.mdown new file mode 100644 index 00000000000..c41b79bc2c8 --- /dev/null +++ b/README.mdown @@ -0,0 +1,52 @@ +Cocos2d-html5 +================== + +[Cocos2d-html5][1] is a cross-platform 2D game engine written in Javascript, based on [Cocos2d-X][2] 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 renderer. + +Cross Platform +------------- + * Popular browsers: Chrome 14+, Safari 5.0+, IE9+, Firefox 3.5+. + * Mobile platforms: coming soon. + * Native App: Same piece of code can run on "Cocos2d JS-Binding Engine" without or with little modification. + +Documentation +------------------ + * Website: [www.cocos2d-x.org][3] + * API References: [http://www.cocos2d-x.org/wiki/Reference] [4] + + +Installing from [bower][8] (version >=3.4) +------------------ + +```shell +$ bower install cocos2d-html5 +``` + +Running the tests (version <3) +------------------ + +```shell +$ git clone git://github.com/cocos2d/cocos2d-html5.git +$ cd cocos2d-html5 +$ git submodule update --init +$ python -m SimpleHTTPServer +``` +... and then open a browser and go to `http://localhost:8000/tests` + + +Contact us +------------------ + * Forum: [http://forum.cocos2d-x.org][5] + * Twitter: [http://www.twitter.com/cocos2dhtml5][6] + * Sina Microblog: [http://t.sina.com.cn/cocos2dhtml5][7] + +[1]: http://www.cocos2d-x.org "Cocos2d-html5" +[2]: http://www.cocos2d-x.org "Cocos2d-X" +[3]: http://www.cocos2d-x.org "www.cocos2d-x.org" +[4]: http://www.cocos2d-x.org/wiki/Reference "API References" +[5]: http://forum.cocos2d-x.org "http://forum.cocos2d-x.org" +[6]: http://www.twitter.com/cocos2dhtml5 "http://www.twitter.com/cocos2dhtml5" +[7]: http://t.sina.com.cn/cocos2dhtml5 "http://t.sina.com.cn/cocos2dhtml5" +[8]: http://bower.io "http://bower.io" \ No newline at end of file diff --git a/bower.json b/bower.json new file mode 100644 index 00000000000..e97d56bb83e --- /dev/null +++ b/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/cocos2d/ToMerge/qunit/test-event-system.js b/cocos2d/ToMerge/qunit/test-event-system.js new file mode 100644 index 00000000000..aa1dd99a41a --- /dev/null +++ b/cocos2d/ToMerge/qunit/test-event-system.js @@ -0,0 +1,360 @@ + +module('EventTarget'); + +test('basic test', function () { + var target = new cc.EventTarget(); + var fireEvent = new cc.Event('fire'); + var jumpEvent = new cc.Event('jump'); + + var cb1 = new Callback(); + var cb2 = new Callback(); + var cb3 = new Callback(); + + target.on('fire', cb1); + target.on('fire', cb2); + target.on('jump', cb3); + + cb1.enable(); + cb2.enable(); + cb3.disable('should only invoke the callbacks with the same event type'); + target.dispatchEvent(fireEvent); + cb1.once('callback1 should be invoked by fire event'); + cb2.once('callback2 should be invoked by fire event'); + + cb3.enable(); + target.dispatchEvent(jumpEvent); + cb3.once('callback3 should be invoked by jump event'); + + target.dispatchEvent(fireEvent); + cb1.once('callback1 should be invoked again'); + cb2.once('callback2 should be invoked again'); + + cb1.disable('should not invoke callback1 after `off`'); + target.off('fire', cb1); + target.dispatchEvent(fireEvent); + cb2.once('callback2 should still be invoked after callback1 canceled'); +}); + +test('emit', function () { + var target = new cc.EventTarget(); + var cb1 = new Callback().enable(); + target.on('fire', cb1); + cb1.callbackFunction(function (event) { + strictEqual(event.detail.param, 123, 'should pass the argument to listener'); + }); + target.emit('fire', { + param: 123 + }); + cb1.once('callback1 should be invoked by fire event'); +}); + +test('once', function () { + var target = new cc.EventTarget(); + var fireEvent = new cc.Event('fire'); + var cb1 = new Callback(); + + // once + target.once('fire', cb1.enable()); + target.dispatchEvent(fireEvent); + cb1.once('should be invoked if registered by once') + .disable('should only invoke once 1'); + target.dispatchEvent(fireEvent); + + // once + on + var cb2 = new Callback().enable(); + + target.once('fire', cb1.enable()); + target.on('fire', cb2); + + target.dispatchEvent(fireEvent); + cb1.once('should be invoked if registered by once and before other callback'); + cb2.once('should still be invoked if previous event was removed'); + + cb1.disable('should only invoke once 2'); + target.dispatchEvent(fireEvent); + cb2.once('should not remove common event'); + + // on + once + + target.once('fire', cb1.enable()); + target.dispatchEvent(fireEvent); + cb2.once(); + cb1.once('should be invoked if registered by once and after other callback') + .disable('should only invoke once 3'); + target.dispatchEvent(fireEvent); + cb2.once(); +}); + +test('test useCapture in on/off', function () { + var target = new cc.EventTarget(); + var event = new cc.Event('fire'); + var cb1 = new Callback().enable(); + var cb2 = new Callback().enable(); + + target.on('fire', cb1, true); + target.on('fire', cb2, false); + target.dispatchEvent(event); + cb1.once('registered as capturing phase should also be invoked in target phase'); + cb2.once('registered as bubbling phase should also be invoked in target phase'); + + target.off('fire', cb1, false); + target.off('fire', cb2, false); + cb2.disable('should not invoke callback2 after `off` with the same phase'); + target.dispatchEvent(event); + cb1.once('should still invoke callback1 after `off` but given another phase'); +}); + +test('test propagation', function () { + // define hierarchy + var node1 = new cc.EventTarget(); + var node2 = new cc.EventTarget(); + node2.parent = node1; + node2._getCapturingTargets = function (type, array) { + for (var target = this.parent; target; target = target.parent) { + if (target._capturingListeners && target._capturingListeners.has(type)) { + array.push(target); + } + } + }; + node2._getBubblingTargets = function (type, array) { + for (var target = this.parent; target; target = target.parent) { + if (target._bubblingListeners && target._bubblingListeners.has(type)) { + array.push(target); + } + } + }; + + var event = new cc.Event('fire', true); + var capture1 = new Callback(); + var capture2 = new Callback(); + var bubble1 = new Callback(); + var bubble2 = new Callback(); + // capture1 -> capture2 -> bubble2 -> bubble1 + node1.on('fire', capture1, true); + node2.on('fire', capture2, true); + node1.on('fire', bubble1, false); + node2.on('fire', bubble2, false); + + // dispatched by node1 + + capture1.callbackFunction(function (event) { + strictEqual(event.eventPhase, cc.FireEvent.AT_TARGET, 'event phase should be target if dispatched by self 1'); + strictEqual(bubble1.calledCount, 0, 'captures should be invoked before bubbles'); + }).enable(); + bubble1.callbackFunction(function (event) { + strictEqual(event.eventPhase, cc.FireEvent.AT_TARGET, 'event phase should be target if dispatched by self 2'); + }).enable(); + node1.dispatchEvent(event); + capture1.once('callback also will be invoked at target phase if registered as capturing'); + bubble1.once('callback also will be invoked at target phase if registered as bubbling'); + + // dispatched by node2 + + capture1.callbackFunction(function (event) { + strictEqual(event.eventPhase, cc.FireEvent.CAPTURING_PHASE, 'event phase should be capturing if dispatched by node2'); + strictEqual(event.target, node2, 'target of capture1 should be node2'); + strictEqual(event.currentTarget, node1, 'current target of capture1 should be node1'); + strictEqual(capture2.calledCount, 0, 'captures1 -> capture2'); + }).enable(); + capture2.callbackFunction(function (event) { + strictEqual(event.eventPhase, cc.FireEvent.AT_TARGET, 'event phase of capture2 should be at target if dispatched by node2'); + strictEqual(event.target, node2, 'target of capture2 should be node2'); + strictEqual(event.currentTarget, node2, 'current target of capture2 should be node2'); + strictEqual(bubble2.calledCount, 0, 'captures2 -> bubble2'); + }).enable(); + bubble2.callbackFunction(function (event) { + strictEqual(event.eventPhase, cc.FireEvent.AT_TARGET, 'event phase of bubble2 should be at target if dispatched by node2'); + strictEqual(event.target, node2, 'target of bubble2 should be node2'); + strictEqual(event.currentTarget, node2, 'current target of bubble2 should be node2'); + strictEqual(bubble1.calledCount, 0, 'bubble2 -> bubble1'); + }).enable(); + bubble1.callbackFunction(function (event) { + strictEqual(event.eventPhase, cc.FireEvent.BUBBLING_PHASE, 'event phase should be bubble if dispatched by node2'); + strictEqual(event.target, node2, 'target of bubble1 should be node2'); + strictEqual(event.currentTarget, node1, 'current target of bubble1 should be node1'); + }).enable(); + node2.dispatchEvent(event); + capture1.once('capture1 should be invoked if dispatched by node2'); + capture2.once('capture2 should be invoked if dispatched by node2'); + bubble1.once('bubble1 should be invoked if dispatched by node2'); + bubble2.once('bubble2 should be invoked if dispatched by node2'); +}); + +test('test stop propagation', function () { + // define hierarchy + var node1 = new cc.EventTarget(); + var node2 = new cc.EventTarget(); + node2.parent = node1; + node2._getCapturingTargets = function (type, array) { + for (var target = this.parent; target; target = target.parent) { + if (target._capturingListeners && target._capturingListeners.has(type)) { + array.push(target); + } + } + }; + node2._getBubblingTargets = function (type, array) { + for (var target = this.parent; target; target = target.parent) { + if (target._bubblingListeners && target._bubblingListeners.has(type)) { + array.push(target); + } + } + }; + + var event = new cc.Event('fire', true); + var capture1 = new Callback().enable(); + var capture2 = new Callback().enable(); + var bubble1 = new Callback().enable(); + var bubble2 = new Callback().enable(); + node1.on('fire', capture1, true); + node2.on('fire', capture2, true); + node1.on('fire', bubble1, false); + node2.on('fire', bubble2, false); + + // stop at bubble 2 + + bubble2.callbackFunction(function (event) { + event.stop(); + }); + bubble1.disable('bubble1 should not be invoked if propagation stopped'); + node2.dispatchEvent(event); + capture1.once('capture1 should be invoked if propagation not yet stopped before'); + capture2.once('capture2 should be invoked if propagation not yet stopped before'); + bubble2.once('bubble2 should be invoked if propagation not yet stopped before'); + + // stop at capture 2 + capture2.callbackFunction(function (event) { + event.stop(); + }); + bubble2.disable('bubble2 should not be invoked if propagation stopped before'); + node2.dispatchEvent(event); + capture1.once('capture1 should be invoked if propagation not yet stopped before'); + capture2.once('capture2 should be invoked if propagation not yet stopped before'); + + // stop at capture 1 + capture1.callbackFunction(function (event) { + event.stop(); + }); + capture2.disable('capture2 should not be invoked if propagation stopped before'); + node2.dispatchEvent(event); + capture1.once('capture1 should be invoked if propagation not yet stopped before'); +}); + +test('test stop propagation immediate', function () { + // define hierarchy + var node1 = new cc.EventTarget(); + var node2 = new cc.EventTarget(); + node2.parent = node1; + node2._getCapturingTargets = function (type, array) { + for (var target = this.parent; target; target = target.parent) { + if (target._capturingListeners && target._capturingListeners.has(type)) { + array.push(target); + } + } + }; + node2._getBubblingTargets = function (type, array) { + for (var target = this.parent; target; target = target.parent) { + if (target._bubblingListeners && target._bubblingListeners.has(type)) { + array.push(target); + } + } + }; + + var event = new cc.Event('fire', true); + var capture1 = new Callback().enable(); + var capture1_2nd = new Callback().enable(); + var capture2 = new Callback().enable(); + var bubble1 = new Callback().enable(); + var bubble2 = new Callback().enable(); + var bubble2_2nd = new Callback().enable(); + node1.on('fire', capture1, true); + node1.on('fire', capture1_2nd, true); + node2.on('fire', capture2, true); + node1.on('fire', bubble1, false); + node2.on('fire', bubble2, false); + node2.on('fire', bubble2_2nd, false); + + // stop at bubble 2 + + bubble2.callbackFunction(function (event) { + event.stop(true); + }); + bubble2_2nd.disable('bubble2_2nd should not be invoked if propagation stopped immediate'); + bubble1.disable('bubble1 should not be invoked if propagation stopped immediate'); + node2.dispatchEvent(event); + capture1.once('capture1 should be invoked if propagation not yet stopped before'); + capture2.once('capture2 should be invoked if propagation not yet stopped before'); + bubble2.once('bubble2 should be invoked if propagation not yet stopped before'); + + // stop at capture 1 + capture1.callbackFunction(function (event) { + event.stop(true); + }); + capture1_2nd.disable('capture1_2nd should not be invoked if propagation stopped immediate'); + capture2.disable('capture2 should not be invoked if propagation stopped immediate'); + node2.dispatchEvent(event); + capture1.once('capture1 should be invoked if propagation not yet stopped before'); +}); + +test('test Event.bubbles', function () { + // define hierarchy + var node1 = new cc.EventTarget(); + var node2 = new cc.EventTarget(); + node2.parent = node1; + node2._getBubblingTargets = function (type, array) { + for (var target = this.parent; target; target = target.parent) { + if (target._bubblingListeners && target._bubblingListeners.has(type)) { + array.push(target); + } + } + }; + + var bubble1 = new Callback(); + node1.on('fire', bubble1, false); + + var event = new cc.Event('fire'); + event.bubbles = false; + bubble1.disable('bubble1 should not be invoked if set event.bubbles to false'); + node2.dispatchEvent(event); + + event.bubbles = true; + bubble1.enable(); + node2.dispatchEvent(event); + bubble1.once('bubble1 should be invoked if set event.bubbles to true'); +}); + +//test('', function () { +// // define hierarchy +// var node1 = new cc.EventTarget(); +// var node2 = new cc.EventTarget(); +// var node3 = new cc.EventTarget(); +// node2.parent = node1; +// node3.parent = node2; +// node3._getCapturingTargets = node2._getCapturingTargets = function (type, array) { +// for (var target = this.parent; target; target = target.parent) { +// if (target._capturingListeners && target._capturingListeners.has(type)) { +// array.push(target); +// } +// } +// }; +// node3._getBubblingTargets = node2._getBubblingTargets = function (type, array) { +// for (var target = this.parent; target; target = target.parent) { +// if (target._bubblingListeners && target._bubblingListeners.has(type)) { +// array.push(target); +// } +// } +// }; + +// var event = new cc.Event('fire'); +// var capture1 = new Callback(); +// var capture2 = new Callback(); +// var capture3 = new Callback(); +// var bubble1 = new Callback(); +// var bubble2 = new Callback(); +// var bubble3 = new Callback(); +// node1.on('fire', capture1, true); +// node2.on('fire', capture2, true); +// node1.on('fire', bubble1, false); +// node3.on('fire', capture3, true); +// node2.on('fire', bubble2, false); +// node3.on('fire', bubble3, false); +//}); diff --git a/cocos2d/ToMerge/qunit/test-ticker.js b/cocos2d/ToMerge/qunit/test-ticker.js new file mode 100644 index 00000000000..86bcd48a930 --- /dev/null +++ b/cocos2d/ToMerge/qunit/test-ticker.js @@ -0,0 +1,39 @@ +// jshint ignore: start + +module('ticker'); + +asyncTest('test now', function() { + ok(typeof Ticker.now() === 'number'); + ok(Ticker.now() >= 0); + + var startTime = Ticker.now(); + setTimeout(function () { + var delta = Ticker.now() - startTime; + ok(0 < delta && delta < 0.1, 'elpased time should in range (0, 0.1): ' + delta); + start(); + }, 10); +}); + +asyncTest('test requestAnimationFrame', function() { + var startTime = Ticker.now(); + var tolerance = 0.5; + + var requestId = Ticker.requestAnimationFrame(function () { + var delta = Ticker.now() - startTime; + ok(0 <= delta && delta < (1 / 60) + tolerance, 'any time to next frame should <= 0.016: ' + delta); + start(); + }); + strictEqual(typeof requestId, 'number', 'requestAnimationFrame should return a request id'); +}); + +asyncTest('test cancelAnimationFrame', 0, function() { + var requestId = Ticker.requestAnimationFrame(function () { + ok(false, 'should not callback after cancelAnimationFrame'); + }); + Ticker.cancelAnimationFrame(requestId); + setTimeout(function () { + start(); + }, 30); +}); + +// jshint ignore: end diff --git a/cocos2d/ToMerge/qunit/test-time.js b/cocos2d/ToMerge/qunit/test-time.js new file mode 100644 index 00000000000..f6908d2a14c --- /dev/null +++ b/cocos2d/ToMerge/qunit/test-time.js @@ -0,0 +1,40 @@ +module('time'); + +var tolerance = 0.000001; + +test('test restart', function() { + var now = 321; + Time._restart(now); + strictEqual(Time.time, 0, 'reset time'); + strictEqual(Time.realTime, 0, 'reset realTime'); + strictEqual(Time.frameCount, 0, 'reset frameCount'); +}); + +test('test update', function() { + Time.maxDeltaTime = 0.2; + var now = 321; + Time._restart(now); + + + now += 0.01; + var startTime = now; + Time._update(now); + close(Time.time, 0, tolerance, 'time should equals 0 in first frame'); + close(Time.realTime, 0, tolerance, 'realTime should equals 0 in first frame'); + close(Time.frameCount, 1, tolerance, 'frameCount should equals update count 1'); + + now += 0.01; + Time._update(now); + close(Time.deltaTime, 0.01, tolerance, 'deltaTime should equals 0.01'); + close(Time.time, 0.01, tolerance, 'time should equals elapsed time since restart 2'); + close(Time.realTime, now - startTime, tolerance, 'realTime should equals elapsed time since restart 2'); + close(Time.frameCount, 2, tolerance, 'frameCount should equals update count 2'); + + now += 0.5; + Time._update(now); + close(Time.deltaTime, Time.maxDeltaTime, tolerance, 'deltaTime should less equals maxDeltaTime'); + var expectedTime = now - startTime - 0.5 + Time.maxDeltaTime; + close(Time.time, expectedTime, tolerance, 'time should lag because of limitation by maxDeltaTime'); + close(Time.realTime, now - startTime, tolerance, 'realTime should equals elapsed time since restart 3'); + close(Time.frameCount, 3, tolerance, 'frameCount should equals update count 2'); +}); diff --git a/cocos2d/ToMerge/wrappers/bitmap-font.js b/cocos2d/ToMerge/wrappers/bitmap-font.js new file mode 100644 index 00000000000..f9a228966d2 --- /dev/null +++ b/cocos2d/ToMerge/wrappers/bitmap-font.js @@ -0,0 +1,177 @@ + +var TextAlign = cc.TextAlignment; + +/** + * Enum for text anchor + * @readOnly + * @enum {number} + */ +var TextAnchor = cc.Enum({ + TopLeft: -1, + TopCenter: -1, + TopRight: -1, + MiddleLeft: -1, + MiddleCenter: -1, + MiddleRight: -1, + BottomLeft: -1, + BottomCenter: -1, + BottomRight: -1, +}); + +cc._TextAnchor = TextAnchor; + +var getAnchorPoint = (function () { + var Anchor2Point = new Array(TextAnchor.BottomRight + 1); + Anchor2Point[TextAnchor.TopLeft] = cc.p(0, 1); + Anchor2Point[TextAnchor.TopCenter] = cc.p(0.5, 1); + Anchor2Point[TextAnchor.TopRight] = cc.p(1, 1); + Anchor2Point[TextAnchor.MiddleLeft] = cc.p(0, 0.5); + Anchor2Point[TextAnchor.MiddleCenter] = cc.p(0.5, 0.5); + Anchor2Point[TextAnchor.MiddleRight] = cc.p(1, 0.5); + Anchor2Point[TextAnchor.BottomLeft] = cc.p(0, 0); + Anchor2Point[TextAnchor.BottomCenter] = cc.p(0.5, 0); + Anchor2Point[TextAnchor.BottomRight] = cc.p(1, 0); + + return (function (textAnchor) { + var anchorPoint = Anchor2Point[textAnchor]; + return cc.p(anchorPoint); + }); +})(); + + +var NodeWrapper = require('./node'); + +var BitmapFontWrapper = cc.Class({ + name: 'cc.BitmapFontWrapper', + extends: NodeWrapper, + + ctor: function () { + }, + + properties: { + + bitmapFont: { + get: function () { + return this.targetN._fntFile || ''; + }, + set: function (value) { + this.targetN._fntFile = value; + + this.onBeforeSerialize(); + this.createNode(this.targetN); + }, + url: cc.BitmapFont + }, + + text: { + get: function () { + return this.targetN.string; + }, + set: function ( value ) { + if (typeof value === 'string') { + this.targetN.string = value; + } + else { + cc.error('The new text must be string'); + } + } + }, + + anchor: { + get: function () { + return this._anchor; + }, + set: function ( value ) { + if (typeof value === 'number') { + this._anchor = value; + + var anchorPoint = getAnchorPoint(value); + this.targetN.setAnchorPoint( anchorPoint ); + } + else { + cc.error('The new text must be number'); + } + }, + type: TextAnchor + }, + + align: { + get: function () { + // jsb not implement yet + if (typeof jsb !== 'undefined') return TextAlign.Left; + + return this.targetN.textAlign; + }, + set: function ( value ) { + if (typeof value === 'number') { + this.targetN.textAlign = value; + } + else { + cc.error('The new text must be number'); + } + }, + type: TextAlign + }, + + childrenN: { + get: function () { + return []; + }, + }, + + + _text: { + default: "" + }, + + _anchor: { + default: TextAnchor.MiddleCenter + }, + + _align: { + default: TextAlign.Left + }, + + _bitmapFont: { + default: '', + url: cc.BitmapFont + } + }, + + onBeforeSerialize: function () { + NodeWrapper.prototype.onBeforeSerialize.call(this); + + this._text = this.text; + this._anchor = this.anchor; + this._align = this.align; + this._bitmapFont = this.bitmapFont; + }, + + createNode: function (node) { + node = node || new cc.LabelBMFont(); + + var bitmapFontUrl = this._bitmapFont; + + if ( bitmapFontUrl ) { + node._fntFile = bitmapFontUrl; + cc.loader.load( bitmapFontUrl, function (err, results) { + node.initWithString(this._text, bitmapFontUrl); + node.setAnchorPoint( getAnchorPoint(this._anchor) ); + node.textAlign = this._align; + + NodeWrapper.prototype.createNode.call(this, node); + }.bind(this)); + } + else { + node.string = this._text; + node.setAnchorPoint( getAnchorPoint(this._anchor) ); + node.textAlign = this._align; + + NodeWrapper.prototype.createNode.call(this, node); + } + + return node; + } +}); + +cc.BitmapFontWrapper = module.exports = BitmapFontWrapper; diff --git a/cocos2d/ToMerge/wrappers/draw-node.js b/cocos2d/ToMerge/wrappers/draw-node.js new file mode 100644 index 00000000000..744def2b11d --- /dev/null +++ b/cocos2d/ToMerge/wrappers/draw-node.js @@ -0,0 +1,17 @@ +var NodeWrapper = require('./node'); + +var DrawNodeWrapper = cc.Class({ + name: 'cc.DrawNodeWrapper', + extends: NodeWrapper, + + createNode: function(node){ + node = node || new cc.DrawNode(); + + NodeWrapper.prototype.createNode.call(this, node); + + return node; + } + +}); + +cc.DrawNodeWrapper = module.exports = DrawNodeWrapper; diff --git a/cocos2d/ToMerge/wrappers/edit-box.js b/cocos2d/ToMerge/wrappers/edit-box.js new file mode 100644 index 00000000000..e1834fd475e --- /dev/null +++ b/cocos2d/ToMerge/wrappers/edit-box.js @@ -0,0 +1,250 @@ +var Vec2 = cc.Vec2; + +var NodeWrapper = require('./node'); + +var EditBoxWrapper = cc.Class({ + name: 'cc.EditBoxWrapper', + extends: NodeWrapper, + + properties: { + normalBackground: { + default: '', + url: cc.Texture2D, + + notify: function() { + if (!this.targetN) { + return; + } + var value = this.normalBackground; + var normalScale9Sprite = new cc.Scale9Sprite(value); + normalScale9Sprite.setPreferredSize(this.targetN.getContentSize()); + var oldPosition = this.targetN.getPosition(); + + this.targetN.initWithSizeAndBackgroundSprite(this.targetN.getContentSize(), normalScale9Sprite); + this.targetN.setPosition(oldPosition); + } + }, + childrenN: { + get: function() { + return []; + } + }, + size: { + get: function() { + var size = this.targetN.getPreferredSize(); + return new Vec2(size.width, size.height); + }, + + set: function(value) { + if (value instanceof Vec2) { + this.targetN.setPreferredSize(cc.size(value.x, value.y)); + } else { + cc.error('The value must be cc.Vec2 -> size.'); + } + } + }, + _placeholder: { + default: 'input your text here' + }, + placeholder: { + get: function() { + return this.targetN.getPlaceHolder(); + }, + set: function(value) { + if (typeof value === 'string') { + this.targetN.setPlaceHolder(value); + } else { + cc.error('The value must be string -> placeholder'); + } + } + }, + text: { + get: function() { + return this.targetN.getString(); + }, + set: function(value) { + if (typeof value === 'string') { + this.targetN.setString(value); + } else { + cc.error('The value must be string -> text.'); + } + } + }, + _text: { + default: '' + }, + fontColor: { + default: cc.Color.WHITE, + type: cc.Color, + + notify: function() { + if (!this.targetN) return; + + var value = this.fontColor; + if (value instanceof cc.Color) { + this.targetN.setFontColor(value); + } else { + cc.error('The value must be cc.Color -> fontColor.'); + } + } + }, + fontName: { + default: 'Arial', + + notify: function() { + if (!this.targetN) return; + + var value = this.fontName; + if (typeof value === 'string') { + this.targetN.setFontName(value); + } else { + cc.error('The value must be a string -> fontName.'); + } + } + }, + fontSize: { + default: 14, + + notify: function() { + if (!this.targetN) return; + + var value = this.fontSize; + if (!isNaN(value)) { + this.targetN.setFontSize(value); + } else { + cc.error('The value is NaN.'); + } + } + }, + placeholderColor: { + default: cc.Color.WHITE, + type: cc.Color, + + notify: function() { + if (!this.targetN) return; + + var value = this.placeholderColor; + if (value instanceof cc.Color) { + this.targetN.setPlaceholderFontColor(value); + } else { + cc.error('The value must be cc.Color -> placeholderColor.'); + } + } + }, + placeholderSize: { + default: 12, + + notify: function() { + if (!this.targetN) return; + + var value = this.placeholderSize; + if (!isNaN(value)) { + this.targetN.setPlaceholderFontSize(value); + } else { + cc.error('The value is NaN!'); + } + } + }, + maxLength: { + get: function() { + return this.targetN.getMaxLength(); + }, + set: function(value) { + if (!isNaN(value)) { + this.targetN.setMaxLength(value); + } else { + cc.error('The value is NaN!'); + } + } + }, + _maxLength: { + default: 50 + }, + inputMode: { + default: cc.EditBox.InputMode.ANY, + type: cc.EditBox.InputMode, + notify: function() { + if (!this.targetN) return; + + var value = this.inputMode; + if (!isNaN(value)) { + this.targetN.setInputMode(value); + } else { + cc.error('The value must be cc.EditBox.InputMode -> inputMode.'); + } + } + }, + inputFlag: { + default: cc.EditBox.InputFlag.SENSITIVE, + type: cc.EditBox.InputFlag, + notify: function() { + if (!this.targetN) return; + + var value = this.inputFlag; + if (!isNaN(value)) { + this.targetN.setInputFlag(value); + } else { + cc.error('The value must be cc.EditBox.InputFlag -> inputFlag.'); + } + } + }, + keyboardType: { + default: cc.KeyboardReturnType.DEFAULT, + type: cc.KeyboardReturnType, + notify: function() { + if (!this.targetN) return; + + var value = this.keyboardType; + if (!isNaN(value)) { + this.targetN.setReturnType(value); + } else { + cc.error('The value must be cc.KeyboardReturnType -> keyboardType.'); + } + } + + } + }, + onBeforeSerialize: function() { + NodeWrapper.prototype.onBeforeSerialize.call(this); + + this._text = this.text; + this._placeholder = this.placeholder; + this._maxLength = this.maxLength; + }, + + createNode: function(node) { + var contentSize; + if (this._size) { + contentSize = cc.size(this._size[0], this._size[1]); + } else { + contentSize = cc.size(100, 50); + } + + var normalScale9Sprite; + if (this.normalBackground) { + normalScale9Sprite = new cc.Scale9Sprite(this.normalBackground); + } else { + normalScale9Sprite = new cc.Scale9Sprite(); + } + + node = node || new cc.EditBox(contentSize, normalScale9Sprite, new cc.Scale9Sprite(), new cc.Scale9Sprite()); + NodeWrapper.prototype.createNode.call(this, node); + + node.setPreferredSize(contentSize); + node.setString(this._text); + node.setFontColor(this.fontColor); + node.setFontSize(this.fontSize); + node.setFontName(this.fontName); + node.setPlaceHolder(this._placeholder); + node.setPlaceholderFontSize(this.placeholderSize); + node.setPlaceholderFontColor(this.placeholderColor); + node.setMaxLength(this._maxLength); + node.setInputMode(this.inputMode); + node.setInputFlag(this.inputFlag); + node.setReturnType(this.keyboardType); + + return node; + } +}); + +cc.EditBoxWrapper = module.exports = EditBoxWrapper; diff --git a/cocos2d/ToMerge/wrappers/index.js b/cocos2d/ToMerge/wrappers/index.js new file mode 100644 index 00000000000..0ca4abc4289 --- /dev/null +++ b/cocos2d/ToMerge/wrappers/index.js @@ -0,0 +1,27 @@ +module.exports = [ + [cc, 'Node', require('./node'), 'Node'], + [cc, 'SpriteBatchNode',require('./sprite-batch-node'), 'SpriteBatchNode'], + [cc, 'LabelBMFont', require('./bitmap-font'), 'LabelBMFont'], + [cc, 'LabelTTF', require('./label-ttf'), 'LabelTTF'], + [cc, 'ParticleSystem', require('./particle'), 'ParticleSystem'], + [cc, 'ProgressTimer', require('./progress-timer'), 'ProgressTimer'], + [cc, 'TMXTiledMap', require('./tiled-map')], + [cc, 'Layer', require('./layer'), 'Layer'], + [cc, 'LayerColor', require('./layer-color'), 'LayerColor'], + [cc, 'DrawNode', require('./draw-node'), 'DrawNode'], + [cc, 'EditBox', require('./edit-box'), 'EditBox'], + + [ccui, 'Button', require('./ui/button'), 'UI/Button'], + [ccui, 'ImageView', require('./ui/scale9-sprite'),'UI/Scale9Sprite'], + [ccui, 'CheckBox', require('./ui/check-box'), 'UI/CheckBox'], + [ccui, 'LoadingBar', require('./ui/loading-bar'), 'UI/LoadingBar'], + [ccui, 'Slider', require('./ui/slider'), 'UI/Slider'], + [ccui, 'ScrollView', require('./ui/scroll-view'), 'UI/ScrollView'], + [ccui, 'ListView', require('./ui/list-view'), 'UI/ListView'], + [ccui, 'PageView', require('./ui/page-view'), 'UI/PageView'], + [ccui, 'Layout', require('./ui/layout'), 'UI/Layout'], + [ccui, 'Text', require('./ui/text'), 'UI/Text'], + [ccui, 'TextField', require('./ui/text-field'), 'UI/TextField'], + [ccui, 'TextAtlas', require('./ui/text-atlas'), 'UI/TextAtlas'], + [ccui, 'TextBMFont', require('./ui/text-bitmap-font'), 'UI/TextBMFont'], +]; diff --git a/cocos2d/ToMerge/wrappers/label-ttf.js b/cocos2d/ToMerge/wrappers/label-ttf.js new file mode 100644 index 00000000000..28b6f76f8ca --- /dev/null +++ b/cocos2d/ToMerge/wrappers/label-ttf.js @@ -0,0 +1,202 @@ +var Utils = require('./utils'); +var NodeWrapper = require('./node'); + +var LabelTTFWrapper = cc.Class({ + name: 'cc.LabelTTFWrapper', + extends: NodeWrapper, + + properties: { + + text: { + get: function () { + return this.targetN.string; + }, + set: function (value) { + if (typeof value === 'string') { + this.targetN.string = value; + } + else { + cc.error('The new text must be String'); + } + } + }, + + fontSize: { + get: function () { + return this.targetN.fontSize; + }, + set: function (value) { + if ( !isNaN(value) ) { + this.targetN.fontSize = value; + } + else { + cc.error('The new fontSize must not be NaN'); + } + } + }, + + font: { + default: null, + type: cc.TTFFont, + + notify: function () { + if ( !this.targetN ) return; + + var value = this.font; + if (!value || value instanceof cc.TTFFont) { + Utils.setFontToNode(value, this.targetN); + } + else { + cc.error('The new font must be cc.TTFFont'); + } + } + }, + + fontFamily: { + get: function () { + return this.targetN.fontName; + }, + set: function (value) { + if (typeof value === 'string') { + this.targetN.fontName = value; + } + else { + cc.error('The new fontFamily must be String'); + } + } + }, + + align: { + get: function () { + return this.targetN.textAlign; + }, + set: function ( value ) { + if (typeof value === 'number') { + this.targetN.textAlign = value; + } + else { + cc.error('The new textAlign must be number'); + } + }, + type: cc.TextAlignment + }, + + verticalAlign: { + get: function () { + return this.targetN.verticalAlign; + }, + set: function ( value ) { + if (typeof value === 'number') { + this.targetN.verticalAlign = value; + } + else { + cc.error('The new verticalAlign must be number'); + } + }, + type: cc.VerticalTextAlignment + }, + + boundingBox: { + get: function () { + var target = this.targetN; + return new cc.Vec2(target.boundingWidth, target.boundingHeight); + }, + set: function (value) { + if (value instanceof cc.Vec2) { + this.targetN.boundingWidth = value.x; + this.targetN.boundingHeight = value.y; + } + else { + cc.error('The new boundingBox must be Vec2'); + } + }, + type: cc.Vec2 + }, + + lineHeight: { + get: function() { + return this.targetN.getLineHeight(); + }, + set: function (value) { + if (typeof value === 'number') { + this.targetN.setLineHeight( value ); + this.targetN._setUpdateTextureDirty(); + } + else { + cc.error('The new lineHeight must be number'); + } + } + }, + + + _text: { + default: 'Label' + }, + + _fontSize: { + default: 16 + }, + + _fontFamily: { + default: null + }, + + _align: { + default: cc.TextAlignment.LEFT + }, + + _verticalAlign: { + default: cc.VerticalTextAlignment.TOP + }, + + _boundingBox: { + default: null + }, + + _lineHeight: { + default: null + } + }, + + onBeforeSerialize: function () { + NodeWrapper.prototype.onBeforeSerialize.call(this); + + this._text = this.text; + this._fontSize = this.fontSize; + this._fontFamily = this.fontFamily; + this._align = this.align; + this._verticalAlign = this.verticalAlign; + this._boundingBox = [this.boundingBox.x, this.boundingBox.y]; + this._lineHeight = this.lineHeight; + }, + + createNode: function (node) { + + node = node || new cc.LabelTTF(); + + NodeWrapper.prototype.createNode.call(this, node); + + node.string = this._text; + node.fontSize = this._fontSize; + node.fontName = this._fontFamily === null ? node.fontName : this._fontFamily; + node.textAlign = this._align; + node.verticalAlign = this._verticalAlign; + + if (typeof this._lineHeight === 'number' && + node.setLineHeight) { + node.setLineHeight( this._lineHeight ); + } + + var boundingBox = this._boundingBox; + if (boundingBox) { + node.boundingWidth = boundingBox[0]; + node.boundingHeight = boundingBox[1]; + } + + Utils.setFontToNode(this.font, node); + + return node; + } +}); + +cc.LabelTTFWrapper = module.exports = LabelTTFWrapper; diff --git a/cocos2d/ToMerge/wrappers/layer-color.js b/cocos2d/ToMerge/wrappers/layer-color.js new file mode 100644 index 00000000000..e6e82bb82a2 --- /dev/null +++ b/cocos2d/ToMerge/wrappers/layer-color.js @@ -0,0 +1,17 @@ + +var LayerWrapper = require('./layer'); + +var LayerColorWrapper = cc.Class({ + name: 'cc.LayerColorWrapper', + extends: LayerWrapper, + + createNode: function (node) { + node = node || new cc.LayerColor(); + + LayerWrapper.prototype.createNode.call(this, node); + + return node; + } +}); + +cc.LayerColorWrapper = module.exports = LayerColorWrapper; diff --git a/cocos2d/ToMerge/wrappers/layer.js b/cocos2d/ToMerge/wrappers/layer.js new file mode 100644 index 00000000000..1a1f0f1795b --- /dev/null +++ b/cocos2d/ToMerge/wrappers/layer.js @@ -0,0 +1,44 @@ + +var NodeWrapper = require('./node'); + +var LayerWrapper = cc.Class({ + name: 'cc.LayerWrapper', + extends: NodeWrapper, + + properties: { + bake: { + default: false, + + notify: function (value) { + if (!this.targetN) return; + + if (typeof value === 'boolean') { + if (value) { + this.targetN.bake(); + } + else { + this.targetN.unbake(); + } + } + else { + cc.error('The new bake must be boolean'); + } + } + } + }, + + createNode: function (node) { + node = node || new cc.Layer(); + node.setAnchorPoint(0, 0); + + if (this.bake) { + node.bake(); + } + + NodeWrapper.prototype.createNode.call(this, node); + + return node; + } +}); + +cc.LayerWrapper = module.exports = LayerWrapper; diff --git a/cocos2d/ToMerge/wrappers/progress-timer.js b/cocos2d/ToMerge/wrappers/progress-timer.js new file mode 100644 index 00000000000..76ac5683fa5 --- /dev/null +++ b/cocos2d/ToMerge/wrappers/progress-timer.js @@ -0,0 +1,167 @@ +var NodeWrapper = require('./node'); + +var ProgressTimerWrapper = cc.Class({ + name: 'cc.ProgressTimerWrapper', + extends: NodeWrapper, + + ctor: function () { + this._midPoint = [0.5, 0.5]; + this._barChangeRate = [1, 1]; + }, + + properties: { + + _type: { + default: cc.ProgressTimer.Type.RADIAL, + type: cc.ProgressTimer.Type + }, + + type: { + get: function () { + return this.targetN.getType(); + }, + set: function (value) { + if (!isNaN(value)) { + this.targetN.setType(value); + } + else { + cc.error('The new type must not be NaN'); + } + }, + type: cc.ProgressTimer.Type + }, + + _percentage: { + default: 50, + }, + + percentage: { + get: function() { + return this.targetN.getPercentage(); + }, + set: function (value) { + if (!isNaN(value)) { + this.targetN.setPercentage(value); + } + else { + cc.error('The new percentage must not be NaN'); + } + }, + }, + + _midPoint: { + default: [] + }, + + midPoint: { + get: function () { + var pt = this.targetN.getMidpoint(); + return new cc.Vec2(pt.x, pt.y); + }, + set: function (value) { + if ( value instanceof cc.Vec2 ) { + this.targetN.setMidpoint(cc.p(value.x, value.y)); + } + else { + cc.error('The new midPoint must be cc.Vec2'); + } + } + }, + + _barChangeRate: { + default: [] + }, + + barChangeRate: { + get: function () { + var pt = this.targetN.getBarChangeRate(); + return new cc.Vec2(pt.x, pt.y); + }, + set: function (value) { + if ( value instanceof cc.Vec2 ) { + this.targetN.setBarChangeRate(cc.p(value.x, value.y)); + } + else { + cc.error('The new barChangeRate must be cc.Vec2'); + } + } + }, + + _reverseDirection : { + default: false, + }, + + reverseDirection: { + get: function () { + return this.targetN.isReverseDirection(); + }, + set: function (value) { + if (typeof value === 'boolean') { + this.targetN.setReverseProgress(value); + } + else { + cc.error('The new reverseDirection must be Boolean'); + } + } + }, + + _texture: { + default: '', + url: cc.Texture2D + }, + + texture: { + get: function () { + var tex = this.targetN.getSprite().texture; + return (tex && tex.url) || ''; + }, + set: function (value) { + this.targetN.getSprite().texture = value; + }, + url: cc.Texture2D + } + }, + + onBeforeSerialize: function () { + NodeWrapper.prototype.onBeforeSerialize.call(this); + this._type = this.type; + this._percentage = this.percentage; + this._midPoint = [this.midPoint.x, this.midPoint.y]; + this._barChangeRate = [this.barChangeRate.x, this.barChangeRate.y]; + this._reverseDirection = this.reverseDirection; + this._texture = this.texture; + }, + + createNode: function (node) { + node = node || new cc.ProgressTimer(new cc.Sprite()); + + var sp = node.getSprite(); + if (this._texture) { + sp.texture = this._texture; + + if (cc.sys.isNative) { + // jsb Texture will not save url, so we save manually. + sp.texture.url = this._texture; + } + } + + node.setType(this._type); + node.setPercentage(this._percentage); + + if (this._midPoint) { + node.setMidpoint(cc.p(this._midPoint[0], this._midPoint[1])); + } + + if (this._barChangeRate) { + node.setBarChangeRate(cc.p(this._barChangeRate[0], this._barChangeRate[1])); + } + + node.setReverseProgress(this._reverseDirection); + + NodeWrapper.prototype.createNode.call(this, node); + + return node; + } +}); + +cc.ProgressTimerWrapper = module.exports = ProgressTimerWrapper; diff --git a/cocos2d/ToMerge/wrappers/sprite-batch-node.js b/cocos2d/ToMerge/wrappers/sprite-batch-node.js new file mode 100644 index 00000000000..60baf90946d --- /dev/null +++ b/cocos2d/ToMerge/wrappers/sprite-batch-node.js @@ -0,0 +1,55 @@ + +var NodeWrapper = require('./node'); + +var SpriteBatchNodeWrapper = cc.Class({ + name: 'cc.SpriteBatchNodeWrapper', + extends: NodeWrapper, + + properties: { + + texture_: { + get: function () { + var tex = this.targetN.texture; + return (tex && tex.url) || ''; + }, + set: function (value) { + this.targetN.initWithFile(value); + }, + url: cc.Texture2D + }, + + texture: { + default: '', + url: cc.Texture2D, + visible: false + }, + + _textureObject: { + default: null + }, + }, + + onBeforeSerialize: function () { + NodeWrapper.prototype.onBeforeSerialize.call(this); + + this.texture = this.texture_; + this._textureObject = this.targetN.texture; + }, + + createNode: function (node) { + node = node || new cc.SpriteBatchNode(new cc.Texture2D()); + + NodeWrapper.prototype.createNode.call(this, node); + + if (this.texture) { + node.texture = cc.textureCache.addImage(this.texture); + } + else if (this._textureObject) { + node.texture = this._textureObject; + } + + return node; + } +}); + +cc.SpriteBatchNodeWrapper = module.exports = SpriteBatchNodeWrapper; diff --git a/cocos2d/ToMerge/wrappers/tiled-map.js b/cocos2d/ToMerge/wrappers/tiled-map.js new file mode 100644 index 00000000000..6a1fcf7d1f5 --- /dev/null +++ b/cocos2d/ToMerge/wrappers/tiled-map.js @@ -0,0 +1,103 @@ + +var NodeWrapper = require('./node'); + +var TiledMapWrapper = cc.Class({ + name: 'cc.TiledMapWrapper', + extends: NodeWrapper, + + properties: { + + file_: { + get: function () { + return this.targetN._file || ''; + }, + set: function (value) { + if ( !value ) { + cc.error('The new file must not be null'); + return; + } + + // first remove all layers + var layers = target.allLayers(); + layers.forEach( function (layer) { + target.removeChild(layer); + }); + + TiledMapWrapper.preloadTmx( value , function (err, textures) { + if (err) { + throw err; + return; + } + + this._textures = textures; + target._file = value; + target.initWithTMXFile( value ); + }.bind(this) ); + }, + url: cc.TiledMapAsset, + displayName: 'File' + }, + + childrenN: { + get: function () { + var children = this.targetN.children.filter( function (child) { + return !(child instanceof cc.TMXLayer); + }); + return children; + }, + }, + + _textures: { + default: [], + url: [cc.Texture2D] + }, + + file: { + default: '', + url: cc.TiledMapAsset, + visible: false + } + }, + + statics: { + preloadTmx: function (file, cb) { + cc.loader.load(file, function (err) { + if (err) { + if (cb) cb(err); + return; + } + + var mapInfo = new cc.TMXMapInfo(file); + var sets = mapInfo.getTilesets(); + + if (sets) { + var textures = sets.map(function (set) { + return set.sourceImage; + }); + + cc.loader.load(textures, function (err) { + cb(err, textures); + }); + } + else { + if (cb) cb(); + } + }); + } + }, + + onBeforeSerialize: function () { + this.file = this.file_; + }, + + createNode: function (node) { + node = node || new cc.TMXTiledMap( this.file ); + node._file = this.file; + + NodeWrapper.prototype.createNode.call(this, node); + + return node; + } +}); + +cc.TiledMapWrapper = module.exports = TiledMapWrapper; diff --git a/cocos2d/ToMerge/wrappers/ui/button.js b/cocos2d/ToMerge/wrappers/ui/button.js new file mode 100644 index 00000000000..aeb5b9f2c62 --- /dev/null +++ b/cocos2d/ToMerge/wrappers/ui/button.js @@ -0,0 +1,182 @@ +var Utils = require('../utils'); + +var Scale9Wrapper = require('./scale9'); + +var ButtonWrapper = cc.Class({ + name: 'cc.ButtonWrapper', + extends: Scale9Wrapper, + + properties: { + normalTexture: { + get: function () { + return this._normalTexture; + }, + set: function ( value ) { + this._normalTexture = value; + this.targetN.loadTextureNormal( value ); + }, + url: cc.Texture2D + }, + + pressedTexture: { + get: function () { + return this._pressedTexture; + }, + set: function ( value ) { + this._pressedTexture = value; + this.targetN.loadTexturePressed( value ); + }, + url: cc.Texture2D + }, + + disabledTexture: { + get: function () { + return this._disabledTexture; + }, + set: function ( value ) { + this._disabledTexture = value; + this.targetN.loadTextureDisabled( value ); + }, + url: cc.Texture2D + }, + + text: { + get: function () { + return this.targetN.titleText; + }, + set: function (value) { + if (typeof value === 'string') { + this.targetN.titleText = value; + } + else { + cc.error('The new text must be String'); + } + } + }, + + fontSize: { + get: function () { + return this.targetN.titleFontSize; + }, + set: function (value) { + if ( !isNaN(value) ) { + this.targetN.titleFontSize = value; + } + else { + cc.error('The new fontSize must not be NaN'); + } + } + }, + + _font: { + default: null, + type: cc.TTFFont, + visible: true, + + notify: function () { + var value = this._font; + if (!value || value instanceof cc.TTFFont) { + Utils.setFontToNode(value, this.targetN); + } + else { + cc.error('The new font must be cc.TTFFont'); + } + } + }, + + fontFamily: { + get: function () { + return this.targetN.titleFontName; + }, + set: function (value) { + if (typeof value === 'string') { + this.targetN.titleFontName = value; + } + else { + cc.error('The new fontFamily must be String'); + } + } + }, + + fontColor: { + get: function () { + var color = this.targetN.titleColor; + return color || cc.Color.WHITE; + }, + set: function (value) { + if (value instanceof cc.Color) { + this.targetN.titleColor = value; + } + else { + cc.error('The new fontColor must be cc.Color'); + } + }, + }, + + _text: { + default: 'Button' + }, + + _fontSize: { + default: 16 + }, + + _fontFamily: { + default: null + }, + + _fontColor: { + default: null + }, + + _normalTexture: { + default: '', + url: cc.Texture2D + }, + + _pressedTexture: { + default: '', + url: cc.Texture2D + }, + + _disabledTexture: { + default: '', + url: cc.Texture2D + } + }, + + onBeforeSerialize: function () { + Scale9Wrapper.prototype.onBeforeSerialize.call(this); + + this._text = this.text; + this._fontSize = this.fontSize; + this._fontFamily = this.fontFamily; + + var color = this.fontColor; + this._fontColor = [color.r, color.g, color.b, color.a]; + }, + + createNode: function (node) { + + node = node || new ccui.Button(); + node.loadTextures(this._normalTexture, this._pressedTexture, this._disabledTexture); + + node.titleText = this._text; + node.titleFontSize = this._fontSize; + node.titleFontName = this._fontFamily === null ? node.titleFontName : this._fontFamily; + + Utils.setFontToNode(this._font, node); + + var color = this._fontColor; + if (color) { + color = new cc.Color(this._fontColor[0], this._fontColor[1], this._fontColor[2], this._fontColor[3]); + node.titleColor = color; + } + + Scale9Wrapper.prototype.createNode.call(this, node); + + return node; + } +}); + +cc.ButtonWrapper = module.exports = ButtonWrapper; diff --git a/cocos2d/ToMerge/wrappers/ui/check-box.js b/cocos2d/ToMerge/wrappers/ui/check-box.js new file mode 100644 index 00000000000..8aa85f4bc07 --- /dev/null +++ b/cocos2d/ToMerge/wrappers/ui/check-box.js @@ -0,0 +1,140 @@ + +var WidgetWrapper = require('./widget'); + +var CheckBoxWrapper = cc.Class({ + name: 'cc.CheckBoxWrapper', + extends: WidgetWrapper, + + properties: { + bg_: { + get: function () { + return this.bg; + }, + set: function ( value ) { + this.bg = value; + this.targetN.loadTextureBackGround( value ); + }, + url: cc.Texture2D, + displayName: 'Bg' + }, + + bgPressed_: { + get: function () { + return this.bgPressed; + }, + set: function ( value ) { + this.bgPressed = value; + this.targetN.loadTextureBackGroundSelected( value ); + }, + url: cc.Texture2D, + displayName: 'Bg Pressed' + }, + + bgDisabled_: { + get: function () { + return this.bgDisabled; + }, + set: function ( value ) { + this.bgDisabled = value; + this.targetN.loadTextureBackGroundDisabled( value ); + }, + url: cc.Texture2D, + displayName: 'Bg Disabled' + }, + + fg_: { + get: function () { + return this.fg; + }, + set: function ( value ) { + this.fg = value; + this.targetN.loadTextureFrontCross( value ); + }, + url: cc.Texture2D, + displayName: 'Fg' + }, + + fgDisabled_: { + get: function () { + return this.fgDisabled; + }, + set: function ( value ) { + this.fgDisabled = value; + this.targetN.loadTextureFrontCrossDisabled( value ); + }, + url: cc.Texture2D, + displayName: 'Fg Disabled' + }, + + selected: { + get: function () { + return this.targetN.selected; + }, + set: function (value) { + if (typeof value === 'boolean') { + this.targetN.selected = value; + } + else { + cc.error('The new selected must be number'); + } + } + }, + + bg: { + default: '', + url: cc.Texture2D, + visible: false + }, + + bgPressed: { + default: '', + url: cc.Texture2D, + visible: false + }, + + bgDisabled: { + default: '', + url: cc.Texture2D, + visible: false + }, + + fg: { + default: '', + url: cc.Texture2D, + visible: false + }, + + fgDisabled: { + default: '', + url: cc.Texture2D, + visible: false + }, + + _selected: { + default: null + } + + }, + + onBeforeSerialize: function () { + WidgetWrapper.prototype.onBeforeSerialize.call(this); + + this._selected = this.selected; + }, + + createNode: function (node) { + + node = node || new ccui.CheckBox(); + node.loadTextures(this.bg, this.bgPressed, this.fg, this.bgDisabled, this.fgDisabled); + + if (this._selected !== null) { + node.selected = this._selected; + } + + WidgetWrapper.prototype.createNode.call(this, node); + + return node; + } +}); + +cc.CheckBoxWrapper = module.exports = CheckBoxWrapper; diff --git a/cocos2d/ToMerge/wrappers/ui/layout.js b/cocos2d/ToMerge/wrappers/ui/layout.js new file mode 100644 index 00000000000..7a7c20b2709 --- /dev/null +++ b/cocos2d/ToMerge/wrappers/ui/layout.js @@ -0,0 +1,110 @@ +var NodeWrapper = require('../node'); +var WidgetWrapper = require('./widget'); + +var LayoutWrapper = cc.Class({ + name: 'cc.LayoutWrapper', + extends: WidgetWrapper, + + properties: { + clippingEnabled: { + get: function () { + return this.targetN.clippingEnabled; + }, + set: function (value) { + if (typeof value === 'boolean') { + this.targetN.clippingEnabled = value; + + cc.renderer.childrenOrderDirty = true; + if (CC_EDITOR) { + cc.engine.repaintInEditMode(); + } + } + else { + cc.error('The new clippingEnabled must be boolean'); + } + } + }, + + layoutType: { + get: function () { + return this.targetN.layoutType; + }, + set: function (value) { + if (typeof value === 'number' && !isNaN(value)) { + this.targetN.layoutType = value; + this.updateChildLayoutType(); + + this.doLayout(); + } + else { + cc.error('The new layoutType must be number'); + } + }, + type: ccui.Layout.Type + }, + + _layoutType: { + default: 0 + }, + + _clippingEnabled: { + default: null + } + }, + + updateChildLayoutType: function () { + var layoutType = this.layoutType; + this.children.forEach( function (child) { + child.widgetLayoutType = layoutType; + }); + }, + + addChildN: function (child) { + var wrapper = cc.getWrapper(child); + wrapper.widgetLayoutType = this.layoutType; + NodeWrapper.prototype.addChildN.call(this, child); + }, + + canAddChildN: function (child) { + if ( !(child instanceof ccui.Widget) ) { + cc.error('Layout can only add ccui.Widget as a child'); + return false; + } + + return true; + }, + + onChildSiblingIndexChanged: function () { + this.doLayout(); + }, + + doLayout: function () { + this.targetN.requestDoLayout(); + cc.renderer.childrenOrderDirty = true; + if (CC_EDITOR) { + cc.engine.repaintInEditMode(); + } + }, + + onBeforeSerialize: function () { + WidgetWrapper.prototype.onBeforeSerialize.call(this); + + this._layoutType = this.layoutType; + this._clippingEnabled = this.clippingEnabled; + }, + + createNode: function (node) { + node = node || new ccui.Layout(); + + WidgetWrapper.prototype.createNode.call(this, node); + + node.layoutType = this._layoutType; + + if (this._clippingEnabled !== null) + node.clippingEnabled = this._clippingEnabled; + + return node; + } +}); + +cc.LayoutWrapper = module.exports = LayoutWrapper; diff --git a/cocos2d/ToMerge/wrappers/ui/list-view.js b/cocos2d/ToMerge/wrappers/ui/list-view.js new file mode 100644 index 00000000000..7baf3a72159 --- /dev/null +++ b/cocos2d/ToMerge/wrappers/ui/list-view.js @@ -0,0 +1,17 @@ + +var ScrollViewWrapper = require('./scroll-view'); + +var ListViewWrapper = cc.Class({ + name: 'cc.ListViewWrapper', + extends: ScrollViewWrapper, + + createNode: function (node) { + node = node || new ccui.ListView(); + + ScrollViewWrapper.prototype.createNode.call(this, node); + + return node; + } +}); + +cc.ListViewWrapper = module.exports = ListViewWrapper; diff --git a/cocos2d/ToMerge/wrappers/ui/loading-bar.js b/cocos2d/ToMerge/wrappers/ui/loading-bar.js new file mode 100644 index 00000000000..2b3293278df --- /dev/null +++ b/cocos2d/ToMerge/wrappers/ui/loading-bar.js @@ -0,0 +1,85 @@ + +var Scale9Wrapper = require('./scale9'); + +var LoadingBarWrapper = cc.Class({ + name: 'cc.LoadingBarWrapper', + extends: Scale9Wrapper, + + properties: { + texture_: { + get: function () { + return this.texture; + }, + set: function (value) { + this.texture = value; + this.targetN.loadTexture( value ); + }, + url: cc.Texture2D, + displayName: 'Texture' + }, + + direction: { + get: function () { + return this.targetN.direction; + }, + set: function (value) { + if ( typeof value === 'number' && !isNaN(value) ) { + this.targetN.direction = value; + } + else { + cc.error('The new direction must be number'); + } + }, + + type: ccui.LoadingBar.Type + }, + + percent: { + get: function () { + return this.targetN.percent; + }, + set: function (value) { + if ( typeof value === 'number' && !isNaN(value) ) { + this.targetN.percent = value; + } + else { + cc.error('The new percent must be number'); + } + } + }, + + _direction: { + default: ccui.LoadingBar.Type.LEFT + }, + + _percent: { + default: 100 + }, + + texture: { + default: '', + url: cc.Texture2D, + visible: false + } + }, + + onBeforeSerialize: function () { + Scale9Wrapper.prototype.onBeforeSerialize.call(this); + + this._direction = this.direction; + this._percent = this.percent; + }, + + createNode: function (node) { + node = node || new ccui.LoadingBar(); + node.loadTexture(this.texture); + node.percent = this._percent; + node.direction = this._direction; + + Scale9Wrapper.prototype.createNode.call(this, node); + + return node; + } +}); + +cc.LoadingBarWrapper = module.exports = LoadingBarWrapper; diff --git a/cocos2d/ToMerge/wrappers/ui/page-view.js b/cocos2d/ToMerge/wrappers/ui/page-view.js new file mode 100644 index 00000000000..0e7dfe5d87e --- /dev/null +++ b/cocos2d/ToMerge/wrappers/ui/page-view.js @@ -0,0 +1,44 @@ + +var LayoutWrapper = require('./layout'); + +var PageViewWrapper = cc.Class({ + name: 'cc.PageViewWrapper', + extends: LayoutWrapper, + + onChildSiblingIndexChanged: function () { + var pages = this.targetN._pages; + pages.sort(function (a, b) { + return a.getOrderOfArrival() > b.getOrderOfArrival() ? 1 : -1; + }); + + LayoutWrapper.prototype.onChildSiblingIndexChanged.call(this); + }, + + addChildN: function (child) { + if (child instanceof ccui.Layout) { + this.targetN.addPage(child); + } + else { + this.targetN.addChild(child); + } + }, + + removeChildN: function (child) { + if (child instanceof ccui.Layout) { + this.targetN.removePage(child); + } + else { + this.targetN.removeChild(child); + } + }, + + createNode: function (node) { + node = node || new ccui.PageView(); + + LayoutWrapper.prototype.createNode.call(this, node); + + return node; + } +}); + +cc.PageViewWrapper = module.exports = PageViewWrapper; diff --git a/cocos2d/ToMerge/wrappers/ui/scale9-sprite.js b/cocos2d/ToMerge/wrappers/ui/scale9-sprite.js new file mode 100644 index 00000000000..b412c00a016 --- /dev/null +++ b/cocos2d/ToMerge/wrappers/ui/scale9-sprite.js @@ -0,0 +1,58 @@ + +var Scale9Wrapper = require('./scale9'); + +var Scale9SpriteWrapper = cc.Class({ + name: 'cc.Scale9SpriteWrapper', + extends: Scale9Wrapper, + + properties: { + + childrenN: { + get: function () { + var renderer = this.targetN.getVirtualRenderer(); + var children = this.targetN.children.filter( function (child) { + return child !== renderer; + }); + return children; + }, + }, + + texture_: { + get: function () { + return this.texture; + }, + set: function (value) { + this.texture = value; + this.targetN.loadTexture( value ); + }, + url: cc.Texture2D + }, + + texture: { + default: '', + url: cc.Texture2D, + visible: false + } + }, + + createNode: function (node) { + node = node || new ccui.ImageView(this.texture); + + Scale9Wrapper.prototype.createNode.call(this, node); + + return node; + } +}); + +// ccui.ImageView not implement getRotation +// we implement here +if (!cc.sys.isNative) { + var _p = ccui.ImageView.prototype; + _p.getRotation = function() { + return this._imageRenderer.getRotation(); + }; + + cc.js.getset(_p, 'rotation', _p.getRotation, _p.setRotation); +} + +cc.Scale9SpriteWrapper = module.exports = Scale9SpriteWrapper; diff --git a/cocos2d/ToMerge/wrappers/ui/scale9.js b/cocos2d/ToMerge/wrappers/ui/scale9.js new file mode 100644 index 00000000000..aee1ca6f30e --- /dev/null +++ b/cocos2d/ToMerge/wrappers/ui/scale9.js @@ -0,0 +1,171 @@ + +var WidgetWrapper = require('./widget'); + +var Scale9Wrapper = cc.Class({ + name: 'cc.Scale9Wrapper', + extends: WidgetWrapper, + + ctor: function () { + this._scale9Size = null; + this._updatingCapInsets = false; + }, + + properties: { + enableScale9: { + get: function () { + return this.targetN.isScale9Enabled(); + }, + set: function (value) { + if (typeof value === 'boolean') { + if (!value) this._scale9Size = this.size; + + this.targetN.setScale9Enabled( value ); + + if (value && this._scale9Size) this.size = this._scale9Size; + } + else { + cc.error('The new enableScale9 must be boolean'); + } + } + }, + + left: { + default: 0, + range: [0, Number.MAX_SAFE_INTEGER], + + notify: function () { + this._updateCapInsets('left'); + } + }, + + right: { + default: 0, + range: [0, Number.MAX_SAFE_INTEGER], + + notify: function () { + this._updateCapInsets('right'); + } + }, + + top: { + default: 0, + range: [0, Number.MAX_SAFE_INTEGER], + + notify: function () { + this._updateCapInsets('top'); + } + }, + + bottom: { + default: 0, + range: [0, Number.MAX_SAFE_INTEGER], + + notify: function () { + this._updateCapInsets('bottom'); + } + }, + + _enableScale9: { + default: false + }, + + _capInsets: { + default: null + } + }, + + getVirtualRendererSize: function () { + var renderer = this.targetN.getVirtualRenderer(); + return renderer ? renderer._spriteRect : cc.size(0,0); + }, + + _updateCapInsets: function (type) { + if (!this.targetN || this._updatingCapInsets) return; + this._updatingCapInsets = true; + + var size = this.getVirtualRendererSize(); + + var w = size.width - this.left - this.right; + var h = size.height - this.top - this.bottom; + + // width and height should be greater than 0 + if (type === 'left') { + if (this.left > size.width-1) { + this.left = size.width-1; + this.right = 0; + w = 1; + } + else if (w <= 0) { + this.right += w; + w = 1; + } + } + else if (type === 'right') { + if (this.right > size.width-1) { + this.right = size.width-1; + this.left = 0; + w = 1; + } + else if (w <= 0) { + this.left += w; + w = 1; + } + } + else if (type === 'top') { + if (this.top > size.height-1) { + this.top = size.height-1; + this.bottom = 0; + h = 1; + } + else if (h <= 0) { + this.bottom += h; + h = 1; + } + } + else if (type === 'bottom') { + if (this.bottom > size.height-1) { + this.bottom = size.height-1; + this.top = 0; + h = 1; + } + else if (h <= 0) { + this.top += h; + h = 1; + } + } + + var x = this.left; + var y = this.top; + + this.targetN.setCapInsets( cc.rect(x, y, w, h) ); + this._capInsets = [x, y, w, h]; + + this._updatingCapInsets = false; + }, + + onBeforeSerialize: function () { + WidgetWrapper.prototype.onBeforeSerialize.call(this); + + this._enableScale9 = this.enableScale9; + }, + + createNode: function (node) { + if (!node) { + cc.error('Can\'t create a node from Scale9Wrapper'); + return; + } + + node.setScale9Enabled( this._enableScale9 ); + + var capInsets = this._capInsets; + if ( capInsets && this._enableScale9 ) { + node.setCapInsets( cc.rect(capInsets[0], capInsets[1], capInsets[2], capInsets[3]) ); + } + + WidgetWrapper.prototype.createNode.call(this, node); + + return node; + } +}); + +module.exports = Scale9Wrapper; diff --git a/cocos2d/ToMerge/wrappers/ui/scroll-view.js b/cocos2d/ToMerge/wrappers/ui/scroll-view.js new file mode 100644 index 00000000000..b89fb8d24eb --- /dev/null +++ b/cocos2d/ToMerge/wrappers/ui/scroll-view.js @@ -0,0 +1,99 @@ + +var LayoutWrapper = require('./layout'); + +var ScrollViewWrapper = cc.Class({ + name: 'cc.ScrollViewWrapper', + extends: LayoutWrapper, + + properties: { + direction: { + get: function () { + return this.targetN.getDirection(); + }, + set: function (value) { + if (typeof value === 'number' && !isNaN(value)) { + this.targetN.setDirection(value); + + this.doLayout(); + } + else { + cc.error('The new direction must be number'); + } + }, + type: ccui.ScrollView.Dir + }, + + bounce: { + get: function () { + return this.targetN.isBounceEnabled(); + }, + set: function (value) { + if (typeof value === 'boolean') { + this.targetN.setBounceEnabled(value); + } + else { + cc.error('The new bounce must be boolean'); + } + } + }, + + innerSize: { + get: function () { + var size = this.targetN.getInnerContainerSize(); + return new cc.Vec2(size.width, size.height); + }, + set: function (value) { + if ( value instanceof cc.Vec2 ) { + this.targetN.setInnerContainerSize( cc.size(value.x, value.y) ); + + this.doLayout(); + } + else { + cc.error('The new innerSize must be cc.Vec2'); + } + } + }, + + _direction: { + default: ccui.ScrollView.Dir.NONE, + type: ccui.ScrollView.Dir + }, + + _bounce: { + default: false + }, + + _innerSize: { + default: null + } + }, + + doLayout: function () { + this.targetN._innerContainer.requestDoLayout(); + LayoutWrapper.prototype.doLayout.call(this); + }, + + onBeforeSerialize: function () { + LayoutWrapper.prototype.onBeforeSerialize.call(this); + + this._direction = this.direction; + this._bounce = this.bounce; + this._innerSize = [this.innerSize.x, this.innerSize.y]; + }, + + createNode: function (node) { + node = node || new ccui.ScrollView(); + + LayoutWrapper.prototype.createNode.call(this, node); + + node.setDirection( this._direction ); + node.setBounceEnabled( this._bounce ); + + var innerSize = this._innerSize; + if (innerSize) node.setInnerContainerSize( cc.size(innerSize[0], innerSize[1]) ); + + return node; + } +}); + +cc.ScrollViewWrapper = module.exports = ScrollViewWrapper; diff --git a/cocos2d/ToMerge/wrappers/ui/slider.js b/cocos2d/ToMerge/wrappers/ui/slider.js new file mode 100644 index 00000000000..48a25d9fc00 --- /dev/null +++ b/cocos2d/ToMerge/wrappers/ui/slider.js @@ -0,0 +1,139 @@ + +var Scale9Wrapper = require('./scale9'); + +var SliderWrapper = cc.Class({ + name: 'cc.SliderWrapper', + extends: Scale9Wrapper, + + properties: { + bgBar_: { + get: function () { + return this.bgBar; + }, + set: function (value) { + this.bgBar = value; + this.targetN.loadBarTexture( value ); + }, + url: cc.Texture2D, + displayName: 'Bg Bar' + }, + + fgBar_: { + get: function () { + return this.fgBar; + }, + set: function (value) { + this.fgBar = value; + this.targetN.loadProgressBarTexture( value ); + }, + url: cc.Texture2D, + displayName: 'Fg Bar' + }, + + control_: { + get: function () { + return this.control; + }, + set: function (value) { + this.control = value; + this.targetN.loadSlidBallTextureNormal( value ); + }, + url: cc.Texture2D, + displayName: 'Control' + }, + + controlPressed_: { + get: function () { + return this.controlPressed; + }, + set: function (value) { + this.controlPressed = value; + this.targetN.loadSlidBallTexturePressed( value ); + }, + url: cc.Texture2D, + displayName: 'Control Pressed' + }, + + controlDisabled_: { + get: function () { + return this.controlDisabled; + }, + set: function (value) { + this.controlDisabled = value; + this.targetN.loadSlidBallTextureDisabled( value ); + }, + url: cc.Texture2D, + displayName: 'Control Disabled' + }, + + percent: { + get: function () { + return this.targetN.percent; + }, + set: function (value) { + if ( typeof value === 'number' && !isNaN(value) ) { + this.targetN.percent = value; + } + else { + cc.error('The new percent must be number'); + } + } + }, + + _percent: { + default: 0 + }, + + bgBar: { + default: '', + url: cc.Texture2D, + visible: false + }, + + fgBar: { + default: '', + url: cc.Texture2D, + visible: false + }, + + control: { + default: '', + url: cc.Texture2D, + visible: false + }, + + controlPressed: { + default: '', + url: cc.Texture2D, + visible: false + }, + + controlDisabled: { + default: '', + url: cc.Texture2D, + visible: false + }, + }, + + onBeforeSerialize: function () { + Scale9Wrapper.prototype.onBeforeSerialize.call(this); + + this._percent = this.percent; + }, + + createNode: function (node) { + node = node || new ccui.Slider(); + + node.loadBarTexture( this.bgBar ); + node.loadProgressBarTexture( this.fgBar ); + node.loadSlidBallTextures( this.control, this.controlPressed, this.controlDisabled ); + + node.percent = this._percent; + + Scale9Wrapper.prototype.createNode.call(this, node); + + return node; + } +}); + +cc.SliderWrapper = module.exports = SliderWrapper; diff --git a/cocos2d/ToMerge/wrappers/ui/text-atlas.js b/cocos2d/ToMerge/wrappers/ui/text-atlas.js new file mode 100644 index 00000000000..4d451e80fd7 --- /dev/null +++ b/cocos2d/ToMerge/wrappers/ui/text-atlas.js @@ -0,0 +1,79 @@ + +var WidgetWrapper = require('./widget'); + +var TextAtlasWrapper = cc.Class({ + name: 'cc.TextAtlasWrapper', + extends: WidgetWrapper, + + properties: { + text: { + get: function () { + return this.targetN.string; + }, + set: function (value) { + if (typeof value === 'string') { + this.targetN.string = value; + } + else { + cc.error('The new text must be String'); + } + } + }, + + texture: { + default: '', + url: cc.Texture2D, + + notify: function () { + this.updateProperties(); + } + }, + + itemWidth: { + default: 0, + notify: function () { + this.updateProperties(); + } + }, + + itemHeight: { + default: 0, + notify: function () { + this.updateProperties(); + } + }, + + startCharMap: { + default: '0', + notify: function () { + this.updateProperties(); + } + }, + + _text: { + default: 'Label' + }, + }, + + updateProperties: function () { + if (!this.targetN) return; + this.targetN.setProperty(this.text, this.texture, this.itemWidth, this.itemHeight, this.startCharMap); + }, + + onBeforeSerialize: function () { + WidgetWrapper.prototype.onBeforeSerialize.call(this); + + this._text = this.text; + }, + + createNode: function (node) { + node = node || new ccui.TextAtlas(); + node.setProperty(this._text, this.texture, this.itemWidth, this.itemHeight, this.startCharMap); + + WidgetWrapper.prototype.createNode.call(this, node); + + return node; + } +}); + +cc.TextAtlasWrapper = module.exports = TextAtlasWrapper; diff --git a/cocos2d/ToMerge/wrappers/ui/text-bitmap-font.js b/cocos2d/ToMerge/wrappers/ui/text-bitmap-font.js new file mode 100644 index 00000000000..937003c913b --- /dev/null +++ b/cocos2d/ToMerge/wrappers/ui/text-bitmap-font.js @@ -0,0 +1,70 @@ + +var WidgetWrapper = require('./widget'); + +var TextBMFontWrapper = cc.Class({ + name: 'cc.TextBMFontWrapper', + extends: WidgetWrapper, + + properties: { + text: { + get: function () { + return this.targetN.string; + }, + set: function (value) { + if (typeof value === 'string') { + this.targetN.string = value; + } + else { + cc.error('The new text must be String'); + } + } + }, + + bitmapFont_: { + get: function () { + return this.targetN._file || ''; + }, + set: function (value) { + cc.loader.load(value, function () { + this.targetN.setFntFile(value); + }.bind(this) ); + }, + url: cc.BitmapFont + }, + + _text: { + default: 'TextBMFont' + }, + + bitmapFont: { + default: '', + url: cc.BitmapFont, + visible: false + } + }, + + onBeforeSerialize: function () { + WidgetWrapper.prototype.onBeforeSerialize.call(this); + + this._text = this.text; + this.bitmapFont = this.bitmapFont_; + }, + + createNode: function (node) { + node = node || new ccui.TextBMFont(); + node.string = this._text; + node.setFntFile(this.bitmapFont); + + WidgetWrapper.prototype.createNode.call(this, node); + + return node; + } +}); + +var originSetFntFile = ccui.TextBMFont.prototype.setFntFile; +ccui.TextBMFont.prototype.setFntFile = function (value) { + this._file = value; + originSetFntFile.call(this, value); +}; + +cc.TextBMFontWrapper = module.exports = TextBMFontWrapper; diff --git a/cocos2d/ToMerge/wrappers/ui/text-field.js b/cocos2d/ToMerge/wrappers/ui/text-field.js new file mode 100644 index 00000000000..f2378735360 --- /dev/null +++ b/cocos2d/ToMerge/wrappers/ui/text-field.js @@ -0,0 +1,214 @@ +var Utils = require('../utils'); + +var WidgetWrapper = require('./widget'); + +var TextFieldWrapper = cc.Class({ + name: 'cc.TextFieldWrapper', + extends: WidgetWrapper, + + properties: { + text: { + get: function () { + return this.targetN.string; + }, + set: function (value) { + if (typeof value === 'string') { + this.targetN.string = value; + + this.setDirtyFlag(); + } + else { + cc.error('The new text must be String'); + } + } + }, + + placeHolder: { + get: function () { + return this.targetN.placeHolder; + }, + set: function (value) { + if (typeof value === 'string') { + this.targetN.placeHolder = value; + + this.setDirtyFlag(); + } + else { + cc.error('The new placeHolder must be String'); + } + } + }, + + fontSize: { + get: function () { + return this.targetN.fontSize; + }, + set: function (value) { + if ( !isNaN(value) ) { + this.targetN.fontSize = value; + + this.setDirtyFlag(); + } + else { + cc.error('The new fontSize must not be NaN'); + } + } + }, + + font: { + default: null, + type: cc.TTFFont, + + notify: function () { + if ( !this.targetN ) return; + + var value = this.font; + if (!value || value instanceof cc.TTFFont) { + Utils.setFontToNode(value, this.targetN); + + this.setDirtyFlag(); + } + else { + cc.error('The new font must be cc.TTFFont'); + } + } + }, + + fontFamily: { + get: function () { + return this.targetN.fontName; + }, + set: function (value) { + if (typeof value === 'string') { + this.targetN.fontName = value; + + this.setDirtyFlag(); + } + else { + cc.error('The new fontFamily must be String'); + } + } + }, + + maxLengthEnabled: { + get: function () { + return this.targetN.maxLengthEnabled; + }, + set: function (value) { + if (typeof value === 'boolean') { + this.targetN.maxLengthEnabled = value; + + this.setDirtyFlag(); + } + else { + cc.error('The new maxLengthEnabled must be boolean'); + } + } + }, + + maxLength: { + get: function () { + return this.targetN.maxLength; + }, + set: function (value) { + if (typeof value === 'number') { + this.targetN.maxLength = value; + + this.setDirtyFlag(); + } + else { + cc.error('The new maxLength must be String'); + } + } + }, + + passwordEnabled: { + get: function () { + return this.targetN.passwordEnabled; + }, + set: function (value) { + if (typeof value === 'boolean') { + var targetN = this.targetN; + targetN.passwordEnabled = value; + + this.text = this.text; + } + else { + cc.error('The new passwordEnabled must be boolean'); + } + } + }, + + _text: { + default: '' + }, + + _placeHolder: { + default: 'Input Something' + }, + + _fontSize: { + default: 16 + }, + + _fontFamily: { + default: null + }, + + _maxLengthEnabled: { + default: false + }, + + _maxLength: { + default: 0 + }, + + _passwordEnabled: { + default: false + } + }, + + onSizeChanged: function () { + WidgetWrapper.prototype.onSizeChanged.call(this); + this.setDirtyFlag(); + }, + + setDirtyFlag: function () { + cc.renderer.childrenOrderDirty = true; + }, + + onBeforeSerialize: function () { + WidgetWrapper.prototype.onBeforeSerialize.call(this); + + this._text = this.text; + this._placeHolder = this.placeHolder; + this._fontSize = this.fontSize; + this._fontFamily = this.fontFamily; + this._maxLengthEnabled = this.maxLengthEnabled; + this._passwordEnabled = this.passwordEnabled; + + if (this.maxLengthEnabled) this._maxLength = this.maxLength; + }, + + createNode: function (node) { + node = node || new ccui.TextField(); + node.ignoreContentAdaptWithSize(false); + + WidgetWrapper.prototype.createNode.call(this, node); + + node.passwordEnabled = this._passwordEnabled; + node.string = this._text; + node.placeHolder = this._placeHolder; + node.fontSize = this._fontSize; + node.fontFamily = this._fontFamily; + node.maxLengthEnabled = this._maxLengthEnabled; + + if (this._maxLengthEnabled) node.maxLength = this._maxLength; + + Utils.setFontToNode(this.font, node); + + return node; + } +}); + +cc.TextFieldWrapper = module.exports = TextFieldWrapper; diff --git a/cocos2d/ToMerge/wrappers/ui/text.js b/cocos2d/ToMerge/wrappers/ui/text.js new file mode 100644 index 00000000000..ecc99cf366b --- /dev/null +++ b/cocos2d/ToMerge/wrappers/ui/text.js @@ -0,0 +1,178 @@ +var Utils = require('../utils'); + +var WidgetWrapper = require('./widget'); + +var TextWrapper = cc.Class({ + name: 'cc.TextWrapper', + extends: WidgetWrapper, + + ctor: function () { + this._boundingBox = [100, 100] + }, + + properties: { + text: { + get: function () { + return this.targetN.string; + }, + set: function (value) { + if (typeof value === 'string') { + this.targetN.string = value; + } + else { + cc.error('The new text must be String'); + } + } + }, + + fontSize: { + get: function () { + return this.targetN.fontSize; + }, + set: function (value) { + if ( !isNaN(value) ) { + this.targetN.fontSize = value; + } + else { + cc.error('The new fontSize must not be NaN'); + } + } + }, + + font: { + default: null, + type: cc.TTFFont, + + notify: function () { + if ( !this.targetN ) return; + + var value = this.font; + if (!value || value instanceof cc.TTFFont) { + Utils.setFontToNode(value, this.targetN); + } + else { + cc.error('The new font must be cc.TTFFont'); + } + } + }, + + fontFamily: { + get: function () { + return this.targetN.fontName; + }, + set: function (value) { + if (typeof value === 'string') { + this.targetN.fontName = value; + } + else { + cc.error('The new fontFamily must be String'); + } + } + }, + + align: { + get: function () { + return this.targetN.getTextHorizontalAlignment(); + }, + set: function ( value ) { + if (typeof value === 'number') { + this.targetN.textAlign = value; + } + else { + cc.error('The new textAlign must be number'); + } + }, + type: cc.TextAlignment + }, + + verticalAlign: { + get: function () { + return this.targetN.getTextVerticalAlignment(); + }, + set: function ( value ) { + if (typeof value === 'number') { + this.targetN.verticalAlign = value; + } + else { + cc.error('The new verticalAlign must be number'); + } + }, + type: cc.VerticalTextAlignment + }, + + boundingBox: { + get: function () { + var size = this.targetN.getTextAreaSize(); + return new cc.Vec2(size.width, size.height); + }, + set: function (value) { + if (value instanceof cc.Vec2) { + this.targetN.setTextAreaSize( cc.size( value.x, value.y ) ); + } + else { + cc.error('The new boundingBox must be Vec2'); + } + }, + type: cc.Vec2 + }, + + _text: { + default: 'Label' + }, + + _fontSize: { + default: 16 + }, + + _fontFamily: { + default: null + }, + + _align: { + default: cc.TextAlignment.CENTER + }, + + _verticalAlign: { + default: cc.VerticalTextAlignment.CENTER + }, + + _boundingBox: { + default: null + } + }, + + onBeforeSerialize: function () { + WidgetWrapper.prototype.onBeforeSerialize.call(this); + + this._text = this.text; + this._fontSize = this.fontSize; + this._fontFamily = this.fontFamily; + this._align = this.align; + this._verticalAlign = this.verticalAlign; + this._boundingBox = [this.boundingBox.x, this.boundingBox.y]; + }, + + createNode: function (node) { + node = node || new ccui.Text(); + node.ignoreContentAdaptWithSize(false); + + WidgetWrapper.prototype.createNode.call(this, node); + + node.string = this._text; + node.fontSize = this._fontSize; + node.fontName = this._fontFamily === null ? node.fontName : this._fontFamily; + node.textAlign = this._align; + node.verticalAlign = this._verticalAlign; + + var boundingBox = this._boundingBox; + if (boundingBox) { + node.setTextAreaSize( cc.size( boundingBox[0], boundingBox[1] ) ); + } + + Utils.setFontToNode(this.font, node); + + return node; + } +}); + +cc.TextWrapper = module.exports = TextWrapper; diff --git a/cocos2d/ToMerge/wrappers/ui/widget.js b/cocos2d/ToMerge/wrappers/ui/widget.js new file mode 100644 index 00000000000..b5899450108 --- /dev/null +++ b/cocos2d/ToMerge/wrappers/ui/widget.js @@ -0,0 +1,245 @@ + +var NodeWrapper = require('../node'); + +var desc = Object.getOwnPropertyDescriptor(NodeWrapper.prototype, 'parentN'); + +var WidgetWrapper = cc.Class({ + name: 'cc.WidgetWrapper', + extends: NodeWrapper, + + ctor: function () { + this._ignoreUpdateLayoutParameter = false; + }, + + properties: { + + position: { + get: function () { + return new cc.Vec2(this.targetN.x, this.targetN.y); + }, + set: function (value) { + if ( value instanceof cc.Vec2 ) { + this.targetN.setPosition(value.x, value.y); + + if (this.canDoAnchor) { + this.updateAnchorPositon(); + } + } + else { + cc.error('The new position must be cc.Vec2'); + } + } + }, + + parentN: { + get: function () { + var parent = this.targetN.parent; + + if (parent) { + var ancient = parent.parent; + if ( ancient && ancient instanceof ccui.ScrollView) { + return ancient; + } + } + + return parent; + }, + set: desc.set + }, + + enabled: { + get: function () { + return this.targetN.enabled; + }, + set: function (value) { + if (typeof value === 'boolean') { + this.targetN.enabled = value; + this.targetN.bright = value; + } + else { + cc.error('The new enabled must be boolean'); + } + } + }, + + touchEnabled: { + get: function () { + return this.targetN.touchEnabled; + }, + set: function (value) { + if (typeof value === 'boolean') { + this.targetN.touchEnabled = value; + } + else { + cc.error('The new touchEnabled must be boolean'); + } + } + }, + + canDoAnchor: { + get: function () { + var parentN = this.parentN; + return parentN && parentN instanceof ccui.Layout; + } + }, + + anchorAlign: { + default: ccui.RelativeLayoutParameter.Type.PARENT_TOP_LEFT, + type: ccui.RelativeLayoutParameter.Type, + + notify: function () { + this.updateAnchorPositon(); + } + }, + + widgetLayoutType: { + default: ccui.Layout.Type.RELATIVE, + type: ccui.Layout.Type, + + notify: function (oldValue) { + if (oldValue === this.widgetLayoutType) return; + + this._ignoreUpdateLayoutParameter = true; + this.anchorLeft = this.anchorTop = this.anchorRight = this.anchorBottom = 0; + this._ignoreUpdateLayoutParameter = false; + + this.updateLayoutParameter(); + } + }, + + anchorLeft: { + default: 0, + + notify: function () { + this.updateLayoutParameter(); + } + }, + + anchorRight: { + default: 0, + + notify: function () { + this.updateLayoutParameter(); + } + }, + + anchorTop: { + default: 0, + + notify: function () { + this.updateLayoutParameter(); + } + }, + + anchorBottom: { + default: 0, + + notify: function () { + this.updateLayoutParameter(); + } + }, + + _enabled: { + default: null + }, + + _touchEnabled: { + default: null + }, + }, + + updateLayoutParameter: function (node) { + if (this._ignoreUpdateLayoutParameter) return; + + node = node || this.targetN; + + var parameter; + + if (this.widgetLayoutType === ccui.Layout.Type.RELATIVE) { + parameter = new ccui.RelativeLayoutParameter(); + parameter.setAlign(this.anchorAlign); + } + else { + parameter = new ccui.LinearLayoutParameter(); + } + parameter.setMargin(this.anchorLeft, this.anchorTop, this.anchorRight, this.anchorBottom); + + node.setLayoutParameter(parameter); + + if (this.targetN) { + this.doParentLayout(); + } + }, + + updateAnchorPositon: function () { + this._ignoreUpdateLayoutParameter = true; + + var targetN = this.targetN; + var cs = targetN.getContentSize(); + var ap = targetN.getAnchorPointInPoints(); + var ls = targetN.parent._getLayoutContentSize(); + + this.anchorLeft = targetN.x - ap.x; + this.anchorRight = ls.width - targetN.x - (cs.width - ap.x); + this.anchorTop = ls.height - targetN.y - (cs.height - ap.y); + this.anchorBottom = targetN.y - ap.y; + + this._ignoreUpdateLayoutParameter = false; + this.updateLayoutParameter(); + }, + + onSizeChanged: function () { + this.doParentLayout(); + }, + + _getUrlFromRenderer: function (renderer) { + if (!renderer) return ''; + + var texture; + if ( renderer.texture ) texture = renderer.texture; + if ( renderer._scale9Image ) texture = renderer._scale9Image.texture; + + return texture ? texture.url : ''; + }, + + doParentLayout: function () { + var parent = this.parent; + while (parent) { + if (parent instanceof cc.LayoutWrapper) { + parent.doLayout(); + return; + } + parent = parent.parent; + } + }, + + onBeforeSerialize: function () { + NodeWrapper.prototype.onBeforeSerialize.call(this); + + this._enabled = this.enabled; + this._touchEnabled = this.touchEnabled; + }, + + createNode: function (node) { + if (!node) { + cc.error('Can\'t create a node from WidgetWrapper'); + return; + } + + NodeWrapper.prototype.createNode.call(this, node); + + this.updateLayoutParameter(node); + + if (this._enabled !== null) { + node.enabled = this._enabled; + node.bright = this._enabled; + } + + if (this._touchEnabled !== null) + node.setTouchEnabled( this._touchEnabled ); + + return node; + } +}); + +module.exports = WidgetWrapper; diff --git a/cocos2d/ToMerge/wrappers/utils.js b/cocos2d/ToMerge/wrappers/utils.js new file mode 100644 index 00000000000..8854e26ae5f --- /dev/null +++ b/cocos2d/ToMerge/wrappers/utils.js @@ -0,0 +1,16 @@ + +module.exports = { + setFontToNode: function(fontAsset, node) { + if (fontAsset) { + var config = {type:'font', name: fontAsset.fontFamily, srcs:[fontAsset.url]}; + cc.loader.load(config, function (err, results) { + if (err) throw err; + + node.fontName = config.name; + }); + } + else { + node.fontName = 'Arial'; + } + } +}; diff --git a/cocos2d/actions/CCAction.js b/cocos2d/actions/CCAction.js new file mode 100644 index 00000000000..0f63364c2fd --- /dev/null +++ b/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 Bindigns (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 Bindigns (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 {Null} + */ + 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/cocos2d/actions/CCActionCamera.js b/cocos2d/actions/CCActionCamera.js new file mode 100644 index 00000000000..0ff5786b717 --- /dev/null +++ b/cocos2d/actions/CCActionCamera.js @@ -0,0 +1,293 @@ +/**************************************************************************** + 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 cc.Camera actions + * @class + * @extends cc.ActionInterval + */ +cc.ActionCamera = cc.ActionInterval.extend(/** @lends cc.ActionCamera# */{ + _centerXOrig:0, + _centerYOrig:0, + _centerZOrig:0, + _eyeXOrig:0, + _eyeYOrig:0, + _eyeZOrig:0, + _upXOrig:0, + _upYOrig:0, + _upZOrig:0, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. + */ + ctor:function(){ + var _t = this; + cc.ActionInterval.prototype.ctor.call(_t); + + _t._centerXOrig=0; + _t._centerYOrig=0; + _t._centerZOrig=0; + _t._eyeXOrig=0; + _t._eyeYOrig=0; + _t._eyeZOrig=0; + _t._upXOrig=0; + _t._upYOrig=0; + _t._upZOrig=0; + }, + + /** + * called before the action start. It will also set the target. + * + * @param {cc.Node} target + */ + startWithTarget:function (target) { + var _t = this; + cc.ActionInterval.prototype.startWithTarget.call(_t, target); + + var camera = target.getCamera(); + var centerXYZ = camera.getCenter(); + _t._centerXOrig = centerXYZ.x; + _t._centerYOrig = centerXYZ.y; + _t._centerZOrig = centerXYZ.z; + + var eyeXYZ = camera.getEye(); + _t._eyeXOrig = eyeXYZ.x; + _t._eyeYOrig = eyeXYZ.y; + _t._eyeZOrig = eyeXYZ.z; + + var upXYZ = camera.getUp(); + _t._upXOrig = upXYZ.x; + _t._upYOrig = upXYZ.y; + _t._upZOrig = upXYZ.z; + }, + + /** + * to copy object with deep copy. + * returns a new clone of the action + * + * @returns {cc.ActionCamera} + */ + clone:function(){ + return new cc.ActionCamera(); + }, + + /** + * 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 + * + */ + reverse:function () { + return new cc.ReverseTime(this); + } +}); + +/** + * Orbits the camera around the center of the screen using spherical coordinates. + * + * @param {Number} t time + * @param {Number} radius + * @param {Number} deltaRadius + * @param {Number} angleZ + * @param {Number} deltaAngleZ + * @param {Number} angleX + * @param {Number} deltaAngleX + * + * @class + * @extends cc.ActionCamera + */ +cc.OrbitCamera = cc.ActionCamera.extend(/** @lends cc.OrbitCamera# */{ + _radius: 0.0, + _deltaRadius: 0.0, + _angleZ: 0.0, + _deltaAngleZ: 0.0, + _angleX: 0.0, + _deltaAngleX: 0.0, + _radZ: 0.0, + _radDeltaZ: 0.0, + _radX: 0.0, + _radDeltaX: 0.0, + + /** + * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
+ * creates a cc.OrbitCamera action with radius, delta-radius, z, deltaZ, x, deltaX. + * @param {Number} t time + * @param {Number} radius + * @param {Number} deltaRadius + * @param {Number} angleZ + * @param {Number} deltaAngleZ + * @param {Number} angleX + * @param {Number} deltaAngleX + */ + ctor:function(t, radius, deltaRadius, angleZ, deltaAngleZ, angleX, deltaAngleX){ + cc.ActionCamera.prototype.ctor.call(this); + + deltaAngleX !== undefined && this.initWithDuration(t, radius, deltaRadius, angleZ, deltaAngleZ, angleX, deltaAngleX); + }, + + /** + * initializes a cc.OrbitCamera action with radius, delta-radius, z, deltaZ, x, deltaX + * @param {Number} t time + * @param {Number} radius + * @param {Number} deltaRadius + * @param {Number} angleZ + * @param {Number} deltaAngleZ + * @param {Number} angleX + * @param {Number} deltaAngleX + * @return {Boolean} + */ + initWithDuration:function (t, radius, deltaRadius, angleZ, deltaAngleZ, angleX, deltaAngleX) { + if (cc.ActionInterval.prototype.initWithDuration.call(this, t)) { + var _t = this; + _t._radius = radius; + _t._deltaRadius = deltaRadius; + _t._angleZ = angleZ; + _t._deltaAngleZ = deltaAngleZ; + _t._angleX = angleX; + _t._deltaAngleX = deltaAngleX; + + _t._radDeltaZ = cc.degreesToRadians(deltaAngleZ); + _t._radDeltaX = cc.degreesToRadians(deltaAngleX); + return true; + } + return false; + }, + + /** + * positions the camera according to spherical coordinates + * @return {Object} + */ + sphericalRadius:function () { + var newRadius, zenith, azimuth; + var camera = this.target.getCamera(); + var eyeXYZ = camera.getEye(); + var centerXYZ = camera.getCenter(); + + var x = eyeXYZ.x - centerXYZ.x, y = eyeXYZ.y - centerXYZ.y, z = eyeXYZ.z - centerXYZ.z; + + var r = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2)); + var s = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); + if (s === 0.0) + s = cc.FLT_EPSILON; + if (r === 0.0) + r = cc.FLT_EPSILON; + + zenith = Math.acos(z / r); + if (x < 0) + azimuth = Math.PI - Math.asin(y / s); + else + azimuth = Math.asin(y / s); + newRadius = r / cc.Camera.getZEye(); + return {newRadius:newRadius, zenith:zenith, azimuth:azimuth}; + }, + + /** + * called before the action start. It will also set the target. + * + * @param {cc.Node} target + */ + startWithTarget:function (target) { + var _t = this; + cc.ActionInterval.prototype.startWithTarget.call(_t, target); + var retValue = _t.sphericalRadius(); + if (isNaN(_t._radius)) + _t._radius = retValue.newRadius; + + if (isNaN(_t._angleZ)) + _t._angleZ = cc.radiansToDegrees(retValue.zenith); + + if (isNaN(_t._angleX)) + _t._angleX = cc.radiansToDegrees(retValue.azimuth); + + _t._radZ = cc.degreesToRadians(_t._angleZ); + _t._radX = cc.degreesToRadians(_t._angleX); + }, + + /** + * to copy object with deep copy. + * returns a new clone of the action + * + * @returns {cc.ActionCamera} + */ + clone:function(){ + var a = new cc.OrbitCamera(), _t = this; + a.initWithDuration(_t._duration, _t._radius, _t._deltaRadius, _t._angleZ, _t._deltaAngleZ, _t._angleX, _t._deltaAngleX); + return a; + }, + + /** + * 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 r = (this._radius + this._deltaRadius * dt) * cc.Camera.getZEye(); + var za = this._radZ + this._radDeltaZ * dt; + var xa = this._radX + this._radDeltaX * dt; + + var i = Math.sin(za) * Math.cos(xa) * r + this._centerXOrig; + var j = Math.sin(za) * Math.sin(xa) * r + this._centerYOrig; + var k = Math.cos(za) * r + this._centerZOrig; + + this.target.getCamera().setEye(i, j, k); + this.target.setNodeDirty(); + } +}); + +/** + * creates a cc.OrbitCamera action with radius, delta-radius, z, deltaZ, x, deltaX + * @function + * @param {Number} t time + * @param {Number} radius + * @param {Number} deltaRadius + * @param {Number} angleZ + * @param {Number} deltaAngleZ + * @param {Number} angleX + * @param {Number} deltaAngleX + * @return {cc.OrbitCamera} + */ +cc.orbitCamera = function (t, radius, deltaRadius, angleZ, deltaAngleZ, angleX, deltaAngleX) { + return new cc.OrbitCamera(t, radius, deltaRadius, angleZ, deltaAngleZ, angleX, deltaAngleX); +}; + +/** + * Please use cc.orbitCamera instead + * creates a cc.OrbitCamera action with radius, delta-radius, z, deltaZ, x, deltaX + * @param {Number} t time + * @param {Number} radius + * @param {Number} deltaRadius + * @param {Number} angleZ + * @param {Number} deltaAngleZ + * @param {Number} angleX + * @param {Number} deltaAngleX + * @return {cc.OrbitCamera} + * @static + * @deprecated since v3.0 please use cc.orbitCamera() instead. + */ +cc.OrbitCamera.create = cc.orbitCamera; diff --git a/cocos2d/actions/CCActionCatmullRom.js b/cocos2d/actions/CCActionCatmullRom.js new file mode 100644 index 00000000000..2110529c905 --- /dev/null +++ b/cocos2d/actions/CCActionCatmullRom.js @@ -0,0 +1,605 @@ +/**************************************************************************** + 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.Vec2} p0 + * @param {cc.Vec2} p1 + * @param {cc.Vec2} p2 + * @param {cc.Vec2} p3 + * @param {Number} tension + * @param {Number} t + * @return {cc.Vec2} + */ +cc.cardinalSplineAt = function (p0, p1, p2, p3, tension, t) { + 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); + 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.Vec2} 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.Vec2} 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/cocos2d/actions/CCActionEase.js b/cocos2d/actions/CCActionEase.js new file mode 100644 index 00000000000..1ec1fb1c715 --- /dev/null +++ b/cocos2d/actions/CCActionEase.js @@ -0,0 +1,3681 @@ +/**************************************************************************** + 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 {null} + */ + 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 begining 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 begining 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 begining 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.
+ * Begining 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.
+ * Begining 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.
+ * Begining 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/cocos2d/actions/CCActionInstant.js b/cocos2d/actions/CCActionInstant.js new file mode 100644 index 00000000000..821d4016798 --- /dev/null +++ b/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.Vec2|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.Vec2|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.Vec2|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.Vec2|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) { + 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/cocos2d/actions/CCActionInterval.js b/cocos2d/actions/CCActionInterval.js new file mode 100644 index 00000000000..69cab12331b --- /dev/null +++ b/cocos2d/actions/CCActionInterval.js @@ -0,0 +1,3567 @@ +/**************************************************************************** + 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 + * 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 {null} + */ + 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 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 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.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.FiniteTimeAction} + * @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.FiniteTimeAction} + */ +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 || 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. + */ + 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.Vec2|Number} deltaPos + * @param {Number} [deltaY] + * @example + * var actionTo = 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.Vec2|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.Vec2} 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.Vec2|Number} deltaPos + * @param {Number} deltaY + * @return {cc.MoveBy} + * @example + * // example + * var actionTo = 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.Vec2|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.Vec2|Number} position + * @param {Number} y + * @example + * var actionBy = 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.Vec2|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.Vec2} 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.Vec2} position + * @param {Number} y + * @return {cc.MoveBy} + * @example + * // example + * var actionBy = 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.Vec2} position + * @param {Number} y + * @return {cc.MoveBy} + */ +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.Vec2|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.Vec2|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.Vec2|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.Vec2|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.Vec2|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.Vec2|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.Vec2|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.Vec2|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.Vec2|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.Vec2|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.js.array.verifyType(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.displayFrame(); + 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.js.array.verifyType(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/cocos2d/actions/CCActionTween.js b/cocos2d/actions/CCActionTween.js new file mode 100644 index 00000000000..1e5fb82be71 --- /dev/null +++ b/cocos2d/actions/CCActionTween.js @@ -0,0 +1,166 @@ +/**************************************************************************** + 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; \ No newline at end of file diff --git a/cocos2d/actions3d/CCActionGrid.js b/cocos2d/actions3d/CCActionGrid.js new file mode 100644 index 00000000000..a52024d3d2f --- /dev/null +++ b/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.Action} + */ + 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.Vec2} position + * @return {cc.Vertex3F} + */ + vertex:function (position) { + return this.getVertex(position); + }, + + /** + * returns the vertex than belongs to certain position in the grid + * @param {cc.Vec2} 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.Vec2} 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.Vec2} 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.Vec2} 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.Vec2} 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.Vec2} 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.Vec2} 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.Vec2} 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.Vec2} 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/cocos2d/actions3d/CCActionGrid3D.js b/cocos2d/actions3d/CCActionGrid3D.js new file mode 100644 index 00000000000..ca72abc6b16 --- /dev/null +++ b/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.Vec2} 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.Vec2} 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.Vec2} + */ + getPosition:function () { + return this._position; + }, + + /** + * set Position + * @param {cc.Vec2} 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.Vec2} 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.Vec2} 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.Vec2} 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.Vec2} 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.Vec2} 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.Vec2} + */ + getPosition:function () { + return this._position; + }, + + /** + * set center position + * @param {cc.Vec2} 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.Vec2} 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.Vec2} 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.Vec2} 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.Vec2} 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.Vec2} 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.Vec2} + */ + getPosition:function () { + return this._position; + }, + + /** + * set twirl center + * @param {cc.Vec2} 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.Vec2} 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.Vec2} 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/cocos2d/actions3d/CCActionPageTurn3D.js b/cocos2d/actions3d/CCActionPageTurn3D.js new file mode 100644 index 00000000000..ae7313b2dd4 --- /dev/null +++ b/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/cocos2d/actions3d/CCActionTiledGrid.js b/cocos2d/actions3d/CCActionTiledGrid.js new file mode 100644 index 00000000000..3210f3331f9 --- /dev/null +++ b/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.Vec2} [position=cc.p(0,0)] + * @param {cc.Vec2} [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.Vec2} 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.Vec2} 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.Vec2} pos + */ + turnOnTile:function (pos) { + this.setTile(pos, this.originalTile(pos)); + }, + + /** + * Turn Off Tile + * @param {cc.Vec2} pos + */ + turnOffTile:function (pos) { + this.setTile(pos, new cc.Quad3()); + }, + + /** + * Transform tile + * @param {cc.Vec2} 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.Vec2} 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.Vec2} 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.Vec2} 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.Vec2} pos + */ + turnOnTile:function (pos) { + this.setTile(pos, this.originalTile(pos)); + }, + + /** + * Turn off title. + * @param {cc.Vec2} 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/cocos2d/animation/animation-animator.js b/cocos2d/animation/animation-animator.js new file mode 100644 index 00000000000..3cca6baf8a3 --- /dev/null +++ b/cocos2d/animation/animation-animator.js @@ -0,0 +1,315 @@ +var JS = cc.js; +var Animator = require('./animators').Animator; +var DynamicAnimCurve = require('./animation-curves').DynamicAnimCurve; +var SampledAnimCurve = require('./animation-curves').SampledAnimCurve; +var sampleMotionPaths = require('./motion-path-helper').sampleMotionPaths; +var EventAnimCurve = require('./animation-curves').EventAnimCurve; +var EventInfo = require('./animation-curves').EventInfo; +var WrapModeMask = require('./types').WrapModeMask; +var binarySearch = require('./binary-search'); + +// The actual animator for Animation Component + +function AnimationAnimator (target, animation) { + Animator.call(this, target); + this.animation = animation; +} +JS.extend(AnimationAnimator, Animator); +var p = AnimationAnimator.prototype; + +p.playState = function (state, startTime) { + var clip = state.clip; + if (!clip) { + return; + } + var curves = state.curves; + if (!state.curveLoaded) { + initClipData(this.target, state); + } + this.playingAnims.push(state); + state.play(); + state.time = startTime || 0; + this.play(); +}; + +p.sample = function () { + var anims = this.playingAnims; + for (var i = 0; i < anims.length; i++) { + var anim = anims[i]; + anim.sample(); + } +}; + +p.stopState = function (state) { + if (JS.array.remove(this.playingAnims, state)) { + state.stop(); + } +}; + +p.pauseState = function (state) { + if (state) { + state.pause(); + } +}; + +p.resumeState = function (state) { + if (state) { + state.resume(); + } +}; + +p.setStateTime = function (state, time) { + if (state) { + state.setTime(time); + } +} + +if (CC_EDITOR || CC_TEST) { + p.reloadClip = function (state) { + if (state.isPlaying) { + initClipData(this.target, state); + } + else { + state.curveLoaded = false; + } + }; +} + +// 这个方法应该是 SampledAnimCurve æ‰èƒ½ç”¨ +function createBatchedProperty (propPath, firstDotIndex, mainValue, animValue) { + mainValue = mainValue.clone(); + var nextValue = mainValue; + var leftIndex = firstDotIndex + 1; + var rightIndex = propPath.indexOf('.', leftIndex); + + // scan property path + while (rightIndex !== -1) { + var nextName = propPath.slice(leftIndex, rightIndex); + nextValue = nextValue[nextName]; + leftIndex = rightIndex + 1; + rightIndex = propPath.indexOf('.', leftIndex); + } + var lastPropName = propPath.slice(leftIndex); + nextValue[lastPropName] = animValue; + + return mainValue; +} + +if (CC_TEST) { + cc._Test.createBatchedProperty = createBatchedProperty; +} + +function splitPropPath (propPath) { + var array = propPath.split('.'); + array.shift(); + //array = array.filter(function (item) { return !!item; }); + return array.length > 0 ? array : null; +} + + +function initClipData (root, state) { + var clip = state.clip; + + var curves = state.curves; + curves.length = 0; + + state.duration = clip.duration; + state.speed = clip.speed; + state.wrapMode = clip.wrapMode; + state.frameRate = clip.sample; + + if ((state.wrapMode & WrapModeMask.Loop) === WrapModeMask.Loop) { + state.repeatCount = Infinity; + } + else { + state.repeatCount = 1; + } + + // create curves + + function checkMotionPath(motionPath) { + if (!Array.isArray(motionPath)) return false; + + for (var i = 0, l = motionPath.length; i < l; i++) { + var controls = motionPath[i]; + + if (!Array.isArray(controls) || controls.length !== 6) return false; + } + + return true; + } + + function createPropCurve (target, propPath, keyframes) { + var curve; + + var isMotionPathProp = (target instanceof cc.ENode) && (propPath === 'position'); + var motionPaths = []; + var curve; + + if (isMotionPathProp) + curve = new SampledAnimCurve(); + else + curve = new DynamicAnimCurve(); + + // 缓存目标对象,所以 Component 必须一开始都创建好并且ä¸èƒ½è¿è¡Œæ—¶åŠ¨æ€æ›¿æ¢â€¦â€¦ + curve.target = target; + + var propName, propValue; + var dotIndex = propPath.indexOf('.'); + var hasSubProp = dotIndex !== -1; + if (hasSubProp) { + propName = propPath.slice(0, dotIndex); + propValue = target[propName]; + + // if (!(propValue instanceof cc.ValueType)) { + // cc.error('Only support sub animation property which is type cc.ValueType'); + // continue; + // } + } + else { + propName = propPath; + } + + curve.prop = propName; + + curve.subProps = splitPropPath(propPath); + + // for each keyframes + for (var j = 0, l = keyframes.length; j < l; j++) { + var keyframe = keyframes[j]; + var ratio = keyframe.frame / state.duration; + curve.ratios.push(ratio); + + if (isMotionPathProp) { + var motionPath = keyframe.motionPath; + + if (motionPath && !checkMotionPath(motionPath)) { + cc.error('motion path of target [' + target.name + '] in prop [' + propPath + '] frame [' + j +'] is not valid'); + motionPath = null; + } + + motionPaths.push(motionPath); + } + + var curveValue = keyframe.value; + //if (hasSubProp) { + // curveValue = createBatchedProperty(propPath, dotIndex, propValue, curveValue); + //} + curve.values.push(curveValue); + + var curveTypes = keyframe.curve; + if (curveTypes) { + if (typeof curveTypes === 'string') { + curve.types.push(curveTypes); + continue; + } + else if (Array.isArray(curveTypes)) { + if (curveTypes[0] === curveTypes[1] && + curveTypes[2] === curveTypes[3]) { + curve.types.push(DynamicAnimCurve.Linear); + } + else { + curve.types.push(DynamicAnimCurve.Bezier(curveTypes)); + } + continue; + } + } + curve.types.push(DynamicAnimCurve.Linear); + } + + if (isMotionPathProp) { + sampleMotionPaths(motionPaths, curve, clip.duration, clip.sample); + } + + return curve; + } + + function createTargetCurves (target, curveData) { + var propsData = curveData.props; + var compsData = curveData.comps; + + if (propsData) { + for (var propPath in propsData) { + var data = propsData[propPath]; + var curve = createPropCurve(target, propPath, data); + + curves.push(curve); + } + } + + if (compsData) { + for (var compName in compsData) { + var comp = target.getComponent(compName); + + if (!comp) { + continue; + } + + var compData = compsData[compName]; + for (var propPath in compData) { + var data = compData[propPath]; + var curve = createPropCurve(comp, propPath, data); + + curves.push(curve); + } + } + } + } + + // events curve + + var events = clip.events; + + if (!CC_EDITOR && events) { + var curve; + + for (var i = 0, l = events.length; i < l; i++) { + if (!curve) { + curve = new EventAnimCurve(); + curve.target = root; + curves.push(curve); + } + + var eventData = events[i]; + var ratio = eventData.frame / state.duration; + + var eventInfo; + var index = binarySearch(curve.ratios, ratio); + if (index >= 0) { + eventInfo = curve.events[index]; + } + else { + eventInfo = new EventInfo(); + curve.ratios.push(ratio); + curve.events.push(eventInfo); + } + + eventInfo.add(eventData.func, eventData.params); + } + } + + // property curves + + var curveData = clip.curveData; + var childrenCurveDatas = curveData.paths; + + createTargetCurves(root, curveData); + + for (var namePath in childrenCurveDatas) { + var target = cc.find(namePath, root); + + if (!target) { + continue; + } + + var childCurveDatas = childrenCurveDatas[namePath]; + createTargetCurves(target, childCurveDatas); + } +} + +if (CC_TEST) { + cc._Test.initClipData = initClipData; +} + + +module.exports = AnimationAnimator; diff --git a/cocos2d/animation/animation-clip.js b/cocos2d/animation/animation-clip.js new file mode 100644 index 00000000000..6b198923028 --- /dev/null +++ b/cocos2d/animation/animation-clip.js @@ -0,0 +1,134 @@ + +var AnimationClip = cc.Class({ + name: 'cc.AnimationClip', + extends: cc.Asset, + + properties: { + _duration: { + default: 0, + type: 'Float', + }, + + /** + * Duration of this animation + * @type {Number} + */ + duration: { + get: function () { return this._duration; }, + }, + + /** + * FrameRate of this animation + * @type {Number} + */ + sample: { + default: 60, + }, + + /** + * Speed of this animation + * @type {Number} + */ + speed: { + default: 1 + }, + + /** + * WrapMode of this animation + * @type {cc.WrapMode} + */ + wrapMode: { + default: cc.WrapMode.Normal + }, + + /** + * Curve data + * @property curveData + * @type {Object} + * @example + * { + * // 根节点ä¸ç”¨æŸ¥æ‰¾è·¯å¾„ + * // root properties + * props: { + * x: [ + * { frame: 0, value: 0, curve: [0,0.5,0.5,1] }, + * { frame: 1, value: 200, curve: null } + * ] + * }, + * comps: { + * // component + * 'comp-1': { + * // component properties + * 'prop-1': [ + * { frame: 0, value: 10, curve: [0,0.5,0.5,1] }, + * { frame: 1, value: 20, curve: null } + * ] + * } + * }, + * paths: { + * // key 为节点到root的路径å, 通过cc.find找到 + * 'foo/bar': { + * // node properties + * props: { + * x: [ + * { frame: 0, value: 0, curve: [0,0.5,0.5,1] }, + * { frame: 1, value: 200, curve: null } + * ] + * }, + * comps: { + * // component + * 'comp-1': { + * // component property + * 'prop-1': [ + * { frame: 0, value: 10, curve: [0,0.5,0.5,1] }, + * { frame: 1, value: 20, curve: null } + * ] + * } + * } + * }, + * 'hello': { + * props: { + * position: [ + * { + * frame: 0, + * value: [0,0], + * motionPath: [ + * [320, 240, 0, 240, 640, 240], + * [640, 0, 400, 0, 1000, 0] + * ] + * }, + * { frame: 5, value: [640, 480] } + * ] + * } + * } + * } + * } + */ + curveData: { + default: {}, + visible: false, + }, + + /** + * Event data + * @property events + * @type {Array} + * @example + * [ + * frame: 0, func: 'onAnimationEvent1', params:['param-1', 'param-2'] + * frame: 2, func: 'onAnimationEvent3', params:['param-1', 'param-2'] + * frame: 3, func: 'onAnimationEvent2', params:['param-1'] + * // The second event at frame 3 + * frame: 3, func: 'onAnimationEvent4', params:['param-1'] + * frame: 4, func: 'onAnimationEvent4', params:['param-1'] + * ] + */ + events: { + default: [], + visible: false, + } + }, + +}); + +cc.AnimationClip = module.exports = AnimationClip; diff --git a/cocos2d/animation/animation-curves.js b/cocos2d/animation/animation-curves.js new file mode 100644 index 00000000000..b7b229baf0a --- /dev/null +++ b/cocos2d/animation/animation-curves.js @@ -0,0 +1,397 @@ + +var bezier = require('./bezier').bezier; +var bezierByTime = require('./bezier').bezierByTime; + +var binarySearch = require('./binary-search'); +var WrapMode = require('./types').WrapMode; +var WrapModeMask = require('./types').WrapModeMask; + +/** + * Compute a new ratio by curve type + * @param {Number} ratio - The origin ratio + * @param {any} + * - If it's Array, then ratio will be computed with bezierByTime + * - If it's string, then ratio will be computed with cc.Easing function + */ +function computeRatioByType (ratio, type) { + if (typeof type === 'string') { + var func = cc.Easing[type]; + if (func) { + ratio = func(ratio); + } + else { + cc.error('Can\'t find easing type [' + type + ']'); + } + } + else if (Array.isArray(type)) { + // bezier curve + ratio = bezierByTime(type, ratio); + } + + return ratio; +} + +// +// 动画数æ®ç±»ï¼Œç›¸å½“于 AnimationClip。 +// 虽然å«åš AnimCurve,但除了曲线,å¯ä»¥ä¿å­˜ä»»ä½•ç±»åž‹çš„值。 +// +// @class AnimCurve +// @constructor +// +var AnimCurve = cc.Class({ + name: 'cc.AnimCurve', + + // + // @method sample + // @param {number} time + // @param {number} ratio - The normalized time specified as a number between 0.0 and 1.0 inclusive. + // @param {AnimationNode} animationNode + // + sample: function (time, ratio, animationNode) {}, + + onTimeChangedManually: function () {} +}); + + +// +// 区别于 SampledAnimCurve。 +// +// @class DynamicAnimCurve +// @constructor +// @extends AnimCurve +// +var DynamicAnimCurve = cc.Class({ + name: 'cc.DynamicAnimCurve', + extends: AnimCurve, + + properties: { + + // The object being animated. + // @property target + // @type {object} + target: null, + + // The name of the property being animated. + // @property prop + // @type {string} + prop: "", + + // The values of the keyframes. (y) + // @property values + // @type {any[]} + values: [], + + // The keyframe ratio of the keyframe specified as a number between 0.0 and 1.0 inclusive. (x) + // @property ratios + // @type {number[]} + ratios: [], + + // @property types + // @param {object[]} + // Each array item maybe type: + // - [x, x, x, x]: Four control points for bezier + // - null: linear + types: [], + + // @property {string[]} subProps - The path of sub property being animated. + subProps: null + }, + + _calcValue: function (frameIndex, ratio) { + var values = this.values; + var fromVal = values[frameIndex - 1]; + var toVal = values[frameIndex]; + + // lerp + if (typeof fromVal === 'number') { + value = fromVal + (toVal - fromVal) * ratio; + } + else { + var lerp = fromVal.lerp; + if (lerp) { + value = fromVal.lerp(toVal, ratio); + } + else { + // no linear lerp function, just return last frame + value = fromVal; + } + } + + return value; + }, + + _applyValue: function (target, prop, value) { + target[prop] = value; + }, + + _findFrameIndex: binarySearch, + + sample: function (time, ratio, animationNode) { + var values = this.values; + var ratios = this.ratios; + var frameCount = ratios.length; + + if (frameCount === 0) { + return; + } + + // evaluate value + var value; + var index = this._findFrameIndex(ratios, ratio); + + if (index < 0) { + index = ~index; + + if (index <= 0) { + value = values[0]; + } + else if (index >= frameCount) { + value = values[frameCount - 1]; + } + else { + var fromRatio = ratios[index - 1]; + var toRatio = ratios[index]; + var type = this.types[index - 1]; + var ratioBetweenFrames = (ratio - fromRatio) / (toRatio - fromRatio); + + ratioBetweenFrames = computeRatioByType(ratioBetweenFrames, type); + + value = this._calcValue(index, ratioBetweenFrames); + } + } + else { + value = values[index]; + } + + var subProps = this.subProps; + if (subProps) { + // create batched value dynamically + var mainProp = this.target[this.prop]; + var subProp = mainProp; + + for (var i = 0; i < subProps.length - 1; i++) { + var subPropName = subProps[i]; + if (subProp) { + subProp = subProp[subPropName]; + } + else { + return; + } + } + + var propName = subProps[subProps.length - 1]; + + if (subProp) { + this._applyValue(subProp, propName, value); + } + else { + return; + } + + value = mainProp; + } + + // apply value + this._applyValue(this.target, this.prop, value); + } +}); + +DynamicAnimCurve.Linear = null; +DynamicAnimCurve.Bezier = function (controlPoints) { + return controlPoints; +}; + + + +/** + * SampledAnimCurve, 这里é¢çš„数值需è¦æ˜¯å·²ç»éƒ½é¢„å…ˆsample好了的, + * 所以 SampledAnimCurve 中查找 frame index 的速度会éžå¸¸å¿« + * + * @class SampledAnimCurve + * @constructor + * @extends DynamicAnimCurve + */ +var SampledAnimCurve = cc.Class({ + name: 'cc.SampledAnimCurve', + extends: DynamicAnimCurve, + + _findFrameIndex: function (ratios, ratio) { + var length = ratios.length - 1; + var eachLength = 1 / length; + + var index = (ratio / eachLength) | 0; + return index; + } +}); + + + +/** + * Event information, + * @class EventInfo + * @constructor + */ +var EventInfo = function () { + this.events = []; +}; + +/** + * @param {Function} [func] event function + * @param {Object[]} [params] event params + */ +EventInfo.prototype.add = function (func, params) { + this.events.push({ + func: func || '', + params: params || [] + }); +}; + + +/** + * + * @class EventAnimCurve + * @constructor + * @extends AnimCurve + */ +var EventAnimCurve = cc.Class({ + name: 'cc.EventAnimCurve', + extends: AnimCurve, + + properties: { + /** + * The object being animated. + * @property target + * @type {object} + */ + target: null, + + /** The keyframe ratio of the keyframe specified as a number between 0.0 and 1.0 inclusive. (x) + * @property ratios + * @type {number[]} + */ + ratios: [], + + /** + * @property events + * @type {EventInfo[]} + */ + events: [], + + /** + * Last wrapped info + * @property _lastWrappedInfo + * @type {Object} + * @default null + */ + _lastWrappedInfo: null + }, + + _wrapIterations: function (iterations) { + if (iterations - (iterations | 0) === 0) iterations -= 1; + return iterations | 0; + }, + + sample: function (time, ratio, animationNode) { + var length = this.ratios.length; + + var currentWrappedInfo = animationNode.getWrappedInfo(animationNode.time); + var direction = currentWrappedInfo.direction; + var currentIndex = binarySearch(this.ratios, currentWrappedInfo.ratio); + if (currentIndex < 0) { + currentIndex = ~currentIndex - 1; + + // if direction is inverse, then increase index + if (direction < 0) currentIndex += 1; + } + + var lastWrappedInfo = this._lastWrappedInfo; + var lastIndex = lastWrappedInfo ? lastWrappedInfo.frameIndex : currentWrappedInfo.frameIndex; + + if (!lastWrappedInfo) { + this._fireEvent(currentIndex); + } + else if (lastIndex !== currentIndex) { + + var wrapMode = animationNode.wrapMode; + + var currentIterations = this._wrapIterations(currentWrappedInfo.iterations); + var lastIntIterations = this._wrapIterations(lastWrappedInfo.iterations); + + direction = lastWrappedInfo.direction; + + do { + if (lastIntIterations <= currentIterations) { + if (direction === -1 && lastIndex === 0) { + if ((wrapMode & WrapModeMask.PingPong) === WrapModeMask.PingPong) { + direction *= -1; + } + else { + lastIndex = length; + } + + lastIntIterations ++; + } + else if (direction === 1 && lastIndex === length - 1) { + if ((wrapMode & WrapModeMask.PingPong) === WrapModeMask.PingPong) { + direction *= -1; + } + else { + lastIndex = -1; + } + + lastIntIterations ++; + } + + if (lastIndex === currentIndex) break; + } + + lastIndex += direction; + + this._fireEvent(lastIndex); + } while (lastIndex !== currentIndex); + } + + currentWrappedInfo.frameIndex = currentIndex; + this._lastWrappedInfo = currentWrappedInfo; + }, + + _fireEvent: function (index) { + if (index < 0 || index >= this.events.length) return; + + var eventInfo = this.events[index]; + var events = eventInfo.events; + var components = this.target._components; + + for (var i = 0; i < events.length; i++) { + var event = events[i]; + var funcName = event.func; + + for (var j = 0; j < components.length; j++) { + var component = components[j]; + var func = component[funcName]; + + if (func) func.apply(component, event.params); + } + } + }, + + onTimeChangedManually: function () { + this._lastWrappedInfo = null; + } +}); + + +if (CC_TEST) { + cc._Test.DynamicAnimCurve = DynamicAnimCurve; + cc._Test.SampledAnimCurve = SampledAnimCurve; + cc._Test.EventAnimCurve = EventAnimCurve; +} + +module.exports = { + AnimCurve: AnimCurve, + DynamicAnimCurve: DynamicAnimCurve, + SampledAnimCurve: SampledAnimCurve, + EventAnimCurve: EventAnimCurve, + EventInfo: EventInfo, + computeRatioByType: computeRatioByType +}; diff --git a/cocos2d/animation/animation-manager.js b/cocos2d/animation/animation-manager.js new file mode 100644 index 00000000000..fa8799ca72d --- /dev/null +++ b/cocos2d/animation/animation-manager.js @@ -0,0 +1,51 @@ +var JS = cc.js; + +var AnimationManager = cc._Class.extend({ + ctor: function () { + this.animators = []; + }, + + // for manager + + update: function (dt) { + var animators = this.animators; + for (var i = 0, len = animators.length; i < len; i++) { + var animator = animators[i]; + if (animator._isPlaying && !animator._isPaused) { + animator.update(dt); + // if removed + if (! animator._isPlaying) { + i--; + len--; + } + } + } + }, + + destruct: function () {}, + + // for animator + + /** + * @param {Animator} animator + */ + addAnimator: function (animator) { + this.animators.push(animator); + }, + + /** + * @param {Animator} animator + */ + removeAnimator: function (animator) { + var index = this.animators.indexOf(animator); + if (index >= 0) { + this.animators.splice(index, 1); + } + else { + cc.error('animator not added or already removed'); + } + } +}); + + +cc.AnimationManager = module.exports = AnimationManager; diff --git a/cocos2d/animation/animation-state.js b/cocos2d/animation/animation-state.js new file mode 100644 index 00000000000..a4a2b86581e --- /dev/null +++ b/cocos2d/animation/animation-state.js @@ -0,0 +1,71 @@ + +var JS = cc.js; +var AnimationNode = require('./types').AnimationNode; + +/** + * The AnimationState gives full control over animation playback process. + * In most cases the Animation Component is sufficient and easier to use. Use the AnimationState if you need full control. + * + * @class AnimationState + * @extends AnimationNode + * @constructor + * @param {AnimationClip} clip + * @param {String} [name] + */ +function AnimationState (clip, name) { + AnimationNode.call(this, null, null, { + duration: clip.length + }); + + this._clip = clip; + this._name = name || clip.name; +} +JS.extend(AnimationState, AnimationNode); + +var state = AnimationState.prototype; + +/** + * The clip that is being played by this animation state. + * @property clip + * @type {AnimationClip} + * @readOnly + */ +JS.get(state, 'clip', function () { + return this._clip; +}); + +/** + * The name of the playing animation. + * @property name + * @type {String} + * @readOnly + */ +JS.get(state, 'name', function () { + return this._name; +}); + +JS.obsolete(state, 'AnimationState.length', 'duration'); + +JS.getset(state, 'curveLoaded', + function () { + return this.curves.length > 0; + }, + function (value) { + this.curves.length = 0; + } +); + +state.onPlay = function () { + // replay + this.setTime(0); +}; + +state.setTime = function (time) { + this.time = time || 0; + + this.curves.forEach(function (curve) { + curve.onTimeChangedManually(); + }); +} + +cc.AnimationState = module.exports = AnimationState; diff --git a/cocos2d/animation/animators.js b/cocos2d/animation/animators.js new file mode 100644 index 00000000000..8595cee4060 --- /dev/null +++ b/cocos2d/animation/animators.js @@ -0,0 +1,197 @@ +var JS = cc.js; +var Playable = require('./playable'); +var AnimationNode = require('./types').AnimationNode; +var DynamicAnimCurve = require('./animation-curves').DynamicAnimCurve; + +// The base of animators +function Animator (target) { + this.target = target; + // {AnimationNodeBase} + this.playingAnims = []; +} + +JS.extend(Animator, Playable); + +var animProto = Animator.prototype; + +// ç”± AnimationManager 调用,åªæœ‰åœ¨è¯¥ animator 处于播放状æ€æ—¶æ‰ä¼šè¢«è°ƒç”¨ +animProto.update = function (deltaTime) { + var anims = this.playingAnims; + for (var i = 0; i < anims.length; i++) { + var anim = anims[i]; + if (anim._isPlaying && !anim._isPaused) { + anim.update(deltaTime); + // if removed + if (! anim._isPlaying) { + anims.splice(i, 1); // TODO: ç”± anim æ¥è´Ÿè´£è°ƒç”¨ splice + i--; + } + } + } + if (anims.length === 0) { + this.stop(); + } +}; + +animProto.onPlay = function () { + cc.director.getAnimationManager().addAnimator(this); +}; + +animProto.onStop = function () { + this.playingAnims.length = 0; + cc.director.getAnimationManager().removeAnimator(this); +}; + + + +// The actual animator for Entity +function EntityAnimator (target) { + Animator.call(this, target); +} +JS.extend(EntityAnimator, Animator); + +var entProto = EntityAnimator.prototype; + +// 通用逻辑 + +function computeNullRatios (keyFrames) { + var lastIndex = 0; + var lastRatio = 0; + + var len = keyFrames.length; + for (var i = 0; i < len; i++) { + var frame = keyFrames[i]; + var ratio = frame.ratio; + if (i === 0 && typeof ratio !== "number") { + // 如果一开始就没有 ratio,则默认从 0 开始 + frame.computedRatio = ratio = 0; + } + else if (i === len - 1 && typeof ratio !== "number") { + // 如果最åŽæ²¡æœ‰ ratio,则设置为 1 + frame.computedRatio = ratio = 1; + } + if (typeof ratio === "number") { + if (lastIndex + 1 < i) { + var count = i - lastIndex; + var step = (ratio - lastRatio) / count; + for (var j = lastIndex + 1; j < i; j++) { + lastRatio += step; + keyFrames[j].computedRatio = lastRatio; // ä¸å ç”¨å·²æœ‰å˜é‡ï¼Œè¿™æ · keyFrames æ‰èƒ½é‡ç”¨ + } + } + lastIndex = i; + lastRatio = ratio; + } + } +} + +if (CC_TEST) { + cc._Test.computeNullRatios = computeNullRatios; +} + +///** +// * @param {object[]} keyFrames +// * @param {object} [timingInput] - This dictionary is used as a convenience for specifying the timing properties of an Animation in bulk. +// * @return {AnimationNode} +// */ +entProto.animate = function (keyFrames, timingInput) { + if (! keyFrames) { + cc.error('[animate] keyFrames must be non-nil'); + return null; + } + // compute absolute ratio of each keyframe with a null ratio + computeNullRatios(keyFrames); + + var anim = this._doAnimate(keyFrames, timingInput); + + this.play(); + return anim; +}; + +// 具体逻辑 + +function findCurve (curves, target, propName) { + var i = 0, curve; + + for (; i < curves.length; i++) { + curve = curves[i]; + if (curve.target === target && curve.prop === propName) { + return curve; + } + } + + return null; +} + +function createPropCurve (curves, target, propName, value, ratio) { + var curve = findCurve(curves, target, propName); + if (! curve) { + curve = new DynamicAnimCurve(); + curves.push(curve); + // 缓存目标对象,所以 Component 必须一开始都创建好并且ä¸èƒ½è¿è¡Œæ—¶åŠ¨æ€æ›¿æ¢â€¦â€¦ + curve.target = target; + curve.prop = propName; + } + curve.values.push(value); + curve.ratios.push(ratio); +} + +entProto._doAnimate = function (keyFrames, timingInput) { + var anim = new AnimationNode(this, null, timingInput); + anim.play(); + var curves = anim.curves; + + // create curves + var lastRatio = -1; + for (var i = 0; i < keyFrames.length; i++) { + var frame = keyFrames[i]; + + // get ratio + var ratio = frame.ratio; + if (typeof ratio !== "number") { + ratio = frame.computedRatio; + } + if (ratio < 0) { + cc.error('[animate] ratio should >= 0!'); + continue; + } + if (ratio < lastRatio) { + cc.error('[animate] ratio should in the order of smallest to largest!'); + continue; + } + lastRatio = ratio; + + // å…ˆé历æ¯ä¸€å¸§ï¼ŒèŽ·å¾—所有曲线 + for (var key in frame) { + + var data = frame[key]; + + if (key === 'props') { + for (var propName in data) { + createPropCurve(curves, this.target, propName, data[propName], ratio); + } + } + else if (key === 'comps') { + for (var compName in data) { + var comp = this.target.getComponent(compName); + var compData = data[compName]; + + for (var propName in compData) { + createPropCurve(curves, comp, propName, compData[propName], ratio); + } + } + } + } + } + this.playingAnims.push(anim); + return anim; +}; + +if (CC_TEST) { + cc._Test.EntityAnimator = EntityAnimator; +} + +module.exports = { + Animator: Animator, + EntityAnimator: EntityAnimator +}; diff --git a/cocos2d/animation/bezier.js b/cocos2d/animation/bezier.js new file mode 100644 index 00000000000..0f284f18271 --- /dev/null +++ b/cocos2d/animation/bezier.js @@ -0,0 +1,196 @@ +//var bezier = (function () { +// function B1 (t) { return (t * t * t); } +// function B2 (t) { return (3 * t * t * (1 - t)); } +// function B3 (t) { return (3 * t * (1 - t) * (1 - t)); } +// function B4 (t) { return ((1 - t) * (1 - t) * (1 - t)); } +// function bezier (C1, C2, C3, C4, t) { +// return C1 * B1(t) + C2 * B2(t) + C3 * B3(t) + C4 * B4(t); +// } +// +// //function bezier (C1, C2, C3, C4, t, out) { +// // out.x = C1.x * B1(t) + C2.x * B2(t) + C3.x * B3(t) + C4.x * B4(t); +// // out.y = C1.y * B1(t) + C2.y * B2(t) + C3.y * B3(t) + C4.y * B4(t); +// //} +// +// return bezier; +//})(); +function bezier (C1, C2, C3, C4, t) { + var t1 = 1 - t; + return C1 * t1 * t1 * t1 + + C2 * 3 * t1 * t1 * t + + C3 * 3 * t1 * t * t + + C4 * t * t * t; +} +//function bezier (c0, c1, c2, c3, t) { +// var cy = 3.0 * (c1); +// var by = 3.0 * (c3 - c1) - cy; +// var ay = 1 - cy - by; +// return (ay * t * t * t) + (by * t * t) + (cy * t); +//} + +//var sin = Math.sin; +var cos = Math.cos, + acos = Math.acos, + max = Math.max, + //atan2 = Math.atan2, + pi = Math.PI, + tau = 2 * pi, + sqrt = Math.sqrt; + +function crt (v) { + if (v < 0) { + return -Math.pow(-v, 1 / 3); + } + else { + return Math.pow(v, 1 / 3); + } +} + +//function align (curve, line) { +// var tx = line.p1.x, +// ty = line.p1.y, +// a = -atan2(line.p2.y-ty, line.p2.x-tx); +// curve = [{x:0, y:1}, {x: curve[0], y: 1-curve[1]}, {x: curve[2], y: 1-curve[3]}, {x:1, y:0}]; +// return curve.map(function(v) { +// return { +// x: (v.x-tx)*cos(a) - (v.y-ty)*sin(a), +// y: (v.x-tx)*sin(a) + (v.y-ty)*cos(a) +// }; +// }); +//} + +// Modified from http://jsbin.com/yibipofeqi/1/edit, optimized for animations. +// The origin Cardano's algorithm is based on http://www.trans4mind.com/personal_development/mathematics/polynomials/cubicAlgebra.htm +function cardano (curve, x) { + // align curve with the intersecting line: + //var line = {p1: {x: x, y: 0}, p2: {x: x, y: 1}}; + //var aligned = align(curve, line); + //// and rewrite from [a(1-t)^3 + 3bt(1-t)^2 + 3c(1-t)t^2 + dt^3] form + // pa = aligned[0].y, + // pb = aligned[1].y, + // pc = aligned[2].y, + // pd = aligned[3].y; + ////// curve = [{x:0, y:1}, {x: curve[0], y: 1-curve[1]}, {x: curve[2], y: 1-curve[3]}, {x:1, y:0}]; + var pa = x - 0; + var pb = x - curve[0]; + var pc = x - curve[2]; + var pd = x - 1; + + // to [t^3 + at^2 + bt + c] form: + var pa3 = pa * 3; + var pb3 = pb * 3; + var pc3 = pc * 3; + var d = (-pa + pb3 - pc3 + pd), + rd = 1 / d, + r3 = 1 / 3, + a = (pa3 - 6 * pb + pc3) * rd, + a3 = a * r3, + b = (-pa3 + pb3) * rd, + c = pa * rd, + // then, determine p and q: + p = (3 * b - a * a) * r3, + p3 = p * r3, + q = (2 * a * a * a - 9 * a * b + 27 * c) / 27, + q2 = q / 2, + // and determine the discriminant: + discriminant = q2 * q2 + p3 * p3 * p3, + // and some reserved variables + u1, v1, x1, x2, x3; + + // If the discriminant is negative, use polar coordinates + // to get around square roots of negative numbers + if (discriminant < 0) { + var mp3 = -p * r3, + mp33 = mp3 * mp3 * mp3, + r = sqrt(mp33), + // compute cosphi corrected for IEEE float rounding: + t = -q / (2 * r), + cosphi = t < -1 ? -1 : t > 1 ? 1 : t, + phi = acos(cosphi), + crtr = crt(r), + t1 = 2 * crtr; + x1 = t1 * cos(phi * r3) - a3; + x2 = t1 * cos((phi + tau) * r3) - a3; + x3 = t1 * cos((phi + 2 * tau) * r3) - a3; + + // choose best percentage + if (0 <= x1 && x1 <= 1) { + if (0 <= x2 && x2 <= 1) { + if (0 <= x3 && x3 <= 1) { + return max(x1, x2, x3); + } + else { + return max(x1, x2); + } + } + else if (0 <= x3 && x3 <= 1) { + return max(x1, x3); + } + else { + return x1; + } + } + else { + if (0 <= x2 && x2 <= 1) { + if (0 <= x3 && x3 <= 1) { + return max(x2, x3); + } + else { + return x2; + } + } + else { + return x3; + } + } + } + else if (discriminant === 0) { + u1 = q2 < 0 ? crt(-q2) : -crt(q2); + x1 = 2 * u1 - a3; + x2 = -u1 - a3; + + // choose best percentage + if (0 <= x1 && x1 <= 1) { + if (0 <= x2 && x2 <= 1) { + return max(x1, x2); + } + else { + return x1; + } + } + else { + return x2; + } + } + // one real root, and two imaginary roots + else { + var sd = sqrt(discriminant); + u1 = crt(-q2 + sd); + v1 = crt(q2 + sd); + x1 = u1 - v1 - a3; + return x1; + } +} + +function bezierByTime (controlPoints, x) { + var percent = cardano(controlPoints, x); // t + var p0y = 0; // a + var p1y = controlPoints[1]; // b + var p2y = controlPoints[3]; // c + var p3y = 1; // d + var t1 = 1 - percent; + return p0y * t1 * t1 * t1 + + p1y * 3 * percent * t1 * t1 + + p2y * 3 * percent * percent * t1 + + p3y * percent * percent * percent; +} + +if (CC_TEST) { + cc._Test.bezier = bezier; + cc._Test.bezierByTime = bezierByTime; +} + +module.exports = { + bezier: bezier, + bezierByTime: bezierByTime +}; diff --git a/cocos2d/animation/binary-search.js b/cocos2d/animation/binary-search.js new file mode 100644 index 00000000000..3ac8b52f41a --- /dev/null +++ b/cocos2d/animation/binary-search.js @@ -0,0 +1,28 @@ +var EPSILON = 1e-6; + +// +// Searches the entire sorted Array for an element and returns the zero-based index of the element. +// @method binarySearch +// @param {number[]} array +// @param {number} value +// @return {number} The zero-based index of item in the sorted Array, if item is found; otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than item or, if there is no larger element, the bitwise complement of array's length. +// +function binarySearch (array, value) { + var l = 0, h = array.length - 1; + while (l <= h) { + var m = ((l + h) >> 1); + + if (Math.abs(array[m] - value) < EPSILON) { + return m; + } + if (array[m] > value) { + h = m - 1; + } + else { + l = m + 1; + } + } + return ~l; +} + +module.exports = binarySearch; diff --git a/cocos2d/animation/easing.js b/cocos2d/animation/easing.js new file mode 100644 index 00000000000..af51505fa4a --- /dev/null +++ b/cocos2d/animation/easing.js @@ -0,0 +1,257 @@ +var Easing = { + linear: function (k) { return k; }, + + // quad + // Easing equation function for a quadratic (t^2) + // @param t: Current time (in frames or seconds). + // @return: The correct value. + + quadIn: function (k) { return k * k; }, + quadOut: function (k) { return k * ( 2 - k ); }, + quadInOut: function (k) { + if (( k *= 2 ) < 1) { + return 0.5 * k * k; + } + return -0.5 * ( --k * ( k - 2 ) - 1 ); + }, + + // cubic + // Easing equation function for a cubic (t^3) + // @param t: Current time (in frames or seconds). + // @return: The correct value. + + cubicIn: function (k) { return k * k * k; }, + cubicOut: function (k) { return --k * k * k + 1; }, + cubicInOut: function (k) { + if (( k *= 2 ) < 1) { + return 0.5 * k * k * k; + } + return 0.5 * ( ( k -= 2 ) * k * k + 2 ); + }, + + // quart + // Easing equation function for a quartic (t^4) + // @param t: Current time (in frames or seconds). + // @return: The correct value. + + quartIn: function (k) { return k * k * k * k; }, + quartOut: function (k) { return 1 - ( --k * k * k * k ); }, + quartInOut: function (k) { + if (( k *= 2 ) < 1) { + return 0.5 * k * k * k * k; + } + return -0.5 * ( ( k -= 2 ) * k * k * k - 2 ); + }, + + // quint + // Easing equation function for a quintic (t^5) + // @param t: Current time (in frames or seconds). + // @return: The correct value. + + quintIn: function (k) { return k * k * k * k * k; }, + quintOut: function (k) { return --k * k * k * k * k + 1; }, + quintInOut: function (k) { + if (( k *= 2 ) < 1) { + return 0.5 * k * k * k * k * k; + } + return 0.5 * ( ( k -= 2 ) * k * k * k * k + 2 ); + }, + + // sine + // Easing equation function for a sinusoidal (sin(t)) + // @param t: Current time (in frames or seconds). + // @return: The correct value. + + sineIn: function (k) { return 1 - Math.cos(k * Math.PI / 2); }, + sineOut: function (k) { return Math.sin(k * Math.PI / 2); }, + sineInOut: function (k) { return 0.5 * ( 1 - Math.cos(Math.PI * k) ); }, + + // expo + // Easing equation function for an exponential (2^t) + // param t: Current time (in frames or seconds). + // return: The correct value. + + expoIn: function (k) { return k === 0 ? 0 : Math.pow(1024, k - 1); }, + expoOut: function (k) { return k === 1 ? 1 : 1 - Math.pow(2, -10 * k); }, + expoInOut: function (k) { + if (k === 0) { + return 0; + } + if (k === 1) { + return 1; + } + if (( k *= 2 ) < 1) { + return 0.5 * Math.pow(1024, k - 1); + } + return 0.5 * ( -Math.pow(2, -10 * ( k - 1 )) + 2 ); + }, + + // circ + // Easing equation function for a circular (sqrt(1-t^2)) + // @param t: Current time (in frames or seconds). + // @return: The correct value. + + circIn: function (k) { return 1 - Math.sqrt(1 - k * k); }, + circOut: function (k) { return Math.sqrt(1 - ( --k * k )); }, + circInOut: function (k) { + if (( k *= 2 ) < 1) { + return -0.5 * ( Math.sqrt(1 - k * k) - 1); + } + return 0.5 * ( Math.sqrt(1 - ( k -= 2) * k) + 1); + }, + + // elastic + // Easing equation function for an elastic (exponentially decaying sine wave) + // @param t: Current time (in frames or seconds). + // @return: The correct value. + // recommand value: elastic (t) + + elasticIn: function (k) { + var s, a = 0.1, p = 0.4; + if (k === 0) { + return 0; + } + if (k === 1) { + return 1; + } + if (!a || a < 1) { + a = 1; + s = p / 4; + } + else { + s = p * Math.asin(1 / a) / ( 2 * Math.PI ); + } + return -( a * Math.pow(2, 10 * ( k -= 1 )) * Math.sin(( k - s ) * ( 2 * Math.PI ) / p) ); + }, + elasticOut: function (k) { + var s, a = 0.1, p = 0.4; + if (k === 0) { + return 0; + } + if (k === 1) { + return 1; + } + if (!a || a < 1) { + a = 1; + s = p / 4; + } + else { + s = p * Math.asin(1 / a) / ( 2 * Math.PI ); + } + return ( a * Math.pow(2, -10 * k) * Math.sin(( k - s ) * ( 2 * Math.PI ) / p) + 1 ); + }, + elasticInOut: function (k) { + var s, a = 0.1, p = 0.4; + if (k === 0) { + return 0; + } + if (k === 1) { + return 1; + } + if (!a || a < 1) { + a = 1; + s = p / 4; + } + else { + s = p * Math.asin(1 / a) / ( 2 * Math.PI ); + } + if (( k *= 2 ) < 1) { + return -0.5 * + ( a * Math.pow(2, 10 * ( k -= 1 )) * Math.sin(( k - s ) * ( 2 * Math.PI ) / p) ); + } + return a * Math.pow(2, -10 * ( k -= 1 )) * Math.sin(( k - s ) * ( 2 * Math.PI ) / p) * 0.5 + 1; + }, + + // back + // Easing equation function for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) + // @param t: Current time (in frames or seconds). + // @return: The correct value. + + backIn: function (k) { + var s = 1.70158; + return k * k * ( ( s + 1 ) * k - s ); + }, + backOut: function (k) { + var s = 1.70158; + return --k * k * ( ( s + 1 ) * k + s ) + 1; + }, + backInOut: function (k) { + var s = 1.70158 * 1.525; + if (( k *= 2 ) < 1) { + return 0.5 * ( k * k * ( ( s + 1 ) * k - s ) ); + } + return 0.5 * ( ( k -= 2 ) * k * ( ( s + 1 ) * k + s ) + 2 ); + }, + + // bounce + // Easing equation function for a bounce (exponentially decaying parabolic bounce) + // @param t: Current time (in frames or seconds). + // @return: The correct value. + + bounceOut: function (k) { + if (k < ( 1 / 2.75 )) { + return 7.5625 * k * k; + } + else if (k < ( 2 / 2.75 )) { + return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75; + } + else if (k < ( 2.5 / 2.75 )) { + return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375; + } + else { + return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375; + } + }, + + // smooth + // t<=0: 0 | 0=1: 1 + smooth: function (t) { + if (t <= 0) { + return 0; + } + if (t >= 1) { + return 1; + } + return t * t * (3 - 2 * t); + }, + + // fade + // t<=0: 0 | 0=1: 1 + fade: function (t) { + if (t <= 0) { + return 0; + } + if (t >= 1) { + return 1; + } + return t * t * t * (t * (t * 6 - 15) + 10); + }, +}; + +function _makeOutIn (fnIn, fnOut) { + return function (k) { + if (k < 0.5) { + return fnOut(k * 2) / 2; + } + return fnIn(2 * k - 1) / 2 + 0.5; + }; +} +Easing.quadOutIn = _makeOutIn(Easing.quadIn, Easing.quadOut); +Easing.cubicOutIn = _makeOutIn(Easing.cubicIn, Easing.cubicOut); +Easing.quartOutIn = _makeOutIn(Easing.quartIn, Easing.quartOut); +Easing.quintOutIn = _makeOutIn(Easing.quintIn, Easing.quintOut); +Easing.sineOutIn = _makeOutIn(Easing.sineIn, Easing.sineOut); +Easing.expoOutIn = _makeOutIn(Easing.expoIn, Easing.expoOut); +Easing.circOutIn = _makeOutIn(Easing.circIn, Easing.circOut); +Easing.backOutIn = _makeOutIn(Easing.backIn, Easing.backOut); +Easing.backOutIn = _makeOutIn(Easing.backIn, Easing.backOut); +Easing.bounceIn = function (k) { return 1 - Easing.bounceOut(1 - k); }; +Easing.bounceInOut = function (k) { + if (k < 0.5) { + return Easing.bounceIn(k * 2) * 0.5; + } + return Easing.bounceOut(k * 2 - 1) * 0.5 + 0.5; +}; +Easing.bounceOutIn = _makeOutIn(Easing.bounceIn, Easing.bounceOut); + +cc.Easing = module.exports = Easing; diff --git a/cocos2d/animation/index.js b/cocos2d/animation/index.js new file mode 100644 index 00000000000..20c03dace53 --- /dev/null +++ b/cocos2d/animation/index.js @@ -0,0 +1,10 @@ +require('./bezier'); +require('./easing'); +require('./types'); +require('./motion-path-helper'); +require('./animation-curves'); +require('./animation-clip'); +require('./animators'); +require('./animation-manager'); +require('./animation-state'); +require('./animation-animator'); diff --git a/cocos2d/animation/motion-path-helper.js b/cocos2d/animation/motion-path-helper.js new file mode 100644 index 00000000000..83bc7489890 --- /dev/null +++ b/cocos2d/animation/motion-path-helper.js @@ -0,0 +1,327 @@ +var DynamicAnimCurve = require('./animation-curves').DynamicAnimCurve; +var computeRatioByType = require('./animation-curves').computeRatioByType; + +var bezier = require('./bezier').bezier; +var bezierByTime = require('./bezier').bezierByTime; + +var binarySearch = require('./binary-search'); + +var v2 = cc.v2; + +function Curve (points) { + this.points = points || []; + this.beziers = []; + this.ratios = []; + this.progresses = []; + + this.length = 0; + + this.computeBeziers(); +} +Curve.prototype.computeBeziers = function () { + this.beziers.length = 0; + this.ratios.length = 0; + this.progresses.length = 0; + this.length = 0; + + for (var i = 1; i < this.points.length; i++) { + var startPoint = this.points[i - 1]; + var endPoint = this.points[i]; + var bezier = new Bezier(); + bezier.start = startPoint.pos; + bezier.startCtrlPoint = startPoint.out; + bezier.end = endPoint.pos; + bezier.endCtrlPoint = endPoint.in; + this.beziers.push(bezier); + + this.length += bezier.getLength(); + } + + var current = 0; + for (var i = 0; i < this.beziers.length; i++) { + var bezier = this.beziers[i]; + this.ratios[i] = bezier.getLength() / this.length; + this.progresses[i] = current = current + this.ratios[i]; + } + + return this.beziers; +}; + +function Bezier () { + this.start = v2(); + this.end = v2(); + this.startCtrlPoint = v2(); // cp0, cp1 + this.endCtrlPoint = v2(); // cp2, cp3 +} + +// Get point at relative position in curve according to arc length +// - u [0 .. 1] +Bezier.prototype.getPointAt = function ( u ) { + var t = this.getUtoTmapping( u ); + return this.getPoint( t ); +}; + + +// Get point at time t +// - t [0 .. 1] +Bezier.prototype.getPoint = function ( t ) { + var x = bezier(this.start.x, this.startCtrlPoint.x, this.endCtrlPoint.x, this.end.x, t); + var y = bezier(this.start.y, this.startCtrlPoint.y, this.endCtrlPoint.y, this.end.y, t); + + return new v2(x, y); +}; + +// Get total curve arc length +Bezier.prototype.getLength = function () { + + var lengths = this.getLengths(); + return lengths[ lengths.length - 1 ]; + +}; + +// Get list of cumulative segment lengths +Bezier.prototype.getLengths = function ( divisions ) { + + if ( ! divisions ) divisions = (this.__arcLengthDivisions) ? (this.__arcLengthDivisions): 200; + + if ( this.cacheArcLengths + && ( this.cacheArcLengths.length === divisions + 1 )) { + + //console.log( "cached", this.cacheArcLengths ); + return this.cacheArcLengths; + + } + + var cache = []; + var current, last = this.getPoint( 0 ); + var p, sum = 0; + + cache.push( 0 ); + + for ( p = 1; p <= divisions; p ++ ) { + + current = this.getPoint ( p / divisions ); + sum += cc.pDistance(current, last); + cache.push( sum ); + last = current; + + } + + this.cacheArcLengths = cache; + + return cache; // { sums: cache, sum:sum }; Sum is in the last element. +}; + +Bezier.prototype.getUtoTmapping = function ( u, distance ) { + + var arcLengths = this.getLengths(); + + var i = 0, il = arcLengths.length; + + var targetArcLength; // The targeted u distance value to get + + if ( distance ) { + targetArcLength = distance; + } else { + targetArcLength = u * arcLengths[ il - 1 ]; + } + + //var time = Date.now(); + + // binary search for the index with largest value smaller than target u distance + + var low = 0, high = il - 1, comparison; + + while ( low <= high ) { + + i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats + + comparison = arcLengths[ i ] - targetArcLength; + + if ( comparison < 0 ) { + + low = i + 1; + continue; + + } else if ( comparison > 0 ) { + + high = i - 1; + continue; + + } else { + + high = i; + break; + + // DONE + + } + + } + + i = high; + + //console.log('b' , i, low, high, Date.now()- time); + + if ( arcLengths[ i ] == targetArcLength ) { + + var t = i / ( il - 1 ); + return t; + + } + + // we could get finer grain at lengths, or use simple interpolatation between two points + + var lengthBefore = arcLengths[ i ]; + var lengthAfter = arcLengths[ i + 1 ]; + + var segmentLength = lengthAfter - lengthBefore; + + // determine where we are between the 'before' and 'after' points + + var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; + + // add that fractional amount to t + + var t = ( i + segmentFraction ) / ( il -1 ); + + return t; +}; + + +function sampleMotionPaths (motionPaths, data, duration, fps) { + + function createControlPoints(array) { + if (array instanceof cc.Vec2) { + return { + in: array, + pos: array, + out: array + }; + } + else if (Array.isArray(array) && array.length === 6) { + return { + in: v2(array[2], array[3]), + pos: v2(array[0], array[1]), + out: v2(array[4], array[5]) + }; + } + + return { + in: cc.Vec2.ZERO, + pos: cc.Vec2.ZERO, + out: cc.Vec2.ZERO + }; + } + + var values = data.values; + + values = values.map(function (value) { + return v2(value[0], value[1]); + }); + + var types = data.types; + var ratios = data.ratios; + + var newValues = data.values = []; + var newTypes = data.types = []; + var newRatios = data.ratios = []; + + function addNewDatas (value, type, ratio) { + newValues.push(value); + newTypes.push(type); + newRatios.push(ratio); + } + + // ensure every ratio section's length is the same + var startRatioOffset = 0; + + // from es6 Number.EPSILON + var EPSILON = 2.220446049250313e-16; + + // do not need to compute last path + for (var i = 0, l = motionPaths.length; i < l-1; i++) { + var motionPath = motionPaths[i]; + + var ratio = ratios[i]; + var nextRatio = ratios[i + 1]; + var betweenRatio = nextRatio - ratio; + + var value = values[i]; + var nextValue = values[i + 1]; + + var type = types[i]; + + var results = []; + var progress = startRatioOffset / betweenRatio; + var speed = 1 / (betweenRatio * duration * fps); + + if (motionPath) { + var points = []; + points.push(createControlPoints(value)); + + for (var j = 0, l2 = motionPath.length; j < l2; j++) { + var controlPoints = createControlPoints(motionPath[j]); + points.push(controlPoints); + } + + points.push(createControlPoints(nextValue)); + + // create Curve to compute beziers + var curve = new Curve(points); + curve.computeBeziers(); + + // sample beziers + var progresses = curve.progresses; + + while ( 1 - progress > EPSILON) { + var finalProgress = progress; + + finalProgress = computeRatioByType(finalProgress, type); + + var bezierIndex = binarySearch(progresses, finalProgress); + if (bezierIndex < 0) bezierIndex = ~bezierIndex; + + finalProgress -= bezierIndex > 0 ? progresses[bezierIndex - 1] : 0; + finalProgress = finalProgress / curve.ratios[bezierIndex]; + + var pos = curve.beziers[bezierIndex].getPointAt(finalProgress); + results.push(pos); + + progress += speed; + } + + } + else { + while ( 1 - progress > EPSILON) { + var finalProgress = progress; + + finalProgress = computeRatioByType(finalProgress, type); + + results.push(value.lerp(nextValue, finalProgress)); + + progress += speed; + } + } + + for (var j = 0, l2 = results.length; j < l2; j++) { + var newRatio = ratio + startRatioOffset + speed * j * betweenRatio; + addNewDatas(results[j], DynamicAnimCurve.Linear, newRatio); + } + + if (Math.abs(progress - 1) > EPSILON) // progress > 1 + startRatioOffset = (progress - 1) * betweenRatio; + else + startRatioOffset = 0; + } + + addNewDatas(values[values.length - 1], DynamicAnimCurve.Linear, 1); +} + +if (CC_TEST) { + cc._Test.sampleMotionPaths = sampleMotionPaths; +} + +module.exports = { + sampleMotionPaths: sampleMotionPaths +}; diff --git a/cocos2d/animation/playable.js b/cocos2d/animation/playable.js new file mode 100644 index 00000000000..7a3fed11c9f --- /dev/null +++ b/cocos2d/animation/playable.js @@ -0,0 +1,131 @@ +var JS = cc.js; + +/** + * @class Playable + * @constructor + */ +function Playable () { + this._isPlaying = false; + this._isPaused = false; + this._stepOnce = false; +} + +var prototype = Playable.prototype; + +/** + * Is playing or paused in play mode? + * @property isPlaying + * @type {boolean} + * @default false + * @readOnly + */ +JS.get(prototype, 'isPlaying', function () { + return this._isPlaying; +}, true); + +/** + * Is currently paused? This can be true even if in edit mode(isPlaying == false). + * @property isPaused + * @type {boolean} + * @default false + * @readOnly + */ +JS.get(prototype, 'isPaused', function () { + return this._isPaused; +}, true); + +// virtual + +var virtual = function () {}; +/** + * @method onPlay + * @private + */ +prototype.onPlay = virtual; +/** + * @method onPause + * @private + */ +prototype.onPause = virtual; +/** + * @method onResume + * @private + */ +prototype.onResume = virtual; +/** + * @method onStop + * @private + */ +prototype.onStop = virtual; +/** + * @method onError + * @param {string} errorCode + * @private + */ +prototype.onError = virtual; + +// public + +/** + * Play this animation + * @method play + */ +prototype.play = function () { + if (this._isPlaying) { + if (this._isPaused) { + this._isPaused = false; + this.onResume(); + } + else { + this.onError('already-playing'); + } + } + else { + this._isPlaying = true; + this.onPlay(); + } +}; + +/** + * Stop this animation + * @method stop + */ +prototype.stop = function () { + if (this._isPlaying) { + this._isPlaying = false; + this._isPaused = false; + this.onStop(); + } +}; + +/** + * Pause this animation + * @method pause + */ +prototype.pause = function () { + this._isPaused = true; + this.onPause(); +}; + +/** + * Resume this animation + * @method resume + */ +prototype.resume = function () { + this._isPaused = false; + this.onResume(); +}; + +/** + * Perform a single frame step. + * @method step + */ +prototype.step = function () { + this.pause(); + this._stepOnce = true; + if (!this._isPlaying) { + this.play(); + } +}; + +module.exports = Playable; diff --git a/cocos2d/animation/types.js b/cocos2d/animation/types.js new file mode 100644 index 00000000000..8d6ce64c123 --- /dev/null +++ b/cocos2d/animation/types.js @@ -0,0 +1,364 @@ +var JS = cc.js; +var Playable = require('./playable'); + +var WrapModeMask = { + Loop: 1 << 1, + ShouldWrap: 1 << 2, + // Reserved: 1 << 3, + PingPong: 1 << 4 | 1 << 1 | 1 << 2, // Loop, ShouldWrap + Reverse: 1 << 5 | 1 << 2, // ShouldWrap +}; + +/** + * Specifies how time is treated when it is outside of the keyframe range of an Animation. + * @enum WrapMode + * @memberof cc + */ +var WrapMode = cc.Enum({ + + /** + * !#en Reads the default wrap mode set higher up. + * !#zh å‘ Animation Component 或者 AnimationClip 查找 wrapMode + * @property {Number} Default + */ + Default: 0, + + /** + * !#en All iterations are played as specified. + * !#zh 动画åªæ’­æ”¾ä¸€é + * @property {Number} Normal + */ + Normal: 1, + + /** + * !#en All iterations are played in the reverse direction from the way they are specified. + * !#zh 从最åŽä¸€å¸§æˆ–结æŸä½ç½®å¼€å§‹åå‘播放,到第一帧或开始ä½ç½®åœæ­¢ + * @property {Number} Reverse + */ + Reverse: WrapModeMask.Reverse, + + /** + * !#en When time reaches the end of the animation, time will continue at the beginning. + * !#zh 循环播放 + * @property {Number} Loop + */ + Loop: WrapModeMask.Loop, + + /** + * !#en All iterations are played in the reverse direction from the way they are specified. + * And when time reaches the start of the animation, time will continue at the ending. + * !#zh åå‘循环播放 + * @property {Number} LoopReverse + */ + LoopReverse: WrapModeMask.Loop | WrapModeMask.Reverse, + + /** + * !#en Even iterations are played as specified, odd iterations are played in the reverse direction from the way they + * are specified. + * !#zh 从第一帧播放到最åŽä¸€å¸§ï¼Œç„¶åŽåå‘播放回第一帧,到第一帧åŽå†æ­£å‘播放,如此循环 + * @property {Number} PingPong + */ + PingPong: WrapModeMask.PingPong, + + /** + * !#en Even iterations are played in the reverse direction from the way they are specified, odd iterations are played + * as specified. + * !#zh 从最åŽä¸€å¸§å¼€å§‹åå‘æ’­æ”¾ï¼Œå…¶ä»–åŒ PingPong + * @property {Number} PingPongReverse + */ + PingPongReverse: WrapModeMask.PingPong | WrapModeMask.Reverse +}); + +cc.WrapMode = WrapMode; + + +/** + * The abstract interface for all playing animation. + * @class AnimationNodeBase + * @constructor + * @extends Playable + */ +var AnimationNodeBase = function () { + Playable.call(this); +}; +JS.extend(AnimationNodeBase, Playable); + +/** + * @method update + * @param deltaTime + * @private + */ +AnimationNodeBase.prototype.update = function (deltaTime) {}; + + +/** + * The collection and instance of playing animations created by entity.animate. + * @class AnimationNode + * @extends AnimationNodeBase + * @constructor + * @param {Animator} animator + * @param {AnimCurve[]} [curves] + * @param {Object} [timingInput] - This dictionary is used as a convenience for specifying the timing properties of an Animation in bulk. + */ +function AnimationNode (animator, curves, timingInput) { + AnimationNodeBase.call(this); + + this.animator = animator; + + /** + * @property curves + * @type {AnimCurve[]} + */ + this.curves = curves || []; + + // http://www.w3.org/TR/web-animations/#idl-def-AnimationTiming + + /** + * !#en The start delay which represents the number of seconds from an animation's start time to the start of + * the active interval. + * !#zh 延迟多少秒播放 + * + * @property delay + * @type {Number} + * @default 0 + */ + this.delay = 0; + + /** + * !#en The animation's iteration count property. + * + * A real number greater than or equal to zero (including positive infinity) representing the number of times + * to repeat the animation node. + * + * Values less than zero and NaN values are treated as the value 1.0 for the purpose of timing model + * calculations. + * + * !#zh 迭代次数, 指动画播放多少次åŽç»“æŸ, normalize time. 如 2.5 ( 2æ¬¡åŠ ) + * + * @property repeatCount + * @type {Number} + * @default 1 + */ + this.repeatCount = 1; + + /** + * !#en The iteration duration of this animation in seconds. (length) + * !#zh å•æ¬¡åŠ¨ç”»çš„æŒç»­æ—¶é—´, 秒 + * + * @property duration + * @type {Number} + * @readOnly + */ + this.duration = 1; + + /** + * !#en The animation's playback speed. 1 is normal playback speed. + * !#zh 播放速率 + * @property speed + * @type {Number} + * @default: 1.0 + */ + this.speed = 1; + + /** + * !#en Wrapping mode of the playing animation. + * !#zh åŠ¨ç”»å¾ªçŽ¯æ–¹å¼ + * + * @property wrapMode + * @type {WrapMode} + * @default: WrapMode.Normal + */ + this.wrapMode = WrapMode.Normal; + + if (timingInput) { + this.delay = timingInput.delay || this.delay; + + var duration = timingInput.duration; + if (typeof duration !== 'undefined') { + this.duration = duration; + } + + var speed = timingInput.speed; + if (typeof speed !== 'undefined') { + this.speed = speed; + } + + // + var wrapMode = timingInput.wrapMode; + if (typeof wrapMode !== 'undefined') { + var isEnum = typeof wrapMode === 'number'; + if (isEnum) { + this.wrapMode = wrapMode; + } + else { + this.wrapMode = WrapMode[wrapMode]; + } + } + + var repeatCount = timingInput.repeatCount; + if (typeof repeatCount !== 'undefined') { + this.repeatCount = repeatCount; + } + else if (this.wrapMode & WrapModeMask.Loop) { + this.repeatCount = Infinity; + } + } + + /** + * The current time of this animation in seconds. + * @property time + * @type {Number} + * @default 0 + */ + this.time = 0; + + this._timeNoScale = 0; + this._firstFramePlayed = false; + + this._duringDelay = false; + + // play + + if (this.delay > 0) { + this._duringDelay = true; + } +} +JS.extend(AnimationNode, AnimationNodeBase); + +JS.mixin(AnimationNode.prototype, { + + update: function (delta) { + + // calculate delay time + + if (this._duringDelay) { + this._timeNoScale += delta; + if (this._timeNoScale < this.delay) { + // still waiting + return; + } + else { + this._duringDelay = false; + } + //// start play + // delta -= (this._timeNoScale - this.delay); + } + + // make first frame perfect + + //var playPerfectFirstFrame = (this.time === 0); + if (this._firstFramePlayed) { + this.time += (delta * this.speed); + } + else { + this._firstFramePlayed = true; + } + + // sample + + if (this.sample()) { + this.stop(); + } + }, + + _needRevers: function (currentIterations) { + var wrapMode = this.wrapMode; + var needRevers = false; + + if ((wrapMode & WrapModeMask.PingPong) === WrapModeMask.PingPong) { + var isEnd = currentIterations - (currentIterations | 0) === 0; + if (isEnd && (currentIterations > 0)) { + currentIterations -= 1; + } + + var isOddIteration = currentIterations & 1; + if (isOddIteration) { + needRevers = !needRevers; + } + } + if ((wrapMode & WrapModeMask.Reverse) === WrapModeMask.Reverse) { + needRevers = !needRevers; + } + return needRevers; + }, + + getWrappedInfo: function (time) { + var stopped = false; + var duration = this.duration; + var ratio = 0; + var wrapMode = this.wrapMode; + + var currentIterations = Math.abs(time / duration); + if (currentIterations > this.repeatCount) currentIterations = this.repeatCount; + + var needRevers = false; + if (wrapMode & WrapModeMask.ShouldWrap) { + needRevers = this._needRevers(currentIterations); + } + + var direction = needRevers ? -1 : 1; + if (this.speed < 0) direction *= -1; + + if (currentIterations >= this.repeatCount) { + stopped = true; + var tempRatio = this.repeatCount - (this.repeatCount | 0); + if (tempRatio === 0) { + tempRatio = 1; // 如果播放过,动画ä¸å¤ä½ + } + time = tempRatio * duration * (time > 0 ? 1 : -1); + } + + if (time > duration) { + var tempTime = time % duration; + time = tempTime === 0 ? duration : tempTime; + } + else if (time < 0) { + time = time % duration; + if (time !== 0 ) time += duration; + } + + // calculate wrapped time + if (wrapMode & WrapModeMask.ShouldWrap) { + if (needRevers) time = duration - time; + } + + ratio = time / duration; + + return { + ratio: ratio, + time: time, + direction: direction, + stopped: stopped, + iterations: currentIterations + } + }, + + sample: function () { + + // sample + + var info = this.getWrappedInfo(this.time); + + var curves = this.curves; + for (var i = 0, len = curves.length; i < len; i++) { + var curve = curves[i]; + curve.sample(info.time, info.ratio, this); + } + + return info.stopped; + } + + //onPlay: function () { + //}, + // + //onStop: function () { + //} +}); + +cc.AnimationNode = AnimationNode; + +module.exports = { + WrapModeMask: WrapModeMask, + WrapMode: WrapMode, + AnimationNode: AnimationNode, +}; diff --git a/cocos2d/audio/CCAudio.js b/cocos2d/audio/CCAudio.js new file mode 100644 index 00000000000..acdc4632f53 --- /dev/null +++ b/cocos2d/audio/CCAudio.js @@ -0,0 +1,1033 @@ +/**************************************************************************** + 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 : WebAudio loop attribute failure, need to manually perform loop + * + * 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 supportTable = { + "common" : {MULTI_CHANNEL: true , WEB_AUDIO: supportWebAudio , AUTOPLAY: true } + }; + supportTable[sys.BROWSER_TYPE_IE] = {MULTI_CHANNEL: true , WEB_AUDIO: supportWebAudio , AUTOPLAY: true, USE_EMPTIED_EVENT: true}; + // ANDROID // + supportTable[sys.BROWSER_TYPE_ANDROID] = {MULTI_CHANNEL: false, WEB_AUDIO: false, AUTOPLAY: false}; + supportTable[sys.BROWSER_TYPE_CHROME] = {MULTI_CHANNEL: true , WEB_AUDIO: true , AUTOPLAY: false}; + supportTable[sys.BROWSER_TYPE_FIREFOX] = {MULTI_CHANNEL: true , WEB_AUDIO: true , AUTOPLAY: true , DELAY_CREATE_CTX: true}; + supportTable[sys.BROWSER_TYPE_UC] = {MULTI_CHANNEL: true , WEB_AUDIO: false, AUTOPLAY: false}; + supportTable[sys.BROWSER_TYPE_QQ] = {MULTI_CHANNEL: false, WEB_AUDIO: false, AUTOPLAY: true }; + supportTable[sys.BROWSER_TYPE_OUPENG] = {MULTI_CHANNEL: false, WEB_AUDIO: false, AUTOPLAY: false, REPLAY_AFTER_TOUCH: true , USE_EMPTIED_EVENT: true }; + supportTable[sys.BROWSER_TYPE_WECHAT] = {MULTI_CHANNEL: false, WEB_AUDIO: false, AUTOPLAY: false, REPLAY_AFTER_TOUCH: true , USE_EMPTIED_EVENT: true }; + supportTable[sys.BROWSER_TYPE_360] = {MULTI_CHANNEL: false, WEB_AUDIO: false, AUTOPLAY: true }; + supportTable[sys.BROWSER_TYPE_MIUI] = {MULTI_CHANNEL: false, WEB_AUDIO: false, AUTOPLAY: true }; + supportTable[sys.BROWSER_TYPE_LIEBAO] = {MULTI_CHANNEL: false, WEB_AUDIO: false, AUTOPLAY: false, REPLAY_AFTER_TOUCH: true , USE_EMPTIED_EVENT: true }; + supportTable[sys.BROWSER_TYPE_SOUGOU] = {MULTI_CHANNEL: false, WEB_AUDIO: false, AUTOPLAY: false, REPLAY_AFTER_TOUCH: true , USE_EMPTIED_EVENT: true }; + //"Baidu" browser can automatically play + //But because it may be play failed, so need to replay and auto + supportTable[sys.BROWSER_TYPE_BAIDU] = {MULTI_CHANNEL: false, WEB_AUDIO: false, AUTOPLAY: false, REPLAY_AFTER_TOUCH: true , USE_EMPTIED_EVENT: true }; + supportTable[sys.BROWSER_TYPE_BAIDU_APP]= {MULTI_CHANNEL: false, WEB_AUDIO: false, AUTOPLAY: false, REPLAY_AFTER_TOUCH: true , USE_EMPTIED_EVENT: true }; + + // APPLE // + supportTable[sys.BROWSER_TYPE_SAFARI] = {MULTI_CHANNEL: true , WEB_AUDIO: true , AUTOPLAY: false, webAudioCallback: function(realUrl){ + document.createElement("audio").src = realUrl; + }}; + + if(cc.sys.isMobile){ + if(cc.sys.os !== cc.sys.OS_IOS) + window.__audioSupport = supportTable[sys.browserType] || supportTable["common"]; + else + window.__audioSupport = supportTable[sys.BROWSER_TYPE_SAFARI]; + }else{ + switch(sys.browserType){ + case sys.BROWSER_TYPE_IE: + window.__audioSupport = supportTable[sys.BROWSER_TYPE_IE]; + break; + case sys.BROWSER_TYPE_FIREFOX: + window.__audioSupport = supportTable[sys.BROWSER_TYPE_FIREFOX]; + break; + default: + window.__audioSupport = supportTable["common"]; + } + } + + /////////////////////////// + // Browser compatibility// + /////////////////////////// + if(version){ + switch(sys.browserType){ + case sys.BROWSER_TYPE_CHROME: + version = parseInt(version); + if(version < 30){ + window.__audioSupport = {MULTI_CHANNEL: false , WEB_AUDIO: true , AUTOPLAY: false}; + }else if(version === 42){ + window.__audioSupport.NEED_MANUAL_LOOP = true; + } + break; + case sys.BROWSER_TYPE_MIUI: + if(cc.sys.isMobile){ + version = version.match(/\d+/g); + if(version[0] < 2 || (version[0] === 2 && version[1] === 0 && version[2] <= 1)){ + window.__audioSupport.AUTOPLAY = false; + } + } + break; + } + } + + 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({ + //TODO Maybe loader shift in will be better + volume: 1, + loop: false, + src: null, + _touch: false, + + _playing: false, + _AUDIO_TYPE: "AUDIO", + _pause: false, + + //Web Audio + _buffer: null, + _currentSource: null, + _startTime: null, + _currentTime: null, + _context: null, + _volume: null, + + _ignoreEnded: false, + _manualLoop: false, + + //DOM Audio + _element: null, + + ctor: function(context, volume, url){ + context && (this._context = context); + volume && (this._volume = volume); + if(context && volume){ + this._AUDIO_TYPE = "WEBAUDIO"; + } + this.src = url; + }, + + _setBufferCallback: null, + setBuffer: function(buffer){ + if(!buffer) return; + var playing = this._playing; + this._AUDIO_TYPE = "WEBAUDIO"; + + if(this._buffer && this._buffer !== buffer && this.getPlaying()) + this.stop(); + + this._buffer = buffer; + if(playing) + this.play(); + + this._volume["gain"].value = this.volume; + this._setBufferCallback && this._setBufferCallback(buffer); + }, + + _setElementCallback: null, + setElement: function(element){ + if(!element) return; + var playing = this._playing; + this._AUDIO_TYPE = "AUDIO"; + + if(this._element && this._element !== element && this.getPlaying()) + this.stop(); + + this._element = element; + if(playing) + this.play(); + + element.volume = this.volume; + element.loop = this.loop; + this._setElementCallback && this._setElementCallback(element); + }, + + play: function(offset, loop){ + this._playing = true; + this.loop = loop === undefined ? this.loop : loop; + if(this._AUDIO_TYPE === "AUDIO"){ + this._playOfAudio(offset); + }else{ + this._playOfWebAudio(offset); + } + }, + + getPlaying: function(){ + if(!this._playing){ + return false; + } + if(this._AUDIO_TYPE === "AUDIO"){ + var audio = this._element; + if(!audio || this._pause || audio.ended){ + return this._playing = false; + } + return true; + } + var sourceNode = this._currentSource; + if(!sourceNode || !sourceNode["playbackState"]) + return true; + return this._currentTime + this._context.currentTime - this._startTime < sourceNode.buffer.duration; + }, + + _playOfWebAudio: function(offset){ + var cs = this._currentSource; + if(!this._buffer){ + return; + } + if(!this._pause && cs){ + if(this._context.currentTime === 0 || this._currentTime + this._context.currentTime - this._startTime > cs.buffer.duration) + this._stopOfWebAudio(); + else + return; + } + var audio = this._context["createBufferSource"](); + audio.buffer = this._buffer; + audio["connect"](this._volume); + if(this._manualLoop) + audio.loop = false; + else + audio.loop = this.loop; + this._startTime = this._context.currentTime; + this._currentTime = offset || 0; + this._ignoreEnded = false; + + /* + * Safari on iOS 6 only supports noteOn(), noteGrainOn(), and noteOff() now.(iOS 6.1.3) + * The latest version of chrome has supported start() and stop() + * start() & stop() are specified in the latest specification (written on 04/26/2013) + * Reference: https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html + * noteOn(), noteGrainOn(), and noteOff() are specified in Draft 13 version (03/13/2012) + * Reference: http://www.w3.org/2011/audio/drafts/2WD/Overview.html + */ + if(audio.start){ + audio.start(0, offset || 0); + }else if(audio["noteGrainOn"]){ + var duration = audio.buffer.duration; + if (this.loop) { + /* + * On Safari on iOS 6, if loop == true, the passed in @param duration will be the duration from now on. + * In other words, the sound will keep playing the rest of the music all the time. + * On latest chrome desktop version, the passed in duration will only be the duration in this cycle. + * Now that latest chrome would have start() method, it is prepared for iOS here. + */ + audio["noteGrainOn"](0, offset, duration); + } else { + audio["noteGrainOn"](0, offset, duration - offset); + } + }else { + // if only noteOn() is supported, resuming sound will NOT work + audio["noteOn"](0); + } + this._currentSource = audio; + var self = this; + audio["onended"] = function(){ + if(self._manualLoop && self._playing && self.loop){ + self.stop(); + self.play(); + return; + } + if(self._ignoreEnded){ + self._ignoreEnded = false; + }else{ + if(!self._pause) + self.stop(); + else + self._playing = false; + } + }; + }, + + _playOfAudio: function(){ + var audio = this._element; + if(audio){ + audio.loop = this.loop; + audio.play(); + } + }, + + stop: function(){ + this._playing = false; + if(this._AUDIO_TYPE === "AUDIO"){ + this._stopOfAudio(); + }else{ + this._stopOfWebAudio(); + } + }, + + _stopOfWebAudio: function(){ + var audio = this._currentSource; + this._ignoreEnded = true; + if(audio){ + audio.stop(0); + this._currentSource = null; + } + }, + + _stopOfAudio: function(){ + var audio = this._element; + if(audio){ + audio.pause(); + if (audio.duration && audio.duration !== Infinity) + audio.currentTime = 0; + } + }, + + pause: function(){ + if(this.getPlaying() === false) + return; + this._playing = false; + this._pause = true; + if(this._AUDIO_TYPE === "AUDIO"){ + this._pauseOfAudio(); + }else{ + this._pauseOfWebAudio(); + } + }, + + _pauseOfWebAudio: function(){ + this._currentTime += this._context.currentTime - this._startTime; + var audio = this._currentSource; + if(audio){ + audio.stop(0); + } + }, + + _pauseOfAudio: function(){ + var audio = this._element; + if(audio){ + audio.pause(); + } + }, + + resume: function(){ + if(this._pause){ + if(this._AUDIO_TYPE === "AUDIO"){ + this._resumeOfAudio(); + }else{ + this._resumeOfWebAudio(); + } + this._pause = false; + this._playing = true; + } + }, + + _resumeOfWebAudio: function(){ + var audio = this._currentSource; + if(audio){ + this._startTime = this._context.currentTime; + var offset = this._currentTime % audio.buffer.duration; + this._playOfWebAudio(offset); + } + }, + + _resumeOfAudio: function(){ + var audio = this._element; + if(audio){ + audio.play(); + } + }, + + setVolume: function(volume){ + if(volume > 1) volume = 1; + if(volume < 0) volume = 0; + this.volume = volume; + if(this._AUDIO_TYPE === "AUDIO"){ + if(this._element){ + this._element.volume = volume; + } + }else{ + if(this._volume){ + this._volume["gain"].value = volume; + } + } + }, + + getVolume: function(){ + return this.volume; + }, + + cloneNode: function(){ + var audio, self; + if(this._AUDIO_TYPE === "AUDIO"){ + audio = new cc.Audio(); + + var elem = document.createElement("audio"); + elem.src = this.src; + audio.setElement(elem); + }else{ + var volume = this._context["createGain"](); + volume["gain"].value = 1; + volume["connect"](this._context["destination"]); + audio = new cc.Audio(this._context, volume, this.src); + if(this._buffer){ + audio.setBuffer(this._buffer); + }else{ + self = this; + this._setBufferCallback = function(buffer){ + audio.setBuffer(buffer); + self._setBufferCallback = null; + }; + } + audio._manualLoop = this._manualLoop; + } + audio._AUDIO_TYPE = this._AUDIO_TYPE; + return audio; + } + +}); + +(function(polyfill){ + + var SWA = polyfill.WEB_AUDIO, + SWB = polyfill.MULTI_CHANNEL, + SWC = polyfill.AUTOPLAY; + + 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)(); + if(polyfill.DELAY_CREATE_CTX) + setTimeout(function(){ context = new (window.AudioContext || window.webkitAudioContext || window.mozAudioContext)(); }, 0); + } + }catch(error){ + SWA = false; + cc.log("browser don't support web audio"); + } + + var loader = { + + cache: {}, + + load: function(realUrl, url, res, cb){ + + if(support.length === 0) + return cb("can not support audio!"); + + var i; + + if(cc.loader.audioPath) + realUrl = cc.path.join(cc.loader.audioPath, realUrl); + + var extname = cc.path.extname(realUrl); + + var typeList = [extname]; + for(i=0; itrue 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.src !== url && bgMusic.getPlaying()){ + bgMusic.stop(); + } + var audio = loader.cache[url]; + if(!audio){ + cc.loader.load(url); + audio = loader.cache[url]; + } + audio.play(0, loop); + audio.setVolume(this._musicVolume); + + 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){ + audio.stop(); + 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: 5, + _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 the browser just support playing single audio + if(!SWB){ + //Must be forced to shut down + //Because playing MULTI_CHANNEL audio will be stuck in chrome 28 (android) + return null; + } + + var effectList = this._audioPool[url]; + if(!effectList){ + effectList = this._audioPool[url] = []; + } + + var i; + + for(i=0; i this._maxAudioInstance){ + cc.log("Error: %s greater than %d", url, this._maxAudioInstance); + }else{ + var audio = loader.cache[url]; + if(!audio){ + cc.loader.load(url); + audio = loader.cache[url]; + } + audio = audio.cloneNode(); + audio.setVolume(this._effectVolume); + audio.loop = loop || false; + audio.play(); + effectList.push(audio); + } + + 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 + * 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# */{ + alphaThreshold: 0, + inverted: false, + + _stencil: null, + _className: "ClippingNode", + + /** + * 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; + this.alphaThreshold = 1; + this.inverted = false; + this._renderCmd.initStencilBits(); + }, + + /** + * Initialization of the node, please do not call this function by yourself, you should pass the parameters to constructor to initialize it. + * @function + * @param {cc.Node} [stencil=null] + */ + init: function (stencil) { + this._stencil = stencil; + this.alphaThreshold = 1; + this.inverted = false; + this._renderCmd.initStencilBits(); + return true; + }, + + /** + *

+ * 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); + this._stencil.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); + this._stencil.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.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.onExit(); + cc.Node.prototype.onExit.call(this); + }, + + /** + *

+ * 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) { + 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; + 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 +cc.defineGetterSetter(_p, "stencil", _p.getStencil, _p.setStencil); +/** @expose */ +_p.stencil; + +/** + * 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/cocos2d/clipping-nodes/CCClippingNodeCanvasRenderCmd.js b/cocos2d/clipping-nodes/CCClippingNodeCanvasRenderCmd.js new file mode 100644 index 00000000000..5c60928ab05 --- /dev/null +++ b/cocos2d/clipping-nodes/CCClippingNodeCanvasRenderCmd.js @@ -0,0 +1,215 @@ +/**************************************************************************** + 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){ + cc.Node.CanvasRenderCmd.call(this, 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.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 0; j--) + context.lineTo(vertices[j].x * scaleX, -vertices[j].y * scaleY); + } + }; + }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(); + context.beginPath(); //save for clip + //Because drawNode's content size is zero + wrapper.setTransform(this._worldTransform, scaleX, scaleY); + + if (this._node.inverted) { + 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 { + 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){ + cc.Node.CanvasRenderCmd.prototype.transform.call(this, parentCmd, recursive); + var node = this._node; + if(node._stencil && node._stencil._renderCmd) + node._stencil._renderCmd.transform(this, recursive); + }; + + proto._cangodhelpme = function (godhelpme) { + if (godhelpme === true || godhelpme === false) + cc.ClippingNode.CanvasRenderCmd.prototype._godhelpme = godhelpme; + return cc.ClippingNode.CanvasRenderCmd.prototype._godhelpme; + }; + + 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 transformRenderCmd = this; + + // 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) + cc.Node.CanvasRenderCmd.prototype.visit.call(this, parentCmd); // draw everything + return; + } + + this._syncStatus(parentCmd); + cc.renderer.pushRenderCommand(this._rendererSaveCmd); + if(this._clipElemType){ + // Draw everything first using node visit function + cc.Node.CanvasRenderCmd.prototype.visit.call(this, parentCmd); + }else{ + node._stencil.visit(this); + } + cc.renderer.pushRenderCommand(this._rendererClipCmd); + + if(this._clipElemType){ + node._stencil.visit(transformRenderCmd); + }else{ + var i, children = node._children; + // 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 len = children.length; + if (len > 0) { + node.sortAllChildren(); + for (i = 0; i < len; i++) + children[i]._renderCmd.visit(this); + } + 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")); + }; +})(); \ No newline at end of file diff --git a/cocos2d/clipping-nodes/CCClippingNodeWebGLRenderCmd.js b/cocos2d/clipping-nodes/CCClippingNodeWebGLRenderCmd.js new file mode 100644 index 00000000000..0e199ff3b78 --- /dev/null +++ b/cocos2d/clipping-nodes/CCClippingNodeWebGLRenderCmd.js @@ -0,0 +1,238 @@ +/**************************************************************************** + 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 WebGL render cmd ------------------------------ +(function(){ + cc.ClippingNode.WebGLRenderCmd = function(renderable){ + cc.Node.WebGLRenderCmd.call(this, 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._currentStencilFunc = null; + this._currentStencilRef = null; + this._currentStencilValueMask = null; + this._currentStencilFail = null; + this._currentStencilPassDepthFail = null; + this._currentStencilPassDepthPass = null; + this._currentStencilWriteMask = null; + this._currentStencilEnabled = null; + this._currentDepthWriteMask = 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; + cc.Node.WebGLRenderCmd.prototype.transform.call(this, parentCmd, recursive); + if(node._stencil) + node._stencil._renderCmd.transform(this, recursive); + }; + + proto.visit = function(parentCmd){ + var node = this._node; + // quick return if not visible + if (!node._visible) + return; + + if( node._parent && node._parent._renderCmd) + this._curLevel = node._parent._renderCmd._curLevel + 1; + + // if stencil buffer disabled + if (cc.stencilBits < 1) { + // draw everything, as if there where no stencil + cc.Node.WebGLRenderCmd.prototype.visit.call(this, parentCmd); + return; + } + + if (!node._stencil || !node._stencil.visible) { + if (node.inverted) + cc.Node.WebGLRenderCmd.prototype.visit.call(this, parentCmd); // 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 where no stencil + cc.Node.WebGLRenderCmd.prototype.visit.call(this, parentCmd); + return; + } + + cc.renderer.pushRenderCommand(this._beforeVisitCmd); + + //optimize performance for javascript + var currentStack = cc.current_stack; + currentStack.stack.push(currentStack.top); + this._syncStatus(parentCmd); + currentStack.top = this._stackMatrix; + + //this._stencil._stackMatrix = this._stackMatrix; + node._stencil._renderCmd.visit(this); + + 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]._renderCmd.visit(this); + } + } + + cc.renderer.pushRenderCommand(this._afterVisitCmd); + + this._dirtyFlag = 0; + //optimize performance for javascript + currentStack.top = currentStack.stack.pop(); + }; + + 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._drawFullScreenQuadClearStencil = function () { + // draw a fullscreen solid rectangle to clear the stencil buffer + var projStack = cc.projection_matrix_stack; + //cc.kmGLMatrixMode(cc.KM_GL_PROJECTION); + //cc.kmGLPushMatrix(); + //cc.kmGLLoadIdentity(); + projStack.push(); + projStack.top.identity(); + + //cc.kmGLMatrixMode(cc.KM_GL_MODELVIEW); + //cc.kmGLPushMatrix(); + //cc.kmGLLoadIdentity(); + var modelViewStack = cc.modelview_matrix_stack; + modelViewStack.push(); + modelViewStack.top.identity(); + + cc._drawingUtil.drawSolidRect(cc.p(-1, -1), cc.p(1, 1), cc.color(255, 255, 255, 255)); + + //cc.kmGLMatrixMode(cc.KM_GL_PROJECTION); + //cc.kmGLPopMatrix(); + projStack.pop(); + + //cc.kmGLMatrixMode(cc.KM_GL_MODELVIEW); + //cc.kmGLPopMatrix(); + modelViewStack.pop(); + }; + + 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); + this._currentStencilWriteMask = gl.getParameter(gl.STENCIL_WRITEMASK); + this._currentStencilFunc = gl.getParameter(gl.STENCIL_FUNC); + this._currentStencilRef = gl.getParameter(gl.STENCIL_REF); + this._currentStencilValueMask = gl.getParameter(gl.STENCIL_VALUE_MASK); + this._currentStencilFail = gl.getParameter(gl.STENCIL_FAIL); + this._currentStencilPassDepthFail = gl.getParameter(gl.STENCIL_PASS_DEPTH_FAIL); + this._currentStencilPassDepthPass = gl.getParameter(gl.STENCIL_PASS_DEPTH_PASS); + + // enable stencil use + gl.enable(gl.STENCIL_TEST); + gl.stencilMask(mask_layer); + this._currentDepthWriteMask = gl.getParameter(gl.DEPTH_WRITEMASK); + + gl.depthMask(false); + + gl.stencilFunc(gl.NEVER, mask_layer, mask_layer); + gl.stencilOp(!node.inverted ? gl.ZERO : gl.REPLACE, gl.KEEP, gl.KEEP); + + this._drawFullScreenQuadClearStencil(); + + gl.stencilFunc(gl.NEVER, mask_layer, mask_layer); + gl.stencilOp(!node.inverted ? gl.REPLACE : gl.ZERO, gl.KEEP, gl.KEEP); + + if (node.alphaThreshold < 1) { //TODO desktop + var program = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLORALPHATEST); + var alphaValueLocation = gl.getUniformLocation(program.getProgram(), cc.UNIFORM_ALPHA_TEST_VALUE_S); + // set our alphaThreshold + cc.glUseProgram(program.getProgram()); + program.setUniformLocationWith1f(alphaValueLocation, node.alphaThreshold); + cc.setProgram(node._stencil, program); + } + }; + + proto._onAfterDrawStencil = function(ctx){ + var gl = ctx || cc._renderContext; + gl.depthMask(this._currentDepthWriteMask); + + gl.stencilFunc(gl.EQUAL, 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; + + gl.stencilFunc(this._currentStencilFunc, this._currentStencilRef, this._currentStencilValueMask); + gl.stencilOp(this._currentStencilFail, this._currentStencilPassDepthFail, this._currentStencilPassDepthPass); + gl.stencilMask(this._currentStencilWriteMask); + if (!this._currentStencilEnabled) + gl.disable(gl.STENCIL_TEST); + + // we are done using this layer, decrement + cc.ClippingNode.WebGLRenderCmd._layer--; + } +})(); diff --git a/cocos2d/compression/ZipUtils.js b/cocos2d/compression/ZipUtils.js new file mode 100644 index 00000000000..ccecd63f508 --- /dev/null +++ b/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/cocos2d/compression/base64.js b/cocos2d/compression/base64.js new file mode 100644 index 00000000000..adb0d688714 --- /dev/null +++ b/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/cocos2d/compression/gzip.js b/cocos2d/compression/gzip.js new file mode 100644 index 00000000000..baed633cef5 --- /dev/null +++ b/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/cocos2d/compression/zlib.min.js b/cocos2d/compression/zlib.min.js new file mode 100644 index 00000000000..8d0537f7bd8 --- /dev/null +++ b/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 ActionManager + * @extends _Class + * @example {@link utils/api/cocos/docs/cocos2d/core/CCActionManager/ActionManager.js} + */ +cc.ActionManager = cc._Class.extend(/** @lends cc.ActionManager# */{ + _hashTargets:null, + _arrayTargets:null, + _currentTarget:null, + _currentTargetSalvaged:false, + + _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; + this._currentTargetSalvaged = false; + }, + + /** 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(): action must be non-null"); + + //check if the action target already exists + var element = this._hashTargets[target.__instanceId]; + //if doesnt exists, create a hashelement and push in mpTargets + if (!element) { + element = new cc.HashElement(); + element.paused = paused; + element.target = target; + this._hashTargets[target.__instanceId] = element; + this._arrayTargets.push(element); + } + //creates a array for that eleemnt to hold the actions + this._actionAllocWithHashElement(element); + + 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) { + if (element.actions.indexOf(element.currentAction) !== -1 && !(element.currentActionSalvaged)) + element.currentActionSalvaged = true; + + element.actions.length = 0; + if (this._currentTarget === element && !forceDelete) { + this._currentTargetSalvaged = true; + } else { + 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); + 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]; + + if ((action === element.currentAction) && (!element.currentActionSalvaged)) + element.currentActionSalvaged = true; + + 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) { + if (this._currentTarget === element) { + this._currentTargetSalvaged = true; + } else { + this._deleteHashElement(element); + } + } + }, + + _deleteHashElement:function (element) { + var ret = false; + if (element) { + if(this._hashTargets[element.target.__instanceId]){ + delete this._hashTargets[element.target.__instanceId]; + cc.js.array.remove(this._arrayTargets, element); + ret = true; + } + element.actions = null; + element.target = null; + } + return ret; + }, + + _actionAllocWithHashElement:function (element) { + // 4 actions per Node by default + if (element.actions == null) { + element.actions = []; + } + }, + + /** + * @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; + //this._currentTargetSalvaged = false; + if (!locCurrTarget.paused) { + // The 'actions' CCMutableArray may change while inside this loop. + for (locCurrTarget.actionIndex = 0; + locCurrTarget.actionIndex < (locCurrTarget.actions ? locCurrTarget.actions.length : 0); + locCurrTarget.actionIndex++) { + locCurrTarget.currentAction = locCurrTarget.actions[locCurrTarget.actionIndex]; + if (!locCurrTarget.currentAction) + continue; + + locCurrTarget.currentActionSalvaged = false; + //use for speed + locCurrTarget.currentAction.step(dt * ( locCurrTarget.currentAction._speedMethod ? locCurrTarget.currentAction._speed : 1 ) ); + if (locCurrTarget.currentActionSalvaged) { + // The currentAction told the node to remove it. To prevent the action from + // accidentally deallocating itself before finishing its step, we retained + // it. Now that step is done, it's safe to release it. + locCurrTarget.currentAction = null;//release + } else if (locCurrTarget.currentAction.isDone()) { + locCurrTarget.currentAction.stop(); + var action = locCurrTarget.currentAction; + // Make currentAction nil to prevent removeAction from salvaging it. + locCurrTarget.currentAction = null; + this.removeAction(action); + } + + locCurrTarget.currentAction = null; + } + } + + // elt, at this moment, is still valid + // so it is safe to ask this here (issue #490) + + // only delete currentTarget if no actions were scheduled during the cycle (issue #481) + if (this._currentTargetSalvaged && locCurrTarget.actions.length === 0) { + this._deleteHashElement(locCurrTarget) && elt--; + } + } + } +}); diff --git a/cocos2d/core/CCCamera.js b/cocos2d/core/CCCamera.js new file mode 100644 index 00000000000..204e2665042 --- /dev/null +++ b/cocos2d/core/CCCamera.js @@ -0,0 +1,282 @@ +/**************************************************************************** + 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 CCCamera is used in every CCNode.
+ * The OpenGL gluLookAt() function is used to locate the camera.
+ *
+ * If the object is transformed by any of the scale, rotation or position attributes, then they will override the camera.
+ *
+ * IMPORTANT: Either your use the camera or the rotation/scale/position properties. You can't use both.
+ * World coordinates won't work if you use the camera.
+ *
+ * Limitations:
+ * - Some nodes, like CCParallaxNode, CCParticle uses world node coordinates, and they won't work properly if you move them (or any of their ancestors)
+ * using the camera.
+ *
+ * - It doesn't work on batched nodes like CCSprite objects when they are parented to a CCSpriteBatchNode object.
+ *
+ * - It is recommended to use it ONLY if you are going to create 3D effects. For 2D effecs, use the action CCFollow or position/scale/rotate. * + *

+ */ +cc.Camera = cc._Class.extend({ + _eyeX:null, + _eyeY:null, + _eyeZ:null, + + _centerX:null, + _centerY:null, + _centerZ:null, + + _upX:null, + _upY:null, + _upZ:null, + + _dirty:false, + _lookupMatrix:null, + /** + * constructor of cc.Camera + */ + ctor:function () { + this._lookupMatrix = new cc.math.Matrix4(); + this.restore(); + }, + + /** + * Description of cc.Camera + * @return {String} + */ + description:function () { + return ""; + }, + + /** + * sets the dirty value + * @param value + */ + setDirty:function (value) { + this._dirty = value; + }, + + /** + * get the dirty value + * @return {Boolean} + */ + isDirty:function () { + return this._dirty; + }, + + /** + * sets the camera in the default position + */ + restore:function () { + this._eyeX = this._eyeY = 0.0; + this._eyeZ = cc.Camera.getZEye(); + + this._centerX = this._centerY = this._centerZ = 0.0; + + this._upX = 0.0; + this._upY = 1.0; + this._upZ = 0.0; + + this._lookupMatrix.identity(); + + this._dirty = false; + }, + + /** + * Sets the camera using gluLookAt using its eye, center and up_vector + */ + locate:function () { + if (this._dirty) { + var eye = new cc.math.Vec3(this._eyeX, this._eyeY , this._eyeZ), + center = new cc.math.Vec3(this._centerX, this._centerY, this._centerZ), + up = new cc.math.Vec3(this._upX, this._upY, this._upZ); + this._lookupMatrix.lookAt(eye, center, up); + this._dirty = false; + } + cc.kmGLMultMatrix( this._lookupMatrix); + }, + + _locateForRenderer: function(matrix){ + if (this._dirty) { + var eye = new cc.math.Vec3(this._eyeX, this._eyeY , this._eyeZ), + center = new cc.math.Vec3(this._centerX, this._centerY, this._centerZ), + up = new cc.math.Vec3(this._upX, this._upY, this._upZ); + this._lookupMatrix.lookAt(eye, center, up); + this._dirty = false; + } + matrix.multiply(this._lookupMatrix); + }, + + /** + * sets the eye values in points + * @param {Number} eyeX + * @param {Number} eyeY + * @param {Number} eyeZ + * @deprecated This function will be deprecated sooner or later please use setEye instead. + */ + setEyeXYZ:function (eyeX, eyeY, eyeZ) { + this.setEye(eyeX,eyeY,eyeZ); + }, + + /** + * sets the eye values in points + * @param {Number} eyeX + * @param {Number} eyeY + * @param {Number} eyeZ + */ + setEye:function (eyeX, eyeY, eyeZ) { + this._eyeX = eyeX ; + this._eyeY = eyeY ; + this._eyeZ = eyeZ ; + + this._dirty = true; + }, + + /** + * sets the center values in points + * @param {Number} centerX + * @param {Number} centerY + * @param {Number} centerZ + * @deprecated This function will be deprecated sooner or later please use setCenter instead. + */ + setCenterXYZ:function (centerX, centerY, centerZ) { + this.setCenter(centerX,centerY,centerZ); + }, + + /** + * sets the center values in points + * @param {Number} centerX + * @param {Number} centerY + * @param {Number} centerZ + */ + setCenter:function (centerX, centerY, centerZ) { + this._centerX = centerX ; + this._centerY = centerY ; + this._centerZ = centerZ ; + + this._dirty = true; + }, + + /** + * sets the up values + * @param {Number} upX + * @param {Number} upY + * @param {Number} upZ + * @deprecated This function will be deprecated sooner or later. + */ + setUpXYZ:function (upX, upY, upZ) { + this.setUp(upX, upY, upZ); + }, + + /** + * sets the up values + * @param {Number} upX + * @param {Number} upY + * @param {Number} upZ + */ + setUp:function (upX, upY, upZ) { + this._upX = upX; + this._upY = upY; + this._upZ = upZ; + + this._dirty = true; + }, + + /** + * get the eye vector values in points (return an object like {x:1,y:1,z:1} in HTML5) + * @param {Number} eyeX + * @param {Number} eyeY + * @param {Number} eyeZ + * @return {Object} + * @deprecated This function will be deprecated sooner or later, please use getEye instead. + */ + getEyeXYZ:function (eyeX, eyeY, eyeZ) { + return {x:this._eyeX , y:this._eyeY , z: this._eyeZ }; + }, + + /** + * get the eye vector values in points (return an object like {x:1,y:1,z:1} in HTML5) + * @return {Object} + */ + getEye:function () { + return {x:this._eyeX , y:this._eyeY , z: this._eyeZ }; + }, + + /** + * get the center vector values int points (return an object like {x:1,y:1,z:1} in HTML5) + * @param {Number} centerX + * @param {Number} centerY + * @param {Number} centerZ + * @return {Object} + * @deprecated This function will be deprecated sooner or later,please use getCenter instead. + */ + getCenterXYZ:function (centerX, centerY, centerZ) { + return {x:this._centerX ,y:this._centerY ,z:this._centerZ }; + }, + + /** + * get the center vector values int points (return an object like {x:1,y:1,z:1} in HTML5) + * @return {Object} + */ + getCenter:function () { + return {x:this._centerX ,y:this._centerY ,z:this._centerZ }; + }, + + /** + * get the up vector values (return an object like {x:1,y:1,z:1} in HTML5) + * @param {Number} upX + * @param {Number} upY + * @param {Number} upZ + * @return {Object} + * @deprecated This function will be deprecated sooner or later,please use getUp instead. + */ + getUpXYZ:function (upX, upY, upZ) { + return {x:this._upX,y:this._upY,z:this._upZ}; + }, + + /** + * get the up vector values (return an object like {x:1,y:1,z:1} in HTML5) + * @return {Object} + */ + getUp:function () { + return {x:this._upX,y:this._upY,z:this._upZ}; + }, + + _DISALLOW_COPY_AND_ASSIGN:function (CCCamera) { + + } +}); + +/** + * returns the Z eye + * @return {Number} + */ +cc.Camera.getZEye = function () { + return cc.FLT_EPSILON; +}; diff --git a/cocos2d/core/CCConfiguration.js b/cocos2d/core/CCConfiguration.js new file mode 100644 index 00000000000..ec90b5f533f --- /dev/null +++ b/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/cocos2d/core/CCDirector.js b/cocos2d/core/CCDirector.js new file mode 100644 index 00000000000..58d69ed2973 --- /dev/null +++ b/cocos2d/core/CCDirector.js @@ -0,0 +1,1173 @@ +/**************************************************************************** + 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 EventTarget = require('./event/event-target'); +var Class = require('./platform/_CCClass'); +var CCObject = require('./platform/CCObject'); + +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 Director + */ +cc.Director = 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, + + _loadingScene: '', + // The root of rendering scene graph + _runningScene: null, + + // The entity-component scene + _scene: null, + + _totalFrames: 0, + _secondsPerFrame: 0, + + _dirtyRegion: null, + + _scheduler: null, + _actionManager: 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 for user registration update + 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; + } + + // Animation manager + if (cc.AnimationManager) { + this._animationManager = new cc.AnimationManager(); + this._scheduler.scheduleUpdate(this._animationManager, cc.Scheduler.PRIORITY_SYSTEM, false); + } + else { + this._animationManager = null; + } + + // Event target + EventTarget.polyfill(this); + + // WidgetManager + cc._widgetManager.init(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. + * @method convertToGL + * @param {Vec2} uiPoint + * @return {Vec2} + */ + convertToGL: null, + + /** + * 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. + * @method convertToUI + * @param {Vec2} glPoint + * @return {Vec2} + */ + convertToUI: null, + + engineUpdate: function (deltaTime) { + //tick before glClear: issue #533 + this._scheduler.update(deltaTime); + }, + + visit: function (deltaTime) { + this.emit(cc.Director.EVENT_BEFORE_VISIT, this); + + if (this._beforeVisitScene) + this._beforeVisitScene(); + + // visit EC + if (this._scene) { + // clear flags + clearFlags(this._scene); + } + + // update the scene + if (this._runningScene) { + var renderer = cc.renderer; + if (renderer.childrenOrderDirty === true) { + renderer.clearRenderCommands(); + this._runningScene._renderCmd._curLevel = 0; //level start from 0; + this._runningScene.visit(); + renderer.resetFlag(); + } else if (renderer.transformDirty() === true) + renderer.transform(); + } + + // visit the notifications node + if (this._notificationNode) + this._notificationNode.visit(); + + this.emit(cc.Director.EVENT_AFTER_VISIT, this); + + if (this._afterVisitScene) + this._afterVisitScene(); + }, + + render: function (deltaTime) { + cc.g_NumberOfDraws = 0; + cc.renderer.clear(); + + cc.renderer.rendering(cc._renderContext); + this._totalFrames++; + + this.emit(cc.Director.EVENT_AFTER_DRAW); + }, + + /** + * Draw the scene. This method is called every frame. Don't call it manually. + */ + drawScene: function () { + // calculate "global" dt + this.calculateDeltaTime(); + + if (!this._paused) { + // Call start for new added components + this.emit(cc.Director.EVENT_BEFORE_UPDATE); + // Update for components + this.emit(cc.Director.EVENT_COMPONENT_UPDATE, this._deltaTime); + // Destroy entities that have been removed recently + CCObject._deferredDestroy(); + // Engine update with scheduler + this.engineUpdate(this._deltaTime); + // Late update for components + this.emit(cc.Director.EVENT_COMPONENT_LATE_UPDATE, this._deltaTime); + // User can use this event to do things after update + this.emit(cc.Director.EVENT_AFTER_UPDATE); + } + + /* 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(); + } + + this.visit(this._deltaTime); + this.render(this._deltaTime); + + this._calculateMPF(); + }, + + _beforeVisitScene: null, + _afterVisitScene: null, + + /** + * 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. + * + * @method getContentScaleFactor + * @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. + * + * @method getNotificationNode + * @return {ENode} + */ + 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. + * + * @method getWinSize + * @return {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. + * + * @method getWinSizeInPixels + * @return {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. + * @method getVisibleSize + * @return {Size} + */ + getVisibleSize: null, + + /** + * Returns the visible origin of the running scene. + * @method getVisibleOrigin + * @return {Vec2} + */ + getVisibleOrigin: null, + + /** + * Returns the z eye, only available in WebGL mode. + * @method getZEye + * @return {Number} + */ + getZEye: null, + + /** + * Pause the director's ticker + * @method pause + */ + 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.onExitTransitionDidStart(); + this._runningScene.onExit(); + this._runningScene.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. + * @method pushScene + * @param {EScene} 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. + * @method runScene + * @param {EScene} scene - The need run scene. + * @param {Function} [onBeforeLoadScene] - The function at the scene before loading. + */ + runScene: function (scene, onBeforeLoadScene) { + cc.assert(scene, cc._LogInfos.Director.pushScene); + + // unload scene + var oldScene = this._scene; + if (cc.isValid(oldScene)) { + oldScene.destroy(); + } + this._scene = null; + + // purge destroyed nodes belongs to old scene + cc.Object._deferredDestroy(); + + if (onBeforeLoadScene) { + onBeforeLoadScene(); + } + this.emit(cc.Director.EVENT_BEFORE_SCENE_LAUNCH, scene); + + // Re-add persist node root + var persistNodes = cc.game._persistRootNodes; + for (var i = 0; i < persistNodes.length; ++i) { + var node = persistNodes[i]; + node.parent = scene; + } + + var sgScene = scene; + + // Run an Entity Scene + if (scene instanceof cc.EScene) { + // ensure scene initialized + scene._load(); + + this._scene = scene; + sgScene = scene._sgNode; + } + + // Run or replace rendering scene + if (!this._runningScene) { + //start scene + this.pushScene(sgScene); + this.startAnimation(); + } else { + //replace scene + var i = this._scenesStack.length; + this._scenesStack[Math.max(i - 1, 0)] = sgScene; + this._sendCleanupToScene = true; + this._nextScene = sgScene; + } + + if (this._nextScene) { + this.setNextScene(); + } + + // Activate + if (scene instanceof cc.EScene) { + scene._activate(); + } + }, + + // @Scene loading section + + /** + * Loads the scene by its name. + * @method loadScene + * @param {String} sceneName - The name of the scene to load. + * @param {Function} [onLaunched] - callback, will be called after scene launched. + * @param {Function} [onUnloaded] - callback, will be called when the previous scene was unloaded. + * @return {Boolean} if error, return false + */ + loadScene: function (sceneName, onLaunched, onUnloaded) { + var uuid, info; + if (this._loadingScene) { + cc.error('[loadScene] Failed to load scene "%s" because "%s" is already loading', sceneName, this._loadingScene); + return false; + } + if (typeof sceneName === 'string') { + if (!sceneName.endsWith('.fire')) { + sceneName += '.fire'; + } + if (sceneName[0] !== '/' && !sceneName.startsWith('assets://')) { + sceneName = '/' + sceneName; // 使用全ååŒ¹é… + } + // search scene + for (var i = 0; i < cc.game._sceneInfos.length; i++) { + info = cc.game._sceneInfos[i]; + var url = info.url; + if (url.endsWith(sceneName)) { + uuid = info.uuid; + break; + } + } + } + else { + info = cc.game._sceneInfos[sceneName]; + if (typeof info === 'object') { + uuid = info.uuid; + } + else { + cc.error('[loadScene] The scene index to load (%s) is out of range.', sceneName); + return false; + } + } + if (uuid) { + this._loadingScene = sceneName; + this._loadSceneByUuid(uuid, onLaunched, onUnloaded); + return true; + } + else { + cc.error('[loadScene] Can not load the scene "%s" because it has not been added to the build settings before play.', sceneName); + return false; + } + }, + + /** + * Loads the scene by its uuid. + * @method _loadSceneByUuid + * @param {String} uuid - the uuid of the scene asset to load + * @param {Function} [onLaunched] + * @param {Function} [onUnloaded] + * @private + */ + _loadSceneByUuid: function (uuid, onLaunched, onUnloaded) { + //cc.AssetLibrary.unloadAsset(uuid); // force reload + cc.AssetLibrary.loadAsset(uuid, function (error, sceneAsset) { + var scene; + if (error) { + error = 'Failed to load scene: ' + error; + cc.error(error); + if (CC_EDITOR) { + console.assert(false, error); + } + } + else { + var uuid = sceneAsset._uuid; + scene = sceneAsset.scene; + if (scene instanceof cc.EScene) { + scene._id = uuid; + cc.director.runScene(scene, onUnloaded); + } + else { + error = 'The asset ' + uuid + ' is not a scene'; + cc.error(error); + scene = null; + } + } + cc.director._loadingScene = ''; + if (onLaunched) { + onLaunched(error, scene); + } + }); + }, + + /** + * Resume director after pause, if the current scene is not paused, nothing will happen. + * @method resume + */ + 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 + * @method setDepthTest + * @param {Boolean} on + */ + setDepthTest: null, + + /** + * set color for clear screen.
+ * Implementation can be found in CCDirectorCanvas.js/CCDirectorWebGL.js + * @method setClearColor + * @param {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.onExitTransitionDidStart(); + locRunningScene.onExit(); + } + + // issue #709. the root node (scene) should receive the cleanup message too + // otherwise it might be leaked. + if (this._sendCleanupToScene && locRunningScene) + locRunningScene.cleanup(); + } + + this._runningScene = this._nextScene; + cc.renderer.childrenOrderDirty = true; + + this._nextScene = null; + if ((!runningIsTransition) && (this._runningScene !== null)) { + this._runningScene.onEnter(); + this._runningScene.onEnterTransitionDidFinish(); + } + }, + + /** + * Sets Notification Node + * @param {ENode} node + */ + setNotificationNode: function (node) { + cc.renderer.childrenOrderDirty = true; + if(this._notificationNode){ + this._notificationNode.onExitTransitionDidStart(); + this._notificationNode.onExit(); + this._notificationNode.cleanup(); + } + this._notificationNode = node; + if(!node) + return; + this._notificationNode.onEnter(); + this._notificationNode.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. + * @method setOpenGLView + * @param {EGLView} openGLView + */ + setOpenGLView: null, + + /** + * Sets an OpenGL projection.
+ * Implementation can be found in CCDirectorCanvas.js/CCDirectorWebGL.js. + * @method setProjection + * @param {Number} projection + */ + setProjection: null, + + /** + * Update the view port.
+ * Implementation can be found in CCDirectorCanvas.js/CCDirectorWebGL.js. + * @method setViewport + */ + setViewport: null, + + /** + * Get the CCEGLView, where everything is rendered.
+ * Implementation can be found in CCDirectorCanvas.js/CCDirectorWebGL.js. + * @method getOpenGLView + * @return {EGLView} + */ + getOpenGLView: null, + + /** + * Sets an OpenGL projection.
+ * Implementation can be found in CCDirectorCanvas.js/CCDirectorWebGL.js. + * @method getProjection + * @return {Number} + */ + getProjection: null, + + /** + * Enables/disables OpenGL alpha blending.
+ * Implementation can be found in CCDirectorCanvas.js/CCDirectorWebGL.js. + * @method setAlphaBlending + * @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. + * @method isSendCleanupToScene + * @return {Boolean} + */ + isSendCleanupToScene: function () { + return this._sendCleanupToScene; + }, + + /** + * Returns current running Scene. Director can only run one Scene at the time. + * @method getRunningScene + * @return {EScene} + */ + getRunningScene: function () { + return this._runningScene; + }, + + /** + * Returns current running Scene. Director can only run one Scene at the time. + * @method getScene + * @return {EScene} + */ + getScene: function () { + return this._scene; + }, + + /** + * Returns the FPS value. + * @method getAnimationInterval + * @return {Number} + */ + getAnimationInterval: function () { + return this._animationInterval; + }, + + /** + * Returns whether or not to display the FPS informations. + * @method isDisplayStats + * @return {Boolean} + */ + isDisplayStats: function () { + return cc.profiler ? cc.profiler.isShowingStats() : false; + }, + + /** + * Sets whether display the FPS on the bottom-left corner. + * @method setDisplayStats + * @param {Boolean} displayStats + */ + setDisplayStats: function (displayStats) { + if (cc.profiler) { + displayStats ? cc.profiler.showStats() : cc.profiler.hideStats(); + } + }, + + /** + * Returns seconds per frame. + * @method getSecondsPerFrame + * @return {Number} + */ + getSecondsPerFrame: function () { + return this._secondsPerFrame; + }, + + /** + * Returns whether next delta time equals to zero + * @method isNextDeltaTimeZero + * @return {Boolean} + */ + isNextDeltaTimeZero: function () { + return this._nextDeltaTimeZero; + }, + + /** + * Returns whether or not the Director is paused + * @method isPaused + * @return {Boolean} + */ + isPaused: function () { + return this._paused; + }, + + /** + * Returns how many frames were called since the director started + * @method getTotalFrames + * @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 (c === 0) { + this.end(); + return; + } + // current level or lower -> nothing + if (level > c) + return; + + // pop stack until reaching desired level + while (c > level) { + var current = locScenesStack.pop(); + if (current.running) { + current.onExitTransitionDidStart(); + current.onExit(); + } + current.cleanup(); + c--; + } + this._nextScene = locScenesStack[locScenesStack.length - 1]; + this._sendCleanupToScene = false; + }, + + /** + * Returns the cc.Scheduler associated with this director. + * @method getScheduler + * @return {Scheduler} + */ + getScheduler: function () { + return this._scheduler; + }, + + /** + * Sets the cc.Scheduler associated with this director. + * @method setScheduler + * @param {Scheduler} scheduler + */ + setScheduler: function (scheduler) { + if (this._scheduler !== scheduler) { + this._scheduler = scheduler; + } + }, + + /** + * Returns the cc.ActionManager associated with this director. + * @method getActionManager + * @return {ActionManager} + */ + getActionManager: function () { + return this._actionManager; + }, + /** + * Sets the cc.ActionManager associated with this director. + * @method setActionManager + * @param {ActionManager} actionManager + */ + setActionManager: function (actionManager) { + if (this._actionManager !== actionManager) { + this._actionManager = actionManager; + } + }, + + /** + * Returns the cc.AnimationManager associated with this director. + * @method getAnimationManager + * @return {AnimationManager} + */ + getAnimationManager: function () { + return this._animationManager; + }, + + /** + * Returns the delta time since last frame. + * @method getDeltaTime + * @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.director.on(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} + */ +cc.Director.EVENT_BEFORE_SCENE_LAUNCH = "director_before_scene_launch"; + +/** + * The event after update of cc.Director + * @constant + * @type {string} + */ +cc.Director.EVENT_BEFORE_UPDATE = "director_before_update"; + +/** + * The event after update of cc.Director + * @constant + * @type {string} + */ +cc.Director.EVENT_COMPONENT_UPDATE = "director_component_update"; + +/** + * The event after update of cc.Director + * @constant + * @type {string} + */ +cc.Director.EVENT_COMPONENT_LATE_UPDATE = "director_component_late_update"; + +/** + * The event after update of cc.Director + * @constant + * @type {string} + */ +cc.Director.EVENT_AFTER_UPDATE = "director_after_update"; + +/** + * The event after visit of cc.Director + * @constant + * @type {string} + */ +cc.Director.EVENT_AFTER_VISIT = "director_after_visit"; + +/** + * The event after visit of cc.Director + * @constant + * @type {string} + */ +cc.Director.EVENT_BEFORE_VISIT = "director_before_visit"; + +/** + * The event after draw of cc.Director + * @constant + * @type {string} + */ +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 3D projection + * @constant + * @type {Number} + */ +cc.Director.PROJECTION_DEFAULT = cc.Director.PROJECTION_3D; + +// clear dirtyFlags for EC +function clearFlags (node) { + var children = node._children; + for (var i = 0, len = children.length; i < len; i++) { + var child = children[i]; + child._dirtyFlags = 0; + clearFlags(child); + } +} diff --git a/cocos2d/core/CCDirectorCanvas.js b/cocos2d/core/CCDirectorCanvas.js new file mode 100644 index 00000000000..0117446e459 --- /dev/null +++ b/cocos2d/core/CCDirectorCanvas.js @@ -0,0 +1,85 @@ +/**************************************************************************** + 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. + ****************************************************************************/ + +require('./CCDirector'); +require('./CCGame'); + +cc.game.once(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; + this.emit(cc.Director.EVENT_PROJECTION_CHANGED, this); + }; + + _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; + } + } +}); \ No newline at end of file diff --git a/cocos2d/core/CCDirectorWebGL.js b/cocos2d/core/CCDirectorWebGL.js new file mode 100644 index 00000000000..68f3a398ef0 --- /dev/null +++ b/cocos2d/core/CCDirectorWebGL.js @@ -0,0 +1,270 @@ +/**************************************************************************** + 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. + ****************************************************************************/ + +require('./CCDirector'); +require('./CCGame'); + +var GLToClipTransform = function (transformOut) { + //var projection = new cc.math.Matrix4(); + //cc.kmGLGetMatrix(cc.KM_GL_PROJECTION, projection); + cc.kmGLGetMatrix(cc.KM_GL_PROJECTION, transformOut); + + var modelview = new cc.math.Matrix4(); + cc.kmGLGetMatrix(cc.KM_GL_MODELVIEW, modelview); + + transformOut.multiply(modelview); +}; + +cc.game.once(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 transform*[0, 0, 0, 1]/w + var zClip = transform.mat[14] / transform.mat[15]; + var glSize = this._openGLView.getDesignResolutionSize(); + var glCoord = new cc.math.Vec3(2.0 * uiPoint.x / glSize.width - 1.0, 1.0 - 2.0 * uiPoint.y / glSize.height, zClip); + glCoord.transformCoord(transformInv); + return cc.p(glCoord.x, glCoord.y); + }; + + _p.convertToUI = function (glPoint) { + var transform = new cc.math.Matrix4(); + GLToClipTransform(transform); + + var clipCoord = new cc.math.Vec3(glPoint.x, glPoint.y, 0.0); + // Need to calculate the zero depth from the transform. + clipCoord.transformCoord(transform); + + var glSize = this._openGLView.getDesignResolutionSize(); + return cc.p(glSize.width * (clipCoord.x * 0.5 + 0.5), glSize.height * (-clipCoord.y * 0.5 + 0.5)); + }; + + _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); + //} + }; + + _p.getZEye = function () { + return (this._winSizeInPoints.height / 1.1566 ); + }; + + _p.setViewport = function () { + var view = this._openGLView; + if (view) { + var locWinSizeInPoints = this._winSizeInPoints; + view.setViewPortInPoints(-view._viewPortRect.x/view._scaleX, -view._viewPortRect.y/view._scaleY, locWinSizeInPoints.width, locWinSizeInPoints.height); + } + }; + + _p.getOpenGLView = function () { + return this._openGLView; + }; + + _p.getProjection = function () { + return this._projection; + }; + + _p.setAlphaBlending = function (on) { + if (on) + cc.glBlendFunc(cc.BLEND_SRC, cc.BLEND_DST); + else + cc.glBlendFunc(cc._renderContext.ONE, cc._renderContext.ZERO); + //cc.checkGLErrorDebug(); + }; + + _p.setGLDefaultValues = function () { + var _t = this; + _t.setAlphaBlending(true); + // XXX: Fix me, should enable/disable depth test according the depth format as cocos2d-iphone did + // [self setDepthTest: view_.depthFormat]; + _t.setDepthTest(false); + _t.setProjection(_t._projection); + + // set other opengl default values + cc._renderContext.clearColor(0.0, 0.0, 0.0, 0.0); + }; +}); diff --git a/cocos2d/core/CCDrawingPrimitivesCanvas.js b/cocos2d/core/CCDrawingPrimitivesCanvas.js new file mode 100644 index 00000000000..1943681b848 --- /dev/null +++ b/cocos2d/core/CCDrawingPrimitivesCanvas.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. + ****************************************************************************/ +/** + * @const + * @type {number} + */ +cc.PI2 = Math.PI * 2; + +/** + * Canvas of DrawingPrimitive implement version use for canvasMode + * @class + * @extends cc._Class + * @param {CanvasRenderingContext2D} renderContext + */ +cc.DrawingPrimitiveCanvas = cc._Class.extend(/** @lends cc.DrawingPrimitiveCanvas# */{ + _cacheArray:[], + _renderContext:null, + /** + * Constructor of cc.DrawingPrimitiveCanvas + * @param {cc.CanvasContextWrapper} renderContext + */ + ctor:function (renderContext) { + this._renderContext = renderContext; + }, + + /** + * draws a point given x and y coordinate measured in points + * @override + * @param {cc.Vec2} point + * @param {Number} size + */ + drawPoint:function (point, size) { + if (!size) { + size = 1; + } + var locScaleX = cc.view.getScaleX(), locScaleY = cc.view.getScaleY(); + var newPoint = cc.p(point.x * locScaleX, point.y * locScaleY); + var ctx = this._renderContext.getContext(); + ctx.beginPath(); + ctx.arc(newPoint.x, -newPoint.y, size * locScaleX, 0, Math.PI * 2, false); + ctx.closePath(); + ctx.fill(); + }, + + /** + * draws an array of points. + * @override + * @param {Array} points point of array + * @param {Number} numberOfPoints + * @param {Number} size + */ + drawPoints:function (points, numberOfPoints, size) { + if (points == null) + return; + + if (!size) { + size = 1; + } + var locContext = this._renderContext.getContext(),locScaleX = cc.view.getScaleX(), locScaleY = cc.view.getScaleY(); + + locContext.beginPath(); + for (var i = 0, len = points.length; i < len; i++) + locContext.arc(points[i].x * locScaleX, -points[i].y * locScaleY, size * locScaleX, 0, Math.PI * 2, false); + locContext.closePath(); + locContext.fill(); + }, + + /** + * draws a line given the origin and destination point measured in points + * @override + * @param {cc.Vec2} origin + * @param {cc.Vec2} destination + */ + drawLine:function (origin, destination) { + var locContext = this._renderContext.getContext(), locScaleX = cc.view.getScaleX(), locScaleY = cc.view.getScaleY(); + locContext.beginPath(); + locContext.moveTo(origin.x * locScaleX, -origin.y * locScaleY); + locContext.lineTo(destination.x * locScaleX, -destination.y * locScaleY); + locContext.closePath(); + locContext.stroke(); + }, + + /** + * draws a rectangle given the origin and destination point measured in points. + * @param {cc.Vec2} origin + * @param {cc.Vec2} destination + */ + drawRect:function (origin, destination) { + //todo need optimize for performance + this.drawLine(cc.p(origin.x, origin.y), cc.p(destination.x, origin.y)); + this.drawLine(cc.p(destination.x, origin.y), cc.p(destination.x, destination.y)); + this.drawLine(cc.p(destination.x, destination.y), cc.p(origin.x, destination.y)); + this.drawLine(cc.p(origin.x, destination.y), cc.p(origin.x, origin.y)); + }, + + /** + * draws a solid rectangle given the origin and destination point measured in points. + * @param {cc.Vec2} origin + * @param {cc.Vec2} destination + * @param {cc.Color} color + */ + drawSolidRect:function (origin, destination, color) { + var vertices = [ + origin, + cc.p(destination.x, origin.y), + destination, + cc.p(origin.x, destination.y) + ]; + + this.drawSolidPoly(vertices, 4, color); + }, + + /** + * draws a polygon given a pointer to cc.Vec2 coordinates and the number of vertices measured in points. + * @override + * @param {Array} vertices a pointer to cc.Vec2 coordinates + * @param {Number} numOfVertices the number of vertices measured in points + * @param {Boolean} closePolygon The polygon can be closed or open + * @param {Boolean} [fill=] The polygon can be closed or open and optionally filled with current color + */ + drawPoly:function (vertices, numOfVertices, closePolygon, fill) { + fill = fill || false; + + if (vertices == null) + return; + + if (vertices.length < 3) + throw new Error("Polygon's point must greater than 2"); + + var firstPoint = vertices[0], locContext = this._renderContext.getContext(); + var locScaleX = cc.view.getScaleX(), locScaleY = cc.view.getScaleY(); + locContext.beginPath(); + locContext.moveTo(firstPoint.x * locScaleX, -firstPoint.y * locScaleY); + for (var i = 1, len = vertices.length; i < len; i++) + locContext.lineTo(vertices[i].x * locScaleX, -vertices[i].y * locScaleY); + + if (closePolygon) + locContext.closePath(); + + if (fill) + locContext.fill(); + else + locContext.stroke(); + }, + + /** + * draws a solid polygon given a pointer to CGPoint coordinates, the number of vertices measured in points, and a color. + * @param {Array} polygons + * @param {Number} numberOfPoints + * @param {cc.Color} color + */ + drawSolidPoly:function (polygons, numberOfPoints, color) { + this.setDrawColor(color.r, color.g, color.b, color.a); + this.drawPoly(polygons, numberOfPoints, true, true); + }, + + /** + * draws a circle given the center, radius and number of segments. + * @override + * @param {cc.Vec2} center center of circle + * @param {Number} radius + * @param {Number} angle angle in radians + * @param {Number} segments + * @param {Boolean} [drawLineToCenter=] + */ + drawCircle: function (center, radius, angle, segments, drawLineToCenter) { + drawLineToCenter = drawLineToCenter || false; + var locContext = this._renderContext.getContext(); + var locScaleX = cc.view.getScaleX(), locScaleY = cc.view.getScaleY(); + locContext.beginPath(); + var endAngle = angle - Math.PI * 2; + locContext.arc(0 | (center.x * locScaleX), 0 | -(center.y * locScaleY), radius * locScaleX, -angle, -endAngle, false); + if (drawLineToCenter) { + locContext.lineTo(0 | (center.x * locScaleX), 0 | -(center.y * locScaleY)); + } + locContext.stroke(); + }, + + /** + * draws a quad bezier path + * @override + * @param {cc.Vec2} origin + * @param {cc.Vec2} control + * @param {cc.Vec2} destination + * @param {Number} segments + */ + drawQuadBezier:function (origin, control, destination, segments) { + //this is OpenGL Algorithm + var vertices = this._cacheArray; + vertices.length =0; + + var 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)); + + this.drawPoly(vertices, segments + 1, false, false); + }, + + /** + * draws a cubic bezier path + * @override + * @param {cc.Vec2} origin + * @param {cc.Vec2} control1 + * @param {cc.Vec2} control2 + * @param {cc.Vec2} destination + * @param {Number} segments + */ + drawCubicBezier:function (origin, control1, control2, destination, segments) { + //this is OpenGL Algorithm + var vertices = this._cacheArray; + vertices.length =0; + + var 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)); + + this.drawPoly(vertices, segments + 1, false, false); + }, + + /** + * draw a CatmullRom curve + * @override + * @param {Array} points + * @param {Number} segments + */ + drawCatmullRom:function (points, segments) { + this.drawCardinalSpline(points, 0.5, segments); + }, + + /** + * draw a cardinal spline path + * @override + * @param {Array} config + * @param {Number} tension + * @param {Number} segments + */ + drawCardinalSpline:function (config, tension, segments) { + //lazy_init(); + cc._renderContext.setStrokeStyle("rgba(255,255,255,1)"); + var points = this._cacheArray; + points.length = 0; + var p, lt; + var 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); + points.push(newPos); + } + this.drawPoly(points, segments + 1, false, false); + }, + + /** + * draw an image + * @override + * @param {HTMLImageElement|HTMLCanvasElement} image + * @param {cc.Vec2} sourcePoint + * @param {cc.Size} sourceSize + * @param {cc.Vec2} destPoint + * @param {cc.Size} destSize + */ + drawImage:function (image, sourcePoint, sourceSize, destPoint, destSize) { + var len = arguments.length; + var ctx = this._renderContext.getContext(); + switch (len) { + case 2: + var height = image.height; + ctx.drawImage(image, sourcePoint.x, -(sourcePoint.y + height)); + break; + case 3: + ctx.drawImage(image, sourcePoint.x, -(sourcePoint.y + sourceSize.height), sourceSize.width, sourceSize.height); + break; + case 5: + ctx.drawImage(image, sourcePoint.x, sourcePoint.y, sourceSize.width, sourceSize.height, destPoint.x, -(destPoint.y + destSize.height), + destSize.width, destSize.height); + break; + default: + throw new Error("Argument must be non-nil"); + break; + } + }, + + /** + * draw a star + * @param {cc.CanvasContextWrapper} ctx canvas context + * @param {Number} radius + * @param {cc.Color} color + */ + drawStar:function (ctx, radius, color) { + var wrapper = ctx || this._renderContext; + var context = wrapper.getContext(); + radius *= cc.view.getScaleX(); + var colorStr = "rgba(" + (0 | color.r) + "," + (0 | color.g) + "," + (0 | color.b); + wrapper.setFillStyle(colorStr + ",1)"); + //context.fillStyle = colorStr + ",1)"; + var subRadius = radius / 10; + + context.beginPath(); + context.moveTo(-radius, radius); + context.lineTo(0, subRadius); + context.lineTo(radius, radius); + context.lineTo(subRadius, 0); + context.lineTo(radius, -radius); + context.lineTo(0, -subRadius); + context.lineTo(-radius, -radius); + context.lineTo(-subRadius, 0); + context.lineTo(-radius, radius); + context.closePath(); + context.fill(); + + var rg = context.createRadialGradient(0, 0, subRadius, 0, 0, radius); + rg.addColorStop(0, colorStr + ", 1)"); + rg.addColorStop(0.3, colorStr + ", 0.8)"); + rg.addColorStop(1.0, colorStr + ", 0.0)"); + wrapper.setFillStyle(rg); + //context.fillStyle = g1; + context.beginPath(); + var startAngle_1 = 0; + var endAngle_1 = cc.PI2; + context.arc(0, 0, radius - subRadius, startAngle_1, endAngle_1, false); + context.closePath(); + context.fill(); + }, + + /** + * draw a color ball + * @param {cc.CanvasContextWrapper} ctx canvas context + * @param {Number} radius + * @param {cc.Color} color + */ + drawColorBall:function (ctx, radius, color) { + var wrapper = ctx || this._renderContext; + var context = wrapper.getContext(); + radius *= cc.view.getScaleX(); + var colorStr = "rgba(" +(0|color.r) + "," + (0|color.g) + "," + (0|color.b); + var subRadius = radius / 10; + + var g1 = context.createRadialGradient(0, 0, subRadius, 0, 0, radius); + g1.addColorStop(0, colorStr + ", 1)"); + g1.addColorStop(0.3, colorStr + ", 0.8)"); + g1.addColorStop(0.6, colorStr + ", 0.4)"); + g1.addColorStop(1.0, colorStr + ", 0.0)"); + wrapper.setFillStyle(g1); + //context.fillStyle = g1; + context.beginPath(); + var startAngle_1 = 0; + var endAngle_1 = cc.PI2; + context.arc(0, 0, radius, startAngle_1, endAngle_1, false); + context.closePath(); + context.fill(); + }, + + /** + * fill text + * @param {String} strText + * @param {Number} x + * @param {Number} y + */ + fillText:function (strText, x, y) { + this._renderContext.getContext().fillText(strText, x, -y); + }, + + /** + * set the drawing color with 4 unsigned bytes + * @param {Number} r red value (0 to 255) + * @param {Number} g green value (0 to 255) + * @param {Number} b blue value (0 to 255) + * @param {Number} a Alpha value (0 to 255) + */ + setDrawColor:function (r, g, b, a) { + this._renderContext.setFillStyle("rgba(" + r + "," + g + "," + b + "," + a / 255 + ")"); + this._renderContext.setStrokeStyle("rgba(" + r + "," + g + "," + b + "," + a / 255 + ")"); + }, + + /** + * set the point size in points. Default 1. + * @param {Number} pointSize + */ + setPointSize:function (pointSize) { + }, + + /** + * set the line width. Default 1. + * @param {Number} width + */ + setLineWidth:function (width) { + this._renderContext.getContext().lineWidth = width * cc.view.getScaleX(); + } +}); \ No newline at end of file diff --git a/cocos2d/core/CCDrawingPrimitivesWebGL.js b/cocos2d/core/CCDrawingPrimitivesWebGL.js new file mode 100644 index 00000000000..660015354e3 --- /dev/null +++ b/cocos2d/core/CCDrawingPrimitivesWebGL.js @@ -0,0 +1,464 @@ +/**************************************************************************** + 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. + ****************************************************************************/ + +/** + * Canvas of DrawingPrimitive implement version use for WebGlMode + * @class + * @extends cc._Class + */ +cc.DrawingPrimitiveWebGL = cc._Class.extend(/** @lends cc.DrawingPrimitiveWebGL# */{ + _renderContext:null, + _initialized:false, + _shader: null, + _colorLocation:-1, + _colorArray: null, + _pointSizeLocation:-1, + _pointSize:-1, + /** + * contructor of cc.DrawingPrimitiveWebGL + * @param ctx rendercontext + */ + ctor:function (ctx) { + if (ctx == null) + ctx = cc._renderContext; + + if (!ctx instanceof WebGLRenderingContext) + throw new Error("Can't initialise DrawingPrimitiveWebGL. context need is WebGLRenderingContext"); + + this._renderContext = ctx; + this._colorArray = new Float32Array([1.0, 1.0, 1.0, 1.0]); + }, + + lazy_init:function () { + var _t = this; + if (!_t._initialized) { + // + // Position and 1 color passed as a uniform (to similate glColor4ub ) + // + _t._shader = cc.shaderCache.programForKey(cc.SHADER_POSITION_UCOLOR); + _t._colorLocation = _t._renderContext.getUniformLocation(_t._shader.getProgram(), "u_color"); + _t._pointSizeLocation = _t._renderContext.getUniformLocation(_t._shader.getProgram(), "u_pointSize"); + + _t._initialized = true; + } + }, + + /** + * initlialize context + */ + drawInit:function () { + this._initialized = false; + }, + + /** + * draws a point given x and y coordinate measured in points + * @param {cc.Vec2} point + */ + drawPoint:function (point) { + this.lazy_init(); + + var glContext = this._renderContext; + this._shader.use(); + this._shader.setUniformForModelViewAndProjectionMatrixWithMat4(); + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSITION); + glContext.uniform4fv(this._colorLocation, this._colorArray); + this._shader.setUniformLocationWith1f(this._pointSizeLocation, this._pointSize); + + var pointBuffer = glContext.createBuffer(); + glContext.bindBuffer(glContext.ARRAY_BUFFER, pointBuffer); + glContext.bufferData(glContext.ARRAY_BUFFER, new Float32Array([point.x, point.y]), glContext.STATIC_DRAW); + glContext.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, glContext.FLOAT, false, 0, 0); + + glContext.drawArrays(glContext.POINTS, 0, 1); + glContext.deleteBuffer(pointBuffer); + + cc.incrementGLDraws(1); + }, + + /** + * draws an array of points. + * @param {Array} points point of array + * @param {Number} numberOfPoints + */ + drawPoints:function (points, numberOfPoints) { + if (!points || points.length === 0) + return; + + this.lazy_init(); + + var glContext = this._renderContext; + this._shader.use(); + this._shader.setUniformForModelViewAndProjectionMatrixWithMat4(); + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSITION); + glContext.uniform4fv(this._colorLocation, this._colorArray); + this._shader.setUniformLocationWith1f(this._pointSizeLocation, this._pointSize); + + var pointBuffer = glContext.createBuffer(); + glContext.bindBuffer(glContext.ARRAY_BUFFER, pointBuffer); + glContext.bufferData(glContext.ARRAY_BUFFER, this._pointsToTypeArray(points), glContext.STATIC_DRAW); + glContext.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, glContext.FLOAT, false, 0, 0); + + glContext.drawArrays(glContext.POINTS, 0, points.length); + glContext.deleteBuffer(pointBuffer); + + cc.incrementGLDraws(1); + }, + + _pointsToTypeArray:function (points) { + var typeArr = new Float32Array(points.length * 2); + for (var i = 0; i < points.length; i++) { + typeArr[i * 2] = points[i].x; + typeArr[i * 2 + 1] = points[i].y; + } + return typeArr; + }, + + /** + * draws a line given the origin and destination point measured in points + * @param {cc.Vec2} origin + * @param {cc.Vec2} destination + */ + drawLine:function (origin, destination) { + this.lazy_init(); + + var glContext = this._renderContext; + this._shader.use(); + this._shader.setUniformForModelViewAndProjectionMatrixWithMat4(); + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSITION); + glContext.uniform4fv(this._colorLocation, this._colorArray); + + var pointBuffer = glContext.createBuffer(); + glContext.bindBuffer(glContext.ARRAY_BUFFER, pointBuffer); + glContext.bufferData(glContext.ARRAY_BUFFER, this._pointsToTypeArray([origin, destination]), glContext.STATIC_DRAW); + glContext.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, glContext.FLOAT, false, 0, 0); + + glContext.drawArrays(glContext.LINES, 0, 2); + glContext.deleteBuffer(pointBuffer); + + cc.incrementGLDraws(1); + }, + + /** + * draws a rectangle given the origin and destination point measured in points. + * @param {cc.Vec2} origin + * @param {cc.Vec2} destination + */ + drawRect:function (origin, destination) { + this.drawLine(cc.p(origin.x, origin.y), cc.p(destination.x, origin.y)); + this.drawLine(cc.p(destination.x, origin.y), cc.p(destination.x, destination.y)); + this.drawLine(cc.p(destination.x, destination.y), cc.p(origin.x, destination.y)); + this.drawLine(cc.p(origin.x, destination.y), cc.p(origin.x, origin.y)); + }, + + /** + * draws a solid rectangle given the origin and destination point measured in points. + * @param {cc.Vec2} origin + * @param {cc.Vec2} destination + * @param {cc.Color} color + */ + drawSolidRect:function (origin, destination, color) { + var vertices = [ + origin, + cc.p(destination.x, origin.y), + destination, + cc.p(origin.x, destination.y) + ]; + + this.drawSolidPoly(vertices, 4, color); + }, + + /** + * draws a polygon given a pointer to cc.Vec2 coordiantes and the number of vertices measured in points. + * @param {Array} vertices a pointer to cc.Vec2 coordiantes + * @param {Number} numOfVertices the number of vertices measured in points + * @param {Boolean} closePolygon The polygon can be closed or open + */ + drawPoly:function (vertices, numOfVertices, closePolygon) { + this.lazy_init(); + + var glContext = this._renderContext; + this._shader.use(); + this._shader.setUniformForModelViewAndProjectionMatrixWithMat4(); + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSITION); + glContext.uniform4fv(this._colorLocation, this._colorArray); + + var pointBuffer = glContext.createBuffer(); + glContext.bindBuffer(glContext.ARRAY_BUFFER, pointBuffer); + glContext.bufferData(glContext.ARRAY_BUFFER, this._pointsToTypeArray(vertices), glContext.STATIC_DRAW); + glContext.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, glContext.FLOAT, false, 0, 0); + + if (closePolygon) + glContext.drawArrays(glContext.LINE_LOOP, 0, vertices.length); + else + glContext.drawArrays(glContext.LINE_STRIP, 0, vertices.length); + glContext.deleteBuffer(pointBuffer); + + cc.incrementGLDraws(1); + }, + + /** + * draws a solid polygon given a pointer to CGPoint coordiantes, the number of vertices measured in points, and a color. + * @param {Array} poli + * @param {Number} numberOfPoints + * @param {cc.Color} color + */ + drawSolidPoly:function (poli, numberOfPoints, color) { + this.lazy_init(); + if (color) + this.setDrawColor(color.r, color.g, color.b, color.a); + + var glContext = this._renderContext; + this._shader.use(); + this._shader.setUniformForModelViewAndProjectionMatrixWithMat4(); + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSITION); + glContext.uniform4fv(this._colorLocation, this._colorArray); + + var pointBuffer = glContext.createBuffer(); + glContext.bindBuffer(glContext.ARRAY_BUFFER, pointBuffer); + glContext.bufferData(glContext.ARRAY_BUFFER, this._pointsToTypeArray(poli), glContext.STATIC_DRAW); + glContext.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, glContext.FLOAT, false, 0, 0); + glContext.drawArrays(glContext.TRIANGLE_FAN, 0, poli.length); + glContext.deleteBuffer(pointBuffer); + + cc.incrementGLDraws(1); + }, + + /** + * draws a circle given the center, radius and number of segments. + * @param {cc.Vec2} center center of circle + * @param {Number} radius + * @param {Number} angle angle in radians + * @param {Number} segments + * @param {Boolean} drawLineToCenter + */ + drawCircle:function (center, radius, angle, segments, drawLineToCenter) { + this.lazy_init(); + + var additionalSegment = 1; + if (drawLineToCenter) + additionalSegment++; + + var coef = 2.0 * Math.PI / segments; + + var vertices = new Float32Array((segments + 2) * 2); + if (!vertices) + return; + + 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[i * 2] = j; + vertices[i * 2 + 1] = k; + } + vertices[(segments + 1) * 2] = center.x; + vertices[(segments + 1) * 2 + 1] = center.y; + + var glContext = this._renderContext; + this._shader.use(); + this._shader.setUniformForModelViewAndProjectionMatrixWithMat4(); + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSITION); + glContext.uniform4fv(this._colorLocation, this._colorArray); + + var pointBuffer = glContext.createBuffer(); + glContext.bindBuffer(glContext.ARRAY_BUFFER, pointBuffer); + glContext.bufferData(glContext.ARRAY_BUFFER, vertices, glContext.STATIC_DRAW); + glContext.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, glContext.FLOAT, false, 0, 0); + + glContext.drawArrays(glContext.LINE_STRIP, 0, segments + additionalSegment); + glContext.deleteBuffer(pointBuffer); + + cc.incrementGLDraws(1); + }, + + /** + * draws a quad bezier path + * @param {cc.Vec2} origin + * @param {cc.Vec2} control + * @param {cc.Vec2} destination + * @param {Number} segments + */ + drawQuadBezier:function (origin, control, destination, segments) { + this.lazy_init(); + + var vertices = new Float32Array((segments + 1) * 2); + + var t = 0.0; + for (var i = 0; i < segments; i++) { + vertices[i * 2] = Math.pow(1 - t, 2) * origin.x + 2.0 * (1 - t) * t * control.x + t * t * destination.x; + vertices[i * 2 + 1] = Math.pow(1 - t, 2) * origin.y + 2.0 * (1 - t) * t * control.y + t * t * destination.y; + t += 1.0 / segments; + } + vertices[segments * 2] = destination.x; + vertices[segments * 2 + 1] = destination.y; + + var glContext = this._renderContext; + this._shader.use(); + this._shader.setUniformForModelViewAndProjectionMatrixWithMat4(); + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSITION); + glContext.uniform4fv(this._colorLocation, this._colorArray); + + var pointBuffer = glContext.createBuffer(); + glContext.bindBuffer(glContext.ARRAY_BUFFER, pointBuffer); + glContext.bufferData(glContext.ARRAY_BUFFER, vertices, glContext.STATIC_DRAW); + glContext.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, glContext.FLOAT, false, 0, 0); + + glContext.drawArrays(glContext.LINE_STRIP, 0, segments + 1); + glContext.deleteBuffer(pointBuffer); + + cc.incrementGLDraws(1); + }, + + /** + * draws a cubic bezier path + * @param {cc.Vec2} origin + * @param {cc.Vec2} control1 + * @param {cc.Vec2} control2 + * @param {cc.Vec2} destination + * @param {Number} segments + */ + drawCubicBezier:function (origin, control1, control2, destination, segments) { + this.lazy_init(); + + var vertices = new Float32Array((segments + 1) * 2); + + var t = 0; + for (var i = 0; i < segments; i++) { + vertices[i * 2] = 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; + vertices[i * 2 + 1] = 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; + t += 1.0 / segments; + } + vertices[segments * 2] = destination.x; + vertices[segments * 2 + 1] = destination.y; + + var glContext = this._renderContext; + this._shader.use(); + this._shader.setUniformForModelViewAndProjectionMatrixWithMat4(); + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSITION); + glContext.uniform4fv(this._colorLocation, this._colorArray); + + var pointBuffer = glContext.createBuffer(); + glContext.bindBuffer(glContext.ARRAY_BUFFER, pointBuffer); + glContext.bufferData(glContext.ARRAY_BUFFER, vertices, glContext.STATIC_DRAW); + glContext.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, glContext.FLOAT, false, 0, 0); + glContext.drawArrays(glContext.LINE_STRIP, 0, segments + 1); + glContext.deleteBuffer(pointBuffer); + + cc.incrementGLDraws(1); + }, + + /** + * draw a catmull rom line + * @param {Array} points + * @param {Number} segments + */ + drawCatmullRom:function (points, segments) { + this.drawCardinalSpline(points, 0.5, segments); + }, + + /** + * draw a cardinal spline path + * @param {Array} config + * @param {Number} tension + * @param {Number} segments + */ + drawCardinalSpline:function (config, tension, segments) { + this.lazy_init(); + + var vertices = new Float32Array((segments + 1) * 2); + var 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; + } + + var newPos = cc.cardinalSplineAt( + cc.getControlPointAt(config, p - 1), + cc.getControlPointAt(config, p), + cc.getControlPointAt(config, p + 1), + cc.getControlPointAt(config, p + 2), + tension, lt); + // Interpolate + + vertices[i * 2] = newPos.x; + vertices[i * 2 + 1] = newPos.y; + } + + var glContext = this._renderContext; + this._shader.use(); + this._shader.setUniformForModelViewAndProjectionMatrixWithMat4(); + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSITION); + glContext.uniform4fv(this._colorLocation, this._colorArray); + + var pointBuffer = glContext.createBuffer(); + glContext.bindBuffer(glContext.ARRAY_BUFFER, pointBuffer); + glContext.bufferData(glContext.ARRAY_BUFFER, vertices, glContext.STATIC_DRAW); + glContext.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, glContext.FLOAT, false, 0, 0); + glContext.drawArrays(glContext.LINE_STRIP, 0, segments + 1); + glContext.deleteBuffer(pointBuffer); + + cc.incrementGLDraws(1); + }, + + /** + * set the drawing color with 4 unsigned bytes + * @param {Number} r red value (0 to 255) + * @param {Number} g green value (0 to 255) + * @param {Number} b blue value (0 to 255) + * @param {Number} a Alpha value (0 to 255) + */ + setDrawColor:function (r, g, b, a) { + this._colorArray[0] = r / 255.0; + this._colorArray[1] = g / 255.0; + this._colorArray[2] = b / 255.0; + this._colorArray[3] = a / 255.0; + }, + + /** + * set the point size in points. Default 1. + * @param {Number} pointSize + */ + setPointSize:function (pointSize) { + this._pointSize = pointSize * cc.contentScaleFactor(); + }, + + /** + * set the line width. Default 1. + * @param {Number} width + */ + setLineWidth:function (width) { + if(this._renderContext.lineWidth) + this._renderContext.lineWidth(width); + } +}); \ No newline at end of file diff --git a/cocos2d/core/CCGame.js b/cocos2d/core/CCGame.js new file mode 100644 index 00000000000..d70b5abace1 --- /dev/null +++ b/cocos2d/core/CCGame.js @@ -0,0 +1,603 @@ +var EventTarget = require('./event/event-target'); + +/** + * An object to boot the game. + * @class game + */ +var game = /** @lends cc.game# */{ + + EVENT_HIDE: "game_on_hide", + EVENT_SHOW: "game_on_show", + EVENT_RESIZE: "game_on_resize", + EVENT_RENDERER_INITED: "renderer_inited", + + RENDER_TYPE_CANVAS: 0, + RENDER_TYPE_WEBGL: 1, + RENDER_TYPE_OPENGL: 2, + + _eventHide: null, + _eventShow: null, + + _persistRootNodes: [], + + /** + * Key of config + * @constant + * @type {Object} + */ + CONFIG_KEY: { + width: "width", + height: "height", + engineDir: "engineDir", + modules: "modules", + debugMode: "debugMode", + showFPS: "showFPS", + frameRate: "frameRate", + id: "id", + renderMode: "renderMode", + registerSystemEvent: "registerSystemEvent", + jsList: "jsList", + scenes: "scenes" + }, + + // states + _paused: true,//whether the game is paused + _isCloning: false, // deserializing or instantiating + _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, + + // Scenes list + _sceneInfos: [], + + /** + * The outer frame of the game canvas, parent of cc.container. + * @property frame + * @type {Object} + */ + frame: null, + /** + * The container of game canvas, equals to cc.container. + * @property container + * @type {Object} + */ + container: null, + /** + * The canvas of the game, equals to cc._canvas. + * @property canvas + * @type {Object} + */ + canvas: null, + + /** + * Config of game + * @property config + * @type {Object} + */ + config: null, + + /** + * Callback when the scripts of engine have been load. + * @method onStart + * @type {Function} + */ + onStart: null, + + /** + * Callback when game exits. + * @method onStop + * @type {Function} + */ + onStop: null, + +//@Public Methods + +// @Game play control + /** + * Set frameRate of game. + * @method setFrameRate + * @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._paused = true; + self._setAnimFrame(); + self._runMainLoop(); + }, + + /** + * Run the game frame by frame. + * @method step + */ + step: function () { + cc.director.mainLoop(); + }, + + /** + * Pause the game. + * @method pause + */ + pause: function () { + if (this._paused) return; + this._paused = true; + // Pause audio engine + cc.audioEngine && cc.audioEngine._pausePlaying(); + // Pause main loop + if (this._intervalId) + window.cancelAnimationFrame(this._intervalId); + this._intervalId = 0; + }, + + /** + * Resume the game from pause. + * @method resume + */ + resume: function () { + if (!this._paused) return; + this._paused = false; + // Resume audio engine + cc.audioEngine && cc.audioEngine._resumePlaying(); + // Resume main loop + this._runMainLoop(); + }, + + /** + * Check whether the game is paused. + * @method isPaused + * @return {Boolean} + */ + isPaused: function () { + return this._paused; + }, + + /** + * Restart game. + * @method restart + */ + restart: function () { + cc.director.popToSceneStackLevel(0); + // Clean up audio + cc.audioEngine && cc.audioEngine.end(); + + game.onStart(); + }, + +// @Game loading + /** + * Prepare game. + * @param cb + * @method prepare + */ + prepare: function (cb) { + var self = this, + config = self.config, + CONFIG_KEY = self.CONFIG_KEY; + + this._loadConfig(); + + // 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]); + + /** + * @type {cc.EGLView} + * @name cc.view + * @memberof cc + * cc.view is the shared view object. + */ + 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); + /** + * @type {cc.Size} + * @name cc.winSize + * @memberof cc + * cc.winSize is the alias object for the size of the current game window. + */ + 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. + * @method run + * @param {Object|Function} [config] - Pass configuration object or onStart function + * @param {Function} [onStart] - function to be executed after game initialized + */ + run: function (config, onStart) { + if (typeof config === 'function') { + game.onStart = config; + } + else { + if (config) { + game.config = config; + } + if (typeof onStart === 'function') { + game.onStart = onStart; + } + } + + this.prepare(game.onStart && game.onStart.bind(game)); + }, + +// @ Persist root node section + /** + * Add a persistent root node to the game, the persistent node won't be destroyed during scene transition + * @method addPersistRootNode + * @param {ENode} node - The node to be made persistent + */ + addPersistRootNode: function (node) { + if (!node instanceof cc.ENode) + return; + var index = this._persistRootNodes.indexOf(node); + if (index === -1) { + var scene = cc.director._scene; + if (cc.isValid(scene)) { + if (!node.parent) { + node.parent = scene; + } + else if (node.parent !== scene) { + cc.warn('The node can not be made persist because it\'s not under root node.'); + return; + } + this._persistRootNodes.push(node); + node._persistNode = true; + } + } + }, + + /** + * Remove a persistent root node + * @method removePersistRootNode + * @param {ENode} node - The node to be removed from persistent node list + */ + removePersistRootNode: function (node) { + var index = this._persistRootNodes.indexOf(node); + if (index !== -1) { + this._persistRootNodes.splice(index, 1); + } + node._persistNode = false; + }, + + /** + * Check whether the node is a persistent root node + * @method isPersistRootNode + * @param {ENode} node - The node to be checked + * @return {Boolean} + */ + isPersistRootNode: function (node) { + return node._persistNode; + }, + +//@Private Methods + +// @Time ticker section + _setAnimFrame: function () { + this._lastTime = new Date(); + this._frameTime = 1000 / game.config[game.CONFIG_KEY.frameRate]; + if((cc.sys.os === cc.sys.OS_IOS && cc.sys.browserType === cc.sys.BROWSER_TYPE_WECHAT) || game.config[game.CONFIG_KEY.frameRate] !== 60) { + 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, game._frameTime - (currTime - game._lastTime)); + var id = window.setTimeout(function() { callback(); }, + timeToCall); + 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; + + director.setDisplayStats(config[CONFIG_KEY.showFPS]); + + callback = function () { + if (!self._paused) { + director.mainLoop(); + if(self._intervalId) + window.cancelAnimationFrame(self._intervalId); + self._intervalId = window.requestAnimFrame(callback); + } + }; + + window.requestAnimFrame(callback); + self._paused = false; + }, + +// @Game loading section + _loadConfig: function () { + // Load config + // Already loaded + if (this.config) { + this._initConfig(this.config); + return; + } + // Load from document.ccConfig + if (document["ccConfig"]) { + this._initConfig(document["ccConfig"]); + } + // Load from project.json + else { + try { + 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 _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'); + } + txt = cc.loader._loadTxtSync(_src); + } + if(!txt){ + txt = cc.loader._loadTxtSync("project.json"); + } + var data = JSON.parse(txt); + this._initConfig(data || {}); + } catch (e) { + cc.log("Failed to read or parse project.json"); + this._initConfig({}); + } + } + }, + + _initConfig: function (config) { + var CONFIG_KEY = this.CONFIG_KEY, + modules = config[CONFIG_KEY.modules]; + + // Configs adjustment + config[CONFIG_KEY.showFPS] = config[CONFIG_KEY.showFPS] || false; + 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.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); + + // Scene parser + this._sceneInfos = this._sceneInfos.concat(config[CONFIG_KEY.scenes]); + + this.config = config; + }, + + _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[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 = 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); + localCanvas.style.outline = "none"; + localConStyle = localContainer.style; + localConStyle.width = (width || 480) + "px"; + localConStyle.height = (height || 320) + "px"; + localConStyle.margin = "0 auto"; + + localConStyle.position = 'relative'; + localConStyle.overflow = 'hidden'; + localContainer.top = '100%'; + + if (cc._renderType === game.RENDER_TYPE_WEBGL) { + this._renderContext = cc._renderContext = cc.webglContext + = cc.create3DContext(localCanvas, { + 'stencil': true, + 'preserveDrawingBuffer': true, + 'antialias': !cc.sys.isMobile, + 'alpha': true + }); + } + // WebGL context created successfully + if (this._renderContext) { + cc.renderer = cc.rendererWebGL; + win.gl = this._renderContext; // global variable declared in CCMacro.js + cc.shaderCache._init(); + cc._drawingUtil = new cc.DrawingPrimitiveWebGL(this._renderContext); + cc.textureCache._initializingRenderer(); + } else { + 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; + game.canvas.oncontextmenu = function () { + if (!cc._isContextMenuEnable) return false; + }; + + this.emit(this.EVENT_RENDERER_INITED, true); + + this._rendererInitialized = true; + }, + + _initEvents: function () { + var win = window, self = this, hidden, visibilityChange, _undef = "undefined"; + + this._eventHide = this._eventHide || new cc.Event.EventCustom(this.EVENT_HIDE); + this._eventHide.setUserData(this); + this._eventShow = this._eventShow || new cc.Event.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.js.isUndefined(document.hidden)) { + hidden = "hidden"; + visibilityChange = "visibilitychange"; + } else if (!cc.js.isUndefined(document.mozHidden)) { + hidden = "mozHidden"; + visibilityChange = "mozvisibilitychange"; + } else if (!cc.js.isUndefined(document.msHidden)) { + hidden = "msHidden"; + visibilityChange = "msvisibilitychange"; + } else if (!cc.js.isUndefined(document.webkitHidden)) { + hidden = "webkitHidden"; + visibilityChange = "webkitvisibilitychange"; + } + + var onHidden = function () { + if (cc.eventManager && game._eventHide) + cc.eventManager.dispatchEvent(game._eventHide); + }; + var onShow = function () { + if (cc.eventManager && game._eventShow) + cc.eventManager.dispatchEvent(game._eventShow); + }; + + if (hidden) { + document.addEventListener(visibilityChange, function () { + if (document[hidden]) onHidden(); + else onShow(); + }, false); + } else { + win.addEventListener("blur", onHidden, false); + win.addEventListener("focus", onShow, false); + } + + if(navigator.userAgent.indexOf("MicroMessenger") > -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(game.EVENT_HIDE, function () { + game.pause(); + }); + cc.eventManager.addCustomListener(game.EVENT_SHOW, function () { + game.resume(); + }); + } +}; + +EventTarget.polyfill(game); + +cc.game = module.exports = game; diff --git a/cocos2d/core/CCNode.js b/cocos2d/core/CCNode.js new file mode 100644 index 00000000000..a39c1a06729 --- /dev/null +++ b/cocos2d/core/CCNode.js @@ -0,0 +1,546 @@ +/**************************************************************************** + Copyright (c) 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. + ****************************************************************************/ + +var JS = cc.js; +var Flags = cc.Object.Flags; +var Destroying = Flags.Destroying; +var DontDestroy = Flags.DontDestroy; +//var RegisteredInEditor = Flags.RegisteredInEditor; + + + +/** + * Class of all entities in Fireball scenes. + * @class ENode + * @extends _BaseNode + */ +var Node = cc.Class({ + name: 'cc.Node', + extends: require('./utils/base-node'), + + properties: { + /** + * The local active state of this node. + * @property active + * @type {Boolean} + * @default true + */ + active: { + get: function () { + return this._active; + }, + set: function (value) { + value = !!value; + if (this._active !== value) { + this._active = value; + var canActiveInHierarchy = (this._parent && this._parent._activeInHierarchy); + if (canActiveInHierarchy) { + this._onActivatedInHierarchy(value); + } + } + } + }, + + /** + * Indicates whether this node is active in the scene. + * @property activeInHierarchy + * @type {Boolean} + */ + activeInHierarchy: { + get: function () { + return this._activeInHierarchy; + } + }, + + // internal properties + + _active: true, + + /** + * @property _components + * @type {Component[]} + * @default [] + * @readOnly + * @private + */ + _components: [], + + /** + * The PrefabInfo object + * @property _prefab + * @type {PrefabInfo} + * @private + */ + _prefab: { + default: null, + editorOnly: true + }, + + /** + * If true, the node is an persist node which won't be destroyed during scene transition. + * If false, the node will be destroyed automatically when loading a new scene. Default is false. + * @property _persistNode + * @type {Boolean} + * @default false + * @private + */ + _persistNode: { + get: function () { + return (this._objFlags & DontDestroy) > 0; + }, + set: function (value) { + if (value) { + this._objFlags |= DontDestroy; + } + else { + this._objFlags &= ~DontDestroy; + } + } + } + }, + + ctor: function () { + var name = arguments[0]; + this._name = typeof name !== 'undefined' ? name : 'New Node'; + this._activeInHierarchy = false; + + // cache component + this._widget = null; + + /** + * Register all related EventTargets, + * all event callbacks will be removed in _onPreDestroy + * @property __eventTargets + * @type {EventTarget[]} + * @private + */ + this.__eventTargets = []; + }, + + statics: { + _DirtyFlags: require('./utils/misc').DirtyFlags + }, + + // OVERRIDES + + destroy: function () { + if (cc.Object.prototype.destroy.call(this)) { + // disable hierarchy + if (this._activeInHierarchy) { + this._deactivateChildComponents(); + } + } + }, + + _onPreDestroy: function () { + var i, len; + + // marked as destroying + this._objFlags |= Destroying; + + // detach self and children from editor + var parent = this._parent; + var destroyByParent = parent && (parent._objFlags & Destroying); + if ( !destroyByParent ) { + if (CC_EDITOR || CC_TEST) { + this._registerIfAttached(false); + } + } + + // destroy children + var children = this._children; + for (i = 0, len = children.length; i < len; ++i) { + // destroy immediate so its _onPreDestroy can be called + children[i]._destroyImmediate(); + } + + // destroy self components + for (i = 0, len = this._components.length; i < len; ++i) { + var component = this._components[i]; + // destroy immediate so its _onPreDestroy can be called + component._destroyImmediate(); + } + + // Remove all listeners + for (i = 0, len = this.__eventTargets.length; i < len; ++i) { + var target = this.__eventTargets[i]; + target && target.targetOff && target.targetOff(this); + } + this.__eventTargets.length = 0; + + // remove from persist + if (this._persistNode) { + cc.game.removePersistRootNode(this); + } + + if ( !destroyByParent ) { + // remove from parent + if (parent) { + parent._children.splice(parent._children.indexOf(this), 1); + } + + this._removeSgNode(); + + // simulate some destruct logic to make undo system work correctly + if (CC_EDITOR) { + // ensure this node can reattach to scene by undo system + this._parent = null; + } + } + }, + + // COMPONENT + + /** + * Returns the component of supplied type if the node has one attached, null if it doesn't. + * You can also get component in the node by passing in the name of the script. + * + * @method getComponent + * @param {Function|String} typeOrClassName + * @returns {Component} + */ + getComponent: function (typeOrClassName) { + if ( !typeOrClassName ) { + cc.error('getComponent: Type must be non-nil'); + return null; + } + var constructor; + if (typeof typeOrClassName === 'string') { + constructor = JS.getClassByName(typeOrClassName); + } + else { + constructor = typeOrClassName; + } + if (constructor) { + for (var c = 0; c < this._components.length; ++c) { + var component = this._components[c]; + if (component instanceof constructor) { + return component; + } + } + } + return null; + }, + + _checkMultipleComp: CC_EDITOR && function (ctor) { + if (this.getComponent(ctor._disallowMultiple)) { + cc.error("The component %s can't be added because %s already contains the same (or subtype) component.", + JS.getClassName(typeOrClassName), this._name); + return false; + } + return true; + }, + + /** + * Adds a component class to the node. You can also add component to entity by passing in the name of the script. + * + * @method addComponent + * @param {Function|String} typeOrClassName - The constructor or the class name of the component to add + * @returns {Component} - The newly added component + */ + addComponent: function (typeOrClassName) { + + if ((this._objFlags & Destroying) && CC_EDITOR) { + cc.error('isDestroying'); + return null; + } + + // check component + + var constructor; + if (typeof typeOrClassName === 'string') { + constructor = JS.getClassByName(typeOrClassName); + if ( !constructor ) { + cc.error('addComponent: Failed to get class "%s"', typeOrClassName); + if (cc._RFpeek()) { + cc.error('addComponent: Should not add component ("%s") when the scripts are still loading.', typeOrClassName); + } + return null; + } + } + else { + if ( !typeOrClassName ) { + cc.error('addComponent: Type must be non-nil'); + return null; + } + constructor = typeOrClassName; + } + if (typeof constructor !== 'function') { + cc.error("addComponent: The component to add must be a constructor"); + return null; + } + if (constructor._disallowMultiple && CC_EDITOR) { + if (!this._checkMultipleComp(constructor)) { + return null; + } + } + + // + + var component = new constructor(); + component.node = this; + this._components.push(component); + + if (this._activeInHierarchy) { + // call onLoad/onEnable + component.__onNodeActivated(true); + } + + return component; + }, + + /** + * This api should only used by undo system + * @method _addComponentAt + * @param {Component} comp + * @param {Number} index + * @private + */ + _addComponentAt: CC_EDITOR && function (comp, index) { + if (this._objFlags & Destroying) { + return cc.error('isDestroying');; + } + if (typeof comp !== 'function') { + return cc.error("_addComponentAt: The component to add must be a constructor"); + } + if (index > this._components.length) { + return cc.error("_addComponentAt: Index out of range"); + } + + var ctor = comp.constructor; + if (ctor._disallowMultiple) { + if (!this._checkMultipleComp(ctor)) { + return; + } + } + + comp.node = this; + this._components.splice(index, 0, comp); + + if (this._activeInHierarchy) { + // call onLoad/onEnable + comp.__onNodeActivated(true); + } + }, + + /** + * Removes a component identified by the given name or removes the component object given. + * You can also use component.destroy() if you already have the reference. + * @method removeComponent + * @param {String|Function|Component} component - The need remove component. + * @deprecated please destroy the component to remove it. + */ + removeComponent: function (component) { + if ( !component ) { + cc.error('removeComponent: Component must be non-nil'); + return null; + } + if (typeof component !== 'object') { + component = this.getComponent(component); + } + if (component) { + component.destroy(); + } + }, + + /** + * Removes all components of cc.ENode. + * @method removeAllComponents + */ + removeAllComponents: function () { + for (var i = 0; i < this._components.length; i++) { + var comp = this._components[i]; + comp.destroy(); + } + }, + + // do remove component, only used internally + _removeComponent: function (component) { + if (!component) { + cc.error('Argument must be non-nil'); + return; + } + if (!(this._objFlags & Destroying)) { + var i = this._components.indexOf(component); + if (i !== -1) { + this._components.splice(i, 1); + component.node = null; + } + else if (component.node !== this) { + cc.error("Component not owned by this entity"); + } + } + }, + + // INTERNAL + + _registerIfAttached: (CC_EDITOR || CC_TEST) && function (register) { + if (register) { + cc.engine.attachedObjsForEditor[this.uuid] = this; + cc.engine.emit('node-attach-to-scene', {target: this}); + //this._objFlags |= RegisteredInEditor; + } + else { + cc.engine.emit('node-detach-from-scene', {target: this}); + delete cc.engine.attachedObjsForEditor[this._id]; + } + var children = this._children; + for (var i = 0, len = children.length; i < len; ++i) { + var child = children[i]; + child._registerIfAttached(register); + } + }, + + _onActivatedInHierarchy: function (newActive) { + this._activeInHierarchy = newActive; + + // component maybe added during onEnable, and the onEnable of new component is already called + // so we should record the origin length + var originCount = this._components.length; + for (var c = 0; c < originCount; ++c) { + var component = this._components[c]; + if (! (component instanceof cc.Component) && CC_EDITOR) { + cc.error('Sorry, the component of entity "%s" which with an index of %s is corrupted! It has been removed.\nSee DevTools for details.', this.name, c); + console.log('Corrupted component value:', component); + this._removeComponent(component); + --c; + --originCount; + } + else { + component.__onNodeActivated(newActive); + } + } + // activate children recursively + for (var i = 0, len = this.childrenCount; i < len; ++i) { + var child = this._children[i]; + if (child._active) { + child._onActivatedInHierarchy(newActive); + } + } + }, + + _onHierarchyChanged: function (oldParent) { + if (this._persistNode && !(this._parent instanceof cc.EScene)) { + cc.game.removePersistRootNode(this); + if (CC_EDITOR) { + cc.warn('Set "%s" to normal node (not persist root node).'); + } + } + var activeInHierarchyBefore = this._active && !!(oldParent && oldParent._activeInHierarchy); + var shouldActiveNow = this._active && !!(this._parent && this._parent._activeInHierarchy); + if (activeInHierarchyBefore !== shouldActiveNow) { + this._onActivatedInHierarchy(shouldActiveNow); + } + if (CC_EDITOR || CC_TEST) { + var scene = cc.director.getScene(); + var inCurrentSceneBefore = oldParent && oldParent.isChildOf(scene); + var inCurrentSceneNow = this._parent && this._parent.isChildOf(scene); + if (!inCurrentSceneBefore && inCurrentSceneNow) { + // attached + this._registerIfAttached(true); + } + else if (inCurrentSceneBefore && !inCurrentSceneNow) { + // detached + this._registerIfAttached(false); + } + } + }, + + _deactivateChildComponents: function () { + // å’Œ _onActivatedInHierarchy 类似但ä¸ä¿®æ”¹ this._activeInHierarchy + var originCount = this._components.length; + for (var c = 0; c < originCount; ++c) { + var component = this._components[c]; + component.__onNodeActivated(false); + } + // deactivate children recursively + for (var i = 0, len = this.childrenCount; i < len; ++i) { + var entity = this._children[i]; + if (entity._active) { + entity._deactivateChildComponents(); + } + } + }, + + _instantiate: function () { + var clone = cc.instantiate._clone(this, this); + clone._parent = null; + + // init + if (CC_EDITOR && cc.engine._isPlaying) { + this._name += ' (Clone)'; + } + clone._onBatchCreated(); + + return clone; + }, + + _onColorChanged: function () { + // update components if also in scene graph + for (var c = 0; c < this._components.length; ++c) { + var comp = this._components[c]; + if (comp instanceof cc._ComponentInSG && comp.isValid) { + comp._sgNode.setColor(this._color); + if ( !this._cascadeOpacityEnabled ) { + comp._sgNode.setOpacity(this._opacity); + } + } + } + }, + + _onCascadeChanged: function () { + // update components which also in scene graph + var opacity = this._cascadeOpacityEnabled ? 255 : this._opacity; + for (var c = 0; c < this._components.length; ++c) { + var comp = this._components[c]; + if (comp instanceof cc._ComponentInSG && comp.isValid) { + comp._sgNode.setOpacity(opacity); + } + } + }, + + _onAnchorChanged: function () { + // update components if also in scene graph + for (var c = 0; c < this._components.length; ++c) { + var comp = this._components[c]; + if (comp instanceof cc._ComponentInSG && comp.isValid) { + comp._sgNode.setAnchorPoint(this._anchorPoint); + comp._sgNode.ignoreAnchorPointForPosition(this._ignoreAnchorPointForPosition); + } + } + }, + + _onOpacityModifyRGBChanged: function () { + for (var c = 0; c < this._components.length; ++c) { + var comp = this._components[c]; + if (comp instanceof cc._ComponentInSG && comp.isValid) { + comp._sgNode.setOpacityModifyRGB(this._opacityModifyRGB); + } + } + } + +}); + +// TODO - 这个类å是临时的,之åŽè¦æ”¹åæˆ cc.Node,å†å¯¹å¤–å±è”½åŽŸ cc.Node +cc.ENode = module.exports = Node; diff --git a/cocos2d/core/CCScene.js b/cocos2d/core/CCScene.js new file mode 100644 index 00000000000..3c3fb6ac94a --- /dev/null +++ b/cocos2d/core/CCScene.js @@ -0,0 +1,136 @@ +/**************************************************************************** + Copyright (c) 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. + ****************************************************************************/ + +var NIL = function () {}; + +/** + *

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

+ *

cc.Scene and cc.Node are almost identical with the difference that users can not modify cc.Scene manually.

+ * + * @class EScene + * @extends _BaseNode + */ +cc.EScene = cc.Class({ + name: 'cc.Scene', + extends: require('./utils/base-node'), + + ctor: function () { + var sgNode = this._sgNode = new cc.Scene(); + sgNode.setAnchorPoint(0.0, 0.0); + + this._activeInHierarchy = false; + this._inited = !cc.game._isCloning; + }, + + destroy: function () { + var children = this._children; + var DontDestroy = cc.Object.Flags.DontDestroy; + + for (var i = 0, len = children.length; i < len; ++i) { + var child = children[i]; + if (child.isValid) { + if (!(child._objFlags & DontDestroy)) { + child.destroy(); + } + } + } + + this._super(); + this._activeInHierarchy = false; + }, + + _onHierarchyChanged: NIL, + _onColorChanged: NIL, + _onAnchorChanged: NIL, + _onOpacityModifyRGBChanged: NIL, + _onCascadeChanged: NIL, + + _load: function () { + if ( ! this._inited) { + this._onBatchCreated(); + this._inited = true; + } + }, + + _activate: function () { + var i, child, children = this._children, len = children.length; + + if (CC_EDITOR || CC_TEST) { + // register all nodes to editor + for (i = 0; i < len; ++i) { + child = children[i]; + child._registerIfAttached(true); + } + } + + this._activeInHierarchy = true; + + // invoke onLoad and onEnable + for (i = 0; i < len; ++i) { + child = children[i]; + if (child._active) { + child._onActivatedInHierarchy(true); + } + } + } +}); + +module.exports = cc.EScene; + +if (CC_EDITOR) { + var ERR = '"%s" is not defined in the Scene, it is only defined in child nodes.'; + Object.defineProperties(cc.EScene.prototype, { + active: { + get: function () { + cc.error(ERR, 'active'); + return true; + }, + set: function () { + cc.error(ERR, 'active'); + } + }, + activeInHierarchy: { + get: function () { + cc.error(ERR, 'activeInHierarchy'); + return true; + }, + }, + getComponent: { + get: function () { + cc.error(ERR, 'getComponent'); + return function () { + return null; + }; + } + }, + addComponent: { + get: function () { + cc.error(ERR, 'addComponent'); + return function () { + return null; + }; + } + }, + }); +} \ No newline at end of file diff --git a/cocos2d/core/CCScheduler.js b/cocos2d/core/CCScheduler.js new file mode 100644 index 00000000000..90078e6bae2 --- /dev/null +++ b/cocos2d/core/CCScheduler.js @@ -0,0 +1,1065 @@ +/**************************************************************************** + 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. + ****************************************************************************/ + +/** + * Minimum priority level for user scheduling. + * @constant + * @type Number + */ +cc.PRIORITY_NON_SYSTEM = cc.PRIORITY_SYSTEM + 1; + +//data structures +/** + * A list double-linked list used for "updates with priority" + * @Class + * @name cc.ListEntry + * @param {cc.ListEntry} prev + * @param {cc.ListEntry} next + * @param {function} callback + * @param {cc._Class} target not retained (retained by hashUpdateEntry) + * @param {Number} priority + * @param {Boolean} paused + * @param {Boolean} markedForDeletion selector will no longer be called and entry will be removed at end of the next tick + */ +cc.ListEntry = function (prev, next, callback, target, priority, paused, markedForDeletion) { + this.prev = prev; + this.next = next; + this.callback = callback; + this.target = target; + this.priority = priority; + this.paused = paused; + this.markedForDeletion = markedForDeletion; +}; +cc.ListEntry.prototype.trigger = function (dt) { + this.callback.call(this.target, dt); +}; + +/** + * A update entry list + * @Class + * @name cc.HashUpdateEntry + * @param {Array} list Which list does it belong to ? + * @param {cc.ListEntry} entry entry in the list + * @param {cc._Class} target hash key (retained) + * @param {function} callback + * @param {Array} hh + */ +cc.HashUpdateEntry = function (list, entry, target, callback, hh) { + this.list = list; + this.entry = entry; + this.target = target; + this.callback = callback; + this.hh = hh; +}; + +// +/** + * Hash Element used for "selectors with interval" + * @Class + * @param {Array} timers + * @param {cc._Class} target hash key (retained) + * @param {Number} timerIndex + * @param {cc.Timer} currentTimer + * @param {Boolean} currentTimerSalvaged + * @param {Boolean} paused + * @param {Array} hh + */ +cc.HashTimerEntry = cc.hashSelectorEntry = function (timers, target, timerIndex, currentTimer, currentTimerSalvaged, paused, hh) { + var _t = this; + _t.timers = timers; + _t.target = target; + _t.timerIndex = timerIndex; + _t.currentTimer = currentTimer; + _t.currentTimerSalvaged = currentTimerSalvaged; + _t.paused = paused; + _t.hh = hh; +}; + +/** + * Light weight timer + * @class Timer + * @extends _Class + */ +cc.Timer = cc._Class.extend(/** @lends cc.Timer# */{ + _scheduler: null, + _elapsed:0.0, + _runForever:false, + _useDelay:false, + _timesExecuted:0, + _repeat:0, //0 = once, 1 is 2 x executed + _delay:0, + _interval:0.0, + + /** + * @return {Number} returns interval of timer + */ + getInterval : function(){return this._interval;}, + /** + * @param {Number} interval set interval in seconds + */ + setInterval : function(interval){this._interval = interval;}, + + setupTimerWithInterval: function(seconds, repeat, delay){ + this._elapsed = -1; + this._interval = seconds; + this._delay = delay; + this._useDelay = (this._delay > 0); + this._repeat = repeat; + this._runForever = (this._repeat === cc.REPEAT_FOREVER); + }, + + trigger: function(){ + return 0; + }, + + cancel: function(){ + return 0; + }, + + /** + * cc.Timer's Constructor + * Constructor of cc.Timer + */ + ctor:function () { + this._scheduler = null; + this._elapsed = -1; + this._runForever = false; + this._useDelay = false; + this._timesExecuted = 0; + this._repeat = 0; + this._delay = 0; + this._interval = 0; + }, + + /** + * 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._runForever && this._timesExecuted > this._repeat) + this.cancel(); + } + } + } +}); + +cc.TimerTargetSelector = cc.Timer.extend({ + _target: null, + _selector: null, + + ctor: function(){ + this._target = null; + this._selector = null; + }, + + initWithSelector: function(scheduler, selector, target, seconds, repeat, delay){ + this._scheduler = scheduler; + this._target = target; + this._selector = selector; + this.setupTimerWithInterval(seconds, repeat, delay); + return true; + }, + + getSelector: function(){ + return this._selector; + }, + + trigger: function(){ + //override + if (this._target && this._selector){ + this._target.call(this._selector, this._elapsed); + } + }, + + cancel: function(){ + //override + this._scheduler.unschedule(this._selector, this._target); + } + +}); + +cc.TimerTargetCallback = cc.Timer.extend({ + + _target: null, + _callback: null, + _key: null, + + ctor: function(){ + this._target = null; + this._callback = null; + }, + + initWithCallback: function(scheduler, callback, target, key, seconds, repeat, delay){ + this._scheduler = scheduler; + this._target = target; + this._callback = callback; + this._key = key; + this.setupTimerWithInterval(seconds, repeat, delay); + return true; + }, + + getCallback: function(){ + return this._callback; + }, + + getKey: function(){ + return this._key; + }, + + trigger: function(){ + //override + if(this._callback) + this._callback.call(this._target, this._elapsed); + }, + + cancel: function(){ + //override + this._scheduler.unschedule(this._callback, this._target); + } + +}); + +var getTargetId = function (target) { + return target.uuid || target.__instanceId; +} + +/** + *

+ * 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 Scheduler + * @extends _Class + */ +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[getTargetId(target)]; + 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 way 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[getTargetId(element.target)]; + cc.js.array.remove(this._arrayForTimers, element); + element.Timer = null; + element.target = null; + element = null; + }, + + _removeUpdateFromHash:function (entry) { + var self = this, element = self._hashForUpdates[getTargetId(entry.target)]; + if (element) { + //list entry + cc.js.array.remove(element.list, element.entry); + + delete self._hashForUpdates[getTargetId(element.target)]; + //cc.js.array.remove(self._hashForUpdates, element); + element.entry = null; + + //hash entry + element.target = null; + } + }, + + _priorityIn:function (ppList, callback, target, priority, paused) { + var self = this, + listElement = new cc.ListEntry(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[getTargetId(target)] = new cc.HashUpdateEntry(ppList, listElement, target, null); + + return ppList; + }, + + _appendIn:function (ppList, callback, target, paused) { + var self = this, listElement = new cc.ListEntry(null, null, callback, target, 0, paused, false); + ppList.push(listElement); + + //update hash entry for quicker access + self._hashForUpdates[getTargetId(target)] = new cc.HashUpdateEntry(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. + *

+ * + * @method setTimeScale + * @param {Number} timeScale + */ + setTimeScale:function (timeScale) { + this._timeScale = timeScale; + }, + + /** + * Returns time scale of scheduler. + * @method getTimeScale + * @return {Number} + */ + getTimeScale:function () { + return this._timeScale; + }, + + /** + * 'update' the scheduler. (You should NEVER call this method, unless you know what you are doing.) + * @method update + * @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
+ *

+ * @method scheduleCallbackForTarget + * @deprecated since v3.4 please use .schedule + * @param {_Class} target + * @param {function} callback_fn + * @param {Number} interval + * @param {Number} repeat + * @param {Number} delay + * @param {Boolean} paused + * @example {@link utils/api/cocos/docs/cocos2d/core/CCScheduler/scheduleCallbackForTarget.js} + */ + 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, getTargetId(target) + ""); + }, + + /** + * The schedule + * @method schedule + * @param {Function} callback + * @param {_Class} target + * @param {Number} interval + * @param {Number} repeat + * @param {Number} delay + * @param {Boolean} paused + * @param {Number} key + * @example {@link utils/api/cocos/docs/cocos2d/core/CCScheduler/schedule.js} + */ + schedule: function(callback, target, interval, repeat, delay, paused, key){ + var isSelector = false; + if(typeof callback !== "function"){ + var selector = callback; + isSelector = true; + } + + if(isSelector === false){ + //callback, target, interval, repeat, delay, paused, key + //callback, target, interval, paused, key + if(arguments.length === 4 || arguments.length === 5){ + key = delay; + paused = repeat; + delay = 0; + repeat = cc.REPEAT_FOREVER; + } + }else{ + //selector, target, interval, repeat, delay, paused + //selector, target, interval, paused + if(arguments.length === 4){ + paused = repeat; + repeat = cc.REPEAT_FOREVER; + delay = 0; + } + } + if (key === undefined) { + key = target.__instanceId + ""; + } + + cc.assert(target, cc._LogInfos.Scheduler_scheduleCallbackForTarget_3); + + var instanceId = getTargetId(target); + var element = this._hashForTimers[instanceId]; + + if(!element){ + // Is this the 1st element ? Then set the pause level to all the callback_fns of this target + element = new cc.HashTimerEntry(null, target, 0, null, null, paused, null); + this._arrayForTimers.push(element); + this._hashForTimers[instanceId] = element; + }else{ + cc.assert(element.paused === paused, ""); + } + + var timer, i; + if (element.timers == null) { + element.timers = []; + } else if(isSelector === false) { + 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; + } + } + }else{ + for (i = 0; i < element.timers.length; ++i){ + timer =element.timers[i]; + if (timer && selector === timer.getSelector()){ + cc.log("CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: %.4f to %.4f", timer.getInterval(), interval); + timer.setInterval(interval); + return; + } + } + //ccArrayEnsureExtraCapacity(element->timers, 1); + } + + if(isSelector === false){ + timer = new cc.TimerTargetCallback(); + timer.initWithCallback(this, callback, target, key, interval, repeat, delay); + element.timers.push(timer); + }else{ + timer = new cc.TimerTargetSelector(); + timer.initWithSelector(this, selector, target, interval, repeat, delay); + element.timers.push(timer); + } + }, + + scheduleUpdate: function(target, priority, paused, updateFunc){ + updateFunc = updateFunc || target.update; + this._schedulePerFrame(updateFunc, target, priority, paused); + }, + + _getUnscheduleMark: function(key, timer){ + //key, callback, selector + switch (typeof key){ + case "number": + case "string": + return key === timer.getKey(); + case "function": + return key === timer._callback; + default: + return key === timer.getSelector(); + } + }, + 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[getTargetId(target)]; + 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); + //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 == null) + return; + + var element = this._hashForUpdates[getTargetId(target)]; + + if (element){ + if (this._updateHashLocked){ + element.entry.markedForDeletion = true; + }else{ + this._removeUpdateFromHash(element.entry); + } + } + }, + + unscheduleAllForTarget: function(target){ + // explicit nullptr handling + if (target == null){ + return; + } + + // Custom Selectors + var element = this._hashForTimers[getTargetId(target)]; + + if (element){ + if (element.timers.indexOf(element.currentTimer) > -1 + && (! element.currentTimerSalvaged)){ + element.currentTimerSalvaged = true; + } + // ccArrayRemoveAllObjects(element.timers); + element.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(key, target){ + //key, target + //selector, target + cc.assert(key, "Argument key must not be empty"); + cc.assert(target, "Argument target must be non-nullptr"); + + var element = this._hashForUpdates[getTargetId(target)]; + + 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 (key === timer.getKey()){ + return true; + } + } + return false; + } + }, + + /** + *

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

+ * + * @method pauseAllTargets + */ + 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. + * @method pauseAllTargetsWithMinPriority + * @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. + * @method resumeTargets + * @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. + *

+ * @method pauseTarget + * @param {_Class} target + */ + pauseTarget:function (target) { + + cc.assert(target, cc._LogInfos.Scheduler.pauseTarget); + + //customer selectors + var self = this, + instanceId = getTargetId(target), + element = self._hashForTimers[instanceId]; + if (element) { + element.paused = true; + } + + //update callback + var elementUpdate = self._hashForUpdates[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. + * @method resumeTarget + * @param {_Class} target + */ + resumeTarget:function (target) { + + cc.assert(target, cc._LogInfos.Scheduler.resumeTarget); + + // custom selectors + var self = this, + instanceId = getTargetId(target), + element = self._hashForTimers[instanceId]; + + if (element) { + element.paused = false; + } + + //update callback + var elementUpdate = self._hashForUpdates[instanceId]; + + if (elementUpdate) { + elementUpdate.entry.paused = false; + } + }, + + /** + * Returns whether or not the target is paused + * @method isTargetPaused + * @param {_Class} target + * @return {Boolean} + */ + isTargetPaused:function (target) { + + cc.assert(target, cc._LogInfos.Scheduler.isTargetPaused); + + // Custom selectors + var instanceId = getTargetId(target), + element = this._hashForTimers[instanceId]; + if (element) { + return element.paused; + } + var elementUpdate = this._hashForUpdates[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. + *

+ * @method scheduleUpdateForTarget + * @deprecated since v3.4 please use .scheduleUpdate + * @param {_Class} target + * @param {Number} priority + * @param {Boolean} paused + * @example {@link utils/api/cocos/docs/cocos2d/core/CCScheduler/scheduleUpdateForTarget.js} + */ + 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. + *

+ * @method unscheduleCallbackForTarget + * @deprecated since v3.4 please use .unschedule + * @param {_Class} target + * @param {Function} callback - callback[Function] or key[String] + * @example {@link utils/api/cocos/docs/cocos2d/core/CCScheduler/unscheduleCallbackForTarget.js} + */ + 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 + * @method unscheduleUpdateForTarget + * @param {_Class} target + * @deprecated since v3.4 please use .unschedule + * @example {@link utils/api/cocos/docs/cocos2d/core/CCScheduler/unscheduleUpdateForTarget.js} + */ + 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. + * @method unscheduleAllCallbacksForTarget + * @deprecated since v3.4 please use .unscheduleAll + * @param {_Class} target + */ + unscheduleAllCallbacksForTarget: function(target){ + //cc.log("unscheduleAllCallbacksForTarget is deprecated. Please use unscheduleAll."); + this.unschedule(getTargetId(target) + "", target); + }, + + /** + *

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

+ * @method unscheduleAllCallbacks + * @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. + *

+ * @method unscheduleAllCallbacksWithMinPriority + * @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); diff --git a/cocos2d/core/assets/CCAsset.js b/cocos2d/core/assets/CCAsset.js new file mode 100644 index 00000000000..614a85065c3 --- /dev/null +++ b/cocos2d/core/assets/CCAsset.js @@ -0,0 +1,154 @@ +var RawAsset = require('./CCRawAsset'); +/** + * Base class for handling assets used in Fireball. This class can be instantiate. + * + * You may want to override: + * - createNode + * - cc.Object._serialize + * - cc.Object._deserialize + * + * @class Asset + * @extends RawAsset + * @constructor + */ +cc.Asset = cc.Class({ + name: 'cc.Asset', extends: RawAsset, + + ctor: function () { + /** + * @property _uuid + * @type {String} + * @private + */ + Object.defineProperty(this, '_uuid', { + value: '', + writable: true, + enumerable: false // avoid uuid being assigned to empty string during destroy, + }); + }, + + properties: { + /** + * Returns the url of this asset's first raw file, if none of rawFile exists, + * it will returns the url of this serialized asset. + * + * @property url + * @type {String} + * @readOnly + */ + url: { + get: function () { + if (this._rawFiles) { + if (cc.AssetLibrary) { + var url = cc.AssetLibrary.getImportedDir(this._uuid); + var filename = this._rawFiles[0]; + return url + cc.path.sep + filename; + } + else { + cc.error('asset.url is not usable in core process'); + } + } + return ''; + }, + visible: false + }, + + /** + * Returns the url of this asset's raw files, if none of rawFile exists, + * it will returns an empty array. + * + * @property urls + * @type {String[]} + * @readOnly + */ + urls: { + get: function () { + if (this._rawFiles) { + if (cc.AssetLibrary) { + var url = cc.AssetLibrary.getImportedDir(this._uuid); + return this._rawFiles.map(function (filename) { + return url + cc.path.sep + filename; + }); + } + else { + cc.error('asset.urls is not usable in core process'); + } + } + return []; + }, + visible: false + }, + + /** + * 在 lite 版的 Fireball 里,raw asset 并ä¸ä»…仅是在 properties 里声明了 rawType æ‰æœ‰ï¼Œ + * 而是æ¯ä¸ª asset 都能指定自己的 raw file url。这些 url 就存在 _rawFiles 字段中。 + * AssetLibrary 并ä¸ä¼šå¸®ä½ åŠ è½½è¿™äº› url,除éžä½ å£°æ˜Žäº† rawType。 + * + * @property _rawFiles + * @type {String[]} + * @default null + * @private + */ + _rawFiles: null + }, + + statics: { + /** + * 应 AssetDB è¦æ±‚æ供这个方法 + * + * @method deserialize + * @param {String} data + * @return {Asset} + * @static + * @private + */ + deserialize: function (data) { + return cc.deserialize(data); + } + }, + + /** + * 应 AssetDB è¦æ±‚æ供这个方法 + * + * @method serialize + * @return {String} + * @private + */ + serialize: function () { + return Editor.serialize(this); + }, + + /** + * Create a new node using this asset in the scene. + * If this type of asset dont have its corresponding node type, this method should be null. + * + * @method createNode + * @param {Function} callback + * @param {String} callback.error - null or the error info + * @param {Object} callback.node - the created node or null + */ + createNode: null, + + /** + * Set raw extname for this asset. + * + * @method _setRawFiles + * @param {String[]} rawFiles + * @private + */ + _setRawFiles: function (rawFiles) { + rawFiles = rawFiles.map(function (item) { + if (item.charAt(0) === '.') { + item = item.slice(1); + } + var nextChar = item.charAt(0); + if (nextChar === '/' || nextChar === '\\') { + item = item.slice(1); + } + return item; + }); + this._rawFiles = rawFiles.length > 0 ? rawFiles : null; + } +}); + +module.exports = cc.Asset; diff --git a/cocos2d/core/assets/CCAudioClip.js b/cocos2d/core/assets/CCAudioClip.js new file mode 100644 index 00000000000..b45bbfba76d --- /dev/null +++ b/cocos2d/core/assets/CCAudioClip.js @@ -0,0 +1,13 @@ +/** + * Class for audio data handling. + * @class AudioClip + * @extends RawAsset + * @constructor + */ +var AudioClip = cc.Class({ + name: 'cc.AudioClip', + extends: cc.RawAsset, +}); + +cc.AudioClip = AudioClip; +module.exports = AudioClip; diff --git a/cocos2d/core/assets/CCBitmapFont.js b/cocos2d/core/assets/CCBitmapFont.js new file mode 100644 index 00000000000..682d751a6bc --- /dev/null +++ b/cocos2d/core/assets/CCBitmapFont.js @@ -0,0 +1,13 @@ +/** + * Class for BitmapFont handling. + * @class BitmapFont + * @extends RawAsset + * @constructor + */ +var BitmapFont = cc.Class({ + name: 'cc.BitmapFont', + extends: cc.RawAsset, +}); + +cc.BitmapFont = BitmapFont; +module.exports = BitmapFont; diff --git a/cocos2d/core/assets/CCPrefab.js b/cocos2d/core/assets/CCPrefab.js new file mode 100644 index 00000000000..e439c182cdc --- /dev/null +++ b/cocos2d/core/assets/CCPrefab.js @@ -0,0 +1,43 @@ +function visitWrapper (wrapper, visitor) { + visitor(wrapper); + + var children = wrapper._children; + for (var i = 0; i < children.length; i++) { + visitor(children[i]); + } +} + +/** + * Class for prefab handling. + * @class Prefab + * @extends Asset + * @constructor + */ +var Prefab = cc.Class({ + name: 'cc.Prefab', + extends: cc.Asset, + + properties: { + data: null + }, + + createNode: function (cb) { + if (CC_EDITOR) { + var node = cc.instantiate(this); + cb(null, node); + } + }, + + _instantiate: function () { + // instantiate + var node = cc.instantiate(this.data); + + if (CC_EDITOR || CC_TEST) { + Editor.PrefabUtils.onPrefabInstantiated(this, node); + } + + return node; + } +}); + +cc._Prefab = module.exports = Prefab; diff --git a/cocos2d/core/assets/CCRawAsset.js b/cocos2d/core/assets/CCRawAsset.js new file mode 100644 index 00000000000..c1c4288e820 --- /dev/null +++ b/cocos2d/core/assets/CCRawAsset.js @@ -0,0 +1,30 @@ +var CCObject = require('../platform/CCObject'); +/** + * The base class for registering asset types. + * + * You may want to override: + * - createNode (static) + * + * @class RawAsset + * @extends CCObject + * @static + */ +cc.RawAsset = cc.Class({ + name: 'cc.RawAsset', extends: CCObject, + + statics: { + /** + * Create a new node in the scene. + * If this type of asset dont have its corresponding node type, this method should be null. + * + * @method createNodeByInfo + * @param {Object} Info + * @param {Function} callback + * @param {String} callback.error - null or the error info + * @param {Object} callback.node - the created node or null + */ + createNodeByInfo: null, + } +}); + +module.exports = cc.RawAsset; diff --git a/cocos2d/core/assets/CCSceneAsset.js b/cocos2d/core/assets/CCSceneAsset.js new file mode 100644 index 00000000000..ebfba6b1b0b --- /dev/null +++ b/cocos2d/core/assets/CCSceneAsset.js @@ -0,0 +1,17 @@ +/** + * Class for scene handling. + * @class Scene + * @extends Asset + * @constructor + */ +var Scene = cc.Class({ + name: 'cc.SceneAsset', + extends: cc.Asset, + + properties: { + scene: null + }, +}); + +cc.SceneAsset = Scene; +module.exports = Scene; diff --git a/cocos2d/core/assets/CCScripts.js b/cocos2d/core/assets/CCScripts.js new file mode 100644 index 00000000000..41cf78f8f88 --- /dev/null +++ b/cocos2d/core/assets/CCScripts.js @@ -0,0 +1,38 @@ +/** + * Class for script handling. + * @class _Script + * @extends Asset + * @constructor + */ +var Script = cc.Class({ + name: 'cc.Script', + extends: cc.Asset, +}); + +cc._Script = Script; + +/** + * Class for JavaScript handling. + * @class _JavaScript + * @extends Asset + * @constructor + */ +var JavaScript = cc.Class({ + name: 'cc.JavaScript', + extends: Script, +}); + +cc._JavaScript = JavaScript; + +/** + * Class for coffee script handling. + * @class CoffeeScript + * @extends Asset + * @constructor + */ +var CoffeeScript = cc.Class({ + name: 'cc.CoffeeScript', + extends: Script, +}); + +cc._CoffeeScript = CoffeeScript; diff --git a/cocos2d/core/assets/CCSpriteAnimation.js b/cocos2d/core/assets/CCSpriteAnimation.js new file mode 100644 index 00000000000..377d6127b4b --- /dev/null +++ b/cocos2d/core/assets/CCSpriteAnimation.js @@ -0,0 +1,86 @@ +/** + * Class for sprite animation asset handling. + * @class SpriteAnimationAsset + * @extends Asset + * @constructor + */ +var SpriteAnimationAsset = cc.Class({ + name: 'cc.SpriteAnimationAsset', + extends: cc.Asset, + + properties: { + 0: { + default: '', + url: cc.Texture2D + }, + + 1: { + default: '', + url: cc.Texture2D + }, + + 2: { + default: '', + url: cc.Texture2D + }, + + 3: { + default: '', + url: cc.Texture2D + }, + + 4: { + default: '', + url: cc.Texture2D + }, + + 5: { + default: '', + url: cc.Texture2D + }, + + 6: { + default: '', + url: cc.Texture2D + }, + + 7: { + default: '', + url: cc.Texture2D + }, + + 8: { + default: '', + url: cc.Texture2D + }, + + 9: { + default: '', + url: cc.Texture2D + }, + + /** + * + * + * @property loop + * @type {Boolean} + */ + loop: { + default: true + }, + + /** + * + * + * @property delay + * @type {Number} + */ + delay: { + default: 0.5 + } + }, +}); + +cc.SpriteAnimationAsset = SpriteAnimationAsset; + +module.exports = SpriteAnimationAsset; diff --git a/cocos2d/core/assets/CCSpriteAtlas.js b/cocos2d/core/assets/CCSpriteAtlas.js new file mode 100644 index 00000000000..f1b32c0cd12 --- /dev/null +++ b/cocos2d/core/assets/CCSpriteAtlas.js @@ -0,0 +1,13 @@ +/** + * Class for sprite atlas handling. + * @class SpriteAtlas + * @extends RawAsset + * @constructor + */ +var SpriteAtlas = cc.Class({ + name: 'cc.SpriteAtlas', + extends: cc.RawAsset, +}); + +cc.SpriteAtlas = SpriteAtlas; +module.exports = SpriteAtlas; diff --git a/cocos2d/core/assets/CCTTFFont.js b/cocos2d/core/assets/CCTTFFont.js new file mode 100644 index 00000000000..03512962271 --- /dev/null +++ b/cocos2d/core/assets/CCTTFFont.js @@ -0,0 +1,19 @@ +/** + * Class for TTFFont handling. + * @class TTFFont + * @extends Asset + * @constructor + */ +var TTFFont = cc.Class({ + name: 'cc.TTFFont', + extends: cc.Asset, + + properties: { + fontFamily: { + default: '' + } + }, +}); + +cc.TTFFont = TTFFont; +module.exports = TTFFont; diff --git a/cocos2d/core/assets/CCTiledMapAsset.js b/cocos2d/core/assets/CCTiledMapAsset.js new file mode 100644 index 00000000000..652d1392619 --- /dev/null +++ b/cocos2d/core/assets/CCTiledMapAsset.js @@ -0,0 +1,45 @@ +/** + * Class for tiled map asset handling. + * @class TiledMapAsset + * @extends RawAsset + * @constructor + */ +var TiledMapAsset = cc.Class({ + name: 'cc.TiledMapAsset', + extends: cc.RawAsset, + + statics: { + createNodeByInfo: function (info, callback) { + if (CC_EDITOR) { + + var Url = require('fire-url'); + + cc.TiledMapWrapper.preloadTmx( info.url , function (err, textures) { + if (err) { + callback(err); + return; + } + + var node; + try { + node = new cc.TMXTiledMap(info.url); + node._file = info.url; + } + catch(e) { + return callback(e); + } + + var wrapper = cc.getWrapper(node); + wrapper.name = Url.basenameNoExt(info.url); + wrapper._textures = textures; + + return callback(null, node); + }.bind(this) ); + + } + } + } +}); + +cc.TiledMapAsset = TiledMapAsset; +module.exports = TiledMapAsset; diff --git a/cocos2d/core/assets/index.js b/cocos2d/core/assets/index.js new file mode 100644 index 00000000000..24ab7c0c915 --- /dev/null +++ b/cocos2d/core/assets/index.js @@ -0,0 +1,39 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + 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. + ****************************************************************************/ + +require('./CCRawAsset'); +require('./CCAsset'); +require('./CCPrefab'); +require('./CCAudioClip'); +require('./CCBitmapFont'); +require('./CCScripts'); +require('./CCSceneAsset'); +require('../sprites/CCSpriteFrame'); +require('../textures/CCTexture2D'); +require('./CCTTFFont'); +require('./CCSpriteAnimation'); +require('./CCSpriteAtlas'); +require('./CCTiledMapAsset'); diff --git a/cocos2d/core/base-nodes/BaseNodesPropertyDefine.js b/cocos2d/core/base-nodes/BaseNodesPropertyDefine.js new file mode 100644 index 00000000000..2b2639cd896 --- /dev/null +++ b/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/cocos2d/core/base-nodes/CCAtlasNode.js b/cocos2d/core/base-nodes/CCAtlasNode.js new file mode 100644 index 00000000000..e1a26020d2d --- /dev/null +++ b/cocos2d/core/base-nodes/CCAtlasNode.js @@ -0,0 +1,297 @@ +/**************************************************************************** + 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. + ****************************************************************************/ + +EventTarget = require("../cocos2d/core/event/event-target"); + +/** + *

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; + +EventTarget.polyfill(_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/cocos2d/core/base-nodes/CCAtlasNodeCanvasRenderCmd.js b/cocos2d/core/base-nodes/CCAtlasNodeCanvasRenderCmd.js new file mode 100644 index 00000000000..6ab5dbd68f2 --- /dev/null +++ b/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){ + cc.Node.CanvasRenderCmd.call(this, 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/cocos2d/core/base-nodes/CCAtlasNodeWebGLRenderCmd.js b/cocos2d/core/base-nodes/CCAtlasNodeWebGLRenderCmd.js new file mode 100644 index 00000000000..a811afcfab5 --- /dev/null +++ b/cocos2d/core/base-nodes/CCAtlasNodeWebGLRenderCmd.js @@ -0,0 +1,145 @@ +/**************************************************************************** + 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){ + cc.Node.WebGLRenderCmd.call(this, renderableObject); + this._needDraw = true; + this._textureAtlas = null; + this._colorUnmodified = cc.Color.WHITE; + this._colorF32Array = null; + this._uniformColor = null; + + //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; + + this._shaderProgram.use(); + this._shaderProgram._setUniformForMVPMatrixWithMat4(this._stackMatrix); + + 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(){ + var locDisplayedColor = this._displayedColor; + this._colorF32Array = new Float32Array([locDisplayedColor.r / 255.0, locDisplayedColor.g / 255.0, + locDisplayedColor.b / 255.0, 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); + }; +})(); \ No newline at end of file diff --git a/cocos2d/core/base-nodes/CCNode.js b/cocos2d/core/base-nodes/CCNode.js new file mode 100644 index 00000000000..44ca87ae94d --- /dev/null +++ b/cocos2d/core/base-nodes/CCNode.js @@ -0,0 +1,2251 @@ +/**************************************************************************** + 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 node will be moved according to the camera values (camera)
+ * -# 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 {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.GLProgram} shaderProgram - The shader program currently used for this node + * @property {Number} glServerState - The state of OpenGL server side + * @property {cc.Scheduler} scheduler - cc.Scheduler used to schedule all "updates" and timers + */ +cc.Node = cc.Class(/** @lends cc.Node# */{ + name: 'cc._Node', + + properties: { + _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, + + _rotationX: 0, + _rotationY: 0.0, + _scaleX: 1.0, + _scaleY: 1.0, + _position: cc.p(0, 0), + + _skewX: 0.0, + _skewY: 0.0, + _children: [], + _visible: true, + _anchorPoint: cc.p(0, 0), + _contentSize: cc.size(0, 0), + _parent: null, + + // "whole screen" objects. like Scenes and Layers, should set _ignoreAnchorPointForPosition to true + _ignoreAnchorPointForPosition: false, + tag: cc.NODE_TAG_INVALID, + + _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) { + this._localZOrder = localZOrder; + if (this._parent) + this._parent.reorderChild(this, 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._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.Vec2|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._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.Vec2} The position (x,y) of the node in OpenGL coordinates + */ + getPosition: function () { + return cc.p(this._position); + }, + + /** + *

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.Vec2} 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.Vec2|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.Vec2} 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; + }, + + /** + * 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 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 () { + if (!this._actionManager) + this._actionManager = cc.director.getActionManager(); + return this._actionManager; + }, + + /** + *

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 () { + if (!this._scheduler) + this._scheduler = cc.director.getScheduler(); + return this._scheduler; + }, + + /** + *

+ * 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); + + // timers + this._arrayMakeObjectsPerformSelector(this._children, cc.Node._stateCallbackType.cleanup); + }, + + // 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(cc.js.isUndefined(tag)){ + tag = undefined; + name = child._name; + } else if(cc.js.isString(tag)){ + name = tag; + tag = undefined; + } else if(cc.js.isNumber(tag)){ + 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.onEnter(); + // prevent onEnterTransitionDidFinish to be called twice when a node is added in onEnter + if (this._isTransitionFinished) + child.onEnterTransitionDidFinish(); + } + 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.onExitTransitionDidStart(); + node.onExit(); + } + + // If you don't do cleanup, the node's actions will not get removed and the + if (cleanup) + node.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.onExitTransitionDidStart(); + child.onExit(); + } + + // If you don't do cleanup, the child's actions will not get removed and the + if (doCleanup) + child.cleanup(); + + // set parent nil at the end + child.parent = null; + child._renderCmd.detachFromParent(); + cc.js.array.remove(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); + cc.renderer.childrenOrderDirty = this._reorderChildDirty = true; + child.arrivalOrder = cc.s_globalOrderOfArrival; + cc.s_globalOrderOfArrival++; + child._setLocalZOrder(zOrder); + }, + + /** + *

+ * 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= 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 managment + /** + *

+ * 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._arrayMakeObjectsPerformSelector(this._children, cc.Node._stateCallbackType.onEnter); + this.resume(); + }, + + /** + *

+ * 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; + this._arrayMakeObjectsPerformSelector(this._children, cc.Node._stateCallbackType.onEnterTransitionDidFinish); + }, + + /** + *

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 () { + this._arrayMakeObjectsPerformSelector(this._children, cc.Node._stateCallbackType.onExitTransitionDidStart); + }, + + /** + *

+ * 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._arrayMakeObjectsPerformSelector(this._children, cc.Node._stateCallbackType.onExit); + }, + + // 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 = (repeat == null) ? 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 () { + //TODO renderCmd has a WorldTransform + 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.Vec2} worldPoint + * @return {cc.Vec2} + */ + convertToNodeSpace: function (worldPoint) { + return cc.pointApplyAffineTransform(worldPoint, this.getWorldToNodeTransform()); + }, + + /** + * Converts a Point to world space coordinates. The result is in Points. + * @function + * @param {cc.Vec2} nodePoint + * @return {cc.Vec2} + */ + 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.Vec2} worldPoint + * @return {cc.Vec2} + */ + 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.Vec2} nodePoint + * @return {cc.Vec2} + */ + 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.Vec2 + * @function + * @param {cc.Touch} touch The touch object + * @return {cc.Vec2} + */ + 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.Vec2} + */ + convertTouchToNodeSpaceAR: function (touch) { + var point = cc.director.convertToGL(touch.getLocation()); + return this.convertToNodeSpaceAR(point); + }, + + /** + *

+ * 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 () { + // Recursively iterate over children + this._arrayMakeObjectsPerformSelector(this._children, cc.Node._stateCallbackType.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 () { + }, + + /** + * Recursive method that visit its children and draw them + * @function + * @param {cc.Node.RenderCmd} parentCmd + */ + visit: function(parentCmd){ + this._renderCmd.visit(parentCmd); + }, + + /** + * 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); + }, + + /** + * 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); + }, + + /** + * 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; + }, + + _initRendererCmd: function(){ + this._renderCmd = cc.renderer.getRenderCmd(this); + }, + + _createRenderCmd: function(){ + if(cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return new cc.Node.CanvasRenderCmd(this); + else + return new cc.Node.WebGLRenderCmd(this); + } +}); + +cc.Node.extend = function (options) { + return cc._Class.extend.call(cc.Node, options); +}; + +// to support calling this._super in sub class +cc.Node.prototype.ctor = cc.Node; + +/** + * 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, updateTransform: 5, onExitTransitionDidStart: 6, sortAllChildren: 7}; + +cc.assert(cc.js.isFunction(cc._tmp.PrototypeCCNode), cc._LogInfos.MissingFile, "BaseNodesPropertyDefine.js"); +cc._tmp.PrototypeCCNode(); +delete cc._tmp.PrototypeCCNode; diff --git a/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js b/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js new file mode 100644 index 00000000000..e3163f523a0 --- /dev/null +++ b/cocos2d/core/base-nodes/CCNodeCanvasRenderCmd.js @@ -0,0 +1,501 @@ +/**************************************************************************** + 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; + + this.rendering = function (ctx, scaleX, scaleY) { + if (!this._callback) + return; + this._callback.call(this._target, ctx, scaleX, scaleY); + } +}; + +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, all: (1 << 8) - 1}; + +//-------------------------Base ------------------------- +cc.Node.RenderCmd = function(renderable){ + this._dirtyFlag = 1; //need update the transform at first. + + this._node = renderable; + this._needDraw = false; + this._anchorPointInPoints = new cc.Vec2(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}; + this._inverse = {a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0}; + + this._displayedOpacity = 255; + this._displayedColor = cc.color(255, 255, 255, 255); + this._cascadeColorEnabledDirty = false; + this._cascadeOpacityEnabledDirty = false; + + this._curLevel = -1; +}; + +cc.Node.RenderCmd.prototype = { + constructor: cc.Node.RenderCmd, + + 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._dirtyFlag & cc.Node._dirtyFlags.transformDirty) + this._inverse = cc.affineTransformInvert(this.getNodeToParentTransform()); + 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; + }, + + _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(); + } + } + } + } + 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(); + } + } + } + } + this._dirtyFlag = this._dirtyFlag & cc.Node._dirtyFlags.opacityDirty ^ this._dirtyFlag; + }, + + _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(){}, + + updateStatus: function () { + var flags = cc.Node._dirtyFlags, locFlag = this._dirtyFlag; + var colorDirty = locFlag & flags.colorDirty, + opacityDirty = locFlag & flags.opacityDirty; + if(colorDirty) + this._updateDisplayColor(); + + if(opacityDirty) + this._updateDisplayOpacity(); + + if(colorDirty || opacityDirty) + this._updateColor(); + + if(locFlag & flags.transformDirty){ + //update the transform + this.transform(this.getParentRenderCmd(), true); + this._dirtyFlag = this._dirtyFlag & cc.Node._dirtyFlags.transformDirty ^ this._dirtyFlag; + } + }, + + getNodeToParentTransform: function () { + var node = this._node; + if (this._dirtyFlag & cc.Node._dirtyFlags.transformDirty) { + var t = this._transform;// quick reference + + // base position + t.tx = node._position.x; + t.ty = node._position.y; + + // rotation Cos and Sin + var a = 1, b = 0, + c = 0, d = 1; + if (node._rotationX) { + var rotationRadiansX = node._rotationX * 0.017453292519943295; //0.017453292519943295 = (Math.PI / 180); for performance + c = Math.sin(rotationRadiansX); + d = Math.cos(rotationRadiansX); + } + + if (node._rotationY) { + var rotationRadiansY = node._rotationY * 0.017453292519943295; //0.017453292519943295 = (Math.PI / 180); for performance + a = Math.cos(rotationRadiansY); + b = -Math.sin(rotationRadiansY); + } + t.a = a; + t.b = b; + t.c = c; + t.d = d; + + var lScaleX = node._scaleX, lScaleY = node._scaleY; + var appX = this._anchorPointInPoints.x, appY = this._anchorPointInPoints.y; + + // Firefox on Vista and XP crashes + // GPU thread in case of scale(0.0, 0.0) + var sx = (lScaleX < 0.000001 && lScaleX > -0.000001) ? 0.000001 : lScaleX, + sy = (lScaleY < 0.000001 && lScaleY > -0.000001) ? 0.000001 : lScaleY; + + // scale + if (lScaleX !== 1 || lScaleY !== 1) { + a = t.a *= sx; + b = t.b *= sx; + c = t.c *= sy; + d = t.d *= sy; + } + + // skew + if (node._skewX || node._skewY) { + // offset the anchorpoint + var skx = Math.tan(-node._skewX * Math.PI / 180); + var sky = Math.tan(-node._skewY * Math.PI / 180); + if (skx === Infinity) + skx = 99999999; + if (sky === Infinity) + sky = 99999999; + var xx = appY * skx; + var yy = appX * sky; + t.a = a - c * sky; + t.b = b - d * sky; + t.c = c - a * skx; + t.d = d - b * skx; + t.tx += a * xx + c * yy; + t.ty += b * xx + d * yy; + } + + // adjust anchorPoint + t.tx -= a * appX + c * appY; + t.ty -= b * appX + d * appY; + + // if ignore anchorPoint + if (node._ignoreAnchorPointForPosition) { + t.tx += appX; + t.ty += appY; + } + + if (node._additionalTransformDirty) + this._transform = cc.affineTransformConcat(t, node._additionalTransform); + } + return this._transform; + }, + + _syncStatus: function (parentCmd) { + // In the visit logic does not restore the _dirtyFlag + // Because child elements need parent's _dirtyFlag to change himself + var flags = cc.Node._dirtyFlags, locFlag = this._dirtyFlag; + var 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 & 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; + + var colorDirty = locFlag & flags.colorDirty, + opacityDirty = locFlag & flags.opacityDirty, + transformDirty = locFlag & flags.transformDirty; + + this._dirtyFlag = locFlag; + + if (colorDirty) + //update the color + this._syncDisplayColor(); + + if (opacityDirty) + //update the opacity + this._syncDisplayOpacity(); + + if(colorDirty) + this._updateColor(); + + if (transformDirty) + //update the transform + this.transform(parentCmd, true); + }, + + visitChildren: function(){ + var node = this._node; + var i, children = node._children, child; + var 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; + } + cc.renderer.pushRenderCommand(this); + for (; i < len; i++) + children[i]._renderCmd.visit(this); + } else { + cc.renderer.pushRenderCommand(this); + } + this._dirtyFlag = 0; + } +}; + +//-----------------------Canvas --------------------------- + +(function() { +//The cc.Node's render command for Canvas + cc.Node.CanvasRenderCmd = function (renderable) { + cc.Node.RenderCmd.call(this, renderable); + this._cachedParent = null; + this._cacheDirty = false; + + }; + + var proto = cc.Node.CanvasRenderCmd.prototype = Object.create(cc.Node.RenderCmd.prototype); + proto.constructor = cc.Node.CanvasRenderCmd; + + proto.transform = function (parentCmd, recursive) { + // transform for canvas + var t = this.getNodeToParentTransform(), + worldT = this._worldTransform; //get the world transform + this._cacheDirty = true; + if (parentCmd) { + var pt = parentCmd._worldTransform; + // cc.AffineTransformConcat is incorrect at get world transform + worldT.a = t.a * pt.a + t.b * pt.c; //a + worldT.b = t.a * pt.b + t.b * pt.d; //b + worldT.c = t.c * pt.a + t.d * pt.c; //c + worldT.d = t.c * pt.b + t.d * pt.d; //d + + worldT.tx = pt.a * t.tx + pt.c * t.ty + pt.tx; + worldT.ty = pt.d * t.ty + pt.ty + pt.b * t.tx; + } else { + worldT.a = t.a; + worldT.b = t.b; + worldT.c = t.c; + worldT.d = t.d; + worldT.tx = t.tx; + worldT.ty = t.ty; + } + if (recursive) { + var locChildren = this._node._children; + if (!locChildren || locChildren.length === 0) + return; + var i, len; + for (i = 0, len = locChildren.length; i < len; i++) { + locChildren[i]._renderCmd.transform(this, recursive); + } + } + }; + + 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; + this._syncStatus(parentCmd); + this.visitChildren(); + }; + + 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(); + } + }; + + proto.setShaderProgram = function (shaderProgram) { + //do nothing. + }; + + proto.getShaderProgram = function () { + return null; + }; + + //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"; + } + }; +})(); \ No newline at end of file diff --git a/cocos2d/core/base-nodes/CCNodeWebGLRenderCmd.js b/cocos2d/core/base-nodes/CCNodeWebGLRenderCmd.js new file mode 100644 index 00000000000..8d328c644f8 --- /dev/null +++ b/cocos2d/core/base-nodes/CCNodeWebGLRenderCmd.js @@ -0,0 +1,104 @@ +/**************************************************************************** + 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) { + cc.Node.RenderCmd.call(this, renderable); + + var mat4 = new cc.math.Matrix4(), mat = mat4.mat; + mat[2] = mat[3] = mat[6] = mat[7] = mat[8] = mat[9] = mat[11] = mat[14] = 0.0; + mat[10] = mat[15] = 1.0; + this._transform4x4 = mat4; + this._stackMatrix = new cc.math.Matrix4(); + this._shaderProgram = null; + + this._camera = null; + }; + + var proto = cc.Node.WebGLRenderCmd.prototype = Object.create(cc.Node.RenderCmd.prototype); + proto.constructor = cc.Node.WebGLRenderCmd; + + proto._updateColor = function(){}; + + proto.visit = function (parentCmd) { + var node = this._node; + // quick return if not visible + if (!node._visible) + return; + + parentCmd = parentCmd || this.getParentRenderCmd(); + if (node._parent && node._parent._renderCmd) + this._curLevel = node._parent._renderCmd._curLevel + 1; + + var currentStack = cc.current_stack; + + //optimize performance for javascript + currentStack.stack.push(currentStack.top); + this._syncStatus(parentCmd); + currentStack.top = this._stackMatrix; + this.visitChildren(); + //optimize performance for javascript + currentStack.top = currentStack.stack.pop(); + }; + + proto.transform = function (parentCmd, recursive) { + var t4x4 = this._transform4x4, stackMatrix = this._stackMatrix, node = this._node; + parentCmd = parentCmd || this.getParentRenderCmd(); + var parentMatrix = (parentCmd ? parentCmd._stackMatrix : cc.current_stack.top); + + // Convert 3x3 into 4x4 matrix + var trans = this.getNodeToParentTransform(); + + this._dirtyFlag = this._dirtyFlag & cc.Node._dirtyFlags.transformDirty ^ this._dirtyFlag; + + var t4x4Mat = t4x4.mat; + t4x4Mat[0] = trans.a; + t4x4Mat[4] = trans.c; + t4x4Mat[12] = trans.tx; + t4x4Mat[1] = trans.b; + t4x4Mat[5] = trans.d; + t4x4Mat[13] = trans.ty; + + // Update Z vertex manually + t4x4Mat[14] = node._vertexZ; + + //optimize performance for Javascript + cc.kmMat4Multiply(stackMatrix, parentMatrix, t4x4); + + if(!recursive || !node._children || node._children.length === 0) + return; + var i, len, locChildren = node._children; + for(i = 0, len = locChildren.length; i< len; i++){ + locChildren[i]._renderCmd.transform(this, recursive); + } + }; + + proto.setShaderProgram = function (shaderProgram) { + this._shaderProgram = shaderProgram; + }; + + proto.getShaderProgram = function () { + return this._shaderProgram; + }; +})(); diff --git a/cocos2d/core/base-ui/CCWidgetManager.js b/cocos2d/core/base-ui/CCWidgetManager.js new file mode 100644 index 00000000000..1419b244693 --- /dev/null +++ b/cocos2d/core/base-ui/CCWidgetManager.js @@ -0,0 +1,141 @@ +/**************************************************************************** + Copyright (c) 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. + ****************************************************************************/ + +// returns a readonly size of the parent node +function getParentSize (parent) { + if (parent instanceof cc.EScene) { + if (CC_EDITOR) { + return cc.engine.getDesignResolutionSize(); + } + else { + return cc.visibleRect; + } + } + else if (!parent._sizeProvider || (parent._sizeProvider instanceof cc.Node)) { + return parent._contentSize; + } + else { + return parent.getContentSize(); + } +} + +function alignToParent (node, widget) { + var parent = node._parent; + var parentAnchor = parent._anchorPoint; + var parentSize = getParentSize(parent); + var parentWidth = parentSize.width; + var parentHeight = parentSize.height; + + var localLeft = -parentAnchor.x * parentWidth; + var localRight = localLeft + parentWidth; + var localBottom = -parentAnchor.y * parentHeight; + var localTop = localBottom + parentHeight; + + // adjust borders according to offsets + + localLeft += widget._isAbsLeft ? widget._left : widget._left * parentWidth; + localRight -= widget._isAbsRight ? widget._right : widget._right * parentWidth; + localBottom += widget._isAbsBottom ? widget._bottom : widget._bottom * parentHeight; + localTop -= widget._isAbsTop ? widget._top : widget._top * parentHeight; + + // align to borders by adjusting node's position and size (ignore rotation and scaling) + + var anchor = node.getAnchorPoint(); + + var width, x = node._position.x, anchorX = anchor.x; + if (widget.isStretchWidth) { + width = localRight - localLeft; + node.width = width; + x = localLeft + anchorX * width; + } + else { + width = node.width; + if (widget.isAlignHorizontalCenter) { + var parentCenter = (0.5 - parentAnchor.x) * parentWidth; // no offset + x = parentCenter + (anchorX - 0.5) * width; + } + else if (widget.isAlignLeft) { + x = localLeft + anchorX * width; + } + else if (widget.isAlignRight) { + x = localRight + anchorX * width - width; + } + } + + var height, y = node._position.y, anchorY = anchor.y; + if (widget.isStretchHeight) { + height = localTop - localBottom; + node.height = height; + y = localBottom + anchorY * height; + } + else { + height = node.height; + if (widget.isAlignVerticalCenter) { + var parentMiddle = (0.5 - parentAnchor.y) * parentHeight; // no offset + y = parentMiddle + (anchorY - 0.5) * height; + } + else if (widget.isAlignBottom) { + y = localBottom + anchorY * height; + } + else if (widget.isAlignTop) { + y = localTop + anchorY * height - height; + } + } + + node.setPosition(x, y); +} + +function visitNode (node) { + var widget = node._widget; + if (widget) { + alignToParent(node, widget); + } + var children = node._children; + for (var i = 0; i < children.length; i++) { + var child = children[i]; + if (child._active) { + visitNode(child); + } + } +} + +function visit () { + var scene = cc.director.getScene(); + if (scene) { + visitNode(scene); + } +} + +cc._widgetManager = { + init: function (director) { + director.on(cc.Director.EVENT_BEFORE_VISIT, visit); + }, + add: function (widget) { + widget.node._widget = widget; + }, + remove: function (widget) { + widget.node._widget = null; + }, + _getParentSize: getParentSize +}; diff --git a/cocos2d/core/cocos2d_externs.js b/cocos2d/core/cocos2d_externs.js new file mode 100644 index 00000000000..deb0d2edb54 --- /dev/null +++ b/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/cocos2d/core/components/CCAnimation.js b/cocos2d/core/components/CCAnimation.js new file mode 100644 index 00000000000..0ffb5ffdb30 --- /dev/null +++ b/cocos2d/core/components/CCAnimation.js @@ -0,0 +1,433 @@ +/**************************************************************************** + Copyright (c) 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. + ****************************************************************************/ + +var AnimationAnimator = require('../../animation/animation-animator'); +var AnimationClip = require('../../animation/animation-clip'); + +function equalClips (clip1, clip2) { + if (clip1 !== clip2) { + return true; + } + + return clip1 && clip2 && (clip1.name === clip2.name || clip1._uuid === clip2._uuid); +} + +/** + * @class AnimationComponent + * @extends CCComponent + */ +var AnimationComponent = cc.Class({ + name: 'cc.AnimationComponent', + extends: require('./CCComponent'), + + editor: CC_EDITOR && { + menu: 'Animation' + }, + + ctor: function () { + // The actual implement for Animation + this._animator = null; + + this._nameToState = {}; + this._didInit = false; + + this._currentClip = null; + }, + + properties: { + /** + * Animation will play the default clip when start game + * @property defaultClip + * @type {cc.AnimationClip} + */ + defaultClip: { + default: null, + type: AnimationClip, + displayName: 'Animation' + }, + + /** + * Current played clip + * @property currentClip + * @type {cc.AnimationClip} + */ + currentClip: { + get: function () { + return this._currentClip; + }, + set: function (value, force) { + this._currentClip = value; + + if (CC_EDITOR && force) { + this._updateClip(value); + } + }, + type: AnimationClip, + visible: false + }, + + /** + * All the clips used in this animation + * @property _clips + * @type {[cc.AnimationClip]} + */ + _clips: { + default: [], + type: [AnimationClip], + displayName: 'Animations', + visible: true + }, + + /** + * Whether the animation should auto play the default clip when start game. + * @property playAutomatically + * @type {bool} + * @default true + */ + playAutomatically: true, + }, + + onLoad: function () { + this._init(); + + if (this.playAutomatically && this.defaultClip) { + var state = this.getAnimationState(this.defaultClip.name); + this._animator.playState(state); + } + }, + + onDisable: function () { + this.setCurrentTime(0); + this.stop(); + }, + + /////////////////////////////////////////////////////////////////////////////// + // Public Methods + /////////////////////////////////////////////////////////////////////////////// + + /** + * Plays an animation. + * @method play + * @param {String} [name] - The name of animation to play. If no name is supplied then the default animation will be played. + * @param {Number} [startTime] - play an animation from startTime + * @return {AnimationState} - The AnimationState of playing animation. In cases where the animation can't be played (ie, there is no default animation or no animation with the specified name), the function will return null. + */ + play: function (name, startTime) { + this._init(); + var state = this.getAnimationState(name || this.defaultClip.name); + if (state) { + if (state.isPlaying) { + if (state.isPaused) { + this._animator.resumeState(state); + } + else { + this._animator.stopState(state); + this._animator.playState(state, startTime); + } + } + else { + this._animator.playState(state, startTime); + } + + this.currentClip = state.clip; + } + return state; + }, + + /** + * Stops an animation named name. If no name is supplied then stops all playing animations that were started with this Animation. + * Stopping an animation also Rewinds it to the Start. + * @method stop + * @param {String} [name] - The animation to stop, if not supplied then stops all playing animations. + */ + stop: function (name) { + if (!this._didInit) { + return; + } + if (name) { + var state = this._nameToState[name]; + if (state) { + this._animator.stopState(state); + } + } + else { + this._animator.stop(); + } + }, + + /** + * Pauses an animation named name. If no name is supplied then pauses all playing animations that were started with this Animation. + * @method pause + * @param {String} [name] - The animation to pauses, if not supplied then pauses all playing animations. + */ + pause: function (name) { + if (!this._didInit) { + return; + } + if (name) { + var state = this._nameToState[name]; + if (state) { + this._animator.pauseState(state); + } + } + else { + this._animator.pause(); + } + }, + + /** + * Resumes an animation named name. If no name is supplied then resumes all paused animations that were started with this Animation. + * @method pause + * @param {String} [name] - The animation to resumes, if not supplied then resumes all paused animations. + */ + resume: function (name) { + if (!this._didInit) { + return; + } + if (name) { + var state = this._nameToState[name]; + if (state) { + this._animator.resumeState(state); + } + } + else { + this._animator.resume(); + } + }, + + /** + * Make an animation named name go to the specified time. If no name is supplied then make all animations go to the specified time. + * @method setCurrentTime + * @param {Number} [time] - The time to go to + * @param {String} [name] - Specified animation name, if not supplied then make all animations go to the time. + */ + setCurrentTime: function (time, name) { + this._init(); + if (name) { + var state = this._nameToState[name]; + if (state) { + this._animator.setStateTime(state, time); + } + } + else { + for (var name in this._nameToState) { + state = this._nameToState[name]; + this._animator.setStateTime(state, time); + } + } + }, + + /** + * Returns the animation state named name. If no animation with the specified name, the function will return null. + * @method getAnimationState + * @param {String} name + * @return {AnimationState} + */ + getAnimationState: function (name) { + this._init(); + var state = this._nameToState[name]; + + if (CC_EDITOR && !state) { + this._didInit = false; + + if (this.animator) { + this.animator.stop(); + } + + this._init(); + state = this._nameToState[name]; + } + + return state || null; + }, + + /** + * Adds a clip to the animation with name newName. If a clip with that name already exists it will be replaced with the new clip. + * @method addClip + * @param {AnimationClip} clip - the clip to add + * @param {String} [newName] + * @return {AnimationState} - The AnimationState which gives full control over the animation clip. + */ + addClip: function (clip, newName) { + if (!clip) { + cc.warn('Invalid clip to add'); + return; + } + this._init(); + + // add clip + if (!cc.js.array.contains(this._clips, clip)) { + this._clips.push(clip); + } + + // replace same name clip + newName = newName || clip.name; + var oldState = this._nameToState[newName]; + if (oldState) { + if (oldState.clip === clip) { + return oldState; + } + else { + this._clips.splice(this._clips.indexOf(oldState.clip), 1); + } + } + + // replace state + var newState = new cc.AnimationState(clip, newName); + this._nameToState[newName] = newState; + return newState; + }, + + _removeStateIfNotUsed: function (state, force) { + var needRemove = state.clip !== this.defaultClip && !cc.js.array.contains(this._clips, state.clip); + if (force || needRemove) { + if (state.isPlaying) { + this.stop(state.name); + } + delete this._nameToState[state.name]; + } + }, + + /** + * Remove clip from the animation list. This will remove the clip and any animation states based on it. + * @method removeClip + * @param {Boolean} force If force is true, then will always remove the clip and any animation states based on it. + * @param {AnimationClip} clip + */ + removeClip: function (clip, force) { + if (!clip) { + cc.warn('Invalid clip to remove'); + return; + } + this._init(); + + this._clips = this._clips.filter(function (item) { + return item !== clip; + }); + + var state; + for (var name in this._nameToState) { + state = this._nameToState[name]; + var stateClip = state.clip; + if (stateClip === clip) { + this._removeStateIfNotUsed(state, force); + } + } + }, + + /** + * Samples animations at the current state. + * This is useful when you explicitly want to set up some animation state, and sample it once. + * @method sample + */ + sample: function () { + this._init(); + this._animator.sample(); + }, + + /////////////////////////////////////////////////////////////////////////////// + // Internal Methods + /////////////////////////////////////////////////////////////////////////////// + + // Dont forget to call _init before every actual process in public methods. + // Just invoking _init by onLoad is not enough because onLoad is called only if the entity is active. + + _init: function () { + if (this._didInit) { + return; + } + this._didInit = true; + this._animator = new AnimationAnimator(this.node, this); + this._createStates(); + }, + + _createStates: function() { + // create animation states + var state = null; + var defaultClipState = false; + for (var i = 0; i < this._clips.length; ++i) { + var clip = this._clips[i]; + if (clip) { + state = new cc.AnimationState(clip); + this._nameToState[state.name] = state; + if (equalClips(this.defaultClip, clip)) { + defaultClipState = state; + } + } + } + if (this.defaultClip && !defaultClipState) { + state = new cc.AnimationState(this.defaultClip); + this._nameToState[state.name] = state; + } + }, + + _updateClip: (CC_TEST || CC_EDITOR) && function (clip, clipName) { + this._init(); + + clipName = clipName || clip.name; + + var oldState; + for (var name in this._nameToState) { + var state = this._nameToState[name]; + var stateClip = state.clip; + if (equalClips(stateClip, clip)) { + if (!clip._uuid) clip._uuid = stateClip._uuid; + oldState = state; + break; + } + } + + if (!oldState) { + cc.error('Can\'t find state from clip [' + clipName + ']'); + return; + } + + var clips = this._clips; + var index = clips.indexOf(oldState.clip); + clips[index] = clip; + + // clip name changed + if (oldState.name !== clipName) { + delete this._nameToState[oldState.name]; + this._nameToState[clipName] = oldState; + oldState._name = clipName; + } + + // wrap time for change wrapMode + if ((clip.wrapMode & cc.WrapMode.Loop) === 0) { + oldState.time = oldState.getWrappedInfo(oldState.time).time; + } + if ((clip.wrapMode & cc.WrapMode.Reverse) !== 0) { + oldState.time = Math.abs(oldState.time - oldState.duration); + } + + oldState._clip = clip; + this._animator.reloadClip(oldState); + + this.sample(); + } +}); + + +cc.AnimationComponent = module.exports = AnimationComponent; diff --git a/cocos2d/core/components/CCAudioSource.js b/cocos2d/core/components/CCAudioSource.js new file mode 100644 index 00000000000..5d60d72e69c --- /dev/null +++ b/cocos2d/core/components/CCAudioSource.js @@ -0,0 +1,219 @@ +/**************************************************************************** + 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 Source. + * @class AudioScource + * @extends CCComponent + */ + +var audioEngine = cc.audioEngine; + +var AudioSource = cc.Class({ + name: 'cc.Audio', + extends: require('./CCComponent'), + + editor: CC_EDITOR && { + menu: 'Audio' + }, + + ctor: function () { + this.audio = null; + }, + + properties: { + _clip: { + default: '', + url: cc.AudioClip + }, + _volume: 1, + _mute: false, + _loop: false, + + /** + * Is the audio source playing (Read Only) + * @property isPlaying + * @type {Boolean} + * @readOnly + * @default false + */ + isPlaying: { + get: function () { + return (this.audio && this.audio.getPlaying()); + }, + visible: false + }, + + /** + * The clip of the audio source. + * @property clip + * @type {AudioClip} + * @default 1 + */ + clip: { + get: function () { + return this._clip; + }, + set: function (value) { + this._clip = value; + }, + url: cc.AudioClip + }, + + /** + * The volume of the audio source. + * @property volume + * @type {Number} + * @default 1 + */ + volume: { + get: function () { + return this._volume; + }, + set: function (value) { + this._volume = value; + if (this.audio) this.audio.setVolume(value); + } + }, + + /** + * Is the audio source mute? + * @property mute + * @type {Boolean} + * @default false + */ + mute: { + get: function () { + return this._mute; + }, + set: function (value) { + this._mute = value; + if (this.audio) { + if (this._mute) { + this.audio.setVolume(0); + } + else { + this.audio.setVolume(this._volume); + } + } + } + }, + + /** + * Is the audio source looping? + * @property loop + * @type {Boolean} + * @default false + */ + loop: { + get: function () { + return this._loop; + }, + set: function (value) { + this._loop = value; + if (this.audio) this.audio.loop = this._loop; + } + }, + + /** + * If set to true, the audio source will automatically start playing on onLoad. + * @property playOnLoad + * @type {Boolean} + * @default true + */ + playOnLoad: true + + }, + + onLoad: function () { + if ( this.isPlaying ) { + this.stop(); + } + }, + + onEnable: function () { + if ( this.playOnLoad ) { + this.play(); + } + }, + + onDisable: function () { + this.stop(); + }, + + onDestroy: function () { + this.stop(); + }, + + /** + * Plays the clip. + * @method play + */ + play: function () { + if ( this._clip ) { + this.audio = audioEngine.playEffect(this._clip, this._loop); + this.audio.play() + } + }, + + /** + * Stops the clip + * @method stop + */ + stop: function () { + if ( this.audio ) this.audio.stop(); + }, + + /** + * Pause the clip. + * @method pause + */ + pause: function () { + if ( this.audio ) this.audio.pause(); + }, + + /** + * Resume the clip. + * @method resume + */ + resume: function () { + if ( this.audio ) this.audio.resume(); + }, + + /** + * Rewind playing music. + * @method rewind + */ + rewind: function(){ + if ( this.audio ) { + this.audio.stop(); + this.audio.play(); + } + }, + +}); + +cc.AudioSource = module.exports = AudioSource; diff --git a/cocos2d/core/components/CCButton.js b/cocos2d/core/components/CCButton.js new file mode 100644 index 00000000000..6fd9ac9ba94 --- /dev/null +++ b/cocos2d/core/components/CCButton.js @@ -0,0 +1,496 @@ +/**************************************************************************** + Copyright (c) 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. + ****************************************************************************/ + +/** + * Enum for transition type + * @enum EButton.Transition + */ +var Transition = cc.Enum({ + /** + * @property {Number} NONE + */ + NONE: 0, + + /** + * @property {Number} COLOR + */ + COLOR: 1, + + /** + * @property {Number} SPRITE + */ + SPRITE: 2 +}); + +/** + * Click event will register a event to target component's handler. + * And will trigger when a click event emit + * + * @class EButton.ClickEvent + */ +var ClickEvent = cc.Class({ + name: 'cc.ClickEvent', + properties: { + /** + * Event target + * @property {cc.ENode} + * @default null + */ + target: { + default: null, + type: cc.ENode + }, + /** + * Component name + * @property {String} + * @default '' + */ + component: { + default: '' + }, + /** + * Event handler + * @property {String} + * @default '' + */ + handler: { + default: '' + } + } +}); + +var WHITE = cc.Color.WHITE; + +var ButtonState = { + Normal: 'normal', + Pressed: 'pressed', + Hover: 'hover', + Disabled: 'disabled' +}; + +var EVENT_TOUCH_DOWN = 'touch-down'; +var EVENT_TOUCH_UP = 'touch-up'; +var EVENT_HOVER_IN = 'hover-in'; +var EVENT_HOVER_MOVE = 'hover-move'; +var EVENT_HOVER_OUT = 'hover-out'; + +/** + * Button has 3 Transition types + * When Button state changed: + * If Transition type is EButton.Transition.NONE, Button will do nothing + * If Transition type is EButton.Transition.COLOR, Button will change target's color + * If Transition type is EButton.Transition.SPRITE, Button will change target SpriteRenderer's sprite + * + * Button will trigger 5 events: + * EButton.EVENT_TOUCH_DOWN + * EButton.EVENT_TOUCH_UP + * EButton.EVENT_HOVER_IN + * EButton.EVENT_HOVER_MOVE + * EButton.EVENT_HOVER_OUT + * + * @class EButton + * @extends Component + */ +var Button = cc.Class({ + name: 'cc.Button', + extends: require('./CCComponent'), + + ctor: function () { + this._touchListener = null; + this._mouseListener = null; + + this._pressed = false; + this._hovered = false; + + this._sprite = null; + + this._fromColor = null; + this._toColor = null; + this._time = 0; + this._tarnsitionFinished = true; + }, + + editor: CC_EDITOR && { + menu: 'UI/Button', + inspector: 'app://editor/page/inspector/button/button.html', + executeInEditMode: true + }, + + properties: { + /** + * Whether the Button is disabled. + * If true, the Button will trigger event and do transition. + * @property {Boolean} interactable + * @default true + */ + interactable: { + default: true, + notify: function () { + this._initState(); + } + }, + + /** + * Transition type + * @property {EButton.Transition} transition + * @default EButton.Transition.Node + */ + transition: { + default: Transition.NONE, + type: Transition + }, + + // color transition + + /** + * Normal state color + * @property {cc.Color} normalColor + */ + normalColor: { + default: WHITE, + displayName: 'Normal', + notify: function () { + this._initState(); + } + }, + + /** + * Pressed state color + * @property {cc.Color} pressedColor + */ + pressedColor: { + default: WHITE, + displayName: 'Pressed' + }, + + /** + * Hover state color + * @property {cc.Color} hoverColor + */ + hoverColor: { + default: WHITE, + displayName: 'Hover' + }, + + /** + * Disabled state color + * @property {cc.Color} disabledColor + */ + disabledColor: { + default: WHITE, + displayName: 'Disabled', + notify: function () { + this._initState(); + } + }, + + /** + * Color transition duration + * @property {float} duration + */ + duration: { + default: 0.1, + range: [0, Number.MAX_VALUE] + }, + + // sprite transition + /** + * Normal state sprite + * @property {cc.SpriteFrame} normalSprite + */ + normalSprite: { + default: null, + type: cc.SpriteFrame, + displayName: 'Normal', + notify: function () { + this._initState(); + } + }, + + /** + * Pressed state sprite + * @property {cc.SpriteFrame} pressedSprite + */ + pressedSprite: { + default: null, + type: cc.SpriteFrame, + displayName: 'Pressed', + }, + + /** + * Hover state sprite + * @property {cc.SpriteFrame} hoverSprite + */ + hoverSprite: { + default: null, + type: cc.SpriteFrame, + displayName: 'Hover', + }, + + /** + * Disabled state sprite + * @property {cc.SpriteFrame} disabledSprite + */ + disabledSprite: { + default: null, + type: cc.SpriteFrame, + displayName: 'Disabled', + notify: function () { + this._initState(); + } + }, + + /** + * Transition target. + * When Button state changed: + * If Transition type is EButton.Transition.NONE, Button will do nothing + * If Transition type is EButton.Transition.COLOR, Button will change target's color + * If Transition type is EButton.Transition.SPRITE, Button will change target SpriteRenderer's sprite + * @property {cc.ENode} target + */ + target: { + default: null, + type: cc.ENode, + + notify: function () { + this._applyTarget(); + } + }, + + /** + * If Button is clicked, will trigger event's handler + * @property {[EButton.ClickEvent]} clickEvents + */ + clickEvents: { + default: [], + type: ClickEvent + } + }, + + statics: { + /** + * Touch down event + * @property {String} EVENT_TOUCH_DOWN + */ + EVENT_TOUCH_DOWN: EVENT_TOUCH_DOWN, + /** + * Touch up event + * @property {String} EVENT_TOUCH_UP + */ + EVENT_TOUCH_UP: EVENT_TOUCH_UP, + /** + * Hover in event + * @property {String} EVENT_HOVER_IN + */ + EVENT_HOVER_IN: EVENT_HOVER_IN, + /** + * Hover move event + * @property {String} EVENT_HOVER_MOVE + */ + EVENT_HOVER_MOVE: EVENT_HOVER_MOVE, + /** + * Hover out event + * @property {String} EVENT_HOVER_OUT + */ + EVENT_HOVER_OUT: EVENT_HOVER_OUT, + + Transition: Transition, + ClickEvent: ClickEvent + }, + + onLoad: function () { + if (!this.target) { + this.target = this.node; + } + + if (!CC_EDITOR) { + this._registerEvent(); + this._registerListeners(); + } + }, + + start: function () { + this._applyTarget(); + this._initState(); + }, + + onDestroy: function () { + if (this._touchListener) cc.eventManager.removeListener(this._touchListener); + if (this._mouseListener) cc.eventManager.removeListener(this._mouseListener); + }, + + update: function (dt) { + var target = this.target; + if (!this.transition === Transition.COLOR || !target || this._tarnsitionFinished) return; + + this.time += dt; + var ratio = this.time / this.duration; + if (ratio > 1) { + ratio = 1; + this._tarnsitionFinished = true; + } + + target.color = this._fromColor.lerp(this._toColor, ratio); + }, + + _registerEvent: function () { + 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.node._sgNode); + + if (!cc.sys.isMobile) { + this._mouseListener = cc.EventListener.create({ + event: cc.EventListener.MOUSE, + onMouseMove: this._onMouseMove.bind(this) + }); + cc.eventManager.addListener(this._mouseListener, this.node._sgNode); + } + }, + + _registerListeners: function () { + var events = this.clickEvents; + for (var i = 0, l = events.length; i < l; i++) { + var event = events[i]; + var target = event.target; + if (!target) continue; + + var comp = target.getComponent(event.component); + if (!comp) continue; + + var handler = comp[event.handler]; + if (!handler) continue; + + this.on(EVENT_TOUCH_UP, handler.bind(comp)); + } + }, + + _applyTarget: function () { + var target = this.target; + if (target) { + this._sprite = target.getComponent(cc.SpriteRenderer); + } + else { + this._sprite = null; + } + }, + + _initState: function () { + var state = this.interactable ? ButtonState.Normal : ButtonState.Disabled; + this._applyState(state); + }, + + // touch event handler + _onTouchBegan: function (touch) { + if (!this.interactable) return false; + + var hit = this._hitTest(touch.getLocation()); + if (hit) { + this._pressed = true; + this._applyState(ButtonState.Pressed); + this.emit(EVENT_TOUCH_DOWN); + } + + return hit; + }, + + _onTouchEnded: function () { + if (this._hovered) + this._applyState(ButtonState.Hover); + else + this._applyState(ButtonState.Normal); + + this.emit(EVENT_TOUCH_UP); + this._pressed = false; + }, + + _onMouseMove: function (event) { + if (this._pressed || !this.interactable) return; + + var hit = this._hitTest(event.getLocation()); + if (hit) { + if (!this._hovered) { + this._hovered = true; + this._applyState(ButtonState.Hover); + this.emit(EVENT_HOVER_IN); + } + this.emit(EVENT_HOVER_MOVE); + } + else if (this._hovered) { + this._hovered = false; + this._applyState(ButtonState.Normal); + this.emit(EVENT_HOVER_OUT); + } + }, + + // state handler + _applyState: function (state) { + var color = this[state + 'Color']; + var sprite = this[state + 'Sprite']; + + this._applyTransition(color, sprite); + }, + _applyTransition: function (color, sprite) { + var transition = this.transition; + + if (transition === Transition.COLOR) { + var target = this.target; + + if (CC_EDITOR) { + target.color = color; + } + else { + this._fromColor = target.color.clone(); + this._toColor = color; + this.time = 0; + this._tarnsitionFinished = false; + } + } + else if (transition === Transition.SPRITE && this._sprite && sprite) { + this._sprite.sprite = sprite; + } + }, + + // hit test + _hitTest: function (pos) { + var target = this.target; + if (!target) return false; + + var w = target.width; + var h = target.height; + var anchor = target.getAnchorPoint(); + + var rect = cc.rect(-anchor.x*w, -anchor.y*h, w, h); + return cc.rectContainsPoint(rect, target.convertToNodeSpace(pos)); + } + +}); + +var EventTarget = require("../event/event-target"); +EventTarget.polyfill(Button.prototype); + +cc.EButton = module.exports = Button; diff --git a/cocos2d/core/components/CCCanvas.js b/cocos2d/core/components/CCCanvas.js new file mode 100644 index 00000000000..03f032dca3e --- /dev/null +++ b/cocos2d/core/components/CCCanvas.js @@ -0,0 +1,178 @@ +/**************************************************************************** + Copyright (c) 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. + ****************************************************************************/ + +var designResolutionWrapper = { + getContentSize: function () { + return CC_EDITOR ? cc.engine.getDesignResolutionSize() : cc.size(cc.visibleRect); + }, + setContentSize: function (size) { + // NYI + }, + + _getWidth: function () { + return this.getContentSize().width; + }, + + _getHeight: function () { + return this.getContentSize().height; + }, +}; + +/** + * !#zh: 作为 UI 根节点,从项目é…置或è¿è¡Œæ—¶ design resolution 设置中获å–自身节点尺寸, + * 为所有å­èŠ‚点æ供视窗四边的ä½ç½®ä¿¡æ¯ä»¥ä¾›å¯¹é½ï¼Œå¦å¤–æä¾›å±å¹•é€‚é…策略接å£ï¼Œæ–¹ä¾¿ä»Žç¼–辑器设置。 + * + * @class Canvas + * @extends Component + */ +var Canvas = cc.Class({ + name: 'cc.Canvas', extends: require('./CCComponent'), + + editor: CC_EDITOR && { + menu: 'Canvas', + executeInEditMode: true, + disallowMultiple: true + }, + + properties: { + + _fitWidth: false, + _fitHeight: false, + + /** + * !#zh: 是å¦ä¼˜å…ˆå°†è®¾è®¡åˆ†è¾¨çŽ‡é«˜åº¦æ’‘满视图高度 + * + * @property {Boolean} fitWidth + * @default false + */ + fitHeight: { + get: function () { + return this._fitHeight; + }, + set: function (value) { + if (this._fitHeight !== value) { + this._fitHeight = value; + this.applyPolicy(); + } + } + }, + + /** + * !#zh: 是å¦ä¼˜å…ˆå°†è®¾è®¡åˆ†è¾¨çŽ‡å®½åº¦æ’‘满视图宽度 + * + * @property {Boolean} fitWidth + * @default false + */ + fitWidth: { + get: function () { + return this._fitWidth; + }, + set: function (value) { + if (this._fitWidth !== value) { + this._fitWidth = value; + this.applyPolicy(); + } + } + } + }, + + ctor: function () { + this._thisOnResized = this.onResized.bind(this); + }, + + onLoad: function () { + if ( !this.node._sizeProvider ) { + this.node._sizeProvider = designResolutionWrapper; + } + else { + cc.error('CCCanvas: Node can only have one size.'); + } + + this.node.position = cc.Vec2.ZERO; + this.node.setAnchorPoint(0, 0); + + if (CC_EDITOR) { + cc.engine.on('design-resolution-changed', this._thisOnResized); + } + else { + if (cc.sys.isMobile) { + window.addEventListener('resize', this._thisOnResized); + } + else { + cc.eventManager.addCustomListener('canvas-resize', this._thisOnResized); + } + } + + this.onResized(); + this.applyPolicy(); + }, + + onDestroy: function () { + if (this.node._sizeProvider === designResolutionWrapper) { + this.node._sizeProvider = null; + } + + if (CC_EDITOR) { + cc.engine.off('design-resolution-changed', this._thisOnResized); + } + else { + if (cc.sys.isMobile) { + window.removeEventListener('resize', this._thisOnResized); + } + else { + cc.eventManager.removeCustomListeners('canvas-resize', this._thisOnResized); + } + } + }, + + // + + onResized: function () { + // TODO - size dirty + }, + + applyPolicy: function () { + var ResolutionPolicy = cc.ResolutionPolicy; + var policy; + + if (this.fitHeight && this.fitWidth) { + policy = ResolutionPolicy.SHOW_ALL; + } + else if (!this.fitHeight && !this.fitWidth) { + policy = ResolutionPolicy.NO_BORDER; + } + else if (this.fitWidth) { + policy = ResolutionPolicy.FIXED_WIDTH; + } + else { // fitHeight + policy = ResolutionPolicy.FIXED_HEIGHT; + } + + var size = cc.view.getDesignResolutionSize(); + cc.view.setDesignResolutionSize(size.width, size.height, policy); + } +}); + + +cc.Canvas = module.exports = Canvas; diff --git a/cocos2d/core/components/CCComponent.js b/cocos2d/core/components/CCComponent.js new file mode 100644 index 00000000000..6b4d94d4834 --- /dev/null +++ b/cocos2d/core/components/CCComponent.js @@ -0,0 +1,716 @@ +require('../platform/CCObject'); +require('../CCNode'); + +var Flags = cc.Object.Flags; +var IsOnEnableCalled = Flags.IsOnEnableCalled; +var IsOnLoadCalled = Flags.IsOnLoadCalled; +var IsOnStartCalled = Flags.IsOnStartCalled; + +var ExecInTryCatchTmpl = CC_EDITOR && '(function call_FUNC_InTryCatch(c){try{c._FUNC_()}catch(e){cc._throw(e)}})'; +if (CC_TEST) { + ExecInTryCatchTmpl = '(function call_FUNC_InTryCatch (c) { c._FUNC_() })'; +} +var callOnEnableInTryCatch = CC_EDITOR && eval(ExecInTryCatchTmpl.replace(/_FUNC_/g, 'onEnable')); +var callOnDisableInTryCatch = CC_EDITOR && eval(ExecInTryCatchTmpl.replace(/_FUNC_/g, 'onDisable')); +var callOnLoadInTryCatch = CC_EDITOR && eval(ExecInTryCatchTmpl.replace(/_FUNC_/g, 'onLoad')); +var callStartInTryCatch = CC_EDITOR && eval(ExecInTryCatchTmpl.replace(/_FUNC_/g, 'start')); +var callOnDestroyInTryCatch = CC_EDITOR && eval(ExecInTryCatchTmpl.replace(/_FUNC_/g, 'onDestroy')); +var callOnFocusInTryCatch = CC_EDITOR && eval(ExecInTryCatchTmpl.replace(/_FUNC_/g, 'onFocusInEditMode')); +var callOnLostFocusInTryCatch = CC_EDITOR && eval(ExecInTryCatchTmpl.replace(/_FUNC_/g, 'onLostFocusInEditMode')); + +function callOnEnable (self, enable) { + //if (CC_EDITOR) { + // if (enable ) { + // if ( !(self._objFlags & IsEditorOnEnabledCalled) ) { + // editorCallback.onComponentEnabled(self); + // self._objFlags |= IsEditorOnEnabledCalled; + // } + // } + // else { + // if (self._objFlags & IsEditorOnEnabledCalled) { + // editorCallback.onComponentDisabled(self); + // self._objFlags &= ~IsEditorOnEnabledCalled; + // } + // } + // if ( !(cc.game.isPlaying || self.constructor._executeInEditMode) ) { + // return; + // } + //} + var enableCalled = self._objFlags & IsOnEnableCalled; + if (enable) { + if (!enableCalled) { + if (self.onEnable) { + if (CC_EDITOR) { + callOnEnableInTryCatch(self); + } + else { + self.onEnable(); + } + } + + _registerEvent(self, true); + + self._objFlags |= IsOnEnableCalled; + } + } + else { + if (enableCalled) { + if (self.onDisable) { + if (CC_EDITOR) { + callOnDisableInTryCatch(self); + } + else { + self.onDisable(); + } + } + + _registerEvent(self, false); + + self._objFlags &= ~IsOnEnableCalled; + } + } +} + +function _registerEvent (self, on) { + if (CC_EDITOR) { + if (self.constructor._executeInEditMode || cc.engine._isPlaying) { + if (on && self.start && !(self._objFlags & IsOnStartCalled)) { + cc.engine.once('before-update', _callStart, self); + } + + if (self.update) { + if (on) cc.engine.on('post-update', _callUpdate, self); + else cc.engine.off('post-update', _callUpdate, self); + } + + if (self.lateUpdate) { + if (on) cc.engine.on('late-update', _callLateUpdate, self); + else cc.engine.off('late-update', _callLateUpdate, self); + } + } + } + else { + if (on && self.start && !(self._objFlags & IsOnStartCalled)) { + cc.director.once(cc.Director.EVENT_BEFORE_UPDATE, _callStart, self); + } + + if (self.update) { + if (on) cc.director.on(cc.Director.EVENT_COMPONENT_UPDATE, _callUpdate, self); + else cc.director.off(cc.Director.EVENT_COMPONENT_UPDATE, _callUpdate, self); + } + + if (self.lateUpdate) { + if (on) cc.director.on(cc.Director.EVENT_COMPONENT_LATE_UPDATE, _callLateUpdate, self); + else cc.director.off(cc.Director.EVENT_COMPONENT_LATE_UPDATE, _callLateUpdate, self); + } + } +} + +var _callStart = CC_EDITOR ? function () { + callStartInTryCatch(this); + this._objFlags |= IsOnStartCalled; +} : function () { + this.start(); + this._objFlags |= IsOnStartCalled; +}; + +var _callUpdate = CC_EDITOR ? function (event) { + try { + this.update(event.detail); + } + catch (e) { + cc._throw(e); + } +} : function (event) { + this.update(event.detail); +}; + +var _callLateUpdate = CC_EDITOR ? function (event) { + try { + this.lateUpdate(event.detail); + } + catch (e) { + cc._throw(e); + } +} : function (event) { + this.lateUpdate(event.detail); +}; + +//var createInvoker = function (timerFunc, timerWithKeyFunc, errorInfo) { +// return function (functionOrMethodName, time) { +// var ms = (time || 0) * 1000; +// var self = this; +// if (typeof functionOrMethodName === "function") { +// return timerFunc(function () { +// if (self.isValid) { +// functionOrMethodName.call(self); +// } +// }, ms); +// } +// else { +// var method = this[functionOrMethodName]; +// if (typeof method === 'function') { +// var key = this.id + '.' + functionOrMethodName; +// timerWithKeyFunc(function () { +// if (self.isValid) { +// method.call(self); +// } +// }, ms, key); +// } +// else { +// cc.error('Can not %s %s.%s because it is not a valid function.', errorInfo, JS.getClassName(this), functionOrMethodName); +// } +// } +// }; +//}; + +// Yes, the id might have a conflict problem once every 365 days +// if the game runs at 60 FPS and each frame 4760273 counts of new HashObject's id are requested. +var CompId = 0; +var IdPrefix = (CC_EDITOR || CC_TEST) && ('Comp' + Editor.NonUuidMark); +var getNewId = (CC_EDITOR || CC_TEST) && function () { + return IdPrefix + (++CompId); +}; + +/** + * Base class for everything attached to Node(Entity). + * + * NOTE: Not allowed to use construction parameters for Component's subclasses, + * because Component is created by the engine. + * + * @class Component + * @extends Object + * @constructor + */ +var Component = cc.Class({ + name: 'cc.Component', + extends: cc.Object, + + ctor: (CC_EDITOR || CC_TEST) && function () { + if (CC_EDITOR) { + Editor._AssetsWatcher.initComponent(this); + } + + // dont reset _id when destroyed + Object.defineProperty(this, '_id', { + value: '', + enumerable: false + }); + }, + + properties: { + /** + * The node this component is attached to. A component is always attached to a node. + * @property node + * @type {ENode} + */ + node: { + default: null, + visible: false + }, + + _id: { + default: '', + serializable: false + }, + + /** + * The uuid for editor + * @property uuid + * @type {String} + * @readOnly + */ + uuid: { + get: function () { + var id = this._id; + if (id) { + return id; + } + if (CC_EDITOR || CC_TEST) { + id = this._id = getNewId(); + cc.engine.attachedObjsForEditor[id] = this; + return id; + } + }, + visible: false + }, + + __scriptAsset: CC_EDITOR && { + get: function () {}, + set: function (value) { + if (this.__scriptUuid !== value) { + if (value && Editor.isUuid(value._uuid)) { + var classId = Editor.compressUuid(value._uuid); + var NewComp = cc.js._getClassById(classId); + if (cc.isChildClassOf(NewComp, cc.Component)) { + cc.warn('Sorry, replacing component script is not yet implemented.'); + //Editor.sendToWindows('reload:window-scripts', Editor._Sandbox.compiled); + } + else { + cc.error('Can not find a component in the script which uuid is "%s".', value._uuid); + } + } + else { + cc.error('Invalid Script'); + } + } + }, + displayName: 'Script', + type: cc._Script + }, + + /** + * @property _enabled + * @type {Boolean} + * @private + */ + _enabled: true, + + /** + * indicates whether this component is enabled or not. + * @property enabled + * @type {Boolean} + * @default true + */ + enabled: { + get: function () { + return this._enabled; + }, + set: function (value) { + if (this._enabled !== value) { + this._enabled = value; + if (this.node._activeInHierarchy) { + callOnEnable(this, value); + } + } + }, + visible: false + }, + + /** + * indicates whether this component is enabled and its entity is also active in the hierarchy. + * @property enabledInHierarchy + * @type {Boolean} + * @readOnly + */ + enabledInHierarchy: { + get: function () { + return this._enabled && this.node._activeInHierarchy; + }, + visible: false + }, + + /** + * @property _isOnLoadCalled + * @type {Boolean} + * @readOnly + */ + _isOnLoadCalled: { + get: function () { + return this._objFlags & IsOnLoadCalled; + }, + visible: false + }, + + /** + * Register all related EventTargets, + * all event callbacks will be removed in _onPreDestroy + * @property __eventTargets + * @type {Array} + * @private + */ + __eventTargets: { + default: [], + serializable: false + }, + + /** + * Only for editor to calculate bounding box + */ + localSize: { + get: function () { + return cc.size(0, 0); + }, + visible: false + } + }, + + // LIFECYCLE METHODS + + // Fireball provides lifecycle methods that you can specify to hook into this process. + // We provide Pre methods, which are called right before something happens, and Post methods which are called right after something happens. + + /** + * Update is called every frame, if the Component is enabled. + * @method update + */ + update: null, + + /** + * LateUpdate is called every frame, if the Component is enabled. + * @method lateUpdate + */ + lateUpdate: null, + + /** + * When attaching to an active entity or its entity first activated + * @method onLoad + */ + onLoad: null, + + /** + * Called before all scripts' update if the Component is enabled + * @method start + */ + start: null, + + /** + * Called when this component becomes enabled and its entity becomes active + * @method onEnable + */ + onEnable: null, + + /** + * Called when this component becomes disabled or its entity becomes inactive + * @method onDisable + */ + onDisable: null, + + /** + * Called when this component will be destroyed. + * @method onDestroy + */ + onDestroy: null, + + ///** + // * Called when the engine starts rendering the scene. + // * @method onPreRender + // */ + //onPreRender: null, + + /** + * @method onFocusInEditMode + */ + onFocusInEditMode: null, + /** + * @method onLostFocusInEditMode + */ + onLostFocusInEditMode: null, + + /** + * Adds a component class to the entity. You can also add component to entity by passing in the name of the + * script. + * + * @method addComponent + * @param {Function|String} typeOrName - the constructor or the class name of the component to add + * @return {Component} - the newly added component + */ + addComponent: function (typeOrTypename) { + return this.node.addComponent(typeOrTypename); + }, + + /** + * Returns the component of supplied type if the entity has one attached, null if it doesn't. You can also get + * component in the entity by passing in the name of the script. + * + * @method getComponent + * @param {Function|String} typeOrName + * @return {Component} + */ + getComponent: function (typeOrTypename) { + return this.node.getComponent(typeOrTypename); + }, + + ///** + // * Invokes the method on this component after a specified delay. + // * The method will be invoked even if this component is disabled, but will not invoked if this component is + // * destroyed. + // * + // * @method invoke + // * @param {function|string} functionOrMethodName + // * @param {number} [delay=0] - The number of seconds that the function call should be delayed by. If omitted, it defaults to 0. The actual delay may be longer. + // * @return {number} - Will returns a new InvokeID if the functionOrMethodName is type function. InvokeID is the numerical ID of the invoke, which can be used later with cancelInvoke(). + // * @example {@link examples/Fire/Component/invoke.js } + // */ + //invoke: createInvoker(Timer.setTimeout, Timer.setTimeoutWithKey, 'invoke'), + // + ///** + // * Invokes the method on this component repeatedly, with a fixed time delay between each call. + // * The method will be invoked even if this component is disabled, but will not invoked if this component is + // * destroyed. + // * + // * @method repeat + // * @param {function|string} functionOrMethodName + // * @param {number} [delay=0] - The number of seconds that the function call should wait before each call to the method. If omitted, it defaults to 0. The actual delay may be longer. + // * @return {number} - Will returns a new RepeatID if the method is type function. RepeatID is the numerical ID of the repeat, which can be used later with cancelRepeat(). + // * @example {@link examples/Fire/Component/repeat.js} + // */ + //repeat: createInvoker(Timer.setInterval, Timer.setIntervalWithKey, 'repeat'), + // + ///** + // * Cancels previous invoke calls with methodName or InvokeID on this component. + // * When using methodName, all calls with the same methodName will be canceled. + // * InvokeID is the identifier of the invoke action you want to cancel, as returned by invoke(). + // * + // * @method cancelInvoke + // * @param {string|number} methodNameOrInvokeId + // * @example {@link examples/Fire/Component/cancelInvoke.js} + // */ + //cancelInvoke: function (methodNameOrInvokeId) { + // if (typeof methodNameOrInvokeId === 'string') { + // var key = this.id + '.' + methodNameOrInvokeId; + // Timer.clearTimeoutByKey(key); + // } + // else { + // Timer.clearTimeout(methodNameOrInvokeId); + // } + //}, + // + ///** + // * Cancels previous repeat calls with methodName or RepeatID on this component. + // * When using methodName, all calls with the same methodName will be canceled. + // * RepeatID is the identifier of the repeat action you want to cancel, as returned by repeat(). + // * + // * @method cancelRepeat + // * @param {string|number} methodNameOrRepeatId + // * @example {@link examples/Fire/Component/cancelRepeat.js} + // */ + //cancelRepeat: function (methodNameOrRepeatId) { + // if (typeof methodNameOrRepeatId === 'string') { + // var key = this.id + '.' + methodNameOrRepeatId; + // Timer.clearIntervalByKey(key); + // } + // else { + // Timer.clearInterval(methodNameOrRepeatId); + // } + //}, + // + //isInvoking: function (methodName) { + // var key = this.id + '.' + methodName; + // return Timer.hasTimeoutKey(key); + //}, + + // OVERRIDES + + destroy: function () { + if (this._super()) { + if (this._enabled && this.node._activeInHierarchy) { + callOnEnable(this, false); + } + } + }, + + __onNodeActivated: CC_EDITOR ? function (active) { + if (!(this._objFlags & IsOnLoadCalled) && + (cc.engine._isPlaying || this.constructor._executeInEditMode)) { + if (this.onLoad) { + callOnLoadInTryCatch(this); + this._objFlags |= IsOnLoadCalled; + + if (!cc.engine._isPlaying) { + var focused = Editor.Selection.curActivate('node') === this.node.uuid; + if (focused && this.onFocusInEditMode) { + callOnFocusInTryCatch(this); + } + else if (this.onLostFocusInEditMode) { + callOnLostFocusInTryCatch(this); + } + } + } + else { + this._objFlags |= IsOnLoadCalled; + } + Editor._AssetsWatcher.start(this); + } + + if (this._enabled) { + callOnEnable(this, active); + } + } : function (active) { + if (!(this._objFlags & IsOnLoadCalled)) { + if (this.onLoad) { + this.onLoad(); + } + this._objFlags |= IsOnLoadCalled; + } + + if (this._enabled) { + callOnEnable(this, active); + } + }, + + _onPreDestroy: function () { + var i, l, target; + // ensure onDisable called + callOnEnable(this, false); + // Remove all listeners + for (i = 0, l = this.__eventTargets.length; i < l; ++i) { + target = this.__eventTargets[i]; + target && target.targetOff && target.targetOff(this); + } + this.__eventTargets.length = 0; + // onDestroy + if (CC_EDITOR) { + Editor._AssetsWatcher.stop(this); + if (cc.engine._isPlaying || this.constructor._executeInEditMode) { + if (this.onDestroy) { + callOnDestroyInTryCatch(this); + } + } + } + else if (this.onDestroy) { + this.onDestroy(); + } + // do remove component + this.node._removeComponent(this); + + if (CC_EDITOR || CC_TEST) { + delete cc.engine.attachedObjsForEditor[this._id]; + } + } +}); + +if (CC_EDITOR || CC_TEST) { + + // INHERITABLE STATIC MEMBERS + + /** + * Makes a component execute in edit mode. + * By default, all components are only executed in play mode, + * which means they will not have their callback functions executed while the Editor is in edit mode. + * + * @property _executeInEditMode + * @type {Boolean} + * @default false + * @static + * @readonly + * @private + */ + Component._executeInEditMode = false; + + /** + * This property is only available if _executeInEditMode is true. + * If specified, the editor's scene view will keep updating this node in 60 fps when it is selected, + * otherwise, it will update only if necessary. + * + * @property _playOnFocus + * @type {Boolean} + * @default false + * @static + * @readonly + * @private + */ + Component._playOnFocus = false; + + /** + * If specified to a type, prevents Component of the same type (or subtype) to be added more than once to a Node. + * + * @property _disallowMultiple + * @type {Function} + * @default false + * @static + * @readonly + * @private + */ + Component._disallowMultiple = null; + + // NON-INHERITED STATIC MEMBERS + + /** + * Specifying the url of the custom html to draw the component in inspector. + * + * @property _inspector + * @type {String} + * @default "" + * @static + * @readonly + * @private + */ + Object.defineProperty(Component, '_inspector', { value: '', enumerable: false }); + + /** + * Specifying the url of the icon to display in inspector. + * + * @property _icon + * @type {String} + * @default "" + * @static + * @readonly + * @private + */ + Object.defineProperty(Component, '_icon', { value: '', enumerable: false }); + + // COMPONENT HELPERS + + cc._componentMenuItems = []; + + Component._addMenuItem = function (cls, path, priority) { + cc._componentMenuItems.push({ + component: cls, + menuPath: path, + priority: priority + }); + }; + + // use defineProperty to prevent inherited by sub classes + Object.defineProperty(Component, '_registerEditorProps', { + value: function (cls, props) { + var name = cc.js.getClassName(cls); + for (var key in props) { + var val = props[key]; + switch (key) { + + case 'executeInEditMode': + cls._executeInEditMode = !!val; + break; + + case 'playOnFocus': + if (val) { + var willExecuteInEditMode = ('executeInEditMode' in props) ? props.executeInEditMode : cls._executeInEditMode; + if (willExecuteInEditMode) { + cls._playOnFocus = true; + } + else { + cc.warn('The editor property "playOnFocus" should be used with "executeInEditMode" in class "%s".', name); + } + } + break; + + case 'inspector': + Object.defineProperty(cls, '_inspector', { value: val }); + break; + + case 'icon': + Object.defineProperty(cls, '_icon', { value: val }); + break; + + // {String} menu + // The menu path to register a component to the editors "Component" menu. Eg. "Rendering/Camera" + case 'menu': + Component._addMenuItem(cls, val, props.menuPriority); + break; + + case 'disallowMultiple': + cls._disallowMultiple = cls; + break; + + // {Number} menuPriority + // the order which the menu item are displayed + case 'menuPriority': + if (!props.menu) { + cc.warn('The editor property "menuPriority" should be used with "menu" in class "%s".', name); + } + break; + + default: + cc.warn('Unknown editor property "%s" in class "%s".', key, name); + break; + } + } + } + }); +} + +Component.prototype.__scriptUuid = ''; + +cc.Component = module.exports = Component; diff --git a/cocos2d/core/components/CCComponentInSG.js b/cocos2d/core/components/CCComponentInSG.js new file mode 100644 index 00000000000..f6c7bf2d00e --- /dev/null +++ b/cocos2d/core/components/CCComponentInSG.js @@ -0,0 +1,86 @@ +var SceneGraphHelper = require('../utils/scene-graph-helper'); + +/** + * Component in scene graph. + * This is the base class for components which will attach a node to the cocos2d scene graph. + * + * You should override: + * - _createSgNode + * + * @class _ComponentInSG + * @extends Component + */ +var ComponentInSG = cc.Class({ + extends: require('./CCComponent'), + + editor: CC_EDITOR && { + executeInEditMode: true + }, + + ctor: function () { + this._sgNode = null; + }, + + onLoad: function () { + var sgNode = this._createSgNode(); + this._appendSgNode(sgNode); + if ( !this.node._sizeProvider ) { + this.node._sizeProvider = sgNode; + } + }, + onEnable: function () { + if (this._sgNode) { + this._sgNode.visible = true; + } + }, + onDisable: function () { + if (this._sgNode) { + this._sgNode.visible = false; + } + }, + onDestroy: function () { + if ( this.node._sizeProvider === this._sgNode ) { + this.node._sizeProvider = null; + } + this._removeSgNode(); + }, + + /** + * Create and returns your new scene graph node (SGNode) to append to scene graph. + * You should call the setContentSize of the SGNode if its size should be the same with the node's. + * + * @method _createSgNode + * @return {cc.Node} + * @private + */ + _createSgNode: null, + + _removeSgNode: SceneGraphHelper.removeSgNode, + + _appendSgNode: function (sgNode) { + var node = this.node; + + sgNode.setColor(node._color); + if ( !node._cascadeOpacityEnabled ) { + sgNode.setOpacity(node._opacity); + } + + sgNode.setAnchorPoint(node._anchorPoint); + sgNode.ignoreAnchorPointForPosition(node._ignoreAnchorPointForPosition); + + sgNode.setOpacityModifyRGB(node._opacityModifyRGB); + + // set z order to -1 to make sure component will rendered before all of its entity's children. + + sgNode.setLocalZOrder(-1); + + var sgParent = node._sgNode; + sgParent.addChild(sgNode); + + // retain immediately + sgNode.retain(); + this._sgNode = sgNode; + } +}); + +cc._ComponentInSG = module.exports = ComponentInSG; diff --git a/cocos2d/core/components/CCELabel.js b/cocos2d/core/components/CCELabel.js new file mode 100644 index 00000000000..296b5eb66ec --- /dev/null +++ b/cocos2d/core/components/CCELabel.js @@ -0,0 +1,175 @@ +/**************************************************************************** + Copyright (c) 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. + ****************************************************************************/ + +var HorizontalAlign = cc.TextAlignment; +var VerticalAlign = cc.VerticalTextAlignment; +var Overflow = cc.Label.Overflow; + +/** + * + * @class ELabel + * @extends _ComponentInSG + */ +var Label = cc.Class({ + name: 'cc.Label', + extends: cc._ComponentInSG, + + editor: CC_EDITOR && { + menu: 'UI/Label' + }, + + properties: { + + /** + * Content string of label + * @property {String} string + */ + string: { + default: '', + notify: function () { + var sgNode = this._sgNode; + if (sgNode) { + sgNode.setString(this.string); + } + } + }, + + /** + * Horizontal Alignment of label + * @property {TextAlignment} horizontalAlign + */ + horizontalAlign: { + default: HorizontalAlign.LEFT, + type: HorizontalAlign, + notify: function () { + var sgNode = this._sgNode; + if (sgNode) { + sgNode.setHorizontalAlign( this.horizontalAlign ); + } + }, + }, + + /** + * Vertical Alignment of label + * @property {VerticalTextAlignment} verticalAlign + */ + verticalAlign: { + default: VerticalAlign.TOP, + type: VerticalAlign, + notify: function () { + var sgNode = this._sgNode; + if (sgNode) { + sgNode.setVerticalAlign( this.verticalAlign ); + } + }, + }, + + /** + * Font size of label + * @property {Number} fontSize + */ + fontSize: { + default: 18, + notify: function () { + var sgNode = this._sgNode; + if (sgNode) { + sgNode.setFontSize( this.fontSize ); + } + } + }, + + /** + * Overflow of label + * @property {Overflow} overflow + */ + overflow: { + default: Overflow.CLAMP, + type: Overflow, + notify: function () { + var sgNode = this._sgNode; + if (sgNode) { + sgNode.setOverflow( this.overFlow ); + } + } + }, + + /** + * Whether auto wrap label when string width is large than label width + * @property {Boolean} enableWrapText + */ + enableWrapText: { + default: false, + notify: function () { + var sgNode = this._sgNode; + if (sgNode) { + sgNode.enableWrapText( this.enableWrapText ); + } + } + }, + + // TODO + // file: { + // default: null, + // type: cc.TTFFont, + // notify: function () { + // var sgNode = this._sgNode; + // if (sgNode) { + // sgNode.file = this.file; + // } + // } + // }, + + // TODO + // enableRichText: { + // default: false, + // notify: function () { + // var sgNode = this._sgNode; + // if (sgNode) { + // sgNode.enableRichText = this.enableRichText; + // } + // } + // } + + }, + + _createSgNode: function () { + var sgNode = new cc.Label(); + + // TODO + // sgNode.file = this.file; + // sgNode.enableRichText = this.enableRichText; + + sgNode.setString( this.string ); + sgNode.setHorizontalAlign( this.horizontalAlign ); + sgNode.setVerticalAlign( this.verticalAlign ); + sgNode.setFontSize( this.fontSize ); + sgNode.setOverflow( this.overflow ); + sgNode.enableWrapText( this.enableWrapText ); + sgNode.setContentSize( this.node.getContentSize() ); + + return sgNode; + }, + }); + + cc.ELabel = module.exports = Label; diff --git a/cocos2d/core/components/CCSpriteRenderer.js b/cocos2d/core/components/CCSpriteRenderer.js new file mode 100644 index 00000000000..99f343d1e82 --- /dev/null +++ b/cocos2d/core/components/CCSpriteRenderer.js @@ -0,0 +1,531 @@ +/**************************************************************************** + Copyright (c) 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 MonitorSize(target) { + this._target = target; +} +MonitorSize.prototype = { + getContentSize: function () { + return this._target._sgNode.getContentSize(); + }, + setContentSize: function (size) { + this._target.useOriginalSize = false; + this._target._sgNode.setPreferredSize(size); + }, + _getWidth: function () { + return this.getContentSize().width; + }, + _getHeight: function () { + return this.getContentSize().height; + } +}; + +var SpriteType = cc.SpriteType; + +/** + * Renders a sprite in the scene. + * @class Sprite + * @extends _ComponentInSG + */ +var SpriteRenderer = cc.Class({ + name: 'cc.Sprite', + extends: require('./CCComponentInSG'), + + editor: CC_EDITOR && { + menu: 'Sprite', + inspector: 'app://editor/page/inspector/sprite.html' + }, + + properties: { + _atlas: { + default: '', + url: cc.SpriteAtlas + }, + _sprite: { + default: null, + type: cc.SpriteFrame + }, + _type: SpriteType.SIMPLE, + _isFlippedX: false, + _isFlippedY: false, + _useOriginalSize: true, + + /** + * The Sprite Atlas. + * @property atlas + * @type {SpriteAtlas} + */ + atlas: { + get: function () { + return this._atlas; + }, + set: function (value) { + this._atlas = value; + //TODO Sprite Atlas + }, + url: cc.SpriteAtlas + }, + + /** + * The sprite frame of the sprite. + * @property sprite + * @type {SpriteFrame} + */ + sprite: { + get: function () { + return this._sprite; + }, + set: function (value, force) { + var lastSprite = this._sprite; + this._sprite = value; + if (this._sgNode) { + if (CC_EDITOR && force) { + this._sgNode._scale9Image = null; + } + this._applySprite(this._sgNode, lastSprite); + // color cleared after reset texture, should reapply color + this._sgNode.setColor(this.node._color); + this._sgNode.setOpacity(this.node._opacity); + } + }, + type: cc.SpriteFrame + }, + + /** + * The sprite type. + * @property type + * @type {SpriteType} + */ + type: { + get: function () { + return this._type; + }, + set: function (value) { + this._type = value; + this._sgNode.setRenderingType(this._type); + // manual settings inset top, bttom, right, left. + this._applyCapInset(); + }, + type: SpriteType + }, + + useOriginalSize: { + get: function () { + return this._useOriginalSize; + }, + set: function (value) { + this._useOriginalSize = value; + if (value) { + this._applySpriteSize(); + } + } + }, + + /** + * Only for editor to calculate bounding box. + */ + localSize: { + get: function () { + var sgNode = this._sgNode; + if (!sgNode) { + return cc.size(0, 0); + } + return cc.size(sgNode.width, sgNode.height); + }, + visible: false + } + }, + + /** + * Sets whether the sprite is visible or not. + * @method setVisible + * @param {Boolean} visible + * @override + */ + setVisible: function (visible) { + this.enabled = visible; + }, + + /** + * Toggle 9-slice feature. + * If Scale9Sprite is 9-slice disabled, the Scale9Sprite will rendered as a normal sprite. + * @method setScale9Enabled + * @param {Boolean} enabled - True to enable 9-slice, false otherwise. + */ + setScale9Enabled: function(enabled) { + this.type = enabled ? cc.SpriteType.SLICED : cc.SpriteType.SIMPLE; + }, + + /** + * Query whether the Scale9Sprite is enable 9-slice or not. + * @method isScale9Enabled + * @return {Boolean} True if 9-slice is enabled, false otherwise. + */ + isScale9Enabled: function(){ + return this.type === cc.SpriteType.SLICED + }, + + /** + * Get the original no 9-sliced sprite. + * @method getSprite + * @return {SpriteFrame} A sprite instance. + */ + getSprite : function(){ + return this._sprite; + }, + + /** + * Initializes a 9-slice sprite with a texture file, a delimitation zone and + * with the specified cap insets. + * Once the sprite is created, you can then call its "setContentSize:" method + * to resize the sprite will all it's 9-slice goodness intract. + * It respects the anchorPoint too. + * + * @method initWithFile + * @param {String} file - The name of the texture file. + * @param {Rect} rect - The rectangle that describes the sub-part of the texture that + * is the whole image. If the shape is the whole texture, set this to the texture's full rect. + * @param {Rect} capInsets - The values to use for the cap insets. + */ + initWithFile: function (file, rect, capInsets) { + this._sgNode.initWithFile(file, rect, capInsets); + }, + + /** + * Initializes a 9-slice sprite with an sprite frame and with the specified + * cap insets. + * Once the sprite is created, you can then call its "setContentSize:" method + * to resize the sprite will all it's 9-slice goodness intract. + * It respects the anchorPoint too. + * + * @method initWithSpriteFrame + * @param {SpriteFrame} spriteFrame - The sprite frame object. + * @param {Rect} capInsets - The values to use for the cap insets. + */ + initWithSpriteFrame: function (spriteFrame, capInsets) { + this._sprite = spriteFrame; + this._sgNode.initWithSpriteFrame(spriteFrame, capInsets); + }, + + /** + * Initializes a 9-slice sprite with an sprite frame name and with the specified + * cap insets. + * Once the sprite is created, you can then call its "setContentSize:" method + * to resize the sprite will all it's 9-slice goodness intract. + * It respects the anchorPoint too. + * + * @method initWithSpriteFrameName + * @param {String} spriteFrameName - The sprite frame name. + * @param {Rect} capInsets - The values to use for the cap insets. + */ + initWithSpriteFrameName: function (spriteFrameName, capInsets) { + var initialized = this._sgNode.initWithSpriteFrame(spriteFrameName, capInsets); + if (initialized === false) { + return; + } + this._sprite = this._sgNode._scale9Image; + }, + + /** + * Creates and returns a new sprite object with the specified cap insets. + * You use this method to add cap insets to a sprite or to change the existing + * cap insets of a sprite. In both cases, you get back a new image and the + * original sprite remains untouched. + * + * @method resizableSpriteWithCapInsets + * @param {Rect} capInsets - The values to use for the cap insets. + * @return {Scale9Sprite} A Scale9Sprite instance. + */ + resizableSpriteWithCapInsets: function (capInsets) { + return this._sgNode.resizableSpriteWithCapInsets(capInsets); + }, + + /** + * Update Scale9Sprite with a specified sprite. + * + * @method updateWithSprite + * @param {SpriteFrame} sprite - A sprite pointer. + * @param {Rect} rect - A delimitation zone. + * @param {Number} rotated - Whether the sprite is rotated or not. + * @param {Size} offset - The offset when slice the sprite. + * @param {Size} originalSize - The origial size of the sprite. + * @param {Rect} capInsets - The Values to use for the cap insets. + * @return {Boolean} True if update success, false otherwise. + */ + updateWithSprite: function (sprite, textureRect, rotated, offset, originalSize, capInsets) { + this._sprite = sprite; + return this._sgNode.updateWithSprite(sprite, textureRect, rotated, offset, originalSize, capInsets); + }, + + /** + * Sets a new sprite frame to the sprite. + * @method setSpriteFrame + * @param {SpriteFrame} spriteFrame + * @param {Rect} capInsets + */ + setSpriteFrame: function (spriteFrame, capInsets) { + this.sprite = spriteFrame; + this.setCapInsets(capInsets); + }, + + /** + * Query the sprite's original size. + * @method getOriginalSize + * @return {Size} Sprite size. + */ + getOriginalSize: function () { + return this._sgNode.getOriginalSize(); + }, + + /** + * Change the preferred size of Scale9Sprite. + * @method setPreferredSize + * @param {Size} size - A delimitation zone. + */ + setPreferredSize: function (size) { + this.node.setContentSize(size); + }, + + /** + * Query the Scale9Sprite's preferred size. + * @method getPreferredSize + * @return {Size} Scale9Sprite's preferred size. + */ + getPreferredSize: function () { + return this._sgNode.getPreferredSize(); + }, + + /** + * Change the cap inset size. + * @method setCapInsets + * @param {Rect} capInsets - A delimitation zone. + */ + setCapInsets: function (capInsets) { + this._sgNode.setCapInsets(capInsets); + }, + /** + * Query the Scale9Sprite's preferred size. + * @method getCapInsets + * @return {Rect} Scale9Sprite's cap inset. + */ + getCapInsets: function () { + return this._sgNode.getCapInsets(); + }, + + /** + * Change the left sprite's cap inset. + * @method setInsetLeft + * @param {Number} leftInset - The values to use for the cap inset. + */ + setInsetLeft: function(insetLeft){ + this._sgNode.setInsetLeft(insetLeft); + }, + + /** + * Query the left sprite's cap inset. + * @method getInsetLeft + * @return {Number} The left sprite's cap inset. + */ + getInsetLeft: function(){ + return this._sgNode.getInsetLeft(); + }, + + /** + * Change the top sprite's cap inset. + * @method setInsetTop + * @param {Number} topInset - The values to use for the cap inset. + */ + setInsetTop: function(insetTop){ + this._sgNode.setInsetTop(insetTop); + }, + + /** + * Query the top sprite's cap inset. + * @method getInsetTop + * @return {Number} The top sprite's cap inset. + */ + getInsetTop: function(){ + return this._sgNode.getInsetTop(); + }, + + /** + * Change the right sprite's cap inset. + * @method setInsetRight + * @param {Number} rightInset - The values to use for the cap inset. + */ + setInsetRight: function(insetRight){ + this._sgNode.setInsetRight(insetRight); + }, + + /** + * Query the right sprite's cap inset. + * @method getInsetRight + * @return {Number} The right sprite's cap inset. + */ + getInsetRight: function(){ + return this._sgNode.getInsetRight(); + }, + + /** + * Change the bottom sprite's cap inset. + * @method setInsetBottom + * @param {Number} bottomInset - The values to use for the cap inset. + */ + setInsetBottom: function(insetBottom) { + this._sgNode.setInsetBottom(insetBottom); + }, + + /** + * @brief Query the bottom sprite's cap inset. + * @method getInsetBottom + * @return {Number} The bottom sprite's cap inset. + */ + getInsetBottom: function(){ + return this._sgNode.getInsetBottom(); + }, + + /** + * Sets whether the widget should be flipped horizontally or not. + * @method setFlippedX + * @param {Boolean} flippedX - true if the sprite should be flipped horizontally, false otherwise. + */ + setFlippedX: function (flippedX) { + this._isFlippedX = flippedX; + this._sgNode.setFlippedX(flippedX); + }, + + /** + * 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); + * + * @method isFlippedX + * @return {Boolean} true if the sprite is flipped horizontally, false otherwise. + */ + isFlippedX: function () { + return this._isFlippedX; + }, + + /** + * Sets whether the sprite should be flipped vertically or not. + * @method setFlippedY + * @param {Boolean} flippedY - true if the sprite should be flipped vertically, false otherwise. + */ + setFlippedY: function (flippedY) { + this._isFlippedY = flippedY; + this._sgNode.setFlippedY(flippedY); + }, + + /** + * 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); + * + * @method isFlippedY + * @return {Boolean} true if the sprite is flipped vertically, false otherwise. + */ + isFlippedY: function () { + return this._isFlippedY; + }, + + onLoad: function () { + this._super(); + this.node._sizeProvider = new MonitorSize(this); + }, + + onDestroy: function () { + this._super(); + this.node._sizeProvider = null; + }, + + _applyCapInset: function (node) { + if (this._type === SpriteType.SLICED) { + var node = node || this._sgNode; + node.setInsetTop(this._sprite.insetTop); + node.setInsetBottom(this._sprite.insetBottom); + node.setInsetRight(this._sprite.insetRight); + node.setInsetLeft(this._sprite.insetLeft); + } + }, + + _applySpriteSize: function (node) { + var node = node || this._sgNode; + if (this._useOriginalSize) { + var rect = this._sprite.getRect(); + node.setPreferredSize(rect.size); + } + else { + node.setPreferredSize(this.node.getContentSize(true)); + } + }, + + _applySprite: function (node, oldSprite) { + if (oldSprite) { + oldSprite.off('load', this._applyCapInset, this); + } + if (!this._sprite) { return; } + node._spriteRect = cc.rect(0, 0); + node._originalSize = cc.size(0, 0); + node.initWithSpriteFrame(this._sprite); + var locLoaded = this._sprite.textureLoaded(); + if (!locLoaded) { + if ( !this._useOriginalSize ) { + node.setPreferredSize(this.node.getContentSize(true)); + } + this._sprite.once('load', function () { + this._applyCapInset(); + this._applySpriteSize(); + }, this) + } + else { + this._applyCapInset(node); + this._applySpriteSize(node); + } + }, + + _createSgNode: function () { + var sgNode = new cc.Scale9Sprite(); + sgNode.setRenderingType(this._type); + this._applySprite(sgNode, null); + return sgNode; + }, +}); + +var misc = require('../utils/misc'); +var SameNameGetSets = ['atlas', 'capInsets', 'insetLeft', 'insetTop', 'insetRight', 'insetBottom']; +var DiffNameGetSets = { + type: [ null, 'setRenderingType'], + sprite: ['getSprite', null], + flippedX: ['isFlippedX', 'setFlippedX'], + flippedY: ['isFlippedY', 'setFlippedY'], +}; +misc.propertyDefine(SpriteRenderer, SameNameGetSets, DiffNameGetSets); + +cc.SpriteRenderer = module.exports = SpriteRenderer; diff --git a/cocos2d/core/components/CCWidget.js b/cocos2d/core/components/CCWidget.js new file mode 100644 index 00000000000..188f11e2a54 --- /dev/null +++ b/cocos2d/core/components/CCWidget.js @@ -0,0 +1,423 @@ +/**************************************************************************** + Copyright (c) 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. + ****************************************************************************/ + +// Mask for AlignFlags +var TOP = 1 << 0; +var MID = 1 << 1; // vertical center +var BOT = 1 << 2; +var LEFT = 1 << 3; +var CENTER = 1 << 4; // horizontal center +var RIGHT = 1 << 5; +var TOP_BOT = TOP | BOT; +var LEFT_RIGHT = LEFT | RIGHT; + +/** + * Stores and manipulate the anchoring based on its parent. + * Widget are used for GUI but can also be used for other things. + * + * @class Widget + * @extends Component + */ +var Widget = cc.Class({ + name: 'cc.Widget', extends: require('./CCComponent'), + + editor: CC_EDITOR && { + menu: 'Widget', + executeInEditMode: true, + disallowMultiple: true, + inspector: 'app://editor/page/inspector/widget/index.html' + }, + + properties: { + + // ENABLE ALIGN ? + + /** + * !#zh: 是å¦å¯¹é½ä¸Šè¾¹ + * + * @property isAlignTop + * @type {Boolean} + * @default false + */ + isAlignTop: { + get: function () { + return (this._alignFlags & TOP) > 0; + }, + set: function (value) { + this._setAlign(TOP, value); + } + }, + + /** + * !#zh: 是å¦åž‚ç›´æ–¹å‘对é½ä¸­ç‚¹ï¼Œå¼€å¯æ­¤é¡¹ä¼šå°†åž‚ç›´æ–¹å‘其他对é½é€‰é¡¹å–消 + * + * @property isAlignVerticalCenter + * @type {Boolean} + * @default false + */ + isAlignVerticalCenter: { + get: function () { + return (this._alignFlags & MID) > 0; + }, + set: function (value) { + if (value) { + this.isAlignTop = false; + this.isAlignBottom = false; + this._alignFlags |= MID; + } + else { + this._alignFlags &= ~MID; + } + } + }, + + /** + * !#zh: 是å¦å¯¹é½ä¸‹è¾¹ + * + * @property isAlignBottom + * @type {Boolean} + * @default false + */ + isAlignBottom: { + get: function () { + return (this._alignFlags & BOT) > 0; + }, + set: function (value) { + this._setAlign(BOT, value); + } + }, + + /** + * !#zh: 是å¦å¯¹é½å·¦è¾¹ + * + * @property isAlignLeft + * @type {Boolean} + * @default false + */ + isAlignLeft: { + get: function () { + return (this._alignFlags & LEFT) > 0; + }, + set: function (value) { + this._setAlign(LEFT, value); + } + }, + + /** + * !#zh: 是å¦æ°´å¹³æ–¹å‘对é½ä¸­ç‚¹ï¼Œå¼€å¯æ­¤é€‰é¡¹ä¼šå°†æ°´å¹³æ–¹å‘其他对é½é€‰é¡¹å–消 + * + * @property isAlignHorizontalCenter + * @type {Boolean} + * @default false + */ + isAlignHorizontalCenter: { + get: function () { + return (this._alignFlags & CENTER) > 0; + }, + set: function (value) { + if (value) { + this.isAlignLeft = false; + this.isAlignRight = false; + this._alignFlags |= CENTER; + } + else { + this._alignFlags &= ~CENTER; + } + } + }, + + /** + * !#zh: 是å¦å¯¹é½å³è¾¹ + * + * @property isAlignRight + * @type {Boolean} + * @default false + */ + isAlignRight: { + get: function () { + return (this._alignFlags & RIGHT) > 0; + }, + set: function (value) { + this._setAlign(RIGHT, value); + } + }, + + /** + * !#zh: 是å¦æ°´å¹³æ‹‰ä¼¸ï¼Œå½“åŒæ—¶å¯ç”¨å·¦å³å¯¹é½æ—¶ï¼Œå°†ä¼šæ°´å¹³æ‹‰ä¼¸ï¼Œæ­¤æ—¶å®½åº¦è®¾ç½®æ— æ•ˆï¼ˆåªè¯»ï¼‰ + * + * @property isStretchHeight + * @type {Boolean} + * @default false + * @readOnly + */ + isStretchWidth: { + get: function () { + return (this._alignFlags & LEFT_RIGHT) === LEFT_RIGHT; + }, + visible: false + }, + /** + * !#zh: 是å¦åž‚直拉伸,当åŒæ—¶å¯ç”¨ä¸Šä¸‹å¯¹é½æ—¶ï¼Œå°†ä¼šåž‚直拉伸,此时高度设置无效(åªè¯»ï¼‰ + * + * @property isStretchHeight + * @type {Boolean} + * @default false + * @readOnly + */ + isStretchHeight: { + get: function () { + return (this._alignFlags & TOP_BOT) === TOP_BOT; + }, + visible: false + }, + + // ALIGN MARGINS + + /** + * !#zh: 本节点顶边和父节点顶边的è·ç¦»ï¼Œå¯å¡«å†™è´Ÿå€¼ï¼Œåªæœ‰åœ¨ isAlignTop å¼€å¯æ—¶æ‰æœ‰ä½œç”¨ + * + * @property top + * @type {Number} + * @default 0 + */ + top: { + get: function () { + return this._top; + }, + set: function (value) { + this._top = value; + } + }, + + /** + * !#zh: 本节点底边和父节点底边的è·ç¦»ï¼Œå¯å¡«å†™è´Ÿå€¼ï¼Œåªæœ‰åœ¨ isAlignBottom å¼€å¯æ—¶æ‰æœ‰ä½œç”¨ + * + * @property bottom + * @type {Number} + * @default 0 + */ + bottom: { + get: function () { + return this._bottom; + }, + set: function (value) { + this._bottom = value; + } + }, + + /** + * !#zh: 本节点左边和父节点左边的è·ç¦»ï¼Œå¯å¡«å†™è´Ÿå€¼ï¼Œåªæœ‰åœ¨ isAlignLeft å¼€å¯æ—¶æ‰æœ‰ä½œç”¨ + * + * @property left + * @type {Number} + * @default 0 + */ + left: { + get: function () { + return this._left; + }, + set: function (value) { + this._left = value; + } + }, + + /** + * !#zh: 本节点å³è¾¹å’Œçˆ¶èŠ‚点å³è¾¹çš„è·ç¦»ï¼Œå¯å¡«å†™è´Ÿå€¼ï¼Œåªæœ‰åœ¨ isAlignRight å¼€å¯æ—¶æ‰æœ‰ä½œç”¨ + * + * @property right + * @type {Number} + * @default 0 + */ + right: { + get: function () { + return this._right; + }, + set: function (value) { + this._right = value; + } + }, + + // PARCENTAGE OR ABSOLUTE + + /** + * If true, top is pixel margin, otherwise is percentage (0 - 1) margin relative to the parent's height + * + * @property isAbsoluteTop + * @type {Boolean} + * @default true + */ + isAbsoluteTop: { + get: function () { + return this._isAbsTop; + }, + set: function (value) { + this._isAbsTop = value; + } + }, + + /** + * If true, bottom is pixel margin, otherwise is percentage (0 - 1) margin relative to the parent's height + * + * @property isAbsoluteBottom + * @type {Boolean} + * @default true + */ + isAbsoluteBottom: { + get: function () { + return this._isAbsBottom; + }, + set: function (value) { + this._isAbsBottom = value; + } + }, + + /** + * If true, left is pixel margin, otherwise is percentage (0 - 1) margin relative to the parent's width + * + * @property isAbsoluteLeft + * @type {Boolean} + * @default true + */ + isAbsoluteLeft: { + get: function () { + return this._isAbsLeft; + }, + set: function (value) { + this._isAbsLeft = value; + } + }, + + /** + * If true, right is pixel margin, otherwise is percentage (0 - 1) margin relative to the parent's width + * + * @property isAbsoluteRight + * @type {Boolean} + * @default true + */ + isAbsoluteRight: { + get: function () { + return this._isAbsRight; + }, + set: function (value) { + this._isAbsRight = value; + } + }, + + // + + /** + * !#zh: 对é½å¼€å…³ï¼Œç”± AlignFlags ç»„æˆ + * + * @property _alignFlags + * @type {Number} + * @default 0 + * @private + */ + _alignFlags: 0, + + _left: 0, + _right: 0, + _top: 0, + _bottom: 0, + _isAbsLeft: true, + _isAbsRight: true, + _isAbsTop: true, + _isAbsBottom: true, + + // original size before align + _originalWidth: 0, + _originalHeight: 0 + }, + + onLoad: function () { + cc._widgetManager.add(this); + }, + + onDestroy: function () { + cc._widgetManager.remove(this); + }, + + _setAlign: function (flag, isAlign) { + var current = (this._alignFlags & flag) > 0; + if (isAlign == current) { + return; + } + var isHorizontal = (flag & LEFT_RIGHT) > 0; + if (isAlign) { + this._alignFlags |= flag; + + if (isHorizontal) { + this.isAlignHorizontalCenter = false; + if (this.isStretchWidth) { + // become stretch + this._originalWidth = this.node.width; + } + } + else { + this.isAlignVerticalCenter = false; + if (this.isStretchHeight) { + // become stretch + this._originalHeight = this.node.height; + } + } + + if (CC_EDITOR && !cc.engine._isPlaying) { + // adjust the offsets to keep the size and position unchanged after alignment chagned + var type; + if (flag & TOP) { + type = 'top'; + } + else if (flag & LEFT) { + type = 'left'; + } + else if (flag & RIGHT) { + type = 'right'; + } + else if (flag & BOT) { + type = 'bottom'; + } + cc._widgetManager.updateOffsetsToStayPut(this, type); + } + } + else { + if (isHorizontal) { + if (this.isStretchWidth) { + // will cancel stretch + this.node.width = this._originalWidth; + } + } + else { + if (this.isStretchHeight) { + // will cancel stretch + this.node.height = this._originalHeight; + } + } + + this._alignFlags &= ~flag; + } + } +}); + + +cc.Widget = module.exports = Widget; diff --git a/cocos2d/core/components/index.js b/cocos2d/core/components/index.js new file mode 100644 index 00000000000..4e6e53e7fbe --- /dev/null +++ b/cocos2d/core/components/index.js @@ -0,0 +1,12 @@ +require('./CCComponent'); +require('./CCComponentInSG'); + +module.exports = [ + require('./CCSpriteRenderer'), + require('./CCWidget'), + require('./CCCanvas'), + require('./CCAudioSource'), + require('./CCAnimation'), + require('./CCButton'), + require('./CCELabel') +]; diff --git a/cocos2d/core/event-manager/CCEventListener.js b/cocos2d/core/event-manager/CCEventListener.js new file mode 100644 index 00000000000..4b2e6797aeb --- /dev/null +++ b/cocos2d/core/event-manager/CCEventListener.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. + ****************************************************************************/ + +/** + *

+ * 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 EventListener + * @extends _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 + _target: null, + _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|cc.ENode} node + * @private + */ + _setSceneGraphPriority: function (node) { + if (cc.ENode && node instanceof cc.ENode) { + this._target = node; + this._node = node._sgNode; + } + else if (cc.Component && node instanceof cc.Component) { + this._target = node; + this._node = node.node._sgNode; + } + else { + this._target = node; + this._node = node; + } + }, + + /** + * Gets scene graph priority of this listener + * @returns {cc.Node|cc.ENode} 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 {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.

+ * + * @method retain + * @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.

+ * + * @method release + * @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 = 5; +/** + * The type code of focus event listener. + * @constant + * @type {Number} + */ +cc.EventListener.ACCELERATION = 6; +/** + * The type code of custom event listener. + * @constant + * @type {Number} + */ +cc.EventListener.CUSTOM = 8; + +/** + * The type code of Focus change event listener. + * @constant + * @type {Number} + */ +cc.EventListener.FOCUS = 7; + +cc._EventListenerCustom = cc.EventListener.extend({ + _onCustomEvent: null, + ctor: function (listenerId, callback) { + this._onCustomEvent = callback; + var selfPointer = this; + var listener = function (event) { + if (selfPointer._onCustomEvent !== null) + selfPointer._onCustomEvent(event); + }; + + cc.EventListener.prototype.ctor.call(this, cc.EventListener.CUSTOM, listenerId, listener); + }, + + 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 () { + var selfPointer = this; + var listener = function (event) { + var eventType = cc.Event.EventMouse; + switch (event._eventType) { + case eventType.DOWN: + if (selfPointer.onMouseDown) + selfPointer.onMouseDown(event); + break; + case eventType.UP: + if (selfPointer.onMouseUp) + selfPointer.onMouseUp(event); + break; + case eventType.MOVE: + if (selfPointer.onMouseMove) + selfPointer.onMouseMove(event); + break; + case eventType.SCROLL: + if (selfPointer.onMouseScroll) + selfPointer.onMouseScroll(event); + break; + default: + break; + } + }; + cc.EventListener.prototype.ctor.call(this, cc.EventListener.MOUSE, cc._EventListenerMouse.LISTENER_ID, listener); + }, + + 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._checkEventListenerAvailable.touchOneByOne); + 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._checkEventListenerAvailable.touchAllAtOnce); + 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 + * @method create + * @static + * @param {Object} argObj a json object + * @returns {EventListener} + * todo: It should be the direct use new + * @example {@link utils/api/cocos/docs/cocos2d/core/event-manager/CCEventListener/create.js} + */ +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(){ + var listener = function(event){ + if(this.onFocusChanged) + this.onFocusChanged(event._widgetLoseFocus, event._widgetGetFocus); + }; + cc.EventListener.prototype.ctor.call(this, cc.EventListener.FOCUS, cc._EventListenerFocus.LISTENER_ID, listener); + } +}); + +cc._EventListenerFocus.LISTENER_ID = "__cc_focus_event"; + +//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._checkEventListenerAvailable.acceleration); + + 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._checkEventListenerAvailable.keyboard); + 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/cocos2d/core/event-manager/CCEventManager.js b/cocos2d/core/event-manager/CCEventManager.js new file mode 100644 index 00000000000..926b05ff655 --- /dev/null +++ b/cocos2d/core/event-manager/CCEventManager.js @@ -0,0 +1,965 @@ +/**************************************************************************** + 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. + ****************************************************************************/ + +/** + * @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; + } +}); + +cc.__getListenerID = function (event) { + var eventType = cc.Event, type = event.getType(); + if (type === eventType.ACCELERATION) + return cc._EventListenerAcceleration.LISTENER_ID; + if (type === eventType.KEYBOARD) + return cc._EventListenerKeyboard.LISTENER_ID; + if (type === eventType.MOUSE) + return cc._EventListenerMouse.LISTENER_ID; + if (type === eventType.FOCUS) + return cc._EventListenerFocus.LISTENER_ID; + if (type === 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 type; +}; + +/** + *

+ * 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 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: [], + _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. + * @method pauseTarget + * @param {ENode} 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. + * @method resumeTarget + * @param {ENode} 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.js.array.remove(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.js.array.remove(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 (listenerID) { + var listeners = this._listenersMap[listenerID]; + if (!listeners) + return; + + var fixedPriorityListeners = listeners.getFixedPriorityListeners(); + var sceneGraphPriorityListeners = listeners.getSceneGraphPriorityListeners(); + var i, selListener; + + if (sceneGraphPriorityListeners) { + for (i = 0; i < sceneGraphPriorityListeners.length;) { + selListener = sceneGraphPriorityListeners[i]; + if (!selListener._isRegistered()) { + cc.js.array.remove(sceneGraphPriorityListeners, selListener); + } else + ++i; + } + } + + if (fixedPriorityListeners) { + for (i = 0; i < fixedPriorityListeners.length;) { + selListener = fixedPriorityListeners[i]; + if (!selListener._isRegistered()) + cc.js.array.remove(fixedPriorityListeners, selListener); + else + ++i; + } + } + + if (sceneGraphPriorityListeners && sceneGraphPriorityListeners.length === 0) + listeners.clearSceneGraphListeners(); + + if (fixedPriorityListeners && fixedPriorityListeners.length === 0) + listeners.clearFixedListeners(); + }, + + _updateListeners: function (event) { + var locInDispatch = this._inDispatch; + cc.assert(locInDispatch > 0, cc._LogInfos.EventManager._updateListeners); + + if(locInDispatch > 1) + return; + + if (event.getType() === cc.Event.TOUCH) { + this._onUpdateListeners(cc._EventListenerTouchOneByOne.LISTENER_ID); + this._onUpdateListeners(cc._EventListenerTouchAllAtOnce.LISTENER_ID); + } else + this._onUpdateListeners(cc.__getListenerID(event)); + + cc.assert(locInDispatch === 1, cc._LogInfos.EventManager._updateListeners_2); + 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]); + this._toAddedListeners.length = 0; + } + }, + + _onTouchEventCallback: function(listener, argsObj){ + // Skip if the listener was removed. + if (!listener._isRegistered) + return false; + + var event = argsObj.event, selTouch = event.currentTouch; + event.currentTarget = listener._node; + + var isClaimed = false, removedIdx; + var getCode = event.getEventCode(), EventTouch = cc.Event.EventTouch; + if (getCode === EventTouch.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 === EventTouch.MOVED && listener.onTouchMoved){ + listener.onTouchMoved(selTouch, event); + } else if(getCode === EventTouch.ENDED){ + if (listener.onTouchEnded) + listener.onTouchEnded(selTouch, event); + if (listener._registered) + listener._claimedTouches.splice(removedIdx, 1); + } else if(getCode === EventTouch.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._updateListeners(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.js.array.copy(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++) { + event.currentTouch = 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._updateListeners(event); + }, + + _onTouchesEventCallback: function (listener, callbackParams) { + // Skip if the listener was removed. + if (!listener._registered) + return false; + + var EventTouch = cc.Event.EventTouch, event = callbackParams.event, touches = callbackParams.touches, getCode = event.getEventCode(); + event.currentTarget = listener._node; + if(getCode === EventTouch.BEGAN && listener.onTouchesBegan) + listener.onTouchesBegan(touches, event); + else if(getCode === EventTouch.MOVED && listener.onTouchesMoved) + listener.onTouchesMoved(touches, event); + else if(getCode === EventTouch.ENDED && listener.onTouchesEnded) + listener.onTouchesEnded(touches, event); + else if(getCode === EventTouch.CANCELLED && listener.onTouchesCancelled) + listener.onTouchesCancelled(touches, event); + + // If the event was stopped, return directly. + if (event.isStopped()) { + cc.eventManager._updateListeners(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.js.array.remove(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.
+ *

+ * @method addListener + * @param {EventListener|Object} listener - The listener of a specified event or a object of some event parameters. + * @param {ENode|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 {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.js.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.js.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. + * @method addCustomListener + * @param {String} eventName + * @param {Function} callback + * @return {EventListener} the generated event. Needed in order to remove the event from the dispatcher + */ + addCustomListener: function (eventName, callback) { + var listener = new cc._EventListenerCustom(eventName, callback); + this.addListener(listener, 1); + return listener; + }, + + /** + * Remove a listener. + * @method removeListener + * @param {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.js.array.remove(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.js.array.remove(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.js.array.remove(listeners, selListener); + return true; + } + } + return false; + }, + + /** + * Removes all listeners with the same event listener type or removes all listeners of a node + * @method removeListeners + * @param {Number|ENode} 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.js.array.remove(_t._dirtyNodes, listenerType); + var listeners = _t._nodeListenersMap[listenerType.__instanceId], i; + if (listeners) { + var listenersCopy = cc.js.array.copy(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 + * @method removeCustomListeners + * @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. + * @method setPriority + * @param {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 + * @method setEnabled + * @param {Boolean} enabled + */ + setEnabled: function (enabled) { + this._isEnabled = enabled; + }, + + /** + * Checks whether dispatching events is enabled + * @method isEnabled + * @returns {Boolean} + */ + isEnabled: function () { + return this._isEnabled; + }, + + /** + * Dispatches the event, also removes all EventListeners marked for deletion from the event dispatcher list. + * @method dispatchEvent + * @param {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.getType() === cc.Event.TOUCH) { + this._dispatchTouchEvent(event); + this._inDispatch--; + return; + } + + var listenerID = cc.__getListenerID(event); + this._sortEventListeners(listenerID); + var selListeners = this._listenersMap[listenerID]; + if (selListeners != null) + this._dispatchEventToListeners(selListeners, this._onListenerCallback, event); + + this._updateListeners(event); + this._inDispatch--; + }, + + _onListenerCallback: function(listener, event){ + event.currentTarget = listener._target; + listener._onEvent(event); + return event.isStopped(); + }, + + /** + * Dispatches a Custom Event with a event name an optional user data + * @method dispatchCustomEvent + * @param {String} eventName + * @param {*} optionalUserData + */ + dispatchCustomEvent: function (eventName, optionalUserData) { + var ev = new cc.Event.EventCustom(eventName); + ev.setUserData(optionalUserData); + this.dispatchEvent(ev); + } +}; diff --git a/cocos2d/core/event-manager/CCSystemEvent.js b/cocos2d/core/event-manager/CCSystemEvent.js new file mode 100644 index 00000000000..23640f147ec --- /dev/null +++ b/cocos2d/core/event-manager/CCSystemEvent.js @@ -0,0 +1,428 @@ +/**************************************************************************** + 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. + ****************************************************************************/ + +var JS = cc.js; + +var Event = cc.Event; + +/** + * The type code of Touch event. + * @constant + * @type {String} + */ +cc.Event.TOUCH = 'touch'; +/** + * The type code of Mouse event. + * @constant + * @type {String} + */ +cc.Event.MOUSE = 'mouse'; +/** + * The type code of UI focus event. + * @constant + * @type {String} + */ +cc.Event.FOCUS = 'focus'; +/** + * The type code of Keyboard event. + * @constant + * @memberof cc.Event + * @type {String} + */ +cc.Event.KEYBOARD = 'keyboard'; +/** + * The type code of Acceleration event. + * @constant + * @memberof cc.Event + * @type {String} + */ +cc.Event.ACCELERATION = 'acceleration'; + +/** + * The mouse event + * @class Event.EventMouse + * @constructor + * @extends Event + * @param {Number} eventType - The mouse event type, UP, DOWN, MOVE, CANCELED + * @param {Boolean} [bubbles=false] - A boolean indicating whether the event bubbles up through the tree or not + */ +var EventMouse = function (eventType, bubbles) { + cc.Event.call(this, cc.Event.MOUSE, bubbles); + this._eventType = eventType; + this._button = 0; + this._x = 0; + this._y = 0; + this._prevX = 0; + this._prevY = 0; + this._scrollX = 0; + this._scrollY = 0; +}; + +JS.extend(EventMouse, cc.Event); +JS.mixin(EventMouse.prototype, { + /** + * Sets scroll data. + * @method setScrollData + * @param {Number} scrollX + * @param {Number} scrollY + */ + setScrollData: function (scrollX, scrollY) { + this._scrollX = scrollX; + this._scrollY = scrollY; + }, + + /** + * Returns the x axis scroll value. + * @method getScrollX + * @returns {Number} + */ + getScrollX: function () { + return this._scrollX; + }, + + /** + * Returns the y axis scroll value. + * @method getScrollY + * @returns {Number} + */ + getScrollY: function () { + return this._scrollY; + }, + + /** + * Sets cursor location. + * @method setLocation + * @param {Number} x + * @param {Number} y + */ + setLocation: function (x, y) { + this._x = x; + this._y = y; + }, + + /** + * Returns cursor location. + * @method getLocation + * @return {Vec2} location + */ + getLocation: function () { + return {x: this._x, y: this._y}; + }, + + /** + * Returns the current cursor location in screen coordinates. + * @method getLocationInView + * @return {Vec2} + */ + 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. + * @method getDelta + * @return {Vec2} + */ + 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. + * @method getDeltaX + * @return {Number} + */ + getDeltaX: function () { + return this._x - this._prevX; + }, + + /** + * Returns the Y axis delta distance from the previous location to current location. + * @method getDeltaY + * @return {Number} + */ + getDeltaY: function () { + return this._y - this._prevY; + }, + + /** + * Sets mouse button. + * @method setButton + * @param {Number} button + */ + setButton: function (button) { + this._button = button; + }, + + /** + * Returns mouse button. + * @method getButton + * @returns {Number} + */ + getButton: function () { + return this._button; + }, + + /** + * Returns location X axis data. + * @method getLocationX + * @returns {Number} + */ + getLocationX: function () { + return this._x; + }, + + /** + * Returns location Y axis data. + * @method getLocationY + * @returns {Number} + */ + getLocationY: function () { + return this._y; + } +}); + +//Inner event types of MouseEvent +/** + * The none event code of mouse event. + * @constant + * @type {Number} + */ +EventMouse.NONE = 0; +/** + * The event type code of mouse down event. + * @constant + * @type {Number} + */ +EventMouse.DOWN = 1; +/** + * The event type code of mouse up event. + * @constant + * @type {Number} + */ +EventMouse.UP = 2; +/** + * The event type code of mouse move event. + * @constant + * @type {Number} + */ +EventMouse.MOVE = 3; +/** + * The event type code of mouse scroll event. + * @constant + * @type {Number} + */ +EventMouse.SCROLL = 4; + +/** + * The tag of Mouse left button + * @constant + * @type {Number} + */ +EventMouse.BUTTON_LEFT = 0; + +/** + * The tag of Mouse right button (The right button number is 2 on browser) + * @constant + * @type {Number} + */ +EventMouse.BUTTON_RIGHT = 2; + +/** + * The tag of Mouse middle button (The right button number is 1 on browser) + * @constant + * @type {Number} + */ +EventMouse.BUTTON_MIDDLE = 1; + +/** + * The tag of Mouse button 4 + * @constant + * @type {Number} + */ +EventMouse.BUTTON_4 = 3; + +/** + * The tag of Mouse button 5 + * @constant + * @type {Number} + */ +EventMouse.BUTTON_5 = 4; + +/** + * The tag of Mouse button 6 + * @constant + * @type {Number} + */ +EventMouse.BUTTON_6 = 5; + +/** + * The tag of Mouse button 7 + * @constant + * @type {Number} + */ +EventMouse.BUTTON_7 = 6; + +/** + * The tag of Mouse button 8 + * @constant + * @type {Number} + */ +EventMouse.BUTTON_8 = 7; + +/** + * The touch event + * @class Event.EventTouch + * @constructor + * @extends Event + * @param {Array} [touchArr=[]] - The array of the touches + * @param {Boolean} [bubbles=false] - A boolean indicating whether the event bubbles up through the tree or not + */ +EventTouch = function (touchArr, bubbles) { + cc.Event.call(this, cc.Event.TOUCH, bubbles); + this._eventCode = 0; + this._touches = touchArr || []; + this.currentTouch = null; +}; + +JS.extend(EventTouch, cc.Event); +JS.mixin(EventTouch.prototype, { + /** + * Returns event code. + * @method getEventCode + * @returns {Number} + */ + getEventCode: function () { + return this._eventCode; + }, + + /** + * Returns touches of event. + * @method getTouches + * @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} + */ +EventTouch.MAX_TOUCHES = 5; + +/** + * The event type code of touch began event. + * @constant + * @type {Number} + */ +EventTouch.BEGAN = 0; +/** + * The event type code of touch moved event. + * @constant + * @type {Number} + */ +EventTouch.MOVED = 1; +/** + * The event type code of touch ended event. + * @constant + * @type {Number} + */ +EventTouch.ENDED = 2; +/** + * The event type code of touch cancelled event. + * @constant + * @type {Number} + */ +EventTouch.CANCELED = 3; + +/** + * Focus change event for UI widget + * @class Event.EventFocus + * @constructor + * @extends Event + * @param {Widget} widgetLoseFocus + * @param {Widget} widgetGetFocus + * @param {Boolean} [bubbles=false] - A boolean indicating whether the event bubbles up through the tree or not + */ +EventFocus = function (widgetGetFocus, widgetLoseFocus, bubbles) { + cc.Event.call(this, cc.Event.FOCUS, bubbles); + this._widgetGetFocus = widgetGetFocus; + this._widgetLoseFocus = widgetLoseFocus; +}; +JS.extend(EventFocus, cc.Event); + +/** + * The acceleration event + * @class Event.EventAcceleration + * @extends Event + * @constructor + * @param {Object} acc - The acceleration + * @param {Boolean} [bubbles=false] - A boolean indicating whether the event bubbles up through the tree or not + */ +EventAcceleration = function (acc, bubbles) { + cc.Event.call(this, Event.ACCELERATION, bubbles); + this._acc = acc; +}; +JS.extend(EventAcceleration, cc.Event); + +/** + * The keyboard event + * @class Event.EventKeyboard + * @extends Event + * @constructor + * @param {Number} keyCode - The key code of which triggered this event + * @param {Boolean} isPressed - A boolean indicating whether the key have been pressed + * @param {Boolean} [bubbles=false] - A boolean indicating whether the event bubbles up through the tree or not + */ +EventKeyboard = function (keyCode, isPressed, bubbles) { + cc.Event.call(this, Event.KEYBOARD, bubbles); + this._keyCode = keyCode; + this._isPressed = isPressed; +}; +JS.extend(EventKeyboard, cc.Event); + +cc.Event.EventMouse = EventMouse; +cc.Event.EventTouch = EventTouch; +cc.Event.EventFocus = EventFocus; +cc.Event.EventAcceleration = EventAcceleration; +cc.Event.EventKeyboard = EventKeyboard; + +module.exports = Event; \ No newline at end of file diff --git a/cocos2d/core/event-manager/CCTouch.js b/cocos2d/core/event-manager/CCTouch.js new file mode 100644 index 00000000000..b82f3db4f04 --- /dev/null +++ b/cocos2d/core/event-manager/CCTouch.js @@ -0,0 +1,186 @@ +/**************************************************************************** + 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 Touch + * @extends _Class + * + * @param {Number} x + * @param {Number} y + * @param {Number} id + */ +cc.Touch = cc._Class.extend(/** @lends cc.Touch# */{ + _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. + * @method getLocation + * @return {Vec2} + */ + getLocation:function () { + //TODO + //return cc.director.convertToGL(this._point); + return {x: this._point.x, y: this._point.y}; + }, + + /** + * Returns X axis location value. + * @method getLocationX + * @returns {Number} + */ + getLocationX: function () { + return this._point.x; + }, + + /** + * Returns Y axis location value. + * @method getLocationY + * @returns {Number} + */ + getLocationY: function () { + return this._point.y; + }, + + /** + * Returns the previous touch location in OpenGL coordinates. + * @method getPreviousLocation + * @return {Vec2} + */ + 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. + * @method getStartLocation + * @returns {Vec2} + */ + 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. + * @method getDelta + * @return {Vec2} + */ + getDelta:function () { + return cc.pSub(this._point, this._prevPoint); + }, + + /** + * Returns the current touch location in screen coordinates. + * @method getLocationInView + * @return {Vec2} + */ + getLocationInView: function() { + return {x: this._point.x, y: this._point.y}; + }, + + /** + * Returns the previous touch location in screen coordinates. + * @method getPreviousLocationInView + * @return {Vec2} + */ + getPreviousLocationInView: function(){ + return {x: this._prevPoint.x, y: this._prevPoint.y}; + }, + + /** + * Returns the start touch location in screen coordinates. + * @method getStartLocationInView + * @return {Vec2} + */ + getStartLocationInView: function(){ + return {x: this._startPoint.x, y: this._startPoint.y}; + }, + + /** + * Returns the id of cc.Touch. + * @method getID + * @return {Number} + */ + getID:function () { + return this._id; + }, + + /** + * Returns the id of cc.Touch. + * @method getId + * @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. + * @method setTouchInfo + * @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); + 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/cocos2d/core/event/event-listeners.js b/cocos2d/core/event/event-listeners.js new file mode 100644 index 00000000000..fa33d92d207 --- /dev/null +++ b/cocos2d/core/event/event-listeners.js @@ -0,0 +1,55 @@ +var JS = cc.js; +var CallbacksHandler = require('../platform/callbacks-invoker').CallbacksHandler; + +// Extends CallbacksHandler to handle and invoke event callbacks. +function EventListeners () { + CallbacksHandler.call(this); +} +JS.extend(EventListeners, CallbacksHandler); + +EventListeners.prototype.invoke = function (event) { + var list = this._callbackTable[event.type], + i, endIndex, offset, valid, + callingFunc, target, hasTarget; + if (list && list.length > 0) { + if (list.length === 1) { + list[0].call(event.currentTarget, event); + return; + } + endIndex = list.length - 1; + for (i = 0; i <= endIndex;) { + callingFunc = list[i]; + target = list[i+1]; + if (target && typeof target === 'object') { + hasTarget = true; + callingFunc.call(target, event); + } + else { + hasTarget = false; + callingFunc.call(event.currentTarget, event); + } + if (event._propagationImmediateStopped || i === endIndex) { + break; + } + // 为了ä¸æ¯æ¬¡è§¦å‘消æ¯æ—¶éƒ½åˆ›å»ºä¸€ä»½å›žè°ƒæ•°ç»„çš„æ‹·è´ï¼Œè¿™é‡Œéœ€è¦å¯¹æ¶ˆæ¯çš„å注册åšæ£€æŸ¥å’Œé™åˆ¶ + // check last one to see if any one removed + offset = endIndex - (list.length-1); + valid = true; + if (offset <= 2) { // åªåˆ ä¸€ä¸ªitem + if (list[i] !== callingFunc) { // 如果删了åŽé¢çš„回调,索引ä¸å˜ + i -= offset; + } + endIndex -= offset; + } + else if (offset !== 0) { + // åªå…许在一个回调里é¢ç§»é™¤ä¸€ä¸ªå›žè°ƒã€‚如果è¦ç§»é™¤å¾ˆå¤šï¼Œåªèƒ½ç”¨ event.stopPropagationImmediate() + cc.error('Call event.stopPropagationImmediate() when you remove more than one callbacks in a event callback.'); + return; + } + // Increment i + hasTarget ? i += 2 : ++i; + } + } +}; + +module.exports = EventListeners; diff --git a/cocos2d/core/event/event-target.js b/cocos2d/core/event/event-target.js new file mode 100644 index 00000000000..0557795e4ec --- /dev/null +++ b/cocos2d/core/event/event-target.js @@ -0,0 +1,343 @@ +var EventListeners = require('./event-listeners'); +var Event = require('./event'); +var EventCustom = Event.EventCustom; +var JS = cc.js; + +var cachedArray = new Array(16); +cachedArray.length = 0; + +var _doDispatchEvent = function (owner, event, args) { + var target, i; + event.target = owner; + + // Event.CAPTURING_PHASE + owner._getCapturingTargets(event.type, cachedArray); + // propagate + event.eventPhase = 1; + for (i = cachedArray.length - 1; i >= 0; --i) { + target = cachedArray[i]; + if (target._isTargetActive(event.type) && target._capturingListeners) { + event.currentTarget = target; + // fire event + target._capturingListeners.invoke(event); + // check if propagation stopped + if (event._propagationStopped) { + return; + } + } + } + cachedArray.length = 0; + + // Event.AT_TARGET + // checks if destroyed in capturing callbacks + if (owner._isTargetActive(event.type)) { + _doSendEvent(owner, event); + if (event._propagationStopped) { + return; + } + } + + if (event.bubbles) { + // Event.BUBBLING_PHASE + owner._getBubblingTargets(event.type, cachedArray); + // propagate + event.eventPhase = 3; + for (i = 0; i < cachedArray.length; ++i) { + target = cachedArray[i]; + if (target._isTargetActive(event.type) && target._bubblingListeners) { + event.currentTarget = target; + // fire event + target._bubblingListeners.invoke(event); + // check if propagation stopped + if (event._propagationStopped) { + return; + } + } + } + } + cachedArray.length = 0; +}; + + +var _doSendEvent = function (owner, event) { + // Event.AT_TARGET + event.eventPhase = 2; + event.currentTarget = owner; + if (owner._capturingListeners) { + owner._capturingListeners.invoke(event); + if (event._propagationStopped) { + return; + } + } + if (owner._bubblingListeners) { + owner._bubblingListeners.invoke(event); + } +}; + +/** + * EventTarget is an object to which an event is dispatched when something has occurred. + * Entity are the most common event targets, but other objects can be event targets too. + * + * Event targets are an important part of the Fireball event model. + * The event target serves as the focal point for how events flow through the scene graph. + * When an event such as a mouse click or a keypress occurs, Fireball dispatches an event object + * into the event flow from the root of the hierarchy. The event object then makes its way through + * the scene graph until it reaches the event target, at which point it begins its return trip through + * the scene graph. This round-trip journey to the event target is conceptually divided into three phases: + * - The capture phase comprises the journey from the root to the last node before the event target's node + * - The target phase comprises only the event target node + * - The bubbling phase comprises any subsequent nodes encountered on the return trip to the root of the tree + * See also: http://www.w3.org/TR/DOM-Level-3-Events/#event-flow + * + * Event targets can implement the following methods: + * - _getCapturingTargets + * - _getBubblingTargets + * + * @class EventTarget + */ +var EventTarget = function () { +}; + +JS.mixin(EventTarget.prototype, { + /** + * @property _capturingListeners + * @type {EventListeners} + * @default null + * @private + */ + _capturingListeners: null, + + /** + * @property _bubblingListeners + * @type {EventListeners} + * @default null + * @private + */ + _bubblingListeners: null, + + /** + * Checks whether the EventTarget object has any callback registered for a specific type of event. + * + * @param {String} type - The type of event. + * @param {Boolean} A value of true if a callback of the specified type is registered; false otherwise. + */ + hasEventListener: function (type) { + return this._bubblingListeners.has(type) || this._capturingListeners.has(type); + }, + + /** + * Register an callback of a specific event type on the EventTarget. + * This method is merely an alias to addEventListener. + * + * @method on + * @param {String} type - A string representing the event type to listen for. + * @param {Function} callback - The callback that will be invoked when the event is dispatched. + * The callback is ignored if it is a duplicate (the callbacks are unique). + * @param {Event} callback.param event + * @param {Object} [target] - The target to invoke the callback, can be null + * @param {Boolean} [useCapture=false] - When set to true, the capture argument prevents callback + * from being invoked when the event's eventPhase attribute value is BUBBLING_PHASE. + * When false, callback will NOT be invoked when event's eventPhase attribute value is CAPTURING_PHASE. + * Either way, callback will be invoked when event's eventPhase attribute value is AT_TARGET. + * @return {Function} - Just returns the incoming callback so you can save the anonymous function easier. + */ + on: function (type, callback, target, useCapture) { + // Accept also patameters like: (type, callback, useCapture) + if (typeof target === 'boolean') { + useCapture = target; + target = undefined; + } + else useCapture = !!useCapture; + if (!callback) { + cc.error('Callback of event must be non-nil'); + return; + } + var listeners = null; + if (useCapture) { + listeners = this._capturingListeners = this._capturingListeners || new EventListeners(); + } + else { + listeners = this._bubblingListeners = this._bubblingListeners || new EventListeners(); + } + if ( ! listeners.has(type, callback, target) ) { + listeners.add(type, callback, target); + + if (target && target.__eventTargets) + target.__eventTargets.push(this); + } + return callback; + }, + + /** + * Removes the callback previously registered with the same type, callback, target and or useCapture. + * This method is merely an alias to removeEventListener. + * + * @method off + * @param {String} type - A string representing the event type being removed. + * @param {Function} callback - The callback to remove. + * @param {Object} [target] - The target to invoke the callback, if it's not given, only callback without target will be removed + * @param {Boolean} [useCapture=false] - Specifies whether the callback being removed was registered as a capturing callback or not. + * If not specified, useCapture defaults to false. If a callback was registered twice, + * one with capture and one without, each must be removed separately. Removal of a capturing callback + * does not affect a non-capturing version of the same listener, and vice versa. + */ + off: function (type, callback, target, useCapture) { + // Accept also patameters like: (type, callback, useCapture) + if (typeof target === 'boolean') { + useCapture = target; + target = undefined; + } + else useCapture = !!useCapture; + if (!callback) { + return; + } + var listeners = useCapture ? this._capturingListeners : this._bubblingListeners; + if (listeners) { + listeners.remove(type, callback, target); + + if (target && target.__eventTargets) { + var index = target.__eventTargets.indexOf(this); + target.__eventTargets.splice(index, 1); + } + } + }, + + /** + * Removes all callbacks previously registered with the same target. + * + * @method targetOff + * @param {Object} target - The target to be searched for all related callbacks + */ + targetOff: function (target) { + this._capturingListeners.removeAll(target); + this._bubblingListeners.removeAll(target); + }, + + /** + * Register an callback of a specific event type on the EventTarget, the callback will remove itself after the first time it is triggered. + * + * @method once + * @param {String} type - A string representing the event type to listen for. + * @param {Function} callback - The callback that will be invoked when the event is dispatched. + * The callback is ignored if it is a duplicate (the callbacks are unique). + * @param {Event} callback.param event + * @param {Object} [target] - The target to invoke the callback, can be null + * @param {Boolean} [useCapture=false] - When set to true, the capture argument prevents callback + * from being invoked when the event's eventPhase attribute value is BUBBLING_PHASE. + * When false, callback will NOT be invoked when event's eventPhase attribute value is CAPTURING_PHASE. + * Either way, callback will be invoked when event's eventPhase attribute value is AT_TARGET. + */ + once: function (type, callback, target, useCapture) { + var self = this; + var cb = function (event) { + self.off(type, cb, target, useCapture); + callback.call(this, event); + }; + this.on(type, cb, target, useCapture); + }, + + /** + * Dispatches an event into the event flow. The event target is the EventTarget object upon which the dispatchEvent() method is called. + * + * @method dispatchEvent + * @param {Event} event - The Event object that is dispatched into the event flow + * @return {Boolean} - returns true if either the event's preventDefault() method was not invoked, + * or its cancelable attribute value is false, and false otherwise. + */ + dispatchEvent: function (event) { + _doDispatchEvent(this, event); + cachedArray.length = 0; + var notPrevented = ! event._defaultPrevented; + // event.unuse(); + return notPrevented; + }, + + /** + * Send an event to this object directly, this method will not propagate the event to any other objects. + * The event will be created from the supplied message, you can get the "detail" argument from event.detail. + * + * @method emit + * @param {String} message - the message to send + * @param {any} [detail] - whatever argument the message needs + */ + emit: function (message, detail) { + if ( typeof message === 'string' ) { + var event = new EventCustom(message); + event.detail = detail; + _doSendEvent(this, event); + } + else { + cc.error('The message must be provided'); + } + }, + + /** + * Get whether the target is active for events. + * The name is for avoiding conflict with user defined functions. + * + * Subclasses can override this method to make event target active or inactive. + * @method _isTargetActive + * @param {String} type - the event type + * @return {Boolean} - A boolean value indicates the event target is active or not + */ + _isTargetActive: function (type) { + return true; + }, + + /** + * Get all the targets listening to the supplied type of event in the target's capturing phase. + * The capturing phase comprises the journey from the root to the last node BEFORE the event target's node. + * The result should save in the array parameter, and MUST SORT from child nodes to parent nodes. + * + * Subclasses can override this method to make event propagable. + * @method _getCapturingTargets + * @param {String} type - the event type + * @param {Array} array - the array to receive targets + * @example {@link utils/api/cocos/docs/cocos2d/core/event/_getCapturingTargets.js} + */ + _getCapturingTargets: function (type, array) { + + }, + + /** + * Get all the targets listening to the supplied type of event in the target's bubbling phase. + * The bubbling phase comprises any SUBSEQUENT nodes encountered on the return trip to the root of the tree. + * The result should save in the array parameter, and MUST SORT from child nodes to parent nodes. + * + * Subclasses can override this method to make event propagable. + * @method _getBubblingTargets + * @param {String} type - the event type + * @param {Array} array - the array to receive targets + */ + _getBubblingTargets: function (type, array) { + // Object can override this method to make event propagable. + } +}); + +/** + * Polyfill the functionalities of EventTarget into a existing object. + * @static + * @memberof EventTarget + * @param {Object} object - An object to be extended with EventTarget capability + */ +EventTarget.polyfill = function (object) { + var proto = EventTarget.prototype; + // Can't use cc.js.mixin because we don't want to inject polyfill or overwrite _getXXXTargets + object._capturingListeners = null; + object._bubblingListeners = null; + + object.hasEventListener = proto.hasEventListener; + object.on = proto.on; + object.off = proto.off; + object.once = proto.once; + object.dispatchEvent = proto.dispatchEvent; + object.emit = proto.emit; + if (!object._isTargetActive) + object._isTargetActive = proto._isTargetActive; + if (!object._getCapturingTargets) + object._getCapturingTargets = proto._getCapturingTargets; + if (!object._getBubblingTargets) + object._getBubblingTargets = proto._getBubblingTargets; +}; + +cc.EventTarget = module.exports = EventTarget; diff --git a/cocos2d/core/event/event.js b/cocos2d/core/event/event.js new file mode 100644 index 00000000000..4f892666d52 --- /dev/null +++ b/cocos2d/core/event/event.js @@ -0,0 +1,279 @@ +/**************************************************************************** + 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. + ****************************************************************************/ + +var JS = require("../platform/js"); + +/** + * Base class of all kinds of events. + * @class Event + * @constructor + * @param {string} type - The name of the event (case-sensitive), e.g. "click", "fire", or "submit" + * @param {boolean} [bubbles=false] - A boolean indicating whether the event bubbles up through the tree or not + */ +cc.Event = function(type, bubbles) { + /** + * The name of the event (case-sensitive), e.g. "click", "fire", or "submit". + * @property type + * @type {String} + */ + this.type = type; + + /** + * A reference to the target to which the event was originally dispatched. + * @property target + * @type {Object} + */ + this.bubbles = bubbles || false; + + /** + * A reference to the target to which the event was originally dispatched. + * @property target + * @type {Object} + */ + this.target = null; + + /** + * A reference to the currently registered target for the event. + * @property currentTarget; + * @type {Object} + */ + this.currentTarget = null; + + /** + * Indicates which phase of the event flow is currently being evaluated. + * Returns an integer value represented by 4 constants: + * - Event.NONE = 0 + * - Event.CAPTURING_PHASE = 1 + * - Event.AT_TARGET = 2 + * - Event.BUBBLING_PHASE = 3 + * The phases are explained in the [section 3.1, Event dispatch and DOM event flow] + * (http://www.w3.org/TR/DOM-Level-3-Events/#event-flow), of the DOM Level 3 Events specification. + * + * @property eventPhase + * @type {Number} + */ + this.eventPhase = 0; + + /** + * Indicates whether or not event.preventDefault() has been called on the event. + * @property _defaultPrevented + * @type {Boolean} + * @private + */ + this._defaultPrevented = false; + + /** + * Indicates whether or not event.stopPropagation() has been called on the event. + * @property _propagationStopped + * @type {Boolean} + * @private + */ + this._propagationStopped = false; + + /** + * Indicates whether or not event.stopPropagationImmediate() has been called on the event. + * @property _propagationImmediateStopped + * @type {Boolean} + * @private + */ + this._propagationImmediateStopped = false; +}; +cc.Event.prototype = { + constructor: cc.Event, + + /** + * Reset the event for being stored in the object pool. + * @method unuse + * @returns {String} + */ + unuse: function () { + this.type = cc.Event.NO_TYPE; + this.target = null; + this.currentTarget = null; + this.eventPhase = cc.Event.NONE; + this._defaultPrevented = false; + this._propagationStopped = false; + this._propagationImmediateStopped = false; + }, + + /** + * Reuse the event for being used again by the object pool. + * @method reuse + * @returns {String} + */ + reuse: function (type, bubbles) { + this.type = type; + this.bubbles = bubbles || false; + }, + + /** + * If invoked when the cancelable attribute value is true, signals to the operation that caused event to be dispatched that it needs to be canceled. + * @method preventDefault + */ + preventDefault: function () { + this._defaultPrevented = true; + }, + + /** + * Stops propagation for current event. + * @method stopPropagation + */ + stopPropagation: function () { + this._propagationStopped = true; + }, + + /** + * Stops propagation for current event immediately, + * the event won't even be dispatched to the listeners attached in the current target. + * @method stopPropagationImmediate + */ + stopPropagationImmediate: function () { + this._propagationImmediateStopped = true; + }, + + /** + * Checks whether the event has been stopped. + * @method isStopped + * @returns {Boolean} + */ + isStopped: function () { + return this._propagationStopped || this._propagationImmediateStopped; + }, + + /** + *

+ * 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. + *

+ * @method getCurrentTarget + * @returns {ENode} The target with which the event associates. + */ + getCurrentTarget: function () { + return this.currentTarget; + }, + + /** + * Gets the event type. + * @method getType + * @returns {String} + */ + getType: function () { + return this.type; + } +}; + +//event type +/** + * Code for event without type. + * @constant + * @type {string} + */ +cc.Event.NO_TYPE = 'no_type'; + +//event phase +/** + * Events not currently dispatched are in this phase + * @property NONE + * @type {Number} + * @static + * @final + */ +cc.Event.NONE = 0; +/** + * The capturing phase comprises the journey from the root to the last node before the event target's node + * see http://www.w3.org/TR/DOM-Level-3-Events/#event-flow + * @property CAPTURING_PHASE + * @type {Number} + * @static + * @final + */ +cc.Event.CAPTURING_PHASE = 1; +/** + * The target phase comprises only the event target node + * see http://www.w3.org/TR/DOM-Level-3-Events/#event-flow + * @property AT_TARGET + * @type {Number} + * @static + * @final + */ +cc.Event.AT_TARGET = 2; +/** + * The bubbling phase comprises any subsequent nodes encountered on the return trip to the root of the hierarchy + * see http://www.w3.org/TR/DOM-Level-3-Events/#event-flow + * @property BUBBLING_PHASE + * @type {Number} + * @static + * @final + */ +cc.Event.BUBBLING_PHASE = 3; + +/** + * The Custom event + * @class Event.EventCustom + * @constructor + * @extends Event + * @param {String} type - The name of the event (case-sensitive), e.g. "click", "fire", or "submit" + * @param {Boolean} [bubbles=false] - A boolean indicating whether the event bubbles up through the tree or not + */ +var EventCustom = function (type, bubbles) { + cc.Event.call(this, type, bubbles); + + /** + * A reference to the detailed data of the event + * @property detail + * @type {Object} + */ + this.detail = null; +}; + +JS.extend(EventCustom, cc.Event); +JS.mixin(EventCustom.prototype, { + /** + * Sets user data + * @param {*} data + */ + setUserData: function (data) { + this.detail = data; + }, + + /** + * Gets user data + * @returns {*} + */ + getUserData: function () { + return this.detail; + }, + + /** + * Gets event name + * @returns {String} + */ + getEventName: cc.Event.prototype.getType +}); + +cc.Event.EventCustom = EventCustom; + +module.exports = cc.Event; \ No newline at end of file diff --git a/cocos2d/core/event/index.js b/cocos2d/core/event/index.js new file mode 100644 index 00000000000..a3464ae514c --- /dev/null +++ b/cocos2d/core/event/index.js @@ -0,0 +1,3 @@ +require('./event.js'); +require('./event-listeners.js'); +require('./event-target.js'); \ No newline at end of file diff --git a/cocos2d/core/index.js b/cocos2d/core/index.js new file mode 100644 index 00000000000..3e20b9f7b0a --- /dev/null +++ b/cocos2d/core/index.js @@ -0,0 +1,11 @@ +require('./platform'); +require('./assets'); + +if (!CC_EDITOR || !Editor.isCoreLevel) { + require('./CCNode'); + require('./CCScene'); + + require('./components'); +} + +require('./base-ui/CCWidgetManager'); diff --git a/cocos2d/core/labelttf/CCLabelTTF.js b/cocos2d/core/labelttf/CCLabelTTF.js new file mode 100644 index 00000000000..47722a4dd66 --- /dev/null +++ b/cocos2d/core/labelttf/CCLabelTTF.js @@ -0,0 +1,869 @@ +/**************************************************************************** + 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 {cc.TextAlignment} [hAlignment=cc.TextAlignment.LEFT] + * @param {cc.VerticalTextAlignment} [vAlignment=cc.VerticalTextAlignment.TOP] + * @example + * var myLabel = new cc.LabelTTF('label text', 'Times New Roman', 32, cc.size(320,32), cc.TextAlignment.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 {cc.TextAlignment} textAlign - Horizontal Alignment of label: cc.TextAlignment.LEFT|cc.TextAlignment.CENTER|cc.TextAlignment.RIGHT + * @property {cc.VerticalTextAlignment} verticalAlign - Vertical Alignment of label: cc.VerticalTextAlignment.TOP|cc.VerticalTextAlignment.CENTER|cc.VerticalTextAlignment.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.TextAlignment.CENTER, + _vAlignment: cc.VerticalTextAlignment.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.TextAlignment.LEFT; + vAlignment = vAlignment || cc.VerticalTextAlignment.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(); + 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.TextAlignment.LEFT; + this._vAlignment = cc.VerticalTextAlignment.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.TextAlignment} + */ + getHorizontalAlignment: function () { + return this._hAlignment; + }, + + /** + * Returns Vertical Alignment of cc.LabelTTF + * @return {cc.VerticalTextAlignment} + */ + 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; + }, + + /** + * 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.TextAlignment} 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.VerticalTextAlignment} 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(); + return cc.Sprite.prototype.getContentSize.call(this); + }, + + _getWidth: function () { + if (this._needUpdateTexture) + this._renderCmd._updateTTF(); + return cc.Sprite.prototype._getWidth.call(this); + }, + _getHeight: function () { + if (this._needUpdateTexture) + this._renderCmd._updateTTF(); + return cc.Sprite.prototype._getHeight.call(this); + }, + + setTextureRect: function (rect, rotated, untrimmedSize) { + //set needConvert to false + cc.Sprite.prototype.setTextureRect.call(this, rect, rotated, untrimmedSize, false); + }, + + /** + * 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.js.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 {cc.TextAlignment} [hAlignment=cc.TextAlignment.LEFT] + * @param {cc.VerticalTextAlignment} [vAlignment=cc.VerticalTextAlignment.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; + +if (cc.USE_LA88_LABELS) + cc.LabelTTF._SHADER_PROGRAM = cc.SHADER_POSITION_TEXTURECOLOR; +else + cc.LabelTTF._SHADER_PROGRAM = cc.SHADER_POSITION_TEXTUREA8COLOR; + +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 () { + window.removeEventListener('load', arguments.callee, false); + document.body.appendChild(cc.LabelTTF.__labelHeightDiv); + }, false); + +cc.LabelTTF.__getFontHeightByDiv = function (fontName, fontSize) { + + if(fontName instanceof cc.FontDefinition){ + /** @type cc.FontDefinition */ + var fontDef = fontName; + var clientHeight = cc.LabelTTF.__fontHeightCache[fontDef._getCanvasFontStr()]; + if (clientHeight > 0) return clientHeight; + var labelDiv = cc.LabelTTF.__labelHeightDiv; + 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 = ""; + return clientHeight; + } + + //Default + var clientHeight = cc.LabelTTF.__fontHeightCache[fontName + "." + fontSize]; + if (clientHeight > 0) return clientHeight; + var labelDiv = cc.LabelTTF.__labelHeightDiv; + 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/cocos2d/core/labelttf/CCLabelTTFCanvasRenderCmd.js b/cocos2d/core/labelttf/CCLabelTTFCanvasRenderCmd.js new file mode 100644 index 00000000000..ae672a3755c --- /dev/null +++ b/cocos2d/core/labelttf/CCLabelTTFCanvasRenderCmd.js @@ -0,0 +1,535 @@ +/**************************************************************************** + 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; + +//Support: English French German +//Other as Oriental Language +cc.LabelTTF._wordRex = /([a-zA-Z0-9ÄÖÜäöüßéèçàùêâîôû]+|\S)/; +cc.LabelTTF._symbolRex = /^[!,.:;}\]%\?>ã€â€˜â€œã€‹ï¼Ÿã€‚,ï¼]/; +cc.LabelTTF._lastWordRex = /([a-zA-Z0-9ÄÖÜäöüßéèçàùêâîôû]+|\S)$/; +cc.LabelTTF._lastEnglish = /[a-zA-Z0-9ÄÖÜäöüßéèçàùêâîôû]+$/; +cc.LabelTTF._firsrEnglish = /^[a-zA-Z0-9ÄÖÜäöüßéèçàùêâîôû]/; + +(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; + }; + var proto = cc.LabelTTF.RenderCmd.prototype; + proto.constructor = 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 { + this._fontStyleStr = fontStyle + " " + fontWeight + " " + fontSize + "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)"; + }; + + proto._updateTTF = function () { + var node = this._node; + var locDimensionsWidth = node._dimensions.width, i, strLength; + var locLineWidth = this._lineWidths; + locLineWidth.length = 0; + + this._isMultiLine = false; + this._measureConfig(); + 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++) { + locLineWidth.push(this._measure(this._strings[i])); + } + } + + 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 * this._strings.length) + locStrokeShadowOffsetY)); + else + locSize = cc.size(Math.ceil(this._measure(node._string) + locStrokeShadowOffsetX), Math.ceil(this._fontClientHeight + locStrokeShadowOffsetY)); + } else { + if (node._dimensions.height === 0) { + if (this._isMultiLine) + locSize = cc.size(Math.ceil(locDimensionsWidth + locStrokeShadowOffsetX), Math.ceil((node.getLineHeight() * this._strings.length) + locStrokeShadowOffsetY)); + else + locSize = cc.size(Math.ceil(locDimensionsWidth + locStrokeShadowOffsetX), Math.ceil(node.getLineHeight() + locStrokeShadowOffsetY)); + } else { + //dimension is already set, contentSize must be same as dimension + locSize = cc.size(Math.ceil(locDimensionsWidth + locStrokeShadowOffsetX), Math.ceil(node._dimensions.height + 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 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(); + var transformTop = (lineHeight - this._fontClientHeight) / 2; + + if (locHAlignment === cc.TextAlignment.RIGHT) + xOffset += locContentWidth; + else if (locHAlignment === cc.TextAlignment.CENTER) + xOffset += locContentWidth / 2; + else + xOffset += 0; + + if (this._isMultiLine) { + var locStrLen = this._strings.length; + if (locVAlignment === cc.VerticalTextAlignment.BOTTOM) + yOffset = lineHeight - transformTop * 2 + locContentSizeHeight - lineHeight * locStrLen; + else if (locVAlignment === cc.VerticalTextAlignment.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.VerticalTextAlignment.BOTTOM) { + //do nothing + } else if (locVAlignment === cc.VerticalTextAlignment.TOP) { + yOffset -= locContentSizeHeight; + } else { + yOffset -= locContentSizeHeight * 0.5; + } + OffsetYArray.push(yOffset); + } + var tmpStatus = { + contextTransform:cc.v2f(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; + + 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; + var colorDirty = locFlag & flags.colorDirty, + opacityDirty = locFlag & flags.opacityDirty; + + if (colorDirty) + this._updateDisplayColor(); + if (opacityDirty) + this._updateDisplayOpacity(); + + if(colorDirty || opacityDirty){ + this._updateColor(); + }else if(locFlag & flags.textDirty) + this._updateTexture(); + + if (this._dirtyFlag & flags.transformDirty){ + this.transform(this.getParentRenderCmd(), true); + this._dirtyFlag = this._dirtyFlag & cc.Node._dirtyFlags.transformDirty ^ this._dirtyFlag; + } + }; + + proto._syncStatus = function (parentCmd) { + 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; + + var colorDirty = locFlag & flags.colorDirty, + opacityDirty = locFlag & flags.opacityDirty; + + this._dirtyFlag = locFlag; + + if (colorDirty) + this._syncDisplayColor(); + if (opacityDirty) + this._syncDisplayOpacity(); + + if(colorDirty || opacityDirty){ + this._updateColor(); + }else if(locFlag & flags.textDirty) + this._updateTexture(); + + if (locFlag & flags.transformDirty) //update the transform + 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.strokeText(line, xOffset, yOffsetArray[i]); + context.fillText(line, xOffset, yOffsetArray[i]); + } + cc.g_NumberOfDraws++; + } +})(); + +(function(){ + cc.LabelTTF.CacheRenderCmd = function (renderable) { + cc.LabelTTF.RenderCmd.call(this,renderable); + 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.js.mixin(cc.LabelTTF.CacheRenderCmd.prototype, cc.LabelTTF.RenderCmd.prototype); + + var proto = cc.LabelTTF.CacheRenderCmd.prototype; + proto.constructor = cc.LabelTTF.CacheRenderCmd; + + 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; + + 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; + node._texture && 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); + node._texture && 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) { + return this._labelContext.measureText(text).width; + }; + +})(); + +(function(){ + cc.LabelTTF.CacheCanvasRenderCmd = function (renderable) { + cc.Sprite.CanvasRenderCmd.call(this, renderable); + cc.LabelTTF.CacheRenderCmd.call(this); + }; + + var proto = cc.LabelTTF.CacheCanvasRenderCmd.prototype = Object.create(cc.Sprite.CanvasRenderCmd.prototype); + cc.js.mixin(proto, cc.LabelTTF.CacheRenderCmd.prototype); + proto.constructor = cc.LabelTTF.CacheCanvasRenderCmd; +})(); + +(function(){ + cc.LabelTTF.CanvasRenderCmd = function (renderable) { + cc.Sprite.CanvasRenderCmd.call(this, renderable); + cc.LabelTTF.RenderCmd.call(this); + }; + + cc.LabelTTF.CanvasRenderCmd.prototype = Object.create(cc.Sprite.CanvasRenderCmd.prototype); + cc.js.mixin(cc.LabelTTF.CanvasRenderCmd.prototype, cc.LabelTTF.RenderCmd.prototype); + + var proto = cc.LabelTTF.CanvasRenderCmd.prototype; + proto.constructor = cc.LabelTTF.CanvasRenderCmd; + + proto._measureConfig = function () {}; + + proto._measure = function (text) { + var context = cc._renderContext.getContext(); + context.font = this._fontStyleStr; + return context.measureText(text).width; + }; + + 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(); + }; +})(); \ No newline at end of file diff --git a/cocos2d/core/labelttf/CCLabelTTFWebGLRenderCmd.js b/cocos2d/core/labelttf/CCLabelTTFWebGLRenderCmd.js new file mode 100644 index 00000000000..d813b6c7281 --- /dev/null +++ b/cocos2d/core/labelttf/CCLabelTTFWebGLRenderCmd.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. + ****************************************************************************/ + +// ----------------------------------- LabelTTF WebGL render cmd ---------------------------- +(function() { + cc.LabelTTF.WebGLRenderCmd = function (renderable) { + cc.Sprite.WebGLRenderCmd.call(this, renderable); + cc.LabelTTF.CacheRenderCmd.call(this); + this.setShaderProgram(cc.shaderCache.programForKey(cc.LabelTTF._SHADER_PROGRAM)); + }; + var proto = cc.LabelTTF.WebGLRenderCmd.prototype = Object.create(cc.Sprite.WebGLRenderCmd.prototype); + cc.js.mixin(proto, cc.LabelTTF.CacheRenderCmd.prototype); + proto.constructor = cc.LabelTTF.WebGLRenderCmd; + proto._updateColor = function () { + this._updateTexture(); + cc.Sprite.WebGLRenderCmd.prototype._updateColor.call(this); + } +})(); \ No newline at end of file diff --git a/cocos2d/core/labelttf/LabelTTFPropertyDefine.js b/cocos2d/core/labelttf/LabelTTFPropertyDefine.js new file mode 100644 index 00000000000..71ff3a964ae --- /dev/null +++ b/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/cocos2d/core/layers/CCLayer.js b/cocos2d/core/layers/CCLayer.js new file mode 100644 index 00000000000..77aaf3464ff --- /dev/null +++ b/cocos2d/core/layers/CCLayer.js @@ -0,0 +1,721 @@ +/**************************************************************************** + 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 () { + var nodep = cc.Node.prototype; + nodep.ctor.call(this); + this._ignoreAnchorPointForPosition = true; + nodep.setAnchorPoint.call(this, 0.5, 0.5); + nodep.setContentSize.call(this, cc.winSize); + }, + + /** + * Initialization of the layer, please do not call this function by yourself, you should pass the parameters to constructor to initialize a layer + */ + init: function(){ + var _t = this; + _t._ignoreAnchorPointForPosition = true; + _t.setAnchorPoint(0.5, 0.5); + _t.setContentSize(cc.winSize); + _t._cascadeColorEnabled = false; + _t._cascadeOpacityEnabled = false; + return true; + }, + + /** + * 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; + }, + + 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 = new cc.BlendFunc(cc.SRC_ALPHA, cc.ONE_MINUS_SRC_ALPHA); + 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) { + if (cc._renderType !== cc.game.RENDER_TYPE_CANVAS) + this.shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_COLOR); + + 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; + }, + + /** + * 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); + }, + + _setWidth: function(width){ + cc.Node.prototype._setWidth.call(this, width); + this._renderCmd._updateSquareVerticesWidth(width); + }, + + _setHeight: function(height){ + cc.Node.prototype._setHeight.call(this, height); + this._renderCmd._updateSquareVerticesHeight(height); + }, + + setContentSize: function(size, height){ + cc.Layer.prototype.setContentSize.call(this, size, height); + this._renderCmd._updateSquareVertices(size, height); + }, + + _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.Vec2} [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.Vec2} [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.Vec2|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.Vec2} 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.Vec2} + */ + 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.Vec2|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)); +}; \ No newline at end of file diff --git a/cocos2d/core/layers/CCLayerCanvasRenderCmd.js b/cocos2d/core/layers/CCLayerCanvasRenderCmd.js new file mode 100644 index 00000000000..ddb802e4892 --- /dev/null +++ b/cocos2d/core/layers/CCLayerCanvasRenderCmd.js @@ -0,0 +1,474 @@ +/**************************************************************************** + 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){ + cc.Node.CanvasRenderCmd.call(this, renderable); + this._isBaked = false; + this._bakeSprite = null; + 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._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.transform = function (parentCmd, recursive) { + var wt = this._worldTransform; + var a = wt.a, b = wt.b, c = wt.c, d = wt.d, tx = wt.tx, ty = wt.ty; + cc.Node.CanvasRenderCmd.prototype.transform.call(this, 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.visit = function(parentCmd){ + if(!this._isBaked){ + cc.Node.CanvasRenderCmd.prototype.visit.call(this, parentCmd); + return; + } + + var node = this._node, children = node._children; + var len = children.length; + // quick return if not visible + if (!node._visible || len === 0) + return; + + this._syncStatus(parentCmd); + cc.renderer.pushRenderCommand(this); + this._cacheDirty = true; + if(this._updateCache === 0) + this._updateCache = 2; + + //the bakeSprite is drawing + this._bakeSprite.visit(this); + this._dirtyFlag = 0; + }; + + 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){ + cc.Layer.CanvasRenderCmd.call(this, 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 * scaleX, -locHeight * scaleY); + + 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 len = children.length, i; + + //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._renderCmd.visit(this); + else + break; + } + cc.renderer.pushRenderCommand(this); + for (; i < len; i++) { + children[i]._renderCmd.visit(this); + } + } else + cc.renderer.pushRenderCommand(this); + cc.renderer._renderingToCacheCanvas(bakeContext, this.__instanceId); + locBakeSprite.transform(); + this._updateCache--; + } + this._cacheDirty = false; + } + }; + + proto.visit = function(parentCmd){ + if(!this._isBaked){ + cc.Node.CanvasRenderCmd.prototype.visit.call(this); + return; + } + + var node = this._node; + // quick return if not visible + if (!node._visible) + return; + + this._syncStatus(parentCmd); + + cc.renderer.pushRenderCommand(this._bakeRenderCmd); + + //the bakeSprite is drawing + this._bakeSprite._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + this._bakeSprite.visit(this); + this._dirtyFlag = 0; + }; + + 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; + }; +})(); + +(function () { + cc.LayerGradient.RenderCmd = { + updateStatus: function () { + var flags = cc.Node._dirtyFlags, locFlag = this._dirtyFlag; + var colorDirty = locFlag & flags.colorDirty, + opacityDirty = locFlag & flags.opacityDirty; + if(colorDirty) + this._updateDisplayColor(); + + if(opacityDirty) + this._updateDisplayOpacity(); + + if(colorDirty || opacityDirty || (locFlag & flags.gradientDirty)) + this._updateColor(); + + if(locFlag & flags.transformDirty) + //update the transform + this.transform(this.getParentRenderCmd(), true); + + this._dirtyFlag = 0; + } + }; +})(); + +/** + * 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); + cc.js.mixin(proto, cc.LayerGradient.RenderCmd); + 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*scaleX, this._startPoint.y*scaleY, this._endPoint.x*scaleX, this._endPoint.y*scaleY); + + 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 * scaleX, -locHeight * scaleY); + cc.g_NumberOfDraws++; + }; + + proto._syncStatus = function (parentCmd) { + 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; + + var colorDirty = locFlag & flags.colorDirty, + opacityDirty = locFlag & flags.opacityDirty; + + this._dirtyFlag = locFlag; + + if (colorDirty) + this._syncDisplayColor(); + + if (opacityDirty) + this._syncDisplayOpacity(); + + if (locFlag & flags.transformDirty) { + //update the transform + this.transform(parentCmd); + } + + if (colorDirty || opacityDirty || (locFlag & flags.gradientDirty)){ + this._updateColor(); + } + }; + + proto._updateColor = function(){ + var node = this._node; + var contentSize = node._contentSize; + var tWidth = contentSize.width * 0.5, tHeight = contentSize.height * 0.5; + this._dirtyFlag = this._dirtyFlag & cc.Node._dirtyFlags.gradientDirty ^ this._dirtyFlag; + + //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) + ")"); + } + } + }; +})(); \ No newline at end of file diff --git a/cocos2d/core/layers/CCLayerWebGLRenderCmd.js b/cocos2d/core/layers/CCLayerWebGLRenderCmd.js new file mode 100644 index 00000000000..02bc6f0b51b --- /dev/null +++ b/cocos2d/core/layers/CCLayerWebGLRenderCmd.js @@ -0,0 +1,311 @@ +/**************************************************************************** + 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){ + cc.Node.WebGLRenderCmd.call(this, renderable); + }; + + var proto = cc.Layer.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + proto.constructor = cc.Layer.WebGLRenderCmd; + + proto.bake = function(){}; + + proto.unbake = function(){}; + + proto._bakeForAddChild = function(){}; +})(); + +/** + * cc.LayerColor's rendering objects of WebGL + */ +(function(){ + cc.LayerColor.WebGLRenderCmd = function(renderable){ + cc.Layer.WebGLRenderCmd.call(this, renderable); + this._needDraw = true; + + // + var _t = this; + _t._squareVerticesAB = new ArrayBuffer(32); + _t._squareColorsAB = new ArrayBuffer(16); + + var locSquareVerticesAB = _t._squareVerticesAB, locSquareColorsAB = _t._squareColorsAB; + var locVertex2FLen = cc.Vertex2F.BYTES_PER_ELEMENT, locColorLen = cc.WebGLColor.BYTES_PER_ELEMENT; + _t._squareVertices = [new cc.Vertex2F(0, 0, locSquareVerticesAB, 0), + new cc.Vertex2F(0, 0, locSquareVerticesAB, locVertex2FLen), + new cc.Vertex2F(0, 0, locSquareVerticesAB, locVertex2FLen * 2), + new cc.Vertex2F(0, 0, locSquareVerticesAB, locVertex2FLen * 3)]; + _t._squareColors = [new cc.WebGLColor(0, 0, 0, 255, locSquareColorsAB, 0), + new cc.WebGLColor(0, 0, 0, 255, locSquareColorsAB, locColorLen), + new cc.WebGLColor(0, 0, 0, 255, locSquareColorsAB, locColorLen * 2), + new cc.WebGLColor(0, 0, 0, 255, locSquareColorsAB, locColorLen * 3)]; + _t._verticesFloat32Buffer = cc._renderContext.createBuffer(); + _t._colorsUint8Buffer = cc._renderContext.createBuffer(); + }; + var proto = cc.LayerColor.WebGLRenderCmd.prototype = Object.create(cc.Layer.WebGLRenderCmd.prototype); + proto.constructor = cc.LayerColor.WebGLRenderCmd; + + proto.rendering = function (ctx) { + var context = ctx || cc._renderContext; + var node = this._node; + + this._shaderProgram.use(); + this._shaderProgram._setUniformForMVPMatrixWithMat4(this._stackMatrix); + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSITION | cc.VERTEX_ATTRIB_FLAG_COLOR); + cc.glBlendFunc(node._blendFunc.src, node._blendFunc.dst); + + // + // Attributes + // + context.bindBuffer(context.ARRAY_BUFFER, this._verticesFloat32Buffer); + context.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, context.FLOAT, false, 0, 0); + + context.bindBuffer(context.ARRAY_BUFFER, this._colorsUint8Buffer); + context.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, context.UNSIGNED_BYTE, true, 0, 0); + + context.drawArrays(context.TRIANGLE_STRIP, 0, this._squareVertices.length); + }; + + proto._updateSquareVertices = function(size, height){ + var locSquareVertices = this._squareVertices; + if (height === undefined) { + locSquareVertices[1].x = size.width; + locSquareVertices[2].y = size.height; + locSquareVertices[3].x = size.width; + locSquareVertices[3].y = size.height; + } else { + locSquareVertices[1].x = size; + locSquareVertices[2].y = height; + locSquareVertices[3].x = size; + locSquareVertices[3].y = height; + } + this._bindLayerVerticesBufferData(); + }; + + proto._updateSquareVerticesWidth = function(width){ + var locSquareVertices = this._squareVertices; + locSquareVertices[1].x = width; + locSquareVertices[3].x = width; + this._bindLayerVerticesBufferData(); + }; + + proto._updateSquareVerticesHeight = function(height){ + var locSquareVertices = this._squareVertices; + locSquareVertices[2].y = height; + locSquareVertices[3].y = height; + this._bindLayerVerticesBufferData(); + }; + + proto._updateColor = function(){ + var locDisplayedColor = this._displayedColor, locDisplayedOpacity = this._displayedOpacity, + locSquareColors = this._squareColors; + for (var i = 0; i < 4; i++) { + locSquareColors[i].r = locDisplayedColor.r; + locSquareColors[i].g = locDisplayedColor.g; + locSquareColors[i].b = locDisplayedColor.b; + locSquareColors[i].a = locDisplayedOpacity; + } + this._bindLayerColorsBufferData(); + }; + + proto._bindLayerVerticesBufferData = function(){ + var glContext = cc._renderContext; + glContext.bindBuffer(glContext.ARRAY_BUFFER, this._verticesFloat32Buffer); + glContext.bufferData(glContext.ARRAY_BUFFER, this._squareVerticesAB, glContext.STATIC_DRAW); + }; + + proto._bindLayerColorsBufferData = function(){ + var glContext = cc._renderContext; + glContext.bindBuffer(glContext.ARRAY_BUFFER, this._colorsUint8Buffer); + glContext.bufferData(glContext.ARRAY_BUFFER, this._squareColorsAB, glContext.STATIC_DRAW); + }; + + proto.updateBlendFunc = function(blendFunc){}; +})(); + +/** + * cc.LayerGradient's rendering objects of WebGL + */ +(function(){ + 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); + cc.js.mixin(proto, cc.LayerGradient.RenderCmd); + proto.constructor = cc.LayerGradient.WebGLRenderCmd; + + proto._syncStatus = function (parentCmd) { + 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; + + var colorDirty = locFlag & flags.colorDirty, + opacityDirty = locFlag & flags.opacityDirty; + + this._dirtyFlag = locFlag; + + if (colorDirty) + this._syncDisplayColor(); + + if (opacityDirty) + this._syncDisplayOpacity(); + + //if (locFlag & flags.transformDirty) { + //update the transform + this.transform(parentCmd); + //} + + if (colorDirty || opacityDirty || (locFlag & flags.gradientDirty)){ + this._updateColor(); + } + }; + + proto._updateColor = function(){ + this._dirtyFlag = this._dirtyFlag & cc.Node._dirtyFlags.gradientDirty ^ this._dirtyFlag; + var node = this._node, stops = node._colorStops; + if(!stops || stops.length < 2) + return; + + this._clippingRectDirty = true; + var stopsLen = stops.length, verticesLen = stopsLen * 2, i, contentSize = node._contentSize; + this._squareVerticesAB = new ArrayBuffer(verticesLen * 8); + this._squareColorsAB = new ArrayBuffer(verticesLen * 4); + var locVertices = this._squareVertices, locColors = this._squareColors; + locVertices.length = 0; + locColors.length = 0; + + var locSquareVerticesAB = this._squareVerticesAB, locSquareColorsAB = this._squareColorsAB; + var locVertex2FLen = cc.Vertex2F.BYTES_PER_ELEMENT, locColorLen = cc.WebGLColor.BYTES_PER_ELEMENT; + for(i = 0; i < verticesLen; i++){ + locVertices.push(new cc.Vertex2F(0, 0, locSquareVerticesAB, locVertex2FLen * i)); + locColors.push(new cc.WebGLColor(0, 0, 0, 255, locSquareColorsAB, locColorLen * i)) + } + + //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); + 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); + locVertices[i * 2].x = p0.x; + locVertices[i * 2].y = p0.y; + var p1 = cc.pointApplyAffineTransform(contentSize.width - locAnchor.x, y - locAnchor.y, transMat); + locVertices[i * 2 + 1].x = p1.x; + locVertices[i * 2 + 1].y = p1.y; + } + + //init color + var opacityf = this._displayedOpacity / 255.0; //, displayColor = this._displayedColor; + for(i = 0; i < stopsLen; i++){ + var stopColor = stops[i].color, locSquareColor0 = locColors[i * 2], locSquareColor1 = locColors[i * 2 + 1]; + locSquareColor0.r = stopColor.r; + locSquareColor0.g = stopColor.g; + locSquareColor0.b = stopColor.b; + locSquareColor0.a = stopColor.a * opacityf; + + locSquareColor1.r = stopColor.r; + locSquareColor1.g = stopColor.g; + locSquareColor1.b = stopColor.b; + locSquareColor1.a = stopColor.a * opacityf; + } + this._bindLayerVerticesBufferData(); + this._bindLayerColorsBufferData(); + }; + + proto.rendering = function (ctx) { + var context = ctx || cc._renderContext, node = this._node; + + //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); + + //draw gradient layer + this._shaderProgram.use(); + this._shaderProgram._setUniformForMVPMatrixWithMat4(this._stackMatrix); + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSITION | cc.VERTEX_ATTRIB_FLAG_COLOR); + cc.glBlendFunc(node._blendFunc.src, node._blendFunc.dst); + // + // Attributes + // + context.bindBuffer(context.ARRAY_BUFFER, this._verticesFloat32Buffer); + context.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, context.FLOAT, false, 0, 0); + context.bindBuffer(context.ARRAY_BUFFER, this._colorsUint8Buffer); + context.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, context.UNSIGNED_BYTE, true, 0, 0); + context.drawArrays(context.TRIANGLE_STRIP, 0, this._squareVertices.length); + + 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; + }; +})(); \ No newline at end of file diff --git a/cocos2d/core/platform/CCAssetLibrary.js b/cocos2d/core/platform/CCAssetLibrary.js new file mode 100644 index 00000000000..c6e4e1b5b8e --- /dev/null +++ b/cocos2d/core/platform/CCAssetLibrary.js @@ -0,0 +1,519 @@ +var JS = require('./js'); +var Asset = require('../assets/CCAsset'); +var callInNextTick = require('./utils').callInNextTick; +var LoadManager = require('./load-manager'); +var CallbacksInvoker = require('./callbacks-invoker'); + +/** + * The asset library which managing loading/unloading assets in project. + * + * @class AssetLibrary + * @static + */ + +// configs + +var _libraryBase = ''; +var _uuidToRawAssets = {}; + +// variables + +// the loading uuid's callbacks +var _uuidToCallbacks = new CallbacksInvoker(); + +// temp deserialize info +var _tdInfo = new cc.deserialize.Details(); + +// create a loading context which reserves all relevant parameters +function LoadingHandle (readMainCache, writeMainCache, recordAssets, deserializeInfo) { + //this.readMainCache = readMainCache; + //this.writeMainCache = writeMainCache; + + // FORCE ignore global cache in fireball lite + this.readMainCache = false; + this.writeMainCache = false; + + // 缓存åŒä¸€ä¸ªä»»åŠ¡ä¸­å·²ç»åŠ è½½å¥½çš„èµ„æº + var needIndieCache = !(this.readMainCache && this.writeMainCache); + this.taskIndieCache = needIndieCache ? {} : null; + + // ä¿å­˜æ­£åœ¨ç­‰å¾…ä¾èµ–项加载完毕的资æºï¼Œé˜²æ­¢å¾ªçŽ¯ä¾èµ– + // (其它缓存包å«çš„是已ç»åŠ è½½å®Œæ¯•çš„资æºï¼Œå¦‚果已ç»ç¼“存会立å³ç»“æŸå¹¶å›žè°ƒï¼Œè€Œè¿™ä¸ªç¼“存存的是还在加载中的资æºï¼Œä¸ä¼šå½±å“加载也ä¸ä¼šè§¦å‘回调) + this.pendingCache = {}; + + // 需è¦è®©åœºæ™¯ preload çš„ assetï¼ˆæ‰€æœ‰åŒ…å« raw file åŽç¼€åçš„ asset 并且ä¸å« rawType 属性的 asset) + this.assetsNeedPostLoad = recordAssets ? [] : null; + // 需è¦è®©åœºæ™¯ preload çš„ url + this.urlsNeedPreload = {}; + + // å¯ä»¥æ供一个ååºåˆ—化å¥æŸ„,用æ¥åœ¨æ‰§è¡Œååºåˆ—化任务时,åšä¸€äº›ç‰¹æ®Šå¤„ç† + this.deserializeInfo = deserializeInfo; +} +LoadingHandle.prototype.readCache = function (uuid) { + if (this.readMainCache && this.writeMainCache) { + return AssetLibrary._uuidToAsset[uuid]; + } + else { + if (this.readMainCache) { + // writeMainCache == false + return AssetLibrary._uuidToAsset[uuid] || this.taskIndieCache[uuid]; + } + else { + return this.taskIndieCache[uuid]; + } + } +}; +LoadingHandle.prototype.writeCache = function (uuid, asset, hasRawType) { + if (this.writeMainCache) { + AssetLibrary._uuidToAsset[uuid] = asset; + } + if (this.taskIndieCache) { + this.taskIndieCache[uuid] = asset; + } + if (this.assetsNeedPostLoad && asset._rawFiles && !hasRawType) { + this.assetsNeedPostLoad.push(asset); + } +}; + +// publics + +var AssetLibrary = { + /** + * @callback loadCallback + * @param {String} error - null or the error info + * @param {Asset} data - the loaded asset or null + */ + + /** + * @method loadAsset + * @param {String} uuid + * @param {loadCallback} callback - the callback function once load finished + * @param {Object} options + * @param {Boolean} options.readMainCache - Default is true. If false, the asset and all its depends assets will reload and create new instances from library. + * @param {Boolean} options.writeMainCache - Default is true. If true, the result will cache to AssetLibrary, and MUST be unload by user manually. + * @param {Asset} options.existingAsset - load to existing asset, this argument is only available in editor + * @param {deserialize.Details} options.deserializeInfo - specified a DeserializeInfo object if you want + * @private + */ + loadAsset: function (uuid, callback, options) { + var readMainCache = typeof (options && options.readMainCache) !== 'undefined' ? readMainCache : true; + var writeMainCache = typeof (options && options.writeMainCache) !== 'undefined' ? writeMainCache : true; + var existingAsset, deserializeInfo; + if (options) { + existingAsset = options.existingAsset; + deserializeInfo = options.deserializeInfo; + } + + var handle = new LoadingHandle(readMainCache, writeMainCache, null, deserializeInfo); + this._loadAssetByUuid(uuid, callback, handle, existingAsset); + }, + + _LoadingHandle: LoadingHandle, + + getImportedDir: function (uuid) { + return _libraryBase + uuid.slice(0, 2)/* + cc.path.sep + uuid*/; + }, + + _queryAssetInfoInEditor: function (uuid, callback) { + if (CC_EDITOR) { + Editor.sendRequestToCore( 'scene:query-asset-info-by-uuid', uuid, function (info) { + if (info) { + Editor.onRawAssetUsed(info.url, uuid); + var ctor = Editor.assets[info.type]; + if (ctor) { + var isRawAsset = !cc.isChildClassOf(ctor, Asset); + callback(null, info.url, isRawAsset, ctor); + } + else { + callback(new Error('Can not find asset type ' + info.type)); + } + } + else { + callback(new Error('Can not get asset url by uuid ' + uuid)); + } + }); + } + }, + + _getAssetInfoInRuntime: function (uuid) { + var info = _uuidToRawAssets[uuid]; + if (info) { + return { + url: cc.url._rawAssets + info.url, + raw: info.raw, + }; + } + else { + var url = this.getImportedDir(uuid) + cc.path.sep + uuid + '.json'; + return { + url: url, + raw: false, + }; + } + }, + + /** + * @method queryAssetInfo + * @param {String} uuid + * @param {Function} callback + * @param {Error} callback.error + * @param {String} callback.url - the url of raw asset or imported asset + * @param {Boolean} callback.raw - indicates whether the asset is raw asset + * @param {Function} callback.ctorInEditor - the actual type of asset, used in editor only + */ + queryAssetInfo: function (uuid, callback) { + if (CC_EDITOR && !CC_TEST) { + this._queryAssetInfoInEditor(uuid, callback); + } + else { + var info = this._getAssetInfoInRuntime(uuid); + callback(null, info.url, info.raw); + } + }, + + // parse uuid out of url + parseUuidInEditor: function (url) { + if (CC_EDITOR) { + var uuid = ""; + var isImported = url.startsWith(_libraryBase); + if (isImported) { + var dir = cc.path.dirname(url); + var dirBasename = cc.path.basename(dir); + + var isAssetUrl = dirBasename.length === 2; + if (isAssetUrl) { + uuid = cc.path.basename(url); + var index = uuid.indexOf('.'); + if (index !== -1) { + uuid = uuid.slice(0, index); + } + } + else { + // raw file url + uuid = dirBasename; + } + } + // If url is not in the library, just return "" + return uuid; + } + }, + + /** + * !#zh uuid加载æµç¨‹ï¼š + * 1. 查找_uuidToAsset,如果已ç»åŠ è½½è¿‡ï¼Œç›´æŽ¥è¿”回 + * 2. 查找_uuidToCallbacks,如果已ç»åœ¨åŠ è½½ï¼Œåˆ™æ³¨å†Œå›žè°ƒï¼Œç›´æŽ¥è¿”回 + * 3. 如果没有url,则将uuid直接作为路径 + * 4. 递归加载AssetåŠå…¶å¼•ç”¨åˆ°çš„其它Asset + * + * @method _loadAssetByUuid + * @param {String} uuid + * @param {loadCallback} callback - the callback to receive the asset, can be null + * @param {LoadingHandle} handle - the loading context which reserves all relevant parameters + * @param {Asset} [existingAsset] - load to existing asset, this argument is only available in editor + * @private + */ + _loadAssetByUuid: function (uuid, callback, handle, existingAsset) { + if (typeof uuid !== 'string') { + return callInNextTick(callback, new Error('[AssetLibrary] uuid must be string'), null); + } + // step 1 + if ( !existingAsset ) { + var asset = handle.readCache(uuid); + if (asset) { + return callInNextTick(callback, null, asset); + } + } + + // step 2 + // 如果必须é‡æ–°åŠ è½½ï¼Œåˆ™ä¸èƒ½åˆå¹¶åˆ°åˆ° _uuidToCallbacks,å¦åˆ™çŽ°æœ‰çš„加载æˆåŠŸåŽä¼šåŒæ—¶è§¦å‘回调, + // 导致æå‰è¿”回的之å‰çš„资æºã€‚ + var canShareLoadingTask = handle.readMainCache && !existingAsset; + if ( canShareLoadingTask && !_uuidToCallbacks.add(uuid, callback) ) { + // already loading + return; + } + + // 防止循环加载 + var loading = handle.pendingCache[uuid]; + if (loading) { + // already loading + return callInNextTick(callback, null, loading); + } + + //if (CC_EDITOR && !_libraryBase) { + // callInNextTick(callback, new Error('Cannot load ' + uuid + ' in editor because AssetLibrary not yet initialized!'), null); + // return; + //} + function onload (error, json, url) { + function onDeserializedWithDepends (err, asset, hasRawType) { + if (asset) { + asset._uuid = uuid; + handle.writeCache(uuid, asset, hasRawType); + } + if ( canShareLoadingTask ) { + _uuidToCallbacks.invokeAndRemove(uuid, err, asset); + } + else if (callback) { + callback(err, asset); + } + } + if (json) { + AssetLibrary._deserializeWithDepends(json, url, uuid, onDeserializedWithDepends, handle, existingAsset); + } + else { + onDeserializedWithDepends(error, null); + } + } + + if (CC_EDITOR && !CC_TEST) { + this._queryAssetInfoInEditor(uuid, function (err, url, isRawAsset) { + if (err) { + callback(err); + } + else { + var shouldLoadByEngine = !isRawAsset; + if (!shouldLoadByEngine) { + return callback(new Error('Should not load raw file in AssetLibrary, uuid: ' + uuid)); + } + LoadManager.loadByLoader(cc.loader.loadJson.bind(cc.loader), url, function (error, json) { + onload(error, json, url); + }); + } + }); + } + else { + var info = this._getAssetInfoInRuntime(uuid); + if (info.raw) { + return callback(new Error('Should not load raw file in AssetLibrary, uuid: ' + uuid)); + } + LoadManager.loadByLoader(cc.loader.loadJson.bind(cc.loader), info.url, function (error, json) { + onload(error, json, info.url); + }); + } + }, + + /** + * @method loadJson + * @param {String|Object} json + * @param {loadCallback} callback + * @param {Boolean} [dontCache=false] - If false, the result will cache to AssetLibrary, and MUST be unload by user manually. + * @param {Boolean} [recordAssets=false] - 是å¦ç»Ÿè®¡æ–°åŠ è½½çš„需è¦è®©åœºæ™¯ preload çš„ assetï¼ˆæ‰€æœ‰åŒ…å« raw file åŽç¼€åçš„ asset 并且ä¸å« rawType 属性的 asset) + * @return {LoadingHandle} + * @private + */ + loadJson: function (json, callback, dontCache, recordAssets) { + var handle = new LoadingHandle(!dontCache, !dontCache, recordAssets); + var thisTick = true; + this._deserializeWithDepends(json, '', '', function (p1, p2) { + if (thisTick) { + callInNextTick(callback, p1, p2); + } + else { + callback(p1, p2); + } + }, handle); + thisTick = false; + return handle; + }, + + _loadDepends: function (asset, _tdInfo, url, handle, callback) { + // load depends + var pendingCount = _tdInfo.uuidList.length; + + // load raw + var rawProp = _tdInfo.rawProp; // _tdInfoä¸èƒ½ç”¨åœ¨å›žè°ƒé‡Œï¼ + if (rawProp) { + // load depends raw objects + var attrs = cc.Class.attr(asset.constructor, _tdInfo.rawProp); + var rawType = attrs.rawType; + ++pendingCount; + LoadManager.load(url, rawType, asset._rawext, function onRawObjLoaded (error, raw) { + if (error) { + cc.error('[AssetLibrary] Failed to load %s of %s. %s', rawType, url, error); + } + asset[rawProp] = raw; + --pendingCount; + if (pendingCount === 0) { + callback(); + } + }); + } + + if (pendingCount === 0) { + callback(); + return; + } + + /* + 如果ä¾èµ–的所有资æºéƒ½è¦é‡æ–°ä¸‹è½½ï¼Œæ‰¹é‡æ“作时将会导致åŒæ—¶æ‰§è¡Œå¤šæ¬¡é‡å¤ä¸‹è½½ã€‚优化方法是增加一全局事件队列, + 队列ä¿å­˜æ¯ä¸ªä»»åŠ¡çš„注册,å¯åŠ¨ï¼Œç»“æŸäº‹ä»¶ï¼Œä»»åŠ¡ä»Žæ³¨å†Œåˆ°å¯åŠ¨è¦å»¶è¿Ÿå‡ å¸§ï¼Œæ¯ä¸ªä»»åŠ¡éƒ½å­˜æœ‰çˆ¶ä»»åŠ¡ã€‚ + 这样通过队列的事件åºåˆ—就能åšåˆ°åˆå¹¶æ‰¹é‡ä»»åŠ¡ã€‚ + 如果ä¾èµ–的资æºä¸é‡æ–°ä¸‹è½½ä¹Ÿè¡Œï¼Œä½†è¦åˆ¤æ–­æ˜¯å¦åˆšå¥½åœ¨ä¸‹è½½è¿‡ç¨‹ä¸­ï¼Œå¦‚果是的è¯å¿…须等待下载完æˆæ‰èƒ½ç»“æŸæœ¬èµ„æºçš„加载, + å¦åˆ™å¤–部获å–到的ä¾èµ–资æºå°±ä¼šæ˜¯æ—§çš„。 + */ + + // load depends assets + for (var i = 0, len = _tdInfo.uuidList.length; i < len; i++) { + var dependsUuid = _tdInfo.uuidList[i]; + (function (dependsUuid, obj, prop) { + AssetLibrary.queryAssetInfo(dependsUuid, function (err, dependsUrl, isRawAsset) { + if (err) { + cc.error('[AssetLibrary] Failed to load "%s", %s', dependsUuid, err); + } + else if (isRawAsset) { + // update url + obj[prop] = dependsUrl; + handle.urlsNeedPreload[dependsUrl] = true; + } + if (err || isRawAsset) { + --pendingCount; + if (callback && pendingCount === 0) { + callback(); + callback = null; + } + return; + } + + var onDependsAssetLoaded = function (error, dependsAsset, hasRawType) { + if (CC_EDITOR && error) { + if (Editor.AssetDB && Editor.AssetDB.isValidUuid(dependsUuid)) { + cc.error('[AssetLibrary] Failed to load "%s", %s', dependsUuid, error); + } + } + //else { + // dependsAsset._uuid = dependsUuid; + //} + // update reference + obj[prop] = dependsAsset; + // + --pendingCount; + if (callback && pendingCount === 0) { + callback(); + callback = null; + } + }; + AssetLibrary._loadAssetByUuid(dependsUuid, onDependsAssetLoaded, handle); + }); + })( dependsUuid, _tdInfo.uuidObjList[i], _tdInfo.uuidPropList[i] ); + } + + // AssetLibrary._loadAssetByUuid 的回调有å¯èƒ½åœ¨å½“帧也å¯èƒ½å»¶åŽæ‰§è¡Œï¼Œæ‰€ä»¥è¿™é‡Œè¦åˆ¤æ–­ callback æ¥é˜²æ­¢å¤šæ¬¡è°ƒç”¨ + if (callback && pendingCount === 0) { + callback(); + callback = null; + } + }, + + /** + * @method _deserializeWithDepends + * @param {String|Object} json + * @param {String} url + * @param {String} uuid + * @param {loadCallback} callback + * @param {Object} handle - the loading context which reserves all relevant parameters + * @param {Asset} [existingAsset] - existing asset to reload + * @private + */ + _deserializeWithDepends: function (json, url, uuid, callback, handle, existingAsset) { + // deserialize asset + //var isScene = typeof Scene !== 'undefined' && json && json[0] && json[0].__type__ === JS._getClassId(Scene); + //var classFinder = isScene ? cc._MissingScript.safeFindClass : function (id) { + var classFinder = function (id) { + var cls = JS._getClassById(id); + if (cls) { + return cls; + } + cc.warn('Can not get class "%s"', id); + return Object; + }; + + var tdInfo = handle.deserializeInfo || _tdInfo; + + var asset = cc.deserialize(json, tdInfo, { + classFinder: classFinder, + target: existingAsset + }); + + var hasRawType = !!tdInfo.rawProp; + + if (uuid) { + handle.pendingCache[uuid] = asset; + } + + this._loadDepends(asset, tdInfo, url, handle, function () { + if (uuid) { + delete handle.pendingCache[uuid]; + } + callback(null, asset, hasRawType); + }); + + // tdInfo 是用æ¥é‡ç”¨çš„临时对象,æ¯æ¬¡ä½¿ç”¨åŽéƒ½è¦é‡è®¾ï¼Œè¿™æ ·æ‰å¯¹ GC å‹å¥½ã€‚ + tdInfo.reset(); + }, + + /** + * Get the exists asset by uuid. + * + * @method getAssetByUuid + * @param {String} uuid + * @return {Asset} - the existing asset, if not loaded, just returns null. + * @private + */ + getAssetByUuid: function (uuid) { + return AssetLibrary._uuidToAsset[uuid] || null; + }, + + /** + * init the asset library + * + * @method init + * @param {String} libraryPath - 能接收的任æ„类型的路径,通常在编辑器里使用ç»å¯¹çš„,在网页里使用相对的。 + * @param {String} rawAssetsBase - base of raw asset's urls (only used in runtime) + * @param {Object} rawAssets - uuid to raw asset's urls (only used in runtime) + */ + init: function (libraryPath, rawAssetsBase, rawAssets) { + if (CC_EDITOR && _libraryBase && !CC_TEST) { + cc.error('AssetLibrary has already been initialized!'); + return; + } + + // 这里将路径转 url,ä¸ä½¿ç”¨è·¯å¾„的原因是有的 runtime ä¸èƒ½è§£æž "\" 符å·ã€‚ + // ä¸ä½¿ç”¨ url.format 的原因是 windows ä¸æ”¯æŒ file:// å’Œ /// 开头的å议,所以åªèƒ½ç”¨ replace æ“ä½œç›´æŽ¥æŠŠè·¯å¾„è½¬æˆ URL。 + libraryPath = libraryPath.replace(/\\/g, '/'); + + _libraryBase = cc.path._setEndWithSep(libraryPath, '/'); + cc.url._rawAssets = cc.path._setEndWithSep(rawAssetsBase || '', '/'); + _uuidToRawAssets = rawAssets || {}; + } +}; + +// unload asset if it is destoryed + +/** + * !#en Caches uuid to all loaded assets in scenes. + * + * !#zh 这里ä¿å­˜æ‰€æœ‰å·²ç»åŠ è½½çš„场景资æºï¼Œé˜²æ­¢åŒä¸€ä¸ªèµ„æºåœ¨å†…存中加载出多份拷è´ã€‚ + * + * 这里用ä¸äº†WeakMap,在æµè§ˆå™¨ä¸­æ‰€æœ‰åŠ è½½è¿‡çš„资æºéƒ½åªèƒ½æ‰‹å·¥è°ƒç”¨ unloadAsset 释放。 + * + * å‚考: + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap + * https://github.com/TooTallNate/node-weak + * + * @property _uuidToAsset + * @type {object} + * @private + */ +AssetLibrary._uuidToAsset = {}; + +//æš‚æ—¶å±è”½ï¼Œå› ä¸ºç›®å‰æ²¡æœ‰ç¼“存任何asset +//if (CC_DEV && Asset.prototype._onPreDestroy) { +// cc.error('_onPreDestroy of Asset has already defined'); +//} +//Asset.prototype._onPreDestroy = function () { +// if (AssetLibrary._uuidToAsset[this._uuid] === this) { +// AssetLibrary.unloadAsset(this); +// } +//}; + +cc.AssetLibrary = AssetLibrary; diff --git a/cocos2d/core/platform/CCClass.js b/cocos2d/core/platform/CCClass.js new file mode 100644 index 00000000000..3d24a57cf96 --- /dev/null +++ b/cocos2d/core/platform/CCClass.js @@ -0,0 +1,1036 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + 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. + ****************************************************************************/ + +var JS = require('./js'); +var Enum = require('../value-types/CCEnum'); +var Utils = require('./utils'); +var _isPlainEmptyObj_DEV = Utils.isPlainEmptyObj_DEV; +var _cloneable_DEV = Utils.cloneable_DEV; +var Attr = require('./attribute'); +var getTypeChecker = Attr.getTypeChecker; +var preprocessAttrs = require('./preprocess-attrs'); + +var BUILTIN_ENTRIES = ['name', 'extends', 'ctor', 'properties', 'statics', 'editor']; + +var TYPO_TO_CORRECT = (CC_EDITOR || CC_TEST) && { + extend: 'extends', + property: 'properties', + static: 'statics', + constructor: 'ctor' +}; + +var INVALID_STATICS = (CC_EDITOR || CC_TEST) && ['name', '__ctors__', '__props__', 'arguments', 'call', 'apply', 'caller', + 'length', 'prototype']; + +///** +// * both getter and prop must register the name into __props__ array +// * @param {String} name - prop name +// */ +var _appendProp = function (cls, name/*, isGetter*/) { + if (CC_DEV) { + //var JsVarReg = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/; + //if (!JsVarReg.test(name)) { + // cc.error('The property name "' + name + '" is not compliant with JavaScript naming standards'); + // return; + //} + if (name.indexOf('.') !== -1) { + cc.error('Disallow to use "." in property name'); + return; + } + } + + var index = cls.__props__.indexOf(name); + if (index < 0) { + cls.__props__.push(name); + } + // 这里ä¸è¿›è¡ŒæŠ¥é”™ï¼Œå› ä¸ºé‡å†™ prop å¯ä»¥æ˜¯ä¸€ä¸ªåˆæ³•çš„行为,å¯ä»¥ç”¨äºŽè®¾ç½®æ–°çš„默认值。 + //else { + // cc.error(cc.getClassName(cls) + '.' + name + ' is already defined!'); + //} +}; + +var _metaClass = { + + prop: function (name, defaultValue, attribute) { + 'use strict'; + if (CC_DEV) { + // check default object value + if (typeof defaultValue === 'object' && defaultValue) { + if (Array.isArray(defaultValue)) { + // check array empty + if (defaultValue.length > 0) { + cc.error('Default array must be empty, set default value of %s.%s to [], ' + + 'and initialize in "onLoad" or "ctor" please. (just like "this.%s = [...];")', + JS.getClassName(this), name, name); + return this; + } + } + else if (!_isPlainEmptyObj_DEV(defaultValue)) { + // check cloneable + if (!_cloneable_DEV(defaultValue)) { + cc.error('Do not set default value to non-empty object, ' + + 'unless the object defines its own "clone" function. Set default value of %s.%s to null or {}, ' + + 'and initialize in "onLoad" or "ctor" please. (just like "this.%s = {foo: bar};")', + JS.getClassName(this), name, name); + return this; + } + } + } + + // check base prototype to avoid name collision + for (var base = this.$super; base; base = base.$super) { + // 这个循环åªèƒ½æ£€æµ‹åˆ°æœ€ä¸Šé¢çš„FireClass的父类,如果å†ä¸Šè¿˜æœ‰çˆ¶ç±»ï¼Œå°†ä¸åšæ£€æµ‹ã€‚ + if (base.prototype.hasOwnProperty(name)) { + cc.error('Can not declare %s.%s, it is already defined in the prototype of %s', + JS.getClassName(this), name, JS.getClassName(base)); + return; + } + } + } + + // set default value + Attr.attr(this, name, { 'default': defaultValue }); + + _appendProp(this, name); + + // apply attributes + if (attribute) { + var onAfterProp = null; + var AttrArgStart = 2; + for (var i = AttrArgStart; i < arguments.length; i++) { + var attr = arguments[i]; + Attr.attr(this, name, attr); + // register callback + if (attr._onAfterProp) { + onAfterProp = onAfterProp || []; + onAfterProp.push(attr._onAfterProp); + } + } + // call callback + if (onAfterProp) { + for (var c = 0; c < onAfterProp.length; c++) { + onAfterProp[c](this, name); + } + } + } + return this; + }, + + get: function (name, getter, attribute) { + 'use strict'; + + if (CC_DEV) { + var d = Object.getOwnPropertyDescriptor(this.prototype, name); + if (d && d.get) { + cc.error('%s: the getter of "%s" is already defined!', JS.getClassName(this), name); + return this; + } + } + + if (attribute) { + var AttrArgStart = 2; + for (var i = AttrArgStart; i < arguments.length; i++) { + var attr = arguments[i]; + if (CC_DEV) { + if (attr._canUsedInGetter === false) { + cc.error('Can not apply the specified attribute to the getter of "%s.%s", attribute index: %s', + JS.getClassName(this), name, (i - AttrArgStart)); + continue; + } + } + + Attr.attr(this, name, attr); + + if (CC_DEV) { + // check attributes + if (attr.serializable === false || attr.editorOnly === true) { + cc.warn('No need to use "serializable: false" or "editorOnly: true" for the getter of %s.%s, ' + + 'every getter is actually non-serialized.', + JS.getClassName(this), name); + } + if (attr.hasOwnProperty('default')) { + cc.error('%s: Can not set default value of a getter!', JS.getClassName(this)); + return this; + } + } + } + } + + var forceSerializable = false; + if ( !forceSerializable ) { + Attr.attr(this, name, Attr.NonSerialized); + } + if (forceSerializable || (CC_EDITOR || CC_TEST)) { + // ä¸è®ºæ˜¯å¦ hide in inspector 都è¦æ·»åŠ åˆ° props,å¦åˆ™ asset watcher ä¸èƒ½æ­£å¸¸å·¥ä½œ + _appendProp(this, name/*, true*/); + } + + if (Object.getOwnPropertyDescriptor(this.prototype, name)) { + Object.defineProperty(this.prototype, name, { + get: getter + }); + } + else { + Object.defineProperty(this.prototype, name, { + get: getter, + configurable: true, + enumerable: true + }); + } + + if (CC_EDITOR || CC_TEST) { + Attr.attr(this, name, {hasGetter: true}); // 方便 editor åšåˆ¤æ–­ + } + return this; + }, + + set: function (name, setter) { + if (CC_DEV) { + var d = Object.getOwnPropertyDescriptor(this.prototype, name); + if (d && d.set) { + cc.error('%s: the setter of "%s" is already defined!', JS.getClassName(this), name); + return this; + } + } + + if (CC_EDITOR || CC_TEST) { + Object.defineProperty(this.prototype, name, { + //set: function setter_editorWrapper (value) { + // if (this._observing) { + // Object.getNotifier(this).notify({ + // type: 'update', + // name: name, + // oldValue: this[name] + // }); + // } + // setter.call(this, value); + //}, + set: setter, + configurable: true, + enumerable: true + }); + Attr.attr(this, name, { hasSetter: true }); // 方便 editor åšåˆ¤æ–­ + } + else { + if (Object.getOwnPropertyDescriptor(this.prototype, name)) { + Object.defineProperty(this.prototype, name, { + set: setter + }); + } + else { + Object.defineProperty(this.prototype, name, { + set: setter, + configurable: true, + enumerable: true + }); + } + } + + return this; + }, + + /** + * Create a new Class that inherits from this Class + * @param {Object} options + * @return {Function} + * @deprecated + */ + extend: function (options) { + options.extends = this; + return FireClass(options); + } +}; + +function getDefault (defaultVal) { + if (typeof defaultVal === 'function') { + if (CC_EDITOR) { + try { + return defaultVal(); + } + catch (e) { + cc._throw(e); + return undefined; + } + } + else { + return defaultVal(); + } + } + return defaultVal; +} + +function instantiateProps (instance, itsClass) { + var propList = itsClass.__props__; + for (var i = 0; i < propList.length; i++) { + var prop = propList[i]; + var attrs = Attr.attr(itsClass, prop); + if (attrs && attrs.hasOwnProperty('default')) { // getter does not have default, default maybe 0 + var def = attrs.default; + if (def) { + if (typeof def === 'object' && def) { + if (typeof def.clone === 'function') { + def = def.clone(); + } + else if (Array.isArray(def)) { + def = []; + } + else { + def = {}; + } + } + else if (typeof def === 'function') { + def = getDefault(def); + } + } + instance[prop] = def; + } + } +} + +/** + * Checks whether subclass is child of superclass or equals to superclass + * + * @method isChildClassOf + * @param {Function} subclass + * @param {Function} superclass + * @return {Boolean} + */ +cc.isChildClassOf = function (subclass, superclass) { + if (subclass && superclass) { + if (typeof subclass !== 'function') { + return false; + } + if (typeof superclass !== 'function') { + if (CC_DEV) { + cc.warn('[isChildClassOf] superclass should be function type, not', superclass); + } + return false; + } + // fireclass + for (; subclass && subclass.$super; subclass = subclass.$super) { + if (subclass === superclass) { + return true; + } + } + if (subclass === superclass) { + return true; + } + // js class + var dunderProto = Object.getPrototypeOf(subclass.prototype); + while (dunderProto) { + subclass = dunderProto.constructor; + if (subclass === superclass) { + return true; + } + dunderProto = Object.getPrototypeOf(subclass.prototype); + } + } + return false; +}; + +function doDefine (className, baseClass, constructor, options) { + var fireClass = _createCtor(constructor, baseClass, className, options); + + // occupy some non-inherited static members + for (var staticMember in _metaClass) { + Object.defineProperty(fireClass, staticMember, { + value: _metaClass[staticMember], + writable: true + }); + } + + if (baseClass) { + // inherit + JS.extend(fireClass, baseClass); // 这里会把父类的 __props__ å¤åˆ¶ç»™å­ç±» + fireClass.$super = baseClass; + + if (baseClass.__props__ && baseClass.__props__.length > 0) { + // copy __props__ + fireClass.__props__ = baseClass.__props__.slice(); + } + else { + fireClass.__props__ = []; + } + } + else { + fireClass.__props__ = []; + } + + JS.setClassName(className, fireClass); + + return fireClass; +} + +function define (className, baseClass, constructor, options) { + if (cc.isChildClassOf(baseClass, cc.Component)) { + var frame = cc._RFpeek(); + if (frame) { + if (CC_DEV && constructor) { + cc.warn('cc.Class: Should not define constructor for cc.Component.'); + } + if (frame.beh) { + cc.error('Each script can have at most one Component.'); + return; + } + var uuid = frame.uuid; + if (uuid) { + if (className && CC_EDITOR) { + cc.warn('Should not specify class name for Component which defines in project.'); + } + } + //else { + // builtin + //} + className = className || frame.script; + var cls = doDefine(className, baseClass, constructor, options); + if (uuid) { + JS._setClassId(uuid, cls); + if (CC_EDITOR) { + cc.Component._addMenuItem(cls, 'Scripts/' + className, -1); + cls.prototype.__scriptUuid = Editor.decompressUuid(uuid); + } + } + frame.beh = cls; + return cls; + } + } + // not project component + return doDefine(className, baseClass, constructor, options); +} + +function _checkCtor (ctor) { + if (CC_DEV) { + if (FireClass._isCCClass(ctor)) { + cc.error("Constructor can not be another CCClass"); + return; + } + if (typeof ctor !== 'function') { + cc.error("Constructor of CCClass must be function type"); + return; + } + if (ctor.length > 0) { + // fireball-x/dev#138: To make a unified FireClass serialization process, + // we don't allow parameters for constructor when creating instances of FireClass. + // For advance user, construct arguments can still get from 'arguments'. + cc.warn("Can not instantiate CCClass with arguments."); + return; + } + } +} + +function normalizeClassName (className) { + if (CC_EDITOR || CC_TEST) { + var DefaultName = 'CCClass'; + if (className) { + className = className.replace(/\./g, '_'); + className = className.split('').filter(function (x) { return /^[a-zA-Z0-9_$]/.test(x) }).join(''); + if (/^[0-9]/.test(className[0])) { + className = '_' + className; + } + try { + // validate name + eval('function ' + className + '(){}'); + } + catch (e) { + className = 'FireClass_' + className; + try { + eval('function ' + className + '(){}'); + } + catch (e) { + return DefaultName; + } + } + return className; + } + return DefaultName; + } +} + +function _createCtor (ctor, baseClass, className, options) { + var useTryCatch = ! (className && className.startsWith('cc.')); + var shouldAddProtoCtor; + if (CC_EDITOR && ctor && baseClass) { + // check super call in constructor + var originCtor = ctor; + if (SuperCallReg.test(ctor)) { + cc.warn(cc._LogInfos.Editor.Class.callSuperCtor, className); + ctor = function () { + this._super = function () {}; + var ret = originCtor.apply(this, arguments); + this._super = null; + return ret; + }; + } + if (/\bprototype.ctor\b/.test(originCtor)) { + cc.warn(cc._LogInfos.Editor.Class.callSuperCtor, className); + shouldAddProtoCtor = true; + } + } + var superCallBounded = options && baseClass && boundSuperCalls(baseClass, options); + + if (ctor && CC_DEV) { + _checkCtor(ctor); + } + // get base user constructors + var ctors; + if (FireClass._isCCClass(baseClass)) { + ctors = baseClass.__ctors__; + if (ctors) { + ctors = ctors.slice(); + } + } + else if (baseClass) { + ctors = [baseClass]; + } + // append subclass user constructors + if (ctors) { + if (ctor) { + ctors.push(ctor); + } + } + else if (ctor) { + ctors = [ctor]; + } + // create class constructor + var body; + if (CC_EDITOR || CC_TEST) { + body = '(function ' + normalizeClassName(className) + '(){\n'; + } + else { + body = '(function(){\n'; + } + //if (CC_EDITOR) { + // body += 'this._observing=false;\n'; + //} + if (superCallBounded) { + body += 'this._super=null;\n'; + } + body += 'instantiateProps(this,fireClass);\n'; + + // call user constructors + if (ctors) { + if (CC_EDITOR || CC_TEST) { + console.assert(ctors.length > 0); + } + + body += 'var cs=fireClass.__ctors__;\n'; + + if (useTryCatch) { + body += 'try{\n'; + } + + if (ctors.length <= 5) { + for (var i = 0; i < ctors.length; i++) { + body += '(cs[' + i + ']).apply(this,arguments);\n'; + } + } + else { + body += 'for(var i=0,l=cs.length;i= 0) { + continue; + } + var func = options[funcName]; + if (typeof func === 'function' || func === null) { + cls.prototype[funcName] = func; + } + else if (CC_EDITOR || CC_TEST) { + var correct = TYPO_TO_CORRECT[funcName]; + if (correct) { + cc.warn('Unknown parameter of %s.%s, maybe you want is "%s".', name, funcName, correct); + } + else if (func) { + cc.error('Unknown parameter of %s.%s, property should be defined in "properties" or "ctor"', name, funcName); + } + } + } + + if (CC_EDITOR || CC_TEST) { + var editor = options.editor; + if (editor) { + if ( cc.isChildClassOf(base, cc.Component) ) { + cc.Component._registerEditorProps(cls, editor); + } + else { + cc.warn('Can only define "editor" attribute for sub class of Components.'); + } + } + } + + return cls; +} + +/** + * Checks whether the constructor is created by cc.Class + * + * @method _isCCClass + * @param {Function} constructor + * @return {Boolean} + * @private + */ +FireClass._isCCClass = function (constructor) { + return !!constructor && (constructor.prop === _metaClass.prop); +}; + +// +// @method _convertToFireClass +// @param {Function} constructor +// @private +// +//FireClass._convertToFireClass = function (constructor) { +// constructor.prop = _metaClass.prop; +//}; + +// +// Specially optimized define function only for internal base classes +// +// @method _fastDefine +// @param {String} className +// @param {Function} constructor +// @param {string[]} serializableFields +// @private +// +function fastDefine (className, constructor, serializableFields) { + JS.setClassName(className, constructor); + constructor.__props__ = serializableFields; + for (var i = 0; i < serializableFields.length; i++) { + Attr.attr(constructor, serializableFields[i], { visible: false }); + } +} + +FireClass.attr = Attr.attr; + +var tmpAttrs = []; +function parseAttributes (attrs, className, propName) { + var ERR_Type = (CC_EDITOR || CC_TEST) ? 'The %s of %s must be type %s' : ''; + + tmpAttrs.length = 0; + var result = tmpAttrs; + + var type = attrs.type; + if (type) { + switch (type) { + // Specify that the input value must be integer in Inspector. + // Also used to indicates that the type of elements in array or the type of value in dictionary is integer. + case 'Integer': + result.push( { type: 'Integer'/*, expectedTypeOf: 'number'*/ } ); + break; + // Indicates that the type of elements in array or the type of value in dictionary is double. + case 'Float': + result.push( { type: 'Float'/*, expectedTypeOf: 'number'*/ } ); + break; + case 'Boolean': + result.push({ + type: 'Boolean', + //expectedTypeOf: 'number', + _onAfterProp: getTypeChecker('Boolean', 'Boolean') + }); + break; + case 'String': + result.push({ + type: 'String', + //expectedTypeOf: 'string', + _onAfterProp: getTypeChecker('String', 'String') + }); + break; + case 'Object': + if (CC_EDITOR || CC_TEST) { + cc.error('Please define "type" parameter of %s.%s as the actual constructor.', className, propName); + } + break; + default: + if (type === Attr.ScriptUuid) { + var attr = Attr.ObjectType(cc.ScriptAsset); + attr.type = 'Script'; + result.push(attr); + } + else { + if (typeof type === 'object') { + if (Enum.isEnum(type)) { + result.push({ + type: 'Enum', + //expectedTypeOf: 'number', + enumList: Enum.getList(type) + }); + } + else if (CC_EDITOR || CC_TEST) { + cc.error('Please define "type" parameter of %s.%s as the constructor of %s.', className, propName, type); + } + } + else if (typeof type === 'function') { + result.push(Attr.ObjectType(type)); + //result.push( { expectedTypeOf: 'object' } ); + } + else if (CC_EDITOR || CC_TEST) { + cc.error('Unknown "type" parameter of %s.%s:%s', className, propName, type); + } + } + break; + } + } + //else { + // if (attrs.default != null) { + // result.push({ + // expectedTypeOf: typeof attrs.default, + // }); + // } + //} + + function parseSimpleAttr (attrName, expectType, attrCreater) { + var val = attrs[attrName]; + if (val) { + if (typeof val === expectType) { + if (typeof attrCreater === 'undefined') { + var attr = {}; + attr[attrName] = val; + result.push(attr); + } + else { + result.push(typeof attrCreater === 'function' ? attrCreater(val) : attrCreater); + } + } + else if (CC_EDITOR || CC_TEST) { + cc.error('The %s of %s.%s must be type %s', attrName, className, propName, expectType); + } + } + } + + parseSimpleAttr('rawType', 'string', Attr.RawType); + parseSimpleAttr('editorOnly', 'boolean', Attr.EditorOnly); + if (CC_EDITOR || CC_TEST) { + parseSimpleAttr('displayName', 'string'); + parseSimpleAttr('multiline', 'boolean', {multiline: true}); + parseSimpleAttr('readonly', 'boolean', {readonly: true}); + parseSimpleAttr('tooltip', 'string'); + } + + if (attrs.url) { + result.push({ saveUrlAsAsset: true }); + } + if (attrs.serializable === false) { + result.push(Attr.NonSerialized); + } + + if (CC_EDITOR || CC_TEST) { + var visible = attrs.visible; + if (typeof visible !== 'undefined') { + if (!attrs.visible) { + result.push({visible: false}); + } + } + else { + var startsWithUS = (propName.charCodeAt(0) === 95); + if (startsWithUS) { + result.push({visible: false}); + } + } + } + + //if (attrs.custom) { + // result.push( { custom: attrs.custom }); + //} + + var range = attrs.range; + if (range) { + if (Array.isArray(range)) { + if (range.length >= 2) { + result.push(Attr.Range(range[0], range[1])); + } + else if (CC_EDITOR || CC_TEST) { + cc.error('The length of range array must be 2'); + } + } + else if (CC_EDITOR || CC_TEST) { + cc.error(ERR_Type, '"range"', className + '.' + propName, 'array'); + } + } + + var nullable = attrs.nullable; + if (nullable) { + if (typeof nullable === 'object') { + var boolPropName = nullable.propName; + if (typeof boolPropName === 'string') { + var def = nullable.default; + if (typeof def === 'boolean') { + result.push(Attr.Nullable(boolPropName, def)); + } + else if (CC_EDITOR || CC_TEST) { + cc.error(ERR_Type, '"default"', 'nullable object', 'boolean'); + } + } + else if (CC_EDITOR || CC_TEST) { + cc.error(ERR_Type, '"propName"', 'nullable object', 'string'); + } + } + else if (CC_EDITOR || CC_TEST) { + cc.error(ERR_Type, '"nullable"', className + '.' + propName, 'object'); + } + } + + if (CC_EDITOR || CC_TEST) { + var watch = attrs.watch; + if (watch) { + if (typeof watch === 'object') { + for (var watchKey in watch) { + var watchCallback = watch[watchKey]; + if (typeof watchCallback === 'function') { + result.push(Attr.Watch(watchKey.split(' '), watchCallback)); + } + else { + cc.error(ERR_Type, 'value', 'watch object', 'function'); + } + } + } + else { + cc.error(ERR_Type, 'watch', className + '.' + propName, 'object'); + } + } + } + + return result; +} + +/** + * @param {Object} options + * @return {Function} + * @deprecated + */ +FireClass.extend = FireClass; + +cc.Class = FireClass; + +module.exports = { + instantiateProps: instantiateProps, + isArray: function (defaultVal) { + defaultVal = getDefault(defaultVal); + return Array.isArray(defaultVal); + }, + fastDefine: fastDefine +}; diff --git a/cocos2d/core/platform/CCCommon.js b/cocos2d/core/platform/CCCommon.js new file mode 100644 index 00000000000..826e8a2172d --- /dev/null +++ b/cocos2d/core/platform/CCCommon.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. + ****************************************************************************/ + +var cc = cc || {}; +cc._tmp = cc._tmp || {}; + +/** + * Function added for JS bindings compatibility. Not needed in cocos2d-html5. + * @method associateWithNative + * @param {Object} jsObj subclass + * @param {Object} superclass + */ +cc.associateWithNative = function (jsObj, superclass) { +}; + +/** + * Key map for keyboard event + * + * @constant + * @type {Object} + * @example {@link utils/api/cocos/docs/cocos2d/core/platform/CCCommon/KEY.js} + */ +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 dont 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 controll + 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 + * @method getImageFormatByData + * @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; +}; + +/** + * Another way to subclass: Using Google Closure. + * The following code was copied + pasted from goog.base / goog.inherits + * @method inherits + * @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 ]; +// } +}; + +/** + * @deprecated since v3.0, please use cc._Class.extend and _super + * @cc._Class.extend + */ +cc.base = function(me, opt_methodName, var_args) { + var caller = arguments.callee.caller; + if (caller.superClass_) { + // This is a constructor. Call the superclass constructor. + ret = caller.superClass_.constructor.apply( me, Array.prototype.slice.call(arguments, 1)); + return ret; + } + + var args = Array.prototype.slice.call(arguments, 2); + var foundCaller = false; + for (var ctor = me.constructor; ctor; ctor = ctor.superClass_ && ctor.superClass_.constructor) { + if (ctor.prototype[opt_methodName] === caller) { + foundCaller = true; + } else if (foundCaller) { + return ctor.prototype[opt_methodName].apply(me, args); + } + } + + // If we did not find the caller in the prototype chain, + // then one of two things happened: + // 1) The caller is an instance method. + // 2) This method was not called by the right caller. + if (me[opt_methodName] === caller) { + return me.constructor.prototype[opt_methodName].apply(me, args); + } else { + throw Error( + 'cc.base called from a method of one name ' + + 'to a method of a different name'); + } +}; diff --git a/cocos2d/core/platform/CCConfig.js b/cocos2d/core/platform/CCConfig.js new file mode 100644 index 00000000000..34796dfd647 --- /dev/null +++ b/cocos2d/core/platform/CCConfig.js @@ -0,0 +1,312 @@ +/**************************************************************************** + 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.9 Beta"; + +/** + *

+ * 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 {Vec2} + */ +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; + +/** + *

+ * Automatically premultiply alpha for PNG resources + *

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

+ * 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 = 0; + +/** + *

+ * 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; + +/** + *

+ * If enabled, cocos2d supports retina display.
+ * For performance reasons, it's recommended disable it in games without retina display support, like iPad only games.
+ *
+ * To enable set it to 1. Use 0 to disable it. Enabled 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.RETINA_DISPLAY_SUPPORT = 1; + +/** + *

+ * 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; + +/** + * Whether or not support retina display + * @constant + * @type {Number} + */ +cc.IS_RETINA_DISPLAY_SUPPORTED = 1; + +/** + * 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/cocos2d/core/platform/CCEGLView.js b/cocos2d/core/platform/CCEGLView.js new file mode 100644 index 00000000000..fc110e66ef2 --- /dev/null +++ b/cocos2d/core/platform/CCEGLView.js @@ -0,0 +1,1387 @@ +/**************************************************************************** + 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"; + +cc.__BrowserGetter = { + init: function(){ + this.html = document.getElementsByTagName("html")[0]; + }, + 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", + "user-scalable": "no" + }, + adaptationType: cc.sys.browserType +}; + +if(window.navigator.userAgent.indexOf("OS 8_1_") > -1) //this mistake like MIUI, so use of MIUI treatment method + cc.__BrowserGetter.adaptationType = cc.sys.BROWSER_TYPE_MIUI; + +if(cc.sys.os === cc.sys.OS_IOS) // All browsers are WebView + cc.__BrowserGetter.adaptationType = cc.sys.BROWSER_TYPE_SAFARI; + +switch(cc.__BrowserGetter.adaptationType){ + case cc.sys.BROWSER_TYPE_SAFARI: + cc.__BrowserGetter.meta["minimal-ui"] = "true"; + cc.__BrowserGetter.availWidth = function(frame){ + return frame.clientWidth; + }; + cc.__BrowserGetter.availHeight = function(frame){ + return frame.clientHeight; + }; + break; + case cc.sys.BROWSER_TYPE_CHROME: + cc.__BrowserGetter.__defineGetter__("target-densitydpi", function(){ + return cc.view._targetDensityDPI; + }); + case cc.sys.BROWSER_TYPE_SOUGOU: + case cc.sys.BROWSER_TYPE_UC: + cc.__BrowserGetter.availWidth = function(frame){ + return frame.clientWidth; + }; + cc.__BrowserGetter.availHeight = function(frame){ + return frame.clientHeight; + }; + break; + case cc.sys.BROWSER_TYPE_MIUI: + cc.__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; +} + +/** + * 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 EGLView + * @extends _Class + */ +cc.EGLView = cc._Class.extend(/** @lends cc.view# */{ + _delegate: null, + // Size of parent node that contains cc.container and cc.game.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: true, + // The device's pixel ratio (for retina displays) + _devicePixelRatio: 1, + // the view name + _viewName: "", + // Custom callback for resize event + _resizeCallback: null, + _scaleX: 1, + _originalScaleX: 1, + _scaleY: 1, + _originalScaleY: 1, + _indexBitsUsed: 0, + _maxTouches: 5, + _resolutionPolicy: null, + _rpExactFit: null, + _rpShowAll: null, + _rpNoBorder: null, + _rpFixedHeight: null, + _rpFixedWidth: null, + _initialized: false, + + _captured: false, + _wnd: null, + _hDC: null, + _hRC: null, + _supportTouch: false, + _contentTranslateLeftTop: 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; + + cc.__BrowserGetter.init(this); + + _t._frameSize = cc.size(0, 0); + _t._initFrameSize(); + + var w = cc.game.canvas.width, h = cc.game.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; + _t.enableRetina(sys.os === sys.OS_IOS || sys.os === sys.OS_OSX); + 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._hDC = cc.game.canvas; + _t._hRC = cc._renderContext; + _t._targetDensityDPI = cc.DENSITYDPI_HIGH; + }, + + // Resize helper functions + _resizeEvent: function () { + var view; + if(this.setDesignResolutionSize){ + view = this; + }else{ + view = cc.view; + } + + // Check frame size changed or not + var prevFrameW = view._frameSize.width, prevFrameH = view._frameSize.height; + view._initFrameSize(); + if (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; + if (width > 0) + view.setDesignResolutionSize(width, height, view._resolutionPolicy); + + cc.eventManager.dispatchCustomEvent('canvas-resize'); + if (view._resizeCallback) { + view._resizeCallback.call(); + } + }, + + /** + *

+ * 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"
+ *

+ * + * @method setTargetDensityDPI + * @param {String} densityDPI + */ + setTargetDensityDPI: function(densityDPI){ + this._targetDensityDPI = densityDPI; + this._adjustViewportMeta(); + }, + + /** + * Returns the current target-densitydpi value of cc.view. + * @method getTargetDensityDPI + * @returns {String} + */ + getTargetDensityDPI: function(){ + return this._targetDensityDPI; + }, + + /** + * Sets whether resize canvas automatically when browser's size changed.
+ * Useful only on web. + * @method resizeWithBrowserSize + * @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); + window.addEventListener('orientationchange', this._resizeEvent); + } + } else { + //disable + if (this.__resizeWithBrowserSize) { + this.__resizeWithBrowserSize = false; + window.removeEventListener('resize', this._resizeEvent); + window.removeEventListener('orientationchange', 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. + * @method setResizeCallback + * @param {Function|Null} callback - The callback function + */ + setResizeCallback: function (callback) { + if (cc.js.isFunction(callback) || callback == null) { + this._resizeCallback = callback; + } + }, + + _initFrameSize: function () { + var locFrameSize = this._frameSize; + locFrameSize.width = cc.__BrowserGetter.availWidth(cc.game.frame); + locFrameSize.height = cc.__BrowserGetter.availHeight(cc.game.frame); + }, + + // 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(cc.__BrowserGetter.meta, 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 + * @method adjustViewPort + * @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 + * @method enableRetina + * @param {Boolean} enabled - Enable or disable retina display + */ + enableRetina: function(enabled) { + this._retinaEnabled = enabled ? true : false; + }, + + /** + * Check whether retina display is enabled.
+ * Only useful on web + * @method isRetinaEnabled + * @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 + * @method enableAutoFullScreen + * @param {Boolean} enabled - Enable or disable auto full screen on mobile devices + */ + enableAutoFullScreen: function(enabled) { + this._autoFullScreen = enabled ? true : false; + }, + + /** + * Check whether auto full screen is enabled.
+ * Only useful on web + * @method isAutoFullScreenEnabled + * @return {Boolean} Auto full screen enabled or not + */ + isAutoFullScreenEnabled: function() { + return this._autoFullScreen; + }, + + /** + * Force destroying EGL view, subclass must implement this method. + */ + end: function () { + }, + + /** + * 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. + * @method isOpenGLReady + * @return {Boolean} + */ + isOpenGLReady: function () { + return (this._hDC !== null && this._hRC !== null); + }, + + /* + * Set zoom factor for frame. This method is for debugging big resolution (e.g.new ipad) app on desktop. + * @method setFrameZoomFactor + * @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. + * @method setIMEKeyboardState + * @param {Boolean} isOpen + */ + setIMEKeyboardState: function (isOpen) { + }, + + /** + * Sets the resolution translate on EGLView. + * @method setContentTranslateLeftTop + * @param {Number} offsetLeft + * @param {Number} offsetTop + */ + setContentTranslateLeftTop: function (offsetLeft, offsetTop) { + this._contentTranslateLeftTop = {left: offsetLeft, top: offsetTop}; + }, + + /** + * Returns the resolution translate on EGLView + * @method getContentTranslateLeftTop + * @return {Size|Object} + */ + getContentTranslateLeftTop: function () { + return this._contentTranslateLeftTop; + }, + + /** + * Not support on native.
+ * On web, it sets the size of the canvas. + * @method setCanvasSize + * @param {Number} width + * @param {Number} height + */ + setCanvasSize: function (width, height) { + var canvas = cc.game.canvas; + var container = cc.game.container; + + canvas.width = width * this._devicePixelRatio; + canvas.height = height * this._devicePixelRatio; + + canvas.style.width = width + 'px'; + canvas.style.height = height + 'px'; + + container.style.width = width + 'px'; + container.style.height = height + 'px'; + + this._resizeEvent(); + }, + + /** + * 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. + * @method getCanvasSize + * @return {Size} + */ + getCanvasSize: function () { + return cc.size(cc.game.canvas.width, cc.game.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. + * @method getFrameSize + * @return {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. + * @method setFrameSize + * @param {Number} width + * @param {Number} height + */ + setFrameSize: function (width, height) { + this._frameSize.width = width; + this._frameSize.height = height; + cc.game.frame.style.width = width + "px"; + cc.game.frame.style.height = height + "px"; + //this.centerWindow(); + this._resizeEvent(); + cc.director.setProjection(cc.director.getProjection()); + }, + + /** + * Empty function + */ + centerWindow: function () { + }, + + /** + * Returns the visible area size of the view port. + * @method getVisibleSize + * @return {Size} + */ + getVisibleSize: function () { + return cc.size(this._visibleRect.width,this._visibleRect.height); + }, + + /** + * Returns the visible area size of the view port. + * @method getVisibleSizeInPixel + * @return {Size} + */ + getVisibleSizeInPixel: function () { + return cc.size( this._visibleRect.width * this._scaleX, + this._visibleRect.height * this._scaleY ); + }, + + /** + * Returns the visible origin of the view port. + * @method getVisibleOrigin + * @return {Vec2} + */ + getVisibleOrigin: function () { + return cc.p(this._visibleRect.x,this._visibleRect.y); + }, + + /** + * Returns the visible origin of the view port. + * @method getVisibleOriginInPixel + * @return {Vec2} + */ + getVisibleOriginInPixel: function () { + return cc.p(this._visibleRect.x * this._scaleX, + this._visibleRect.y * this._scaleY); + }, + + /** + * Returns whether developer can set content's scale factor. + * @method canSetContentScaleFactor + * @return {Boolean} + */ + canSetContentScaleFactor: function () { + return true; + }, + + /** + * Returns the current resolution policy + * @see cc.ResolutionPolicy + * @method getResolutionPolicy + * @return {ResolutionPolicy} + */ + getResolutionPolicy: function () { + return this._resolutionPolicy; + }, + + /** + * Sets the current resolution policy + * @see cc.ResolutionPolicy + * @method setResolutionPolicy + * @param {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
+ * @method setDesignResolutionSize + * @param {Number} width Design resolution width. + * @param {Number} height Design resolution height. + * @param {ResolutionPolicy|Number} resolutionPolicy The resolution policy desired + */ + setDesignResolutionSize: function (width, height, resolutionPolicy) { + // Defensive code + if( !(width > 0 || height > 0) ){ + cc.log(cc._LogInfos.view.setDesignResolutionSize); + return; + } + + this.setResolutionPolicy(resolutionPolicy); + var policy = this._resolutionPolicy; + if (!policy){ + cc.log(cc._LogInfos.view.setDesignResolutionSize_2); + return; + } + policy.preApply(this); + + // Reinit frame size + if(cc.sys.isMobile) + this._adjustViewportMeta(); + + this._initFrameSize(); + + 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.game.canvas.width / this._scaleX; + vb.height = cc.game.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(); + } + + this._originalScaleX = this._scaleX; + this._originalScaleY = this._scaleY; + // For editbox + if (cc.DOM) + cc.DOM._resetEGLViewDiv(); + cc.visibleRect && cc.visibleRect.init(this._visibleRect); + }, + + /** + * Returns the designed size for the view. + * Default resolution size is the same as 'getFrameSize'. + * @method getDesignResolutionSize + * @return {Size} + */ + getDesignResolutionSize: function () { + return cc.size(this._designResolutionSize.width, this._designResolutionSize.height); + }, + + /** + * Sets the container 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. + * @method setRealPixelResolution + * @param {Number} width Design resolution width. + * @param {Number} height Design resolution height. + * @param {ResolutionPolicy|Number} resolutionPolicy The resolution policy desired + */ + setRealPixelResolution: function (width, height, resolutionPolicy) { + // Set viewport's width + this._setViewportMeta({"width": width, "user-scalable": "no"}, true); + + // Set body width to the exact pixel resolution + 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. + * @method setViewPortInPoints + * @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. + * @method setScissorInPoints + * @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; + cc._renderContext.scissor((x * locScaleX * locFrameZoomFactor + this._viewPortRect.x * locFrameZoomFactor), + (y * locScaleY * locFrameZoomFactor + this._viewPortRect.y * locFrameZoomFactor), + (w * locScaleX * locFrameZoomFactor), + (h * locScaleY * locFrameZoomFactor)); + }, + + /** + * Returns whether GL_SCISSOR_TEST is enable + * @method isScissorEnabled + * @return {Boolean} + */ + isScissorEnabled: function () { + var gl = cc._renderContext; + return gl.isEnabled(gl.SCISSOR_TEST); + }, + + /** + * Returns the current scissor rectangle + * @method getScissorRect + * @return {Rect} + */ + getScissorRect: function () { + var gl = cc._renderContext, scaleX = this._scaleX, scaleY = this._scaleY; + var boxArr = gl.getParameter(gl.SCISSOR_BOX); + return cc.rect((boxArr[0] - this._viewPortRect.x) / scaleX, (boxArr[1] - this._viewPortRect.y) / scaleY, + boxArr[2] / scaleX, boxArr[3] / scaleY); + }, + + /** + * Sets the name of the view + * @method setViewName + * @param {String} viewName + */ + setViewName: function (viewName) { + if (viewName != null && viewName.length > 0) { + this._viewName = viewName; + } + }, + + /** + * Returns the name of the view + * @method getViewName + * @return {String} + */ + getViewName: function () { + return this._viewName; + }, + + /** + * Returns the view port rectangle. + * @method getViewPortRect + * @return {Rect} + */ + getViewPortRect: function () { + return this._viewPortRect; + }, + + /** + * Returns scale factor of the horizontal direction (X axis). + * @method getScaleX + * @return {Number} + */ + getScaleX: function () { + return this._scaleX; + }, + + /** + * Returns scale factor of the vertical direction (Y axis). + * @method getScaleY + * @return {Number} + */ + getScaleY: function () { + return this._scaleY; + }, + + /** + * Returns device pixel ratio for retina display. + * @method getDevicePixelRatio + * @return {Number} + */ + getDevicePixelRatio: function() { + return this._devicePixelRatio; + }, + + /** + * Returns the real location in view for a translation based on a related position + * @method convertToLocationInView + * @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 {Vec2} + */ + convertToLocationInView: function (tx, ty, relatedPos) { + return {x: this._devicePixelRatio * (tx - relatedPos.left), y: this._devicePixelRatio * (relatedPos.top + relatedPos.height - ty)}; + }, + + _convertMouseToLocationInView: function(point, relatedPos) { + var locViewPortRect = this._viewPortRect, _t = this; + point.x = ((_t._devicePixelRatio * (point.x - relatedPos.left)) - locViewPortRect.x) / _t._scaleX; + point.y = (_t._devicePixelRatio * (relatedPos.top + relatedPos.height - point.y) - locViewPortRect.y) / _t._scaleY; + }, + + _convertTouchesWithScale: function(touches){ + var locViewPortRect = this._viewPortRect, locScaleX = this._scaleX, locScaleY = this._scaleY, selTouch, selPoint, selPrePoint; + for( var i = 0; i < touches.length; i ++){ + selTouch = touches[i]; + selPoint = selTouch._point; + selPrePoint = selTouch._prevPoint; + selTouch._setPoint((selPoint.x - locViewPortRect.x) / locScaleX, + (selPoint.y - locViewPortRect.y) / locScaleY); + selTouch._setPrevPoint((selPrePoint.x - locViewPortRect.x) / locScaleX, + (selPrePoint.y - locViewPortRect.y) / locScaleY); + } + } +}); + +/** + * @method _getInstance + * @return {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.game.canvas object

+ * + * @class ContainerStrategy + * @extends _Class + */ +cc.ContainerStrategy = cc._Class.extend(/** @lends cc.ContainerStrategy# */{ + /** + * Manipulation before appling the strategy + * @method preApply + * @param {view} view - The target view + */ + preApply: function (view) { + }, + + /** + * Function to apply this strategy + * @method apply + * @param {view} view + * @param {Size} designedResolution + */ + apply: function (view, designedResolution) { + }, + + /** + * Manipulation after applying the strategy + * @method postApply + * @param {view} view The target view + */ + postApply: function (view) { + + }, + + _setupContainer: function (view, w, h) { + var frame = cc.game.frame; + if (cc.view._autoFullScreen && cc.sys.isMobile && frame === document.documentElement) { + // Automatically full screen when user touches on mobile version + cc.screen.autoFullScreen(frame); + } + + var locCanvasElement = cc.game.canvas, locContainer = cc.game.container; + // Setup container + locContainer.style.width = locCanvasElement.style.width = w + "px"; + locContainer.style.height = locCanvasElement.style.height = h + "px"; + // Setup pixel ratio for retina display + var devicePixelRatio = view._devicePixelRatio = 1; + if (view.isRetinaEnabled()) + devicePixelRatio = view._devicePixelRatio = window.devicePixelRatio || 1; + // Setup canvas + locCanvasElement.width = w * devicePixelRatio; + locCanvasElement.height = h * devicePixelRatio; + cc._renderContext.resetCache && cc._renderContext.resetCache(); + + var body = document.body, style; + if (body && (style = body.style)) { + style.paddingTop = style.paddingTop || "0px"; + style.paddingRight = style.paddingRight || "0px"; + style.paddingBottom = style.paddingBottom || "0px"; + style.paddingLeft = style.paddingLeft || "0px"; + style.borderTop = style.borderTop || "0px"; + style.borderRight = style.borderRight || "0px"; + style.borderBottom = style.borderBottom || "0px"; + style.borderLeft = style.borderLeft || "0px"; + style.marginTop = style.marginTop || "0px"; + style.marginRight = style.marginRight || "0px"; + style.marginBottom = style.marginBottom || "0px"; + style.marginLeft = style.marginLeft || "0px"; + } + }, + + _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 ContentStrategy + * @extends _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 + * @method preApply + * @param {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 + * @method apply + * @param {view} view + * @param {Size} designedResolution + * @return {Object} scaleAndViewportRect + */ + apply: function (view, designedResolution) { + return {"scale": [1, 1]}; + }, + + /** + * Manipulation after applying the strategy + * @method postApply + * @param {view} view - The target view + */ + postApply: function (view) { + } +}); + +(function () { + +// Container scale strategys + /** + * @class EqualToFrame + * @extends ContainerStrategy + */ + var EqualToFrame = cc.ContainerStrategy.extend({ + apply: function (view) { + this._setupContainer(view, view._frameSize.width, view._frameSize.height); + } + }); + + /** + * @class ProportionalToFrame + * @extends 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 + containerStyle.marginLeft = offx + "px"; + containerStyle.marginRight = offx + "px"; + containerStyle.marginTop = offy + "px"; + containerStyle.marginBottom = offy + "px"; + } + }); + + /** + * @class EqualToWindow + * @extends EqualToFrame + */ + var EqualToWindow = EqualToFrame.extend({ + preApply: function (view) { + this._super(view); + cc.game.frame = document.documentElement; + }, + + apply: function (view) { + this._super(view); + this._fixContainer(); + } + }); + + /** + * @class ProportionalToWindow + * @extends ProportionalToFrame + */ + var ProportionalToWindow = ProportionalToFrame.extend({ + preApply: function (view) { + this._super(view); + cc.game.frame = document.documentElement; + }, + + apply: function (view, designedResolution) { + this._super(view, designedResolution); + this._fixContainer(); + } + }); + + /** + * @class OriginalContainer + * @extends ContainerStrategy + */ + var OriginalContainer = cc.ContainerStrategy.extend({ + apply: function (view) { + this._setupContainer(view, cc.game.canvas.width, cc.game.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.game.canvas.width, containerH = cc.game.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.game.canvas.width, containerH = cc.game.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.game.canvas.width, containerH = cc.game.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.game.canvas.width, containerH = cc.game.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.game.canvas.width, containerH = cc.game.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 ResolutionPolicy + * @extends _Class + * @param {ContainerStrategy} containerStg The container strategy + * @param {ContentStrategy} contentStg The content strategy + */ +cc.ResolutionPolicy = cc._Class.extend(/** @lends cc.ResolutionPolicy# */{ + _containerStrategy: null, + _contentStrategy: null, + + /** + * Constructor of cc.ResolutionPolicy + * @param {ContainerStrategy} containerStg + * @param {ContentStrategy} contentStg + */ + ctor: function (containerStg, contentStg) { + this.setContainerStrategy(containerStg); + this.setContentStrategy(contentStg); + }, + + /** + * Manipulation before applying the resolution policy + * @method preApply + * @param {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 + * @method apply + * @param {view} view - The target view + * @param {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 + * @method postApply + * @param {view} view - The target view + */ + postApply: function (view) { + this._containerStrategy.postApply(view); + this._contentStrategy.postApply(view); + }, + + /** + * Setup the container's scale strategy + * @method setContainerStrategy + * @param {ContainerStrategy} containerStg + */ + setContainerStrategy: function (containerStg) { + if (containerStg instanceof cc.ContainerStrategy) + this._containerStrategy = containerStg; + }, + + /** + * Setup the content's scale strategy + * @method setContentStrategy + * @param {ContentStrategy} contentStg + */ + setContentStrategy: function (contentStg) { + if (contentStg instanceof cc.ContentStrategy) + this._contentStrategy = contentStg; + } +}); + +cc.js.get(cc.ResolutionPolicy.prototype, "canvasSize", function () { + return cc.v2(cc.game.canvas.width, cc.game.canvas.height); +}); + +/** + * @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/cocos2d/core/platform/CCInputExtension.js b/cocos2d/core/platform/CCInputExtension.js new file mode 100644 index 00000000000..a920621c402 --- /dev/null +++ b/cocos2d/core/platform/CCInputExtension.js @@ -0,0 +1,133 @@ +/**************************************************************************** + 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 inputManager = require("./CCInputManager"); + +/** + * whether enable accelerometer event + * @method setAccelerometerEnabled + * @param {Boolean} isEnable + */ +inputManager.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; + scheduler.scheduleUpdate(_t); + } else { + _t._accelCurTime = 0; + scheduler.scheduleUpdate(_t); + } +}; + +/** + * set accelerometer interval value + * @method setAccelerometerInterval + * @param {Number} interval + */ +inputManager.setAccelerometerInterval = function(interval){ + if (this._accelInterval !== interval) { + this._accelInterval = interval; + } +}; + +inputManager._registerKeyboardEvent = function(){ + cc.game.canvas.addEventListener("keydown", function (e) { + cc.eventManager.dispatchEvent(new cc.Event.EventKeyboard(e.keyCode, true)); + e.stopPropagation(); + e.preventDefault(); + }, false); + cc.game.canvas.addEventListener("keyup", function (e) { + cc.eventManager.dispatchEvent(new cc.Event.EventKeyboard(e.keyCode, false)); + e.stopPropagation(); + e.preventDefault(); + }, false); +}; + +inputManager._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; + } + + w.addEventListener(_deviceEventType, _t.didAccelerate.bind(_t), false); +}; + +inputManager.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; + } + + if(cc.sys.os === cc.sys.OS_ANDROID){ + mAcceleration.x = -x; + mAcceleration.y = -y; + }else{ + mAcceleration.x = x; + mAcceleration.y = y; + } + mAcceleration.z = z; + + mAcceleration.timestamp = eventData.timeStamp || Date.now(); + + var tmpX = mAcceleration.x; + if(w.orientation === cc.UIOrientation.LANDSCAPE_RIGHT){ + mAcceleration.x = -mAcceleration.y; + mAcceleration.y = tmpX; + }else if(w.orientation === cc.UIOrientation.LANDSCAPE_LEFT){ + mAcceleration.x = mAcceleration.y; + mAcceleration.y = -tmpX; + }else if(w.orientation === cc.UIOrientation.PORTRAIT_UPSIDE_DOWN){ + mAcceleration.x = -mAcceleration.x; + mAcceleration.y = -mAcceleration.y; + } +}; \ No newline at end of file diff --git a/cocos2d/core/platform/CCInputManager.js b/cocos2d/core/platform/CCInputManager.js new file mode 100644 index 00000000000..bc170bfb5a8 --- /dev/null +++ b/cocos2d/core/platform/CCInputManager.js @@ -0,0 +1,615 @@ +/**************************************************************************** + 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. + ****************************************************************************/ + +Enum = require("../value-types/CCEnum"); + +/** + * Enum for UIOrientation. + * @readOnly + * @enum UIOrientation + */ +cc.UIOrientation = cc.Enum({ + /** + * @property {number} PORTRAIT + */ + PORTRAIT: 0, + /** + * @property {number} LANDSCAPE_LEFT + */ + LANDSCAPE_LEFT: -90, + /** + * @property {number} LANDSCAPE_RIGHT + */ + LANDSCAPE_RIGHT: 90, + /** + * @property {number} PORTRAIT_UPSIDE_DOWN + */ + PORTRAIT_UPSIDE_DOWN: 180, +}); + +/** + *

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

+ * @class inputManager + */ +var inputManager = /** @lends cc.inputManager# */{ + _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; + + for (var i = 0; i < this._maxTouches; i++) { + if (!(temp & 0x00000001)) { + this._indexBitsUsed |= (1 << i); + 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, + + /** + * @method handleTouchesBegin + * @param {Array} touches + */ + handleTouchesBegin: function (touches) { + var selTouch, index, curTouch, touchID, handleTouches = [], locTouchIntDict = this._touchesIntegerDict; + 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._setPrevPoint(selTouch._prevPoint); + locTouchIntDict[touchID] = unusedIndex; + handleTouches.push(curTouch); + } + } + if(handleTouches.length > 0){ + this._glView._convertTouchesWithScale(handleTouches); + var touchEvent = new cc.Event.EventTouch(handleTouches); + touchEvent._eventCode = cc.Event.EventTouch.BEGAN; + cc.eventManager.dispatchEvent(touchEvent); + } + }, + + /** + * @method handleTouchesMove + * @param {Array} touches + */ + handleTouchesMove: function(touches){ + var selTouch, index, touchID, handleTouches = [], locTouches = this._touches; + 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); + handleTouches.push(locTouches[index]); + } + } + if(handleTouches.length > 0){ + this._glView._convertTouchesWithScale(handleTouches); + var touchEvent = new cc.Event.EventTouch(handleTouches); + touchEvent._eventCode = cc.Event.EventTouch.MOVED; + cc.eventManager.dispatchEvent(touchEvent); + } + }, + + /** + * @method handleTouchesEnd + * @param {Array} touches + */ + handleTouchesEnd: function(touches){ + var handleTouches = this.getSetOfTouchesEndOrCancel(touches); + if(handleTouches.length > 0) { + this._glView._convertTouchesWithScale(handleTouches); + var touchEvent = new cc.Event.EventTouch(handleTouches); + touchEvent._eventCode = cc.Event.EventTouch.ENDED; + cc.eventManager.dispatchEvent(touchEvent); + } + }, + + /** + * @method handleTouchesCancel + * @param {Array} touches + */ + handleTouchesCancel: function(touches){ + var handleTouches = this.getSetOfTouchesEndOrCancel(touches); + if(handleTouches.length > 0) { + this._glView._convertTouchesWithScale(handleTouches); + var touchEvent = new cc.Event.EventTouch(handleTouches); + touchEvent._eventCode = cc.Event.EventTouch.CANCELLED; + cc.eventManager.dispatchEvent(touchEvent); + } + }, + + /** + * @method getSetOfTouchesEndOrCancel + * @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; + }, + + /** + * @method getHTMLElementPosition + * @param {HTMLElement} element + * @return {Object} + */ + getHTMLElementPosition: function (element) { + var docElem = document.documentElement; + var win = window; + var box = null; + if (cc.js.isFunction(element.getBoundingClientRect)) { + box = element.getBoundingClientRect(); + } else { + if (element instanceof HTMLCanvasElement) { + box = { + left: 0, + top: 0, + width: element.width, + height: element.height + }; + } 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 + }; + }, + + /** + * @method getPreTouch + * @param {Touch} touch + * @return {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; + }, + + /** + * @method setPreTouch + * @param {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; + } + } + }, + + /** + * @method getTouchByXY + * @param {Number} tx + * @param {Number} ty + * @param {Vec2} pos + * @return {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; + }, + + /** + * @method getTouchByXY + * @param {Vec2} location + * @param {Vec2} pos + * @param {Number} eventType + * @returns {Event.EventMouse} + */ + getMouseEvent: function(location, pos, eventType){ + var locPreMouse = this._prevMousePoint; + this._glView._convertMouseToLocationInView(location, pos); + var mouseEvent = new cc.Event.EventMouse(eventType); + mouseEvent.setLocation(location.x, location.y); + mouseEvent._setPrevCursor(locPreMouse.x, locPreMouse.y); + locPreMouse.x = location.x; + locPreMouse.y = location.y; + return mouseEvent; + }, + + /** + * @method getPointByEvent + * @param {Touch} event + * @param {Vec2} pos + * @return {Vec2} + */ + getPointByEvent: function(event, pos){ + if (event.pageX != null) //not avalable 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}; + }, + + /** + * @method getTouchesByEvent + * @param {Touch} event + * @param {Vec2} 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; + }, + + /** + * @method registerSystemEvent + * @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) { + !prohibition && window.addEventListener('mousedown', function () { + selfPointer._mousePressed = true; + }, false); + + !prohibition && window.addEventListener('mouseup', function (event) { + 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.Event.EventMouse.UP); + mouseEvent.setButton(event.button); + cc.eventManager.dispatchEvent(mouseEvent); + } + }, false); + + //register canvas mouse event + !prohibition && element.addEventListener("mousedown", function (event) { + 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.Event.EventMouse.DOWN); + mouseEvent.setButton(event.button); + cc.eventManager.dispatchEvent(mouseEvent); + + event.stopPropagation(); + event.preventDefault(); + element.focus(); + }, false); + + !prohibition && element.addEventListener("mouseup", function (event) { + 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.Event.EventMouse.UP); + mouseEvent.setButton(event.button); + cc.eventManager.dispatchEvent(mouseEvent); + + event.stopPropagation(); + event.preventDefault(); + }, false); + + !prohibition && element.addEventListener("mousemove", function (event) { + 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.Event.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.Event.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.Event.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(){}, + + _registerAccelerometerEvent: function(){}, + + /** + * @method update + * @param {Number} dt + */ + update:function(dt){ + if(this._accelCurTime > this._accelInterval){ + this._accelCurTime -= this._accelInterval; + cc.eventManager.dispatchEvent(new cc.Event.EventAcceleration(this._acceleration)); + } + this._accelCurTime += dt; + } +}; + +cc.inputManager = inputManager; + +module.exports = inputManager; \ No newline at end of file diff --git a/cocos2d/core/platform/CCLoader.js b/cocos2d/core/platform/CCLoader.js new file mode 100644 index 00000000000..75a006c2ae4 --- /dev/null +++ b/cocos2d/core/platform/CCLoader.js @@ -0,0 +1,583 @@ + +/** + * Loader for resource loading process. It's a singleton object. + * @class loader + * @static + */ +cc.loader = cc.loader || (function () { + var _jsCache = {}, //cache for js + _register = {}, //register of loaders + _langPathCache = {}, //cache for lang path + _aliases = {}, //aliases for res url + _urlRegExp = new RegExp( + "^" + + // protocol identifier + "(?:(?:https?|ftp)://)" + + // user:pass authentication + "(?:\\S+(?::\\S*)?@)?" + + "(?:" + + // IP address dotted notation octets + // excludes loopback network 0.0.0.0 + // excludes reserved space >= 224.0.0.0 + // excludes network & broacast addresses + // (first & last IP address of each class) + "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" + + "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" + + "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" + + "|" + + // host name + "(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)" + + // domain name + "(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*" + + // TLD identifier + "(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))" + + "|" + + "(?:localhost)" + + ")" + + // port number + "(?::\\d{2,5})?" + + // resource path + "(?:/\\S*)?" + + "$", "i" + ); + + return /** @lends cc.loader# */{ + resPath: "",//root path of resource + audioPath: "",//root path of audio + cache: {},//cache for data loaded + + /** + * Get XMLHttpRequest. + * @returns {XMLHttpRequest} + */ + getXMLHttpRequest: function () { + return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject("MSXML2.XMLHTTP"); + }, + + //@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; + }, + + /** + * Load js files. + * If the third parameter doesn't exist, then the baseDir turns to be "". + * + * @method loadJs + * @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. + * + * @method loadJsWithImg + * @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); + s.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. + * + * @method loadTxt + * @param {String} url + * @param {Function} [cb] - arguments are : err, txt + */ + loadTxt: function (url, cb) { + 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"); + xhr.onload = function () { + if(xhr.readyState === 4) { + (xhr.status === 200 || xhr.status === 0) ? cb(null, xhr.responseText) : cb({status:xhr.status, errorMessage:errInfo}, null); + } + }; + xhr.onerror = function(){ + cb({status:xhr.status, errorMessage:errInfo}, null); + }; + } + xhr.send(null); + }, + _loadTxtSync: function (url) { + var xhr = this.getXMLHttpRequest(); + xhr.open("GET", url, false); + if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) { + // IE-specific logic here + xhr.setRequestHeader("Accept-Charset", "utf-8"); + } else { + if (xhr.overrideMimeType) xhr.overrideMimeType("text\/plain; charset=utf-8"); + } + xhr.send(null); + if (!xhr.readyState === 4 || !(xhr.status === 200 || xhr.status === 0)) { + return null; + } + return xhr.responseText; + }, + + loadCsb: function(url, cb){ + var xhr = new XMLHttpRequest(), + errInfo = "load " + url + " failed!"; + xhr.open("GET", url, true); + xhr.responseType = "arraybuffer"; + + xhr.onload = function () { + var arrayBuffer = xhr.response; // Note: not oReq.responseText + if (arrayBuffer) { + window.msg = arrayBuffer; + } + if(xhr.readyState === 4) + xhr.status === 200 ? cb(null, xhr.response) : cb({status:xhr.status, errorMessage:errInfo}, null); + }; + xhr.onerror = function(){ + cb({status:xhr.status, errorMessage:errInfo}, null); + }; + xhr.send(null); + }, + + /** + * Load a single resource as json. + * + * @method loadJson + * @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. + * + * @method loadImg + * @param {String} url + * @param {Object} [option] + * @param {Function} callback + * @returns {Image} + */ + loadImg: function (url, option, callback) { + var opt = { + isCrossOrigin: true + }; + if (callback !== undefined) + opt.isCrossOrigin = option.isCrossOrigin === null ? opt.isCrossOrigin : option.isCrossOrigin; + else if (option !== undefined) + callback = option; + + var img = this.getRes(url); + if (img) { + callback && callback(null, img); + return img; + } + + img = new Image(); + if (opt.isCrossOrigin && location.origin !== "file://") + img.crossOrigin = "Anonymous"; + + var loadCallback = function () { + img.removeEventListener('load', loadCallback, false); + img.removeEventListener('error', errorCallback, false); + + if (callback) + callback(null, img); + }; + + var self = this; + var errorCallback = function () { + img.removeEventListener('load', loadCallback, false); + img.removeEventListener('error', errorCallback, false); + + if(img.crossOrigin && img.crossOrigin.toLowerCase() === "anonymous"){ + opt.isCrossOrigin = false; + self.release(url); + cc.loader.loadImg(url, opt, callback); + }else{ + typeof callback === "function" && callback("load image failed"); + } + }; + + img.addEventListener("load", loadCallback); + img.addEventListener("error", errorCallback); + img.src = url; + return img; + }, + + /** + * Iterator function to load res. + * + * @method _loadResIterator + * @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 + "] not exists!"); + 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. + * + * @method getUrl + * @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. + * + * @method load + * @param {String} resources + * @param {Function} [option] - callback or trigger + * @param {Function|Object} [loadCallback] + * @return {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, 0, + 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
+ *

+ *

+ *

+ *

+ * + * @method loadAliases + * @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. + * + * @method register + * @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. + * + * @method getRes + * @param url + * @returns {*} + */ + getRes: function (url) { + return this.cache[url] || this.cache[_aliases[url]]; + }, + + /** + * Get aliase by url. + * + * @method getAliase + * @param url + * @returns {*} + */ + getAliase: function (url) { + return _aliases[url]; + }, + + /** + * Release the cache of resource by url. + * + * @method release + * @param url + */ + release: function (url) { + var cache = this.cache; + delete cache[url]; + delete cache[_aliases[url]]; + delete _aliases[url]; + }, + + /** + * Resource cache of all resources. + * + * @method releaseAll + */ + releaseAll: function () { + var locCache = this.cache; + for (var key in locCache) + delete locCache[key]; + for (var key in _aliases) + delete _aliases[key]; + } + }; +})(); \ No newline at end of file diff --git a/cocos2d/core/platform/CCLoaders.js b/cocos2d/core/platform/CCLoaders.js new file mode 100644 index 00000000000..73ed0714375 --- /dev/null +++ b/cocos2d/core/platform/CCLoaders.js @@ -0,0 +1,156 @@ +/**************************************************************************** + 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){ + cc.loader.cache[url] = cc.loader.loadImg(realUrl, function(err, img){ + if(err) + return cb(err); + cc.textureCache.handleLoadedTexture(url); + cb(null, img); + }); + } +}; +cc.loader.register(["png", "jpg", "bmp","jpeg","gif", "ico", "tiff", "webp"], cc._imgLoader); +cc._serverImgLoader = { + load : function(realUrl, url, res, cb){ + cc.loader.cache[url] = cc.loader.loadImg(res.src, function(err, img){ + if(err) + return cb(err); + cc.textureCache.handleLoadedTexture(url); + cb(null, img); + }); + } +}; +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.js.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/cocos2d/core/platform/CCMacro.js b/cocos2d/core/platform/CCMacro.js new file mode 100644 index 00000000000..5fbc64ac6e7 --- /dev/null +++ b/cocos2d/core/platform/CCMacro.js @@ -0,0 +1,742 @@ +/**************************************************************************** + 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 + * @method swap + * @deprecated since v3.0 + */ +cc.swap = function (x, y, ref) { + if (cc.js.isObject(ref) && !cc.js.isUndefined(ref.x) && !cc.js.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 + * @method lerp + * @example {@link utils/api/cocos/docs/cocos2d/core/platform/CCMacro/.js} + */ +cc.lerp = function (a, b, r) { + return a + (b - a) * r; +}; + +/** + * get a random number from 0 to 0xffffff + * @method rand + * @returns {Number} + */ +cc.rand = function () { + return Math.random() * 0xffffff; +}; + +/** + * returns a random float between -1 and 1 + * @return {Number} + * @method randomMinus1To1 + */ +cc.randomMinus1To1 = function () { + return (Math.random() - 0.5) * 2; +}; + +/** + * returns a random float between 0 and 1 + * @return {Number} + * @method random0To1 + */ +cc.random0To1 = Math.random; + +/** + * converts degrees to radians + * @param {Number} angle + * @return {Number} + * @method degreesToRadians + */ +cc.degreesToRadians = function (angle) { + return angle * cc.RAD; +}; + +/** + * converts radians to degrees + * @param {Number} angle + * @return {Number} + * @method radiansToDegrees + */ +cc.radiansToDegrees = function (angle) { + return angle * cc.DEG; +}; +/** + * converts radians to degrees + * @param {Number} angle + * @return {Number} + * @method radiansToDegress + */ +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 {ENode} node setup node + * @method nodeDrawSetup + */ +cc.nodeDrawSetup = function (node) { + //cc.glEnable(node._glServerState); + if (node._shaderProgram) { + //cc._renderContext.useProgram(node._shaderProgram._programObj); + node._shaderProgram.use(); + node._shaderProgram.setUniformForModelViewAndProjectionMatrixWithMat4(); + } +}; + +/** + *

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

+ * @method enableDefaultGLStates + */ +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
+ *

+ * @method disableDefaultGLStates + */ +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 + * @method incrementGLDraws + */ +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} + * @method contentScaleFactor + */ +cc.contentScaleFactor = cc.IS_RETINA_DISPLAY_SUPPORTED ? function () { + return cc.director.getContentScaleFactor(); +} : function () { + return 1; +}; + +/** + * Converts a Point in points to pixels + * @param {Vec2} points + * @return {Vec2} + * @method pointPointsToPixels + */ +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 {Rect} pixels + * @return {Vec2} + * @method pointPixelsToPoints + */ +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 {Size} sizeInPoints + * @return {Size} + * @method sizePointsToPixels + */ +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 {Size} sizeInPixels + * @return {Size} + * @method sizePixelsToPoints + */ +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 {Rect} pixel + * @return {Rect} + * @method rectPixelsToPoints + */ +cc.rectPixelsToPoints = cc.IS_RETINA_DISPLAY_SUPPORTED ? function (pixel) { + var scale = cc.contentScaleFactor(); + return cc.rect(pixel.x / scale, pixel.y / scale, + pixel.width / scale, pixel.height / scale); +} : function (p) { + return cc.rect(p); +}; + +/** + * Converts a rect in points to pixels + * @param {Rect} point + * @return {Rect} + * @method rectPointsToPixels + */ +cc.rectPointsToPixels = cc.IS_RETINA_DISPLAY_SUPPORTED ? function (point) { + var scale = cc.contentScaleFactor(); + return cc.rect(point.x * scale, point.y * scale, + point.width * scale, point.height * scale); +} : function (p) { + return cc.rect(p); +}; + +//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; + +/** + * default gl blend src function. Compatible with premultiplied alpha images. + * @constant + * @name BLEND_SRC + * @type {Number} + */ +cc.defineGetterSetter(cc, "BLEND_SRC", function (){ + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL + && cc.OPTIMIZE_BLEND_FUNC_FOR_PREMULTIPLIED_ALPHA) { + return cc.ONE; + } + else { + return cc.SRC_ALPHA; + } +}); + +/** + * default gl blend dst function. Compatible with premultiplied alpha images. + * @constant + * @type {Number} + */ +cc.BLEND_DST = 0x0303; + +/** + * Check webgl error.Error will be shown in console if exists. + * @method checkGLErrorDebug + */ +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.DEVICE_ORIENTATION_PORTRAIT = 0; + +/** + * Device oriented horizontally, home button on the right (UIDeviceOrientationLandscapeLeft) + * @constant + * @type {Number} + */ +cc.DEVICE_ORIENTATION_LANDSCAPE_LEFT = 1; + +/** + * Device oriented vertically, home button on the top (UIDeviceOrientationPortraitUpsideDown) + * @constant + * @type {Number} + */ +cc.DEVICE_ORIENTATION_PORTRAIT_UPSIDE_DOWN = 2; + +/** + * Device oriented horizontally, home button on the left (UIDeviceOrientationLandscapeRight) + * @constant + * @type {Number} + */ +cc.DEVICE_ORIENTATION_LANDSCAPE_RIGHT = 3; + +/** + * In browsers, we only support 2 orientations by change window size. + * @constant + * @type {Number} + */ +cc.DEVICE_MAX_ORIENTATIONS = 2; + + +// ------------------- 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 = 3; + +//------------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_POSITION_TEXTURECOLORALPHATEST = "ShaderPositionTextureColorAlphaTest"; +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_COLOR = "ShaderPositionColor"; +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_TEXTURE = "ShaderPositionTexture"; +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_TEXTURE_UCOLOR = "ShaderPositionTexture_uColor"; +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_TEXTUREA8COLOR = "ShaderPositionTextureA8Color"; +/** + * @constant + * @type {String} + */ +cc.SHADER_POSITION_UCOLOR = "ShaderPosition_uColor"; +/** + * @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"; + + +/** + * 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; diff --git a/cocos2d/core/platform/CCObject.js b/cocos2d/core/platform/CCObject.js new file mode 100644 index 00000000000..0ecb2bacce6 --- /dev/null +++ b/cocos2d/core/platform/CCObject.js @@ -0,0 +1,362 @@ +var JS = require('./js'); + +// definitions for CCObject.Flags + +var Destroyed = 1 << 0; +var ToDestroy = 1 << 1; +var DontSave = 1 << 2; +var EditorOnly = 1 << 3; +var Dirty = 1 << 4; +var DontDestroy = 1 << 5; +var Destroying = 1 << 6; +//var RegisteredInEditor = 1 << 8; +var HideInGame = 1 << 9; +var HideInEditor = 1 << 10; + +var IsOnEnableCalled = 1 << 12; +var IsOnLoadCalled = 1 << 13; +var IsOnStartCalled = 1 << 14; + +var Hide = HideInGame | HideInEditor; +// should not clone or serialize these flags +var PersistentMask = ~(ToDestroy | Dirty | Destroying | DontDestroy | + IsOnEnableCalled | IsOnLoadCalled | IsOnStartCalled + /*RegisteredInEditor*/); + +/** + * The base class of most of all the objects in Fireball. + * @class CCObject + * @constructor + */ +function CCObject () { + /** + * @property _name + * @type string + * @default "" + * @private + */ + this._name = ''; + + /** + * @property _objFlags + * @type number + * @default 0 + * @private + */ + this._objFlags = 0; +} + +/** + * Bit mask that controls object states. + * @class Flags + * @static + * @private + */ +CCObject.Flags = { + + //Destroyed: Destroyed, + //ToDestroy: ToDestroy, + + /** + * The object will not be saved. + * @property DontSave + * @type {Number} + */ + DontSave: DontSave, + + /** + * The object will not be saved when building a player. + * @property EditorOnly + * @type {Number} + */ + EditorOnly: EditorOnly, + + Dirty: Dirty, + + /** + * Dont destroy automatically when loading a new scene. + * @property DontDestroy + * @private + */ + DontDestroy: DontDestroy, + + PersistentMask: PersistentMask, + + // FLAGS FOR ENGINE + + Destroying: Destroying, + + /** + * Hide in game and hierarchy. + * This flag is readonly, it can only be used as an argument of scene.addEntity() or Entity.createWithFlags() + * @property HideInGame + * @type {Number} + */ + HideInGame: HideInGame, + + // FLAGS FOR EDITOR + + /** + * This flag is readonly, it can only be used as an argument of scene.addEntity() or Entity.createWithFlags() + * @property HideInEditor + * @type {Number} + */ + HideInEditor: HideInEditor, + + /** + * Hide in game view, hierarchy, and scene view... etc. + * This flag is readonly, it can only be used as an argument of scene.addEntity() or Entity.createWithFlags() + * @property Hide + * @type {Number} + */ + Hide: Hide, + + //// UUID Registered in editor + //RegisteredInEditor: RegisteredInEditor, + + // FLAGS FOR COMPONENT + + IsOnLoadCalled: IsOnLoadCalled, + IsOnEnableCalled: IsOnEnableCalled, + IsOnStartCalled: IsOnStartCalled +}; + +require('./CCClass').fastDefine('cc.Object', CCObject, ['_name', '_objFlags']); + +// internal static + +var objectsToDestroy = []; + +function deferredDestroy () { + var deleteCount = objectsToDestroy.length; + for (var i = 0; i < deleteCount; ++i) { + var obj = objectsToDestroy[i]; + if (!(obj._objFlags & Destroyed)) { + obj._destroyImmediate(); + } + } + // if we called b.destory() in a.onDestroy(), objectsToDestroy will be resized, + // but we only destroy the objects which called destory in this frame. + if (deleteCount === objectsToDestroy.length) { + objectsToDestroy.length = 0; + } + else { + objectsToDestroy.splice(0, deleteCount); + } + + if (CC_EDITOR) { + deferredDestroyTimer = null; + } +} + +Object.defineProperty(CCObject, '_deferredDestroy', { + value: deferredDestroy, + // enumerable is false by default +}); + +if (CC_EDITOR) { + Object.defineProperty(CCObject, '_clearDeferredDestroyTimer', { + value: function () { + if (deferredDestroyTimer !== null) { + clearImmediate(deferredDestroyTimer); + deferredDestroyTimer = null; + } + }, + enumerable: false + }); +} + +// MEMBER + +var prototype = CCObject.prototype; + +/** + * The name of the object. + * @property name + * @type {String} + * @default "" + */ +JS.getset(prototype, 'name', + function () { + return this._name; + }, + function (value) { + this._name = value; + } +); + +/** + * Indicates whether the object is not yet destroyed + * @property isValid + * @type {Boolean} + * @default true + * @readOnly + */ +JS.get(prototype, 'isValid', function () { + return !(this._objFlags & Destroyed); +}); + +var deferredDestroyTimer = null; + +/** + * Destroy this Object, and release all its own references to other objects. + * + * After destroy, this CCObject is not usable any more. + * You can use cc.isValid(obj) (or obj.isValid if obj is non-nil) to check whether the object is destroyed before + * accessing it. + * + * @method destroy + * @return {Boolean} whether it is the first time the destroy being called + */ +prototype.destroy = function () { + if (this._objFlags & Destroyed) { + cc.warn('object already destroyed'); + return false; + } + if (this._objFlags & ToDestroy) { + return false; + } + this._objFlags |= ToDestroy; + objectsToDestroy.push(this); + + if (deferredDestroyTimer === null && cc.engine && ! cc.engine._isUpdating && CC_EDITOR) { + // auto destroy immediate in edit mode + deferredDestroyTimer = setImmediate(deferredDestroy); + } + return true; +}; + +if (CC_EDITOR || CC_TEST) { + /** + * In fact, Object's "destroy" will not trigger the destruct operation in Firebal Editor. + * The destruct operation will be executed by Undo system later. + * + * @method realDestroyInEditor + */ + prototype.realDestroyInEditor = function () { + if (this._objFlags & Destroyed) { + cc.warn('object already destroyed'); + return false; + } + if (this._objFlags & ToDestroy) { + return false; + } + this._objFlags |= ToDestroy; + objectsToDestroy.push(this); + + if (deferredDestroyTimer === null && cc.engine && ! cc.engine._isUpdating && CC_EDITOR) { + // auto destroy immediate in edit mode + deferredDestroyTimer = setImmediate(deferredDestroy); + } + return true; + }; +} + +/** + * Clear all references in the instance. + * + * NOTE: this method will not clear the getter or setter functions which defined in the INSTANCE of CCObject. + * You can override the _destruct method if you need. + * @method _destruct + * @private + */ +prototype._destruct = function () { + if (CC_EDITOR && !(this._objFlags & Destroyed)) { + return cc.error('object not yet destroyed'); + } + // 所有å¯æžšä¸¾åˆ°çš„属性,都会被清空 + for (var key in this) { + if (this.hasOwnProperty(key)) { + switch (typeof this[key]) { + case 'string': + this[key] = ''; + break; + case 'object': + case 'function': + this[key] = null; + break; + } + } + } +}; + +/** + * Called before the object being destroyed. + * @method _onPreDestroy + * @private + */ +prototype._onPreDestroy = null; + +prototype._destroyImmediate = function () { + if (this._objFlags & Destroyed) { + cc.error('object already destroyed'); + return; + } + // engine internal callback + if (this._onPreDestroy) { + this._onPreDestroy(); + } + + if (!CC_EDITOR || cc.engine._isPlaying) { + // 这里 _destruct 将由编辑器进行调用 + this._destruct(); + } + + // mark destroyed + this._objFlags |= Destroyed; +}; + +if (CC_EDITOR) { + /** + * The customized serialization for this object. (Editor Only) + * @method _serialize + * @param {Boolean} exporting + * @return {object} the serialized json data object + * @private + */ + prototype._serialize = null; +} + +/** + * Init this object from the custom serialized data. + * @method _deserialize + * @param {Object} data - the serialized json data + * @param {_Deserializer} ctx + * @private + */ +prototype._deserialize = null; + +/** + * Checks whether the object is non-nil and not yet destroyed + * @method isValid + * @param {Object|any} value + * @return {Boolean} whether is valid + */ +cc.isValid = function (value) { + if (typeof value === 'object') { + return !!value && !(value._objFlags & Destroyed); + } + else { + return typeof value !== 'undefined'; + } +}; + +if (CC_EDITOR || CC_TEST) { + Object.defineProperty(CCObject, '_willDestroy', { + value: function (obj) { + return !(obj._objFlags & Destroyed) && (obj._objFlags & ToDestroy) > 0; + } + }); + Object.defineProperty(CCObject, '_cancelDestroy', { + value: function (obj) { + obj._objFlags &= ~ToDestroy; + var index = objectsToDestroy.indexOf(obj); + if (index !== -1) { + objectsToDestroy.splice(index, 1); + } + } + }); +} + +cc.Object = CCObject; +module.exports = CCObject; diff --git a/cocos2d/core/platform/CCSAXParser.js b/cocos2d/core/platform/CCSAXParser.js new file mode 100644 index 00000000000..eb3c7b5b83f --- /dev/null +++ b/cocos2d/core/platform/CCSAXParser.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. + ****************************************************************************/ + +/** + * A SAX Parser + * @class saxParser + * @extends _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; + } + }, + + /** + * @method parse + * @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 plistParser + * @extends 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(); +/** + * @type {PlistParser} + * @name plistParser + * A Plist Parser + */ +cc.plistParser = new cc.PlistParser(); diff --git a/cocos2d/core/platform/CCScreen.js b/cocos2d/core/platform/CCScreen.js new file mode 100644 index 00000000000..11c44fff2b7 --- /dev/null +++ b/cocos2d/core/platform/CCScreen.js @@ -0,0 +1,164 @@ +/**************************************************************************** + 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 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 + * @method init + */ + 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. + * @method fullScreen + * @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. + * @method requestFullScreen + * @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. + * @method exitFullScreen + * @return {Boolean} + */ + exitFullScreen: function () { + return this._supportsFullScreen ? document[this._fn.exitFullscreen]() : true; + }, + + /** + * Automatically request full screen with a touch/click event + * @method autoFullScreen + * @param {Element} element + * @param {Function} onFullScreenChange + */ + autoFullScreen: function (element, onFullScreenChange) { + element = element || document.body; + var touchTarget = cc._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() { + theScreen.requestFullScreen(element, onFullScreenChange); + touchTarget.removeEventListener(theScreen._touchEvent, callback); + } + this.requestFullScreen(element, onFullScreenChange); + touchTarget.addEventListener(this._touchEvent, callback); + } +}; +cc.screen.init(); diff --git a/cocos2d/core/platform/CCSys.js b/cocos2d/core/platform/CCSys.js new file mode 100644 index 00000000000..7acfe064137 --- /dev/null +++ b/cocos2d/core/platform/CCSys.js @@ -0,0 +1,675 @@ +if (cc.sys) return; + +/** + * System variables + * @class sys + * @static + */ +cc.sys = {}; +var sys = cc.sys; + +/** + * English language code + * @property LANGUAGE_ENGLISH + * @type {String} + * @readOnly + */ +sys.LANGUAGE_ENGLISH = "en"; + +/** + * Chinese language code + * @property LANGUAGE_CHINESE + * @type {String} + * @readOnly + */ +sys.LANGUAGE_CHINESE = "zh"; + +/** + * French language code + * @property LANGUAGE_FRENCH + * @type {String} + * @readOnly + */ +sys.LANGUAGE_FRENCH = "fr"; + +/** + * Italian language code + * @property LANGUAGE_ITALIAN + * @type {String} + * @readOnly + */ +sys.LANGUAGE_ITALIAN = "it"; + +/** + * German language code + * @property LANGUAGE_GERMAN + * @type {String} + * @readOnly + */ +sys.LANGUAGE_GERMAN = "de"; + +/** + * Spanish language code + * @property LANGUAGE_SPANISH + * @type {String} + * @readOnly + */ +sys.LANGUAGE_SPANISH = "es"; + +/** + * Spanish language code + * @property LANGUAGE_DUTCH + * @type {String} + * @readOnly + */ +sys.LANGUAGE_DUTCH = "du"; + +/** + * Russian language code + * @property LANGUAGE_RUSSIAN + * @type {String} + * @readOnly + */ +sys.LANGUAGE_RUSSIAN = "ru"; + +/** + * Korean language code + * @property LANGUAGE_KOREAN + * @type {String} + * @readOnly + */ +sys.LANGUAGE_KOREAN = "ko"; + +/** + * Japanese language code + * @property LANGUAGE_JAPANESE + * @type {String} + * @readOnly + */ +sys.LANGUAGE_JAPANESE = "ja"; + +/** + * Hungarian language code + * @property LANGUAGE_HUNGARIAN + * @constant + * @type {String} + * @readOnly + */ +sys.LANGUAGE_HUNGARIAN = "hu"; + +/** + * Portuguese language code + * @property LANGUAGE_PORTUGUESE + * @type {String} + * @readOnly + */ +sys.LANGUAGE_PORTUGUESE = "pt"; + +/** + * Arabic language code + * @property LANGUAGE_ARABIC + * @type {String} + * @readOnly + */ +sys.LANGUAGE_ARABIC = "ar"; + +/** + * Norwegian language code + * @property LANGUAGE_NORWEGIAN + * @type {String} + * @readOnly + */ +sys.LANGUAGE_NORWEGIAN = "no"; + +/** + * Polish language code + * @property LANGUAGE_POLISH + * @type {String} + * @readOnly + */ +sys.LANGUAGE_POLISH = "pl"; + +/** + * Unknown language code + * @property LANGUAGE_UNKNOWN + * @type {String} + * @readOnly + */ +sys.LANGUAGE_UNKNOWN = "unkonwn"; + +/** + * @property OS_IOS + * @type {String} + * @readOnly + */ +sys.OS_IOS = "iOS"; +/** + * @property OS_ANDROID + * @type {String} + * @readOnly + */ +sys.OS_ANDROID = "Android"; +/** + * @property OS_WINDOWS + * @type {String} + * @readOnly + */ +sys.OS_WINDOWS = "Windows"; +/** + * @property OS_MARMALADE + * @type {String} + * @readOnly + */ +sys.OS_MARMALADE = "Marmalade"; +/** + * @property OS_LINUX + * @type {String} + * @readOnly + */ +sys.OS_LINUX = "Linux"; +/** + * @property OS_BADA + * @type {String} + * @readOnly + */ +sys.OS_BADA = "Bada"; +/** + * @property OS_BLACKBERRY + * @type {String} + * @readOnly + */ +sys.OS_BLACKBERRY = "Blackberry"; +/** + * @property OS_OSX + * @type {String} + * @readOnly + */ +sys.OS_OSX = "OS X"; +/** + * @property OS_WP8 + * @type {String} + * @readOnly + */ +sys.OS_WP8 = "WP8"; +/** + * @property OS_WINRT + * @type {String} + * @readOnly + */ +sys.OS_WINRT = "WINRT"; +/** + * @property OS_UNKNOWN + * @type {String} + * @readOnly + */ +sys.OS_UNKNOWN = "Unknown"; + +/** + * @property UNKNOWN + * @type {Number} + * @readOnly + * @default -1 + */ +sys.UNKNOWN = -1; +/** + * @property WIN32 + * @type {Number} + * @readOnly + * @default 0 + */ +sys.WIN32 = 0; +/** + * @property LINUX + * @type {Number} + * @readOnly + * @default 1 + */ +sys.LINUX = 1; +/** + * @property MACOS + * @type {Number} + * @readOnly + * @default 2 + */ +sys.MACOS = 2; +/** + * @property ANDROID + * @type {Number} + * @readOnly + * @default 3 + */ +sys.ANDROID = 3; +/** + * @property IOS + * @type {Number} + * @readOnly + * @default 4 + */ +sys.IPHONE = 4; +/** + * @property IPAD + * @type {Number} + * @readOnly + * @default 5 + */ +sys.IPAD = 5; +/** + * @property BLACKBERRY + * @type {Number} + * @readOnly + * @default 6 + */ +sys.BLACKBERRY = 6; +/** + * @property NACL + * @type {Number} + * @readOnly + * @default 7 + */ +sys.NACL = 7; +/** + * @property EMSCRIPTEN + * @type {Number} + * @readOnly + * @default 8 + */ +sys.EMSCRIPTEN = 8; +/** + * @property TIZEN + * @type {Number} + * @readOnly + * @default 9 + */ +sys.TIZEN = 9; +/** + * @property WINRT + * @type {Number} + * @readOnly + * @default 10 + */ +sys.WINRT = 10; +/** + * @property WP8 + * @type {Number} + * @readOnly + * @default 11 + */ +sys.WP8 = 11; +/** + * @property MOBILE_BROWSER + * @type {Number} + * @readOnly + * @default 100 + */ +sys.MOBILE_BROWSER = 100; +/** + * @property DESKTOP_BROWSER + * @type {Number} + * @readOnly + * @default 101 + */ +sys.DESKTOP_BROWSER = 101; + +/** + * Indicates whether executes in editor's window process (Electron's renderer context) + * @property EDITOR_PAGE + * @type {Number} + * @readOnly + * @default 102 + */ +sys.EDITOR_PAGE = 102; +/** + * Indicates whether executes in editor's main process (Electron's browser context) + * @property EDITOR_CORE + * @type {Number} + * @readOnly + * @default 103 + */ +sys.EDITOR_CORE = 103; + +sys.BROWSER_TYPE_WECHAT = "wechat"; +sys.BROWSER_TYPE_ANDROID = "androidbrowser"; +sys.BROWSER_TYPE_IE = "ie"; +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. + * @property isNative + * @type {Boolean} + */ +sys.isNative = false; + +/** + * Is web browser ? + * @property isBrowser + * @type {Boolean} + */ +sys.isBrowser = typeof window === 'object' && typeof document === 'object'; + +if (typeof Editor !== 'undefined' && Editor.isCoreLevel) { + sys.isMobile = false; + sys.platform = sys.EDITOR_CORE; + sys.language = sys.LANGUAGE_UNKNOWN; + sys.os = ({ + darwin: sys.OS_OSX, + win32: sys.OS_WINDOWS, + linux: sys.OS_LINUX + })[process.platform] || sys.OS_UNKNOWN; + sys.browserType = null; + sys.browserVersion = null; + sys.windowPixelResolution = { + width: 0, + height: 0 + }; +} +else { + // browser or runtime + var win = window, nav = win.navigator, doc = document, docEle = doc.documentElement; + var ua = nav.userAgent.toLowerCase(); + + if (cc.isEditor) { + sys.isMobile = false; + sys.platform = sys.EDITOR_PAGE; + } + else { + /** + * Indicate whether system is mobile system + * @property isMobile + * @type {Boolean} + */ + sys.isMobile = ua.indexOf('mobile') !== -1 || ua.indexOf('android') !== -1; + + /** + * Indicate the running platform + * @property 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 + * @property language + * @type {String} + */ + sys.language = currLanguage; + + // Get the os of system + var iOS = ( ua.match(/(iPad|iPhone|iPod)/i) ? true : false ); + var isAndroid = ua.match(/android/i) || nav.platform.match(/android/i) ? true : false; + 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 + * @property os + * @type {String} + */ + sys.os = osName; + + /** + * Indicate the running browser type + * @property browserType + * @type {String} + */ + sys.browserType = sys.BROWSER_TYPE_UNKNOWN; + /* Determine the browser type */ + (function(){ + var typeReg1 = /sogou|qzone|liebao|micromessenger|ucbrowser|360 aphone|360browser|baiduboxapp|baidubrowser|maxthon|mxbrowser|trident|miuibrowser/i; + var typeReg2 = /qqbrowser|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" && (ua.match(/android.*applewebkit/))) + 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 + * @property browserVersion + * @type {Number} + */ + sys.browserVersion = ""; + /* Determine the browser version number */ + (function(){ + var versionReg1 = /(micromessenger|mx|maxthon|baidu|sogou)(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 + * @property windowPixelResolution + * @type {Number} + */ + 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."); + }; + + 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; + }; + + //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. + * @property 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; + var tmpCanvas = document.createElement("CANVAS"); + if (win.WebGLRenderingContext) { + try{ + var context = cc.create3DContext(tmpCanvas, {'stencil': true, 'preserveDrawingBuffer': true }); + if(context) { + _supportWebGL = true; + } + } + catch (e) {} + } + + /** + * The capabilities of the current platform + * @property 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; + + delete _tmpCanvas1; + delete _tmpCanvas2; +} + +/** + * Forces the garbage collection, only available in JSB + * @method garbageCollect + */ +sys.garbageCollect = function () { + // N/A in cocos2d-html5 +}; + +/** + * Dumps rooted objects, only available in JSB + * @method dumpRoot + */ +sys.dumpRoot = function () { + // N/A in cocos2d-html5 +}; + +/** + * Restart the JS VM, only available in JSB + * @method restartVM + */ +sys.restartVM = function () { + // N/A in cocos2d-html5 +}; + +/** + * Clean a script in the JS VM, only available in JSB + * @method cleanScript + * @param {String} jsfile + */ +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 + * @method isObjectValid + * @param {Object} obj + * @return {Boolean} Validity of the object + */ +sys.isObjectValid = function (obj) { + if (obj) return true; + else return false; +}; + +/** + * Dump system informations + * @method dump + */ +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 += "capabilities : " + JSON.stringify(self.capabilities) + "\r\n"; + str += "os : " + self.os + "\r\n"; + str += "platform : " + self.platform + "\r\n"; + cc.log(str); +}; + +/** + * Open a url in browser + * @method openURL + * @param {String} url + */ +sys.openURL = function(url){ + window.open(url); +}; + +module.exports = sys; \ No newline at end of file diff --git a/cocos2d/core/platform/CCVisibleRect.js b/cocos2d/core/platform/CCVisibleRect.js new file mode 100644 index 00000000000..4cdc1489803 --- /dev/null +++ b/cocos2d/core/platform/CCVisibleRect.js @@ -0,0 +1,154 @@ +/**************************************************************************** + 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() + * + * @class 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 {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; + } +}; + +/** + * Top left coordinate of the screen related to the game scene. + * @property topLeft + * @type {Vec2} + */ + +/** + * Top right coordinate of the screen related to the game scene. + * @property topRight + * @type {Vec2} + */ + +/** + * Top center coordinate of the screen related to the game scene. + * @property top + * @type {Vec2} + */ + +/** + * Bottom left coordinate of the screen related to the game scene. + * @property bottomLeft + * @type {Vec2} + */ + +/** + * Bottom right coordinate of the screen related to the game scene. + * @property bottomRight + * @type {Vec2} + */ + +/** + * Bottom center coordinate of the screen related to the game scene. + * @property bottom + * @type {Vec2} + */ + +/** + * Center coordinate of the screen related to the game scene. + * @property center + * @type {Vec2} + */ + +/** + * Left center coordinate of the screen related to the game scene. + * @property left + * @type {Vec2} + */ + +/** + * Right center coordinate of the screen related to the game scene. + * @property right + * @type {Vec2} + */ + +/** + * Width of the screen. + * @property width + * @type {Number} + */ + +/** + * Height of the screen. + * @property height + * @type {Number} + */ + diff --git a/cocos2d/core/platform/_CCClass.js b/cocos2d/core/platform/_CCClass.js new file mode 100644 index 00000000000..a69d303c33d --- /dev/null +++ b/cocos2d/core/platform/_CCClass.js @@ -0,0 +1,217 @@ +/**************************************************************************** + 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. + ****************************************************************************/ + +/** + * @namespace + * @name ClassManager + */ +var ClassManager = cc.ClassManager = { + id : (0|(Math.random()*998)), + + instanceId : (0|(Math.random()*998)), + + getNewID : function(){ + return this.id++; + }, + + getNewInstanceId : function(){ + return this.instanceId++; + } +}; + +/* Managed JavaScript Inheritance + * Based on John Resig's Simple JavaScript Inheritance http://ejohn.org/blog/simple-javascript-inheritance/ + * MIT Licensed. + */ +var fnTest = /\b_super\b/; + +/** + * The base Class implementation (does nothing) + * @class Class + */ +var Class = function () { +}; + +/** + * Create a new Class that inherits from this Class + * @static + * @param {Object} props + * @return {Function} + */ +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); + + var classId = ClassManager.getNewID(); + ClassManager[classId] = _super; + // Copy the properties over onto the new prototype. We make function + // properties non-eumerable as this makes typeof === 'function' check + // unneccessary 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 }; + + prototype.__instanceId = null; + + // The dummy Class constructor + function _Class() { + this.__instanceId = ClassManager.getNewInstanceId(); + // All construction is actually done in the init method + if (this.ctor) + this.ctor.apply(this, arguments); + } + + _Class.id = classId; + // desc = { writable: true, enumerable: false, configurable: true, + // value: XXX }; Again, we make this non-enumerable. + desc.value = classId; + Object.defineProperty(prototype, '__cid__', desc); + + // Populate our constructed prototype object + _Class.prototype = prototype; + + // Enforce the constructor to be what we expect + desc.value = _Class; + Object.defineProperty(_Class.prototype, 'constructor', desc); + + 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]; + } + } + } + + // And make this Class extendable + _Class.extend = Class.extend; + + //add implementation method + _Class.implement = function (prop) { + for (var name in prop) { + prototype[name] = prop[name]; + } + }; + return _Class; +}; + +/** + * Common getter setter configuration function + * @method defineGetterSetter + * @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"); + } +}; + +/** + * Create a new object and copy all properties in an exist object to the new object + * @method clone + * @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 + // becasue 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._Class = module.exports = Class; \ No newline at end of file diff --git a/cocos2d/core/platform/attribute.js b/cocos2d/core/platform/attribute.js new file mode 100644 index 00000000000..545486266af --- /dev/null +++ b/cocos2d/core/platform/attribute.js @@ -0,0 +1,301 @@ +/**************************************************************************** + 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 JS = require('./js'); +var isPlainEmptyObj = require('./utils').isPlainEmptyObj_DEV; + +/** + * Tag the class with any meta attributes, then return all current attributes assigned to it. + * This function holds only the attributes, not their implementations. + * + * @method attr + * @param {Function|Object} constructor - the class or instance. If instance, the attribute will be dynamic and only available for the specified instance. + * @param {String} propertyName - the name of property or function, used to retrieve the attributes + * @param {Object} [attributes] - the attribute table to mark, new attributes will merged with existed attributes. Attribute whose key starts with '_' will be ignored. + * @return {Object|Undefined} return all attributes associated with the property. if none undefined will be returned + * @example {@link utils/api/cocos/docs/cocos2d/core/platform/attribute/attr.js} + */ +function attr (constructor, propertyName, attributes) { + var key = '_attr$' + propertyName; + var instance, attrs, name; + if (typeof constructor === 'function') { + // attributes in class + instance = constructor.prototype; + attrs = instance[key]; + if (typeof attributes !== 'undefined') { + // set + if (typeof attributes === 'object') { + if (!attrs) { + instance[key] = attrs = {}; + } + for (name in attributes) { + if (name[0] !== '_') { + attrs[name] = attributes[name]; + } + } + } + else { + instance[key] = attributes; + return attributes; + } + } + return attrs; + } + else { + // attributes in instance + instance = constructor; + if (typeof attributes !== 'undefined') { + // set + if (typeof attributes === 'object') { + if (instance.hasOwnProperty(key)) { + attrs = instance[key]; + } + if (!attrs) { + instance[key] = attrs = {}; + } + for (name in attributes) { + if (name[0] !== '_') { + attrs[name] = attributes[name]; + } + } + return JS.addon({}, attrs, instance.constructor.prototype[key]); + } + else { + instance[key] = attributes; + return attributes; + } + } + else { + // get + attrs = instance[key]; + if (typeof attrs === 'object') { + return JS.addon({}, attrs, instance.constructor.prototype[key]); + } + else { + return attrs; + } + } + } +} + +/* +BuiltinAttributes: { + default: defaultValue, + _canUsedInGetter: true, (default true) + _canUsedInSetter: false, (default false) (NYI) +} +Getter or Setter: { + hasGetter: true, + hasSetter: true, +} +Callbacks: { + _onAfterProp: function (constructor, propName) {}, + _onAfterGetter: function (constructor, propName) {}, (NYI) + _onAfterSetter: function (constructor, propName) {}, (NYI) +} + */ + +var NonSerialized = { + serializable: false, + _canUsedInGetter: false +}; + +var EditorOnly = { + editorOnly: true, + _canUsedInGetter: false +}; + +function getTypeChecker (type, attrName, objectTypeCtor) { + if (CC_DEV) { + return function (constructor, mainPropName) { + var mainPropAttrs = cc.Class.attr(constructor, mainPropName) || {}; + if (mainPropAttrs.type !== type) { + cc.warn('Can only indicate one type attribute for %s.%s.', JS.getClassName(constructor), + mainPropName); + return; + } + if (!mainPropAttrs.hasOwnProperty('default')) { + return; + } + var defaultVal = mainPropAttrs.default; + if (typeof defaultVal === 'undefined') { + return; + } + var isContainer = Array.isArray(defaultVal) || isPlainEmptyObj(defaultVal); + if (isContainer) { + return; + } + var defaultType = typeof defaultVal; + var type_lowerCase = type.toLowerCase(); + if (defaultType === type_lowerCase) { + if (type_lowerCase === 'object') { + if (defaultVal && !(defaultVal instanceof objectTypeCtor)) { + cc.warn('The default value of %s.%s is not instance of %s.', + JS.getClassName(constructor), mainPropName, JS.getClassName(objectTypeCtor)); + } + else { + return; + } + } + else { + cc.warn('No needs to indicate the "%s" attribute for %s.%s, which its default value is type of %s.', + attrName, JS.getClassName(constructor), mainPropName, type); + } + } + else { + cc.warn('Can not indicate the "%s" attribute for %s.%s, which its default value is type of %s.', + attrName, JS.getClassName(constructor), mainPropName, defaultType); + } + delete mainPropAttrs.type; + }; + } +} + +function ObjectType (typeCtor) { + return { + type: 'Object', + ctor: typeCtor, + // _onAfterProp: (function () { + // if (CC_DEV) { + // return function (classCtor, mainPropName) { + // var check = getTypeChecker('Object', 'ObjectType', typeCtor); + // check(classCtor, mainPropName); + // // check ValueType + // var mainPropAttrs = cc.Class.attr(classCtor, mainPropName) || {}; + // if (!Array.isArray(mainPropAttrs.default) && typeof typeCtor.prototype.clone === 'function') { + // var typename = JS.getClassName(typeCtor); + // var hasDefault = mainPropAttrs.default === null || mainPropAttrs.default === undefined; + // if (hasDefault) { + // cc.warn('%s is a ValueType, no need to specify the "type" of "%s.%s", ' + + // 'because the type information can obtain from its default value directly.', + // typename, JS.getClassName(classCtor), mainPropName, typename); + // } + // else { + // cc.warn('%s is a ValueType, no need to specify the "type" of "%s.%s", ' + + // 'just set the default value to "new %s()" and it will be handled properly.', + // typename, JS.getClassName(classCtor), mainPropName, typename); + // } + // } + // }; + // } + // else { + // return undefined; + // } + // })() + }; +} + +function RawType (typename) { + var NEED_EXT_TYPES = ['image', 'json', 'text', 'audio']; // the types need to specify exact extname + return { + // type: 'raw', + rawType: typename, + serializable: false, + // hideInInspector: true, + _canUsedInGetter: false, + + _onAfterProp: function (constructor, mainPropName) { + // check raw object + var checked = !CC_DEV || (function checkRawType(constructor) { + if (! cc.isChildClassOf(constructor, cc.Asset)) { + cc.error('RawType is only available for Assets'); + return false; + } + var found = false; + for (var p = 0; p < constructor.__props__.length; p++) { + var propName = constructor.__props__[p]; + var attrs = cc.Class.attr(constructor, propName); + var rawType = attrs.rawType; + if (rawType) { + var containsUppercase = (rawType.toLowerCase() !== rawType); + if (containsUppercase) { + cc.error('RawType name cannot contain uppercase'); + return false; + } + if (found) { + cc.error('Each asset cannot have more than one RawType'); + return false; + } + found = true; + } + } + return true; + })(constructor); + } + }; +} + +function Nullable (boolPropName, hasValueByDefault) { + return { + nullable: boolPropName, + + _onAfterProp: function (constructor, mainPropName) { + // declare boolean + constructor.prop(boolPropName, hasValueByDefault, { visible: false }); + // copy attributes from main property + var mainPropAttr = cc.Class.attr(constructor, mainPropName) || {}; + if (mainPropAttr.serializable === false) { + cc.Class.attr(constructor, boolPropName, NonSerialized); + } + else if (mainPropAttr.editorOnly) { + cc.Class.attr(constructor, boolPropName, EditorOnly); + } + } + }; +} + +// +// @method Watch +// @param {String} names - the name of target property to watch, array is also acceptable. +// @param {Function} callback - the callback function to invoke when target property(s) is changed. +// @param {Object} callback.param object - the instance object which contains watching property(s). +// @param {Object} callback.param element - the property element which displays watching property(s). +// @return {object} the attribute +// @private +// +function Watch (names, callback) { + return { + watch: [].concat(names), // array of property name to watch + watchCallback: callback + }; +} + +function Range (min, max) { + return { min: min, max: max }; +} + +module.exports = { + attr: attr, + getTypeChecker: getTypeChecker, + NonSerialized: NonSerialized, + EditorOnly: EditorOnly, + ObjectType: ObjectType, + RawType: RawType, + ScriptUuid: {}, // the value will be represented as a uuid string + Nullable: Nullable, + Watch: Watch, + Range: Range +}; diff --git a/cocos2d/core/platform/callbacks-invoker.js b/cocos2d/core/platform/callbacks-invoker.js new file mode 100644 index 00000000000..6bfa11c3101 --- /dev/null +++ b/cocos2d/core/platform/callbacks-invoker.js @@ -0,0 +1,245 @@ +var JS = require('./js'); + +/** + * The CallbacksHandler is an abstract class that can register and unregister callbacks by key. + * Subclasses should implement their own methods about how to invoke the callbacks. + * @class _CallbacksHandler + * @constructor + * @private + */ +var CallbacksHandler = (function () { + this._callbackTable = {}; +}); + +/** + * @method add + * @param {String} key + * @param {Function} callback - can be null + * @param {Object} target - can be null + * @return {Boolean} whether the key is new + */ +CallbacksHandler.prototype.add = function (key, callback, target) { + var list = this._callbackTable[key]; + if (typeof list !== 'undefined') { + if (typeof callback === 'function') { + if (list !== null) { + list.push(callback); + } + else { + list = [callback]; + this._callbackTable[key] = list; + } + // Just append the target after callback + if (typeof target === 'object') { + list.push(target); + } + } + return false; + } + else { + // new key + list = (typeof callback === 'function') ? [callback] : null; + // Just append the target after callback + if (list && typeof target === 'object') { + list.push(target); + } + this._callbackTable[key] = list; + return true; + } +}; + +/** + * Check if the specified key has any registered callback. If a callback is also specified, + * it will only return true if the callback is registered. + * @method has + * @param {String} key + * @param {Function} [callback] + * @param {Object} [target] + * @return {Boolean} + */ +CallbacksHandler.prototype.has = function (key, callback, target) { + var list = this._callbackTable[key], callbackTarget, index; + if (list && list.length > 0) { + // callback not given, but key found + if (!callback) + return true; + // wrong callback type, can't found anything + else if (typeof callback !== 'function') + return false; + // Search callback, target pair in the list + index = list.indexOf(callback); + while (index !== -1) { + callbackTarget = list[index+1]; + if (typeof callbackTarget !== 'object') { + callbackTarget = undefined; + } + if (callbackTarget === target) { + return true; + } + index = list.indexOf(callback, index + 1); + } + // callback given but not found + return false; + } + return false; +}; + +/** + * Removes all callbacks registered in a certain event type or all callbacks registered with a certain target + * @method removeAll + * @param {String|Object} key - The event key to be removed or the target to be removed + */ +CallbacksHandler.prototype.removeAll = function (key) { + if (typeof key === 'object') { + var target = key, list, index, callback; + // loop for all event types + for (key in this._callbackTable) { + list = this._callbackTable[key]; + index = list.lastIndexOf(target); + while (index !== -1) { + callback = list[index-1]; + if (typeof callback === 'function') + list.splice(index-1, 2); + else + list.splice(index, 1); + index = list.lastIndexOf(target); + } + } + } + else + delete this._callbackTable[key]; +}; + +/** + * @method remove + * @param {String} key + * @param {Function} callback + * @param {Object} target + * @return {Boolean} removed + */ +CallbacksHandler.prototype.remove = function (key, callback, target) { + var list = this._callbackTable[key], index, callbackTarget; + if (list) { + index = list.indexOf(callback); + while (index !== -1) { + callbackTarget = list[index+1]; + if (typeof callbackTarget !== 'object') { + callbackTarget = undefined; + } + if (callbackTarget === target) { + list.splice(index, callbackTarget ? 2 : 1); + break; + } + + index = list.indexOf(callback, index + 1); + } + return true; + } + return false; +}; + + +/** + * The callbacks invoker to handle and invoke callbacks by key + * + * @class CallbacksInvoker + * @constructor + * @extends _CallbacksHandler + */ +var CallbacksInvoker = function () { + CallbacksHandler.call(this); +}; +JS.extend(CallbacksInvoker, CallbacksHandler); + +if (CC_TEST) { + cc._Test.CallbacksInvoker = CallbacksInvoker; +} + +/** + * @method invoke + * @param {String} key + * @param {any} [p1] + * @param {any} [p2] + * @param {any} [p3] + * @param {any} [p4] + * @param {any} [p5] + */ +CallbacksInvoker.prototype.invoke = function (key, p1, p2, p3, p4, p5) { + var list = this._callbackTable[key], i, l, target; + if (list) { + for (i = 0, l = list.length; i < l;) { + target = list[i+1]; + if (target && typeof target === 'object') { + list[i].call(target, p1, p2, p3, p4, p5); + i += 2; + } + else { + list[i](p1, p2, p3, p4, p5); + ++i; + } + } + } +}; + +/** + * @method invokeAndRemove + * @param {String} key + * @param {any} [p1] + * @param {any} [p2] + * @param {any} [p3] + * @param {any} [p4] + * @param {any} [p5] + */ +CallbacksInvoker.prototype.invokeAndRemove = function (key, p1, p2, p3, p4, p5) { + // this.invoke(key, p1, p2, p3, p4, p5); + // 这里ä¸ç›´æŽ¥è°ƒç”¨invoke仅仅是为了å‡å°‘调用堆栈的深度,方便调试 + var list = this._callbackTable[key], i, l, target; + if (list) { + for (i = 0, l = list.length; i < l;) { + target = list[i+1]; + if (target && typeof target === 'object') { + list[i].call(target, p1, p2, p3, p4, p5); + i += 2; + } + else { + list[i](p1, p2, p3, p4, p5); + ++i; + } + } + } + this.removeAll(key); +}; + +/** + * @method bindKey + * @param {String} key + * @param {Boolean} [remove=false] - remove callbacks after invoked + * @return {Function} the new callback which will invoke all the callbacks binded with the same supplied key + */ +CallbacksInvoker.prototype.bindKey = function (key, remove) { + var self = this; + return function bindedInvocation (p1, p2, p3, p4, p5) { + // this.invoke(key, p1, p2, p3, p4, p5); + // 这里ä¸ç›´æŽ¥è°ƒç”¨invoke仅仅是为了å‡å°‘调用堆栈的深度,方便调试 + var list = self._callbackTable[key], i, l, target; + if (list) { + for (i = 0, l = list.length; i < l;) { + target = list[i+1]; + if (target && typeof target === 'object') { + list[i].call(target, p1, p2, p3, p4, p5); + i += 2; + } + else { + list[i](p1, p2, p3, p4, p5); + ++i; + } + } + } + if (remove) { + self.removeAll(key); + } + }; +}; + +CallbacksInvoker.CallbacksHandler = CallbacksHandler; +module.exports = CallbacksInvoker; diff --git a/cocos2d/core/platform/deserialize.js b/cocos2d/core/platform/deserialize.js new file mode 100644 index 00000000000..e446fcd0f75 --- /dev/null +++ b/cocos2d/core/platform/deserialize.js @@ -0,0 +1,517 @@ +/**************************************************************************** + 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 JS = require('./js'); +var CCObject = require('./CCObject'); + +var EDITOR = CC_EDITOR || CC_TEST; +var ENABLE_TARGET = EDITOR; + +// HELPERS + +/** + * !#en Contains information collected during deserialization + * !#zh 包å«ååºåˆ—åŒ–æ—¶çš„ä¸€äº›ä¿¡æ¯ + * @class Details + * @constructor + */ +var Details = function () { + + //this.urlList = []; + //this.callbackList = []; + + // uuids(assets) need to load + + /** + * list of the depends assets' uuid + * @property uuidList + * @type {String[]} + */ + this.uuidList = []; + /** + * the obj list whose field needs to load asset by uuid + * @property uuidObjList + * @type {Object[]} + */ + this.uuidObjList = []; + /** + * the corresponding field name which referenced to the asset + * @property uuidPropList + * @type {String[]} + */ + this.uuidPropList = []; + + /** + * the corresponding field name which referenced to the raw object + * @property rawProp + * @type {String} + */ + this.rawProp = ''; + + if (EDITOR) { + /** + * 用户å¯ä»¥æŒ‡å®šä¸€ä¸ªåœ¨ååºåˆ—化结æŸæ—¶ä¼šè¢«è§¦å‘的回调,该回调会传回ååºåˆ—化时统计到的所有解æžè¿‡çš„字段。 + * NOTE: + * - 会被传回的字段仅é™äºŽéž Asset 类型,并且如果字段值为 null 或 undefined,则å¯èƒ½ä¸ä¼šè¢«ä¼ å›žã€‚ + * - 该回调在 DeserializeInfo 第一次调用 reset 时就会被清空。 + * @callback visitorInEditor + * @param {Object[]} objs + * @param {String[]} propNames + * @param {Object} _Deserializer + * @private + */ + this.visitorInEditor = null; + + this.visitObjList = []; + this.visitPropList = []; + } +}; +/** + * @method reset + */ +Details.prototype.reset = function () { + this.uuidList.length = 0; + this.uuidObjList.length = 0; + this.uuidPropList.length = 0; + this.rawProp = ''; + //this.rawObjList.length = 0; + //this.rawPropList.length = 0; + + if (EDITOR) { + this.visitorInEditor = null; + this.visitObjList.length = 0; + this.visitPropList.length = 0; + } +}; +if (EDITOR) { + Details.prototype.visitLater = function (obj, propName) { + this.visitObjList.push(obj); + this.visitPropList.push(propName); + }; +} +/** + * @method getUuidOf + * @param {Object} obj + * @param {String} propName + * @return {String} + */ +Details.prototype.getUuidOf = function (obj, propName) { + for (var i = 0; i < this.uuidObjList.length; i++) { + if (this.uuidObjList[i] === obj && this.uuidPropList[i] === propName) { + return this.uuidList[i]; + } + } + return ""; +}; +/** + * @method assignAssetsBy + * @param {Function} getter + * @return {Boolean} success + */ +Details.prototype.assignAssetsBy = function (getter) { + var success = true; + for (var i = 0, len = this.uuidList.length; i < len; i++) { + var uuid = this.uuidList[i]; + var asset = getter(uuid); + if (asset) { + var obj = this.uuidObjList[i]; + var prop = this.uuidPropList[i]; + obj[prop] = asset; + } + else { + cc.error('Failed to assign asset: ' + uuid); + success = false; + } + } + return success; +}; + +// IMPLEMENT OF DESERIALIZATION + +var _Deserializer = (function () { + ///** + // * @param {Boolean} isEditor - if false, "editorOnly" properties will be discarded + // */ + function _Deserializer(jsonObj, result, target, classFinder) { + this._classFinder = classFinder; + if (ENABLE_TARGET) { + this._target = target; + } + this._idList = []; + this._idObjList = []; + this._idPropList = []; + this.result = result || new Details(); + + if (Array.isArray(jsonObj)) { + var jsonArray = jsonObj; + var refCount = jsonArray.length; + this.deserializedList = new Array(refCount); + // deserialize + for (var i = 0; i < refCount; i++) { + if (jsonArray[i]) { + var mainTarget; + if (ENABLE_TARGET) { + mainTarget = (i === 0 && target); + } + this.deserializedList[i] = _deserializeObject(this, jsonArray[i], mainTarget); + } + } + this.deserializedData = refCount > 0 ? this.deserializedList[0] : []; + + //// callback + //for (var j = 0; j < refCount; j++) { + // if (referencedList[j].onAfterDeserialize) { + // referencedList[j].onAfterDeserialize(); + // } + //} + } + else { + this.deserializedList = [null]; + this.deserializedData = jsonObj ? _deserializeObject(this, jsonObj, target) : null; + this.deserializedList[0] = this.deserializedData; + + //// callback + //if (deserializedData.onAfterDeserialize) { + // deserializedData.onAfterDeserialize(); + //} + } + + // dereference + _dereference(this); + + // call visitor after all properties initialized + if (EDITOR) { + this._callVisitorInEditor(); + } + } + + var _dereference = function (self) { + // 这里ä¸é‡‡ç”¨é历ååºåˆ—化结果的方å¼ï¼Œå› ä¸ºååºåˆ—化的结果如果引用到å¤æ‚的外部库,很容易堆栈溢出。 + var deserializedList = self.deserializedList; + for (var i = 0, len = self._idList.length; i < len; i++) { + var propName = self._idPropList[i]; + var id = self._idList[i]; + self._idObjList[i][propName] = deserializedList[id]; + } + }; + + if (EDITOR) { + _Deserializer.prototype._callVisitorInEditor = function () { + var result = this.result; + if (result.visitorInEditor) { + result.visitorInEditor(result.visitObjList, result.visitPropList, this); + } + }; + } + + // å’Œ _deserializeObject ä¸åŒçš„地方在于会判断 id å’Œ uuid + _Deserializer.prototype._deserializeObjField = function (obj, jsonObj, propName, target) { + var id = jsonObj.__id__; + if (typeof id === 'undefined') { + var uuid = jsonObj.__uuid__; + if (uuid) { + //if (ENABLE_TARGET) { + //这里ä¸åšä»»ä½•æ“作,因为有å¯èƒ½è°ƒç”¨è€…需è¦çŸ¥é“ä¾èµ–哪些 asset。 + //调用者使用 uuidList 时,å¯ä»¥åˆ¤æ–­ obj[propName] 是å¦ä¸ºç©ºï¼Œä¸ºç©ºåˆ™è¡¨ç¤ºå¾…进一步加载, + //ä¸ä¸ºç©ºåˆ™åªæ˜¯è¡¨æ˜Žä¾èµ–关系。 + // if (target && target[propName] && target[propName]._uuid === uuid) { + // console.assert(obj[propName] === target[propName]); + // return; + // } + // } + this.result.uuidList.push(uuid); + this.result.uuidObjList.push(obj); + this.result.uuidPropList.push(propName); + } + else { + if (ENABLE_TARGET) { + obj[propName] = _deserializeObject(this, jsonObj, target && target[propName]); + } + else { + obj[propName] = _deserializeObject(this, jsonObj); + } + if (this.result.visitorInEditor && EDITOR) { + this.result.visitLater(obj, propName); + } + } + } + else { + var dObj = this.deserializedList[id]; + if (dObj) { + obj[propName] = dObj; + } + else { + this._idList.push(id); + this._idObjList.push(obj); + this._idPropList.push(propName); + } + if (this.result.visitorInEditor && EDITOR) { + this.result.visitLater(obj, propName); + } + } + }; + + function _deserializePrimitiveObject (self, instance, serialized) { + for (var propName in serialized) { + if (serialized.hasOwnProperty(propName)) { + var prop = serialized[propName]; + if (typeof prop !== 'object') { + if (propName !== '__type__'/* && k != '__id__'*/) { + instance[propName] = prop; + if (self.result.visitorInEditor && EDITOR) { + self.result.visitLater(instance, propName); + } + } + } + else { + if (prop) { + if (ENABLE_TARGET) { + self._deserializeObjField(instance, prop, propName, self._target && instance); + } + else { + self._deserializeObjField(instance, prop, propName); + } + } + else { + instance[propName] = null; + } + } + + } + } + } + + function _deserializeTypedObject (self, instance, serialized) { + //++self.stackCounter; + //if (self.stackCounter === 100) { + // debugger; + //} + for (var propName in instance) { // é历 instance,如果具有类型,æ‰ä¸ä¼šæŠŠ __type__ ä¹Ÿè¯»è¿›æ¥ + var prop = serialized[propName]; + if (typeof prop !== 'undefined' && serialized.hasOwnProperty(propName)) { + if (typeof prop !== 'object') { + instance[propName] = prop; + } + else { + if (prop) { + if (ENABLE_TARGET) { + self._deserializeObjField(instance, prop, propName, self._target && instance); + } + else { + self._deserializeObjField(instance, prop, propName); + } + } + else { + instance[propName] = null; + } + } + } + } + //--self.stackCounter; + } + + function _deserializeFireClass(self, obj, serialized, klass, target) { + var props = klass.__props__; + for (var p = 0; p < props.length; p++) { + var propName = props[p]; + var attrs = cc.Class.attr(klass, propName); + // assume all prop in __props__ must have attr + var rawType = attrs.rawType; + if (!rawType) { + if (!EDITOR && attrs.editorOnly) { + continue; // skip editor only if not editor + } + if (attrs.serializable === false) { + continue; // skip nonSerialized + } + var prop = serialized[propName]; + if (typeof prop === 'undefined') { + continue; + } + if (typeof prop !== 'object') { + obj[propName] = prop; + } + else { + if (prop) { + if (ENABLE_TARGET) { + self._deserializeObjField(obj, prop, propName, target && obj); + } + else { + self._deserializeObjField(obj, prop, propName); + } + } + else { + obj[propName] = null; + } + } + } + else { + // always load raw objects even if property not serialized + if (self.result.rawProp) { + cc.error('not support multi raw object in a file'); + // 这里å‡å®šæ¯ä¸ªasset都有uuid,æ¯ä¸ªjsonåªèƒ½åŒ…å«ä¸€ä¸ªasset,åªèƒ½åŒ…å«ä¸€ä¸ªrawProp + } + self.result.rawProp = propName; + } + } + if (props[props.length - 1] === '_$erialized') { + // save original serialized data + obj._$erialized = serialized; + // parse the serialized data as primitive javascript object, so its __id__ will be dereferenced + _deserializePrimitiveObject(self, obj._$erialized, serialized); + } + } + + ///** + // * @param {Object} serialized - The obj to deserialize, must be non-nil + // * @param {Object} [target=null] + // */ + var _deserializeObject = function (self, serialized, target) { + var propName, prop; + var obj = null; // the obj to return + var klass = null; + if (serialized.__type__) { + + // Type Object (including FireClass) + + klass = self._classFinder(serialized.__type__); + if (!klass) { + cc.error('[cc.deserialize] unknown type: ' + serialized.__type__); + return null; + } + + if (ENABLE_TARGET && target) { + // use target + if ( !(target instanceof klass) ) { + cc.warn('Type of target to deserialize not matched with data: target is %s, data is %s', + JS.getClassName(target), klass); + } + obj = target; + } + else { + // instantiate a new object + obj = new klass(); + } + + if (obj instanceof CCObject && obj._deserialize) { + obj._deserialize(serialized.content, self); + return obj; + } + if ( cc.Class._isCCClass(klass) ) { + _deserializeFireClass(self, obj, serialized, klass, target); + } + else { + _deserializeTypedObject(self, obj, serialized); + } + } + else if ( !Array.isArray(serialized) ) { + + // embedded primitive javascript object + + obj = (ENABLE_TARGET && target) || {}; + _deserializePrimitiveObject(self, obj, serialized); + } + else { + + // Array + + if (ENABLE_TARGET && target) { + target.length = serialized.length; + obj = target; + } + else { + obj = new Array(serialized.length); + } + + for (var i = 0; i < serialized.length; i++) { + prop = serialized[i]; + if (typeof prop === 'object' && prop) { + if (ENABLE_TARGET) { + self._deserializeObjField(obj, prop, '' + i, target && obj); + } + else { + self._deserializeObjField(obj, prop, '' + i); + } + } + else { + obj[i] = prop; + + if (self.result.visitorInEditor && EDITOR) { + self.result.visitLater(obj, '' + i); + } + } + } + } + return obj; + }; + + return _Deserializer; +})(); + +// FACADE + +/** + * !#en Deserialize json to cc.Asset + * !#zh å°† JSON ååºåˆ—化为对象实例。 + * + * 当指定了 target 选项时,如果 target 引用的其它 asset çš„ uuid ä¸å˜ï¼Œåˆ™ä¸ä¼šæ”¹å˜ target 对 asset 的引用, + * 也ä¸ä¼šå°† uuid ä¿å­˜åˆ° result 对象中。 + * + * @method deserialize + * @param {(String|Object)} data - the serialized cc.Asset json string or json object. + * @param {deserialize.Details} [result] - additional loading result + * @param {Object} [options] + * @return {object} the main data(asset) + */ +cc.deserialize = function (data, result, options) { + var classFinder = (options && options.classFinder) || JS._getClassById; + // å¯ç”¨ createAssetRefs åŽï¼Œå¦‚果有 url 属性则会被统一强制设置为 { uuid: 'xxx' },必须åŽé¢å†ç‰¹æ®Šå¤„ç† + var createAssetRefs = (options && options.createAssetRefs) || cc.sys.platform === cc.sys.EDITOR_CORE; + var target = ENABLE_TARGET && (options && options.target); + + if (CC_EDITOR && Buffer.isBuffer(data)) { + data = data.toString(); + } + + if (typeof data === 'string') { + data = JSON.parse(data); + } + + if (createAssetRefs && !result) { + result = new Details(); + } + cc.game._isCloning = true; + var deserializer = new _Deserializer(data, result, target, classFinder); + cc.game._isCloning = false; + + if (createAssetRefs) { + result.assignAssetsBy(Editor.serialize.asAsset); + } + + return deserializer.deserializedData; +}; + +cc.deserialize.Details = Details; diff --git a/cocos2d/core/platform/index.js b/cocos2d/core/platform/index.js new file mode 100644 index 00000000000..e6fdceef938 --- /dev/null +++ b/cocos2d/core/platform/index.js @@ -0,0 +1,43 @@ +/**************************************************************************** + 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. + ****************************************************************************/ + +require('./js'); +require('./CCClass'); +require('./CCObject'); +require('./callbacks-invoker'); +require('./url'); +require('./deserialize'); +require('./instantiate'); +require('./prefab-info'); +require('./requiring-frame'); +require('./CCSys'); +require('./CCLoader'); +require('./CCMacro'); + +if (cc.sys.isBrowser || cc.sys.isNative) { + require('./load-manager'); + require('./CCAssetLibrary'); +} diff --git a/cocos2d/core/platform/instantiate.js b/cocos2d/core/platform/instantiate.js new file mode 100644 index 00000000000..e8e616e1a05 --- /dev/null +++ b/cocos2d/core/platform/instantiate.js @@ -0,0 +1,243 @@ +/**************************************************************************** + 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 CCObject = require('./CCObject'); +var PersistentMask = CCObject.Flags.PersistentMask; +var _isDomNode = require('./utils').isDomNode; + +/** + * !#en Clones the object original and returns the clone. + * + * See [Clone exists Entity](/en/scripting/create-destroy-entities/#instantiate) + * + * !#zh å¤åˆ¶ç»™å®šçš„对象 + * + * 详细用法å¯å‚考[å¤åˆ¶å·²æœ‰Entity](/zh/scripting/create-destroy-entities/#instantiate) + * + * Instantiate 时,function å’Œ dom ç­‰éžå¯åºåˆ—化对象会直接ä¿ç•™åŽŸæœ‰å¼•ç”¨ï¼ŒAsset 会直接进行浅拷è´ï¼Œå¯åºåˆ—化类型会进行深拷è´ã€‚ + * 对于 Entity / Component ç­‰ Scene Object,如果对方也会被一起 Instantiate,则é‡å®šå‘到新的引用,å¦åˆ™ä¿ç•™ä¸ºåŽŸæ¥çš„引用。 + * + * @method instantiate + * @param {Object} original - An existing object that you want to make a copy of. + * @return {Object} the newly instantiated object + */ +function instantiate (original) { + if (typeof original !== 'object' || Array.isArray(original)) { + cc.error('The thing you want to instantiate must be an object'); + return null; + } + if (!original) { + cc.error('The thing you want to instantiate is nil'); + return null; + } + if (original instanceof CCObject && !original.isValid) { + cc.error('The thing you want to instantiate is destroyed'); + return null; + } + + var clone; + if (original instanceof CCObject) { + // invoke _instantiate method if supplied + if (original._instantiate) { + cc.game._isCloning = true; + clone = original._instantiate(); + cc.game._isCloning = false; + return clone; + } + else if (original instanceof cc.Asset) { + // ä¸å…è®¸ç”¨é€šç”¨æ–¹æ¡ˆå®žä¾‹åŒ–èµ„æº + cc.error('The instantiate method for given asset do not implemented'); + return null; + } + } + + cc.game._isCloning = true; + clone = doInstantiate(original); + cc.game._isCloning = false; + return clone; +} + +/* + * Reserved tags: + * - _iN$t: the cloned instance + */ + +var objsToClearTmpVar = []; // 用于é‡è®¾ä¸´æ—¶å˜é‡ + +///** +// * Do instantiate object, the object to instantiate must be non-nil. +// * 这是一个通用的 instantiate 方法,å¯èƒ½æ•ˆçŽ‡æ¯”较低。 +// * 之åŽå¯ä»¥ç»™å„ç§ç±»åž‹é‡è½½å¿«é€Ÿå®žä¾‹åŒ–的特殊实现,但应该在å•å…ƒæµ‹è¯•ä¸­å°†ç»“果和这个方法的结果进行对比。 +// * 值得注æ„的是,这个方法ä¸å¯é‡å…¥ï¼Œä¸æ”¯æŒ mixin。 +// * +// * @param {Object} obj - 该方法仅供内部使用,用户需负责ä¿è¯å‚æ•°åˆæ³•ã€‚什么å‚数是åˆæ³•çš„请å‚考 cc.instantiate 的实现。 +// * @param {ENode} [parent] - åªæœ‰åœ¨è¯¥å¯¹è±¡ä¸‹çš„场景物体会被克隆。 +// * @return {Object} +// * @private +// */ +function doInstantiate (obj, parent) { + if (Array.isArray(obj)) { + cc.error('Can not instantiate array'); + return null; + } + if (_isDomNode && _isDomNode(obj)) { + cc.error('Can not instantiate DOM element'); + return null; + } + + var clone = enumerateObject(obj, parent); + + for (var i = 0, len = objsToClearTmpVar.length; i < len; ++i) { + objsToClearTmpVar[i]._iN$t = null; + } + objsToClearTmpVar.length = 0; + + return clone; +} + +///** +// * @param {Object} obj - The object to instantiate, typeof must be 'object' and should not be an array. +// * @return {Object} - the instantiated instance +// */ +var enumerateObject = function (obj, parent) { + var value, type, key; + var klass = obj.constructor; + var clone = new klass(); + obj._iN$t = clone; + objsToClearTmpVar.push(obj); + if (cc.Class._isCCClass(klass)) { + var props = klass.__props__; + for (var p = 0; p < props.length; p++) { + key = props[p]; + var attrs = cc.Class.attr(klass, key); + if (attrs.serializable !== false) { + value = obj[key]; + type = typeof value; + if (type === 'object') { + clone[key] = value ? instantiateObj(value, parent, clone, key) : value; + } + else { + clone[key] = (type !== 'function') ? value : null; + } + } + } + if (clone instanceof cc._BaseNode && CC_EDITOR) { + clone._id = ''; + } + } + else { + // primitive javascript object + for (key in obj) { + //cc.log(key); + if (!obj.hasOwnProperty(key) || (key.charCodeAt(0) === 95 && key.charCodeAt(1) === 95)) { // starts with __ + continue; + } + value = obj[key]; + if (value === clone) { + continue; // value is obj._iN$t + } + // instantiate field + type = typeof value; + if (type === 'object') { + clone[key] = value ? instantiateObj(value, parent, clone, key) : value; + } + else { + clone[key] = (type !== 'function') ? value : null; + } + } + } + if (obj instanceof CCObject) { + clone._objFlags &= PersistentMask; + } + return clone; +}; + +///** +// * @return {Object} - the original non-nil object, typeof must be 'object' +// */ +function instantiateObj (obj, parent, ownerObj, ownerKey) { + // ç›®å‰ä½¿ç”¨â€œ_iN$tâ€è¿™ä¸ªç‰¹æ®Šå­—段æ¥å­˜å®žä¾‹åŒ–åŽçš„对象,这样åšä¸»è¦æ˜¯ä¸ºäº†é˜²æ­¢å¾ªçŽ¯å¼•ç”¨ + // 注æ„,为了é¿å…循环引用,所有新创建的实例,必须在赋值å‰è¢«è®¾ä¸ºæºå¯¹è±¡çš„_iN$t + var clone = obj._iN$t; + if (clone) { + // has been instantiated + return clone; + } + + if (obj instanceof cc.Asset) { + // 所有资æºç›´æŽ¥å¼•ç”¨ï¼Œä¸éœ€è¦æ‹·è´ + return obj; + } + else if (Array.isArray(obj)) { + var len = obj.length; + clone = new Array(len); + obj._iN$t = clone; + for (var i = 0; i < len; ++i) { + var value = obj[i]; + // instantiate field + var type = typeof value; + if (type === 'object') { + clone[i] = value ? instantiateObj(value, parent, clone, '' + i) : value; + } + else { + clone[i] = (type !== 'function') ? value : null; + } + } + objsToClearTmpVar.push(obj); + return clone; + } + else if (obj instanceof cc.ValueType) { + return obj.clone(); + } + else { + var ctor = obj.constructor; + if (cc.Class._isCCClass(ctor)) { + if (parent) { + if (obj instanceof cc._BaseNode) { + if (!obj.isChildOf(parent)) { + // should not clone other nodes if not descendant + return obj; + } + } + else if (obj instanceof cc.Component) { + if (!obj.node.isChildOf(parent)) { + // should not clone other component if not descendant + return obj; + } + } + } + } + else if (ctor !== Object) { + // unknown type + return obj; + } + return enumerateObject(obj, parent); + } +} + +instantiate._clone = doInstantiate; +cc.instantiate = instantiate; +module.exports = instantiate; diff --git a/cocos2d/core/platform/js.js b/cocos2d/core/platform/js.js new file mode 100644 index 00000000000..d730dc2e9f1 --- /dev/null +++ b/cocos2d/core/platform/js.js @@ -0,0 +1,629 @@ +/**************************************************************************** + 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 _getPropertyDescriptor (obj, name) { + var pd = Object.getOwnPropertyDescriptor(obj, name); + if (pd) { + return pd; + } + var p = Object.getPrototypeOf(obj); + if (p) { + return _getPropertyDescriptor(p, name); + } + else { + return null; + } +} + +function _copyprop(name, source, target) { + var pd = _getPropertyDescriptor(source, name); + Object.defineProperty(target, name, pd); +} + +/** + * This module provides some JavaScript utilities. + * + * @module js + */ +var js = { + + /** + * Check the obj whether is function or not + * @param {*} obj + * @returns {Boolean} + */ + isFunction: function(obj) { + return typeof obj === 'function'; + }, + + /** + * Check the obj whether is number or not + * @param {*} obj + * @returns {Boolean} + */ + 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} + */ + 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} + */ + 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} + */ + isUndefined: function(obj) { + return typeof obj === 'undefined'; + }, + + /** + * Check the obj whether is object or not + * @param {*} obj + * @returns {Boolean} + */ + isObject: function(obj) { + return typeof obj === "object" && Object.prototype.toString.call(obj) === '[object Object]'; + }, + + /** + * copy all properties not defined in obj from arguments[1...n] + * @method addon + * @param {Object} obj object to extend its properties + * @param {Object} ...sourceObj source object to copy properties from + * @return {Object} the result obj + */ + addon: function (obj) { + 'use strict'; + obj = obj || {}; + for (var i = 1, length = arguments.length; i < length; i++) { + var source = arguments[i]; + if (source) { + if (typeof source !== 'object') { + cc.error('cc.js.addon called on non-object:', source); + continue; + } + for ( var name in source) { + if ( !(name in obj) ) { + _copyprop( name, source, obj); + } + } + } + } + return obj; + }, + + /** + * copy all properties from arguments[1...n] to obj + * @method mixin + * @param {Object} obj + * @param {Object} ...sourceObj + * @return {Object} the result obj + */ + mixin: function (obj) { + 'use strict'; + obj = obj || {}; + for (var i = 1, length = arguments.length; i < length; i++) { + var source = arguments[i]; + if (source) { + if (typeof source !== 'object') { + cc.error('cc.js.mixin: arguments must be type object:', source); + continue; + } + for ( var name in source) { + _copyprop( name, source, obj); + } + } + } + return obj; + }, + + /** + * Derive the class from the supplied base class. + * Both classes are just native javascript constructors, not created by cc.Class, so + * usually you will want to inherit using {% crosslink cc.Class cc.Class %} instead. + * + * @method extend + * @param {Function} cls + * @param {Function} base - the baseclass to inherit + * @return {Function} the result class + */ + extend: function (cls, base) { + if (CC_DEV) { + if (!base) { + cc.error('The base class to extend from must be non-nil'); + return; + } + if (!cls) { + cc.error('The class to extend must be non-nil'); + return; + } + } + for (var p in base) if (base.hasOwnProperty(p)) cls[p] = base[p]; + cls.prototype = Object.create(base.prototype); + cls.prototype.constructor = cls; + return cls; + }, + + /** + * Removes all enumerable properties from object + * @method clear + * @param {any} obj + */ + clear: function (obj) { + var keys = Object.keys(obj); + for (var i = 0; i < keys.length; i++) { + delete obj[keys[i]]; + } + }, + + /** + * Get property descriptor in object and all its ancestors + * @method getPropertyDescriptor + * @param {Object} obj + * @param {String} name + * @return {Object} + */ + getPropertyDescriptor: _getPropertyDescriptor +}; + +/** + * Get class name of the object, if object is just a {} (and which class named 'Object'), it will return null. + * (modified from the code from this stackoverflow post) + * @method getClassName + * @param {Object|Function} obj - instance or constructor + * @return {String} + */ +js.getClassName = function (obj) { + if (typeof obj === 'function') { + if (obj.prototype.__classname__) { + return obj.prototype.__classname__; + } + } + else if (obj && obj.constructor) { + if (obj.constructor.prototype && obj.constructor.prototype.hasOwnProperty('__classname__')) { + return obj.__classname__; + } + var retval; + // for browsers which have name property in the constructor of the object, such as chrome + if (obj.constructor.name) { + retval = obj.constructor.name; + } + if (obj.constructor.toString) { + var arr, str = obj.constructor.toString(); + if (str.charAt(0) === '[') { + // str is "[object objectClass]" + arr = str.match(/\[\w+\s*(\w+)\]/); + } + else { + // str is function objectClass () {} for IE Firefox + arr = str.match(/function\s*(\w+)/); + } + if (arr && arr.length === 2) { + retval = arr[1]; + } + } + return retval !== 'Object' ? retval : ''; + } + return ''; +}; + +var TCID_PREFIX = 'cc.TmpCId.'; +var id = 0; +function getTempCID () { + return TCID_PREFIX + (id++); +} + +js._isTempClassId = function (id) { + return (CC_EDITOR || CC_TEST) && (typeof id !== 'string' || id.startsWith(TCID_PREFIX)); +}; + +// id 注册 +(function () { + var _idToClass = {}; + var _nameToClass = {}; + + function getRegister (key, table) { + return function (id, constructor) { + // deregister old + if (constructor.prototype.hasOwnProperty(key)) { + delete table[constructor.prototype[key]]; + } + constructor.prototype[key] = id; + // register class + if (id) { + var registered = table[id]; + if (registered && registered !== constructor) { + var error = 'A Class already exists with the same ' + key + ' : "' + id + '".'; + if (CC_TEST) { + error += ' (This may be caused by error of unit test.) \ +If you dont need serialization, you can set class id to "". You can also call \ +cc.js.unregisterClass to remove the id of unused class'; + } + cc.error(error); + } + else { + table[id] = constructor; + } + //if (id === "") { + // console.trace("", table === _nameToClass); + //} + } + }; + } + + /** + * Register the class by specified id, if its classname is not defined, the class name will also be set. + * @method _setClassId + * @param {String} classId + * @param {Function} constructor + * @private + */ + js._setClassId = getRegister('__cid__', _idToClass); + + var doSetClassName = getRegister('__classname__', _nameToClass); + + /** + * Register the class by specified name manually + * @method setClassName + * @param {String} className + * @param {Function} constructor + */ + js.setClassName = function (className, constructor) { + doSetClassName(className, constructor); + // auto set class id + if (!constructor.prototype.hasOwnProperty('__cid__')) { + var id = className || getTempCID(); + if (id) { + js._setClassId(id, constructor); + } + } + }; + + /** + * Unregister a class from fireball. + * + * If you dont need a registered class anymore, you should unregister the class so that Fireball will not keep its reference anymore. + * Please note that its still your responsibility to free other references to the class. + * + * @method unregisterClass + * @param {Function} ...constructor - the class you will want to unregister, any number of classes can be added + */ + js.unregisterClass = function (constructor) { + 'use strict'; + for (var i = 0; i < arguments.length; i++) { + var p = arguments[i].prototype; + var classId = p.__cid__; + if (classId) { + delete _idToClass[classId]; + } + var classname = p.__classname__; + if (classname) { + delete _nameToClass[classname]; + } + } + }; + + /** + * Get the registered class by id + * @method _getClassById + * @param {String} classId + * @return {Function} constructor + * @private + */ + js._getClassById = function (classId) { + return _idToClass[classId]; + }; + + /** + * Get the registered class by name + * @method getClassByName + * @param {String} classname + * @return {Function} constructor + */ + js.getClassByName = function (classname) { + return _nameToClass[classname]; + }; + + /** + * Get class id of the object + * @method _getClassId + * @param {Object|Function} obj - instance or constructor + * @param {Boolean} [allowTempId=true] - can return temp id in editor + * @return {String} + * @private + */ + js._getClassId = function (obj, allowTempId) { + allowTempId = (typeof allowTempId !== 'undefined' ? allowTempId: true); + + var res; + if (typeof obj === 'function' && obj.prototype.hasOwnProperty('__cid__')) { + res = obj.prototype.__cid__; + if (!allowTempId && js._isTempClassId(res)) { + return ''; + } + return res; + } + if (obj && obj.constructor) { + var prototype = obj.constructor.prototype; + if (prototype && prototype.hasOwnProperty('__cid__')) { + res = obj.__cid__; + if (!allowTempId && js._isTempClassId(res)) { + return ''; + } + return res; + } + } + return ''; + }; + + if (CC_EDITOR) { + Object.defineProperty(js, '_registeredClassIds', { + get: function () { + var dump = {}; + for (var id in _idToClass) { + dump[id] = _idToClass[id]; + } + return dump; + }, + set: function (value) { + js.clear(_idToClass); + for (var id in value) { + _idToClass[id] = value[id]; + } + } + }); + Object.defineProperty(js, '_registeredClassNames', { + get: function () { + var dump = {}; + for (var id in _nameToClass) { + dump[id] = _nameToClass[id]; + } + return dump; + }, + set: function (value) { + js.clear(_nameToClass); + for (var id in value) { + _nameToClass[id] = value[id]; + } + } + }); + } + +})(); + +/** + * Define get set accessor, just help to call Object.defineProperty(...) + * @method getset + * @param {any} obj + * @param {String} prop + * @param {Function} getter + * @param {Function} setter + * @param {Boolean} [enumerable=false] + */ +js.getset = function (obj, prop, getter, setter, enumerable) { + if (typeof setter !== 'function') { + enumerable = setter; + setter = undefined; + } + Object.defineProperty(obj, prop, { + get: getter, + set: setter, + enumerable: !!enumerable + }); +}; + +/** + * Define get accessor, just help to call Object.defineProperty(...) + * @method get + * @param {any} obj + * @param {String} prop + * @param {Function} getter + * @param {Boolean} [enumerable=false] + */ +js.get = function (obj, prop, getter, enumerable) { + Object.defineProperty(obj, prop, { + get: getter, + enumerable: !!enumerable + }); +}; + +/** + * Define set accessor, just help to call Object.defineProperty(...) + * @method set + * @param {any} obj + * @param {String} prop + * @param {Function} setter + * @param {Boolean} [enumerable=false] + */ +js.set = function (obj, prop, setter, enumerable) { + Object.defineProperty(obj, prop, { + set: setter, + enumerable: !!enumerable + }); +}; + +/** + * Defines a polyfill field for obsoleted codes. + * @method obsolete + * @param {any} obj - YourObject or YourClass.prototype + * @param {String} obsoleted - "OldParam" or "YourClass.OldParam" + * @param {String} newPropName - "NewParam" + * @param {Boolean} [writable=false] + */ +js.obsolete = function (obj, obsoleted, newPropName, writable) { + var oldName = obsoleted.split('.').slice(-1); + js.get(obj, oldName, function () { + if (CC_DEV) { + cc.warn('"%s" is deprecated, use "%s" instead please.', obsoleted, newPropName); + } + return obj[newPropName]; + }); + if (writable) { + js.set(obj, oldName, function (value) { + if (CC_DEV) { + cc.warn('"%s" is deprecated, use "%s" instead please.', obsoleted, newPropName); + } + obj[newPropName] = value; + }); + } +}; + +/** + * Defines all polyfill fields for obsoleted codes corresponding to the enumerable properties of props. + * @method obsoletes + * @param {any} obj - YourObject or YourClass.prototype + * @param {any} objName - "YourObject" or "YourClass" + * @param {Object} props + * @param {Boolean} [writable=false] + */ +js.obsoletes = function (obj, objName, props, writable) { + for (var obsoleted in props) { + var newName = props[obsoleted]; + js.obsolete(obj, objName + '.' + obsoleted, newName, writable); + } +}; + +/** + * @class array + * @static + */ +js.array = { + /** + * Removes the first occurrence of a specific object from the array. + * @method remove + * @param {any[]} array + * @param {any} value + * @return {Boolean} + */ + remove: function (array, value) { + var index = array.indexOf(value); + if (index !== -1) { + array.splice(index, 1); + return true; + } + else { + return false; + } + }, + + /** + * Removes the array item at the specified index. + * @method removeAt + * @param {any[]} array + * @param {Number} index + */ + removeAt: function (array, index) { + array.splice(index, 1); + }, + + /** + * Determines whether the array contains a specific value. + * @method contains + * @param {any[]} array + * @param {any} value + * @return {Boolean} + */ + contains: function (array, value) { + return array.indexOf(value) !== -1; + }, + + /** + * Verify array's Type + * @param {array} array + * @param {Function} type + * @return {Boolean} + * @method + */ + verifyType: function (array, type) { + if (array && array.length > 0) { + for (var i = 0; i < array.length; i++) { + if (!(array[i] instanceof type)) { + cc.log(cc._LogInfos.Array.verifyType); + return false; + } + } + } + return true; + }, + + /** + * Removes from array all values in minusArr. For each Value in minusArr, the first matching instance in array will be removed. + * @method + * @param {Array} array Source Array + * @param {Array} minusArr minus Array + */ + removeArray: function (array, minusArr) { + for (var i = 0, l = minusArr.length; i < l; i++) { + remove(array, minusArr[i]); + } + }, + + /** + * Inserts some objects at index + * @method + * @param {Array} array + * @param {Array} addObjs + * @param {Number} index + * @return {Array} + */ + appendObjectsAt: function(array, addObjs, index) { + array.splice.apply(array, [index, 0].concat(addObjs)); + return array; + }, + + /** + * Copy an array's item to a new array (its performance is better than Array.slice) + * @param {Array} array + * @return {Array} + */ + copy: function(array) { + var i, len = array.length, arr_clone = new Array(len); + for (i = 0; i < len; i += 1) + arr_clone[i] = array[i]; + return arr_clone; + } +}; + +cc.js = js; + +module.exports = js; diff --git a/cocos2d/core/platform/load-manager.js b/cocos2d/core/platform/load-manager.js new file mode 100644 index 00000000000..16731e7de80 --- /dev/null +++ b/cocos2d/core/platform/load-manager.js @@ -0,0 +1,206 @@ +var CallbacksInvoker = require('./callbacks-invoker'); + +function getBuiltinRawTypes () { + return { + image: { + loader: cc.loader.loadImg.bind(cc.loader), + defaultExtname: '.host' + }, + json: { + loader: cc.loader.loadJson.bind(cc.loader), + defaultExtname: '.json' + }, + text: { + loader: cc.loader.loadTxt.bind(cc.loader), + defaultExtname: '.txt' + } + }; +} + +var urlToCallbacks = new CallbacksInvoker(); + + + // list of elements to load, the element type is { + // url: url, + // loader: loader, + // callback: callback, + // } +var loadQueue = []; + +var loadNext = function () { + if (LoadManager._curConcurrent >= LoadManager.maxConcurrent) { + cc.error('too many concurrent requests'); + return; + } + var nextOne = loadQueue.pop(); + if (nextOne) { + doLoad(nextOne.loader, nextOne.url, nextOne.callback); + } +}; + +function doLoad (loader, url, callback) { + LoadManager._curConcurrent += 1; + loader(url, function doLoadCB (error, asset) { + callback(error, asset); + LoadManager._curConcurrent = Math.max(0, LoadManager._curConcurrent - 1); + loadNext(); + }); +} + +/** +* The manager scheduling resources loading +* - It will: +* - select registered loader +* - merge same url request +* - limit the max concurrent request +* - It will NOT: +* - cache what has being loaded +* - load depends of resource +* @class LoadManager +* @static +*/ +var LoadManager = { + + /** + * Max allowed concurrent request count + * @property maxConcurrent + * @type {Number} + * @default 2 + */ + maxConcurrent: 2, + + /** + * Current concurrent request count + * @property _curConcurrent + * @type {Number} + * @readOnly + */ + _curConcurrent: 0, + + /** + * NOTE: Request the same url with different loader is disallowed + * @method loadByLoader + * @param {Function} loader + * @param {String} url + * @param {Function} callback + * @param {String} callback.param error - null or the error info + * @param {any} callback.param data - the loaded data + * @private + */ + loadByLoader: function (loader, url, callback) { + if (urlToCallbacks.add(url, callback)) { + var callbackBundle = urlToCallbacks.bindKey(url, true); + if (this._curConcurrent < this.maxConcurrent) { + doLoad(loader, url, callbackBundle); + } + else { + loadQueue.push({ + url: url, + loader: loader, + callback: callbackBundle + }); + } + } + }, + + /** + * @method load + * @param {String} url + * @param {String} rawType + * @param {String} [rawExtname] + * @param {Function} callback + * @param {String} callback.param error - null or the error info + * @param {any} callback.param data - the loaded data + * @private + */ + load: function (url, rawType, rawExtname, callback) { + if (typeof rawExtname === 'function') { + callback = rawExtname; + } + var typeInfo = this._rawTypes[rawType]; + if (typeInfo) { + var extname = rawExtname ? ('.' + rawExtname) : typeInfo.defaultExtname; + if (extname) { + var rawUrl = url + extname; + this.loadByLoader(typeInfo.loader, rawUrl, callback); + } + else { + callback(new Error('Undefined extname for the raw ' + rawType + ' file of ' + url), null); + } + } + else { + callback(new Error('Unknown raw type "' + rawType + '" of ' + url), null); + } + }, + + _rawTypes: getBuiltinRawTypes(), + + /** + * @method registerRawTypes + * @param {String} rawType + * @param {Function} loader + * @param {String} defaultExtname + */ + registerRawTypes: function (rawType, loader, defaultExtname) { + if (CC_DEV) { + if (!rawType) { + cc.error('[AssetLibrary.registerRawTypes] rawType must be non-nil'); + return; + } + if (typeof rawType !== 'string') { + cc.error('[AssetLibrary.registerRawTypes] rawType must be string'); + return; + } + if (!loader) { + cc.error('[AssetLibrary.registerRawTypes] loader must be non-nil'); + return; + } + if (typeof loader !== 'function') { + cc.error('[AssetLibrary.registerRawTypes] loader must be function'); + return; + } + } + if (this._rawTypes[rawType]) { + cc.error('rawType "%s" has already defined', rawType); + return; + } + if (defaultExtname && defaultExtname[0] !== '.') { + defaultExtname = '.' + defaultExtname; + } + this._rawTypes[rawType] = { + loader: loader, + defaultExtname: defaultExtname + }; + }, + + reset: function () { + if (CC_EDITOR) { + var audio = this._rawTypes.audio; + this._rawTypes = getBuiltinRawTypes(); + this._rawTypes.audio = audio; + } + }, + + isLoading: function (url, alsoCheckRaw) { + if (CC_EDITOR) { + if (this._curConcurrent === 0) { + return false; + } + if (urlToCallbacks.has(url)) { + return true; + } + if (alsoCheckRaw) { + for (var u in urlToCallbacks._callbackTable) { + if (u.indexOf(url) === 0) { + return true; + } + } + } + return false; + } + } +}; + +// TODO - merge with cc.loader +cc._LoadManager = LoadManager; +module.exports = LoadManager; diff --git a/cocos2d/core/platform/miniFramework.js b/cocos2d/core/platform/miniFramework.js new file mode 100644 index 00000000000..2e53f45aa98 --- /dev/null +++ b/cocos2d/core/platform/miniFramework.js @@ -0,0 +1,260 @@ +/**************************************************************************** + 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 + * @method $ + * @return {$} + */ +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.$# + * @method find + * @param {HTMLElement|String} x pass in a css selector in string or the whole HTMLElement + * @return {$} + */ + el.find = el.find || cc.$; + /** + * check if a DOMNode has a specific class + * @lends cc.$# + * @method hasClass + * @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.$# + * @method addClass + * @param {String} cls + * @return {$} + */ + 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.$# + * @method removeClass + * @param {String} cls + * @return {$} + */ + 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.$# + * @method remove + */ + el.remove = el.remove || function () { + if (this.parentNode) + this.parentNode.removeChild(this); + return this; + }; + + /** + * add to another element as a child + * @lends cc.$# + * @method appendTo + * @param {HTMLElement|$} x + * @return {$} + */ + 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.$# + * @method prependTo + * @param {HTMLElement|$} x + * @return {$} + */ + 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.$# + * @method transforms + * @return {$} + */ + 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.$# + * @method translates + * @param {Number} x in pixel + * @param {Number} y in pixel + * @return {$} + */ + el.translates = function (x, y) { + this.position.x = x; + this.position.y = y; + this.transforms(); + return this + }; + + /** + * rotate the element + * @memberOf cc.$# + * @method rotate + * @param {Number} x in degrees + * @return {$} + */ + el.rotate = function (x) { + this.rotation = x; + this.transforms(); + return this + }; + + /** + * resize the element + * @memberOf cc.$# + * @method resize + * @param {Number} x + * @param {Number} y + * @return {$} + */ + el.resize = function (x, y) { + this.scale.x = x; + this.scale.y = y; + this.transforms(); + return this + }; + + /** + * skews the element + * @memberOf cc.$# + * @method setSkew + * @param {Number} x in degrees + * @param {Number} y + * @return {$} + */ + 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.$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/cocos2d/core/platform/prefab-info.js b/cocos2d/core/platform/prefab-info.js new file mode 100644 index 00000000000..1f6df52d7e4 --- /dev/null +++ b/cocos2d/core/platform/prefab-info.js @@ -0,0 +1,28 @@ +// ä¿å­˜ç¼–辑器下用到的 prefab ç›¸å…³ä¿¡æ¯ +var PrefabInfo = cc.Class({ + name: 'cc.PrefabInfo', + properties: { + //// the serialized version + //VER: { + // default: 1 + //}, + + // the most top node of this prefab in the scene + root: { + default: null, + }, + + // 所属的 prefab 资æºå¯¹è±¡ (cc._Prefab) + // (这个属性在场景中是会ä¿å­˜çš„,但是ä¸ä¼šä¿å­˜åœ¨ prefab 里é¢ï¼Œå› ä¸ºåˆ›å»º prefab 时还ä¸çŸ¥é“自己的 uuid 是多少。) + asset: { + default: null, + }, + + // 用æ¥æ ‡è¯†åˆ«è¯¥èŠ‚点在 prefab 资æºä¸­çš„ä½ç½®ï¼Œå› æ­¤è¿™ä¸ª ID åªéœ€è¦ä¿è¯åœ¨ Assets 里ä¸é‡å¤å°±è¡Œ + fileId: { + default: '' + }, + } +}); + +cc._PrefabInfo = module.exports = PrefabInfo; diff --git a/cocos2d/core/platform/preprocess-attrs.js b/cocos2d/core/platform/preprocess-attrs.js new file mode 100644 index 00000000000..c44bf155f28 --- /dev/null +++ b/cocos2d/core/platform/preprocess-attrs.js @@ -0,0 +1,179 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + 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. + ****************************************************************************/ + +var Attr = require('./attribute'); + +// 增加预处ç†å±žæ€§è¿™ä¸ªæ­¥éª¤çš„目的是é™ä½Ž FireClass 的实现难度,将比较稳定的通用逻辑和一些需求比较çµæ´»çš„属性需求分隔开。 + +var SerializableAttrs = { + url: { + canUsedInGet: true + }, + default: {}, + serializable: {}, + editorOnly: {}, + rawType: {}, +}; + +// é¢„å¤„ç† notify 等扩展属性 +function parseNotify (val, propName, notify, properties) { + if (val.get || val.set) { + if (CC_DEV) { + cc.warn('"notify" can\'t work with "get/set" !'); + } + return; + } + if (val.hasOwnProperty('default')) { + // 添加新的内部属性,将原æ¥çš„属性修改为 getter/setter å½¢å¼ + // 以 _ 开头将自动设置property 为 visible: false + var newKey = "_N$" + propName; + + val.get = function () { + return this[newKey]; + }; + val.set = function (value) { + var oldValue = this[newKey]; + this[newKey] = value; + notify.call(this, oldValue); + }; + + var newValue = {}; + properties[newKey] = newValue; + // å°†ä¸èƒ½ç”¨äºŽget方法中的属性移动到newValue中 + for (var attr in SerializableAttrs) { + var v = SerializableAttrs[attr]; + if (val.hasOwnProperty(attr)) { + newValue[attr] = val[attr]; + if (!v.canUsedInGet) { + delete val[attr]; + } + } + } + } + else if (CC_DEV) { + cc.warn('"notify" must work with "default" !'); + } +} + +function checkUrl (val, className, propName, url) { + if (Array.isArray(url)) { + if (url.length > 0) { + url = url[0]; + } + else if (CC_EDITOR) { + return cc.error('Invalid url of %s.%s', className, propName); + } + } + if (CC_EDITOR) { + if (url == null) { + return cc.warn('The "url" attribute of "%s.%s" is undefined when loading script.', className, propName); + } + if (typeof url !== 'function' || !cc.isChildClassOf(url, cc.RawAsset)) { + return cc.error('The "url" type of "%s.%s" must be child class of cc.RawAsset.', className, propName); + } + if (cc.isChildClassOf(url, cc.Asset)) { + return cc.error('The "url" type of "%s.%s" must not be child class of cc.Asset,' + + 'otherwise you should use "type: %s" instead.', className, propName, cc.js.getClassName(url)); + } + if (val.type) { + return cc.warn('Can not specify "type" attribute for "%s.%s", because its "url" is already defined.', className, propName); + } + } + val.type = url; +} + +function parseType (val, type, className, propName) { + if (Array.isArray(type)) { + if (CC_EDITOR) { + var isArray = require('./CCClass').isArray; // require lazily to avoid circular require() calls + if (!isArray(val.default)) { + cc.warn('The "default" attribute of "%s.%s" must be an array', className, propName); + } + } + if (type.length > 0) { + val.type = type = type[0]; + } + else { + return cc.error('Invalid type of %s.%s', className, propName); + } + } + if (CC_EDITOR) { + if (typeof type === 'function') { + var isRaw = cc.isChildClassOf(type, cc.RawAsset) && !cc.isChildClassOf(type, cc.Asset); + if (isRaw) { + cc.warn('The "type" attribute of "%s.%s" must be child class of cc.Asset, ' + + 'otherwise you should use "url: %s" instead', className, propName, + cc.js.getClassName(type)); + } + } + else if (type === 'Number') { + cc.warn('The "type" attribute of "%s.%s" can not be "Number", use "Float" or "Integer" instead please.', className, propName); + } + else if (type == null) { + cc.warn('The "type" attribute of "%s.%s" is undefined when loading script.', className, propName); + } + } +} + +function postCheckType (val, type, className, propName) { + if (typeof type === 'function' && CC_EDITOR) { + if (cc.Class._isCCClass(type) && val.serializable !== false && !cc.js._getClassId(type, false)) { + cc.warn('Can not serialize "%s.%s" because the specified type is anonymous, please provide a class name or set the "serializable" attribute of "%s.%s" to "false".', className, propName, className, propName); + } + } +} + +module.exports = function (properties, className) { + for (var propName in properties) { + var val = properties[propName]; + + var isObj = val && typeof val === 'object' && !Array.isArray(val); + var isLiteral = isObj && val.constructor === Object; + if ( !isLiteral ) { + properties[propName] = val = { + default: val + }; + } + if (val) { + var notify = val.notify; + if (notify) { + parseNotify(val, propName, notify, properties); + } + + if ('type' in val) { + parseType(val, val.type, className, propName); + } + + if ('url' in val) { + checkUrl(val, className, propName, val.url); + } + + if ('type' in val) { + postCheckType(val, val.type, className, propName); + } + } + } +}; diff --git a/cocos2d/core/platform/requiring-frame.js b/cocos2d/core/platform/requiring-frame.js new file mode 100644 index 00000000000..cc8cea3638a --- /dev/null +++ b/cocos2d/core/platform/requiring-frame.js @@ -0,0 +1,40 @@ +var requiringFrames = []; // the requiring frame infos + +cc._RFpush = function (module, uuid, script) { + if (arguments.length === 2) { + script = uuid; + uuid = ''; + } + requiringFrames.push({ + uuid: uuid, + script: script, + module: module, + exports: module.exports, // original exports + beh: null + }); +}; + +cc._RFpop = function () { + var frameInfo = requiringFrames.pop(); + // check exports + var module = frameInfo.module; + var exports = module.exports; + if (exports === frameInfo.exports) { + for (var anyKey in exports) { + // exported + return; + } + // auto export component + module.exports = exports = frameInfo.beh; + } +}; + +cc._RFpeek = function () { + return requiringFrames[requiringFrames.length - 1]; +}; + +if (CC_EDITOR) { + cc._RFreset = function () { + requiringFrames = []; + }; +} diff --git a/cocos2d/core/platform/url.js b/cocos2d/core/platform/url.js new file mode 100644 index 00000000000..a8257b3c6db --- /dev/null +++ b/cocos2d/core/platform/url.js @@ -0,0 +1,32 @@ +/** + * @class url + * @static + */ +cc.url = { + + /** + * The base url of raw assets. + * @property _rawAssets + * @readOnly + */ + _rawAssets: '', + + /** + * Returns the url of raw assets. + * @method raw + * @param {String} url + * @return {String} + * @example {@link utils/api/cocos/docs/cocos2d/core/platform/url/raw.js} + */ + raw: function (url) { + if (url[0] === '.' && url[1] === '/') { + url = url.slice(2); + } + else if (url[0] === '/') { + url = url.slice(1); + } + return this._rawAssets + url; + } +}; + +module.exports = cc.url; diff --git a/cocos2d/core/platform/utils.js b/cocos2d/core/platform/utils.js new file mode 100644 index 00000000000..18e86b7c0b2 --- /dev/null +++ b/cocos2d/core/platform/utils.js @@ -0,0 +1,77 @@ +/**************************************************************************** + 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. + ****************************************************************************/ + +module.exports = { + isDomNode: typeof window === 'object' && function (obj) { + return ( + typeof Node === "object" ? obj instanceof Node : + obj && typeof obj === "object" && typeof obj.nodeType === "number" && typeof obj.nodeName === "string" + ); + }, + + callInNextTick: function (callback, p1, p2) { + if (callback) { + setTimeout(function () { + callback(p1, p2); + }, 1); + } + } +}; + +if (CC_DEV) { + cc.js.mixin(module.exports, { + ///** + // * @param {Object} obj + // * @return {Boolean} is {} ? + // */ + isPlainEmptyObj_DEV: function (obj) { + if (!obj || obj.constructor !== Object) { + return false; + } + // jshint ignore: start + for (var k in obj) { + return false; + } + // jshint ignore: end + return true; + }, + cloneable_DEV: function (obj) { + return obj && typeof obj.clone === 'function' && + (obj.constructor.prototype.hasOwnProperty('clone') || obj.hasOwnProperty('clone')); + } + }); +} + +if (CC_TEST) { + // editor mocks using in unit tests + if (typeof Editor === 'undefined') { + Editor = {}; + } + Editor.uuid = function () { + return '' + ((new Date()).getTime() + Math.random()); + }; + Editor.NonUuidMark = '.'; +} diff --git a/cocos2d/core/renderer/RendererCanvas.js b/cocos2d/core/renderer/RendererCanvas.js new file mode 100644 index 00000000000..e63e754ea48 --- /dev/null +++ b/cocos2d/core/renderer/RendererCanvas.js @@ -0,0 +1,289 @@ +/**************************************************************************** + 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, + _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)", + + getRenderCmd: function (renderableObject) { + //TODO Add renderCmd pool here + return renderableObject._createRenderCmd(); + }, + + /** + * drawing all renderer command to context (default is cc._renderContext) + * @param {cc.CanvasContextWrapper} [ctx=cc._renderContext] + */ + rendering: function (ctx) { + var locCmds = this._renderCmds, i, len, + scaleX = cc.view.getScaleX(), + scaleY = cc.view.getScaleY(); + var context = ctx || cc._renderContext; + context.computeRealOffsetY(); + for (i = 0, len = locCmds.length; i < len; i++) { + locCmds[i].rendering(context, scaleX, scaleY); + } + }, + + /** + * 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.js.isUndefined(scaleX) ? 1 : scaleX; + scaleY = cc.js.isUndefined(scaleY) ? 1 : scaleY; + instanceID = instanceID || this._currentID; + var locCmds = this._cacheToCanvasCmds[instanceID], i, len; + ctx.computeRealOffsetY(); + for (i = 0, len = locCmds.length; i < len; i++) { + locCmds[i].rendering(ctx, scaleX, scaleY); + } + locCmds.length = 0; + var locIDs = this._cacheInstanceIds; + delete this._cacheToCanvasCmds[instanceID]; + cc.js.array.remove(locIDs, instanceID); + + 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; + }, + + 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 () { + var viewport = cc._canvas; + var wrapper = cc._renderContext; + var ctx = wrapper.getContext(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.clearRect(0, 0, viewport.width, viewport.height); + if (this._clearColor.r !== 0 || + this._clearColor.g !== 0 || + this._clearColor.b !== 0) { + wrapper.setFillStyle(this._clearFillStyle); + wrapper.setGlobalAlpha(this._clearColor.a); + ctx.fillRect(0, 0, viewport.width, viewport.height); + } + }, + + clearRenderCommands: function () { + this._renderCmds.length = 0; + this._cacheInstanceIds.length = 0; + this._isCacheToCanvasOn = false; + }, + + pushRenderCommand: function (cmd) { + if(!cmd._needDraw) + return; + 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, -t.b, -t.c, t.d, t.tx * scaleX, -(t.ty * scaleY)); + } else { + this._context.setTransform(t.a, -t.b, -t.c, t.d, 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/cocos2d/core/renderer/RendererWebGL.js b/cocos2d/core/renderer/RendererWebGL.js new file mode 100644 index 00000000000..7f836703237 --- /dev/null +++ b/cocos2d/core/renderer/RendererWebGL.js @@ -0,0 +1,154 @@ +/**************************************************************************** + 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.rendererWebGL = { + childrenOrderDirty: true, + _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(), //background color,default BLACK + + getRenderCmd: function (renderableObject) { + //TODO Add renderCmd pool here + return renderableObject._createRenderCmd(); + }, + + /** + * drawing all renderer command to context (default is cc._renderContext) + * @param {WebGLRenderingContext} [ctx=cc._renderContext] + */ + rendering: function (ctx) { + var locCmds = this._renderCmds, + i, + len; + var context = ctx || cc._renderContext; + for (i = 0, len = locCmds.length; i < len; i++) { + locCmds[i].rendering(context); + } + }, + + _turnToCacheMode: function (renderTextureID) { + this._isCacheToBufferOn = true; + renderTextureID = renderTextureID || 0; + this._cacheToBufferCmds[renderTextureID] = []; + this._cacheInstanceIds.push(renderTextureID); + this._currentID = renderTextureID; + }, + + _turnToNormalMode: function () { + this._isCacheToBufferOn = false; + }, + + /** + * drawing all renderer command to cache canvas' context + * @param {Number} [renderTextureId] + */ + _renderingToBuffer: function (renderTextureId) { + renderTextureId = renderTextureId || this._currentID; + var locCmds = this._cacheToBufferCmds[renderTextureId], i, len; + var ctx = cc._renderContext, locIDs = this._cacheInstanceIds; + for (i = 0, len = locCmds.length; i < len; i++) { + locCmds[i].rendering(ctx); + } + locCmds.length = 0; + delete this._cacheToBufferCmds[renderTextureId]; + cc.js.array.remove(locIDs, renderTextureId); + + if (locIDs.length === 0) + this._isCacheToBufferOn = false; + else + this._currentID = locIDs[locIDs.length - 1]; + }, + + //reset renderer's flag + resetFlag: function () { + 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 + for (var i = 0, len = locPool.length; i < len; i++) { + 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) { + //if (this._transformNodePool.indexOf(node) === -1) + this._transformNodePool.push(node); + }, + + clearRenderCommands: function () { + this._renderCmds.length = 0; + }, + + clear: function () { + var gl = cc._renderContext; + gl.clearColor(this._clearColor.r, this._clearColor.g, this._clearColor.b, this._clearColor.a); + 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._needDraw) + 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); + } + } +}; diff --git a/cocos2d/core/scenes/CCLoaderScene.js b/cocos2d/core/scenes/CCLoaderScene.js new file mode 100644 index 00000000000..646fcdae766 --- /dev/null +++ b/cocos2d/core/scenes/CCLoaderScene.js @@ -0,0 +1,167 @@ +/**************************************************************************** + 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, + _className:"LoaderScene", + _onProjectionChange: null, + 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); + this._onProjectionChange = function(){ + self._updateTransform(); + }; + cc.director.on(cc.Director.EVENT_PROJECTION_CHANGED, this._onProjectionChange); + }, + /** + * custom onExit + */ + onExit: function () { + cc.director.off(cc.Director.EVENT_PROJECTION_CHANGED, this._onProjectionChange); + 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.js.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._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.loaderScene.initWithResources(resources, cb, target); + + cc.director.runScene(_cc.loaderScene); + return _cc.loaderScene; +}; \ No newline at end of file diff --git a/cocos2d/core/scenes/CCScene.js b/cocos2d/core/scenes/CCScene.js new file mode 100644 index 00000000000..61cdd1280bc --- /dev/null +++ b/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 and 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/cocos2d/core/sprites/CCAnimation.js b/cocos2d/core/sprites/CCAnimation.js new file mode 100644 index 00000000000..ac60096ce8b --- /dev/null +++ b/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.js.array.verifyType(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.js.array.verifyType(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; diff --git a/cocos2d/core/sprites/CCAnimationCache.js b/cocos2d/core/sprites/CCAnimationCache.js new file mode 100644 index 00000000000..2255d6be357 --- /dev/null +++ b/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/cocos2d/core/sprites/CCBakeSprite.js b/cocos2d/core/sprites/CCBakeSprite.js new file mode 100644 index 00000000000..f24bc051212 --- /dev/null +++ b/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 + * @extends 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/cocos2d/core/sprites/CCSprite.js b/cocos2d/core/sprites/CCSprite.js new file mode 100644 index 00000000000..9d2713c2396 --- /dev/null +++ b/cocos2d/core/sprites/CCSprite.js @@ -0,0 +1,1032 @@ +/**************************************************************************** + 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 EventTarget = require("../cocos2d/core/event/event-target"); + +/** + *

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); + 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 EventTarget instead + */ + addLoadedEventListener:function(callback, target){ + this.once("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.Vec2} + */ + 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); + + if(!spriteFrame.textureLoaded()){ + //add event listener + this._textureLoaded = false; + spriteFrame.once("load", this._renderCmd._spriteFrameLoadedCallback, this._renderCmd); + } + + //TODO + var rotated = cc._renderType === cc.game.RENDER_TYPE_CANVAS ? false : spriteFrame._rotated; + var ret = this.initWithTexture(spriteFrame.getTexture(), spriteFrame.getRect(), rotated); + this.setSpriteFrame(spriteFrame); + + return ret; + }, + + /** + * 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) { + this.textureAtlas = batchNode.getTextureAtlas(); // weak ref + this._batchNode = 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; + }, + + /** + * Sort all children of this sprite node. + * @override + */ + sortAllChildren:function () { + if (this._reorderChildDirty) { + var _children = this._children; + + // insertion sort + var len = _children.length, i, j, tmp; + for(i=1; i= 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; + } + + if (this._batchNode) { + this._arrayMakeObjectsPerformSelector(_children, cc.Node._stateCallbackType.sortAllChildren); + } + + //don't need to check children recursively, that's done in visit of each child + this._reorderChildDirty = false; + } + + }, + + /** + * Reorders a child according to a new z value. (override cc.Node ) + * @param {cc.Node} child + * @param {Number} zOrder + * @override + */ + reorderChild:function (child, zOrder) { + cc.assert(child, cc._LogInfos.Sprite.reorderChild_2); + if(this._children.indexOf(child) === -1){ + cc.log(cc._LogInfos.Sprite.reorderChild); + return; + } + + if (zOrder === child.zIndex) + return; + + if (this._batchNode && !this._reorderChildDirty) { + this._setReorderChildDirtyRecursively(); + this._batchNode.reorderBatch(true); + } + cc.Node.prototype.reorderChild.call(this, child, zOrder); + }, + + /** + * Removes a child from the sprite. + * @param child + * @param cleanup whether or not cleanup all running actions + * @override + */ + removeChild:function (child, cleanup) { + if (this._batchNode) + this._batchNode.removeSpriteFromAtlas(child); + cc.Node.prototype.removeChild.call(this, child, cleanup); + }, + + /** + * Sets whether the sprite is visible or not. + * @param {Boolean} visible + * @override + */ + setVisible:function (visible) { + cc.Node.prototype.setVisible.call(this, visible); + this._renderCmd.setDirtyRecursively(true); + }, + + /** + * Removes all children from the container. + * @param cleanup whether or not cleanup all running actions + * @override + */ + removeAllChildren:function (cleanup) { + var locChildren = this._children, locBatchNode = this._batchNode; + if (locBatchNode && locChildren != null) { + for (var i = 0, len = locChildren.length; i < len; i++) + locBatchNode.removeSpriteFromAtlas(locChildren[i]); + } + + cc.Node.prototype.removeAllChildren.call(this, cleanup); + this._hasChildren = false; + }, + + // + // cc.Node property overloads + // + + /** + * Sets whether ignore anchor point for positioning + * @param {Boolean} relative + * @override + */ + ignoreAnchorPointForPosition:function (relative) { + if(this._batchNode){ + cc.log(cc._LogInfos.Sprite.ignoreAnchorPointForPosition); + return; + } + cc.Node.prototype.ignoreAnchorPointForPosition.call(this, relative); + }, + + /** + * 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; + }, + + _setReorderChildDirtyRecursively:function () { + //only set parents flag the first time + if (!this._reorderChildDirty) { + this._reorderChildDirty = true; + var pNode = this._parent; + while (pNode && pNode !== this._batchNode) { + pNode._setReorderChildDirtyRecursively(); + pNode = pNode.parent; + } + } + }, + + // 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 (cc.js.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); + 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 this._renderCmd.getQuad(); + }, + + /** + * 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; + + this._renderCmd._init(); + // 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); + return this.initWithTexture(tex, rect || cc.rect(0, 0, tex._contentSize.width, tex._contentSize.height)); + } else { + 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.SpriteBatchNode.initWithTexture); + + 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; + + // default transform anchor: center + _t.setAnchorPoint(0.5, 0.5); + + // zwoptex default values + _t._offsetPosition.x = 0; + _t._offsetPosition.y = 0; + _t._hasChildren = false; + + this._renderCmd._init(); + + var locTextureLoaded = texture.isLoaded(); + _t._textureLoaded = locTextureLoaded; + + if (!locTextureLoaded) { + _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(_t.texture) + _t.texture.off("load", _t._renderCmd._textureLoadedCallback, _t._renderCmd); + texture.once("load", _t._renderCmd._textureLoadedCallback, _t._renderCmd); + _t.setTexture(texture); + return true; + } + + 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); + this.emit("load"); + 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; + + // rendering using batch node + if (_t._batchNode) { + // update dirty, don't update _recursiveDirty + _t.dirty = true; + } else { + // self rendering + // Atlas: Vertex + this._renderCmd._resetForBatchNode(); + } + }, + + // BatchNode methods + /** + * Updates the quad according the the rotation, position, scale values. + * @function + */ + updateTransform: function(){ + this._renderCmd.updateTransform(); + }, + + /** + * Add child to sprite (override cc.Node) + * @function + * @param {cc.Sprite} child + * @param {Number} localZOrder child's zOrder + * @param {String} [tag] child's tag + * @override + */ + addChild: function (child, localZOrder, tag) { + cc.assert(child, cc._LogInfos.SpriteBatchNode.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(cc.js.isString(newFrame)){ + newFrame = cc.spriteFrameCache.getSpriteFrame(newFrame); + cc.assert(newFrame, cc._LogInfos.Sprite.setSpriteFrame) + } + + this.setNodeDirty(true); + + var frameOffset = newFrame.getOffset(); + _t._unflippedOffsetPositionFromCenter.x = frameOffset.x; + _t._unflippedOffsetPositionFromCenter.y = frameOffset.y; + + // update rect + var pNewTexture = newFrame.getTexture(); + var locTextureLoaded = newFrame.textureLoaded(); + if (!locTextureLoaded) { + if (pNewTexture !== _t._texture) + _t.setTexture(pNewTexture); + _t._textureLoaded = false; + newFrame.once("load", function (event) { + var sender = event.currentTarget; + _t._textureLoaded = true; + _t.setTextureRect(sender.getRect(), sender.isRotated(), sender.getOriginalSize()); + _t.emit("load"); + _t.setColor(_t.color); + }, _t); + }else{ + // update texture before updating texture rect + if (pNewTexture !== _t._texture) + _t.setTexture(pNewTexture); + _t.setTextureRect(newFrame.getRect(), newFrame.isRotated(), newFrame.getOriginalSize()); + } + this._renderCmd._updateForSetSpriteFrame(pNewTexture); + }, + + /** + * 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) { + var _t = this; + _t._batchNode = spriteBatchNode; // weak reference + + // self render + if (!_t._batchNode) { + _t.atlasIndex = cc.Sprite.INDEX_NOT_INITIALIZED; + _t.textureAtlas = null; + _t._recursiveDirty = false; + _t.dirty = false; + + this._renderCmd._resetForBatchNode(); + } else { + // using batch + _t._transformToBatch = cc.affineTransformIdentity(); + _t.textureAtlas = _t._batchNode.getTextureAtlas(); // weak ref + } + }, + + // 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 = cc.js.isString(texture); + + if(isFileName) + texture = cc.textureCache.addImage(texture); + + if(texture._textureLoaded){ + this._setTexture(texture, isFileName); + this.setColor(this._realColor); + this._textureLoaded = true; + this.emit("load"); + }else{ + this._renderCmd._setTexture(texture); + texture.once("load", function (event) { + this._setTexture(texture, isFileName); + this.setColor(this._realColor); + this._textureLoaded = true; + this.emit("load"); + }, this); + } + }, + + _setTexture: function(texture, change){ + this._renderCmd._setTexture(texture); + if(change) + this._changeRectWithTexture(texture); + }, + + _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; + +EventTarget.polyfill(cc.Sprite.prototype); + +cc.assert(cc.js.isFunction(cc._tmp.PrototypeSprite), cc._LogInfos.MissingFile, "SpritesPropertyDefine.js"); +cc._tmp.PrototypeSprite(); +delete cc._tmp.PrototypeSprite; \ No newline at end of file diff --git a/cocos2d/core/sprites/CCSpriteBatchNode.js b/cocos2d/core/sprites/CCSpriteBatchNode.js new file mode 100644 index 00000000000..cdd4a6c240c --- /dev/null +++ b/cocos2d/core/sprites/CCSpriteBatchNode.js @@ -0,0 +1,676 @@ +/**************************************************************************** + 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 + * @param {Number} capacity + * @example + * + * // 1. create a SpriteBatchNode with image path + * var spriteBatchNode = new cc.SpriteBatchNode("res/animations/grossini.png", 50); + * + * // 2. create a SpriteBatchNode with texture + * var texture = cc.textureCache.addImage("res/animations/grossini.png"); + * var spriteBatchNode = new cc.SpriteBatchNode(texture,50); + * + * @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... + _descendants: null, + _className: "SpriteBatchNode", + + ctor: function (fileImage, capacity) { + cc.Node.prototype.ctor.call(this); + this._descendants = []; + this._blendFunc = new cc.BlendFunc(cc.BLEND_SRC, cc.BLEND_DST); + + var texture2D; + capacity = capacity || cc.SpriteBatchNode.DEFAULT_CAPACITY; + if (cc.js.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, capacity); + }, + + /** + *

+ * This is the opposite of "addQuadFromSprite.
+ * It add the sprite to the children and descendants array, but it doesn't update add it to the texture atlas
+ *

+ * @param {cc.Sprite} child + * @param {Number} z zOrder + * @param {Number} aTag + * @return {cc.SpriteBatchNode} + */ + addSpriteWithoutQuad: function (child, z, aTag) { + cc.assert(child, cc._LogInfos.SpriteBatchNode.addSpriteWithoutQuad_2); + + if (!(child instanceof cc.Sprite)) { + cc.log(cc._LogInfos.SpriteBatchNode.addSpriteWithoutQuad); + return null; + } + + // quad index is Z + child.atlasIndex = z; + + // XXX: optimize with a binary search + var i = 0, len, locDescendants = this._descendants; + if (locDescendants && locDescendants.length > 0) { + for (i = 0, len = locDescendants.length; i < len; i++) { + var obj = locDescendants[i]; + if (obj && (obj.atlasIndex >= z)) + break; + } + } + locDescendants.splice(i, 0, child); + + // IMPORTANT: Call super, and not self. Avoid adding it to the texture atlas array + cc.Node.prototype.addChild.call(this, child, z, aTag); + + //#issue 1262 don't use lazy sorting, tiles are added as quads not as sprites, so sprites need to be added in order + this.reorderBatch(false); + return this; + }, + + // property + /** + * Return TextureAtlas of cc.SpriteBatchNode + * @return {cc.TextureAtlas} + */ + getTextureAtlas: function () { + return this._renderCmd.getTextureAtlas(); + }, + + /** + * TextureAtlas of cc.SpriteBatchNode setter + * @param {cc.TextureAtlas} textureAtlas + */ + setTextureAtlas: function (textureAtlas) { + this._renderCmd.getTextureAtlas(textureAtlas); + }, + + /** + * Return Descendants of cc.SpriteBatchNode + * @return {Array} + */ + getDescendants: function () { + return this._descendants; + }, + + /** + *

+ * 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); + }, + + _setNodeDirtyForCache: function () { + if(this._renderCmd && this._renderCmd._setNodeDirtyForCache) + this._renderCmd._setNodeDirtyForCache(); + }, + + /** + *

+ * 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); + }, + + /** + * Increase Atlas Capacity + */ + increaseAtlasCapacity: function () { + this._renderCmd.increaseAtlasCapacity(); + }, + + /** + * 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); + }, + + /** + * Rebuild index in order for child + * @param {cc.Sprite} pobParent + * @param {Number} index + * @return {Number} + */ + rebuildIndexInOrder: function (pobParent, index) { + var children = pobParent.children; + if (children && children.length > 0) { + for (var i = 0; i < children.length; i++) { + var obj = children[i]; + if (obj && (obj.zIndex < 0)) + index = this.rebuildIndexInOrder(obj, index); + } + } + // ignore self (batch node) + if (!pobParent === this) { + pobParent.atlasIndex = index; + index++; + } + if (children && children.length > 0) { + for (i = 0; i < children.length; i++) { + obj = children[i]; + if (obj && (obj.zIndex >= 0)) + index = this.rebuildIndexInOrder(obj, index); + } + } + return index; + }, + + /** + * Returns highest atlas index in child + * @param {cc.Sprite} sprite + * @return {Number} + */ + highestAtlasIndexInChild: function (sprite) { + var children = sprite.children; + + if (!children || children.length === 0) + return sprite.atlasIndex; + else + return this.highestAtlasIndexInChild(children[children.length - 1]); + }, + + /** + * Returns lowest atlas index in child + * @param {cc.Sprite} sprite + * @return {Number} + */ + lowestAtlasIndexInChild: function (sprite) { + var children = sprite.children; + if (!children || children.length === 0) + return sprite.atlasIndex; + else + return this.lowestAtlasIndexInChild(children[children.length - 1]); + }, + + /** + * Returns atlas index for child + * @param {cc.Sprite} sprite + * @param {Number} nZ + * @return {Number} + */ + atlasIndexForChild: function (sprite, nZ) { + var selParent = sprite.parent; + var brothers = selParent.children; + var childIndex = brothers.indexOf(sprite); + + // ignore parent Z if parent is spriteSheet + var ignoreParent = selParent === this; + var previous = null; + if (childIndex > 0 && childIndex < cc.UINT_MAX) + previous = brothers[childIndex - 1]; + + // first child of the sprite sheet + if (ignoreParent) { + if (childIndex === 0) + return 0; + return this.highestAtlasIndexInChild(previous) + 1; + } + + // parent is a cc.Sprite, so, it must be taken into account + // first child of an cc.Sprite ? + if (childIndex === 0) { + // less than parent and brothers + if (nZ < 0) + return selParent.atlasIndex; + else + return selParent.atlasIndex + 1; + } else { + // previous & sprite belong to the same branch + if ((previous.zIndex < 0 && nZ < 0) || (previous.zIndex >= 0 && nZ >= 0)) + return this.highestAtlasIndexInChild(previous) + 1; + + // else (previous < 0 and sprite >= 0 ) + return selParent.atlasIndex + 1; + } + }, + + /** + * Sprites use this to start sortChildren, don't call this manually + * @param {Boolean} reorder + */ + 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); + }, + + /** + * Reorder children (override reorderChild of cc.Node) + * @override + * @param {cc.Sprite} child + * @param {Number} zOrder + */ + reorderChild: function (child, zOrder) { + cc.assert(child, cc._LogInfos.SpriteBatchNode.reorderChild_2); + if (this._children.indexOf(child) === -1) { + cc.log(cc._LogInfos.SpriteBatchNode.reorderChild); + return; + } + if (zOrder === child.zIndex) + return; + + //set the z-order and sort later + cc.Node.prototype.reorderChild.call(this, child, zOrder); + //this.setNodeDirty(); + }, + + /** + * Removes a child from cc.SpriteBatchNode (override removeChild of cc.Node) + * @param {cc.Sprite} child + * @param {Boolean} cleanup + */ + removeChild: function (child, cleanup) { + // explicit null handling + if (child == null) + return; + if (this._children.indexOf(child) === -1) { + cc.log(cc._LogInfos.SpriteBatchNode.removeChild); + return; + } + + // cleanup before removing + this.removeSpriteFromAtlas(child); + cc.Node.prototype.removeChild.call(this, child, cleanup); + }, + + /** + *

+ * 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.SpriteBatchNode.updateQuadFromSprite_2); + if (!(sprite instanceof cc.Sprite)) { + cc.log(cc._LogInfos.SpriteBatchNode.updateQuadFromSprite); + return; + } + this._renderCmd.checkAtlasCapacity(); + + // + // update the quad directly. Don't add the sprite to the scene graph + // + sprite.batchNode = this; + sprite.atlasIndex = index; + sprite.dirty = true; + // UpdateTransform updates the textureAtlas quad + sprite.updateTransform(); + }, + + /** + *

+ * Inserts a quad at a certain index into the texture atlas. The cc.Sprite won't be added into the children array.
+ * This method should be called only when you are dealing with very big AtlasSprite 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 (cc.LabelBMFont) + *

+ * @function + * @param {cc.Sprite} sprite + * @param {Number} index + */ + insertQuadFromSprite: function (sprite, index) { + cc.assert(sprite, cc._LogInfos.SpriteBatchNode.insertQuadFromSprite_2); + if (!(sprite instanceof cc.Sprite)) { + cc.log(cc._LogInfos.SpriteBatchNode.insertQuadFromSprite); + return; + } + this._renderCmd.insertQuad(sprite, index); + + // + // update the quad directly. Don't add the sprite to the scene graph + // + sprite.batchNode = this; + sprite.atlasIndex = index; + + // XXX: updateTransform will update the textureAtlas too, using updateQuad. + // XXX: so, it should be AFTER the insertQuad + sprite.dirty = true; + sprite.updateTransform(); + this._renderCmd.cutting(sprite, index); + }, + + /** + *

+ * Initializes a cc.SpriteBatchNode with a texture2d and capacity of children.
+ * The capacity will be increased in 33% in runtime if it run out of space.
+ * Please pass parameters to constructor to initialize the sprite batch node, do not call this function yourself. + *

+ * @function + * @param {cc.Texture2D} tex + * @param {Number} [capacity] + * @return {Boolean} + */ + initWithTexture: function (tex, capacity) { + this._children.length = 0; + this._descendants.length = 0; + + capacity = capacity || cc.SpriteBatchNode.DEFAULT_CAPACITY; + this._renderCmd.initWithTexture(tex, capacity); + return true; + }, + + /** + * Insert a child + * @param {cc.Sprite} sprite The child sprite + * @param {Number} index The insert index + */ + insertChild: function (sprite, index) { + //TODO WebGL only ? + sprite.batchNode = this; + sprite.atlasIndex = index; + sprite.dirty = true; + + this._renderCmd.insertQuad(sprite, index); + this._descendants.splice(index, 0, sprite); + + // update indices + var i = index + 1, locDescendant = this._descendants; + if (locDescendant && locDescendant.length > 0) { + for (; i < locDescendant.length; i++) + locDescendant[i].atlasIndex++; + } + + // add children recursively + var locChildren = sprite.children, child, l; + if (locChildren) { + for (i = 0, l = locChildren.length || 0; i < l; i++) { + child = locChildren[i]; + if (child) { + var getIndex = this.atlasIndexForChild(child, child.zIndex); + this.insertChild(child, getIndex); + } + } + } + }, + + /** + * Add child at the end, faster than insert child + * @function + * @param {cc.Sprite} sprite + */ + appendChild: function (sprite) { + this._reorderChildDirty = true; + sprite.batchNode = this; + sprite.dirty = true; + + this._descendants.push(sprite); + var index = this._descendants.length - 1; + + sprite.atlasIndex = index; + this._renderCmd.insertQuad(sprite, index); + + // add children recursively + var children = sprite.children; + for (var i = 0, l = children.length || 0; i < l; i++) + this.appendChild(children[i]); + }, + + /** + * Removes sprite from TextureAtlas + * @function + * @param {cc.Sprite} sprite + */ + removeSpriteFromAtlas: function (sprite) { + this._renderCmd.removeQuadAtIndex(sprite.atlasIndex); + + // Cleanup sprite. It might be reused (issue #569) + sprite.batchNode = null; + var locDescendants = this._descendants; + var index = locDescendants.indexOf(sprite); + if (index !== -1) { + locDescendants.splice(index, 1); + + // update all sprites beyond this one + var len = locDescendants.length; + for (; index < len; ++index) { + var s = locDescendants[index]; + s.atlasIndex--; + } + } + + // remove children recursively + var children = sprite.children; + if (children) { + for (var i = 0, l = children.length || 0; i < l; i++) + children[i] && this.removeSpriteFromAtlas(children[i]); + } + }, + // CCTextureProtocol + /** + * Returns texture of the sprite batch node + * @function + * @return {cc.Texture2D} + */ + getTexture: function () { + return this._renderCmd.getTexture(); + }, + + /** + * Sets the texture of the sprite batch node. + * @function + * @param {cc.Texture2D} texture + */ + setTexture: function(texture){ + this._renderCmd.setTexture(texture); + }, + + /** + * 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 != null, cc._LogInfos.SpriteBatchNode.addChild_3); + + if(!this._renderCmd.isValidChild(child)) + return; + + zOrder = (zOrder == null) ? child.zIndex : zOrder; + tag = (tag == null) ? child.tag : tag; + cc.Node.prototype.addChild.call(this, child, zOrder, tag); + this.appendChild(child); + //this.setNodeDirty(); + }, + + /** + * Removes all children from the container and do a cleanup all running actions depending on the cleanup parameter.
+ * (override removeAllChildren of cc.Node) + * @function + * @param {Boolean} cleanup + */ + removeAllChildren: function (cleanup) { + // Invalidate atlas index. issue #569 + // useSelfRender should be performed on all descendants. issue #1216 + var locDescendants = this._descendants; + if (locDescendants && locDescendants.length > 0) { + for (var i = 0, len = locDescendants.length; i < len; i++) { + if (locDescendants[i]) + locDescendants[i].batchNode = null; + } + } + cc.Node.prototype.removeAllChildren.call(this, cleanup); + this._descendants.length = 0; + this._renderCmd.removeAllQuads(); + }, + + /** + * Sort all children nodes (override draw of cc.Node) + */ + sortAllChildren: function () { + if (this._reorderChildDirty) { + var childrenArr = this._children; + var i, j = 0, length = childrenArr.length, tempChild; + //insertion sort + for (i = 1; i < length; i++) { + var tempItem = childrenArr[i]; + j = i - 1; + tempChild = childrenArr[j]; + + //continue moving element downwards while zOrder is smaller or when zOrder is the same but mutatedIndex is smaller + while (j >= 0 && ( tempItem._localZOrder < tempChild._localZOrder || + ( tempItem._localZOrder === tempChild._localZOrder && tempItem.arrivalOrder < tempChild.arrivalOrder ))) { + childrenArr[j + 1] = tempChild; + j = j - 1; + tempChild = childrenArr[j]; + } + childrenArr[j + 1] = tempItem; + } + + //sorted now check all children + if (childrenArr.length > 0) { + //first sort all children recursively based on zOrder + this._arrayMakeObjectsPerformSelector(childrenArr, cc.Node._stateCallbackType.sortAllChildren); + this._renderCmd.updateChildrenAtlasIndex(childrenArr); + } + this._reorderChildDirty = false; + } + }, + + _createRenderCmd: function(){ + if(cc._renderType === cc.game.RENDER_TYPE_CANVAS) + return new cc.SpriteBatchNode.CanvasRenderCmd(this); + else + return new cc.SpriteBatchNode.WebGLRenderCmd(this); + } +}); + +var _p = cc.SpriteBatchNode.prototype; + +// Override properties +cc.defineGetterSetter(_p, "texture", _p.getTexture, _p.setTexture); +cc.defineGetterSetter(_p, "textureAtlas", _p.getTextureAtlas, _p.setTextureAtlas); + +// Extended properties +/** @expose */ +_p.descendants; +cc.defineGetterSetter(_p, "descendants", _p.getDescendants); + + +/** + * @constant + * @type Number + */ +cc.SpriteBatchNode.DEFAULT_CAPACITY = 29; + +/** + *

+ * 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 + * @param {Number} capacity + * @return {cc.SpriteBatchNode} + */ +cc.SpriteBatchNode.create = function (fileImage, capacity) { + return new cc.SpriteBatchNode(fileImage, capacity); +}; + +/** + * @deprecated since v3.0, please use new construction instead + * @see cc.SpriteBatchNode + * @function + */ +cc.SpriteBatchNode.createWithTexture = cc.SpriteBatchNode.create; \ No newline at end of file diff --git a/cocos2d/core/sprites/CCSpriteBatchNodeCanvasRenderCmd.js b/cocos2d/core/sprites/CCSpriteBatchNodeCanvasRenderCmd.js new file mode 100644 index 00000000000..4f134b82844 --- /dev/null +++ b/cocos2d/core/sprites/CCSpriteBatchNodeCanvasRenderCmd.js @@ -0,0 +1,100 @@ +/**************************************************************************** + 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(){ + //SpriteBatchNode's canvas render command + cc.SpriteBatchNode.CanvasRenderCmd = function(renderable){ + cc.Node.CanvasRenderCmd.call(this, renderable); + + this._texture = null; + this._textureToRender = null; + }; + + var proto = cc.SpriteBatchNode.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + proto.constructor = cc.SpriteBatchNode.CanvasRenderCmd; + + proto.checkAtlasCapacity = function(){}; + + proto.isValidChild = function(child){ + if (!(child instanceof cc.Sprite)) { + cc.log(cc._LogInfos.Sprite.addChild_4); + return false; + } + return true; + }; + + proto.initWithTexture = function(texture, capacity){ + this._textureToRender = this._texture = texture; + }; + + proto.insertQuad = function(sprite, index){}; + + proto.increaseAtlasCapacity = function(){}; + + proto.removeQuadAtIndex = function(){}; + + proto.removeAllQuads = function(){}; + + proto.getTexture = function(){ + return this._texture; + }; + + proto.setTexture = function(texture){ + this._texture = texture; + var locChildren = this._node._children; + for (var i = 0; i < locChildren.length; i++) + locChildren[i].setTexture(texture); + }; + + proto.updateChildrenAtlasIndex = function(children){ + this._node._descendants.length = 0; + //update _descendants after sortAllChildren + for (var i = 0, len = children.length; i < len; i++) + this._updateAtlasIndex(children[i]); + }; + + proto._updateAtlasIndex = function (sprite) { + var locDescendants = this._node._descendants; + var pArray = sprite.children, i, len = pArray.length; + for (i = 0; i < len; i++) { + if (pArray[i]._localZOrder < 0) { + locDescendants.push(pArray[i]); + } else + break + } + locDescendants.push(sprite); + for (; i < len; i++) { + locDescendants.push(pArray[i]); + } + }; + + proto.getTextureAtlas = function(){}; + + proto.setTextureAtlas = function(textureAtlas){}; + + proto.cutting = function(sprite, index){ + var node = this._node; + node._children.splice(index, 0, sprite); + } +})(); \ No newline at end of file diff --git a/cocos2d/core/sprites/CCSpriteBatchNodeWebGLRenderCmd.js b/cocos2d/core/sprites/CCSpriteBatchNodeWebGLRenderCmd.js new file mode 100644 index 00000000000..06dd6fd5833 --- /dev/null +++ b/cocos2d/core/sprites/CCSpriteBatchNodeWebGLRenderCmd.js @@ -0,0 +1,243 @@ +/**************************************************************************** + 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(){ + //SpriteBatchNode's WebGL render command + cc.SpriteBatchNode.WebGLRenderCmd = function(renderable){ + cc.Node.WebGLRenderCmd.call(this, renderable); + this._needDraw = true; + + this._textureAtlas = null; + }; + + var proto = cc.SpriteBatchNode.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + proto.constructor = cc.SpriteBatchNode.WebGLRenderCmd; + + proto.isValidChild = function(child){ + if (!(child instanceof cc.Sprite)) { + cc.log(cc._LogInfos.Sprite.addChild_4); + return false; + } + if (child.texture != this.getTexture()) { + cc.log(cc._LogInfos.Sprite.addChild_5); + return false; + } + return true; + }; + + proto.rendering = function () { + var node = this._node; + if (this._textureAtlas.totalQuads === 0) + return; + + this._shaderProgram.use(); + this._shaderProgram._setUniformForMVPMatrixWithMat4(this._stackMatrix); + node._arrayMakeObjectsPerformSelector(node._children, cc.Node._stateCallbackType.updateTransform); + cc.glBlendFunc(node._blendFunc.src, node._blendFunc.dst); + + this._textureAtlas.drawQuads(); + }; + + proto.visit = function(parentCmd){ + var node = this._node; + // quick return if not visible + if (!node._visible) + return; + + if (node._parent && node._parent._renderCmd) + this._curLevel = node._parent._renderCmd._curLevel + 1; + + var currentStack = cc.current_stack; + + //optimize performance for javascript + currentStack.stack.push(currentStack.top); + + if(!(this._dirtyFlag & cc.Node._dirtyFlags.transformDirty)) //batchNode's transform must update in visit + this.transform(parentCmd); + this.updateStatus(parentCmd); //because batchNode doesn't visit its children. + currentStack.top = this._stackMatrix; + + node.sortAllChildren(); + + cc.renderer.pushRenderCommand(this); + + this._dirtyFlag = 0; + //optimize performance for javascript + currentStack.top = currentStack.stack.pop(); + }; + + proto.checkAtlasCapacity = function(index){ + // make needed room + var locAtlas = this._textureAtlas; + while (index >= locAtlas.capacity || locAtlas.capacity === locAtlas.totalQuads) { + this.increaseAtlasCapacity(); + } + }; + + proto.increaseAtlasCapacity = function(){ + // if we're going beyond the current TextureAtlas's capacity, + // all the previously initialized sprites will need to redo their texture coords + // this is likely computationally expensive + var locCapacity = this._textureAtlas.capacity; + var quantity = Math.floor((locCapacity + 1) * 4 / 3); + + cc.log(cc._LogInfos.SpriteBatchNode.increaseAtlasCapacity, locCapacity, quantity); + + if (!this._textureAtlas.resizeCapacity(quantity)) { + // serious problems + cc.log(cc._LogInfos.SpriteBatchNode.increaseAtlasCapacity_2); + } + }; + + proto.initWithTexture = function(texture, capacity){ + this._textureAtlas = new cc.TextureAtlas(); + this._textureAtlas.initWithTexture(texture, capacity); + this._updateBlendFunc(); + this._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR); + }; + + proto.insertQuad = function(sprite, index){ + var locTextureAtlas = this._textureAtlas; + if (locTextureAtlas.totalQuads >= locTextureAtlas.capacity) + this.increaseAtlasCapacity(); + locTextureAtlas.insertQuad(sprite.quad, index); + }; + + proto.removeQuadAtIndex = function(index){ + this._textureAtlas.removeQuadAtIndex(index); // remove from TextureAtlas + }; + + proto.getTexture = function(){ + return this._textureAtlas.texture; + }; + + proto.setTexture = function(texture){ + this._textureAtlas.setTexture(texture); + if(texture) + this._updateBlendFunc(); + }; + + proto.removeAllQuads = function(){ + this._textureAtlas.removeAllQuads(); + }; + + proto._swap = function (oldIndex, newIndex) { + var locDescendants = this._node._descendants; + var locTextureAtlas = this._textureAtlas; + var quads = locTextureAtlas.quads; + var tempItem = locDescendants[oldIndex]; + var tempIteQuad = cc.V3F_C4B_T2F_QuadCopy(quads[oldIndex]); + + //update the index of other swapped item + locDescendants[newIndex].atlasIndex = oldIndex; + locDescendants[oldIndex] = locDescendants[newIndex]; + + locTextureAtlas.updateQuad(quads[newIndex], oldIndex); + locDescendants[newIndex] = tempItem; + locTextureAtlas.updateQuad(tempIteQuad, newIndex); + }; + + proto._updateAtlasIndex = function (sprite, curIndex) { + var count = 0; + var pArray = sprite.children; + if (pArray) + count = pArray.length; + + var oldIndex = 0; + if (count === 0) { + oldIndex = sprite.atlasIndex; + sprite.atlasIndex = curIndex; + sprite.arrivalOrder = 0; + if (oldIndex !== curIndex) + this._swap(oldIndex, curIndex); + curIndex++; + } else { + var needNewIndex = true; + if (pArray[0].zIndex >= 0) { + //all children are in front of the parent + oldIndex = sprite.atlasIndex; + sprite.atlasIndex = curIndex; + sprite.arrivalOrder = 0; + if (oldIndex !== curIndex) + this._swap(oldIndex, curIndex); + curIndex++; + needNewIndex = false; + } + for (var i = 0; i < pArray.length; i++) { + var child = pArray[i]; + if (needNewIndex && child.zIndex >= 0) { + oldIndex = sprite.atlasIndex; + sprite.atlasIndex = curIndex; + sprite.arrivalOrder = 0; + if (oldIndex !== curIndex) { + this._swap(oldIndex, curIndex); + } + curIndex++; + needNewIndex = false; + } + curIndex = this._updateAtlasIndex(child, curIndex); + } + + if (needNewIndex) { + //all children have a zOrder < 0) + oldIndex = sprite.atlasIndex; + sprite.atlasIndex = curIndex; + sprite.arrivalOrder = 0; + if (oldIndex !== curIndex) { + this._swap(oldIndex, curIndex); + } + curIndex++; + } + } + return curIndex; + }; + + proto.updateChildrenAtlasIndex = function(children){ + var index = 0; + //fast dispatch, give every child a new atlasIndex based on their relative zOrder (keep parent -> child relations intact) + // and at the same time reorder descedants and the quads to the right index + for (var i = 0; i < children.length; i++) + index = this._updateAtlasIndex(children[i], index); + }; + + proto._updateBlendFunc = function () { + if (!this._textureAtlas.texture.hasPremultipliedAlpha()) { + var blendFunc = this._node._blendFunc; + blendFunc.src = cc.SRC_ALPHA; + blendFunc.dst = cc.ONE_MINUS_SRC_ALPHA; + } + }; + + proto.getTextureAtlas = function(){ + return this._textureAtlas; + }; + + proto.setTextureAtlas = function(textureAtlas){ + if (textureAtlas !== this._textureAtlas) { + this._textureAtlas = textureAtlas; + } + }; + + proto.cutting = function(){}; +})(); diff --git a/cocos2d/core/sprites/CCSpriteCanvasRenderCmd.js b/cocos2d/core/sprites/CCSpriteCanvasRenderCmd.js new file mode 100644 index 00000000000..feab7d30211 --- /dev/null +++ b/cocos2d/core/sprites/CCSpriteCanvasRenderCmd.js @@ -0,0 +1,313 @@ +/**************************************************************************** + 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) { + cc.Node.CanvasRenderCmd.call(this, 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._textureToRender = null; + }; + + var proto = cc.Sprite.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + proto.constructor = cc.Sprite.CanvasRenderCmd; + + proto._init = function () {}; + + proto.setDirtyRecursively = function (value) {}; + + proto._resetForBatchNode = function () {}; + + proto._setTexture = function (texture) { + var node = this._node; + if (node._texture !== texture) { + if (texture) { + node._textureLoaded = texture._textureLoaded; + }else{ + node._textureLoaded = false; + } + node._texture = texture; + 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 * scaleX; + y = locY * scaleY; + w = locWidth * scaleX; + h = locHeight * scaleY; + + if (texture) { + 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.getQuad = function () { + //throw an error. it doesn't support this function. + return null; + }; + + proto._updateForSetSpriteFrame = function (pNewTexture, textureLoaded){ + this._colorized = false; + this._textureCoord.renderX = this._textureCoord.x; + this._textureCoord.renderY = this._textureCoord.y; + textureLoaded = textureLoaded || pNewTexture._textureLoaded; + if (textureLoaded) { + var curColor = this._node.getColor(); + if (curColor.r !== 255 || curColor.g !== 255 || curColor.b !== 255) + this._updateColor(); + } + }; + + proto.updateTransform = function () { //TODO need delete, because Canvas needn't + var _t = this, node = this._node; + + // re-calculate matrix only if it is dirty + if (node.dirty) { + // If it is not visible, or one of its ancestors is not visible, then do nothing: + var locParent = node._parent; + if (!node._visible || ( locParent && locParent !== node._batchNode && locParent._shouldBeHidden)) { + node._shouldBeHidden = true; + } else { + node._shouldBeHidden = false; + + if (!locParent || locParent === node._batchNode) { + node._transformToBatch = _t.getNodeToParentTransform(); + } else { + //cc.assert(_t._parent instanceof cc.Sprite, "Logic error in CCSprite. Parent must be a CCSprite"); + node._transformToBatch = cc.affineTransformConcat(_t.getNodeToParentTransform(), locParent._transformToBatch); + } + } + node._recursiveDirty = false; + node.dirty = false; + } + + // recursively iterate over children + if (node._hasChildren) + node._arrayMakeObjectsPerformSelector(node._children, cc.Node._stateCallbackType.updateTransform); + }; + + proto._updateDisplayColor = function (parentColor) { + cc.Node.CanvasRenderCmd.prototype._updateDisplayColor.call(this, parentColor); + //this._updateColor(); + }; + + proto._spriteFrameLoadedCallback = function (event) { + var node = this._node, spriteFrame = event.currentTarget; + node.setTextureRect(spriteFrame.getRect(), spriteFrame.isRotated(), spriteFrame.getOriginalSize()); + + this._updateColor(); + node.emit("load"); + }; + + proto._textureLoadedCallback = function (event) { + var node = this._node, sender = event.currentTarget; + if (node._textureLoaded) + return; + + node._textureLoaded = true; + var texture = node._texture, + locRect = node._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; + } + + node.texture = sender; + node.setTextureRect(locRect, node._rectRotated); + + //set the texture's color after the it loaded + var locColor = this._displayedColor; + if (locColor.r !== 255 || locColor.g !== 255 || locColor.b !== 255) + this._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.emit("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/cocos2d/core/sprites/CCSpriteFrame.js b/cocos2d/core/sprites/CCSpriteFrame.js new file mode 100644 index 00000000000..4bbebf05c19 --- /dev/null +++ b/cocos2d/core/sprites/CCSpriteFrame.js @@ -0,0 +1,603 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + 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. + ****************************************************************************/ + +var EventTarget = require("../event/event-target"); + + + +/** + *

+ * 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 SpriteFrame + * @extends Asset + * @constructor + * @param {String|Texture2D} filename + * @param {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 {Vec2} [offset] - The offset of the frame in the texture + * @param {Size} [originalSize] - The size of the frame in the texture + * @example {@link utils/api/cocos/docs/cocos2d/core/sprites/SpriteFrame.js} + */ +cc.SpriteFrame = cc.Class(/** @lends cc.SpriteFrame# */{ + name:'cc.SpriteFrame', + extends:require('../assets/CCAsset'), + + //properties:{ + // /** + // * @property pivot + // * @type {cc.Vec2} + // * @default new cc.Vec2(0.5, 0.5) + // */ + // pivot: { + // default: new cc.Vec2(0.5, 0.5), + // tooltip: 'The pivot is normalized, like a percentage.\n' + + // '(0,0) means the bottom-left corner and (1,1) means the top-right corner.\n' + + // 'But you can use values higher than (1,1) and lower than (0,0) too.' + // }, + //}, + + ctor:function () { + var filename = arguments[0]; + var rect = arguments[1]; + var rotated = arguments[2]; + var offset = arguments[3]; + var originalSize = arguments[4]; + + // the location of the sprite on rendering texture + this._rect = new cc.Rect(); + this._rectInPixels = new cc.Rect(); + + // for trimming + this._offset = new cc.Vec2(); + this._offsetInPixels = new cc.Vec2(); + + // for trimming + this._originalSize = new cc.Size(); + this._originalSizeInPixels = new cc.Size(); + + this._rotated = false; + + /** + * Top border of the sprite + * @property insetTop + * @type {Number} + * @default 0 + */ + this.insetTop = 0; + + /** + * Bottom border of the sprite + * @property insetBottom + * @type {Number} + * @default 0 + */ + this.insetBottom = 0; + + /** + * Left border of the sprite + * @property insetLeft + * @type {Number} + * @default 0 + */ + this.insetLeft = 0; + + /** + * Right border of the sprite + * @property insetRight + * @type {Number} + * @default 0 + */ + this.insetRight = 0; + + this._texture = null; + this._textureFilename = ''; + this._textureLoaded = false; + + // The current parsing uuid for editor + this._loadingUuid = ''; + + 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 EventTarget API instead + */ + addLoadedEventListener:function(callback, target){ + this.once("load", callback, target); + }, + + /** + * Gets the rect of the frame in the texture. + * @method getRectInPixels + * @return {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. + * @method setRectInPixels + * @param {Rect} rectInPixels + */ + setRectInPixels:function (rectInPixels) { + var rect = this._rectInPixels; + if (!rect){ + this._rectInPixels = rect = cc.rect(); + } + rect.x = rectInPixels.x; + rect.y = rectInPixels.y; + rect.width = rectInPixels.width; + rect.height = rectInPixels.height; + this._rect = cc.rectPixelsToPoints(rectInPixels); + }, + + /** + * Returns whether the sprite frame is rotated in the texture. + * @method isRotated + * @return {Boolean} + */ + isRotated:function () { + return this._rotated; + }, + + /** + * Set whether the sprite frame is rotated in the texture. + * @method setRotated + * @param {Boolean} bRotated + */ + setRotated:function (bRotated) { + this._rotated = bRotated; + }, + + /** + * Returns the rect of the sprite frame in the texture. + * @method getRect + * @return {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. + * @method setRect + * @param {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. + * @method getOffsetInPixels + * @return {Vec2} + */ + getOffsetInPixels:function () { + return cc.p(this._offsetInPixels); + }, + + /** + * Sets the offset of the sprite frame in the texture in pixel. + * @method setOffsetInPixels + * @param {Vec2} 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. + * @method getOriginalSizeInPixels + * @return {Size} + */ + getOriginalSizeInPixels:function () { + return cc.size(this._originalSizeInPixels); + }, + + /** + * Sets the original size of the trimmed image. + * @method setOriginalSizeInPixels + * @param {Size} sizeInPixels + */ + setOriginalSizeInPixels:function (sizeInPixels) { + this._originalSizeInPixels.width = sizeInPixels.width; + this._originalSizeInPixels.height = sizeInPixels.height; + }, + + /** + * Returns the original size of the trimmed image. + * @method getOriginalSize + * @return {Size} + */ + getOriginalSize:function () { + return cc.size(this._originalSize); + }, + + /** + * Sets the original size of the trimmed image. + * @method setOriginalSize + * @param {Size} size + */ + setOriginalSize:function (size) { + this._originalSize.width = size.width; + this._originalSize.height = size.height; + }, + + /** + * Returns the texture of the frame. + * @method getTexture + * @return {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. + * @method setTexture + * @param {Texture2D} texture + */ + setTexture:function (texture) { + if (this._texture !== texture) { + var locLoaded = texture.isLoaded(); + this._textureLoaded = locLoaded; + this._texture = texture; + if(!locLoaded){ + texture.once("load", function (event) { + var sender = event.currentTarget; + 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.emit("load"); + }, this); + } + } + }, + + /** + * Returns the offset of the frame in the texture. + * @method getOffset + * @return {Vec2} + */ + getOffset:function () { + return cc.p(this._offset); + }, + + /** + * Sets the offset of the frame in the texture. + * @method setOffset + * @param {Vec2} offsets + */ + setOffset:function (offsets) { + this._offset.x = offsets.x; + this._offset.y = offsets.y; + }, + + /** + * Clone the sprite frame. + * @method clone + * @return {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; + }, + + /** + * 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 {Rect} rect - if parameters' length equal 2, rect in points, else rect in pixels + * @param {Boolean} [rotated=false] + * @param {Vec2} [offset=cc.p(0,0)] + * @param {Size} [originalSize=rect.size] + * @return {Boolean} + */ + initWithTexture:function (texture, rect, rotated, offset, originalSize, _uuid) { + + function check(texture) { + 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.getPixelWidth()) { + cc.error(cc._LogInfos.RectWidth, texture.url); + } + if (_y > texture.getPixelHeight()) { + cc.error(cc._LogInfos.RectHeight, texture.url); + } + } + } + + if (!texture && _uuid) { + this._texture = new cc.Texture2D(); + // deserialize texture from uuid + cc.AssetLibrary.queryAssetInfo(_uuid, function (err, url) { + if (err) { + cc.error('SpriteFrame: Failed to load sprite texture "%s", %s', _uuid, err); + return; + } + + this._textureFilename = url; + this._loadingUuid = ''; + + var locTexture = new cc.Texture2D(); + locTexture.url = url; + cc.textureCache.cacheImage(url, locTexture); + cc.loader.load(url, function (err) { + if (err) { + cc.error('SpriteFrame: Failed to load sprite texture "%s", %s', url, err); + return; + } + var premultiplied = cc.AUTO_PREMULTIPLIED_ALPHA_FOR_PNG && cc.path.extname(url) === '.png'; + + var img = cc.loader.getRes(url); + var loaded = img.width > 0 || img.height > 0; + if (loaded) { + locTexture.handleLoadedTexture(premultiplied); + } + else { + // if not yet loaded, we have to register onLoad + // see https://github.com/fireball-x/fireball/issues/668 + function loadCallback () { + img.removeEventListener('load', loadCallback, false); + img.removeEventListener('error', errorCallback, false); + + locTexture.handleLoadedTexture(premultiplied); + } + function errorCallback () { + img.removeEventListener('load', loadCallback, false); + img.removeEventListener('error', errorCallback, false); + } + img.addEventListener("load", loadCallback); + img.addEventListener("error", errorCallback); + } + }); + this.setTexture(locTexture); + + if (locTexture.isLoaded()) { + this.emit("load"); + } + + check(locTexture); + + }.bind(this)); + } + else { + if (cc.js.isString(texture)){ + this._texture = null; + this._textureFilename = texture; + this._loadingUuid = ''; + } else if (texture instanceof cc.Texture2D) { + this.setTexture(texture); + } + + check(this.getTexture()); + } + + if(arguments.length === 2) + rect = cc.rectPointsToPixels(rect); + + offset = offset || cc.p(0, 0); + originalSize = originalSize || rect; + rotated = rotated || false; + + this._rectInPixels = rect; + rect = this._rect = cc.rectPixelsToPoints(rect); + + 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; + }, + + // SERIALIZATION + + _serialize: function () { + if (CC_EDITOR) { + var rect = this._rect; + var offset = this._offset; + var size = this._originalSize; + var url = this._textureFilename; + var uuid; + if (url) { + uuid = Editor.urlToUuid(url); + } + else { + uuid = this._loadingUuid; + } + var capInsets = undefined; + if (this.insetLeft !== 0 || + this.insetTop !== 0 || + this.insetRight !== 0 || + this.insetBottom !== 0) { + capInsets = [this.insetLeft, this.insetTop, this.insetRight, this.insetBottom]; + } + return { + name: this._name, + texture: uuid, + rect: [rect.x, rect.y, rect.width, rect.height], + offset: [offset.x, offset.y], + originalSize: [size.width, size.height], + rotated: this._rotated ? 1 : 0, + capInsets: capInsets + }; + } + }, + + _deserialize: function (data) { + var rect = data.rect; + rect = new cc.Rect(rect[0], rect[1], rect[2], rect[3]); + var rectInP = cc.rectPointsToPixels(rect); + var offset = new cc.Vec2(data.offset[0], data.offset[1]); + var offsetInP = cc.pointPointsToPixels(offset); + var size = new cc.Size(data.originalSize[0], data.originalSize[1]); + var sizeInP = cc.sizePointsToPixels(size); + var rotated = data.rotated === 1; + // init properties not included in this.initWithTexture() + this._name = data.name; + var capInsets = data.capInsets; + if (capInsets) { + this.insetLeft = capInsets[0]; + this.insetTop = capInsets[1]; + this.insetRight = capInsets[2]; + this.insetBottom = capInsets[3]; + } + var uuid = data.texture; + this._loadingUuid = uuid; + this.initWithTexture(null, rectInP, rotated, offsetInP, sizeInP, uuid); + }, +}); + +var proto = cc.SpriteFrame.prototype; + +/** + * Copy the sprite frame + * @return {SpriteFrame} + */ +proto.copyWithZone = proto.clone; + +/** + * Copy the sprite frame + * @returns {cc.SpriteFrame} + */ +proto.copy = proto.clone; + +EventTarget.polyfill(proto); + +/** + *

+ * 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.Vec2} 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/cocos2d/core/sprites/CCSpriteFrameCache.js b/cocos2d/core/sprites/CCSpriteFrameCache.js new file mode 100644 index 00000000000..21fdc969ea3 --- /dev/null +++ b/cocos2d/core/sprites/CCSpriteFrameCache.js @@ -0,0 +1,361 @@ +/**************************************************************************** + 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. + ****************************************************************************/ + +// init CCSpriteFrame +require("../cocos2d/core/sprites/CCSpriteFrame"); + + + +/** + *

+ * cc.spriteFrameCache is a singleton that handles the loading of the sprite frames. It saves in a cache the sprite frames.
+ *
+ * @class 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.js.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, 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); + + 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 method.
+ *

+ * @method addSpriteFrames + * @param {String} url - file path + * @param {HTMLImageElement|Texture2D|string} texture + * @example {@link utils/api/cocos/docs/cocos2d/core/sprites/addSpriteFrames.js} + */ + 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. + *

+ * @method addSpriteFrame + * @param {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.
+ *

+ * @method removeSpriteFrames + */ + removeSpriteFrames: function () { + this._spriteFrames = {}; + this._spriteFramesAliases = {}; + }, + + /** + * Deletes an sprite frame from the sprite frame cache. + * @method removeSpriteFrameByName + * @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.
+ *

+ * @method removeSpriteFramesFromFile + * @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. + *

+ * @method removeSpriteFramesFromTexture + * @param {HTMLImageElement|HTMLCanvasElement|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.
+ *

+ * @method getSpriteFrame + * @param {String} name - name of SpriteFrame + * @return {SpriteFrame} + * @example {@link utils/api/cocos/docs/cocos2d/core/sprites/getSpriteFrame.js} + */ + 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/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js b/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js new file mode 100644 index 00000000000..c03309bcee6 --- /dev/null +++ b/cocos2d/core/sprites/CCSpriteWebGLRenderCmd.js @@ -0,0 +1,503 @@ +/**************************************************************************** + 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) { + cc.Node.WebGLRenderCmd.call(this, renderable); + this._needDraw = true; + + this._quad = new cc.V3F_C4B_T2F_Quad(); + this._quadWebBuffer = cc._renderContext.createBuffer(); + this._quadDirty = true; + this._dirty = false; + this._recursiveDirty = false; + }; + + var proto = cc.Sprite.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + proto.constructor = 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._init = function () { + var tempColor = {r: 255, g: 255, b: 255, a: 255}, quad = this._quad; + quad.bl.colors = tempColor; + quad.br.colors = tempColor; + quad.tl.colors = tempColor; + quad.tr.colors = tempColor; + this._quadDirty = true; + }; + + proto._resetForBatchNode = function () { + var node = this._node; + var x1 = node._offsetPosition.x; + var y1 = node._offsetPosition.y; + var x2 = x1 + node._rect.width; + var y2 = y1 + node._rect.height; + var locQuad = this._quad; + locQuad.bl.vertices = {x: x1, y: y1, z: 0}; + locQuad.br.vertices = {x: x2, y: y1, z: 0}; + locQuad.tl.vertices = {x: x1, y: y2, z: 0}; + locQuad.tr.vertices = {x: x2, y: y2, z: 0}; + this._quadDirty = true; + }; + + proto.getQuad = function () { + return this._quad; + }; + + proto._updateForSetSpriteFrame = function () {}; + + proto._spriteFrameLoadedCallback = function (event) { + var spriteFrame = event.currentTarget; + this._node.setTextureRect(spriteFrame.getRect(), spriteFrame.isRotated(), spriteFrame.getOriginalSize()); + this._node.emit("load"); + }; + + proto._textureLoadedCallback = function (event) { + var node = this._node, sender = event.currentTarget; + if (node._textureLoaded) + return; + + node._textureLoaded = true; + var locRect = node._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; + } + + node.texture = sender; + node.setTextureRect(locRect, node._rectRotated); + + // 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); + this._quadDirty = true; + node.emit("load"); + }; + + 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; + if (!tex) + return; + + var atlasWidth = tex.pixelWidth; + var atlasHeight = tex.pixelHeight; + + var left, right, top, bottom, tempSwap, locQuad = this._quad; + 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; + } + + locQuad.bl.texCoords.u = left; + locQuad.bl.texCoords.v = top; + locQuad.br.texCoords.u = left; + locQuad.br.texCoords.v = bottom; + locQuad.tl.texCoords.u = right; + locQuad.tl.texCoords.v = top; + locQuad.tr.texCoords.u = right; + locQuad.tr.texCoords.v = bottom; + } 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; + } + + locQuad.bl.texCoords.u = left; + locQuad.bl.texCoords.v = bottom; + locQuad.br.texCoords.u = right; + locQuad.br.texCoords.v = bottom; + locQuad.tl.texCoords.u = left; + locQuad.tl.texCoords.v = top; + locQuad.tr.texCoords.u = right; + locQuad.tr.texCoords.v = top; + } + this._quadDirty = true; + }; + + proto.transform = function(parentCmd, recursive){ + cc.Node.WebGLRenderCmd.prototype.transform.call(this, parentCmd, recursive); + this._dirty = true; //use for batching + }; + + proto._setColorDirty = function () {}; + + proto._updateColor = function () { + var node = this._node, + locDisplayedColor = this._displayedColor, + r = locDisplayedColor.r, + g = locDisplayedColor.g, + b = locDisplayedColor.b, + a = locDisplayedColor.a = this._displayedOpacity; + // special opacity for premultiplied textures + if (node._opacityModifyRGB) { + locDisplayedColor.r *= a / 255.0; + locDisplayedColor.g *= a / 255.0; + locDisplayedColor.b *= a / 255.0; + } + var locQuad = this._quad; + locQuad.bl.colors = locDisplayedColor; + locQuad.br.colors = locDisplayedColor; + locQuad.tl.colors = locDisplayedColor; + locQuad.tr.colors = locDisplayedColor; + + // Roll back color + locDisplayedColor.r = r; + locDisplayedColor.g = g; + locDisplayedColor.b = b; + + // renders using Sprite Manager + if (node._batchNode) { + if (node.atlasIndex !== cc.Sprite.INDEX_NOT_INITIALIZED) { + node.textureAtlas.updateQuad(locQuad, node.atlasIndex) + } else { + // no need to set it recursively + // update dirty_, don't update recursiveDirty_ + this._dirty = true; + } + } + // self render + // do nothing + this._quadDirty = true; + }; + + 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; + if (!node._texture || !node._texture.hasPremultipliedAlpha()) { + node._blendFunc.src = cc.SRC_ALPHA; + node._blendFunc.dst = cc.ONE_MINUS_SRC_ALPHA; + node.opacityModifyRGB = false; + } else { + node._blendFunc.src = cc.BLEND_SRC; + node._blendFunc.dst = cc.BLEND_DST; + node.opacityModifyRGB = true; + } + }; + + proto._setTexture = function (texture) { + var node = this._node; + // If batchnode, then texture id should be the same + if (node._batchNode) { + if(node._batchNode.texture !== texture){ + cc.log(cc._LogInfos.Sprite_setTexture); + return; + } + }else{ + if(node._texture !== texture){ + node._textureLoaded = texture ? texture._textureLoaded : false; + node._texture = texture; + this._updateBlendFunc(); + } + } + + if (texture) + this._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR); + else + this._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_COLOR); + + }; + + proto.updateTransform = function () { //called only at batching. + var _t = this, node = this._node; + + // recalculate matrix only if it is dirty + if (this._dirty) { + var locQuad = _t._quad, locParent = node._parent; + // If it is not visible, or one of its ancestors is not visible, then do nothing: + if (!node._visible || ( locParent && locParent !== node._batchNode && locParent._shouldBeHidden)) { + locQuad.br.vertices = locQuad.tl.vertices = locQuad.tr.vertices = locQuad.bl.vertices = {x: 0, y: 0, z: 0}; + node._shouldBeHidden = true; + } else { + node._shouldBeHidden = false; + if(this._dirtyFlag !== 0){ //because changing color and opacity uses dirty flag at visit, but visit doesn't call at batching. + this.updateStatus(); + this._dirtyFlag = 0; + } + + if (!locParent || locParent === node._batchNode) { + node._transformToBatch = _t.getNodeToParentTransform(); + } else { + node._transformToBatch = cc.affineTransformConcat(_t.getNodeToParentTransform(), locParent._transformToBatch); + } + + // + // calculate the Quad based on the Affine Matrix + // + var locTransformToBatch = node._transformToBatch; + var rect = node._rect; + var x1 = node._offsetPosition.x; + var y1 = node._offsetPosition.y; + + var x2 = x1 + rect.width; + var y2 = y1 + rect.height; + var x = locTransformToBatch.tx; + var y = locTransformToBatch.ty; + + var cr = locTransformToBatch.a; + var sr = locTransformToBatch.b; + var cr2 = locTransformToBatch.d; + var sr2 = -locTransformToBatch.c; + var ax = x1 * cr - y1 * sr2 + x; + var ay = x1 * sr + y1 * cr2 + y; + + var bx = x2 * cr - y1 * sr2 + x; + var by = x2 * sr + y1 * cr2 + y; + + var cx = x2 * cr - y2 * sr2 + x; + var cy = x2 * sr + y2 * cr2 + y; + + var dx = x1 * cr - y2 * sr2 + x; + var dy = x1 * sr + y2 * cr2 + y; + + var locVertexZ = node._vertexZ; + if (!cc.SPRITEBATCHNODE_RENDER_SUBPIXEL) { + ax = 0 | ax; + ay = 0 | ay; + bx = 0 | bx; + by = 0 | by; + cx = 0 | cx; + cy = 0 | cy; + dx = 0 | dx; + dy = 0 | dy; + } + locQuad.bl.vertices = {x: ax, y: ay, z: locVertexZ}; + locQuad.br.vertices = {x: bx, y: by, z: locVertexZ}; + locQuad.tl.vertices = {x: dx, y: dy, z: locVertexZ}; + locQuad.tr.vertices = {x: cx, y: cy, z: locVertexZ}; + } + node.textureAtlas.updateQuad(locQuad, node.atlasIndex); + node._recursiveDirty = false; + this._dirty = false; + } + + // recursively iterate over children + if (node._hasChildren) + node._arrayMakeObjectsPerformSelector(node._children, cc.Node._stateCallbackType.updateTransform); + + /*if (cc.SPRITE_DEBUG_DRAW) { //TODO + // draw bounding box + var vertices = [ + cc.p(_t._quad.bl.vertices.x, _t._quad.bl.vertices.y), + cc.p(_t._quad.br.vertices.x, _t._quad.br.vertices.y), + cc.p(_t._quad.tr.vertices.x, _t._quad.tr.vertices.y), + cc.p(_t._quad.tl.vertices.x, _t._quad.tl.vertices.y) + ]; + cc._drawingUtil.drawPoly(vertices, 4, 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.rendering = function (ctx) { + var node = this._node, locTexture = node._texture; + if ((locTexture &&!locTexture._textureLoaded) || this._displayedOpacity === 0) + return; + + var gl = ctx || cc._renderContext ; + //cc.assert(!_t._batchNode, "If cc.Sprite is being rendered by cc.SpriteBatchNode, cc.Sprite#draw SHOULD NOT be called"); + + if (locTexture) { + if (locTexture._textureLoaded) { + this._shaderProgram.use(); + this._shaderProgram._setUniformForMVPMatrixWithMat4(this._stackMatrix); + + cc.glBlendFunc(node._blendFunc.src, node._blendFunc.dst); + //optimize performance for javascript + cc.glBindTexture2DN(0, locTexture); // = cc.glBindTexture2D(locTexture); + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX); + + gl.bindBuffer(gl.ARRAY_BUFFER, this._quadWebBuffer); + if (this._quadDirty) { + gl.bufferData(gl.ARRAY_BUFFER, this._quad.arrayBuffer, gl.DYNAMIC_DRAW); + this._quadDirty = false; + } + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 24, 0); //cc.VERTEX_ATTRIB_POSITION + gl.vertexAttribPointer(1, 4, gl.UNSIGNED_BYTE, true, 24, 12); //cc.VERTEX_ATTRIB_COLOR + gl.vertexAttribPointer(2, 2, gl.FLOAT, false, 24, 16); //cc.VERTEX_ATTRIB_TEX_COORDS + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + } + } else { + this._shaderProgram.use(); + this._shaderProgram._setUniformForMVPMatrixWithMat4(this._stackMatrix); + + cc.glBlendFunc(node._blendFunc.src, node._blendFunc.dst); + cc.glBindTexture2D(null); + + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSITION | cc.VERTEX_ATTRIB_FLAG_COLOR); + + gl.bindBuffer(gl.ARRAY_BUFFER, this._quadWebBuffer); + if (this._quadDirty) { + gl.bufferData(gl.ARRAY_BUFFER, this._quad.arrayBuffer, gl.STATIC_DRAW); + this._quadDirty = false; + } + 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.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + } + cc.g_NumberOfDraws++; + + if (cc.SPRITE_DEBUG_DRAW === 0 && !node._showNode) + return; + + cc.kmGLMatrixMode(cc.KM_GL_MODELVIEW); + //cc.kmGLPushMatrixWitMat4(node._stackMatrix); + cc.current_stack.stack.push(cc.current_stack.top); + cc.current_stack.top = this._stackMatrix; + + if (cc.SPRITE_DEBUG_DRAW === 1 || node._showNode) { + // draw bounding box + var locQuad = this._quad; + var verticesG1 = [ + cc.p(locQuad.tl.vertices.x, locQuad.tl.vertices.y), + cc.p(locQuad.bl.vertices.x, locQuad.bl.vertices.y), + cc.p(locQuad.br.vertices.x, locQuad.br.vertices.y), + cc.p(locQuad.tr.vertices.x, locQuad.tr.vertices.y) + ]; + cc._drawingUtil.drawPoly(verticesG1, 4, true); + } else if (cc.SPRITE_DEBUG_DRAW === 2) { + // draw texture box + var drawRectG2 = node.getTextureRect(); + var offsetPixG2 = node.getOffsetPosition(); + var verticesG2 = [cc.p(offsetPixG2.x, offsetPixG2.y), cc.p(offsetPixG2.x + drawRectG2.width, offsetPixG2.y), + cc.p(offsetPixG2.x + drawRectG2.width, offsetPixG2.y + drawRectG2.height), cc.p(offsetPixG2.x, offsetPixG2.y + drawRectG2.height)]; + cc._drawingUtil.drawPoly(verticesG2, 4, true); + } // CC_SPRITE_DEBUG_DRAW + cc.current_stack.top = cc.current_stack.stack.pop(); + }; +})(); \ No newline at end of file diff --git a/cocos2d/core/sprites/SpritesPropertyDefine.js b/cocos2d/core/sprites/SpritesPropertyDefine.js new file mode 100644 index 00000000000..20e8cc938a9 --- /dev/null +++ b/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/cocos2d/core/support/CCPointExtension.js b/cocos2d/core/support/CCPointExtension.js new file mode 100644 index 00000000000..61ac78cfa85 --- /dev/null +++ b/cocos2d/core/support/CCPointExtension.js @@ -0,0 +1,561 @@ +/**************************************************************************** + 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.Vec2 extensions based on Chipmunk's cpVect file.
+ * These extensions work both with cc.Vec2

+ * + *

The "ccp" prefix means: "CoCos2d Point"

+ */ + +/** + * smallest such that 1.0+FLT_EPSILON != 1.0 + * @constant + * @type Number + */ +cc.POINT_EPSILON = parseFloat('1.192092896e-07F'); + +/** + * Returns opposite of point. + * @method pNeg + * @param {Vec2} point + * @return {Vec2} + */ +cc.pNeg = function (point) { + return cc.p(-point.x, -point.y); +}; + +/** + * Calculates sum of two points. + * @method pAdd + * @param {Vec2} v1 + * @param {Vec2} v2 + * @return {Vec2} + * @examples {@link utils/api/cocos/docs/cocos2d/core/support/pAdd.js} + */ +cc.pAdd = function (v1, v2) { + return cc.p(v1.x + v2.x, v1.y + v2.y); +}; + +/** + * Calculates difference of two points. + * @method pSub + * @param {Vec2} v1 + * @param {Vec2} v2 + * @return {Vec2} + */ +cc.pSub = function (v1, v2) { + return cc.p(v1.x - v2.x, v1.y - v2.y); +}; + +/** + * Returns point multiplied by given factor. + * @method pMult + * @param {Vec2} point + * @param {Number} floatVar + * @return {Vec2} + */ +cc.pMult = function (point, floatVar) { + return cc.p(point.x * floatVar, point.y * floatVar); +}; + +/** + * Calculates midpoint between two points. + * @method pMidpoint + * @param {Vec2} v1 + * @param {Vec2} v2 + * @return {pMult} + */ +cc.pMidpoint = function (v1, v2) { + return cc.pMult(cc.pAdd(v1, v2), 0.5); +}; + +/** + * Calculates dot product of two points. + * @method pDot + * @param {Vec2} v1 + * @param {Vec2} v2 + * @return {Number} + */ +cc.pDot = function (v1, v2) { + return v1.x * v2.x + v1.y * v2.y; +}; + +/** + * Calculates cross product of two points. + * @method pCross + * @param {Vec2} v1 + * @param {Vec2} 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 + * @method pPerp + * @param {Vec2} point + * @return {Vec2} + */ +cc.pPerp = function (point) { + return cc.p(-point.y, point.x); +}; + +/** + * Calculates perpendicular of v, rotated 90 degrees clockwise -- cross(v, rperp(v)) <= 0 + * @method pRPerp + * @param {Vec2} point + * @return Vec2} + */ +cc.pRPerp = function (point) { + return cc.p(point.y, -point.x); +}; + +/** + * Calculates the projection of v1 over v2. + * @method pProject + * @param {Vec2} v1 + * @param {Vec2} v2 + * @return {pMult} + */ +cc.pProject = function (v1, v2) { + return cc.pMult(v2, cc.pDot(v1, v2) / cc.pDot(v2, v2)); +}; + +/** + * Rotates two points. + * @method pRotate + * @param {Vec2} v1 + * @param {Vec2} v2 + * @return {Vec2} + */ +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. + * @method pUnrotate + * @param {Vec2} v1 + * @param {Vec2} v2 + * @return {Vec2} + */ +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.Vec2 (not calling sqrt() ) + * @method pLengthSQ + * @param {Vec2} v + * @return {Number} + */ +cc.pLengthSQ = function (v) { + return cc.pDot(v, v); +}; + +/** + * Calculates the square distance between two points (not calling sqrt() ) + * @method pDistanceSQ + * @param {Vec2} point1 + * @param {Vec2} point2 + * @return {Number} + */ +cc.pDistanceSQ = function(point1, point2){ + return cc.pLengthSQ(cc.pSub(point1,point2)); +}; + +/** + * Calculates distance between point an origin + * @method pLength + * @param {Vec2} v + * @return {Number} + */ +cc.pLength = function (v) { + return Math.sqrt(cc.pLengthSQ(v)); +}; + +/** + * Calculates the distance between two points + * @method pDistance + * @param {Vec2} v1 + * @param {Vec2} v2 + * @return {Number} + */ +cc.pDistance = function (v1, v2) { + return cc.pLength(cc.pSub(v1, v2)); +}; + +/** + * Returns point multiplied to a length of 1. + * @method pNormalize + * @param {Vec2} v + * @return {Vec2} + */ +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. + * @method pForAngle + * @param {Number} a + * @return {Vec2} + */ +cc.pForAngle = function (a) { + return cc.p(Math.cos(a), Math.sin(a)); +}; + +/** + * Converts a vector to radians. + * @method pToAngle + * @param {Vec2} v + * @return {Number} + */ +cc.pToAngle = function (v) { + return Math.atan2(v.y, v.x); +}; + +/** + * Clamp a value between from and to. + * @method clampf + * @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 value between 0 and 1. + * @method clamp01 + * @param {Number} value + * @return {Number} + */ +cc.clamp01 = function (value) { + return value < 0 ? 0 : value < 1 ? value : 1; +}; + +/** + * Clamp a point between from and to. + * @method pClamp + * @param {Point} p + * @param {Number} min_inclusive + * @param {Number} max_inclusive + * @return {Vec2} + */ +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.Vec2. + * @method pFromSize + * @param {Size} s + * @return {Vec2} + */ +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. + * + * @method pCompOp + * @param {Vec2} p + * @param {Function} opFunc + * @return {Vec2} + * @example {@link utils/api/cocos/docs/cocos2d/core/support/pCompOp.js} + */ +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 + * + * @method pLerp + * @param {Vec2} a + * @param {Vec2} b + * @param {Number} alpha + * @return {pAdd} + */ +cc.pLerp = function (a, b, alpha) { + return cc.pAdd(cc.pMult(a, 1 - alpha), cc.pMult(b, alpha)); +}; + +/** + * @method pFuzzyEqual + * @param {Vec2} a + * @param {Vec2} 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. + * @method pCompMult + * @param {Vec2} a + * @param {Vec2} b + * @return {Vec2} + */ +cc.pCompMult = function (a, b) { + return cc.p(a.x * b.x, a.y * b.y); +}; + +/** + * @method pAngleSigned + * @param {Vec2} a + * @param {Vec2} 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; +}; + +/** + * @method pAngle + * @param {Vec2} a + * @param {Vec2} 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. + * @method pRotateByAngle + * @param {Vec2} v - v is the point to rotate + * @param {Vec2} pivot - pivot is the pivot, naturally + * @param {Number} angle - angle is the angle of rotation cw in radians + * @return {Vec2} 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); + * + * @method pLineIntersect + * @param {Vec2} A - A is the startpoint for the first line P1 = (p1 - p2). + * @param {Vec2} B - B is the endpoint for the first line P1 = (p1 - p2). + * @param {Vec2} C - C is the startpoint for the second line P2 = (p3 - p4). + * @param {Vec2} D - D is the endpoint for the second line P2 = (p3 - p4). + * @param {Vec2} 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. + * @method pSegmentIntersect + * @param {Vec2} A + * @param {Vec2} B + * @param {Vec2} C + * @param {Vec2} 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. + * @method pIntersectPoint + * @param {Vec2} A + * @param {Vec2} B + * @param {Vec2} C + * @param {Vec2} D + * @return {Vec2} + */ +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. + * @method pSameAs + * @param {Vec2} A - A ccp a + * @param {Vec2} 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 Perfomance In Place Operationrs --------------------------------------- + +/** + * sets the position of the point to 0. + * @method pZeroIn + * @param {Vec2} v + */ +cc.pZeroIn = function(v) { + v.x = 0; + v.y = 0; +}; + +/** + * copies the position of one point to another. + * @method pIn + * @param {Vec2} v1 + * @param {Vec2} v2 + */ +cc.pIn = function(v1, v2) { + v1.x = v2.x; + v1.y = v2.y; +}; + +/** + * multiplies the point with the given factor (inplace). + * @method pMultIn + * @param {Vec2} point + * @param {Number} floatVar + */ +cc.pMultIn = function(point, floatVar) { + point.x *= floatVar; + point.y *= floatVar; +}; + +/** + * subtracts one point from another (inplace). + * @method pSubIn + * @param {Vec2} v1 + * @param {Vec2} v2 + */ +cc.pSubIn = function(v1, v2) { + v1.x -= v2.x; + v1.y -= v2.y; +}; + +/** + * adds one point to another (inplace). + * @method pAddIn + * @param {Vec2} v1 + * @param {point} v2 + */ +cc.pAddIn = function(v1, v2) { + v1.x += v2.x; + v1.y += v2.y; +}; + +/** + * normalizes the point (inplace). + * @method pNormalizeIn + * @param {Vec2} v + */ +cc.pNormalizeIn = function(v) { + cc.pMultIn(v, 1.0 / Math.sqrt(v.x * v.x + v.y * v.y)); +}; + diff --git a/cocos2d/core/support/CCVertex.js b/cocos2d/core/support/CCVertex.js new file mode 100644 index 00000000000..44b92f0d22d --- /dev/null +++ b/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/cocos2d/core/support/TransformUtils.js b/cocos2d/core/support/TransformUtils.js new file mode 100644 index 00000000000..bb57c6f8c4c --- /dev/null +++ b/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/cocos2d/core/textures/CCTexture2D.js b/cocos2d/core/textures/CCTexture2D.js new file mode 100644 index 00000000000..c1f52f3e14d --- /dev/null +++ b/cocos2d/core/textures/CCTexture2D.js @@ -0,0 +1,1158 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + 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. + ****************************************************************************/ + +var EventTarget = require('../event/event-target'); +var sys = require('../platform/CCSys'); +var JS = require('../platform/js'); +var game = require('../CCGame'); +require('../platform/_CCClass'); +require('../platform/CCClass'); + +//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; + +/** + * The texture wrap mode + * @class Texture2D.WrapMode + * @static + * @namespace Texture2D + */ +var WrapMode = cc.Enum({ + /** + * The constant variable equals gl.REPEAT for texture + * @property REPEAT + * @type {Number} + * @readonly + */ + REPEAT: 0x2901, + /** + * The constant variable equals gl.CLAMP_TO_EDGE for texture + * @property CLAMP_TO_EDGE + * @type {Number} + * @readonly + */ + CLAMP_TO_EDGE: 0x812f, + /** + * The constant variable equals gl.MIRRORED_REPEAT for texture + * @property MIRRORED_REPEAT + * @type {Number} + * @readonly + */ + MIRRORED_REPEAT: 0x8370 +}); + +//----------------------Possible texture pixel formats---------------------------- + +/** + *

+ * 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!

+ + * @class Texture2D + * @extends RawAsset + */ +var Texture2D = cc.Class(/** @lends cc.Texture2D# */{ + + name: 'cc.Texture2D', + extends: require('../assets/CCRawAsset'), + + ctor: function () { + this.url = null; + this._textureLoaded = false; + this._htmlElementObj = null; + this._contentSize = cc.size(0, 0); + + if (cc._renderType === game.RENDER_TYPE_CANVAS) { + this._pattern = ""; + // hack for gray effect + this._grayElementObj = null; + this._backupElement = null; + this._isGray = false; + } + else if (cc._renderType === game.RENDER_TYPE_WEBGL) { + this._hasPremultipliedAlpha = false; + this._pixelFormat = Texture2D.defaultPixelFormat; + this._pixelsWide = 0; + this._pixelsHigh = 0; + this._hasPremultipliedAlpha = false; + this._hasMipmaps = false; + + this._webTextureObj = null; + } + }, + + /** + * Get width in pixels. + * @method getPixelWidth + * @return {Number} + */ + getPixelWidth: function () { + return this._contentSize.width; + }, + + /** + * Get height of in pixels. + * @method getPixelHeight + * @return {Number} + */ + getPixelHeight: function () { + return this._contentSize.height; + }, + + /** + * Get content size. + * @method getContentSize + * @returns {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. + * @method getContentSizeInPixels + * @returns {Size} + */ + getContentSizeInPixels: function () { + return this._contentSize; + }, + + /** + * Init with HTML element. + * @method initWithElement + * @param {HTMLImageElement|HTMLCanvasElement} element + */ + initWithElement: function (element) { + if (!element) + return; + this._htmlElementObj = element; + this._contentSize.width = element.width; + this._contentSize.height = element.height; + this._textureLoaded = true; + }, + + /** + * Intializes with a texture2d with data. + * @method initWithData + * @param {Array} data + * @param {Number} pixelFormat + * @param {Number} pixelsWide + * @param {Number} pixelsHigh + * @param {Size} contentSize + * @return {Boolean} + */ + initWithData: function (data, pixelFormat, pixelsWide, pixelsHigh, contentSize) { + //support only in WebGl rendering mode + return false; + }, + + /** + * Initializes a texture from a UIImage object. + * 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). + * @method initWithImage + * @param {HTMLImageElement} uiImage + * @return {Boolean} + */ + initWithImage: function (uiImage) { + //support only in WebGl rendering mode + return false; + }, + + /** + * HTMLElement Object getter. + * @method getHtmlElementObj + * @return {HTMLImageElement|HTMLCanvasElement} + */ + getHtmlElementObj: function () { + return this._htmlElementObj; + }, + + /** + * Check whether texture is loaded. + * @method isLoaded + * @returns {Boolean} + */ + isLoaded: function () { + return this._textureLoaded; + }, + + /** + * Handler of texture loaded event. + * @method handleLoadedTexture + * @param {Boolean} [premultiplied] + */ + handleLoadedTexture: function () { + var self = this; + if (self._textureLoaded) return; + if (!self._htmlElementObj) { + var img = cc.loader.getRes(self.url); + if (!img) return; + self.initWithElement(img); + } + + var locElement = self._htmlElementObj; + self._contentSize.width = locElement.width; + self._contentSize.height = locElement.height; + + //dispatch load event to listener. + self.emit("load"); + }, + + /** + * Description of cc.Texture2D. + * @method description + * @returns {String} + */ + description: function () { + return ""; + }, + + /** + * Release texture. + * @method releaseTexture + */ + releaseTexture: function () { + if (this._webTextureObj) + cc._renderContext.deleteTexture(this._webTextureObj); + cc.loader.release(this.url); + }, + + getName: function () { + return this._webTextureObj || null; + }, + + /** + * Pixel format of the texture. + * @method getPixelFormat + * @return {Number} + */ + getPixelFormat: function () { + //support only in WebGl rendering mode + return this._pixelFormat || null; + }, + + /** + * Whether or not the texture has their Alpha premultiplied, + * support only in WebGl rendering mode. + * @method hasPremultipliedAlpha + * @return {Boolean} + */ + hasPremultipliedAlpha: function () { + return this._hasPremultipliedAlpha || false; + }, + + /** + * Whether or not use mipmap, support only in WebGl rendering mode. + * @method hasMipmaps + * @return {Boolean} + */ + hasMipmaps: function () { + return this._hasMipmaps || 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}. + * @method setTexParameters + * @param {Object|Number} texParams texParams object or minFilter + * @param {Number} [magFilter] + * @param {Texture2D.WrapMode} [wrapS] + * @param {Texture2D.WrapMode} [wrapT] + */ + setTexParameters: function (texParams, magFilter, wrapS, wrapT) { + if(magFilter !== undefined) + texParams = {minFilter: texParams, magFilter: magFilter, wrapS: wrapS, wrapT: wrapT}; + + if(texParams.wrapS === WrapMode.REPEAT && texParams.wrapT === WrapMode.REPEAT){ + this._pattern = "repeat"; + return; + } + + if(texParams.wrapS === WrapMode.REPEAT ){ + this._pattern = "repeat-x"; + return; + } + + if(texParams.wrapT === WrapMode.REPEAT){ + this._pattern = "repeat-y"; + return; + } + + this._pattern = ""; + }, + + /** + * sets antialias texture parameters:
+ * - GL_TEXTURE_MIN_FILTER = GL_NEAREST
+ * - GL_TEXTURE_MAG_FILTER = GL_NEAREST + */ + setAntiAliasTexParameters: function () { + //support only in WebGl rendering mode + }, + + /** + * Sets alias texture parameters: + * GL_TEXTURE_MIN_FILTER = GL_NEAREST + * GL_TEXTURE_MAG_FILTER = GL_NEAREST + */ + setAliasTexParameters: function () { + //support only in WebGl rendering mode + }, + + /** + * Generates mipmap images for the texture.
+ * It only works if the texture size is POT (power of 2). + */ + generateMipmap: function () { + //support only in WebGl rendering mode + }, + + /** + * returns the pixel format. + * @return {String} + */ + stringForFormat: function () { + //support only in WebGl rendering mode + return ""; + }, + + /** + * returns the bits-per-pixel of the in-memory OpenGL texture + * @return {Number} + */ + bitsPerPixelForFormat: function (format) { + //support only in WebGl rendering mode + return -1; + } +}); + +Texture2D.WrapMode = WrapMode; + +var _c = Texture2D; + +/** + * 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 = 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 = 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 = Texture2D.prototype; + +// Extended properties +/** @expose */ +_p.name; +cc.defineGetterSetter(_p, "name", _p.getName); +/** @expose */ +_p.pixelFormat; +cc.defineGetterSetter(_p, "pixelFormat", _p.getPixelFormat); +/** @expose */ +_p.pixelWidth; +cc.defineGetterSetter(_p, "pixelWidth", _p.getPixelWidth); +/** @expose */ +_p.pixelHeight; +cc.defineGetterSetter(_p, "pixelHeight", _p.getPixelHeight); +/** @expose */ +_p.width; +cc.defineGetterSetter(_p, "width", _p._getWidth); +/** @expose */ +_p.height; +cc.defineGetterSetter(_p, "height", _p._getHeight); + +EventTarget.polyfill(Texture2D.prototype); + +game.once(game.EVENT_RENDERER_INITED, function () { + if(cc._renderType === game.RENDER_TYPE_CANVAS) { + + 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; + }; + + var 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; + }; + + JS.mixin(Texture2D.prototype, { + _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; + }, + + _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 = 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 = generateGrayTexture(this._htmlElementObj);; + var newTexture = new Texture2D(); + newTexture.initWithElement(grayElement); + newTexture.handleLoadedTexture(); + return newTexture; + }, + + //change color function + _generateColorTexture: sys._supportCanvasNewBlendModes ? 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 Texture2D(); + newTexture.initWithElement(canvas); + newTexture.handleLoadedTexture(); + return newTexture; + } : 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 Texture2D(); + newTexture.initWithElement(canvas); + newTexture.handleLoadedTexture(); + return newTexture; + } + }); + + } else if (cc._renderType === game.RENDER_TYPE_WEBGL) { + JS.mixin(Texture2D.prototype, { + getPixelWidth: function () { + return this._pixelsWide; + }, + + getPixelHeight: function () { + return this._pixelsHigh; + }, + + initWithData: function (data, pixelFormat, pixelsWide, pixelsHigh, contentSize) { + var self = this, tex2d = Texture2D; + var gl = cc._renderContext; + var format = gl.RGBA, type = gl.UNSIGNED_BYTE; + + var bitsPerPixel = 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._hasPremultipliedAlpha = false; + self._hasMipmaps = false; + + self._textureLoaded = true; + + return true; + }, + + 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); + }, + + initWithElement: function (element) { + if (!element) + return; + this._webTextureObj = cc._renderContext.createTexture(); + this._htmlElementObj = element; + this._textureLoaded = true; + }, + + // [premultiplied=false] + handleLoadedTexture: function (premultiplied) { + premultiplied = !!premultiplied; + var self = this; + // Not sure about this ! Some texture need to be updated even after loaded + if (!game._rendererInitialized) + return; + if (!self._htmlElementObj) { + var img = cc.loader.getRes(self.url); + if (!img) return; + self.initWithElement(img); + } + 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); + + 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 = Texture2D.PIXEL_FORMAT_RGBA8888; + + self._hasPremultipliedAlpha = premultiplied; + self._hasMipmaps = false; + + //dispatch load event to listener. + self.emit("load"); + }, + + 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); + }, + + 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); + }, + + 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); + }, + + 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; + }, + + stringForFormat: function () { + return Texture2D._M[this._pixelFormat]; + }, + + bitsPerPixelForFormat: function (format) {//TODO I want to delete the format argument, use this._pixelFormat + format = format || this._pixelFormat; + var value = Texture2D._B[format]; + if (value != null) return value; + cc.log(cc._LogInfos.Texture2D.bitsPerPixelForFormat, format); + return -1; + }, + + _initPremultipliedATextureWithImage: function (uiImage, width, height) { + var tex2d = 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(); + var i; + + // 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 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; + } + }); + } +}); + +/** + * WebGLTexture Object. + * @property name + * @type {WebGLTexture} + * @readonly + */ + +/** + * Pixel format of the texture. + * @property pixelFormat + * @type {Number} + * @readonly + */ + +/** + * Width in pixels. + * @property pixelWidth + * @type {Number} + * @readonly + */ + +/** + * Height in pixels. + * @property pixelHeight + * @type {Number} + * @readonly + */ + +/** + * Content width in points. + * @property width + * @type {Number} + */ + +/** + * Content height in points. + * @property height + * @type {Number} + */ + +cc.Texture2D = module.exports = Texture2D; \ No newline at end of file diff --git a/cocos2d/core/textures/CCTextureAtlas.js b/cocos2d/core/textures/CCTextureAtlas.js new file mode 100644 index 00000000000..f2bb5bdeda1 --- /dev/null +++ b/cocos2d/core/textures/CCTextureAtlas.js @@ -0,0 +1,733 @@ +/**************************************************************************** + 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 Class = require('../platform/_CCClass'); +var JS = require('../platform/js'); +var game = require('../CCGame'); + +/** + *

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 TextureAtlas + * @extends _Class + * @constructor + */ + +var TextureAtlas = 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 + * @constructor + * @param {String|Texture2D} fileName + * @param {Number} capacity + * @example {@link utils/api/cocos/docs/cocos2d/core/textures/TextureAtlas.js} + */ + ctor: function (fileName, capacity) { + this._buffersVBO = []; + + if (cc.js.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. + * @method getTotalQuads + * @return {Number} + */ + getTotalQuads: function () { + //return this._quads.length; + return this._totalQuads; + }, + + /** + * Quantity of quads that can be stored with the current texture atlas size. + * @method getCapacity + * @return {Number} + */ + getCapacity: function () { + return this._capacity; + }, + + /** + * Texture of the texture atlas. + * @method getTexture + * @return {Image} + */ + getTexture: function () { + return this.texture; + }, + + /** + * Set texture for texture atlas. + * @method setTexture + * @param {Image} texture + */ + setTexture: function (texture) { + this.texture = texture; + }, + + /** + * specify if the array buffer of the VBO needs to be updated. + * @method setDirty + * @param {Boolean} dirty + */ + setDirty: function (dirty) { + this.dirty = dirty; + }, + + /** + * whether or not the array buffer of the VBO needs to be updated. + * @method isDirty + * @returns {Boolean} + */ + isDirty: function () { + return this.dirty; + }, + + /** + * Quads that are going to be rendered. + * @method getQuads + * @return {Array} + */ + getQuads: function () { + return this._quads; + }, + + /** + * @method setQuads + * @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; + } + } + }, + + /** + *

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.

+ * @method initWithFile + * @param {String} file + * @param {Number} capacity + * @return {Boolean} + * @example {@link utils/api/cocos/docs/cocos2d/core/textures/initWithFile.js} + */ + 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

+ * @method initWithTexture + * @param {Image} texture + * @param {Number} capacity + * @return {Boolean} + * @example {@link utils/api/cocos/docs/cocos2d/core/textures/initWithTexture.js} + */ + 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

+ * @method updateQuad + * @param {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

+ * @method insertQuad + * @param {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
+ *

+ * + * @method insertQuads + * @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

+ * @method insertQuadFromIndex + * @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

+ * @method removeQuadAtIndex + * @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. + * @method removeQuadsAtIndex + * @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

+ * @method removeAllQuads + */ + 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

+ * @method resizeCapacity + * @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. + * @method increaseTotalQuadsWith + * @param {Number} amount + */ + increaseTotalQuadsWith: function (amount) { + this._totalQuads += amount; + }, + + /** + * Moves an amount of quads from oldIndex at newIndex. + * @method moveQuadsFromIndex + * @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. + * @method fillWithEmptyQuadsFromIndex + * @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; + }, + + _setupVBO: function () {}, + _mapBuffers: function () {}, + + // TextureAtlas - Drawing + + /** + * Draws all the Atlas's Quads + */ + drawQuads: function () {}, + + /** + *

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

+ * + * @method drawNumberOfQuads + * @param {Number} n + * @param {Number} start + */ + drawNumberOfQuads: null, + + _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 = 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); + +game.once(game.EVENT_RENDERER_INITED, function () { +if (cc._renderType === game.RENDER_TYPE_WEBGL) { + JS.mixin(TextureAtlas.prototype, { + _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(); + }, + + _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(); + }, + + drawQuads: function () { + this.drawNumberOfQuads(this._totalQuads, 0); + }, + + 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 + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX); + + gl.bindBuffer(gl.ARRAY_BUFFER, _t._quadsWebBuffer); + if (_t.dirty){ + gl.bufferData(gl.ARRAY_BUFFER, _t._quadsArrayBuffer, gl.DYNAMIC_DRAW); + _t.dirty = false; + } + + 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(); + } + }); +} +}); + +/** + * Indicates whether or not the array buffer of the VBO needs to be updated. + * @property dirty + * @type {Boolean} + */ + +/** + * Image texture for cc.TextureAtlas. + * @property texture + * @type {Image} + */ + +/** + * Quantity of quads that can be stored with the current texture atlas size. + * @property capacity + * @type {Number} + * @readonly + */ + +/** + * Quantity of quads that are going to be drawn. + * @property totalQuads + * @type {Number} + * @readonly + */ + +/** + * Quads that are going to be rendered. + * @property quads + * @type {Array} + * @readonly + */ + +cc.TextureAtlas = module.exports = TextureAtlas; \ No newline at end of file diff --git a/cocos2d/core/textures/CCTextureCache.js b/cocos2d/core/textures/CCTextureCache.js new file mode 100644 index 00000000000..104607f6a17 --- /dev/null +++ b/cocos2d/core/textures/CCTextureCache.js @@ -0,0 +1,393 @@ +/**************************************************************************** + 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 JS = require('../platform/js'); +var game = require('../CCGame'); +var Texture2D = require('./CCTexture2D'); + +/** + * cc.textureCache is a singleton object, it's the global cache for cc.Texture2D + * @class textureCache + */ +var textureCache = /** @lends cc.textureCache# */{ + _textures: {}, + _textureColorsCache: {}, + _textureKeySeq: (0 | Math.random() * 1000), + + _loadedTexturesBefore: {}, + + handleLoadedTexture: null, + + _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 = {}; + }, + + /** + * Description + * @method description + * @return {String} + */ + description: function () { + return ""; + }, + + /** + * Returns an already created texture. Returns null if the texture doesn't exist. + * @method textureForKey + * @param {String} textureKeyName + * @return {Texture2D|Null} + * @deprecated + * @example {@link utils/api/cocos/docs/cocos2d/core/textures/textureForKey.js} + */ + 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. + * @method getTextureForKey + * @param {String} textureKeyName + * @return {Texture2D|Null} + * @example {@link utils/api/cocos/docs/cocos2d/core/textures/getTextureForKey.js} + */ + getTextureForKey: function(textureKeyName){ + return this._textures[textureKeyName] || this._textures[cc.loader.getAliase(textureKeyName)]; + }, + + /** + * @method getKeyByTexture + * @param {Image} texture + * @return {String|Null} + * @example {@link utils/api/cocos/docs/cocos2d/core/textures/getKeyByTexture.js} + */ + getKeyByTexture: function (texture) { + for (var key in this._textures) { + if (this._textures[key] === texture) { + return key; + } + } + return null; + }, + + _generalTextureKey: function (id) { + return "_textureKey_" + id; + }, + + /** + * @method getTextureColors + * @param {Image} texture + * @return {Array} + * @example {@link utils/api/cocos/docs/cocos2d/core/textures/getTextureColors.js} + */ + 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]; + }, + + /** + *

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

+ * @method removeAllTextures + * @example {@link utils/api/cocos/docs/cocos2d/core/textures/removeAllTextures.js} + */ + 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. + * @method removeTexture + * @param {Image} texture + * @example {@link utils/api/cocos/docs/cocos2d/core/textures/removeTexture.js} + */ + 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. + * @method removeTextureForKey + * @param {String} textureKeyName + * @example {@link utils/api/cocos/docs/cocos2d/core/textures/removeTextureForKey.js} + */ + removeTextureForKey: function (textureKeyName) { + if (textureKeyName == null) + return; + if (this._textures[textureKeyName]) + delete(this._textures[textureKeyName]); + }, + + /** + *

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

+ * @method addImage + * @param {String} url + * @param {Function} cb + * @param {Object} target + * @return {Texture2D} + * @example {@link utils/api/cocos/docs/cocos2d/core/textures/addImage.js} + */ + addImage: null, + addImageAsync: null, + + /** + * Cache the image data. + * @method cacheImage + * @param {String} path + * @param {Image|HTMLImageElement|HTMLCanvasElement} texture + */ + cacheImage: function (path, texture) { + if (texture instanceof Texture2D) { + this._textures[path] = texture; + return; + } + var texture2d = new 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.

+ * @method addUIImage + * @param {HTMLImageElement|HTMLCanvasElement} image + * @param {String} key + * @return {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 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.getPixelWidth(), selTexture.getPixelHeight()); + else { + cc.log(cc._LogInfos.textureCache.dumpCachedTextureInfo_2, key, selTexture.getPixelWidth(), selTexture.getPixelHeight()); + } + totalBytes += selTexture.getPixelWidth() * selTexture.getPixelHeight() * 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 = {}; + } +}; + +game.once(game.EVENT_RENDERER_INITED, function () { + var _p = textureCache; + if (cc._renderType === game.RENDER_TYPE_CANVAS) { + _p.handleLoadedTexture = function (url) { + var locTexs = this._textures; + //remove judge + var tex = locTexs[url]; + if (!tex) { + tex = locTexs[url] = new Texture2D(); + tex.url = url; + } + tex.handleLoadedTexture(); + }; + + _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.once("load", function(){ + cb && cb.call(target, tex); + }, target); + return tex; + } + } + + tex = locTexs[url] = new Texture2D(); + tex.url = url; + var loadFunc = cc.loader._checkIsImageURL(url) ? cc.loader.load : cc.loader.loadImg; + loadFunc.call(cc.loader, url, function (err, img) { + if (err) + return cb && cb.call(target, err); + textureCache.handleLoadedTexture(url); + + var texResult = locTexs[url]; + cb && cb.call(target, texResult); + }); + + return tex; + }; + + _p.addImageAsync = _p.addImage; + + } else if (cc._renderType === game.RENDER_TYPE_WEBGL) { + + _p.handleLoadedTexture = function (url) { + var locTexs = this._textures, tex, premultiplied; + //remove judge(webgl) + if (!cc.game._rendererInitialized) { + locTexs = this._loadedTexturesBefore; + } + tex = locTexs[url]; + if (!tex) { + tex = locTexs[url] = new Texture2D(); + tex.url = url; + } + premultiplied = cc.AUTO_PREMULTIPLIED_ALPHA_FOR_PNG && (cc.path.extname(url) === ".png"); + tex.handleLoadedTexture(premultiplied); + }; + + _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.once("load", function(){ + cb && cb.call(target, tex); + }, target); + return tex; + } + } + + tex = locTexs[url] = new Texture2D(); + tex.url = url; + var loadFunc = cc.loader._checkIsImageURL(url) ? cc.loader.load : cc.loader.loadImg; + loadFunc.call(cc.loader, url, function (err, img) { + if (err) + return cb && cb.call(target, err); + textureCache.handleLoadedTexture(url); + + var texResult = locTexs[url]; + cb && cb.call(target, texResult); + }); + + return tex; + }; + + _p.addImageAsync = _p.addImage; + } +}); + +cc.textureCache = module.exports = textureCache; \ No newline at end of file diff --git a/cocos2d/core/textures/index.js b/cocos2d/core/textures/index.js new file mode 100644 index 00000000000..92efd4c7500 --- /dev/null +++ b/cocos2d/core/textures/index.js @@ -0,0 +1,3 @@ +require('./CCTexture2D'); +require('./CCTextureAtlas'); +require('./CCTextureCache'); \ No newline at end of file diff --git a/cocos2d/core/utils/Async.js b/cocos2d/core/utils/Async.js new file mode 100644 index 00000000000..ee0c3ae8b16 --- /dev/null +++ b/cocos2d/core/utils/Async.js @@ -0,0 +1,181 @@ +/** + * Async Pool class, a helper of cc.async + * @class AsyncPool + * @constructor + * @param {Object|Array} srcObj + * @param {Number} limit the limit of parallel number + * @param {Function} iterator + * @param {Function} onEnd + * @param {Object} target + */ +cc.AsyncPool = function(srcObj, limit, iterator, onEnd, target){ + var self = this; + 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 ? [] : {}; + + 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) { + + self.finishedSize++; + self._workingSize--; + + var arr = Array.prototype.slice.call(arguments, 1); + self._results[this.index] = arr[0]; + if (self.finishedSize === self.size) { + if (self._onEnd) + self._onEnd.call(self._onEndTarget, null, 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(); + } +}; + +/** + * @class async + */ +cc.async = /** @lends cc.async# */{ + /** + * Do tasks series. + * @method series + * @param {Array|Object} tasks + * @param {Function} [cb] - callback + * @param {Object} [target] + * @return {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. + * @method parallel + * @param {Array|Object} tasks + * @param {Function} cb - callback + * @param {Object} [target] + * @return {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. + * @method waterfall + * @param {Array|Object} tasks + * @param {Function} cb - callback + * @param {Object} [target] + * @return {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. + * @method map + * @param {Array|Object} tasks + * @param {Function|Object} iterator + * @param {Function} [callback] + * @param {Object} [target] + * @return {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. + * @method mapLimit + * @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; + } +}; \ No newline at end of file diff --git a/cocos2d/core/utils/BinaryLoader.js b/cocos2d/core/utils/BinaryLoader.js new file mode 100644 index 00000000000..440a94aee00 --- /dev/null +++ b/cocos2d/core/utils/BinaryLoader.js @@ -0,0 +1,159 @@ +/**************************************************************************** + 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. + ****************************************************************************/ + +require('../platform/CCLoader'); + +/** + * @module cc + * @class loader + */ + +/** + * Load binary data by url. + * @method loadBinary + * @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); + 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 || (CC_TEST && xhr.status === 0))) { + 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 || (CC_TEST && xhr.status === 0)) ? cb(null, self._str2Uint8Array(xhr.responseText)) : 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. + * @method loadBinarySync + * @param {String} url + * @return {Uint8Array} + */ +cc.loader.loadBinarySync = function (url) { + var self = this; + var req = this.getXMLHttpRequest(); + 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 = this._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; + }; +} \ No newline at end of file diff --git a/cocos2d/core/utils/CCPath.js b/cocos2d/core/utils/CCPath.js new file mode 100644 index 00000000000..05361759339 --- /dev/null +++ b/cocos2d/core/utils/CCPath.js @@ -0,0 +1,162 @@ +require('../platform/CCSys'); +/** + * @class path + * @static + */ +cc.path = /** @lends cc.path# */{ + normalizeRE: /[^\.\/]+\/\.\.\//, + + /** + * Join strings to be a path. + * @method join + * @example {@link utils/api/cocos/docs/cocos2d/core/utils/CCPath/join.js} + * @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. + * @method extname + * @example {@link utils/api/cocos/docs/cocos2d/core/utils/CCPath/extname.js} + * @param {String} pathStr + * @returns {*} + */ + extname: function (pathStr) { + var temp = /(\.[^\.\/\?\\]*)(\?.*)?$/.exec(pathStr); + return temp ? temp[1] : null; + }, + + /** + * Get the main name of a file name + * @method mainFileName + * @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. + * @method basename + * @example {@link utils/api/cocos/docs/cocos2d/core/utils/CCPath/basename.js} + * @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. + * @method dirname + * @example {@link utils/api/cocos/docs/cocos2d/core/utils/CCPath/dirname.js} + * @param {String} pathStr + * @returns {*} + */ + dirname: function (pathStr) { + return pathStr.replace(/((.*)(\/|\\|\\\\))?(.*?\..*$)?/, '$2'); + }, + + /** + * Change extname of a file path. + * @method changeExtname + * @example {@link utils/api/cocos/docs/cocos2d/core/utils/CCPath/changeExtname.js} + * @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 {@link utils/api/cocos/docs/cocos2d/core/utils/CCPath/changeBasename.js} + * @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; + }, + + // The platform-specific file separator. '\\' or '/'. + sep: (cc.sys.os === cc.sys.OS_WINDOWS ? '\\' : '/'), + + // @param {string} path + // @param {boolean|string} [endsWithSep = true] + // @returns {string} + _setEndWithSep: function (path, endsWithSep) { + var sep = cc.path.sep; + if (typeof endsWithSep === 'undefined') { + endsWithSep = true; + } + else if (typeof endsWithSep === 'string') { + sep = endsWithSep; + endsWithSep = !!endsWithSep; + } + + var endChar = path[path.length - 1]; + var oldEndWithSep = (endChar === '\\' || endChar === '/'); + if (!oldEndWithSep && endsWithSep) { + path += sep; + } + else if (oldEndWithSep && !endsWithSep) { + path = path.slice(0, -1); + } + return path; + } +}; \ No newline at end of file diff --git a/cocos2d/core/utils/CCProfiler.js b/cocos2d/core/utils/CCProfiler.js new file mode 100644 index 00000000000..e6199e5d062 --- /dev/null +++ b/cocos2d/core/utils/CCProfiler.js @@ -0,0 +1,160 @@ +/** + * @class profiler + */ +cc.profiler = (function () { + var _inited = _showFPS = false; + var _frames = _frameRate = _lastSPF = _accumDt = 0; + var _afterProjection = _afterVisitListener = _FPSLabel = _SPFLabel = _drawsLabel = null; + + 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; + + var createStatsLabel = function () { + var fontSize = 0; + var w = cc.winSize.width, h = cc.winSize.height; + var locStatsPosition = cc.DIRECTOR_STATS_POSITION; + if (w > h) + fontSize = 0 | (h / 320 * 24); + else + fontSize = 0 | (w / 320 * 24); + + _FPSLabel = new cc.LabelTTF("000.0", "Arial", fontSize); + _SPFLabel = new cc.LabelTTF("0.000", "Arial", fontSize); + _drawsLabel = new cc.LabelTTF("0000", "Arial", fontSize); + + _drawsLabel.setPosition(_drawsLabel.width / 2 + locStatsPosition.x, _drawsLabel.height * 5 / 2 + locStatsPosition.y); + _SPFLabel.setPosition(_SPFLabel.width / 2 + locStatsPosition.x, _SPFLabel.height * 3 / 2 + locStatsPosition.y); + _FPSLabel.setPosition(_FPSLabel.width / 2 + locStatsPosition.x, _FPSLabel.height / 2 + locStatsPosition.y); + }; + + var analyseFPS = function (fps) { + var lastId = i = LEVELS.length - 1, 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) { + _SPFLabel.string = _lastSPF.toFixed(3); + _FPSLabel.string = _frameRate.toFixed(1); + _drawsLabel.string = (0 | cc.g_NumberOfDraws).toString(); + } + } + + if (_showFPS) { + _FPSLabel.visit(); + _SPFLabel.visit(); + _drawsLabel.visit(); + } + }; + + var afterProjection = function(){ + _FPSLabel._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + _SPFLabel._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + _drawsLabel._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.transformDirty); + }; + + 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.director.on(cc.Director.EVENT_AFTER_VISIT, afterVisit); + cc.director.on(cc.Director.EVENT_PROJECTION_CHANGED, afterProjection); + }, + + stopProfiling: function () { + cc.director.off(cc.Director.EVENT_AFTER_VISIT, afterVisit); + cc.director.off(cc.Director.EVENT_PROJECTION_CHANGED, afterProjection); + }, + + isShowingStats: function () { + return _showFPS; + }, + + showStats: function () { + if (!_inited) { + this.init(); + } + if (cc.LabelTTF && !_FPSLabel) { + createStatsLabel(); + } + if (_FPSLabel) { + _showFPS = true; + } + }, + + hideStats: function () { + _showFPS = false; + }, + + init: function () { + if (!_inited) { + this.resumeProfiling(); + _inited = true; + } + } + }; + + return profiler; +})(); + +module.exports = cc.profiler; \ No newline at end of file diff --git a/cocos2d/core/utils/base-node.js b/cocos2d/core/utils/base-node.js new file mode 100644 index 00000000000..26c4db590ea --- /dev/null +++ b/cocos2d/core/utils/base-node.js @@ -0,0 +1,1365 @@ +/**************************************************************************** + Copyright (c) 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. + ****************************************************************************/ + +var JS = cc.js; +var SceneGraphHelper = require('./scene-graph-helper'); +var SGProto = cc.Node.prototype; +var Destroying = require('../platform/CCObject').Flags.Destroying; +var DirtyFlags = require('./misc').DirtyFlags; + +// called after changing parent +function setMaxZOrder (node) { + var siblings = node._parent.getChildren(); + var z = 0; + if (siblings.length >= 2) { + var prevNode = siblings[siblings.length - 2]; + z = prevNode.getOrderOfArrival() + 1; + } + node.setOrderOfArrival(z); + return z; +} + +/** + * A base node for CCENode and CCEScene, it will: + * - provide the same api with origin cocos2d rendering node (SGNode) + * - maintains properties of the internal SGNode + * - retain and release the SGNode + * - serialize datas for SGNode (but SGNode itself will not being serialized) + * - notifications if some properties changed + * - define some interfaces shares between CCENode and CCEScene + * + * @class _BaseNode + * @extends Object + */ +var BaseNode = cc.Class(/** @lends cc.ENode# */{ + extends: cc.Object, + + properties: { + + // SERIALIZABLE + + _name: '', + _opacity: 255, + _color: cc.Color.WHITE, + _cascadeOpacityEnabled: true, + _parent: null, + _anchorPoint: cc.p(0.5, 0.5), + _contentSize: cc.size(0, 0), + _children: [], + _rotationX: 0, + _rotationY: 0.0, + _scaleX: 1.0, + _scaleY: 1.0, + _position: cc.p(0, 0), + _skewX: 0, + _skewY: 0, + _localZOrder: 0, + _globalZOrder: 0, + _ignoreAnchorPointForPosition: false, + _tag: cc.NODE_TAG_INVALID, + _opacityModifyRGB: false, + + // API + + /** + * Name of node + * @property name + * @type {String} + */ + name: { + get: function () { + return this._name; + }, + set: function (value) { + this._name = value; + }, + }, + + /** + * Parent node + * @property name + * @type {ENode} + */ + parent: { + get: function () { + return this._parent; + }, + set: function (value) { + if (this._parent === value) { + return; + } + var node = this._sgNode; + if (node._parent) { + node._parent.removeChild(node, false); + } + if (value) { + var parent = value._sgNode; + parent.addChild(node); + setMaxZOrder(node); + value._children.push(this); + } + // + var oldParent = this._parent; + this._parent = value || null; + if (oldParent) { + if (!(oldParent._objFlags & Destroying)) { + var removeAt = oldParent._children.indexOf(this); + if (removeAt < 0 && CC_EDITOR) { + return cc.error('Internal error, should not remove unknown node from parent.'); + } + oldParent._children.splice(removeAt, 1); + this._onHierarchyChanged(oldParent); + } + } + else if (value) { + this._onHierarchyChanged(null); + } + }, + }, + + /** + * uuid + * @property _id + * @type {String} + * @private + */ + _id: { + default: '', + editorOnly: true + }, + + /** + * The uuid for editor, will be stripped before building project + * @type {String} + * @readOnly + */ + uuid: { + get: function () { + return this._id || (this._id = Editor.uuid()); + } + }, + + /** + * Skew x + * @property skewX + * @type {Number} + */ + skewX: { + get: SGProto.getSkewX, + set: function (value) { + this._skewX = value; + this._sgNode.skewX = value; + } + }, + + /** + * Skew y + * @property skewY + * @type {Number} + */ + skewY: { + get: SGProto.getSkewY, + set: function (value) { + this._skewY = value; + this._sgNode._skewY = value; + } + }, + + /** + * Z order in depth which stands for the drawing order. + * @property zIndex + * @type {Number} + */ + zIndex: { + get: SGProto.getLocalZOrder, + set: function (value) { + this._localZOrder = value; + this._sgNode.zIndex = value; + } + }, + + /** + * Rotation of node + * @property rotation + * @type {Number} + */ + rotation: { + get: SGProto.getRotation, + set: function (value) { + this._rotationX = this._rotationY = value; + this._sgNode.rotation = value; + }, + tooltip: "The clockwise degrees of rotation relative to the parent" + }, + + /** + * Rotation on x axis + * @property rotationX + * @type {Number} + */ + rotationX: { + get: SGProto.getRotationX, + set: function (value) { + this._rotationX = value; + this._sgNode.rotationX = value; + }, + }, + + /** + * Rotation on y axis + * @property rotationX + * @type {Number} + */ + rotationY: { + get: SGProto.getRotationY, + set: function (value) { + this._rotationY = value; + this._sgNode.rotationY = value; + }, + }, + + /** + * Scale on x axis + * @property scaleX + * @type {Number} + */ + scaleX: { + get: SGProto.getScaleX, + set: function (value) { + this._scaleX = value; + this._sgNode.scaleX = value; + }, + }, + + /** + * Scale on y axis + * @property scaleY + * @type {Number} + */ + scaleY: { + get: SGProto.getScaleY, + set: function (value) { + this._scaleY = value; + this._sgNode.scaleY = value; + }, + }, + + /** + * position of node. + * @property position + * @type {Vec2} + */ + position: { + get: SGProto.getPosition, + set: function (value) { + this._position.x = value.x; + this._sgNode.x = value.x; + + this._position.y = value.y; + this._sgNode.y = value.y; + } + }, + + /** + * x axis position of node. + * @property x + * @type {Number} + */ + x: { + get: SGProto.getPositionX, + set: function (value) { + this._position.x = value; + this._sgNode.x = value; + }, + }, + + /** + * y axis position of node. + * @property y + * @type {Number} + */ + y: { + get: SGProto.getPositionY, + set: function (value) { + this._position.y = value; + this._sgNode.y = value; + }, + }, + + /** + * All children nodes + * @property children + * @type {ENode[]} + * @readOnly + */ + children: { + get: function () { + return this._children; + } + }, + + /** + * All children nodes. + * @property childrenCount + * @type {Number} + * @readOnly + */ + childrenCount: { + get: function () { + return this._children.length; + } + }, + + /** + * Anchor point's position on x axis. + * @property anchorX + * @type {Number} + */ + anchorX: { + get: SGProto._getAnchorX, + set: function (value) { + this._anchorPoint.x = value; + this._onAnchorChanged(); + }, + }, + + /** + * Anchor point's position on y axis. + * @property anchorY + * @type {Number} + */ + anchorY: { + get: SGProto._getAnchorY, + set: function (value) { + this._anchorPoint.y = value; + this._onAnchorChanged(); + }, + }, + + /** + * Width of node. + * @property width + * @type {Number} + */ + width: { + get: function () { + if (this._sizeProvider) { + var w = this._sizeProvider._getWidth(); + this._contentSize.width = w; + return w; + } + else { + return this._contentSize.width; + } + }, + set: function (value) { + if (this._sizeProvider) { + this._sizeProvider.setContentSize(new cc.Size(value, this._sizeProvider._getHeight())); + } + this._contentSize.width = value; + }, + }, + + /** + * Height of node. + * @property height + * @type {Number} + */ + height: { + get: function () { + if (this._sizeProvider) { + var h = this._sizeProvider._getHeight(); + this._contentSize.height = h; + return h; + } + else { + return this._contentSize.height; + } + }, + set: function (value) { + if (this._sizeProvider) { + this._sizeProvider.setContentSize(new cc.Size(this._sizeProvider._getWidth(), value)); + } + this._contentSize.height = value; + }, + }, + + //running: { + // get: SGProto.isRunning + //}, + + /** + * Indicate whether ignore the anchor point property for positioning. + * @property ignoreAnchor + * @type {Boolean} + */ + ignoreAnchor: { + get: SGProto.isIgnoreAnchorPointForPosition, + set: function (value) { + this._ignoreAnchorPointForPosition = value; + this._sgNode.ignoreAnchor = value; + this._onAnchorChanged(); + }, + }, + + /** + * Tag of node. + * @property tag + * @type {Number} + */ + tag: { + get: function () { + return this._tag; + }, + set: function (value) { + this._tag = value; + this._sgNode.tag = value; + }, + }, + + /** + * Opacity of node, default value is 255. + * @property tag + * @type {Number} + */ + opacity: { + get: function () { + return this._opacity; + }, + set: function (value) { + this._opacity = value; + this._sgNode.opacity = value; + this._onColorChanged(); + }, + range: [0, 255] + }, + + /** + * Indicate whether node's opacity value affect its child nodes, default value is false. + * @property cascadeOpacity + * @type {Boolean} + */ + cascadeOpacity: { + get: SGProto.isCascadeOpacityEnabled, + set: function (value) { + if (this._cascadeOpacityEnabled !== value) { + this._cascadeOpacityEnabled = value; + this._sgNode.cascadeOpacity = value; + this._onCascadeChanged(); + } + }, + }, + + /** + * Color of node, default value is white: (255, 255, 255). + * @property color + * @type {Color} + */ + color: { + get: function () { + var color = this._color; + return new cc.Color(color.r, color.g, color.b, color.a); + }, + set: function (value) { + var color = this._color; + color.r = value.r; + color.g = value.g; + color.b = value.b; + if (CC_DEV && value.a !== 255) { + cc.warn('Should not set alpha via "color", use "opacity" please.'); + } + //this._sgNode.color = value; + this._onColorChanged(); + }, + }, + }, + + ctor: function () { + // dont reset _id when destroyed + Object.defineProperty(this, '_id', { + value: '', + enumerable: false + }); + + var sgNode = this._sgNode = new cc.Node(); + if (!cc.game._isCloning) { + sgNode.cascadeOpacity = true; + } + + this._dirtyFlags = DirtyFlags.ALL; + + /** + * Current active scene graph node which provides content size. + * + * @property _sizeProvider + * @type {cc.Node} + * @private + */ + this._sizeProvider = null; + }, + + // ABSTRACT INTERFACES + + // called when the node's parent changed + _onHierarchyChanged: null, + // called when the node's color or opacity changed + _onColorChanged: null, + // called when the node's anchor changed + _onAnchorChanged: null, + // called when the node's cascadeOpacity or cascadeColor changed + _onCascadeChanged: null, + // called when the node's isOpacityModifyRGB changed + _onOpacityModifyRGBChanged: null, + + + /** + * Initializes the instance of cc.ENode + * @method init + * @returns {Boolean} Whether the initialization was successful. + * @deprecated, no need anymore + */ + init: function () { + return true; + }, + + /** + *

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.
+ *

+ * @method attr + * @param {Object} attrs - Properties to be set to node + */ + attr: SGProto.attr, + + //Helper function used by `setLocalZOrder`. Don't use it unless you know what you are doing. + _setLocalZOrder: function (localZOrder) { + this._localZOrder = localZOrder; + this._sgNode._localZOrder = localZOrder; + }, + + /** + *

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.

+ * @method setGlobalZOrder + * @param {Number} globalZOrder + */ + setGlobalZOrder: function (globalZOrder) { + this._globalZOrder = globalZOrder; + this._sgNode.setGlobalZOrder(globalZOrder); + }, + + /** + * Return the Node's Global Z Order. + * @method getGlobalZOrder + * @returns {number} The node's global Z order + */ + getGlobalZOrder: SGProto.getGlobalZOrder, + + /** + * Returns the scale factor of the node. + * @warning: Assertion will fail when _scaleX != _scaleY. + * @method getScale + * @return {Number} The scale factor + */ + getScale: SGProto.getScale, + + /** + * 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. + * @method setScale + * @param {Number} scale - or scaleX value + * @param {Number} [scaleY=] + */ + setScale: function (scale, scaleY) { + if (scale instanceof cc.Vec2) { + scaleY = scale.y; + scale = scale.x + } + + this._scaleX = scale; + this._scaleY = (scaleY || scaleY === 0) ? scaleY : scale; + this._sgNode.setScale(scale, scaleY); + }, + + /** + *

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

+ * @method getPosition + * @return {Vec2} The position (x,y) of the node in OpenGL coordinates + */ + getPosition: SGProto.getPosition, + + /** + *

+ * 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. + *

+ * @method setPosition + * @param {Vec2|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 {@link utils/api/cocos/docs/cocos2d/core/utils/node-wrapper/setPosition.js} + */ + 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._sgNode.setPosition(newPosOrxValue, yValue); + }, + + /** + *

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.

+ * @method getAnchorPoint + * @return {Vec2} The anchor point of node. + */ + getAnchorPoint: SGProto.getAnchorPoint, + + /** + *

+ * 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. + *

+ * @method setAnchorPoint + * @param {Vec2|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._onAnchorChanged(); + }, + + /** + * 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.ENode#getAnchorPoint + * @method getAnchorPointInPoints + * @return {Vec2} The anchor point in absolute pixels. + */ + getAnchorPointInPoints: function () { + return this._sgNode.getAnchorPointInPoints(); + }, + + /** + *

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.

+ * @method getContentSize + * @param {Boolean} [ignoreSizeProvider=false] - true if you need to get the original size of the node + * @return {Size} The untransformed size of the node. + */ + getContentSize: function (ignoreSizeProvider) { + if (this._sizeProvider && !ignoreSizeProvider) { + var size = this._sizeProvider.getContentSize(); + this._contentSize = size; + return size; + } + else { + 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. + *

+ * @method setContentSize + * @param {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; + } + if (this._sizeProvider) { + this._sizeProvider.setContentSize(locContentSize); + } + }, + + /** + * Returns a "local" axis aligned bounding box of the node.
+ * The returned box is relative only to its parent. + * @method getBoundingBox + * @return {Rect} The calculated bounding box of the node + */ + getBoundingBox: function () { + return this._sgNode.getBoundingBox(); + }, + + /** + * Stops all running actions and schedulers + * @method cleanup + */ + cleanup: function () { + //// actions + //this.stopAllActions(); + //this.unscheduleAllCallbacks(); + // + //// event + //cc.eventManager.removeListeners(this); + + // children + SGProto._arrayMakeObjectsPerformSelector(this._children, cc.Node._stateCallbackType.cleanup); + }, + + // composition: GET + + /** + * Returns a child from the container given its tag + * @method getChildByTag + * @param {Number} aTag - An identifier to find the child node. + * @return {ENode} a CCNode object whose tag equals to the input parameter + */ + getChildByTag: SGProto.getChildByTag, + + /** + * Returns a child from the container given its name + * @method getChildByName + * @param {String} name - A name to find the child node. + * @return {ENode} a CCNode object whose name equals to the input parameter + */ + getChildByName: SGProto.getChildByName, + + // 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.

+ * @method addChild + * @param {ENode} 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(cc.js.isUndefined(tag)){ + tag = undefined; + name = child._name; + } else if(cc.js.isString(tag)){ + name = tag; + tag = undefined; + } else if(cc.js.isNumber(tag)){ + 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){ + this._insertChild(child, localZOrder); + if (setTag) + child.setTag(tag); + else + child.setName(name); + }, + + _insertChild: function (child, z) { + child.parent = this; + child._setLocalZOrder(z); + }, + + // 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. + * @method removeFromParent + * @param {Boolean} [cleanup=true] - true if all actions and callbacks on this node should be removed, false otherwise. + * @see cc.ENode#removeFromParentAndCleanup + */ + removeFromParent: function (cleanup) { + if (this._parent) { + if (cleanup === undefined) + cleanup = true; + this._parent.removeChild(this, 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

+ * @method removeChild + * @param {ENode} 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); + }, + + /** + * 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.
+ * @method removeChildByTag + * @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.ENode#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.
+ * If the cleanup parameter is not passed, it will force a cleanup.
+ * @method removeAllChildren + * @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 (cleanup === undefined) + cleanup = true; + for (var i = 0; i < children.length; i++) { + var node = children[i]; + if (node) { + //if (this._running) { + // node.onExitTransitionDidStart(); + // node.onExit(); + //} + + // If you don't do cleanup, the node's actions will not get removed and the + if (cleanup) + node.cleanup(); + + node.parent = null; + } + } + this._children.length = 0; + }, + + _detachChild: function (child, doCleanup) { + // IMPORTANT: + // - 1st do onExit + // - 2nd cleanup + //if (this._running) { + // child.onExitTransitionDidStart(); + // child.onExit(); + //} + + // If you don't do cleanup, the child's actions will not get removed and the + if (doCleanup) + child.cleanup(); + + child.parent = null; + cc.js.array.remove(this._children, child); + }, + + setNodeDirty: function(){ + this._sgNode.setNodeDirty(); + }, + + /** + * Returns the matrix that transform parent's space coordinates to the node's (local) space coordinates.
+ * The matrix is in Pixels. + * @method getParentToNodeTransform + * @return {AffineTransform} + */ + getParentToNodeTransform: function () { + return this._sgNode.getParentToNodeTransform(); + }, + + /** + * Returns the world affine transform matrix. The matrix is in Pixels. + * @method getNodeToWorldTransform + * @return {AffineTransform} + */ + getNodeToWorldTransform: function () { + return this._sgNode.getNodeToWorldTransform(); + }, + + /** + * Returns the inverse world affine transform matrix. The matrix is in Pixels. + * @method getWorldToNodeTransform + * @return {AffineTransform} + */ + getWorldToNodeTransform: function () { + return this._sgNode.getWorldToNodeTransform(); + }, + + /** + * Converts a Point to node (local) space coordinates. The result is in Points. + * @method convertToNodeSpace + * @param {Vec2} worldPoint + * @return {Vec2} + */ + convertToNodeSpace: function (worldPoint) { + return this._sgNode.convertToNodeSpace(worldPoint); + }, + + /** + * Converts a Point to world space coordinates. The result is in Points. + * @method convertToWorldSpace + * @param {Vec2} nodePoint + * @return {Vec2} + */ + convertToWorldSpace: function (nodePoint) { + return this._sgNode.convertToWorldSpace(nodePoint); + }, + + /** + * Converts a Point to node (local) space coordinates. The result is in Points.
+ * treating the returned/received node point as anchor relative. + * @method convertToNodeSpaceAR + * @param {Vec2} worldPoint + * @return {Vec2} + */ + convertToNodeSpaceAR: function (worldPoint) { + return this._sgNode.convertToNodeSpaceAR(worldPoint); + }, + + /** + * Converts a local Point to world space coordinates.The result is in Points.
+ * treating the returned/received node point as anchor relative. + * @method convertToWorldSpaceAR + * @param {Vec2} nodePoint + * @return {Vec2} + */ + convertToWorldSpaceAR: function (nodePoint) { + return this._sgNode.convertToWorldSpaceAR(nodePoint); + }, + + _convertToWindowSpace: function (nodePoint) { + return this._sgNode._convertToWindowSpace(nodePoint); + }, + + /** + * convenience methods which take a cc.Touch instead of cc.Vec2 + * @method convertTouchToNodeSpace + * @param {Touch} touch - The touch object + * @return {Vec2} + */ + convertTouchToNodeSpace: function (touch) { + return this._sgNode.convertTouchToNodeSpace(touch) + }, + + /** + * converts a cc.Touch (world coordinates) into a local coordinate. This method is AR (Anchor Relative). + * @method convertTouchToNodeSpaceAR + * @param {Touch} touch - The touch object + * @return {Vec2} + */ + convertTouchToNodeSpaceAR: function (touch) { + return this._sgNode.convertTouchToNodeSpaceAR(touch); + }, + + /** + * Returns the matrix that transform the node's (local) space coordinates into the parent's space coordinates.
+ * The matrix is in Pixels. + * @method getNodeToParentTransform + * @return {AffineTransform} The affine transform object + */ + getNodeToParentTransform: function (ancestor) { + return this._sgNode.getNodeToParentTransform(); + }, + + /** + * Returns a "world" axis aligned bounding box of the node. + * @method getBoundingBoxToWorld + * @return {Rect} + */ + getBoundingBoxToWorld: function () { + return this._sgNode.getBoundingBoxToWorld(); + }, + + _getBoundingBoxToCurrentNode: function (parentTransform) { + return this._sgNode._getBoundingBoxToCurrentNode(parentTransform); + }, + + /** + * 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. + * @method getDisplayedOpacity + * @returns {number} displayed opacity + */ + getDisplayedOpacity: function () { + return this._sgNode.getDisplayedOpacity(); + }, + + /** + * Update displayed opacity + * @method + * @param {Number} parentOpacity + */ + _updateDisplayedOpacity: function (parentOpacity) { + this._sgNode.updateDisplayedOpacity(parentOpacity); + }, + + /** + * 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. + * @method getDisplayedColor + * @returns {Color} + */ + getDisplayedColor: function () { + return this._sgNode.getDisplayedColor(); + }, + + /** + * Update the displayed color of Node + * @method + * @param {Color} parentColor + */ + _updateDisplayedColor: function (parentColor) { + this._sgNode._updateDisplayedColor(parentColor); + }, + + /** + * 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. + * @method setOpacityModifyRGB + * @param {Boolean} opacityValue + */ + setOpacityModifyRGB: function (opacityValue) { + if (this._opacityModifyRGB !== opacityValue) { + this._opacityModifyRGB = opacityValue; + this._sgNode.setOpacityModifyRGB(opacityValue); + this._onOpacityModifyRGBChanged(); + } + }, + + /** + * Get whether color should be changed with the opacity value + * @method isOpacityModifyRGB + * @return {Boolean} + */ + isOpacityModifyRGB: function () { + return this._opacityModifyRGB; + }, + + // HIERARCHY METHODS + + /** + * Get the sibling index. + * + * @method getSiblingIndex + * @return {number} + */ + getSiblingIndex: function () { + if (this._parent) { + return this._parent._children.indexOf(this); + } + else { + return 0; + } + }, + + /** + * Set the sibling index of this node. + * + * @method setSiblingIndex + * @param {Number} index + */ + setSiblingIndex: function (index) { + if (!this._parent) { + return; + } + var array = this._parent._children; + index = index !== -1 ? index : array.length - 1; + var oldIndex = array.indexOf(this); + if (index !== oldIndex) { + array.splice(oldIndex, 1); + if (index < array.length) { + array.splice(index, 0, this); + } + else { + array.push(this); + } + + // update rendering scene graph, sort them by arrivalOrder + var siblings = this._parent._children; + for (var i = 0, len = siblings.length; i < len; i++) { + var sibling = siblings[i]; + sibling._sgNode.arrivalOrder = i; + } + cc.renderer.childrenOrderDirty = this._parent._sgNode._reorderChildDirty = true; + } + }, + + /** + * Is this node a child of the given node? + * + * @method isChildOf + * @param {ENode} parent + * @return {Boolean} - Returns true if this node is a child, deep child or identical to the given node. + */ + isChildOf: function (parent) { + var child = this; + do { + if (child === parent) { + return true; + } + child = child._parent; + } + while (child); + return false; + }, + + // The deserializer for sgNode which will be called before components onLoad + _onBatchCreated: function () { + var sgNode = this._sgNode; + sgNode.setOpacity(this._opacity); + sgNode.setCascadeOpacityEnabled(this._cascadeOpacityEnabled); + sgNode.setRotationX(this._rotationX); + sgNode.setRotationY(this._rotationY); + sgNode.setScale(this._scaleX, this._scaleY); + sgNode.setPosition(this._position); + sgNode.setSkewX(this._skewX); + sgNode.setSkewY(this._skewY); + sgNode.setLocalZOrder(this._localZOrder); + sgNode.setGlobalZOrder(this._globalZOrder); + sgNode.ignoreAnchorPointForPosition(this._ignoreAnchorPointForPosition); + sgNode.setTag(this._tag); + sgNode.setOpacityModifyRGB(this._opacityModifyRGB); + + if (this._parent) { + this._parent._sgNode.addChild(sgNode); + } + + var children = this._children; + for (var i = 0, len = children.length; i < len; i++) { + children[i]._onBatchCreated(); + } + }, + + _removeSgNode: SceneGraphHelper.removeSgNode, +}); + + +(function () { + + // Define public getter and setter methods to ensure api compatibility. + + var SameNameGetSets = ['name', 'skewX', 'skewY', 'rotation', 'rotationX', 'rotationY', + 'scale', 'scaleX', 'scaleY', 'children', 'childrenCount', 'parent', 'running', + /*'actionManager',*/ 'scheduler', /*'shaderProgram',*/ 'opacity', 'color', 'tag']; + var DiffNameGetSets = { + x: ['getPositionX', 'setPositionX'], + y: ['getPositionY', 'setPositionY'], + zIndex: ['getLocalZOrder', 'setLocalZOrder'], + //running: ['isRunning'], + ignoreAnchor: ['isIgnoreAnchorPointForPosition', 'ignoreAnchorPointForPosition'], + opacityModifyRGB: ['isOpacityModifyRGB'], + cascadeOpacity: ['isCascadeOpacityEnabled', 'setCascadeOpacityEnabled'], + cascadeColor: ['isCascadeColorEnabled', 'setCascadeColorEnabled'], + //// privates + //width: ['_getWidth', '_setWidth'], + //height: ['_getHeight', '_setHeight'], + //anchorX: ['_getAnchorX', '_setAnchorX'], + //anchorY: ['_getAnchorY', '_setAnchorY'], + }; + var propName, np = BaseNode.prototype; + for (var i = 0; i < SameNameGetSets.length; i++) { + propName = SameNameGetSets[i]; + var suffix = propName[0].toUpperCase() + propName.slice(1); + var pd = Object.getOwnPropertyDescriptor(np, propName); + if (pd) { + if (pd.get) np['get' + suffix] = pd.get; + if (pd.set) np['set' + suffix] = pd.set; + } + else { + JS.getset(np, propName, np['get' + suffix], np['set' + suffix]); + } + } + for (propName in DiffNameGetSets) { + var getset = DiffNameGetSets[propName]; + var pd = Object.getOwnPropertyDescriptor(np, propName); + if (pd) { + np[getset[0]] = pd.get; + if (getset[1]) np[getset[1]] = pd.set; + } + else { + JS.getset(np, propName, np[getset[0]], np[getset[1]]); + } + } +})(); + +/** + * Scale of node + * @property scale + * @type {Number} + */ + +/** + *

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

+ * @method getPositionX + * @param {Number} x - The new position in x axis + */ + +/** + *

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

+ * @method setPositionX + * @return {Number} + */ + +/** + *

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

+ * @method getPositionY + * @return {Number} + */ + +/** + *

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

+ * @method setPositionY + * @param {Number} y - The new position in y axis + */ + +/** + * Returns the local Z order of this node. + * @method getLocalZOrder + * @returns {Number} The local (relative to its siblings) Z order. + */ + +/** + *

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.

+ * @method setLocalZOrder + * @param {Number} localZOrder + */ + +/** + * 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. + * @method isIgnoreAnchorPointForPosition + * @return {Boolean} true if the anchor point will be ignored when you position this node. + */ + +/** + *

+ * 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 + *

+ * @method ignoreAnchorPointForPosition + * @param {Boolean} newValue - true if anchor point will be ignored when you position this node + */ + +/** + * Returns whether node's opacity value affect its child nodes. + * @method isCascadeOpacityEnabled + * @returns {Boolean} + */ + +/** + * Enable or disable cascade opacity, if cascade enabled, child nodes' opacity will be the multiplication of parent opacity and its own opacity. + * @method setCascadeOpacityEnabled + * @param {Boolean} cascadeOpacityEnabled + */ + +/** + * Returns whether node's color value affect its child nodes. + * @method isCascadeColorEnabled + * @returns {Boolean} + */ + +/** + * Enable or disable cascade color, if cascade enabled, child nodes' opacity will be the cascade value of parent color and its own color. + * @method setCascadeColorEnabled + * @param {Boolean} cascadeColorEnabled + */ + + +cc._BaseNode = module.exports = BaseNode; diff --git a/cocos2d/core/utils/find.js b/cocos2d/core/utils/find.js new file mode 100644 index 00000000000..bccf0076284 --- /dev/null +++ b/cocos2d/core/utils/find.js @@ -0,0 +1,66 @@ +/** + * Finds a node by hierarchy path, the path is case-sensitive. + * It will traverse the hierarchy by splitting the path using '/' character. + * This function will still returns the node even if it is inactive. + * It is recommended to not use this function every frame instead cache the result at startup. + * + * @method find + * @static + * @param {String} path + * @param {ENode} [referenceNode] + * @return {ENode} the node or null if not found + */ +cc.find = module.exports = function (path, referenceNode) { + if (path == null) { + cc.error('Argument must be non-nil'); + return null; + } + if (!referenceNode) { + var scene = cc.director.getScene(); + if (!scene) { + cc.warn('Can not get current scene.'); + return null; + } + referenceNode = scene; + } + + var match = referenceNode; + var startIndex = (path[0] !== '/') ? 0 : 1; // skip first '/' + var nameList = path.split('/'); + + // parse path + for (var n = startIndex; n < nameList.length; n++) { + var name = nameList[n]; + var findByComp = name[0] === '<' && name[name.length - 1] === '>'; + var Comp; + if (findByComp) { + var compName = name.slice(1, -1); + Comp = cc.js.getClassByName(compName); + if (!Comp) { + cc.warn('Failed to find component ' + compName); + return null; + } + } + // visit sub nodes + var children = match._children; + match = null; + for (var t = 0, len = children.length; t < len; ++t) { + var subChild = children[t]; + if (findByComp) { + if (subChild.getComponent(Comp)) { + match = subChild; + break; + } + } + else if (subChild.name === name) { + match = subChild; + break; + } + } + if (!match) { + return null; + } + } + + return match; +}; diff --git a/cocos2d/core/utils/index.js b/cocos2d/core/utils/index.js new file mode 100644 index 00000000000..ed36db128fb --- /dev/null +++ b/cocos2d/core/utils/index.js @@ -0,0 +1,4 @@ +require('./Async'); +require('./CCPath'); +require('./CCProfiler'); +require('./find'); diff --git a/cocos2d/core/utils/misc.js b/cocos2d/core/utils/misc.js new file mode 100644 index 00000000000..f50b2d836c9 --- /dev/null +++ b/cocos2d/core/utils/misc.js @@ -0,0 +1,47 @@ +var JS = cc.js; + +var misc = {}; + +misc.propertyDefine = function (ctor, sameNameGetSets, diffNameGetSets) { + var propName, np = ctor.prototype; + for (var i = 0; i < sameNameGetSets.length; i++) { + propName = sameNameGetSets[i]; + var suffix = propName[0].toUpperCase() + propName.slice(1); + var pd = Object.getOwnPropertyDescriptor(np, propName); + if (pd) { + if (pd.get) np['get' + suffix] = pd.get; + if (pd.set) np['set' + suffix] = pd.set; + } + else { + JS.getset(np, propName, np['get' + suffix], np['set' + suffix]); + } + } + for (propName in diffNameGetSets) { + var getset = diffNameGetSets[propName]; + var pd = Object.getOwnPropertyDescriptor(np, propName); + if (pd) { + np[getset[0]] = pd.get; + if (getset[1]) np[getset[1]] = pd.set; + } + else { + JS.getset(np, propName, np[getset[0]], np[getset[1]]); + } + } +}; + +var DirtyFlags = misc.DirtyFlags = { + TRANSFORM: 1 << 0, + SIZE: 1 << 1, + //Visible: + //Color: + //Opacity + //Cache + //Order + //Text + //Gradient + ALL: (1 << 2) - 1 +}; + +DirtyFlags.WIDGET = DirtyFlags.TRANSFORM | DirtyFlags.SIZE; + +module.exports = misc; diff --git a/cocos2d/core/utils/scene-graph-helper.js b/cocos2d/core/utils/scene-graph-helper.js new file mode 100644 index 00000000000..592517e1d2f --- /dev/null +++ b/cocos2d/core/utils/scene-graph-helper.js @@ -0,0 +1,63 @@ + +// Helps to maintain the actual scene graph for Entity-Component. + +var SceneGraphUtils = { + removeSgNode: function () { + var node = this._sgNode; + if (node) { + if (node._parent) { + node._parent.removeChild(node); + } + this._sgNode = null; + } + }, +}; + +if (CC_DEV) { + SceneGraphUtils._getChildrenOffset = function (entityParent) { + if (entityParent) { + var sgParent = entityParent._sgNode; + var firstChildEntity = entityParent._children[0]; + if (firstChildEntity) { + var firstChildSg = firstChildEntity._sgNode; + var offset = sgParent._children.indexOf(firstChildSg); + if (offset !== -1) { + return offset; + } + else { + cc.error("%s's scene graph node not contains in the parent's children", firstChildEntity.name); + return -1; + } + } + else { + return sgParent._children.length; + } + } + else { + return 0; // the root of hierarchy + } + }; + SceneGraphUtils.checkMatchCurrentScene = function () { + var scene = cc.director.getScene(); + var sgScene = cc.director.getRunningScene(); + function checkMatch (ent, sgNode) { + if (ent._sgNode !== sgNode) { + throw new Error('scene graph node not equal: ' + ent.name); + } + + var childCount = ent._children.length; + var childrenOffset = SceneGraphUtils._getChildrenOffset(ent); + if (sgNode._children.length !== childCount + childrenOffset) { + throw new Error('Mismatched child scene graphs: ' + ent.name); + } + for (var i = 0; i < childCount; i++) { + checkMatch(ent._children[i], sgNode._children[childrenOffset + i]); + } + } + + checkMatch(scene, sgScene); + }; + cc._Test.SceneGraphUtils = SceneGraphUtils; +} + +module.exports = SceneGraphUtils; diff --git a/cocos2d/core/value-types/CCAffineTransform.js b/cocos2d/core/value-types/CCAffineTransform.js new file mode 100644 index 00000000000..6295ecd633d --- /dev/null +++ b/cocos2d/core/value-types/CCAffineTransform.js @@ -0,0 +1,288 @@ +/**************************************************************************** + 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 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 + * @method affineTransformMake + * + * @param {Number} a + * @param {Number} b + * @param {Number} c + * @param {Number} d + * @param {Number} tx + * @param {Number} ty + * @return {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. + * @method pointApplyAffineTransform + * + * @param {Vec2|Number} point - or x. + * @param {AffineTransform|Number} transOrY - transform matrix or y. + * @param {AffineTransform} t - transform matrix or y. + * @return {Vec2} + */ +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. + * @method sizeApplyAffineTransform + * + * @param {Size} size + * @param {AffineTransform} t + * @return {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 ]

+ * + * @method affineTransformMakeIdentity + * @return {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 ]

+ * + * @method affineTransformIdentity + * @return {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. + * + * @method rectApplyAffineTransform + * @param {Rect} rect + * @param {AffineTransform} anAffineTransform + * @return {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. + * + * @method affineTransformTranslate + * @param {AffineTransform} t - The base affine transform object. + * @param {Number} tx - The translation on x axis. + * @param {Number} ty - The translation on y axis. + * @return {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. + * @method affineTransformScale + * @param {AffineTransform} t - The base affine transform object. + * @param {Number} sx - The scale on x axis. + * @param {Number} sy - The scale on y axis. + * @return {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. + * @method affineTransformRotate + * @param {AffineTransform} aTransform - The base affine transform object. + * @param {Number} anAngle - The angle to rotate. + * @return {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 + * @method affineTransformConcat + * @param {AffineTransform} t1 - The first transform object. + * @param {AffineTransform} t2 - The transform object to concatenate. + * @return {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 + * @method affineTransformConcatIn + * @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. + * @method affineTransformEqualToTransform + * @param {AffineTransform} t1 + * @param {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. + * @method affineTransformInvert + * @param {AffineTransform} t + * @return {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)}; +}; diff --git a/cocos2d/core/value-types/CCColor.js b/cocos2d/core/value-types/CCColor.js new file mode 100644 index 00000000000..51d2102ef89 --- /dev/null +++ b/cocos2d/core/value-types/CCColor.js @@ -0,0 +1,491 @@ +var ValueType = require('./CCValueType'); +var JS = require('../platform/js'); + +var Color = (function () { + + /** + * Representation of RGBA colors. + * + * Each color component is a floating point value with a range from 0 to 1. + * + * You can also use the convenience method <% crosslink cc.fireColor cc.fireColor %> to create a new Color. + * + * @class Color + * @extends ValueType + * @constructor + * @param {Number} [r=0] - red component of the color. + * @param {Number} [g=0] - green component of the color. + * @param {Number} [b=0] - blue component of the color. + * @param {Number} [a=255] - alpha component of the color. + */ + function Color( r, g, b, a ) { + if (typeof r === 'object') { + g = r.g; + b = r.b; + a = r.a; + r = r.r; + } + this.r = typeof r === 'number' ? r : 0; + this.g = typeof g === 'number' ? g : 0; + this.b = typeof b === 'number' ? b : 0; + this.a = typeof a === 'number' ? a : 255; + } + JS.extend(Color, ValueType); + require('../platform/CCClass').fastDefine('cc.Color', Color, ['r', 'g', 'b', 'a']); + + var DefaultColors = { + // color: [r, g, b, a] + /** + * @property WHITE + * @type {Color} + * @static + */ + WHITE: [255, 255, 255, 255], + /** + * @property BLACK + * @type {Color} + * @static + */ + BLACK: [0, 0, 0, 255], + /** + * @property TRANSPARENT + * @type {Color} + * @static + */ + TRANSPARENT:[0, 0, 0, 0], + /** + * @property GRAY + * @type {Color} + * @static + */ + GRAY: [127.5, 127.5, 127.5], + /** + * @property RED + * @type {Color} + * @static + */ + RED: [255, 0, 0], + /** + * @property GREEN + * @type {Color} + * @static + */ + GREEN: [0, 255, 0], + /** + * @property BLUE + * @type {Color} + * @static + */ + BLUE: [0, 0, 255], + /** + * @property YELLOW + * @type {Color} + * @static + */ + YELLOW: [255, 235, 4], + /** + * @property ORANGE + * @type {Color} + * @static + */ + ORANGE: [255, 127, 0], + /** + * @property CYAN + * @type {Color} + * @static + */ + CYAN: [0, 255, 255], + /** + * @property MAGENTA + * @type {Color} + * @static + */ + MAGENTA: [255, 0, 255] + }; + for (var colorName in DefaultColors) { + var colorGetter = (function (r, g, b, a) { + return function () { + return new Color(r, g, b, a); + }; + }).apply(null, DefaultColors[colorName]); + Object.defineProperty(Color, colorName, { get: colorGetter }); + } + + /** + * Clone a new color from the current color. + * @method clone + * @return {Color} Newly created color. + */ + Color.prototype.clone = function () { + return new Color(this.r, this.g, this.b, this.a); + }; + + /** + * @method equals + * @param {Color} other + * @return {Boolean} + */ + Color.prototype.equals = function (other) { + return other && + this.r === other.r && + this.g === other.g && + this.b === other.b && + this.a === other.a; + }; + + /** + * @method lerp + * @param {Color} to + * @param {number} ratio - the interpolation coefficient. + * @param {Color} [out] - optional, the receiving vector. + * @return {Color} + */ + Color.prototype.lerp = function (to, ratio, out) { + out = out || new Color(); + var r = this.r; + var g = this.g; + var b = this.b; + var a = this.a; + out.r = r + (to.r - r) * ratio; + out.g = g + (to.g - g) * ratio; + out.b = b + (to.b - b) * ratio; + out.a = a + (to.a - a) * ratio; + return out; + }; + + /** + * @method toString + * @return {String} + */ + Color.prototype.toString = function () { + return "rgba(" + + this.r.toFixed() + ", " + + this.g.toFixed() + ", " + + this.b.toFixed() + ", " + + this.a.toFixed() + ")" + ; + }; + + /** + * @method setR + * @param {Number} red - the new Red component. + * @return {Color} this color. + */ + Color.prototype.setR = function (red) { + this.r = red; + return this; + }; + /** + * @method setG + * @param {Number} green - the new Green component. + * @return {Color} this color. + */ + Color.prototype.setG = function (green) { + this.g = green; + return this; + }; + /** + * @method setB + * @param {Number} blue - the new Blue component. + * @return {Color} this color. + */ + Color.prototype.setB = function (blue) { + this.b = blue; + return this; + }; + /** + * @method setA + * @param {Number} alpha - the new Alpha component. + * @return {Color} this color. + */ + Color.prototype.setA = function (alpha) { + this.a = alpha; + return this; + }; + + /** + * @method toCSS + * @param {String} opt - "rgba", "rgb", "#rgb" or "#rrggbb". + * @return {String} + */ + Color.prototype.toCSS = function ( opt ) { + if ( opt === 'rgba' ) { + return "rgba(" + + (this.r | 0 ) + "," + + (this.g | 0 ) + "," + + (this.b | 0 ) + "," + + (this.a / 255).toFixed(2) + ")" + ; + } + else if ( opt === 'rgb' ) { + return "rgb(" + + (this.r | 0 ) + "," + + (this.g | 0 ) + "," + + (this.b | 0 ) + ")" + ; + } + else { + return '#' + this.toHEX(opt); + } + }; + + /** + * Clamp this color to make all components between 0 to 1. + * @method clamp + */ + Color.prototype.clamp = function () { + this.r = cc.clampf(this.r, 0, 255); + this.g = cc.clampf(this.g, 0, 255); + this.b = cc.clampf(this.b, 0, 255); + this.a = cc.clampf(this.a, 0, 255); + }; + + /** + * @method fromHEX + * @param {String} hexString + * @return {Color} + * @chainable + */ + Color.prototype.fromHEX = function (hexString) { + var hex = parseInt(((hexString.indexOf('#') > -1) ? hexString.substring(1) : hexString), 16); + this.r = (hex >> 16); + this.g = ((hex & 0x00FF00) >> 8); + this.b = ((hex & 0x0000FF)); + return this; + }; + + /** + * @method toHEX + * @param {String} fmt - "#rgb" or "#rrggbb". + * @return {String} + */ + Color.prototype.toHEX = function ( fmt ) { + var hex = [ + (this.r | 0 ).toString(16), + (this.g | 0 ).toString(16), + (this.b | 0 ).toString(16), + ]; + var i = -1; + if ( fmt === '#rgb' ) { + for ( i = 0; i < hex.length; ++i ) { + if ( hex[i].length > 1 ) { + hex[i] = hex[i][0]; + } + } + } + else if ( fmt === '#rrggbb' ) { + for ( i = 0; i < hex.length; ++i ) { + if ( hex[i].length === 1 ) { + hex[i] = '0' + hex[i]; + } + } + } + return hex.join(''); + }; + + /** + * Convert to 24bit rgb value. + * @method toRGBValue + * @return {Number} + */ + Color.prototype.toRGBValue = function () { + return (cc.clampf(this.r, 0, 255) << 16) + + (cc.clampf(this.g, 0, 255) << 8) + + (cc.clampf(this.b, 0, 255)); + }; + + /** + * @method fromHSV + * @param {Number} h + * @param {Number} s + * @param {Number} v + * @return {Color} + * @chainable + */ + Color.prototype.fromHSV = function ( h, s, v ) { + var rgb = Color.hsv2rgb( h, s, v ); + this.r = rgb.r; + this.g = rgb.g; + this.b = rgb.b; + return this; + }; + + /** + * @method toHSV + * @return {Object} - {h: number, s: number, v: number}. + */ + Color.prototype.toHSV = function () { + return Color.rgb2hsv( this.r, this.g, this.b ); + }; + + return Color; +})(); + +/** + * @method rgb2hsv + * @param {Number} r - red, must be [0, 255]. + * @param {Number} g - red, must be [0, 255]. + * @param {Number} b - red, must be [0, 255]. + * @return {Object} - {h: number, s: number, v: number}. + */ +Color.rgb2hsv = function ( r, g, b ) { + r = r / 255; + g = g / 255; + b = b / 255; + var hsv = { h: 0, s: 0, v: 0 }; + var max = Math.max(r,g,b); + var min = Math.min(r,g,b); + var delta = 0; + hsv.v = max; + hsv.s = max ? (max - min) / max : 0; + if (!hsv.s) hsv.h = 0; + else { + delta = max - min; + if (r === max) hsv.h = (g - b) / delta; + else if (g === max) hsv.h = 2 + (b - r) / delta; + else hsv.h = 4 + (r - g) / delta; + hsv.h /= 6; + if (hsv.h < 0) hsv.h += 1.0; + } + return hsv; +}; + +/** + * @method hsv2rgb + * @param {Number} h + * @param {Number} s + * @param {Number} v + * @return {Object} - {r: number, g: number, b: number}}, rgb will be in [0, 255]. + */ +Color.hsv2rgb = function ( h, s, v ) { + var rgb = { r: 0, g: 0, b: 0 }; + if (s === 0) { + rgb.r = rgb.g = rgb.b = v; + } + else { + if (v === 0) { + rgb.r = rgb.g = rgb.b = 0; + } + else { + if (h === 1) h = 0; + h *= 6; + s = s; + v = v; + var i = Math.floor(h); + var f = h - i; + var p = v * (1 - s); + var q = v * (1 - (s * f)); + var t = v * (1 - (s * (1 - f))); + switch (i) { + case 0: + rgb.r = v; + rgb.g = t; + rgb.b = p; + break; + + case 1: + rgb.r = q; + rgb.g = v; + rgb.b = p; + break; + + case 2: + rgb.r = p; + rgb.g = v; + rgb.b = t; + break; + + case 3: + rgb.r = p; + rgb.g = q; + rgb.b = v; + break; + + case 4: + rgb.r = t; + rgb.g = p; + rgb.b = v; + break; + + case 5: + rgb.r = v; + rgb.g = p; + rgb.b = q; + break; + } + } + } + rgb.r *= 255; + rgb.g *= 255; + rgb.b *= 255; + return rgb; +}; + +cc.Color = Color; + +/** + * The convenience method to create a new <% crosslink cc.Color color %> + * Alpha channel is optional. Default value is 255. + * @method color + * @param {Number} [r=0] + * @param {Number} [g=0] + * @param {Number} [b=0] + * @param {Number} [a=255] + * @return {Color} + * + * @examples {@link utils/api/cocos/docs/cocos2d/core/value-types/CCColor/color.js} + */ +cc.color = function color (r, g, b, a) { + if (JS.isString(r)) { + var result = new cc.Color(); + return result.fromHEX(r); + } + if (cc.js.isObject(r)) { + return new cc.Color(r.r, r.g, r.b, r.a); + } + return new cc.Color(r, g, b, a); +}; + + +// Functional style API, for backward compatibility + +/** + * returns true if both ccColor3B are equal. Otherwise it returns false. + * @method colorEqual + * @param {Color} color1 + * @param {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; +}; + +/** + * convert a string of color for style to Color. + * e.g. "#ff06ff" to : cc.color(255,6,255) + * @method hexToColor + * @param {String} hex + * @return {Color} + */ +cc.hexToColor = function (hex) { + hex = hex.replace(/^#?/, "0x"); + var c = parseInt(hex); + var r = (c >> 16); + var g = ((c & 0x00FF00) >> 8); + var b = ((c & 0x0000FF)); + return cc.color(r, g, b); +}; + +/** + * convert Color to a string of color for style. + * e.g. cc.color(255,6,255) to : "#ff06ff" + * @method colorToHex + * @param {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); +}; + +module.exports = cc.Color; \ No newline at end of file diff --git a/cocos2d/core/value-types/CCEnum.js b/cocos2d/core/value-types/CCEnum.js new file mode 100644 index 00000000000..1c1e142c7d0 --- /dev/null +++ b/cocos2d/core/value-types/CCEnum.js @@ -0,0 +1,132 @@ +/**************************************************************************** + 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. + ****************************************************************************/ + +// enum + +/** + * Define an enum type. If a enum item has a value of -1, it will be given an Integer number according to it's order in the list. Otherwise it will use the value specified by user who writes the enum definition. + * @method defineEnum + * @param {object} obj - a JavaScript literal object containing enum names and values + * @return {object} the defined enum type + * + * @example + Texture.WrapMode = cc.Enum({ + Repeat: -1, + Clamp: -1 +}); + // Texture.WrapMode.Repeat == 0 + // Texture.WrapMode.Clamp == 1 + // Texture.WrapMode[0] == "Repeat" + // Texture.WrapMode[1] == "Clamp" + + var FlagType = cc.Enum({ + Flag1: 1, + Flag2: 2, + Flag3: 4, + Flag4: 8, +}); + var AtlasSizeList = cc.Enum({ + 128: 128, + 256: 256, + 512: 512, + 1024: 1024, +}); + */ + +cc.Enum = function (obj) { + var enumType = {}; + Object.defineProperty(enumType, '__enums__', { + value: undefined, + writable: true + }); + + var lastIndex = -1; + for (var key in obj) { + var val = obj[key]; + if (val === -1) { + val = ++lastIndex; + } + else { + lastIndex = val; + } + enumType[key] = val; + + var reverseKey = '' + val; + if (key !== reverseKey) { + Object.defineProperty(enumType, reverseKey, { + value: key, + enumerable: false + }); + } + } + return enumType; +}; + +cc.Enum.isEnum = function (enumType) { + return enumType && enumType.hasOwnProperty('__enums__'); +}; + +/** + * @method getList + * @param {Object} enumDef - the enum type defined from cc.Enum + * @return {Object[]} + * @private + */ +cc.Enum.getList = function (enumDef) { + if ( enumDef.__enums__ !== undefined ) + return enumDef.__enums__; + + var enums = []; + for ( var entry in enumDef ) { + if ( enumDef.hasOwnProperty(entry) ) { + var value = enumDef[entry]; + var isInteger = typeof value === 'number' && (value | 0) === value; // polyfill Number.isInteger + if ( isInteger ) { + enums.push( { name: entry, value: value } ); + } + } + } + enums.sort( function ( a, b ) { return a.value - b.value; } ); + + enumDef.__enums__ = enums; + return enums; +}; + +if (CC_DEV) { + // check key order in object literal + var _TestEnum = cc.Enum({ + ZERO: -1, + ONE: -1, + TWO: -1, + THREE: -1 + }); + if (_TestEnum.ZERO !== 0 || _TestEnum.ONE !== 1 || _TestEnum.TWO !== 2 || _TestEnum.THREE !== 3) { + cc.error('Sorry, "cc.Enum" not available on this platform, ' + + 'please report this error here: https://github.com/fireball-x/fireball/issues/new'); + } +} + +module.exports = cc.Enum; \ No newline at end of file diff --git a/cocos2d/core/value-types/CCRect.js b/cocos2d/core/value-types/CCRect.js new file mode 100644 index 00000000000..9089b05f97a --- /dev/null +++ b/cocos2d/core/value-types/CCRect.js @@ -0,0 +1,416 @@ +var ValueType = require('./CCValueType'); +var JS = require('../platform/js'); + +/** + * A 2D rectangle defined by x, y position and width, height. + * + * see {% crosslink cc.Rect cc.rect %} + * + * @class Rect + * @extends ValueType + * @constructor + * @param {Number} [x=0] + * @param {Number} [y=0] + * @param {Number} [w=0] + * @param {Number} [h=0] + */ +function Rect (x, y, w, h) { + if (typeof x === 'object') { + y = x.y; + w = x.width; + h = x.height; + x = x.x; + } + this.x = typeof x === 'number' ? x : 0.0; + this.y = typeof y === 'number' ? y : 0.0; + this.width = typeof w === 'number' ? w : 0.0; + this.height = typeof h === 'number' ? h : 0.0; +} +JS.extend(Rect, ValueType); +require('../platform/CCClass').fastDefine('cc.Rect', Rect, ['x', 'y', 'width', 'height']); + +/** + * Creates a rectangle from two coordinate values. + * @static + * @method fromMinMax + * @param {Vec2} v1 + * @param {Vec2} v2 + * @return {Rect} + */ +Rect.fromMinMax = function (v1, v2) { + var min_x = Math.min(v1.x, v2.x); + var min_y = Math.min(v1.y, v2.y); + var max_x = Math.max(v1.x, v2.x); + var max_y = Math.max(v1.y, v2.y); + + return new Rect(min_x, min_y, max_x - min_x, max_y - min_y); +}; + +/** + * Checks if rect contains. + * @static + * @method contain + * @param a {Rect} Rect a + * @param b {Rect} Rect b + * @return {Number} The contains result, 1 is a contains b, -1 is b contains a, 0 is no contains. + */ +Rect.contain = function _Contain (a, b) { + if (a.x <= b.x && + a.x + a.width >= b.x + b.width && + a.y <= b.y && + a.y + a.height >= b.y + b.height) { + // a contains b + return 1; + } + if (b.x <= a.x && + b.x + b.width >= a.x + a.width && + b.y <= a.y && + b.y + b.height >= a.y + a.height) { + // b contains a + return -1; + } + return 0; +}; + +var proto = Rect.prototype; + +/** + * @method clone + * @return {Rect} + */ +proto.clone = function () { + return new Rect(this.x, this.y, this.width, this.height); +}; + +/** + * @method equals + * @param {Rect} other + * @return {Boolean} + */ +proto.equals = function (other) { + return other && + this.x === other.x && + this.y === other.y && + this.width === other.width && + this.height === other.height; +}; + +/** + * @method lerp + * @param {Rect} to + * @param {Number} ratio - the interpolation coefficient. + * @param {Rect} [out] - optional, the receiving vector. + * @return {Rect} + */ +proto.lerp = function (to, ratio, out) { + out = out || new Rect(); + var x = this.x; + var y = this.y; + var width = this.width; + var height = this.height; + out.x = x + (to.x - x) * ratio; + out.y = y + (to.y - y) * ratio; + out.width = width + (to.width - width) * ratio; + out.height = height + (to.height - height) * ratio; + return out; +}; + +/** + * @method toString + * @return {String} + */ +proto.toString = function () { + return '(' + this.x.toFixed(2) + ', ' + this.y.toFixed(2) + ', ' + this.width.toFixed(2) + + ', ' + this.height.toFixed(2) + ')'; +}; + +/** + * @property xMin + * @type {Number} + */ +Object.defineProperty(proto, 'xMin', { + get: function () { return this.x; }, + set: function (value) { + this.width += this.x - value; + this.x = value; + } +}); + +/** + * @property yMin + * @type {Number} + */ +Object.defineProperty(proto, 'yMin', { + get: function () { return this.y; }, + set: function (value) { + this.height += this.y - value; + this.y = value; + } +}); + +/** + * @property xMax + * @type {Number} + */ +Object.defineProperty(proto, 'xMax', { + get: function () { return this.x + this.width; }, + set: function (value) { this.width = value - this.x; } +}); + +/** + * @property yMax + * @type {Number} + */ +Object.defineProperty(proto, 'yMax', { + get: function () { return this.y + this.height; }, + set: function (value) { this.height = value - this.y; } +}); + +/** + * @property center + * @type {Number} + */ +Object.defineProperty(proto, 'center', { + get: function () { + return new cc.Vec2(this.x + this.width * 0.5, + this.y + this.height * 0.5); + }, + set: function (value) { + this.x = value.x - this.width * 0.5; + this.y = value.y - this.height * 0.5; + } +}); + +/** + * @property {Size} size + */ +Object.defineProperty(proto, 'size', { + get: function () { + return new cc.Size(this.width, this.height); + }, + set: function (value) { + this.width = value.width; + this.height = value.height; + } +}); + +/** + * @method intersects + * @param {Rect} rect + * @type {Boolean} + */ +proto.intersects = function (rect) { + return cc.rectIntersectsRect(this, rect); +}; + +/** + * Returns true if the point inside this rectangle. + * @method contains + * @param {Vec2} point + * @type {Boolean} + */ +proto.contains = function (point) { + return (this.x <= point.x && + this.x + this.width >= point.x && + this.y <= point.y && + this.y + this.height >= point.y); +}; + +/** + * Returns true if the other rect totally inside this rectangle. + * @method containsRect + * @param {Rect} rect + * @type {Boolean} + */ +proto.containsRect = function (rect) { + return (this.x <= rect.x && + this.x + this.width >= rect.x + rect.width && + this.y <= rect.y && + this.y + this.height >= rect.y + rect.height); +}; + +cc.Rect = Rect; + + + +/** + * The convenience method to create a new Rect. + * @method rect + * @param {Number[]|Number} [x=0] + * @param {Number} [y=0] + * @param {Number} [w=0] + * @param {Number} [h=0] + * @return {Rect} + */ +cc.rect = function rect (x, y, w, h) { + return new Rect(x, y, w, h); +}; + + +// Functional style API, for backward compatibility + +/** + * Check whether a rect's value equals to another. + * @method rectEqualToRect + * @param {Rect} rect1 + * @param {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. + * @method rectContainsRect + * @param {Rect} rect1 + * @param {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. + * @method rectGetMaxX + * @param {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. + * @method rectGetMidX + * @param {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. + * @method rectGetMinX + * @param {Rect} rect + * @return {Number} The leftmost x value. + */ +cc.rectGetMinX = function (rect) { + return rect.x; +}; + +/** + * Return the topmost y-value of a rect. + * @method rectGetMaxY + * @param {Rect} rect + * @return {Number} The topmost y value. + */ +cc.rectGetMaxY = function (rect) { + return(rect.y + rect.height); +}; + +/** + * Return the midpoint y-value of `rect'. + * @method rectGetMidY + * @param {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. + * @method rectGetMinY + * @param {Rect} rect + * @return {Number} The bottommost y value. + */ +cc.rectGetMinY = function (rect) { + return rect.y; +}; + +/** + * Check whether a rect contains a point. + * @method rectContainsPoint + * @param {Rect} rect + * @param {Vec2} 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. + * @method rectIntersectsRect + * @param {Rect} rectA + * @param {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. + * @method rectOverlapsRect + * @param {Rect} rectA + * @param {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. + * @method rectUnion + * @param {Rect} rectA + * @param {Rect} rectB + * @return {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. + * @method rectUnion + * @param {Rect} rectA + * @param {Rect} rectB + * @return {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; +}; + +module.exports = cc.Rect; \ No newline at end of file diff --git a/cocos2d/core/value-types/CCSize.js b/cocos2d/core/value-types/CCSize.js new file mode 100644 index 00000000000..fd97f7db1a8 --- /dev/null +++ b/cocos2d/core/value-types/CCSize.js @@ -0,0 +1,104 @@ +var ValueType = require('./CCValueType'); +var JS = require('../platform/js'); + +/** + * cc.Size is the class for size object, please do not use its constructor to create sizes, use cc.size() alias function instead. + * It will be deprecated soon, please use cc.Vec2 instead + * @class Size + * @param {Number} width + * @param {Number} height + * @see cc.size + */ +function Size (width, height) { + if (typeof width === 'object') { + height = width.height; + width = width.width; + } + this.width = typeof width === 'number' ? width : 0; + this.height = typeof height === 'number' ? height : 0; +} +JS.extend(Size, ValueType); +require('../platform/CCClass').fastDefine('cc.Size', Size, ['width', 'height']); + +/** + * return a Size object with width = 0 and height = 0. + * @property ZERO + * @type {Size} + * @default new Size(0, 0) + * @static + */ +JS.get(Size, 'ZERO', function () { + return new Size(0.0, 0.0); +}); + +var proto = Size.prototype; + +/** + * @method clone + * @return {Size} + */ +proto.clone = function () { + return new Size(this.width, this.height); +}; + +/** + * @method equals + * @param {Size} other + * @return {Boolean} + */ +proto.equals = function (other) { + return other && + this.width === other.width && + this.height === other.height; +}; + +/** + * @method lerp + * @param {Rect} to + * @param {Number} ratio - the interpolation coefficient. + * @param {Size} [out] - optional, the receiving vector. + * @return {Size} + */ +proto.lerp = function (to, ratio, out) { + out = out || new Size(); + var width = this.width; + var height = this.height; + out.width = width + (to.width - width) * ratio; + out.height = height + (to.height - height) * ratio; + return out; +}; + +/** + * @method toString + * @return {String} + */ +proto.toString = function () { + return '(' + this.width.toFixed(2) + ', ' + this.height.toFixed(2) + ')'; +}; + +/** + * Helper function that creates a cc.Size. + * Please use cc.p or cc.v2 instead, it will soon replace cc.Size. + * + * @method size + * @param {Number|Size} w - width or a size object + * @param {Number} h - height + * @return {Size} + * @example {@link utils/api/cocos/docs/cocos2d/core/value-types/CCSize/size.js} + */ +cc.size = function (w, h) { + return new Size(w, h); +}; + +/** + * Check whether a point's value equals to another. + * @method sizeEqualToSize + * @param {Size} size1 + * @param {Size} size2 + * @return {Boolean} + */ +cc.sizeEqualToSize = function (size1, size2) { + return (size1 && size2 && (size1.width === size2.width) && (size1.height === size2.height)); +}; + +cc.Size = module.exports = Size; diff --git a/cocos2d/core/value-types/CCTypes.js b/cocos2d/core/value-types/CCTypes.js new file mode 100644 index 00000000000..55bfe5936d7 --- /dev/null +++ b/cocos2d/core/value-types/CCTypes.js @@ -0,0 +1,282 @@ +/**************************************************************************** + Copyright (c) 2008-2010 Ricardo Quesada + 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 device accelerometer reports values for each axis in units of g-force. + * @class 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; +}; + +/** + * Blend Function used for textures. + * @Class 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; +}; + +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.js.get(cc.BlendFunc, "DISABLE", cc.BlendFunc._disable); +/** @expose */ +cc.BlendFunc.ALPHA_PREMULTIPLIED; +cc.js.get(cc.BlendFunc, "ALPHA_PREMULTIPLIED", cc.BlendFunc._alphaPremultiplied); +/** @expose */ +cc.BlendFunc.ALPHA_NON_PREMULTIPLIED; +cc.js.get(cc.BlendFunc, "ALPHA_NON_PREMULTIPLIED", cc.BlendFunc._alphaNonPremultiplied); +/** @expose */ +cc.BlendFunc.ADDITIVE; +cc.js.get(cc.BlendFunc, "ADDITIVE", cc.BlendFunc._additive); + +/** + * @method blendFuncDisable + * @returns {BlendFunc} + */ +cc.blendFuncDisable = function () { + return new cc.BlendFunc(cc.ONE, cc.ZERO); +}; + +/** + * Common usage: + * + * var fontDef = new cc.FontDefinition(); + * fontDef.fontName = "Arial"; + * fontDef.fontSize = 12; + * ... + * + * OR using inline definition usefull for constructor injection + * + * var fontDef = new cc.FontDefinition({ + * fontName: "Arial", + * fontSize: 12 + * }); + * + * @class 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.TextAlignment.CENTER; + _t.verticalAlign = cc.VerticalTextAlignment.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 + "'"; +}; + +/** + * Enum for sprite type + * @enum SpriteType + */ +cc.SpriteType = cc.Enum({ + /** + * @property {Number} SIMPLE + */ + SIMPLE: 0, + /** + * @property {Number} SLICED + */ + SLICED: 1, + ///* + // * @property {Number} TILED + // */ + //TILED : 2, + ///* + // * @property {Number} FILLED + // */ + //FILLED: 3 +}); + +/** + * Enum for text alignment + * @enum TextAlignment + */ +cc.TextAlignment = cc.Enum({ + /** + * @property {Number} LEFT + */ + LEFT: 0, + /** + * @property {Number} CENTER + */ + CENTER: 1, + /** + * @property {Number} RIGHT + */ + RIGHT: 2 +}); + +/** + * Enum for vertical text alignment + * @enum VerticalTextAlignment + */ +cc.VerticalTextAlignment = cc.Enum({ + /** + * @property {Number} TOP + */ + TOP: 0, + /** + * @property {Number} CENTER + */ + CENTER: 1, + /** + * @property {Number} BOTTOM + */ + BOTTOM: 2 +}); + +cc._Dictionary = cc.Class({ + + 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; + } +}); diff --git a/cocos2d/core/value-types/CCTypesWebGL.js b/cocos2d/core/value-types/CCTypesWebGL.js new file mode 100644 index 00000000000..debcf98c2e2 --- /dev/null +++ b/cocos2d/core/value-types/CCTypesWebGL.js @@ -0,0 +1,662 @@ +/**************************************************************************** + 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. + ****************************************************************************/ + +// define some types with ArrayBuffer for WebGL + +/** + * @class WebGLColor + * @param {Number} r + * @param {Number} g + * @param {Number} b + * @param {Number} a + * @param {Array} arrayBuffer + * @param {Number} offset + * @constructor + */ +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, locElementLen = Uint8Array.BYTES_PER_ELEMENT; + this._rU8 = new Uint8Array(locArrayBuffer, locOffset, 1); + this._gU8 = new Uint8Array(locArrayBuffer, locOffset + locElementLen, 1); + this._bU8 = new Uint8Array(locArrayBuffer, locOffset + locElementLen * 2, 1); + this._aU8 = new Uint8Array(locArrayBuffer, locOffset + locElementLen * 3, 1); + + this._rU8[0] = r || 0; + this._gU8[0] = g || 0; + this._bU8[0] = b || 0; + this._aU8[0] = (a == null) ? 255 : a; + + if (a === undefined) + this.a_undefined = true; +}; +/** + * @constant + * @type {Number} + */ +cc.WebGLColor.BYTES_PER_ELEMENT = 4; +var _p = cc.WebGLColor.prototype; +_p._getR = function () { + return this._rU8[0]; +}; +_p._setR = function (value) { + this._rU8[0] = value < 0 ? 0 : value; +}; +_p._getG = function () { + return this._gU8[0]; +}; +_p._setG = function (value) { + this._gU8[0] = value < 0 ? 0 : value; +}; +_p._getB = function () { + return this._bU8[0]; +}; +_p._setB = function (value) { + this._bU8[0] = value < 0 ? 0 : value; +}; +_p._getA = function () { + return this._aU8[0]; +}; +_p._setA = function (value) { + this._aU8[0] = value < 0 ? 0 : value; +}; +/** @expose */ +_p.r; +cc.js.getset(_p, "r", _p._getR, _p._setR); +/** @expose */ +_p.g; +cc.js.getset(_p, "g", _p._getG, _p._setG); +/** @expose */ +_p.b; +cc.js.getset(_p, "b", _p._getB, _p._setB); +/** @expose */ +_p.a; +cc.js.getset(_p, "a", _p._getA, _p._setA); + +//redefine cc.Vertex2F +/** + * @class 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._xF32 = new Float32Array(this._arrayBuffer, this._offset, 1); + this._yF32 = new Float32Array(this._arrayBuffer, this._offset + 4, 1); + this._xF32[0] = x || 0; + this._yF32[0] = y || 0; +}; +/** + * @constant + * @type {Number} + */ +cc.Vertex2F.BYTES_PER_ELEMENT = 8; + +_p = cc.Vertex2F.prototype; +_p._getX = function () { + return this._xF32[0]; +}; +_p._setX = function (xValue) { + this._xF32[0] = xValue; +}; +_p._getY = function () { + return this._yF32[0]; +}; +_p._setY = function (yValue) { + this._yF32[0] = yValue; +}; +/** @expose */ +_p.x; +cc.js.getset(_p, "x", _p._getX, _p._setX); +/** @expose */ +_p.y; +cc.js.getset(_p, "y", _p._getY, _p._setY); + +// redefine cc.Vertex3F +/** + * @class 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._xF32 = new Float32Array(locArrayBuffer, locOffset, 1); + this._xF32[0] = x || 0; + this._yF32 = new Float32Array(locArrayBuffer, locOffset + Float32Array.BYTES_PER_ELEMENT, 1); + this._yF32[0] = y || 0; + this._zF32 = new Float32Array(locArrayBuffer, locOffset + Float32Array.BYTES_PER_ELEMENT * 2, 1); + this._zF32[0] = z || 0; +}; +/** + * @constant + * @type {Number} + */ +cc.Vertex3F.BYTES_PER_ELEMENT = 12; + +_p = cc.Vertex3F.prototype; +_p._getX = function () { + return this._xF32[0]; +}; +_p._setX = function (xValue) { + this._xF32[0] = xValue; +}; +_p._getY = function () { + return this._yF32[0]; +}; +_p._setY = function (yValue) { + this._yF32[0] = yValue; +}; +_p._getZ = function () { + return this._zF32[0]; +}; +_p._setZ = function (zValue) { + this._zF32[0] = zValue; +}; +/** @expose */ +_p.x; +cc.js.getset(_p, "x", _p._getX, _p._setX); +/** @expose */ +_p.y; +cc.js.getset(_p, "y", _p._getY, _p._setY); +/** @expose */ +_p.z; +cc.js.getset(_p, "z", _p._getZ, _p._setZ); + +// redefine cc.Tex2F +/** + * @class 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._uF32 = new Float32Array(this._arrayBuffer, this._offset, 1); + this._vF32 = new Float32Array(this._arrayBuffer, this._offset + 4, 1); + this._uF32[0] = u || 0; + this._vF32[0] = v || 0; +}; +/** + * @constants + * @type {Number} + */ +cc.Tex2F.BYTES_PER_ELEMENT = 8; + +_p = cc.Tex2F.prototype; +_p._getU = function () { + return this._uF32[0]; +}; +_p._setU = function (xValue) { + this._uF32[0] = xValue; +}; +_p._getV = function () { + return this._vF32[0]; +}; +_p._setV = function (yValue) { + this._vF32[0] = yValue; +}; +/** @expose */ +_p.u; +cc.js.getset(_p, "u", _p._getU, _p._setU); +/** @expose */ +_p.v; +cc.js.getset(_p, "v", _p._getV, _p._setV); + +//redefine cc.Quad2 +/** + * @class Quad2 + * @param {Vertex2F} tl + * @param {Vertex2F} tr + * @param {Vertex2F} bl + * @param {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, locElementLen = cc.Vertex2F.BYTES_PER_ELEMENT; + this._tl = tl ? new cc.Vertex2F(tl.x, tl.y, locArrayBuffer, 0) : new cc.Vertex2F(0, 0, locArrayBuffer, 0); + this._tr = tr ? new cc.Vertex2F(tr.x, tr.y, locArrayBuffer, locElementLen) : new cc.Vertex2F(0, 0, locArrayBuffer, locElementLen); + this._bl = bl ? new cc.Vertex2F(bl.x, bl.y, locArrayBuffer, locElementLen * 2) : new cc.Vertex2F(0, 0, locArrayBuffer, locElementLen * 2); + this._br = br ? new cc.Vertex2F(br.x, br.y, locArrayBuffer, locElementLen * 3) : new cc.Vertex2F(0, 0, locArrayBuffer, locElementLen * 3); +}; +/** + * @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.x = tlValue.x; + this._tl.y = tlValue.y; +}; +_p._getTR = function () { + return this._tr; +}; +_p._setTR = function (trValue) { + this._tr.x = trValue.x; + this._tr.y = trValue.y; +}; +_p._getBL = function() { + return this._bl; +}; +_p._setBL = function (blValue) { + this._bl.x = blValue.x; + this._bl.y = blValue.y; +}; +_p._getBR = function () { + return this._br; +}; +_p._setBR = function (brValue) { + this._br.x = brValue.x; + this._br.y = brValue.y; +}; + +/** @expose */ +_p.tl; +cc.js.getset(_p, "tl", _p._getTL, _p._setTL); +/** @expose */ +_p.tr; +cc.js.getset(_p, "tr", _p._getTR, _p._setTR); +/** @expose */ +_p.bl; +cc.js.getset(_p, "bl", _p._getBL, _p._setBL); +/** @expose */ +_p.br; +cc.js.getset(_p, "br", _p._getBR, _p._setBR); + +/** + * A 3D Quad. 4 * 3 floats + * @Class Quad3 + * @Construct + * @param {Vertex3F} bl1 + * @param {Vertex3F} br1 + * @param {Vertex3F} tl1 + * @param {Vertex3F} tr1 + */ +cc.Quad3 = function (bl1, br1, tl1, tr1) { + this.bl = bl1 || new cc.Vertex3F(0, 0, 0); + this.br = br1 || new cc.Vertex3F(0, 0, 0); + this.tl = tl1 || new cc.Vertex3F(0, 0, 0); + this.tr = tr1 || new cc.Vertex3F(0, 0, 0); +}; + +//redefine cc.V3F_C4B_T2F +/** + * @class V3F_C4B_T2F + * @param {Vertex3F} vertices + * @param {Color} colors + * @param {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, locElementLen = cc.Vertex3F.BYTES_PER_ELEMENT; + this._vertices = vertices ? new cc.Vertex3F(vertices.x, vertices.y, vertices.z, locArrayBuffer, locOffset) : + new cc.Vertex3F(0, 0, 0, locArrayBuffer, locOffset); + this._colors = colors ? new cc.WebGLColor(colors.r, colors.g, colors.b, colors.a, locArrayBuffer, locOffset + locElementLen) : + new cc.WebGLColor(0, 0, 0, 0, locArrayBuffer, locOffset + locElementLen); + this._texCoords = texCoords ? new cc.Tex2F(texCoords.u, texCoords.v, locArrayBuffer, locOffset + locElementLen + cc.WebGLColor.BYTES_PER_ELEMENT) : + new cc.Tex2F(0, 0, locArrayBuffer, locOffset + locElementLen + cc.WebGLColor.BYTES_PER_ELEMENT); +}; +/** + * @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.x = verticesValue.x; + locVertices.y = verticesValue.y; + locVertices.z = verticesValue.z; +}; +_p._getColor = function () { + return this._colors; +}; +_p._setColor = function (colorValue) { + var locColors = this._colors; + locColors.r = colorValue.r; + locColors.g = colorValue.g; + locColors.b = colorValue.b; + locColors.a = colorValue.a; +}; +_p._getTexCoords = function () { + return this._texCoords; +}; +_p._setTexCoords = function (texValue) { + this._texCoords.u = texValue.u; + this._texCoords.v = texValue.v; +}; +/** @expose */ +_p.vertices; +cc.js.getset(_p, "vertices", _p._getVertices, _p._setVertices); +/** @expose */ +_p.colors; +cc.js.getset(_p, "colors", _p._getColor, _p._setColor); +/** @expose */ +_p.texCoords; +cc.js.getset(_p, "texCoords", _p._getTexCoords, _p._setTexCoords); + +//redefine cc.V3F_C4B_T2F_Quad +/** + * @class V3F_C4B_T2F_Quad + * @param {V3F_C4B_T2F} tl + * @param {V3F_C4B_T2F} bl + * @param {V3F_C4B_T2F} tr + * @param {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); + this._bl = bl ? new cc.V3F_C4B_T2F(bl.vertices, bl.colors, bl.texCoords, locArrayBuffer, locOffset + locElementLen) : + new cc.V3F_C4B_T2F(null, null, null, locArrayBuffer, locOffset + locElementLen); + this._tr = tr ? new cc.V3F_C4B_T2F(tr.vertices, tr.colors, tr.texCoords, locArrayBuffer, locOffset + locElementLen * 2) : + new cc.V3F_C4B_T2F(null, null, null, locArrayBuffer, locOffset + locElementLen * 2); + this._br = br ? new cc.V3F_C4B_T2F(br.vertices, br.colors, br.texCoords, locArrayBuffer, locOffset + locElementLen * 3) : + new cc.V3F_C4B_T2F(null, null, null, locArrayBuffer, locOffset + locElementLen * 3); +}; +/** + * @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.js.getset(_p, "tl", _p._getTL, _p._setTL); +/** @expose */ +_p.tr; +cc.js.getset(_p, "tr", _p._getTR, _p._setTR); +/** @expose */ +_p.bl; +cc.js.getset(_p, "bl", _p._getBL, _p._setBL); +/** @expose */ +_p.br; +cc.js.getset(_p, "br", _p._getBR, _p._setBR); +/** @expose */ +_p.arrayBuffer; +cc.js.get(_p, "arrayBuffer", _p._getArrayBuffer); + +/** + * @method V3F_C4B_T2F_QuadZero + * @returns {V3F_C4B_T2F_Quad} + */ +cc.V3F_C4B_T2F_QuadZero = function () { + return new cc.V3F_C4B_T2F_Quad(); +}; + +/** + * @method V3F_C4B_T2F_QuadCopy + * @param {V3F_C4B_T2F_Quad} sourceQuad + * @return {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}} + }; +}; + +/** + * @method V3F_C4B_T2F_QuadsCopy + * @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 V2F_C4B_T2F + * @param {Vertex2F} vertices + * @param {Color} colors + * @param {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, locElementLen = cc.Vertex2F.BYTES_PER_ELEMENT; + this._vertices = vertices ? new cc.Vertex2F(vertices.x, vertices.y, locArrayBuffer, locOffset) : + new cc.Vertex2F(0, 0, locArrayBuffer, locOffset); + this._colors = colors ? new cc.WebGLColor(colors.r, colors.g, colors.b, colors.a, locArrayBuffer, locOffset + locElementLen) : + new cc.WebGLColor(0, 0, 0, 0, locArrayBuffer, locOffset + locElementLen); + this._texCoords = texCoords ? new cc.Tex2F(texCoords.u, texCoords.v, locArrayBuffer, locOffset + locElementLen + cc.WebGLColor.BYTES_PER_ELEMENT) : + new cc.Tex2F(0, 0, locArrayBuffer, locOffset + locElementLen + cc.WebGLColor.BYTES_PER_ELEMENT); +}; + +/** + * @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.x = verticesValue.x; + this._vertices.y = verticesValue.y; +}; +_p._getColor = function () { + return this._colors; +}; +_p._setColor = function (colorValue) { + var locColors = this._colors; + locColors.r = colorValue.r; + locColors.g = colorValue.g; + locColors.b = colorValue.b; + locColors.a = colorValue.a; +}; +_p._getTexCoords = function () { + return this._texCoords; +}; +_p._setTexCoords = function (texValue) { + this._texCoords.u = texValue.u; + this._texCoords.v = texValue.v; +}; + +/** @expose */ +_p.vertices; +cc.js.getset(_p, "vertices", _p._getVertices, _p._setVertices); +/** @expose */ +_p.colors; +cc.js.getset(_p, "colors", _p._getColor, _p._setColor); +/** @expose */ +_p.texCoords; +cc.js.getset(_p, "texCoords", _p._getTexCoords, _p._setTexCoords); + +//redefine cc.V2F_C4B_T2F_Triangle +/** + * @class V2F_C4B_T2F_Triangle + * @param {V2F_C4B_T2F} a + * @param {V2F_C4B_T2F} b + * @param {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); + this._b = b ? new cc.V2F_C4B_T2F(b.vertices, b.colors, b.texCoords, locArrayBuffer, locOffset + locElementLen) : + new cc.V2F_C4B_T2F(null, null, null, locArrayBuffer, locOffset + locElementLen); + this._c = c ? new cc.V2F_C4B_T2F(c.vertices, c.colors, c.texCoords, locArrayBuffer, locOffset + locElementLen * 2) : + new cc.V2F_C4B_T2F(null, null, null, locArrayBuffer, locOffset + locElementLen * 2); +}; +/** + * @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.js.getset(_p, "a", _p._getA, _p._setA); +/** @expose */ +_p.b; +cc.js.getset(_p, "b", _p._getB, _p._setB); +/** @expose */ +_p.c; +cc.js.getset(_p, "c", _p._getC, _p._setC); \ No newline at end of file diff --git a/cocos2d/core/value-types/CCValueType.js b/cocos2d/core/value-types/CCValueType.js new file mode 100644 index 00000000000..bfabfff9448 --- /dev/null +++ b/cocos2d/core/value-types/CCValueType.js @@ -0,0 +1,57 @@ +var JS = require('../platform/js'); + +/** + * The base class of all value types. + * @class ValueType + * @constructor + */ +function ValueType () {} +JS.setClassName('cc.ValueType', ValueType); + +JS.mixin(ValueType.prototype, { + /** + * !#en This method returns an exact copy of current value. + * !#zh 克隆当å‰å€¼ï¼Œè¯¥æ–¹æ³•è¿”回一个新对象,新对象的值和原对象相等。 + * @method clone + * @return {ValueType} + */ + clone: CC_EDITOR && function () { + cc.error("%.clone not yet implemented.", JS.getClassName(this)); + return null; + }, + + /** + * Compares this object with the other one. + * @method equals + * @param {ValueType} other + * @return {Boolean} + */ + equals: CC_EDITOR && function (other) { + cc.error("%.equals not yet implemented.", JS.getClassName(this)); + return false; + }, + + /** + * @method toString + * @return {string} + */ + toString: function () { + return '' + {}; + }, + + /** + * Linearly interpolates between this value to to value by ratio which is in the range [0, 1]. + * When ratio = 0 returns this. When ratio = 1 return to. When ratio = 0.5 returns the average of this and to. + * @method lerp + * @param {ValueType} to - the to value + * @param {number} ratio - the interpolation coefficient + * @return {ValueType} + */ + lerp: CC_EDITOR && function (to, ratio) { + cc.warn("%.lerp not yet implemented.", JS.getClassName(this)); + return this.clone(); + } +}); + +cc.ValueType = ValueType; +module.exports = ValueType; diff --git a/cocos2d/core/value-types/CCVec2.js b/cocos2d/core/value-types/CCVec2.js new file mode 100644 index 00000000000..750a8877489 --- /dev/null +++ b/cocos2d/core/value-types/CCVec2.js @@ -0,0 +1,483 @@ +var ValueType = require('./CCValueType'); +var JS = require('../platform/js'); +var FireClass = require('../platform/CCClass'); + +/** + * Representation of 2D vectors and points. + * + * see {% crosslink cc.Vec2 cc.v2 %} + * @class Vec2 + * @extends ValueType + * @constructor + * @param {number} [x=0] + * @param {number} [y=0] + */ +function Vec2 (x, y) { + if (typeof x === 'object') { + y = x.y; + x = x.x; + } + this.x = (typeof x === 'number' ? x : 0.0); + this.y = (typeof y === 'number' ? y : 0.0); +} +JS.extend(Vec2, ValueType); +FireClass.fastDefine('cc.Vec2', Vec2, ['x', 'y']); + +JS.mixin(Vec2.prototype, { + + /** + * !#en clone a Vec2 value + * !#zh 克隆一个 Vec2 值 + * @method clone + * @return {Vec2} + */ + clone: function () { + return new Vec2(this.x, this.y); + }, + + /** + * @method set + * @param {Vec2} newValue - !#en new value to set. !#zh è¦è®¾ç½®çš„新值 + * @return {Vec2} returns this + * @chainable + */ + set: function (newValue) { + this.x = newValue.x; + this.y = newValue.y; + return this; + }, + + /** + * @method equals + * @param {Vec2} other + * @return {Boolean} + */ + equals: function (other) { + return other && this.x === other.x && this.y === other.y; + }, + + /** + * @method toString + * @return {string} + */ + toString: function () { + return "(" + + this.x.toFixed(2) + ", " + + this.y.toFixed(2) + ")" + ; + }, + + /** + * @method lerp + * @param {Vec2} to + * @param {number} ratio - the interpolation coefficient + * @param {Vec2} [out] - optional, the receiving vector + * @return {Vec2} + */ + lerp: function (to, ratio, out) { + out = out || new Vec2(); + var x = this.x; + var y = this.y; + out.x = x + (to.x - x) * ratio; + out.y = y + (to.y - y) * ratio; + return out; + }, + + /** + * Adds this vector. If you want to save result to another vector, use add() instead. + * @method addSelf + * @param {Vec2} vector + * @return {Vec2} returns this + * @chainable + */ + addSelf: function (vector) { + this.x += vector.x; + this.y += vector.y; + return this; + }, + + /** + * Adds two vectors, and returns the new result. + * @method add + * @param {Vec2} vector + * @param {Vec2} [out] - optional, the receiving vector + * @return {Vec2} the result + */ + add: function (vector, out) { + out = out || new Vec2(); + out.x = this.x + vector.x; + out.y = this.y + vector.y; + return out; + }, + + /** + * Subtracts one vector from this. If you want to save result to another vector, use sub() instead. + * @method subSelf + * @param {Vec2} vector + * @return {Vec2} returns this + * @chainable + */ + subSelf: function (vector) { + this.x -= vector.x; + this.y -= vector.y; + return this; + }, + + /** + * Subtracts one vector from this, and returns the new result. + * @method sub + * @param {Vec2} vector + * @param {Vec2} [out] - optional, the receiving vector + * @return {Vec2} the result + */ + sub: function (vector, out) { + out = out || new Vec2(); + out.x = this.x - vector.x; + out.y = this.y - vector.y; + return out; + }, + + /** + * Multiplies this by a number. If you want to save result to another vector, use mul() instead. + * @method mulSelf + * @param {number} num + * @return {Vec2} returns this + * @chainable + */ + mulSelf: function (num) { + this.x *= num; + this.y *= num; + return this; + }, + + /** + * Multiplies by a number, and returns the new result. + * @method mul + * @param {number} num + * @param {Vec2} [out] - optional, the receiving vector + * @return {Vec2} the result + */ + mul: function (num, out) { + out = out || new Vec2(); + out.x = this.x * num; + out.y = this.y * num; + return out; + }, + + /** + * Multiplies two vectors. + * @method scaleSelf + * @param {Vec2} vector + * @return {Vec2} returns this + * @chainable + */ + scaleSelf: function (vector) { + this.x *= vector.x; + this.y *= vector.y; + return this; + }, + + /** + * Multiplies two vectors, and returns the new result. + * @method scale + * @param {Vec2} vector + * @param {Vec2} [out] - optional, the receiving vector + * @return {Vec2} the result + */ + scale: function (vector, out) { + out = out || new Vec2(); + out.x = this.x * vector.x; + out.y = this.y * vector.y; + return out; + }, + + /** + * Divides by a number. If you want to save result to another vector, use div() instead. + * @method divSelf + * @param {Vec2} vector + * @return {Vec2} returns this + * @chainable + */ + divSelf: function (num) { + this.x /= num; + this.y /= num; + return this; + }, + + /** + * Divides by a number, and returns the new result. + * @method div + * @param {Vec2} vector + * @param {Vec2} [out] - optional, the receiving vector + * @return {Vec2} the result + */ + div: function (num, out) { + out = out || new Vec2(); + out.x = this.x / num; + out.y = this.y / num; + return out; + }, + + /** + * Negates the components. If you want to save result to another vector, use neg() instead. + * @method negSelf + * @return {Vec2} returns this + * @chainable + */ + negSelf: function () { + this.x = -this.x; + this.y = -this.y; + return this; + }, + + /** + * Negates the components, and returns the new result. + * @method neg + * @param {Vec2} [out] - optional, the receiving vector + * @return {Vec2} the result + */ + neg: function (out) { + out = out || new Vec2(); + out.x = -this.x; + out.y = -this.y; + return out; + }, + + /** + * Dot product + * @method dot + * @param {Vec2} [vector] + * @return {number} the result + */ + dot: function (vector) { + return this.x * vector.x + this.y * vector.y; + }, + + /** + * Cross product + * @method cross + * @param {Vec2} [vector] + * @return {number} the result + */ + cross: function (vector) { + return this.y * vector.x - this.x * vector.y; + }, + + /** + * Returns the length of this vector. + * @method mag + * @return {number} the result + */ + mag: function () { + return Math.sqrt(this.x * this.x + this.y * this.y); + }, + + /** + * Returns the squared length of this vector. + * @method magSqr + * @return {number} the result + */ + magSqr: function () { + return this.x * this.x + this.y * this.y; + }, + + /** + * Make the length of this vector to 1. + * @method normalizeSelf + * @return {Vec2} returns this + * @chainable + */ + normalizeSelf: function () { + var magSqr = this.x * this.x + this.y * this.y; + if (magSqr === 1.0) + return this; + + if (magSqr === 0.0) { + console.warn("Can't normalize zero vector"); + return this; + } + + var invsqrt = 1.0 / Math.sqrt(magSqr); + this.x *= invsqrt; + this.y *= invsqrt; + + return this; + }, + + /** + * Returns this vector with a magnitude of 1. + * + * Note that the current vector is unchanged and a new normalized vector is returned. If you want to normalize the current vector, use normalizeSelf function. + * @method normalize + * @param {Vec2} [out] - optional, the receiving vector + * @return {Vec2} result + */ + normalize: function (out) { + out = out || new Vec2(); + out.x = this.x; + out.y = this.y; + out.normalizeSelf(); + return out; + }, + + /** + * Get angle in radian between this and vector + * @method angle + * @param {Vec2} vector + * @return {number} from 0 to Math.PI + */ + angle: function (vector) { + var magSqr1 = this.magSqr(); + var magSqr2 = vector.magSqr(); + + if (magSqr1 === 0 || magSqr2 === 0) { + console.warn("Can't get angle between zero vector"); + return 0.0; + } + + var dot = this.dot(vector); + var theta = dot / (Math.sqrt(magSqr1 * magSqr2)); + theta = cc.clampf(theta, -1.0, 1.0); + return Math.acos(theta); + }, + + /** + * Get angle in radian between this and vector with direction + * @method signAngle + * @param {Vec2} vector + * @return {number} from -MathPI to Math.PI + */ + signAngle: function (vector) { + // NOTE: this algorithm will return 0.0 without signed if vectors are parallex + // var angle = this.angle(vector); + // var cross = this.cross(vector); + // return Math.sign(cross) * angle; + + return Math.atan2(this.y, this.x) - Math.atan2(vector.y, vector.x); + }, + + /** + * rotate + * @method rotate + * @param {number} radians + * @param {Vec2} [out] - optional, the receiving vector + * @return {Vec2} the result + */ + rotate: function (radians, out) { + out = out || new Vec2(); + out.x = this.x; + out.y = this.y; + return out.rotateSelf(radians); + }, + + /** + * rotate self + * @method rotateSelf + * @param {number} radians + * @return {Vec2} returns this + * @chainable + */ + rotateSelf: function (radians) { + var sin = Math.sin(radians); + var cos = Math.cos(radians); + var x = this.x; + this.x = cos * x - sin * this.y; + this.y = sin * x + cos * this.y; + return this; + } + + //_serialize: function () { + // return [this.x, this.y]; + //}, + //_deserialize: function (data) { + // this.x = data[0]; + // this.y = data[1]; + //} +}); + +// static + +/** + * return a Vec2 object with x = 1 and y = 1 + * @property ONE + * @type Vec2 + * @static + */ +JS.get(Vec2, 'ONE', function () { + return new Vec2(1.0, 1.0); +}); + +/** + * return a Vec2 object with x = 0 and y = 0 + * @property ZERO + * @type Vec2 + * @static + */ +JS.get(Vec2, 'ZERO', function () { + return new Vec2(0.0, 0.0); +}); + +/** + * return a Vec2 object with x = 0 and y = 1 + * @property up + * @type Vec2 + * @static + */ +JS.get(Vec2, 'UP', function () { + return new Vec2(0.0, 1.0); +}); + +/** + * return a Vec2 object with x = 1 and y = 0 + * @property RIGHT + * @type Vec2 + * @static + */ +JS.get(Vec2, 'RIGHT', function () { + return new Vec2(1.0, 0.0); +}); + +cc.Vec2 = Vec2; +var proto = Vec2.prototype; + + +/** + * The convenience method to create a new {% crosslink Vec2 Vec2 %} + * @method v2 + * @param {Number|Object} [x=0] + * @param {Number} [y=0] + * @return {Vec2} + */ +cc.v2 = function v2 (x, y) { + return new Vec2(x, y); +}; + +/** + * Helper function that creates a cc.Vec2. + * @function + * @param {Number|Object} [x=0] a Number or a size object + * @param {Number} [y=0] + * @return {cc.Vec2} + * @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 = cc.v2; + + +// Functional style API, for backward compatibility + +/** + * Check whether a point's value equals to another + * @function + * @param {cc.Vec2} point1 + * @param {cc.Vec2} point2 + * @return {Boolean} + */ +cc.pointEqualToPoint = function (point1, point2) { + return point1 && point2 && (point1.x === point2.x) && (point1.y === point2.y); +}; + +module.exports = cc.Vec2; \ No newline at end of file diff --git a/cocos2d/core/value-types/index.js b/cocos2d/core/value-types/index.js new file mode 100644 index 00000000000..12f0c3ff26a --- /dev/null +++ b/cocos2d/core/value-types/index.js @@ -0,0 +1,9 @@ +require('./CCValueType'); +require('./CCEnum'); +require('./CCVec2'); +require('./CCSize'); +require('./CCRect'); +require('./CCColor'); +require('./CCTypes'); +require('./CCAffineTransform') +require('./CCTypesWebGL'); \ No newline at end of file diff --git a/cocos2d/deprecated.js b/cocos2d/deprecated.js new file mode 100644 index 00000000000..d85d39a1066 --- /dev/null +++ b/cocos2d/deprecated.js @@ -0,0 +1,503 @@ +if (CC_DEV) { + var js = cc.js; + + var INFO = cc._LogInfos.deprecated; + + /** + * Inject all of the properties in source objects to target object and return the target object. + * @param {object} target + * @param {object} *sources + * @name cc.inject + * @memberof cc + * @deprecated + * @returns {object} + */ + js.get(cc, "inject", function () { + cc.warn(INFO + " The first argument should be the destination object", 'cc.inject', 'cc.js.addon'); + return js.addon; + }); + + /** + * Copy all of the properties in source objects to target object and return the target object. + * @param {object} target + * @param {object} *sources + * @name cc.extend + * @memberof cc + * @deprecated + * @returns {object} + */ + js.get(cc, "extend", function () { + cc.warn(INFO, 'cc.extend', 'cc.js.mixin'); + return js.mixin; + }); + + /** + * Create new DOM element by tag name + * @name cc.newElement + * @memberof cc + * @deprecated + * @returns {object} + */ + js.get(cc, "newElement", function () { + cc.warn(INFO, 'cc.newElement', 'document.createElement'); + return document.createElement; + }); + + /** + * Check the obj whether is function or not + * @name cc.isFunction + * @memberof cc + * @deprecated + * @param {*} obj + * @returns {boolean} + */ + js.get(cc, "isFunction", function () { + cc.warn(INFO, 'cc.isFunction', 'cc.js.isFunction'); + return js.isFunction; + }); + + /** + * Check the obj whether is number or not + * @name cc.isNumber + * @memberof cc + * @deprecated + * @param {*} obj + * @returns {boolean} + */ + js.get(cc, "isNumber", function () { + cc.warn(INFO, 'cc.isNumber', 'cc.js.isNumber'); + return js.isNumber; + }); + + /** + * Check the obj whether is string or not + * @name cc.isString + * @memberof cc + * @deprecated + * @param {*} obj + * @returns {boolean} + */ + js.get(cc, "isString", function () { + cc.warn(INFO, 'cc.isString', 'cc.js.isString'); + return js.isString; + }); + + /** + * Check the obj whether is array or not + * @name cc.isArray + * @memberof cc + * @deprecated + * @param {*} obj + * @returns {boolean} + */ + js.get(cc, "isArray", function () { + cc.warn(INFO, 'cc.isArray', 'cc.js.isArray'); + return js.isArray; + }); + + /** + * Check the obj whether is undefined or not + * @name cc.isUndefined + * @memberof cc + * @deprecated + * @param {*} obj + * @returns {boolean} + */ + js.get(cc, "isUndefined", function () { + cc.warn(INFO, 'cc.isUndefined', 'cc.js.isUndefined'); + return js.isUndefined; + }); + + /** + * Check the obj whether is object or not + * @name cc.isObject + * @memberof cc + * @deprecated + * @param {*} obj + * @returns {boolean} + */ + js.get(cc, "isObject", function () { + cc.warn(INFO, 'cc.isObject', 'cc.js.isObject'); + return js.isObject; + }); + + /** + * 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 + * @memberof cc + * @deprecated + * @param {Number} x + * @param {Number} y + * @see cc.Vec2 + */ + js.obsoletes(cc, 'cc', { + "Point": 'Vec2' + }); + + /** + * Verify Array's Type + * @memberof cc + * @deprecated + * @param {Array} arr + * @param {function} type + * @return {Boolean} + * @function + */ + js.get(cc, 'arrayVerifyType', function () { + cc.warn(INFO, 'cc.arrayVerifyType', 'cc.js.array.verifyType'); + return cc.js.array.verifyType; + }); + + /** + * Searches for the first occurance of object and removes it. If object is not found the function has no effect. + * @function + * @memberof cc + * @deprecated + * @param {Array} arr Source Array + * @param {*} delObj remove object + */ + js.get(cc, 'arrayRemoveObject', function () { + cc.warn(INFO, 'cc.arrayRemoveObject', 'cc.js.array.remove'); + return cc.js.array.remove; + }); + + /** + * Removes from arr all values in minusArr. For each Value in minusArr, the first matching instance in arr will be removed. + * @function + * @memberof cc + * @deprecated + * @param {Array} arr Source Array + * @param {Array} minusArr minus Array + */ + js.get(cc, 'arrayRemoveArray', function () { + cc.warn(INFO, 'cc.arrayRemoveArray', 'cc.js.array.removeArray'); + return cc.js.array.removeArray; + }); + + /** + * Inserts some objects at index + * @function + * @memberof cc + * @deprecated + * @param {Array} arr + * @param {Array} addObjs + * @param {Number} index + * @return {Array} + */ + js.get(cc, 'arrayAppendObjectsToIndex', function() { + cc.warn(INFO, 'cc.arrayAppendObjectsToIndex', 'cc.js.array.appendObjectsAt'); + return cc.js.array.appendObjectsAt; + }); + + /** + * Copy an array's item to a new array (its performance is better than Array.slice) + * @memberof cc + * @deprecated + * @param {Array} arr + * @return {Array} + */ + js.get(cc, 'copyArray', function() { + cc.warn(INFO, 'cc.copyArray', 'cc.js.array.copy'); + return cc.js.array.copy; + }); + + function deprecateEnum (obj, oldPath, newPath, hasTypePrefixBefore) { + hasTypePrefixBefore = hasTypePrefixBefore !== false; + var enumDef = eval(newPath); + var entries = cc.Enum.getList(enumDef); + var delimiter = hasTypePrefixBefore ? '_' : '.'; + for (var i = 0; i < entries.length; i++) { + var entry = entries[i].name; + var oldPropName; + if (hasTypePrefixBefore) { + var oldTypeName = oldPath.split('.').slice(-1)[0]; + oldPropName = oldTypeName + '_' + entry; + } + else { + oldPropName = entry; + } + js.get(obj, oldPropName, function (entry) { + cc.warn(INFO, oldPath + delimiter + entry, newPath + '.' + entry); + return enumDef[entry]; + }.bind(null, entry)); + } + } + + deprecateEnum(cc, 'cc.TEXT_ALIGNMENT', 'cc.TextAlignment'); + deprecateEnum(cc, 'cc.VERTICAL_TEXT_ALIGNMENT', 'cc.VerticalTextAlignment'); + deprecateEnum(cc.ParticleSystem, 'cc.ParticleSystem.TYPE', 'cc.ParticleSystem.Type'); + deprecateEnum(cc.ParticleSystem, 'cc.ParticleSystem.MODE', 'cc.ParticleSystem.Mode'); + deprecateEnum(cc.EParticleSystem, 'cc.EParticleSystem.TYPE', 'cc.EParticleSystem.PositionType'); + deprecateEnum(cc.EParticleSystem, 'cc.EParticleSystem.MODE', 'cc.EParticleSystem.EmitterMode'); + deprecateEnum(ccui.ScrollView, 'ccui.ScrollView.DIR', 'ccui.ScrollView.Dir'); + deprecateEnum(ccui.ScrollView, 'ccui.ScrollView.EVENT', 'ccui.ScrollView.Event'); + deprecateEnum(ccui.Layout, 'ccui.Layout', 'ccui.Layout.Type', false); + deprecateEnum(ccui.LoadingBar, 'ccui.LoadingBar.TYPE', 'ccui.LoadingBar.Type'); + deprecateEnum(ccui.RelativeLayoutParameter, 'ccui.RelativeLayoutParameter', 'ccui.RelativeLayoutParameter.Type', false); + deprecateEnum(cc.ProgressTimer, 'cc.ProgressTimer.TYPE', 'cc.ProgressTimer.Type'); + deprecateEnum(cc.game, 'cc.game.DEBUG_MODE', 'cc.DebugMode'); + if (cc.EditBox) { + deprecateEnum(cc, 'cc.KEYBOARD_RETURNTYPE', 'cc.KeyboardReturnType'); + deprecateEnum(cc, 'cc.EDITBOX_INPUT_MODE', 'cc.EditBox.InputMode'); + deprecateEnum(cc, 'cc.EDITBOX_INPUT_FLAG', 'cc.EditBox.InputFlag'); + } + cc.game.once(cc.game.EVENT_RENDERER_INITED, function () { + deprecateEnum(cc, 'cc', 'cc.Texture2D.WrapMode', false); + }); + + function markAsRemoved (ownerCtor, removedProps, ownerName) { + ownerName = ownerName || js.getClassName(ownerCtor); + removedProps.forEach(function (prop) { + function error () { + cc.error('Sorry, %s.%s is removed.', ownerName, prop); + } + js.getset(ownerCtor.prototype, prop, error, error); + }); + } + + function provideClearError (owner, obj) { + var className = cc.js.getClassName(owner); + var Info = 'Sorry,' + className + '.%s is removed, please use %s instead.'; + for (var prop in obj) { + function define (prop, getset) { + function accessor (newProp) { + cc.error(Info, prop, newProp); + } + if (!Array.isArray(getset)) { + getset = getset.split(',') + .map(function (x) { + return x.trim(); + }); + } + js.getset(owner, prop, accessor.bind(null, getset[0]), getset[1] && accessor.bind(null, getset[1])); + } + var getset = obj[prop]; + if (prop[0] === '*') { + // get set + var etProp = prop.slice(1); + define('g' + etProp, getset); + define('s' + etProp, getset); + } + else { + prop.split(',') + .map(function (x) { + return x.trim(); + }) + .forEach(function (x) { + define(x, getset); + }); + } + } + } + + function shouldNotUseNodeProp (component) { + var compName = cc.js.getClassName(component); + var Info = 'Sorry, ' + compName + '.%s is removed, please use cc.ENode.%s instead.'; + var compProto = component.prototype; + for (var prop in cc.ENode.prototype) { + (function (prop) { + if (!(prop in compProto) && prop[0] !== '_') { + js.getset(compProto, prop, + function () { + cc.error(Info, prop, prop); + }, + function () { + cc.error(Info, prop, prop); + } + ); + } + })(prop); + } + } + + // cc.ENode + + markAsRemoved(cc.ENode, [ + '_componentContainer', + '_camera', + '_additionalTransform', + '_scheduler', + '_actionManager', + 'actionManager', + '_isTransitionFinished', + '_additionalTransformDirty', + '_shaderProgram', + 'shaderProgram', + '_reorderChildDirty', + '_normalizedPositionDirty', + '_normalizedPosition', + '_usingNormalizedPosition', + '_renderCmd', + '_vertexZ', + '_showNode', + '_arrayMakeObjectsPerformSelector', + 'getActionManager', + 'setActionManager', + 'getScheduler', + 'setScheduler', + 'sortAllChildren', + 'reorderChild', + 'draw', + 'transformAncestors', + 'onEnter', + 'onEnterTransitionDidFinish', + 'onExitTransitionDidStart', + 'onExit', + 'runAction', + 'stopAllActions', + 'stopAction', + 'stopActionByTag', + 'getActionByTag', + 'getNumberOfRunningActions', + 'scheduleUpdate', + 'scheduleUpdateWithPriority', + 'unscheduleUpdate', + 'schedule', + 'scheduleOnce', + 'unschedule', + 'unscheduleAllCallbacks', + 'resumeSchedulerAndActions', + 'resume', + 'pauseSchedulerAndActions', + 'pause', + 'setAdditionalTransform', + 'updateTransform', + 'retain', + 'release', + 'visit', + 'transform', + 'getCamera', + 'grid', + 'getGrid', + 'setGrid', + 'getShaderProgram', + 'setShaderProgram', + 'getGLServerState', + 'setGLServerState', + '_initRendererCmd', + '_createRenderCmd', + 'updateDisplayedOpacity', + 'updateDisplayedColor', + 'userData', + 'userObject', + '_cascadeColorEnabled', + 'cascadeColor' + ]); + provideClearError(cc.ENode.prototype, { + arrivalOrder: 'getSiblingIndex, setSiblingIndex', + _visible: '_activeInHierarchy, active', + _running: '_activeInHierarchy, active', + running: 'activeInHierarchy, active', + _realOpacity: '_opacity, _opacity', + _realColor: '_color, _color', + getZOrder: 'getLocalZOrder', + setZOrder: 'setLocalZOrder', + getOrderOfArrival: 'getSiblingIndex', + setOrderOfArrival: 'setSiblingIndex', + boundingBox: 'getBoundingBox', + removeFromParentAndCleanup: 'removeFromParent', + removeAllChildrenWithCleanup: 'removeAllChildren', + parentToNodeTransform: 'getParentToNodeTransform', + nodeToWorldTransform: 'getNodeToWorlshaderProgramdTransform', + worldToNodeTransform: 'getWorldToNodeTransform', + nodeToParentTransform: 'getNodeToParentTransform', + getNodeToParentAffineTransform: 'getNodeToParentTransform', + }); + + // cc.SpriteRenderer + + markAsRemoved(cc.SpriteRenderer, [ + 'textureLoaded', + 'setBlendFunc', + 'getBlendFunc', + 'setState', + 'getState', + ]); + provideClearError(cc.SpriteRenderer, { + create: 'node.addComponent', + createWithTexture: 'node.addComponent', + createWithSpriteFrameName: 'node.addComponent', + createWithSpriteFrame: 'node.addComponent', + }); + provideClearError(cc.SpriteRenderer.prototype, { + ignoreAnchorPointForPosition: 'instance.ignoreAnchor', + }); + shouldNotUseNodeProp(cc.SpriteRenderer); + + // Particle + markAsRemoved(cc.EParticleSystem, [ + 'batchNode', + 'drawMode', + 'getDrawMode', + 'setDrawMode', + 'shapeType', + 'getShapeType', + 'setShapeType', + 'atlasIndex', + 'init', + 'initParticle', + 'updateWithNoTime', + ]); + provideClearError(cc.EParticleSystem, { + initWithFile: 'instance.file', + initWithDictionary: 'instance.file', + initWithTotalParticles: 'instance.totalParticles' + }); + provideClearError(cc.EParticleSystem.prototype, { + destroyParticleSystem: 'destroy', + clone: 'cc.instantiate', + isActive: 'active', + '*etParticleCount': 'particleCount', + '*etDuration': 'duration', + '*etSourcePosition': 'sourcePos', + '*etPosVar': 'posVar', + '*etGravity': 'gravity', + '*etSpeed': 'speed', + '*etSpeedVar': 'speedVar', + '*etTangentialAccel': 'tangentialAccel', + '*etTangentialAccelVar': 'tangentialAccelVar', + '*etRadialAccel': 'radialAccel', + '*etRadialAccelVar': 'radialAccelVar', + '*etRotationIsDir': 'rotationIsDir', + '*etStartRadius': 'startRadius', + '*etStartRadiusVar': 'startRadiusVar', + '*etEndRadius': 'endRadius', + '*etEndRadiusVar': 'endRadiusVar', + '*etRotatePerSecond': 'rotatePerS', + '*etRotatePerSecondVar': 'rotatePerSVar', + '*etStartColor': 'startColor', + '*etStartColorVar': 'startColorVar', + '*etEndColor': 'endColor', + '*etEndColorVar': 'endColorVar', + '*etTotalParticles': 'totalParticles', + '*etTexture': 'texture', + }); + shouldNotUseNodeProp(cc.EParticleSystem); + js.obsoletes(cc.EParticleSystem, 'cc.EParticleSystem', { + Type: 'PositionType', + Mode: 'EmitterMode' + }); + + // cc.Node + markAsRemoved(cc.Node, [ + '_normalizedPositionDirty', + '_normalizedPosition', + '_usingNormalizedPosition', + 'grid', + 'userData', + 'userObject', + 'getNormalizedPosition', + 'setNormalizedPosition', + 'getCamera', + 'getUserData', + 'setUserData', + 'getUserObject', + 'setUserObject', + 'getComponent', + 'addComponent', + 'removeComponent', + 'removeAllComponents', + 'enumerateChildren' + ], 'cc.Node'); + +} diff --git a/cocos2d/effects/CCGrabber.js b/cocos2d/effects/CCGrabber.js new file mode 100644 index 00000000000..bbc754ae67e --- /dev/null +++ b/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/cocos2d/effects/CCGrid.js b/cocos2d/effects/CCGrid.js new file mode 100644 index 00000000000..dc6a59182df --- /dev/null +++ b/cocos2d/effects/CCGrid.js @@ -0,0 +1,846 @@ +/**************************************************************************** + 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, + _shaderProgram: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._shaderProgram=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.Vec2} + */ + getStep:function () { + return cc.p(this._step.x, this._step.y); + }, + + /** + * set pixels between the grids + * @param {cc.Vec2} 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._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURE); + this.calculateVertexPoints(); + return true; + }, + + beforeDraw:function () { + // save projection + this._directorProjection = cc.director.getProjection(); + + //this.set2DProjection(); //TODO why? + this._grabber.beforeRender(this._texture); + }, + + afterDraw:function (target) { + this._grabber.afterRender(this._texture); + + // restore projection + //cc.director.setProjection(this._directorProjection); + + if (target && target.getCamera().isDirty()) { + var offset = target.getAnchorPointInPoints(); + + //TODO hack + var stackMatrix = target._renderCmd._stackMatrix; + // + // XXX: Camera should be applied in the AnchorPoint + // + //cc.kmGLTranslatef(offset.x, offset.y, 0); + var translation = cc.math.Matrix4.createByTranslation(offset.x, offset.y, 0); + stackMatrix.multiply(translation); + + //target.getCamera().locate(); + target._camera._locateForRenderer(stackMatrix); + + //cc.kmGLTranslatef(-offset.x, -offset.y, 0); + translation = cc.math.Matrix4.createByTranslation(-offset.x, -offset.y, 0, translation); + stackMatrix.multiply(translation); + } + + 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; + + 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.Vec2} pos + * @return {cc.Vertex3F} + */ + vertex:function (pos) { + return this.getVertex(pos); + }, + + /** + * returns the vertex at a given position + * @param {cc.Vec2} 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.Vec2} pos + * @return {cc.Vertex3F} + */ + originalVertex:function (pos) { + return this.getOriginalVertex(pos); + }, + + /** + * returns the original (non-transformed) vertex at a given position + * @param {cc.Vec2} 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.Vec2} 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; + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSITION | cc.VERTEX_ATTRIB_FLAG_TEX_COORDS); + this._shaderProgram.use(); + //this._shaderProgram.setUniformsForBuiltins(); + this._shaderProgram._setUniformForMVPMatrixWithMat4(target._renderCmd._stackMatrix); + + var gl = cc._renderContext, locDirty = this._dirty; + // + // 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.getPixelWidth(); + var height = this._texture.getPixelHeight(); + 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; + + 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.Vec2} pos + * @return {cc.Quad3} + */ + tile:function (pos) { + return this.getTile(pos); + }, + + /** + * returns the tile at the given position + * @param {cc.Vec2} 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.Vec2} 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.Vec2} pos + * @return {cc.Quad3} + */ + originalTile: function(pos) { + return this.getOriginalTile(pos); + }, + + /** + * sets a new tile + * @param {cc.Vec2} 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; + + this._shaderProgram.use(); + this._shaderProgram._setUniformForMVPMatrixWithMat4(target._renderCmd._stackMatrix); + //this._shaderProgram.setUniformsForBuiltins(); + + // + // Attributes + // + var gl = cc._renderContext, locDirty = this._dirty; + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POSITION | cc.VERTEX_ATTRIB_FLAG_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.getPixelWidth(); + var height = this._texture.getPixelHeight(); + 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/cocos2d/kazmath/SIMDPolyfill.js b/cocos2d/kazmath/SIMDPolyfill.js new file mode 100644 index 00000000000..7c65b0a2144 --- /dev/null +++ b/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/cocos2d/kazmath/aabb.js b/cocos2d/kazmath/aabb.js new file mode 100644 index 00000000000..8c5dc5b6643 --- /dev/null +++ b/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/cocos2d/kazmath/gl/mat4stack.js b/cocos2d/kazmath/gl/mat4stack.js new file mode 100644 index 00000000000..ecd3aceba34 --- /dev/null +++ b/cocos2d/kazmath/gl/mat4stack.js @@ -0,0 +1,98 @@ +/** + 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._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/cocos2d/kazmath/gl/matrix.js b/cocos2d/kazmath/gl/matrix.js new file mode 100644 index 00000000000..d1c56835877 --- /dev/null +++ b/cocos2d/kazmath/gl/matrix.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. + 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 shouldnt 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.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/cocos2d/kazmath/mat3.js b/cocos2d/kazmath/mat3.js new file mode 100644 index 00000000000..aa04fe3d1d8 --- /dev/null +++ b/cocos2d/kazmath/mat3.js @@ -0,0 +1,344 @@ +/** + 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){ + 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 proto = cc.math.Matrix3.prototype; + + proto.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; + }; + + proto.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; + }; + + proto.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 + + proto.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; + }; + + proto.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); + }; + + proto.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; + }; + + proto.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 substract 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; + }; + + proto.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; + }; + + proto.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; + }; + + proto.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; + }; + + proto.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; + }; + + proto.rotationToAxisAngle = function() { //cc.kmMat3RotationToAxisAngle + return cc.math.Quaternion.rotationMatrix(this).toAxisAndAngle(); + } +})(cc); + + + + + + + diff --git a/cocos2d/kazmath/mat4.js b/cocos2d/kazmath/mat4.js new file mode 100644 index 00000000000..d2115895314 --- /dev/null +++ b/cocos2d/kazmath/mat4.js @@ -0,0 +1,1014 @@ +/** + 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 | + *

+ * @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 + * @Params pOut - A pointer to the matrix to set to identity + * @Return 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, dum, 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) { + dum = 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) * dum); + + for (l = 0; l < m; l++) + b.set(ll, l, a.get(ll, l) - b.get(icol, l) * dum); + } + } + } + // 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/cocos2d/kazmath/mat4SIMD.js b/cocos2d/kazmath/mat4SIMD.js new file mode 100644 index 00000000000..f716de796b7 --- /dev/null +++ b/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/cocos2d/kazmath/plane.js b/cocos2d/kazmath/plane.js new file mode 100644 index 00000000000..94ab4e9b711 --- /dev/null +++ b/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 + Planed = −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/cocos2d/kazmath/quaternion.js b/cocos2d/kazmath/quaternion.js new file mode 100644 index 00000000000..fcbd652da9d --- /dev/null +++ b/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/cocos2d/kazmath/ray2.js b/cocos2d/kazmath/ray2.js new file mode 100644 index 00000000000..5a29794e885 --- /dev/null +++ b/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/cocos2d/kazmath/simd_benchmark/base.js b/cocos2d/kazmath/simd_benchmark/base.js new file mode 100644 index 00000000000..7bbb84533e9 --- /dev/null +++ b/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/cocos2d/kazmath/simd_benchmark/index.html b/cocos2d/kazmath/simd_benchmark/index.html new file mode 100644 index 00000000000..5b115d91c91 --- /dev/null +++ b/cocos2d/kazmath/simd_benchmark/index.html @@ -0,0 +1,44 @@ + + + + + Kazmath SIMD benchmarks + + + +

Running benchmarks...

+
+
+

+ +
+
+

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cocos2d/kazmath/simd_benchmark/kernel-template.js b/cocos2d/kazmath/simd_benchmark/kernel-template.js new file mode 100644 index 00000000000..d7c377585d5 --- /dev/null +++ b/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/cocos2d/kazmath/simd_benchmark/kmMat4AreEqual.js b/cocos2d/kazmath/simd_benchmark/kmMat4AreEqual.js new file mode 100644 index 00000000000..a3dee727ac3 --- /dev/null +++ b/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/cocos2d/kazmath/simd_benchmark/kmMat4Assign.js b/cocos2d/kazmath/simd_benchmark/kmMat4Assign.js new file mode 100644 index 00000000000..e8e386f781f --- /dev/null +++ b/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/cocos2d/kazmath/simd_benchmark/kmMat4Inverse.js b/cocos2d/kazmath/simd_benchmark/kmMat4Inverse.js new file mode 100644 index 00000000000..212dff1af1f --- /dev/null +++ b/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/cocos2d/kazmath/simd_benchmark/kmMat4IsIdentity.js b/cocos2d/kazmath/simd_benchmark/kmMat4IsIdentity.js new file mode 100644 index 00000000000..9ab17ac744a --- /dev/null +++ b/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/cocos2d/kazmath/simd_benchmark/kmMat4LookAt.js b/cocos2d/kazmath/simd_benchmark/kmMat4LookAt.js new file mode 100644 index 00000000000..1015ad14eb2 --- /dev/null +++ b/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/cocos2d/kazmath/simd_benchmark/kmMat4Multiply.js b/cocos2d/kazmath/simd_benchmark/kmMat4Multiply.js new file mode 100644 index 00000000000..325b6f95bf7 --- /dev/null +++ b/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/cocos2d/kazmath/simd_benchmark/kmMat4Transpose.js b/cocos2d/kazmath/simd_benchmark/kmMat4Transpose.js new file mode 100644 index 00000000000..cd1b776bf0b --- /dev/null +++ b/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/cocos2d/kazmath/simd_benchmark/kmVec3TransformCoord.js b/cocos2d/kazmath/simd_benchmark/kmVec3TransformCoord.js new file mode 100644 index 00000000000..541a926c9db --- /dev/null +++ b/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/cocos2d/kazmath/simd_benchmark/run.js b/cocos2d/kazmath/simd_benchmark/run.js new file mode 100644 index 00000000000..693b17f7040 --- /dev/null +++ b/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/cocos2d/kazmath/simd_benchmark/run_browser.js b/cocos2d/kazmath/simd_benchmark/run_browser.js new file mode 100644 index 00000000000..5d2d0cdd742 --- /dev/null +++ b/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/cocos2d/kazmath/utility.js b/cocos2d/kazmath/utility.js new file mode 100644 index 00000000000..9cd3fb4c714 --- /dev/null +++ b/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/cocos2d/kazmath/vec2.js b/cocos2d/kazmath/vec2.js new file mode 100644 index 00000000000..a682bc48531 --- /dev/null +++ b/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/cocos2d/kazmath/vec3.js b/cocos2d/kazmath/vec3.js new file mode 100644 index 00000000000..282b34aaae6 --- /dev/null +++ b/cocos2d/kazmath/vec3.js @@ -0,0 +1,201 @@ +/** + 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.kmVec3 = cc.math.Vec3 = 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 proto = cc.math.Vec3.prototype; + + proto.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; + }; + + proto.length = function () { //=cc.kmVec3Length + return Math.sqrt(cc.math.square(this.x) + cc.math.square(this.y) + cc.math.square(this.z)); + }; + + proto.lengthSq = function () { //=cc.kmVec3LengthSq + return cc.math.square(this.x) + cc.math.square(this.y) + cc.math.square(this.z) + }; + + proto.normalize = function () { //= cc.kmVec3Normalize + var l = 1.0 / this.length(); + this.x *= l; + this.y *= l; + this.z *= l; + return this; + }; + + proto.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; + }; + + proto.dot = function (vec) { //= cc.kmVec3Dot + return ( this.x * vec.x + this.y * vec.y + this.z * vec.z ); + }; + + proto.add = function(vec){ //= cc.kmVec3Add + this.x += vec.x; + this.y += vec.y; + this.z += vec.z; + return this; + }; + + proto.subtract = function (vec) { // = cc.kmVec3Subtract + this.x -= vec.x; + this.y -= vec.y; + this.z -= vec.z; + return this; + }; + + proto.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; + }; + + proto.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; + }; + + proto.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; + }; + + proto.scale = function(scale){ // = cc.kmVec3Scale + this.x *= scale; + this.y *= scale; + this.z *= scale; + return this; + }; + + proto.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)); + }; + + proto.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; + }; + + proto.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; + }; + + proto.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; + }; + + proto.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/cocos2d/kazmath/vec3SIMD.js b/cocos2d/kazmath/vec3SIMD.js new file mode 100644 index 00000000000..0bf4ff4b875 --- /dev/null +++ b/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/cocos2d/kazmath/vec4.js b/cocos2d/kazmath/vec4.js new file mode 100644 index 00000000000..fb2e376380f --- /dev/null +++ b/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/cocos2d/labels/CCLabel.js b/cocos2d/labels/CCLabel.js new file mode 100644 index 00000000000..340f262c69a --- /dev/null +++ b/cocos2d/labels/CCLabel.js @@ -0,0 +1,251 @@ +/*global cc */ + +/**************************************************************************** + Copyright (c) 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. + ****************************************************************************/ + +var EventTarget = require("../cocos2d/core/event/event-target"); + +cc.Label = cc.Node.extend({ + _hAlign: cc.TextAlignment.LEFT, + _vAlign: cc.VerticalTextAlignment.BOTTOM, + _string: "", + _fontSize: 20, + _overFlow: 0, //0 clamp, 1 shrink 2, resize to content + _isWrapText: true, + _spacingX: 0, + _spacingY: 0, + _blendFunc: null, + _labelSkinDirty: true, + _labelIsTTF: true, + _fontHandle: "", + + //fontHandle it is a font name or bmfont file. + ctor: function(fontHandle) { + this.setFontFileOrFamily(fontHandle); + cc.Node.prototype.ctor.call(this); + this.setContentSize(cc.size(128, 128)); + this._blendFunc = cc.BlendFunc._alphaNonPremultiplied(); + }, + + setHorizontalAlign: function(align) { + if (this._hAlign === align) return; + this._hAlign = align; + this._notifyLabelSkinDirty(); + }, + + getHorizontalAlign: function() { + return this._hAlign; + }, + + setVerticalAlign: function(align) { + if (this._vAlign === align) return; + this._vAlign = align; + this._notifyLabelSkinDirty(); + }, + + getVerticalAlign: function() { + return this._vAlign; + }, + + setString: function(string) { + if (this._string === string) return; + this._string = string; + this._notifyLabelSkinDirty(); + }, + + getString: function() { + return this._string; + }, + + enableWrapText: function(enabled) { + if (this._isWrapText === enabled) return; + this._isWrapText = enabled; + this._notifyLabelSkinDirty(); + }, + + isWrapTextEnabled: function() { + return this._isWrapText; + }, + + setFontSize: function(fntSize) { + if (this._fontSize === fntSize) return; + this._fontSize = fntSize; + this._notifyLabelSkinDirty(); + }, + + getFontSize: function() { + return this._fontSize; + }, + + setOverflow: function(overflow) { + if (this._overFlow === overflow) return; + this._overFlow = overflow; + this._notifyLabelSkinDirty(); + }, + + getOverflow: function() { + return this._overFlow; + }, + + setSpacingX: function(spacing) { + if (this._spacingX === spacing) return; + this._spacingX = spacing; + if(this._labelIsTTF === false) + this._notifyLabelSkinDirty(); + }, + + setLineHeight: function(spacing) { + if (this._spacingY === spacing) return; + this._spacingY = spacing; + this._notifyLabelSkinDirty(); + }, + + getSpacingX: function() { + return this._spacingX; + }, + + getLineHeight: function() { + return this._spacingY; + }, + + setFontFileOrFamily: function( fontHandle ) { + fontHandle = fontHandle || ""; + var extName = cc.path.extname(fontHandle); + + //specify font family name directly + if( extName === null) { + this._fontHandle = fontHandle; + this._labelIsTTF = true; + this._notifyLabelSkinDirty(); + return; + } + //add resource path + fontHandle = cc.path.join(cc.loader.resPath, fontHandle); + + if(extName === ".ttf") { + this._labelIsTTF = true; + this._fontHandle = this._loadTTFFont(fontHandle); + } + else { + //todo add bmfont here + this._fontHandle = fontHandle; + this._labelIsTTF = false; + this._notifyLabelSkinDirty(); + } + }, + + _loadTTFFont : function(fontHandle){ + var ttfIndex = fontHandle.lastIndexOf(".ttf"); + if(ttfIndex === -1) return fontHandle; + var slashPos = fontHandle.lastIndexOf("/"); + var fontFamilyName; + if(slashPos === -1) fontFamilyName = fontHandle.substring(0,ttfIndex ) + "_LABEL"; + else fontFamilyName = fontHandle.substring(slashPos + 1, ttfIndex) + "_LABEL"; + var self = this; + if(FontFace) { + var fontFace = new FontFace(fontFamilyName, "url('" + fontHandle + "')"); + fontFace.load().then( function (loadedFace) { + document.fonts.add(loadedFace); + self._notifyLabelSkinDirty(); + }); + } else { + //fall back implementations + var doc = document, fontStyle = document.createElement("style"); + fontStyle.type = "text/css"; + doc.body.appendChild(fontStyle); + + var fontStr = ""; + if(isNaN(fontFamilyName - 0)) + fontStr += "@font-face { font-family:" + fontFamilyName + "; src:"; + else + fontStr += "@font-face { font-family:'" + fontFamilyName + "'; src:"; + + fontStr += "url('" + fontHandle + "');"; + + 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); + self.scheduleOnce(self._notifyLabelSkinDirty,2); + } + + return fontFamilyName; + }, + + setContentSize: function(size, height) { + var oldWidth = this._contentSize.width; + var oldHeight = this._contentSize.height; + cc.Node.prototype.setContentSize.call(this, size,height); + if (oldWidth === this._contentSize.width && oldHeight === this._contentSize.height) { + return; + } + this._notifyLabelSkinDirty(); + }, + + 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; + } + }, + + + getBlendFunc: function() { + return new cc.BlendFunc(this._blendFunc.src, this._blendFunc.dst); + }, + + _notifyLabelSkinDirty: function() { + this._labelSkinDirty = true; + if(this._renderCmd) + this._renderCmd.setDirtyFlag(cc.Node._dirtyFlags.textDirty); + }, + _createRenderCmd: function() { + + if (this._labelIsTTF) { + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) + return new cc.Label.TTFWebGLRenderCmd(this); + else + return new cc.Label.TTFCanvasRenderCmd(this); + } else { + //todo:add label bmfont here + } + } +}); + +cc.Label.Overflow = cc.Enum({ + CLAMP: 0, + SHRINK: 1, + RESIZE: 2 +}); diff --git a/cocos2d/labels/CCLabelAtlas.js b/cocos2d/labels/CCLabelAtlas.js new file mode 100644 index 00000000000..0c45e96f55a --- /dev/null +++ b/cocos2d/labels/CCLabelAtlas.js @@ -0,0 +1,240 @@ +/**************************************************************************** + 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.AtlasNode + * + * @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.AtlasNode.extend(/** @lends cc.LabelAtlas# */{ + //property String is Getter and Setter + // string to render + _string: null, + // the first char in the charmap + _mapStartChar: null, + + _textureLoaded: false, + _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.AtlasNode.prototype.ctor.call(this); + + this._renderCmd.setCascade(); + 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.LabelAtlas.WebGLRenderCmd(this); + else + return new cc.LabelAtlas.CanvasRenderCmd(this); + }, + + /** + * Return texture is loaded. + * @returns {boolean} + */ + textureLoaded: function () { + return this._textureLoaded; + }, + + /** + * Add texture loaded event listener. + * @param {Function} callback + * @param {cc.Node} target + * @deprecated since 3.1, please use EventTarget API instead + */ + addLoadedEventListener: function (callback, target) { + this.once("load", callback, target); + }, + + /** + *

+ * 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; + 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 = null; + if (textureFilename instanceof cc.Texture2D) + texture = textureFilename; + else + texture = cc.textureCache.addImage(textureFilename); + var locLoaded = texture.isLoaded(); + this._textureLoaded = locLoaded; + if (!locLoaded) { + this._string = label; + texture.once("load", function (event) { + this.initWithTexture(texture, width, height, label.length); + this.string = this._string; + this.setColor(this._renderCmd._displayedColor); + this.emit("load"); + }, this); + } + if (this.initWithTexture(texture, width, height, label.length)) { + this._mapStartChar = startChar; + this.string = label; + return true; + } + return false; + }, + + /** + * Set the color. + * @param {cc.Color} color3 + */ + setColor: function (color3) { + cc.AtlasNode.prototype.setColor.call(this, color3); + this._renderCmd.updateAtlasValues(); + }, + + /** + * return the text of this label + * @return {String} + */ + getString: function () { + return this._string; + }, + + addChild: function(child, localZOrder, tag){ + this._renderCmd._addChild(child); + cc.Node.prototype.addChild.call(this, child, localZOrder, tag); + }, + + /** + * Atlas generation + * @function + */ + updateAtlasValues: function(){ + this._renderCmd.updateAtlasValues(); + }, + + /** + * set the display string + * @function + * @param {String} label + */ + setString: function(label){ + label = String(label); + var len = label.length; + this._string = label; + this.setContentSize(len * this._itemWidth, this._itemHeight); + this._renderCmd.setString(label); + + this._renderCmd.updateAtlasValues(); + this.quadsToDraw = len; + } +}); + +(function(){ + var proto = cc.LabelAtlas.prototype; + // Override properties + cc.defineGetterSetter(proto, "opacity", proto.getOpacity, proto.setOpacity); + cc.defineGetterSetter(proto, "color", proto.getColor, proto.setColor); + + // Extended properties + /** @expose */ + proto.string; + cc.defineGetterSetter(proto, "string", proto.getString, proto.setString); +})(); + +/** + *

+ * 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/cocos2d/labels/CCLabelAtlasCanvasRenderCmd.js b/cocos2d/labels/CCLabelAtlasCanvasRenderCmd.js new file mode 100644 index 00000000000..890c98be253 --- /dev/null +++ b/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/cocos2d/labels/CCLabelAtlasWebGLRenderCmd.js b/cocos2d/labels/CCLabelAtlasWebGLRenderCmd.js new file mode 100644 index 00000000000..87e9fa79f54 --- /dev/null +++ b/cocos2d/labels/CCLabelAtlasWebGLRenderCmd.js @@ -0,0 +1,153 @@ +/**************************************************************************** + 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.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.getPixelWidth(); + var textureHigh = texture.getPixelHeight(); + 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 locDisplayedColor = this._displayedColor; + var curColor = {r: locDisplayedColor.r, g: locDisplayedColor.g, b: locDisplayedColor.b, a: node._displayedOpacity}; + 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; + locQuadTL.colors = curColor; + locQuadTR.colors = curColor; + locQuadBL.colors = curColor; + locQuadBR.colors = curColor; + } + 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(){}; +})(); \ No newline at end of file diff --git a/cocos2d/labels/CCLabelBMFont.js b/cocos2d/labels/CCLabelBMFont.js new file mode 100644 index 00000000000..898a5bcdfac --- /dev/null +++ b/cocos2d/labels/CCLabelBMFont.js @@ -0,0 +1,971 @@ +/**************************************************************************** + 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) + ****************************************************************************/ + +EventTarget = require("../cocos2d/core/event/event-target"); + +/** + * @constant + * @type Number + */ +cc.LABEL_AUTOMATIC_WIDTH = -1; + +/** + *

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 {cc.TextAlignment} textAlign - Horizontal Alignment of label + * @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 {cc.TextAlignment} [alignment=cc.TextAlignment.LEFT] + * @param {cc.Vec2} [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.TextAlignment.LEFT); + * + * // Example 03 + * var label3 = new cc.LabelBMFont("This is a \n test case", "test.fnt", 200, cc.TextAlignment.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.TextAlignment.CENTER, + + // max width until a line break is added + _width: -1, + _lineBreakWithoutSpaces: false, + _imageOffset: null, + + _reusedChar: 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) { + 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 {cc.TextAlignment} [alignment=cc.TextAlignment.LEFT] + * @param {cc.Vec2} [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._reusedChar = []; + 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 EventTarget API instead + */ + addLoadedEventListener: function (callback, target) { + this.once("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 {cc.TextAlignment} [alignment=cc.TextAlignment.LEFT] + * @param {cc.Vec2} [imageOffset=cc.p(0,0)] + * @return {Boolean} + */ + initWithString: function (str, fntFile, width, alignment, imageOffset) { + var self = this, theString = str || ""; + var cmd = this._renderCmd; + + 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) { + cc.log("cc.LabelBMFont.initWithString(): Impossible to create font. Please check file"); + return false; + } + + self._config = newConf; + self._fntFile = fntFile; + texture = cc.textureCache.addImage(newConf.atlasName); + var locIsLoaded = texture.isLoaded(); + self._textureLoaded = locIsLoaded; + if (!locIsLoaded) { + texture.once("load", function (event) { + var self1 = this; + self1._textureLoaded = true; + //reset the LabelBMFont + self1.initWithTexture(texture, self1._initialString.length); + self1.setString(self1._initialString, true); + self1.emit("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.TextAlignment.LEFT; + self._imageOffset = imageOffset || cc.p(0, 0); + self._width = (width == null) ? -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); + + this._renderCmd._initBatchTexture(); + + self.setString(theString, true); + return true; + } + return false; + }, + + /** + * updates the font chars based on the string to render + */ + createFontChars: function () { + var self = this; + var cmd = this._renderCmd; + var locTexture = cmd._texture || self.textureAtlas.texture; + + var nextFontPositionX = 0; + + var tmpSize = cc.size(0, 0); + + var longestLine = 0; + + var quantityOfLines = 1; + + var locStr = self._string; + var stringLen = locStr ? locStr.length : 0; + + if (stringLen === 0) + return; + + 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; + 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; + var 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 fontChar = self.getChildByTag(i); + + if(!fontChar){ + fontChar = new cc.Sprite(); + fontChar.initWithTexture(locTexture, rect, false); + fontChar._newTextureWhenChangeColor = true; + this.addChild(fontChar, 0, i); + }else{ + this._renderCmd._updateCharTexture(fontChar, rect, key); + } + + // Apply label properties + fontChar.opacityModifyRGB = this._opacityModifyRGB; + this._renderCmd._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) { + for (var i = 0, li = locChildren.length; i < li; i++) { + var node = locChildren[i]; + if (node) node.visible = false; + } + } + if (self._config) + 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 == null) + needUpdateLabel = true; + if (newString == null || !cc.js.isString(newString)) + 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.TextAlignment.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.TextAlignment.CENTER: + shift = self.width / 2 - lineWidth / 2; + break; + case cc.TextAlignment.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; + self.texture = texture; + if (!locIsLoaded) { + texture.once("load", function (event) { + var self1 = this; + self1._textureLoaded = true; + self1.texture = texture; + self1.createFontChars(); + self1._changeTextureColor(); + self1.updateLabel(); + + self1.emit("load"); + }, self); + } else { + self.createFontChars(); + } + } + }, + + /** + * Return the fnt file path. + * @return {String} + */ + getFntFile: function () { + return this._fntFile; + }, + + setTexture: function(texture){ + this._renderCmd.setTexture(texture); + }, + + /** + * Set the AnchorPoint of the labelBMFont.
+ * In order to change the location of label. + * @override + * @param {cc.Vec2|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; + EventTarget.polyfill(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); +})(); + +/** + * 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 {cc.TextAlignment} [alignment=cc.TextAlignment.LEFT] + * @param {cc.Vec2} [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._fntLoader = { + 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; + }, + + /** + * Parse Fnt string. + * @param fntStr + * @param url + * @returns {{}} + */ + parseFnt: function (fntStr, url) { + var self = this, fnt = {}; + //padding + var infoObj = self._parseStrToObj(fntStr.match(self.INFO_EXP)[0]); + var paddingArr = infoObj["padding"].split(","); + var padding = { + left: parseInt(paddingArr[0]), + top: parseInt(paddingArr[1]), + right: parseInt(paddingArr[2]), + bottom: parseInt(paddingArr[3]) + }; + + //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"); + fnt.atlasName = cc.path.changeBasename(url, 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; + }, + + /** + * 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"], cc._fntLoader); diff --git a/cocos2d/labels/CCLabelBMFontCanvasRenderCmd.js b/cocos2d/labels/CCLabelBMFontCanvasRenderCmd.js new file mode 100644 index 00000000000..1d438429933 --- /dev/null +++ b/cocos2d/labels/CCLabelBMFontCanvasRenderCmd.js @@ -0,0 +1,107 @@ +/**************************************************************************** + 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){ + cc.SpriteBatchNode.CanvasRenderCmd.call(this, renderableObject); + this._needDraw = true; + }; + + var proto = cc.LabelBMFont.CanvasRenderCmd.prototype = Object.create(cc.SpriteBatchNode.CanvasRenderCmd.prototype); + proto.constructor = cc.LabelBMFont.CanvasRenderCmd; + + proto.rendering = function(){ + void 0; + }; + + 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 (this._texture !== cm._texture && (childDColor.r !== locDisplayedColor.r || + childDColor.g !== locDisplayedColor.g || childDColor.b !== locDisplayedColor.b)) + continue; + selChild.texture = texture; + } + this._texture = texture; + }; + + proto._changeTextureColor = function(){ + var node = this._node; + var texture = this._textureToRender, + 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; + this._textureToRender = oTexture._generateColorTexture(disColor.r, disColor.g, disColor.b, textureRect); + } + }; + + proto._updateChildrenDisplayedOpacity = function(locChild){ + cc.Node.prototype.updateDisplayedOpacity.call(locChild, this._displayedOpacity); + }; + + proto._updateChildrenDisplayedColor = function(locChild){ + cc.Node.prototype.updateDisplayedColor.call(locChild, this._displayedColor); + }; + + proto._initBatchTexture = function(){}; + +})(); \ No newline at end of file diff --git a/cocos2d/labels/CCLabelBMFontWebGLRenderCmd.js b/cocos2d/labels/CCLabelBMFontWebGLRenderCmd.js new file mode 100644 index 00000000000..69c126cf6da --- /dev/null +++ b/cocos2d/labels/CCLabelBMFontWebGLRenderCmd.js @@ -0,0 +1,84 @@ +/**************************************************************************** + 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){ + cc.SpriteBatchNode.WebGLRenderCmd.call(this, renderableObject); + this._needDraw = true; + }; + + var proto = cc.LabelBMFont.WebGLRenderCmd.prototype = Object.create(cc.SpriteBatchNode.WebGLRenderCmd.prototype); + proto.constructor = cc.LabelBMFont.WebGLRenderCmd; + + proto._updateCharTexture = function(fontChar, rect, key){ + // updating previous sprite + fontChar.setTextureRect(rect, false); + // restore to default in case they were modified + fontChar.visible = true; + }; + + proto._changeTextureColor = function(){}; + + proto._updateChildrenDisplayedOpacity = function(locChild){ + locChild.updateDisplayedOpacity(this._displayedOpacity); + }; + + proto._updateChildrenDisplayedColor = function(locChild){ + locChild.updateDisplayedColor(this._displayedColor); + }; + + proto._initBatchTexture = function(){ + var node = this._node; + var locTexture = node.textureAtlas.texture; + node._opacityModifyRGB = locTexture.hasPremultipliedAlpha(); + + var reusedChar = node._reusedChar = new cc.Sprite(); + reusedChar.initWithTexture(locTexture, cc.rect(0, 0, 0, 0), false); + reusedChar.batchNode = node; + }; + + proto.rendering = function(ctx){ + cc.SpriteBatchNode.WebGLRenderCmd.prototype.rendering.call(this, ctx); + + var node = this._node; + //LabelBMFont - Debug draw + if (cc.LABELBMFONT_DEBUG_DRAW) { + var size = node.getContentSize(); + var pos = cc.p(0 | ( -this._anchorPointInPoints.x), 0 | ( -this._anchorPointInPoints.y)); + var vertices = [cc.p(pos.x, pos.y), cc.p(pos.x + size.width, pos.y), cc.p(pos.x + size.width, pos.y + size.height), cc.p(pos.x, pos.y + size.height)]; + cc._drawingUtil.setDrawColor(0, 255, 0, 255); + cc._drawingUtil.drawPoly(vertices, 4, true); + } + }; + + proto._updateCharColorAndOpacity = function(){}; +})(); \ No newline at end of file diff --git a/cocos2d/labels/CCLabelCanvasRenderCmd.js b/cocos2d/labels/CCLabelCanvasRenderCmd.js new file mode 100644 index 00000000000..c692ba6475d --- /dev/null +++ b/cocos2d/labels/CCLabelCanvasRenderCmd.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. + + 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.Label.TTFLabelBaker = function() { + + }; + var proto = cc.Label.TTFLabelBaker.prototype = Object.create(Object.prototype); + proto._getLineHeight = function() { + //todo refine it + var nodeSpacingY = this._node._spacingY; + if(nodeSpacingY === 0) nodeSpacingY = this._drawFontsize; + else { nodeSpacingY = nodeSpacingY * this._drawFontsize / this._node._fontSize;} + return nodeSpacingY | 0; + }; + + proto._prepareQuad = function() { + var quad = this._quad; + var white = cc.color(255,255,255,this._displayedOpacity); + var width = this._node._contentSize.width; + var height = this._node._contentSize.height; + quad._bl.colors = white; + quad._br.colors = white; + quad._tl.colors = white; + quad._tr.colors = white; + + quad._bl.vertices = new cc.Vertex3F(0,0,0); + quad._br.vertices = new cc.Vertex3F(width,0,0); + quad._tl.vertices = new cc.Vertex3F(0,height,0); + quad._tr.vertices = new cc.Vertex3F(width,height,0); + + //texture coordinate should be y-flipped + quad._bl.texCoords = new cc.Tex2F(0,1); + quad._br.texCoords = new cc.Tex2F(1,1); + quad._tl.texCoords = new cc.Tex2F(0,0); + quad._tr.texCoords = new cc.Tex2F(1,0); + + this._quadDirty = true; + }; + + proto._fragmentText = function fragmentText(text, maxWidth, ctx) { + var words = text.split(' '), + lines = [], + line = ""; + if (ctx.measureText(text).width < maxWidth) { + return [text]; + } + while (words.length > 0) { + while (ctx.measureText(words[0]).width >= maxWidth && words[0].length > 1) { + var tmp = words[0]; + words[0] = tmp.slice(0, -1); + if (words.length > 1) { + words[1] = tmp.slice(-1) + words[1]; + } else { + words.push(tmp.slice(-1)); + } + } + if (ctx.measureText(line + words[0]).width < maxWidth) { + line += words.shift() + " "; + } else if(line.length === 0 && words[0].length === 1){ + lines.push(words.shift()); + } else { + lines.push(line.slice(0,-1)); + line = ""; + } + if (words.length === 0 && line.length > 0) { + lines.push(line); + } + } + return lines; + }; + proto._updateDisplayOpacity = function(parentOpacity) { + cc.Node.RenderCmd.prototype._updateDisplayOpacity.call(this, parentOpacity); + //specify opacity to quad + var color = cc.color(255,255,255,this._displayedOpacity); + var quad = this._quad; + quad._bl.colors = color; + quad._br.colors = color; + quad._tl.colors = color; + quad._tr.colors = color; + this._quadDirty = true; + }; + proto.updateStatus = function () { + cc.Node.RenderCmd.prototype.updateStatus.call(this); + + var textDirty = this._dirtyFlag & cc.Node._dirtyFlags.textDirty; + if(textDirty) { + this._dirtyFlag = this._dirtyFlag & cc.Node._dirtyFlags.textDirty ^ this._dirtyFlag; + } + }; + proto._bakeLabel = function() { + var node = this._node; + this._drawFontsize = node._fontSize; + var ctx = this._labelContext; + var canvasSizeX = node._contentSize.width; + var canvasSizeY = node._contentSize.height; + if(canvasSizeX <=0) canvasSizeX = 1; + if(canvasSizeY <=0) canvasSizeY = 1; + var paragraphedStrings = node._string.split("\n"); + var paragraphLength = []; + this._drawFontsize = node._fontSize; + var fontDesc = this._drawFontsize.toString() + "px "; + var fontFamily = node._fontHandle.length === 0? "serif" : node._fontHandle; + fontDesc = fontDesc + fontFamily; + this._labelContext.font = fontDesc; + for(var i = 0; i < paragraphedStrings.length; ++i) { + var textMetric = ctx.measureText(paragraphedStrings[i]); + paragraphLength.push(textMetric.width); + } + + if(cc.Label.Overflow.CLAMP == node._overFlow) { + if(node._isWrapText) { + this._splitedStrings = []; + for(var i = 0; i < paragraphedStrings.length; ++i) { + this._splitedStrings = this._splitedStrings.concat(this._fragmentText(paragraphedStrings[i], canvasSizeX, ctx)); + } + } + else { + this._splitedStrings = paragraphedStrings; + } + } + else if(cc.Label.Overflow.RESIZE == node._overFlow) { + //todo fix it + if(node._isWrapText) { + this._splitedStrings = []; + for(var i = 0; i < paragraphedStrings.length; ++i) { + this._splitedStrings = this._splitedStrings.concat(this._fragmentText(paragraphedStrings[i], canvasSizeX, ctx)); + } + canvasSizeY = this._splitedStrings.length * this._getLineHeight(); + node.setContentSize(cc.size(canvasSizeX,canvasSizeY)); + } + else { + this._splitedStrings = paragraphedStrings; + canvasSizeY = this._splitedStrings.length * this._getLineHeight(); + node.setContentSize(cc.size(canvasSizeX,canvasSizeY)); + } + } + else { + this._splitedStrings = paragraphedStrings; + //shrink + if(node._isWrapText) { + var totalLength = 0; + for(var i = 0; i < paragraphedStrings.length; ++i) { totalLength += ((paragraphLength[i]/canvasSizeX + 1) | 0) * canvasSizeX; } + var scale = canvasSizeX * ((canvasSizeY/this._getLineHeight())|0)/totalLength; + this._drawFontsize = (this._drawFontsize * Math.min(Math.sqrt(scale),1) ) | 0; + fontDesc = this._drawFontsize.toString() + "px " + fontFamily; + this._labelContext.font = fontDesc; + // + this._splitedStrings = []; + for(var i = 0; i < paragraphedStrings.length; ++i) { + this._splitedStrings = this._splitedStrings.concat(this._fragmentText(paragraphedStrings[i], canvasSizeX, ctx)); + } + } + else { + var maxLength = 0; + var totalHeight = paragraphedStrings.length * this._getLineHeight(); + for(var i = 0; i < paragraphedStrings.length; ++i) { + if(maxLength < paragraphLength[i]) maxLength = paragraphLength[i]; + } + var scaleX = canvasSizeX/maxLength; + var scaleY = canvasSizeY/totalHeight; + + this._drawFontsize = (this._drawFontsize * Math.min(1, scaleX, scaleY)) | 0; + fontDesc = this._drawFontsize.toString() + "px " + fontFamily; + this._splitedStrings = paragraphedStrings; + } + + } + + this._labelCanvas.width = canvasSizeX; + this._labelCanvas.height = canvasSizeY; + this._labelContext.clearRect(0,0,this._labelCanvas.width,this._labelCanvas.height); + //this._labelContext.fillStyle = "rgb(128,128,128)"; + //this._labelContext.fillRect(0,0,this._labelCanvas.width,this._labelCanvas.height); + var color = this._displayedColor; + this._labelContext.fillStyle = "rgb(" + color.r + "," + color.g + "," + + color.b + ")"; + + var lineHeight = this._getLineHeight(); + var lineCount = this._splitedStrings.length; + var labelX; var firstLinelabelY; + var hAlign; var vAlign; + //apply align + { + if(cc.TextAlignment.RIGHT === node._hAlign) { + hAlign = "right"; + labelX = canvasSizeX; + } + else if(cc.TextAlignment.CENTER === node._hAlign) { + hAlign = "center"; + labelX = canvasSizeX/2; + } + else { + hAlign = "left"; + labelX = 0; + } + + this._labelContext.textAlign = hAlign; + if(cc.VerticalTextAlignment.TOP === node._vAlign) { + vAlign = "top"; + firstLinelabelY = 0; + } + else if(cc.VerticalTextAlignment.CENTER === node._vAlign) { + vAlign = "middle"; + firstLinelabelY = canvasSizeY/2 - lineHeight * (lineCount -1 ) / 2; + } + else { + vAlign = "bottom"; + firstLinelabelY = canvasSizeY - lineHeight * (lineCount -1 ); + } + this._labelContext.textBaseline = vAlign; + } + + this._labelContext.font = fontDesc; + + //do real rendering + for(var i = 0; i < this._splitedStrings.length; ++i) { + this._labelContext.fillText(this._splitedStrings[i],labelX,firstLinelabelY + i * lineHeight); + } + + this._labelTexture._textureLoaded = false; + this._labelTexture.handleLoadedTexture(); + }; + + proto._rebuildLabelSkin = function() { + if(this._node._labelSkinDirty) { + this._bakeLabel(); + this._prepareQuad(); + this._node._labelSkinDirty = false; + } + }; +})(); + +(function(){ + cc.Label.TTFCanvasRenderCmd = function(renderableObject){ + cc.Node.CanvasRenderCmd.call(this, renderableObject); + this._needDraw = true; + this._labelTexture = new cc.Texture2D(); + this._labelCanvas = document.createElement("canvas"); + this._labelCanvas.width = 1; + this._labelCanvas.height = 1; + this._labelContext = this._labelCanvas.getContext("2d"); + this._labelTexture.initWithElement(this._labelCanvas); + this._quad = new cc.V3F_C4B_T2F_Quad(); + this._quadDirty = true; + this._splitedStrings = null; + this._drawFontsize = 0; + }; + + var proto = cc.Label.TTFCanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + cc.js.mixin(proto, cc.Label.TTFLabelBaker.prototype); + + proto.constructor = cc.Label.TTFCanvasRenderCmd; + + proto.rendering = function (ctx, scaleX, scaleY) { + this._rebuildLabelSkin(); + var node = this._node; + var locDisplayOpacity = this._displayedOpacity; + var alpha = locDisplayOpacity/ 255; + //var locTexture = this._labelTexture; + if (locDisplayOpacity === 0) + return; + + 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._labelTexture) { + + var quad = this._quad; + { + var sx,sy,sw,sh; + var x, y, w,h; + + x = quad._bl.vertices.x; + y = quad._bl.vertices.y; + w = quad._tr.vertices.x - quad._bl.vertices.x; + h = quad._tr.vertices.y - quad._bl.vertices.y; + y = - y - h; + + var textureWidth = this._labelTexture.getPixelWidth(); + var textureHeight = this._labelTexture.getPixelHeight(); + + sx = quad._bl.texCoords.u * textureWidth; + sy = quad._bl.texCoords.v * textureHeight; + sw = (quad._tr.texCoords.u - quad._bl.texCoords.u) * textureWidth; + sh = (quad._tr.texCoords.v - quad._bl.texCoords.v) * textureHeight; + + x = x * scaleX; + y = y * scaleY; + w = w * scaleX; + h = h * scaleY; + + var image = this._labelTexture._htmlElementObj; + if (this._labelTexture._pattern !== "") { + wrapper.setFillStyle(context.createPattern(image, this._labelTexture._pattern)); + context.fillRect(x, y, w, h); + } else { + context.drawImage(image, + sx, sy, sw, sh, + x, y, w, h); + } + } + + } + cc.g_NumberOfDraws = cc.g_NumberOfDraws + 1; + }; + +})(); \ No newline at end of file diff --git a/cocos2d/labels/CCLabelWebGLRenderCmd.js b/cocos2d/labels/CCLabelWebGLRenderCmd.js new file mode 100644 index 00000000000..ad4726afd0a --- /dev/null +++ b/cocos2d/labels/CCLabelWebGLRenderCmd.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. + + 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.Label.TTFWebGLRenderCmd = function(renderableObject){ + cc.Node.WebGLRenderCmd.call(this, renderableObject); + this._needDraw = true; + this._labelTexture = new cc.Texture2D(); + this._labelCanvas = document.createElement("canvas"); + this._labelCanvas.width = 1; + this._labelCanvas.height = 1; + this._labelContext = this._labelCanvas.getContext("2d"); + this._labelTexture.initWithElement(this._labelCanvas); + this._quad = new cc.V3F_C4B_T2F_Quad(); + this._quadDirty = true; + this._quadWebBuffer = cc._renderContext.createBuffer(); + this._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR); + this._splitedStrings = null; + this._drawFontsize = 0; + }; + + var proto = cc.Label.TTFWebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + cc.js.mixin(proto, cc.Label.TTFLabelBaker.prototype); + + proto.constructor = cc.Label.TTFWebGLRenderCmd; + + proto.rendering = function (ctx) { + var node = this._node; + this._rebuildLabelSkin(); + + var gl = ctx || cc._renderContext ; + this._shaderProgram.use(); + this._shaderProgram._setUniformForMVPMatrixWithMat4(this._stackMatrix); + cc.glBlendFunc(node._blendFunc.src,node._blendFunc.dst); + cc.glBindTexture2DN(0,this._labelTexture); + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX); + + gl.bindBuffer(gl.ARRAY_BUFFER, this._quadWebBuffer); + if (this._quadDirty) { + gl.bufferData(gl.ARRAY_BUFFER, this._quad.arrayBuffer, gl.DYNAMIC_DRAW); + this._quadDirty = false; + } + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 24, 0); //cc.VERTEX_ATTRIB_POSITION + gl.vertexAttribPointer(1, 4, gl.UNSIGNED_BYTE, true, 24, 12); //cc.VERTEX_ATTRIB_COLOR + gl.vertexAttribPointer(2, 2, gl.FLOAT, false, 24, 16); //cc.VERTEX_ATTRIB_TEX_COORDS + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + }; + +})(); \ No newline at end of file diff --git a/cocos2d/menus/CCMenu.js b/cocos2d/menus/CCMenu.js new file mode 100644 index 00000000000..9a92b8cc433 --- /dev/null +++ b/cocos2d/menus/CCMenu.js @@ -0,0 +1,600 @@ +/**************************************************************************** + 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 + }); + + if ((arguments.length > 0) && (arguments[arguments.length - 1] == null)) + cc.log("parameters should not be ending with null in Javascript"); + + var argc = arguments.length, items; + if (argc === 0) { + items = []; + } else if (argc === 1) { + if (menuItems instanceof Array) { + items = menuItems; + } + else items = [menuItems]; + } + else if (argc > 1) { + 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); + }, + + /** + * 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) { + 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) { + 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 rows = []; + for (var 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 columns = [], i; + 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/cocos2d/menus/CCMenuItem.js b/cocos2d/menus/CCMenuItem.js new file mode 100644 index 00000000000..3fbe6559c45 --- /dev/null +++ b/cocos2d/menus/CCMenuItem.js @@ -0,0 +1,1349 @@ +/**************************************************************************** + 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.js.isString(locCallback)) { + locTarget[locCallback](this); + } else if (locTarget && cc.js.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); + + 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|_p.string|ret.string|q.string|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; + + if (selectedSprite !== undefined) { + //normalSprite = normalSprite; + //selectedSprite = selectedSprite; + 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.js.isFunction(four)) { + disabledImage = three; + callback = four; + } else if (four !== undefined && cc.js.isFunction(three)) { + target = four; + callback = three; + disabledImage = null; + } else if (three === undefined) { + disabledImage = null; + } + this.initWithNormalSprite(normalSprite, selectedSprite, disabledImage, callback, target); + } + }, + + /** + * 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.once("load", function (event) { + this.width = normalImage.width; + this.height = normalImage.height; + }, 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.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; + + if (locNormalImage.textureLoaded && !locNormalImage.textureLoaded()) { + locNormalImage.once("load", function (event) { + this.width = locNormalImage.width; + this.height = locNormalImage.height; + this.setCascadeColorEnabled(true); + this.setCascadeOpacityEnabled(true); + }, this); + } + } + 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} args[0...last-2] the rest in the array are cc.MenuItems + * @param {function|String} args[last-1] the second item in the args array is the callback + * @param {cc.Node} args[last] the first item in the args array is a target + * @return {Boolean} + */ + initWithItems: function (args) { + var l = args.length; + // passing callback. + if (cc.js.isFunction(args[args.length - 2])) { + this.initWithCallback(args[args.length - 2], args[args.length - 1]); + l = l - 2; + } else if (cc.js.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} + * @example + */ +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/cocos2d/motion-streak/CCMotionStreak.js b/cocos2d/motion-streak/CCMotionStreak.js new file mode 100644 index 00000000000..78fae3e0a46 --- /dev/null +++ b/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.js.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.Vec2|Number} position + * @param {Number} [yValue=undefined] If not exists, the first parameter must be cc.Vec2. + */ + 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 newIdx, newIdx2, i, 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/cocos2d/motion-streak/CCMotionStreakWebGLRenderCmd.js b/cocos2d/motion-streak/CCMotionStreakWebGLRenderCmd.js new file mode 100644 index 00000000000..45ab07cafd2 --- /dev/null +++ b/cocos2d/motion-streak/CCMotionStreakWebGLRenderCmd.js @@ -0,0 +1,66 @@ +/**************************************************************************** + 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){ + cc.Node.WebGLRenderCmd.call(this, renderableObject); + this._needDraw = true; + 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; + this._shaderProgram.use(); + this._shaderProgram._setUniformForMVPMatrixWithMat4(this._stackMatrix); + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX); + cc.glBlendFunc(node._blendFunc.src, node._blendFunc.dst); + + cc.glBindTexture2D(node.texture); + + //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++; + } +}; \ No newline at end of file diff --git a/cocos2d/node-grid/CCNodeGrid.js b/cocos2d/node-grid/CCNodeGrid.js new file mode 100644 index 00000000000..f1cb9b46581 --- /dev/null +++ b/cocos2d/node-grid/CCNodeGrid.js @@ -0,0 +1,150 @@ +/**************************************************************************** + 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({ + 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; + }, + + _transformForWebGL: function () { + //optimize performance for javascript + var t4x4 = this._transform4x4, topMat4 = cc.current_stack.top; + + // Convert 3x3 into 4x4 matrix + var trans = this.getNodeToParentTransform(); + var t4x4Mat = t4x4.mat; + t4x4Mat[0] = trans.a; + t4x4Mat[4] = trans.c; + t4x4Mat[12] = trans.tx; + t4x4Mat[1] = trans.b; + t4x4Mat[5] = trans.d; + t4x4Mat[13] = trans.ty; + + // Update Z vertex manually + //this._transform4x4.mat[14] = this._vertexZ; + t4x4Mat[14] = this._vertexZ; + + //optimize performance for Javascript + topMat4.multiply(t4x4) ; // = cc.kmGLMultMatrix(this._transform4x4); + + // XXX: Expensive calls. Camera should be integrated into the cached affine matrix + if (this._camera !== null && !(this.grid && this.grid.isActive())) { + var app = this._renderCmd._anchorPointInPoints, + apx = app.x, apy = app.y, + translate = (apx !== 0.0 || apy !== 0.0); + if (translate) { + if(!cc.SPRITEBATCHNODE_RENDER_SUBPIXEL) { + apx = 0 | apx; + apy = 0 | apy; + } + cc.kmGLTranslatef(apx, apy, 0); + this._camera.locate(); + cc.kmGLTranslatef(-apx, -apy, 0); + } else { + this._camera.locate(); + } + } + }, + + _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(); +}; \ No newline at end of file diff --git a/cocos2d/node-grid/CCNodeGridWebGLRenderCmd.js b/cocos2d/node-grid/CCNodeGridWebGLRenderCmd.js new file mode 100644 index 00000000000..8c45377dfe9 --- /dev/null +++ b/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){ + cc.Node.WebGLRenderCmd.call(this, 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 (node._parent && node._parent._renderCmd) + this._curLevel = node._parent._renderCmd._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/cocos2d/parallax/CCParallaxNode.js b/cocos2d/parallax/CCParallaxNode.js new file mode 100644 index 00000000000..afa8e214e00 --- /dev/null +++ b/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.Vec2} Not point, this is ratio. + */ + getRatio:function () { + return this._ratio; + }, + + /** + * Set the ratio. + * @param {cc.Vec2} value + */ + setRatio:function (value) { + this._ratio = value; + }, + + /** + * Gets the offset. + * @return {cc.Vec2} + */ + getOffset:function () { + return this._offset; + }, + + /** + * Set the offset. + * @param {cc.Vec2} 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.Vec2} ratio Not point, this is a ratio. + * @param {cc.Vec2} 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.Vec2} ratio + * @param {cc.Vec2} 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.Vec2} ratio + * @param {cc.Vec2} 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 + */ + 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/cocos2d/parallax/CCParallaxNodeRenderCmd.js b/cocos2d/parallax/CCParallaxNodeRenderCmd.js new file mode 100644 index 00000000000..985e7d0d319 --- /dev/null +++ b/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){ + cc.Node.CanvasRenderCmd.call(this, 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(); + cc.Node.CanvasRenderCmd.prototype.updateStatus.call(this); + }; + + proto._syncStatus = function(parentCmd){ + this._node._updateParallaxPosition(); + cc.Node.CanvasRenderCmd.prototype._syncStatus.call(this, parentCmd); + } +})(); + +cc.game.once(cc.game.EVENT_RENDERER_INITED, function () { + if(cc._renderType !== cc.game.RENDER_TYPE_WEBGL) + return; + + cc.ParallaxNode.WebGLRenderCmd = function(renderable){ + cc.Node.WebGLRenderCmd.call(this, 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(); + cc.Node.WebGLRenderCmd.prototype.updateStatus.call(this); + }; + + proto._syncStatus = function(parentCmd){ + this._node._updateParallaxPosition(); + cc.Node.WebGLRenderCmd.prototype._syncStatus.call(this, parentCmd); + } +}); + diff --git a/cocos2d/particle/CCEParticleSystem.js b/cocos2d/particle/CCEParticleSystem.js new file mode 100644 index 00000000000..04abe4bc77a --- /dev/null +++ b/cocos2d/particle/CCEParticleSystem.js @@ -0,0 +1,830 @@ +/**************************************************************************** + Copyright (c) 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. + ****************************************************************************/ + +/** + * Enum for emitter modes + * @enum EmitterMode + * @namespace EParticleSystem + */ +var EmitterMode = cc.Enum({ + /** + * @property {Number} GRAVITY - Uses gravity, speed, radial and tangential acceleration. + */ + GRAVITY: 0, + /** + * @property {Number} RADIUS - Uses radius movement + rotation. + */ + RADIUS: 1 +}); + +/** + * Enum for particles movement type + * @enum PositionType + * @namespace EParticleSystem + */ +var PositionType = cc.Enum({ + /** + * Living particles are attached to the world and are unaffected by emitter repositioning. + * @property {Number} FREE + */ + 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. + * @property {Number} RELATIVE + */ + RELATIVE: 1, + + /** + * Living particles are attached to the emitter and are translated along with it. + * @property {Number} GROUPED + */ + GROUPED: 2 +}); + +var properties = { + + /** + * Play particle in edit mode. + * @property {Boolean} preview + * @default false + */ + preview: { + default: true, + editorOnly: true, + notify: CC_EDITOR && function () { + if (this._sgNode) { + this._sgNode.resetSystem(); + if ( !this.preview ) { + this._sgNode.stopSystem(); + } + cc.engine.repaintInEditMode(); + } + } + }, + + /** + * If set custom to true, then use custom properties insteadof read particle file. + * @property {Boolean} custom + * @default false + */ + _custom: false, + custom: { + get: function () { + return this._custom; + }, + set: function (value) { + if (CC_EDITOR && !value && !this._file) { + return cc.warn('Custom should not be false if file is not specified.'); + } + if (this._custom !== value) { + this._custom = value; + if (this._sgNode) { + if (value) { + this._applyCustoms(); + } + else { + this._applyFile(); + } + cc.engine.repaintInEditMode(); + //if (CC_EDITOR) { + // self.preview = self.preview; + //} + } + } + }, + }, + + /** + * The plist file. + * @property {string} file + * @default "" + */ + _file: { + default: '', + url: cc.ParticleAsset + }, + file: { + get: function () { + return this._file; + }, + set: function (value, force) { + if (this._file !== value || (CC_EDITOR && force)) { + this._file = value; + if (this._sgNode && value) { + this._applyFile(); + cc.engine.repaintInEditMode(); + //if (CC_EDITOR) { + // self.preview = self.preview; + //} + } + else { + this.custom = true; + } + } + }, + url: cc.ParticleAsset + }, + + /** + * @property {Texture2D} texture - Texture of Particle System. + */ + _texture: { + default: '', + url: cc.Texture2D + }, + texture: { + get: function () { + return this._texture; + }, + set: function (value) { + this._texture = value; + + if (this._sgNode) { + this._sgNode.texture = value ? cc.textureCache.addImage( value ) : null; + } + }, + url: cc.Texture2D + }, + + /** + * @property {Number} particleCount - Current quantity of particles that are being simulated. + */ + particleCount: { + get: function () { + return this._sgNode ? this._sgNode.particleCount : 0; + }, + set: function (value) { + if (this._sgNode) { + this._sgNode.particleCount = value; + } + }, + visible: false + }, + + /** + * !#en If set to true, the particle system will automatically start playing on onLoad. + * !#zh 如果设置为true è¿è¡Œæ—¶ä¼šè‡ªåŠ¨å‘å°„ç²’å­ + * @property playOnLoad + * @type {boolean} + * @default true + */ + playOnLoad: true, + + /** + * @property {Boolean} autoRemoveOnFinish - Indicate whether the owner node will be auto-removed when it has no particles left. + */ + _autoRemoveOnFinish: false, + autoRemoveOnFinish: { + get: function () { + return this._autoRemoveOnFinish; + }, + set: function (value) { + if (this._autoRemoveOnFinish !== value) { + this._autoRemoveOnFinish = value; + if (CC_EDITOR && !cc.engine.isPlaying) { + return; + } + this._applyAutoRemove(); + } + }, + }, + + /** + * Indicate whether the particle system is activated. + * @property {Boolean} active + * @readonly + */ + active: { + get: function () { + return this._sgNode ? this._sgNode.isActive() : false; + }, + visible: false + } +}; + +/** + * @class EParticleSystem + */ +var CustomProps = (function () { + var DefaultValues = { + /** + * @property {Number} totalParticles - Maximum particles of the system. + * @default 150 + */ + totalParticles: 150, + /** + * @property {Number} duration - How many seconds the emitter wil run. -1 means 'forever' + * @default EParticleSystem.DURATION_INFINITY + */ + duration: -1, + /** + * @property {Number} emissionRate - Emission rate of the particles. + * @default 10 + */ + emissionRate: 10, + /** + * @property {Number} life - Life of each particle setter. + * @default 1 + */ + life: 1, + /** + * @property {Number} lifeVar - Variation of life. + * @default 0 + */ + lifeVar: 0, + + /** + * @property {Color} startColor - Start color of each particle. + * @default cc.Color.WHITE + */ + startColor: cc.Color.WHITE, + /** + * @property {Color} startColorVar - Variation of the start color. + * @default cc.Color.BLACK + */ + startColorVar: cc.Color.BLACK, + /** + * @property {Color} endColor - Ending color of each particle. + * @default new cc.Color(255, 255, 255, 0) + */ + endColor: cc.color(255, 255, 255, 0), + /** + * @property {Color} endColorVar - Variation of the end color. + * @default Color.TRANSPARENT + */ + endColorVar: cc.color(0, 0, 0, 0), + + /** + * @property {Number} angle - Angle of each particle setter. + * @default 90 + */ + angle: 90, + /** + * @property {Number} angleVar - Variation of angle of each particle setter. + * @default 20 + */ + angleVar: 20, + /** + * @property {Number} startSize - Start size in pixels of each particle. + * @default 50 + */ + startSize: 50, + /** + * @property {Number} startSizeVar - Variation of start size in pixels. + * @default 0 + */ + startSizeVar: 0, + /** + * @property {Number} endSize - End size in pixels of each particle. + * @default 0 + */ + endSize: 0, + /** + * @property {Number} endSizeVar - Variation of end size in pixels. + * @default 0 + */ + endSizeVar: 0, + /** + * @property {Number} startSpin - Start angle of each particle. + * @default 0 + */ + startSpin: 0, + /** + * @property {Number} startSpinVar - Variation of start angle. + * @default 0 + */ + startSpinVar: 0, + /** + * @property {Number} endSpin - End angle of each particle. + * @default 0 + */ + endSpin: 0, + /** + * @property {Number} endSpinVar - Variation of end angle. + * @default 0 + */ + endSpinVar: 0, + + /** + * @property {Vec2} sourcePos - Source position of the emitter. + * @default cc.Vec2.ZERO + */ + sourcePos: cc.p(0, 0), + + /** + * @property {Vec2} posVar - Variation of source position. + * @default cc.Vec2.ZERO + */ + posVar: cc.p(0, 0), + + /** + * Particles movement type. + * @property {EParticleSystem.PositionType} positionType + * @default EParticleSystem.PositionType.FREE + */ + positionType: PositionType.FREE, + /** + * Particles emitter modes. + * @property {EParticleSystem.EmitterMode} emitterMode + * @default EParticleSystem.EmitterMode.GRAVITY + */ + emitterMode: EmitterMode.GRAVITY, + + // GRAVITY MODE + + /** + * @property {Vec2} gravity - Gravity of the emitter. + * @default cc.Vec2.ZERO + */ + gravity: cc.p(0, 0), + /** + * @property {Number} speed - Speed of the emitter. + * @default 180 + */ + speed: 180, + /** + * @property {Number} speedVar - Variation of the speed. + * @default 50 + */ + speedVar: 50, + /** + * @property {Number} tangentialAccel - Tangential acceleration of each particle. Only available in 'Gravity' mode. + * @default 80 + */ + tangentialAccel: 80, + /** + * @property {Number} tangentialAccelVar - Variation of the tangential acceleration. + * @default 0 + */ + tangentialAccelVar: 0, + /** + * @property {Number} radialAccel - Radial acceleration of each particle. Only available in 'Gravity' mode. + * @default 0 + */ + radialAccel: 0, + /** + * @property {Number} radialAccelVar - Variation of the radial acceleration. + * @default 0 + */ + radialAccelVar: 0, + + /** + * @property {Boolean} rotationIsDir - Indicate whether the rotation of each particle equals to its direction. Only available in 'Gravity' mode. + * @default false + */ + rotationIsDir: false, + + // RADIUS MODE + + /** + * @property {Number} startRadius - Starting radius of the particles. Only available in 'Radius' mode. + * @default 0 + */ + startRadius: 0, + /** + * @property {Number} startRadiusVar - Variation of the starting radius. + * @default 0 + */ + startRadiusVar: 0, + /** + * @property {Number} endRadius - Ending radius of the particles. Only available in 'Radius' mode. + * @default 0 + */ + endRadius: 0, + /** + * @property {Number} endRadiusVar - Variation of the ending radius. + * @default 0 + */ + endRadiusVar: 0, + /** + * @property {Number} rotatePerS - Number of degress to rotate a particle around the source pos per second. Only available in 'Radius' mode. + * @default 0 + */ + rotatePerS: 0, + /** + * @property {Number} rotatePerSVar - Variation of the degress to rotate a particle around the source pos per second. + * @default 0 + */ + rotatePerSVar: 0 + }; + var props = Object.keys(DefaultValues); + + for (var i = 0; i < props.length; ++i) { + var prop = props[i]; + (function (prop, defaultValue) { + var internalProp = '_' + prop; + properties[internalProp] = defaultValue; + + var type = defaultValue.constructor; + var propDef = properties[prop] = {}; + + if (cc.isChildClassOf(type, cc.ValueType)) { + propDef.get = function () { + return new type(this[internalProp]); + }; + propDef.type = type; + } + else { + propDef.get = function () { + return this[internalProp]; + }; + } + + if (cc.isChildClassOf(type, cc.ValueType)) { + propDef.set = function (value) { + this[internalProp] = new type(value); + if (this._sgNode) { + this._sgNode[prop] = value; + } + }; + } + else if (CC_EDITOR && typeof defaultValue === "number") { + propDef.set = function (value) { + this[internalProp] = value; + if (!isNaN(value)) { + if (this._sgNode) { + this._sgNode[prop] = value; + } + } + else { + cc.error('The new %s must not be NaN', prop); + } + }; + } + else { + propDef.set = function (value) { + this[internalProp] = value; + if (this._sgNode) { + this._sgNode[prop] = value; + } + }; + } + })(prop, DefaultValues[prop]); + } + return props; +})(); + +properties.positionType.type = PositionType; +properties.emitterMode.type = EmitterMode; + +/** + *

+ * Particle System base class.
+ * Attributes of a Particle System:
+ * - emmision 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 EParticleSystem + * @extends cc._ComponentInSG + */ +var ParticleSystem = cc.Class({ + name: 'cc.ParticleSystem', + extends: cc._ComponentInSG, + editor: CC_EDITOR && { + menu: 'ParticleSystem', + inspector: 'app://editor/page/inspector/particle-system/index.html', + playOnFocus: true, + }, + + ctor: function () { + this._previewTimer = null; + this._focused = false; + this._willStart = false; + }, + + properties: properties, + + statics: { + + /** + * The Particle emitter lives forever + * @property {Number} DURATION_INFINITY + * @default -1 + * @static + * @readonly + */ + DURATION_INFINITY: -1, + + /** + * The starting size of the particle is equal to the ending size + * @property {Number} START_SIZE_EQUAL_TO_END_SIZE + * @default -1 + * @static + * @readonly + */ + START_SIZE_EQUAL_TO_END_SIZE: -1, + + /** + * The starting radius of the particle is equal to the ending radius + * @property {Number} START_RADIUS_EQUAL_TO_END_RADIUS + * @default -1 + * @static + * @readonly + */ + START_RADIUS_EQUAL_TO_END_RADIUS: -1, + + EmitterMode: EmitterMode, + PositionType: PositionType, + }, + + // LIFE-CYCLE METHODS + onLoad: function () { + this._super(); + // auto play + if (CC_EDITOR && !cc.engine.isPlaying) { + if (this.preview) { + this.resetSystem(); + } + } + else { + if (this.playOnLoad) { + this.resetSystem(); + } + this._applyAutoRemove(); + } + }, + //onEnable: function () { + // this._super(); + //}, + //onDisable: function () { + // this._super(); + //}, + onDestroy: function () { + if (this._autoRemoveOnFinish) { + this.autoRemoveOnFinish = false; // already removed + } + this._super(); + }, + + onFocusInEditor: CC_EDITOR && function () { + this._focused = true; + if (this.preview && this._sgNode) { + this._sgNode.resetSystem(); + + var self = this; + this._previewTimer = setInterval(function () { + // attemptToReplay + if (self.particleCount === 0 && !self._willStart) { + self._willStart = true; + setTimeout(function () { + self._willStart = false; + if (self.preview && self._focused && !self.active && !cc.engine.isPlaying) { + self.resetSystem(); + } + }, 600); + } + }, 100); + } + }, + + onLostFocusInEditor: CC_EDITOR && function () { + this._focused = false; + if (this.preview && this._sgNode) { + this._sgNode.resetSystem(); + this._sgNode.stopSystem(); + this._sgNode.update(); + cc.engine.repaintInEditMode(); + } + if (this._previewTimer) { + clearInterval(this._previewTimer); + } + }, + + // OVERRIDE METHODS + + _createSgNode: function () { + var sgNode = this._sgNode = new cc.ParticleSystem(); + + var loadCustomAfterFile = false; + if (this._file) { + var missCustomTexture = this._custom && !this._texture; + loadCustomAfterFile = missCustomTexture; + this._applyFile(loadCustomAfterFile && this._applyCustoms.bind(this)); + } + if (this._custom && !loadCustomAfterFile) { + this._applyCustoms(); + } + + // stop by default + sgNode.stopSystem(); + return sgNode; + }, + + // APIS + + /** + * Add a particle to the emitter + * @method addParticle + * @return {Boolean} + */ + addParticle: function () { + if (this._sgNode) { + return this._sgNode.addParticle(); + } + else { + return false; + } + }, + + /** + * stop emitting particles. Running particles will continue to run until they die + * @method stopSystem + */ + stopSystem: function () { + if (this._sgNode) { + this._sgNode.stopSystem(); + } + }, + + /** + * Kill all living particles. + * @method resetSystem + */ + resetSystem: function () { + if (this._sgNode) { + this._sgNode.resetSystem(); + } + }, + + /** + * whether or not the system is full + * @method isFull + * @return {Boolean} + */ + isFull: function () { + return (this.particleCount >= this._totalParticles); + }, + + /** + *

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

+ * @method setDisplayFrame + * @param {SpriteFrame} spriteFrame + */ + setDisplayFrame: function (spriteFrame) { + if (!spriteFrame) + return; + + var texture = spriteFrame.getTexture(); + if (texture) { + this._texture = texture.url; + } + if (this._sgNode) { + this._sgNode.setDisplayFrame(spriteFrame); + } + }, + + /** + * Sets a new texture with a rect. The rect is in Points. + * @method setTextureWithRect + * @param {Texture2D} texture + * @param {Rect} rect + */ + setTextureWithRect: function (texture, rect) { + if (texture instanceof cc.Texture2D) { + this._texture = texture.url; + } + if (this._sgNode) { + this._sgNode.setTextureWithRect(texture, rect); + } + }, + + // PRIVATE METHODS + + _applyFile: function (done) { + var node = this._sgNode; + if (!node) { + return; + } + var file = this._file; + if (file) { + var self = this; + cc.loader.load(file, function (err, results) { + if (err) throw err; + + node.particleCount = 0; + + var active = node.isActive(); + node.initWithFile(file); + + // recover sgNode properties + + if (!active) { + node.stopSystem(); + } + + var sourcePos = node.getPosition(); + if (CC_EDITOR && (sourcePos.x !== 0 || sourcePos.y !== 0)) { + cc.log('Discard sourcePosition: %s from "%s", you can set position in the node directly.', sourcePos, cc.path.basename(file)); + } + node.setPosition(0, 0); + + node.autoRemoveOnFinish = self._autoRemoveOnFinish; + + // + + if (done) { + done(); + } + }); + } + }, + + _applyCustoms: function () { + var node = this._sgNode; + if (!node) { + return; + } + + var active = node.isActive(); + + for (var i = 0; i < CustomProps.length; i++) { + var prop = CustomProps[i]; + node[prop] = this['_' + prop]; + } + if (this._texture) { + node.texture = cc.textureCache.addImage(this._texture); + } + + // recover sgNode properties + if (!active) { + node.stopSystem(); + } + node.autoRemoveOnFinish = this._autoRemoveOnFinish; + }, + + _applyAutoRemove: function () { + var sgNode = this._sgNode; + if (sgNode) { + var autoRemove = this._autoRemoveOnFinish; + sgNode.autoRemoveOnFinish = autoRemove; + if (autoRemove) { + cc.assert(!sgNode.onExit); + var self = this; + sgNode.onExit = function () { + cc.Node.prototype.onExit.call(this); + self.node.destroy(); + }; + } + else { + sgNode.onExit = null; + } + } + } +}); + +cc.EParticleSystem = module.exports = ParticleSystem; diff --git a/cocos2d/particle/CCPNGReader.js b/cocos2d/particle/CCPNGReader.js new file mode 100644 index 00000000000..44cde5b4f86 --- /dev/null +++ b/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/cocos2d/particle/CCParticleAsset.js b/cocos2d/particle/CCParticleAsset.js new file mode 100644 index 00000000000..da960e75e5c --- /dev/null +++ b/cocos2d/particle/CCParticleAsset.js @@ -0,0 +1,12 @@ +/** + * Class for particle asset handling. + * @class ParticleAsset + * @extends RawAsset + * @constructor + */ +var ParticleAsset = cc.Class({ + name: 'cc.ParticleAsset', + extends: cc.RawAsset, +}); + +cc.ParticleAsset = module.exports = ParticleAsset; diff --git a/cocos2d/particle/CCParticleBatchNode.js b/cocos2d/particle/CCParticleBatchNode.js new file mode 100644 index 00000000000..c63fcb658e5 --- /dev/null +++ b/cocos2d/particle/CCParticleBatchNode.js @@ -0,0 +1,527 @@ +/** + * 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.js.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); + }, + + /** + * 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; + } + + if (zOrder === child.zIndex) + 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 + */ + 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.onEnter(); + child.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/cocos2d/particle/CCParticleBatchNodeCanvasRenderCmd.js b/cocos2d/particle/CCParticleBatchNodeCanvasRenderCmd.js new file mode 100644 index 00000000000..36da185ecba --- /dev/null +++ b/cocos2d/particle/CCParticleBatchNodeCanvasRenderCmd.js @@ -0,0 +1,38 @@ +/**************************************************************************** + 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){ + cc.Node.CanvasRenderCmd.call(this, 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/cocos2d/particle/CCParticleBatchNodeWebGLRenderCmd.js b/cocos2d/particle/CCParticleBatchNodeWebGLRenderCmd.js new file mode 100644 index 00000000000..16da24b496d --- /dev/null +++ b/cocos2d/particle/CCParticleBatchNodeWebGLRenderCmd.js @@ -0,0 +1,74 @@ +/**************************************************************************** + 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){ + cc.Node.WebGLRenderCmd.call(this, renderable); + this._needDraw = true; + }; + + 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; + + this._shaderProgram.use(); + this._shaderProgram._setUniformForMVPMatrixWithMat4(this._stackMatrix); + cc.glBlendFuncForParticle(_t._blendFunc.src, _t._blendFunc.dst); + _t.textureAtlas.drawQuads(); + }; + + proto._initWithTexture = function(){ + this._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR); + }; + + proto.visit = function(parentCmd){ + var node = this._node; + // CAREFUL: + // This visit is almost identical to cc.Node#visit + // with the exception that it doesn't call visit on it's children + // + // The alternative is to have a void cc.Sprite#visit, but + // although this is less mantainable, is faster + // + if (!node._visible) + return; + + var currentStack = cc.current_stack; + currentStack.stack.push(currentStack.top); + this._syncStatus(parentCmd); + currentStack.top = this._stackMatrix; + //this.draw(ctx); + cc.renderer.pushRenderCommand(this); + + this._dirtyFlag = 0; + cc.kmGLPopMatrix(); + }; +})(); \ No newline at end of file diff --git a/cocos2d/particle/CCParticleExamples.js b/cocos2d/particle/CCParticleExamples.js new file mode 100644 index 00000000000..49d603e568f --- /dev/null +++ b/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/cocos2d/particle/CCParticleSystem.js b/cocos2d/particle/CCParticleSystem.js new file mode 100644 index 00000000000..661fdaa22f9 --- /dev/null +++ b/cocos2d/particle/CCParticleSystem.js @@ -0,0 +1,2241 @@ +/**************************************************************************** + 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.Vec2} [pos=cc.p(0,0)] Position of particle + * @param {cc.Vec2} [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.Vec2} 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:
+ * - emmision 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.Vec2} sourcePos - Source position of the emitter. + * @property {cc.Vec2} 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.Vec2} gravity - Gravity of the emitter. + * @property {cc.Vec2} speed - Speed of the emitter. + * @property {cc.Vec2} 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 {cc.ParticleSystem.Mode} emitterMode - Emitter modes: cc.ParticleSystem.Mode.GRAVITY: uses gravity, speed, radial and tangential acceleration; cc.ParticleSystem.Mode.RADIUS: uses radius movement + rotation. + * @property {cc.ParticleSystem.Type} 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} 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.js.isNumber(plistFile)) { + var ton = plistFile || 100; + this.setDrawMode(cc.ParticleSystem.TEXTURE_MODE); + this.initWithTotalParticles(ton); + } else if (cc.js.isString(plistFile)) { + this.initWithFile(plistFile); + } else if (cc.js.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.Vec2 | Object} + */ + getSourcePosition:function () { + return {x: this._sourcePosition.x, y: this._sourcePosition.y}; + }, + + /** + * sourcePosition of the emitter setter + * @param sourcePosition + */ + setSourcePosition:function (sourcePosition) { + this._sourcePosition = sourcePosition; + }, + + /** + * Return Position variance of the emitter + * @return {cc.Vec2 | Object} + */ + getPosVar:function () { + return {x: this._posVar.x, y: this._posVar.y}; + }, + + /** + * Position variance of the emitter setter + * @param {cc.Vec2} posVar + */ + setPosVar:function (posVar) { + this._posVar = posVar; + }, + + /** + * 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.Vec2} + */ + getGravity:function () { + var locGravity = this.modeA.gravity; + return cc.p(locGravity.x, locGravity.y); + }, + + /** + * Gravity of emitter setter + * @param {cc.Vec2} gravity + */ + setGravity:function (gravity) { + this.modeA.gravity = gravity; + }, + + /** + * Return Speed of each particle + * @return {Number} + */ + getSpeed:function () { + return this.modeA.speed; + }, + + /** + * Speed of each particle setter + * @param {Number} speed + */ + setSpeed:function (speed) { + this.modeA.speed = speed; + }, + + /** + * return speed variance of each particle. Only available in 'Gravity' mode. + * @return {Number} + */ + getSpeedVar:function () { + return this.modeA.speedVar; + }, + + /** + * speed variance of each particle setter. Only available in 'Gravity' mode. + * @param {Number} speedVar + */ + setSpeedVar:function (speedVar) { + this.modeA.speedVar = speedVar; + }, + + /** + * Return tangential acceleration of each particle. Only available in 'Gravity' mode. + * @return {Number} + */ + getTangentialAccel:function () { + return this.modeA.tangentialAccel; + }, + + /** + * Tangential acceleration of each particle setter. Only available in 'Gravity' mode. + * @param {Number} tangentialAccel + */ + setTangentialAccel:function (tangentialAccel) { + this.modeA.tangentialAccel = tangentialAccel; + }, + + /** + * Return tangential acceleration variance of each particle. Only available in 'Gravity' mode. + * @return {Number} + */ + getTangentialAccelVar:function () { + return this.modeA.tangentialAccelVar; + }, + + /** + * tangential acceleration variance of each particle setter. Only available in 'Gravity' mode. + * @param {Number} tangentialAccelVar + */ + setTangentialAccelVar:function (tangentialAccelVar) { + this.modeA.tangentialAccelVar = tangentialAccelVar; + }, + + /** + * Return radial acceleration of each particle. Only available in 'Gravity' mode. + * @return {Number} + */ + getRadialAccel:function () { + return this.modeA.radialAccel; + }, + + /** + * radial acceleration of each particle setter. Only available in 'Gravity' mode. + * @param {Number} radialAccel + */ + setRadialAccel:function (radialAccel) { + this.modeA.radialAccel = radialAccel; + }, + + /** + * Return radial acceleration variance of each particle. Only available in 'Gravity' mode. + * @return {Number} + */ + getRadialAccelVar:function () { + return this.modeA.radialAccelVar; + }, + + /** + * radial acceleration variance of each particle setter. Only available in 'Gravity' mode. + * @param {Number} radialAccelVar + */ + setRadialAccelVar:function (radialAccelVar) { + this.modeA.radialAccelVar = radialAccelVar; + }, + + /** + * get the rotation of each particle to its direction Only available in 'Gravity' mode. + * @returns {boolean} + */ + getRotationIsDir: function(){ + return this.modeA.rotationIsDir; + }, + + /** + * set the rotation of each particle to its direction Only available in 'Gravity' mode. + * @param {boolean} t + */ + setRotationIsDir: function(t){ + this.modeA.rotationIsDir = t; + }, + + // mode B + /** + * Return starting radius of the particles. Only available in 'Radius' mode. + * @return {Number} + */ + getStartRadius:function () { + return this.modeB.startRadius; + }, + + /** + * starting radius of the particles setter. Only available in 'Radius' mode. + * @param {Number} startRadius + */ + setStartRadius:function (startRadius) { + this.modeB.startRadius = startRadius; + }, + + /** + * Return starting radius variance of the particles. Only available in 'Radius' mode. + * @return {Number} + */ + getStartRadiusVar:function () { + return this.modeB.startRadiusVar; + }, + + /** + * starting radius variance of the particles setter. Only available in 'Radius' mode. + * @param {Number} startRadiusVar + */ + setStartRadiusVar:function (startRadiusVar) { + this.modeB.startRadiusVar = startRadiusVar; + }, + + /** + * Return ending radius of the particles. Only available in 'Radius' mode. + * @return {Number} + */ + getEndRadius:function () { + return this.modeB.endRadius; + }, + + /** + * ending radius of the particles setter. Only available in 'Radius' mode. + * @param {Number} endRadius + */ + setEndRadius:function (endRadius) { + this.modeB.endRadius = endRadius; + }, + + /** + * Return ending radius variance of the particles. Only available in 'Radius' mode. + * @return {Number} + */ + getEndRadiusVar:function () { + return this.modeB.endRadiusVar; + }, + + /** + * ending radius variance of the particles setter. Only available in 'Radius' mode. + * @param endRadiusVar + */ + setEndRadiusVar:function (endRadiusVar) { + 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 () { + 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) { + this.modeB.rotatePerSecond = degrees; + }, + + /** + * Return Variance in degrees for rotatePerSecond. Only available in 'Radius' mode. + * @return {Number} + */ + getRotatePerSecondVar:function () { + return this.modeB.rotatePerSecondVar; + }, + + /** + * Variance in degrees for rotatePerSecond setter. Only available in 'Radius' mode. + * @param degrees + */ + setRotatePerSecondVar:function (degrees) { + 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 = cc.color(startColor); + }, + + /** + * 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 = cc.color(startColorVar); + }, + + /** + * 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 = cc.color(endColor); + }, + + /** + * 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 = cc.color(endColorVar); + }, + + /** + * 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.once("load", function (event) { + this._textureLoaded = true; + this.setTextureWithRect(texture, cc.rect(0, 0, texture.width, texture.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).toLowerCase(); + locModeA.rotationIsDir = (locRotationIsDir != null && (locRotationIsDir === "true" || locRotationIsDir === "1")); + } 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.Vec2} 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.Vec2} [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; + +/** + * Enum for particle mode + * @readonly + * @enum {number} + */ +cc.ParticleSystem.Mode = cc.Enum({ + /** The gravity mode (A mode) */ + GRAVITY: 0, + /** The radius mode (B mode) */ + RADIUS: 1 +}); + +/** + * Enum for particle type + * @readonly + * @enum {number} + */ +cc.ParticleSystem.Type = cc.Enum({ + /** + * Living particles are attached to the world and are unaffected by emitter repositioning. + */ + 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. + */ + RELATIVE: 1, + + /** + * Living particles are attached to the emitter and are translated along with it. + */ + GROUPED: 2 +}); diff --git a/cocos2d/particle/CCParticleSystemCanvasRenderCmd.js b/cocos2d/particle/CCParticleSystemCanvasRenderCmd.js new file mode 100644 index 00000000000..de782523469 --- /dev/null +++ b/cocos2d/particle/CCParticleSystemCanvasRenderCmd.js @@ -0,0 +1,199 @@ +/**************************************************************************** + 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){ + cc.Node.CanvasRenderCmd.call(this, 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 = document.createElement("canvas"); + }; + 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){ + 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) { + 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/cocos2d/particle/CCParticleSystemWebGLRenderCmd.js b/cocos2d/particle/CCParticleSystemWebGLRenderCmd.js new file mode 100644 index 00000000000..78119471222 --- /dev/null +++ b/cocos2d/particle/CCParticleSystemWebGLRenderCmd.js @@ -0,0 +1,401 @@ +/**************************************************************************** + 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){ + cc.Node.WebGLRenderCmd.call(this, renderable); + this._needDraw = true; + + 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; + + this._shaderProgram.use(); + this._shaderProgram._setUniformForMVPMatrixWithMat4(this._stackMatrix); //; + + cc.glBindTexture2D(node._texture); + cc.glBlendFuncForParticle(node._blendFunc.src, node._blendFunc.dst); + + // + // Using VBO without VAO + // + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX); + + 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.getPixelWidth(); + high = texture.getPixelHeight(); + } + + 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.bufferData(gl.ARRAY_BUFFER, this._quadsArrayBuffer, gl.DYNAMIC_DRAW); + }; + + 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; + }; +})(); \ No newline at end of file diff --git a/cocos2d/particle/CCTIFFReader.js b/cocos2d/particle/CCTIFFReader.js new file mode 100644 index 00000000000..185486539a2 --- /dev/null +++ b/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/cocos2d/physics/CCPhysicsDebugNode.js b/cocos2d/physics/CCPhysicsDebugNode.js new file mode 100644 index 00000000000..003bfe3f743 --- /dev/null +++ b/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/cocos2d/physics/CCPhysicsDebugNodeCanvasRenderCmd.js b/cocos2d/physics/CCPhysicsDebugNodeCanvasRenderCmd.js new file mode 100644 index 00000000000..be1f6f14bae --- /dev/null +++ b/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){ + cc.Node.CanvasRenderCmd.call(this, 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/cocos2d/physics/CCPhysicsDebugNodeWebGLRenderCmd.js b/cocos2d/physics/CCPhysicsDebugNodeWebGLRenderCmd.js new file mode 100644 index 00000000000..38ddf9f6300 --- /dev/null +++ b/cocos2d/physics/CCPhysicsDebugNodeWebGLRenderCmd.js @@ -0,0 +1,53 @@ +/**************************************************************************** + 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) { + cc.Node.WebGLRenderCmd.call(this, renderableObject); + this._needDraw = true; + }; + + 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)); + + //cc.DrawNode.prototype.draw.call(node); + cc.glBlendFunc(node._blendFunc.src, node._blendFunc.dst); + this._shaderProgram.use(); + this._shaderProgram._setUniformForMVPMatrixWithMat4(this._stackMatrix); + node._render(); + + node.clear(); + }; +})(); \ No newline at end of file diff --git a/cocos2d/physics/CCPhysicsSprite.js b/cocos2d/physics/CCPhysicsSprite.js new file mode 100644 index 00000000000..30d7bcdb11a --- /dev/null +++ b/cocos2d/physics/CCPhysicsSprite.js @@ -0,0 +1,447 @@ +/** + * 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.js.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.js.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.Vec2} + */ + getPosition:function () { + var pos = this._body.GetPosition(); + var locPTMRatio =this._PTMRatio; + return cc.p(pos.x * locPTMRatio, pos.y * locPTMRatio); + }, + + /** + * set position + * @param {cc.Vec2} 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 pos = this._body.GetPosition(); + this._position.x = pos.x * this._PTMRatio; + this._position.y = pos.y * this._PTMRatio; + this._rotationRadians = this._rotation * (Math.PI / 180); + }, + _syncRotation:function () { + this._rotationRadians = this._body.GetAngle(); + }, + /** + * visit + */ + visit:function () { + if (this._body && this._PTMRatio) { + this._syncPosition(); + if (!this._ignoreBodyRotation) + this._syncRotation(); + } + else { + cc.log("PhysicsSprite body or PTIMRatio was not set"); + } + this._super(); + }, + + /** + * 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.js.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.js.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.Vec2} + */ + 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.Vec2|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; + } + //this._syncPosition(); + }, + + /** + * set position x + * @param {Number} xValue + */ + setPositionX:function (xValue) { + this._body.p.x = xValue; + //this._syncPosition(); + }, + + /** + * set position y + * @param {Number} yValue + */ + setPositionY:function (yValue) { + this._body.p.y = yValue; + //this._syncPosition(); + }, + + _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); + //this._syncRotation(); + } + }, + _syncRotation:function () { + if (this._rotationX !== -cc.radiansToDegrees(this._body.a)) { + cc.Sprite.prototype.setRotation.call(this, -cc.radiansToDegrees(this._body.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); + } + }; + 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/cocos2d/physics/CCPhysicsSpriteCanvasRenderCmd.js b/cocos2d/physics/CCPhysicsSpriteCanvasRenderCmd.js new file mode 100644 index 00000000000..ae95d94df9e --- /dev/null +++ b/cocos2d/physics/CCPhysicsSpriteCanvasRenderCmd.js @@ -0,0 +1,93 @@ +/**************************************************************************** + 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){ + cc.Sprite.CanvasRenderCmd.call(this, 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); + }; + + proto.getNodeToParentTransform = function(){ + var node = this._node; + + var t = this._transform;// quick reference + // base position + var locBody = node._body, locScaleX = node._scaleX, locScaleY = node._scaleY, locAnchorPIP = this._anchorPointInPoints; + t.tx = locBody.p.x; + t.ty = locBody.p.y; + + // rotation Cos and Sin + var radians = -locBody.a; + var Cos = 1, Sin = 0; + if (radians && !node._ignoreBodyRotation) { + Cos = Math.cos(radians); + Sin = Math.sin(radians); + } + + // base abcd + t.a = t.d = Cos; + t.b = -Sin; + t.c = Sin; + + // scale + if (locScaleX !== 1 || locScaleY !== 1) { + t.a *= locScaleX; + t.c *= locScaleX; + t.b *= locScaleY; + t.d *= locScaleY; + } + + // adjust anchorPoint + t.tx += Cos * -locAnchorPIP.x * locScaleX + -Sin * locAnchorPIP.y * locScaleY; + t.ty -= Sin * -locAnchorPIP.x * locScaleX + Cos * locAnchorPIP.y * locScaleY; + + // if ignore anchorPoint + if (this._ignoreAnchorPointForPosition) { + t.tx += locAnchorPIP.x; + t.ty += locAnchorPIP.y; + } + + return this._transform; + }; + +})(); \ No newline at end of file diff --git a/cocos2d/physics/CCPhysicsSpriteWebGLRenderCmd.js b/cocos2d/physics/CCPhysicsSpriteWebGLRenderCmd.js new file mode 100644 index 00000000000..339610ed360 --- /dev/null +++ b/cocos2d/physics/CCPhysicsSpriteWebGLRenderCmd.js @@ -0,0 +1,92 @@ +/**************************************************************************** + 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){ + cc.Sprite.WebGLRenderCmd.call(this, renderableObject); + this._needDraw = true; + }; + + var proto = cc.PhysicsSprite.WebGLRenderCmd.prototype = Object.create(cc.Sprite.WebGLRenderCmd.prototype); + proto.constructor = cc.PhysicsSprite.WebGLRenderCmd; + + proto.rendering = function(ctx){ + // 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); + + cc.Sprite.WebGLRenderCmd.prototype.rendering.call(this, ctx); + }; + + proto.getNodeToParentTransform = function(){ + var node = this._node; + var locBody = node._body, locAnchorPIP = this._anchorPointInPoints, locScaleX = node._scaleX, locScaleY = node._scaleY; + var x = locBody.p.x; + var y = locBody.p.y; + + if (this._ignoreAnchorPointForPosition) { + x += locAnchorPIP.x; + y += locAnchorPIP.y; + } + + // Make matrix + var radians = locBody.a, c = 1, s=0; + if (radians && !node._ignoreBodyRotation) { + c = Math.cos(radians); + s = Math.sin(radians); + } + + // Although scale is not used by physics engines, it is calculated just in case + // the sprite is animated (scaled up/down) using actions. + // For more info see: http://www.cocos2d-iphone.org/forum/topic/68990 + if (!cc._rectEqualToZero(locAnchorPIP)) { + x += c * -locAnchorPIP.x * locScaleX + -s * -locAnchorPIP.y * locScaleY; + y += s * -locAnchorPIP.x * locScaleX + c * -locAnchorPIP.y * locScaleY; + } + + // Rot, Translate Matrix + this._transform = cc.affineTransformMake(c * locScaleX, s * locScaleX, + -s * locScaleY, c * locScaleY, x, y); + + return this._transform; + }; + + proto.updateTransform = function(){ + var node = this._node; + var dirty = node.isDirty(); + if(dirty){ + var cmd = node._renderCmd; + cmd && cmd.setDirtyRecursively(true); + } + cc.Sprite.WebGLRenderCmd.prototype.updateTransform.call(this); + }; +})(); \ No newline at end of file diff --git a/cocos2d/progress-timer/CCActionProgressTimer.js b/cocos2d/progress-timer/CCActionProgressTimer.js new file mode 100644 index 00000000000..026af89750e --- /dev/null +++ b/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/cocos2d/progress-timer/CCProgressTimer.js b/cocos2d/progress-timer/CCProgressTimer.js new file mode 100644 index 00000000000..65f103744bb --- /dev/null +++ b/cocos2d/progress-timer/CCProgressTimer.js @@ -0,0 +1,344 @@ +/**************************************************************************** + 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.Vec2} 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.Vec2} barChangeRate - This allows the bar type to move the component at a specific rate. + * @property {cc.ProgressTimer.Type} type - Type of the progress timer. + * @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.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); + }, + + /** + * 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.Vec2} + */ + getMidpoint:function () { + return cc.p(this._midPoint.x, this._midPoint.y); + }, + + /** + * Midpoint setter + * @param {cc.Vec2} 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.Vec2} + */ + getBarChangeRate:function () { + return cc.p(this._barChangeRate.x, this._barChangeRate.y); + }, + + /** + * @param {cc.Vec2} 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} + */ + 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.releaseData(); + } + }, + + /** + * 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); + else + this.setContentSize(0,0); + this._renderCmd.releaseData(); + } + }, + + /** + * set Progress type of cc.ProgressTimer + * @function + * @param {cc.ProgressTimer.Type} type + */ + setType: function(type){ + if (type !== this._type){ + this._type = type; + this._renderCmd.releaseData(); + } + }, + + /** + * Reverse Progress setter + * @function + * @param {Boolean} reverse + */ + setReverseDirection: function(reverse){ + if (this._reverseDirection !== reverse){ + this._reverseDirection = reverse; + this._renderCmd.releaseData(); + } + }, + + /** + * 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.initCmd(); + 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; + +/** + * Enum for type of ProgressTimer + * @readonly + * @enum {number} + */ +cc.ProgressTimer.Type = cc.Enum({ + /** + * Radial Counter-Clockwise + */ + RADIAL: 0, + BAR: 1 +}); diff --git a/cocos2d/progress-timer/CCProgressTimerCanvasRenderCmd.js b/cocos2d/progress-timer/CCProgressTimerCanvasRenderCmd.js new file mode 100644 index 00000000000..7015d944ead --- /dev/null +++ b/cocos2d/progress-timer/CCProgressTimerCanvasRenderCmd.js @@ -0,0 +1,270 @@ +/**************************************************************************** + 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){ + cc.Node.CanvasRenderCmd.call(this, 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; + }; + + 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 * scaleX, locBarRect.y * scaleY, locBarRect.width * scaleX, locBarRect.height * scaleY); + context.clip(); + context.closePath(); + } else if (node._type === cc.ProgressTimer.Type.RADIAL) { + var locOriginX = this._origin.x * scaleX; + var locOriginY = this._origin.y * scaleY; + context.beginPath(); + context.arc(locOriginX, locOriginY, this._radius * scaleY, this._PI180 * this._startAngle, this._PI180 * this._endAngle, this._counterClockWise); + context.lineTo(locOriginX, locOriginY); + context.clip(); + context.closePath(); + } + + //draw sprite + var image = locSprite._texture.getHtmlElementObj(); + if (locSprite._renderCmd._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); + } + wrapper.restore(); + cc.g_NumberOfDraws++; + }; + + proto.releaseData = function(){}; + + proto.initCmd = function(){}; + + proto._updateProgress = function(){ + 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._updateColor = function(){}; + + 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(); + } + + if (opacityDirty){ + spriteCmd._syncDisplayOpacity(); + } + + if(colorDirty || opacityDirty){ + spriteCmd._updateColor(); + //this._updateColor(); + } + + if (locFlag & flags.transformDirty) { + //update the transform + this.transform(parentCmd); + } + }; + + 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(); + } + + if(opacityDirty){ + spriteCmd._updateDisplayOpacity(); + } + + if(colorDirty || opacityDirty){ + spriteCmd._updateColor(); + //this._updateColor(); + } + + if(locFlag & flags.transformDirty){ + //update the transform + this.transform(this.getParentRenderCmd(), true); + } + this._dirtyFlag = 0; + }; +})(); \ No newline at end of file diff --git a/cocos2d/progress-timer/CCProgressTimerWebGLRenderCmd.js b/cocos2d/progress-timer/CCProgressTimerWebGLRenderCmd.js new file mode 100644 index 00000000000..cc14d703d5a --- /dev/null +++ b/cocos2d/progress-timer/CCProgressTimerWebGLRenderCmd.js @@ -0,0 +1,486 @@ +/**************************************************************************** + 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(){ + cc.ProgressTimer.WebGLRenderCmd = function(renderableObject){ + cc.Node.WebGLRenderCmd.call(this, renderableObject); + this._needDraw = true; + + this._vertexWebGLBuffer = cc._renderContext.createBuffer(); + this._vertexDataCount = 0; + this._vertexData = null; + this._vertexArrayBuffer = null; + this._vertexDataDirty = false; + }; + + var proto = cc.ProgressTimer.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + proto.constructor = cc.ProgressTimer.WebGLRenderCmd; + + proto.rendering = function (ctx) { + var node = this._node; + var context = ctx || cc._renderContext; + if (!this._vertexData || !node._sprite) + return; + + this._shaderProgram.use(); + this._shaderProgram._setUniformForMVPMatrixWithMat4(this._stackMatrix); + + var blendFunc = node._sprite._blendFunc; + cc.glBlendFunc(blendFunc.src, blendFunc.dst); + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX); + + cc.glBindTexture2D(node._sprite.texture); + + context.bindBuffer(context.ARRAY_BUFFER, this._vertexWebGLBuffer); + if (this._vertexDataDirty) { + context.bufferData(context.ARRAY_BUFFER, this._vertexArrayBuffer, context.DYNAMIC_DRAW); + this._vertexDataDirty = false; + } + var locVertexDataLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT; + context.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, context.FLOAT, false, locVertexDataLen, 0); + context.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, context.UNSIGNED_BYTE, true, locVertexDataLen, 8); + context.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, context.FLOAT, false, locVertexDataLen, 12); + + 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 = spriteFlag & flags.colorDirty, + opacityDirty = spriteFlag & flags.opacityDirty; + + if (colorDirty){ + spriteCmd._syncDisplayColor(); + } + + if (opacityDirty){ + spriteCmd._syncDisplayOpacity(); + } + + if(colorDirty || opacityDirty){ + spriteCmd._updateColor(); + this._updateColor(); + } + + //if (locFlag & flags.transformDirty) { + //update the transform + this.transform(parentCmd); + //} + + 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 = spriteFlag & flags.colorDirty, + opacityDirty = spriteFlag & flags.opacityDirty; + + if(colorDirty){ + spriteCmd._updateDisplayColor(); + this._dirtyFlag = this._dirtyFlag & flags.colorDirty ^ this._dirtyFlag; + } + + if(opacityDirty){ + spriteCmd._updateDisplayOpacity(); + this._dirtyFlag = this._dirtyFlag & flags.opacityDirty ^ this._dirtyFlag; + } + + if(colorDirty || opacityDirty){ + spriteCmd._updateColor(); + this._updateColor(); + } + + if(locFlag & flags.transformDirty){ + //update the transform + this.transform(this.getParentRenderCmd(), true); + } + }; + + proto.releaseData = function(){ + if (this._vertexData) { + //release all previous information + this._vertexData = null; + this._vertexArrayBuffer = null; + this._vertexDataCount = 0; + } + }; + + proto.initCmd = function(){ + this._vertexData = null; + this._vertexArrayBuffer = null; + this._vertexDataCount = 0; + + //shader program + this._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR); + }; + + proto._updateProgress = 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; + }; + + /** + *

+ * 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._vertexData) { + this._vertexDataCount = 4; + var vertexDataLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT, locCount = 4; + this._vertexArrayBuffer = new ArrayBuffer(locCount * vertexDataLen); + this._vertexData = []; + for (i = 0; i < locCount; i++) + this._vertexData[i] = new cc.V2F_C4B_T2F(null, null, null, this._vertexArrayBuffer, i * vertexDataLen); + } + + locVertexData = this._vertexData; + // TOPLEFT + locVertexData[0].texCoords = this._textureCoordFromAlphaPoint(cc.p(min.x, max.y)); + locVertexData[0].vertices = this._vertexFromAlphaPoint(cc.p(min.x, max.y)); + + // BOTLEFT + locVertexData[1].texCoords = this._textureCoordFromAlphaPoint(cc.p(min.x, min.y)); + locVertexData[1].vertices = this._vertexFromAlphaPoint(cc.p(min.x, min.y)); + + // TOPRIGHT + locVertexData[2].texCoords = this._textureCoordFromAlphaPoint(cc.p(max.x, max.y)); + locVertexData[2].vertices = this._vertexFromAlphaPoint(cc.p(max.x, max.y)); + + // BOTRIGHT + locVertexData[3].texCoords = this._textureCoordFromAlphaPoint(cc.p(max.x, min.y)); + locVertexData[3].vertices = this._vertexFromAlphaPoint(cc.p(max.x, min.y)); + } else { + if (!this._vertexData) { + this._vertexDataCount = 8; + var rVertexDataLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT, rLocCount = 8; + this._vertexArrayBuffer = new ArrayBuffer(rLocCount * rVertexDataLen); + var rTempData = []; + for (i = 0; i < rLocCount; i++) + rTempData[i] = new cc.V2F_C4B_T2F(null, null, null, this._vertexArrayBuffer, i * rVertexDataLen); + // TOPLEFT 1 + rTempData[0].texCoords = this._textureCoordFromAlphaPoint(cc.p(0, 1)); + rTempData[0].vertices = this._vertexFromAlphaPoint(cc.p(0, 1)); + + // BOTLEFT 1 + rTempData[1].texCoords = this._textureCoordFromAlphaPoint(cc.p(0, 0)); + rTempData[1].vertices = this._vertexFromAlphaPoint(cc.p(0, 0)); + + // TOPRIGHT 2 + rTempData[6].texCoords = this._textureCoordFromAlphaPoint(cc.p(1, 1)); + rTempData[6].vertices = this._vertexFromAlphaPoint(cc.p(1, 1)); + + // BOTRIGHT 2 + rTempData[7].texCoords = this._textureCoordFromAlphaPoint(cc.p(1, 0)); + rTempData[7].vertices = this._vertexFromAlphaPoint(cc.p(1, 0)); + + this._vertexData = rTempData; + } + + locVertexData = this._vertexData; + // TOPRIGHT 1 + locVertexData[2].texCoords = this._textureCoordFromAlphaPoint(cc.p(min.x, max.y)); + locVertexData[2].vertices = this._vertexFromAlphaPoint(cc.p(min.x, max.y)); + + // BOTRIGHT 1 + locVertexData[3].texCoords = this._textureCoordFromAlphaPoint(cc.p(min.x, min.y)); + locVertexData[3].vertices = this._vertexFromAlphaPoint(cc.p(min.x, min.y)); + + // TOPLEFT 2 + locVertexData[4].texCoords = this._textureCoordFromAlphaPoint(cc.p(max.x, max.y)); + locVertexData[4].vertices = this._vertexFromAlphaPoint(cc.p(max.x, max.y)); + + // BOTLEFT 2 + locVertexData[5].texCoords = this._textureCoordFromAlphaPoint(cc.p(max.x, min.y)); + locVertexData[5].vertices = this._vertexFromAlphaPoint(cc.p(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._vertexData = null; + this._vertexArrayBuffer = null; + this._vertexDataCount = 0; + } + + if (!this._vertexData) { + this._vertexDataCount = index + 3; + var locCount = this._vertexDataCount, vertexDataLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT; + this._vertexArrayBuffer = new ArrayBuffer(locCount * vertexDataLen); + var locData = []; + for (i = 0; i < locCount; i++) + locData[i] = new cc.V2F_C4B_T2F(null, null, null, this._vertexArrayBuffer, i * vertexDataLen); + + this._vertexData = locData; + if(!this._vertexData){ + cc.log( "cc.ProgressTimer._updateRadial() : Not enough memory"); + return; + } + } + this._updateColor(); + + var locVertexData = this._vertexData; + if (!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 + locVertexData[0].texCoords = this._textureCoordFromAlphaPoint(locMidPoint); + locVertexData[0].vertices = this._vertexFromAlphaPoint(locMidPoint); + + locVertexData[1].texCoords = this._textureCoordFromAlphaPoint(topMid); + locVertexData[1].vertices = this._vertexFromAlphaPoint(topMid); + + for (i = 0; i < index; i++) { + var alphaPoint = this._boundaryTexCoord(i); + locVertexData[i + 2].texCoords = this._textureCoordFromAlphaPoint(alphaPoint); + locVertexData[i + 2].vertices = this._vertexFromAlphaPoint(alphaPoint); + } + } + + // hitpoint will go last + locVertexData[this._vertexDataCount - 1].texCoords = this._textureCoordFromAlphaPoint(hit); + locVertexData[this._vertexDataCount - 1].vertices = this._vertexFromAlphaPoint(hit); + }; + + 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 (alpha) { + var locSprite = this._node._sprite; + if (!locSprite) { + return {u:0, v:0}; //new cc.Tex2F(0, 0); + } + var quad = locSprite.quad; + var min = cc.p(quad.bl.texCoords.u, quad.bl.texCoords.v); + var max = cc.p(quad.tr.texCoords.u, quad.tr.texCoords.v); + + // Fix bug #1303 so that progress timer handles sprite frame texture rotation + if (locSprite.textureRectRotated) { + var temp = alpha.x; + alpha.x = alpha.y; + alpha.y = temp; + } + return {u: min.x * (1 - alpha.x) + max.x * alpha.x, v: min.y * (1 - alpha.y) + max.y * alpha.y}; + }; + + proto._vertexFromAlphaPoint = function (alpha) { + var locSprite = this._node._sprite; + if (!locSprite) { + return {x: 0, y: 0}; + } + var quad = locSprite.quad; + var min = cc.p(quad.bl.vertices.x, quad.bl.vertices.y); + var max = cc.p(quad.tr.vertices.x, quad.tr.vertices.y); + return {x: min.x * (1 - alpha.x) + max.x * alpha.x, y: min.y * (1 - alpha.y) + max.y * alpha.y}; + }; + + proto._updateColor = function(){ + var node = this._node; + if (!node._sprite || !this._vertexData) + return; + + var sc = node._sprite.quad.tl.colors; + var locVertexData = this._vertexData; + for (var i = 0, len = this._vertexDataCount; i < len; ++i) + locVertexData[i].colors = sc; + this._vertexDataDirty = true; + }; +})(); \ No newline at end of file diff --git a/cocos2d/render-texture/CCRenderTexture.js b/cocos2d/render-texture/CCRenderTexture.js new file mode 100644 index 00000000000..24c8d7ea079 --- /dev/null +++ b/cocos2d/render-texture/CCRenderTexture.js @@ -0,0 +1,406 @@ +/**************************************************************************** + 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); + }, + + /** + * 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.Vec2} 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-1 + * @param {Number} g green 0-1 + * @param {Number} b blue 0-1 + * @param {Number} a alpha 0-1 + */ + 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 compatiblity 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 compatiblity 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/cocos2d/render-texture/CCRenderTextureCanvasRenderCmd.js b/cocos2d/render-texture/CCRenderTextureCanvasRenderCmd.js new file mode 100644 index 00000000000..854ebba1ccb --- /dev/null +++ b/cocos2d/render-texture/CCRenderTextureCanvasRenderCmd.js @@ -0,0 +1,107 @@ +/**************************************************************************** + 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){ + cc.Node.CanvasRenderCmd.call(this, renderableObject); + this._needDraw = true; + 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); + }; + + 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"); + }; + + proto.visit = function(parentCmd){ + var node = this._node; + this._syncStatus(parentCmd); + node.sprite.visit(this); + this._dirtyFlag = 0; + }; +})(); \ No newline at end of file diff --git a/cocos2d/render-texture/CCRenderTextureWebGLRenderCmd.js b/cocos2d/render-texture/CCRenderTextureWebGLRenderCmd.js new file mode 100644 index 00000000000..50bed35f169 --- /dev/null +++ b/cocos2d/render-texture/CCRenderTextureWebGLRenderCmd.js @@ -0,0 +1,387 @@ +/**************************************************************************** + 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){ + cc.Node.WebGLRenderCmd.call(this, 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.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._renderCmd.visit(node.sprite._renderCmd); //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 Andreno 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(); + }; + + proto.visit = function(parentCmd){ + var node = this._node; + if (!node._visible) + return; + cc.kmGLPushMatrix(); + + //TODO using GridNode + /* var locGrid = this.grid; + if (locGrid && locGrid.isActive()) { + locGrid.beforeDraw(); + this.transformAncestors(); + }*/ + + this._syncStatus(parentCmd); + //this.toRenderer(); + cc.renderer.pushRenderCommand(this); + node.sprite.visit(this); + + //TODO GridNode + /* if (locGrid && locGrid.isActive()) + locGrid.afterDraw(this);*/ + + this._dirtyFlag = 0; + cc.kmGLPopMatrix(); + }; +})(); \ No newline at end of file diff --git a/cocos2d/shaders/CCGLProgram.js b/cocos2d/shaders/CCGLProgram.js new file mode 100644 index 00000000000..3eb4f505ad9 --- /dev/null +++ b/cocos2d/shaders/CCGLProgram.js @@ -0,0 +1,767 @@ +/**************************************************************************** + 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. + ****************************************************************************/ + +cc.HashUniformEntry = function (value, location, hh) { + this.value = value; + this.location = location; + this.hh = hh || {}; +}; + +/** + * 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, + + // Uniform cache + _updateUniformLocation: function (location, data, bytes) { + if (location == null) + return false; + + var updated = true; + var element = null; + for (var i = 0; i < this._hashForUniforms.length; i++) + if (this._hashForUniforms[i].location == location) + element = this._hashForUniforms[i]; + + if (!element) { + element = new cc.HashUniformEntry(); + // key + element.location = location; + // value + element.value = data; + this._hashForUniforms.push(element); + } else { + if (element.value == data) + updated = false; + else + element.value = data; + } + + 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); + this._hashForUniforms.length = 0; + + 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._uniforms[cc.UNIFORM_PMATRIX] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_PMATRIX_S); + this._uniforms[cc.UNIFORM_MVMATRIX] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_MVMATRIX_S); + this._uniforms[cc.UNIFORM_MVPMATRIX] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_MVPMATRIX_S); + this._uniforms[cc.UNIFORM_TIME] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_TIME_S); + this._uniforms[cc.UNIFORM_SINTIME] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_SINTIME_S); + this._uniforms[cc.UNIFORM_COSTIME] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_COSTIME_S); + + this._usesTime = (this._uniforms[cc.UNIFORM_TIME] != null || this._uniforms[cc.UNIFORM_SINTIME] != null || this._uniforms[cc.UNIFORM_COSTIME] != null); + + this._uniforms[cc.UNIFORM_RANDOM01] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_RANDOM01_S); + this._uniforms[cc.UNIFORM_SAMPLER] = this._glContext.getUniformLocation(this._programObj, cc.UNIFORM_SAMPLER_S); + + this.use(); + // Since sample most probably won't change, set it to 0 now. + this.setUniformLocationWith1i(this._uniforms[cc.UNIFORM_SAMPLER], 0); + }, + + /** + * 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"); + + return this._glContext.getUniformLocation(this._programObj, name); + }, + + /** + * get uniform MVP matrix + * @returns {WebGLUniformLocation} + */ + getUniformMVPMatrix: function () { + return this._uniforms[cc.UNIFORM_MVPMATRIX]; + }, + + /** + * get uniform sampler + * @returns {WebGLUniformLocation} + */ + getUniformSampler: function () { + return this._uniforms[cc.UNIFORM_SAMPLER]; + }, + + /** + * calls glUniform1i only if the values are different than the previous call for this same shader program. + * @param {WebGLUniformLocation} location + * @param {Number} i1 + */ + setUniformLocationWith1i: function (location, i1) { + var updated = this._updateUniformLocation(location, i1); + if (updated) + this._glContext.uniform1i(location, i1); + }, + + /** + * calls glUniform2i only if the values are different than the previous call for this same shader program. + * @param {WebGLUniformLocation} location + * @param {Number} i1 + * @param {Number} i2 + */ + setUniformLocationWith2i:function(location, i1,i2){ + var intArray= [i1,i2]; + var updated = this._updateUniformLocation(location, intArray); + + if( updated ) + 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} location + * @param {Number} i1 + * @param {Number} i2 + * @param {Number} i3 + */ + setUniformLocationWith3i:function(location, i1, i2, i3){ + var intArray = [i1,i2,i3]; + var updated = this._updateUniformLocation(location, intArray); + + if( updated ) + 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} location + * @param {Number} i1 + * @param {Number} i2 + * @param {Number} i3 + * @param {Number} i4 + */ + setUniformLocationWith4i:function(location, i1, i2, i3, i4){ + var intArray = [i1,i2,i3,i4]; + var updated = this._updateUniformLocation(location, intArray); + + if( updated ) + this._glContext.uniform4i(location, i1, i2, i3, i4); + }, + + /** + * calls glUniform2iv only if the values are different than the previous call for this same shader program. + * @param {WebGLUniformLocation} location + * @param {Int32Array} intArray + * @param {Number} numberOfArrays + */ + setUniformLocationWith2iv:function(location, intArray, numberOfArrays){ + var updated = this._updateUniformLocation(location, intArray); + + if( updated ) + this._glContext.uniform2iv(location, intArray); + }, + + /** + * calls glUniform3iv only if the values are different than the previous call for this same shader program. + * @param {WebGLUniformLocation} location + * @param {Int32Array} intArray + * @param {Number} numberOfArrays + */ + setUniformLocationWith3iv:function(location, intArray, numberOfArrays){ + var updated = this._updateUniformLocation(location, intArray); + + if( updated ) + this._glContext.uniform3iv(location, intArray); + }, + + /** + * calls glUniform4iv only if the values are different than the previous call for this same shader program. + * @param {WebGLUniformLocation} location + * @param {Int32Array} intArray + * @param {Number} numberOfArrays + */ + setUniformLocationWith4iv:function(location, intArray, numberOfArrays){ + var updated = this._updateUniformLocation(location, intArray); + + if( updated ) + this._glContext.uniform4iv(location, intArray); + }, + + /** + * calls glUniform1i only if the values are different than the previous call for this same shader program. + * @param {WebGLUniformLocation} location + * @param {Number} i1 + */ + setUniformLocationI32: function (location, i1) { + this.setUniformLocationWith1i(arguments[0], arguments[1]); + }, + + /** + * calls glUniform1f only if the values are different than the previous call for this same shader program. + * @param {WebGLUniformLocation} location + * @param {Number} f1 + */ + setUniformLocationWith1f: function (location, f1) { + var updated = this._updateUniformLocation(location, f1); + if (updated) + this._glContext.uniform1f(location, f1); + }, + + /** + * calls glUniform2f only if the values are different than the previous call for this same shader program. + * @param {WebGLUniformLocation} location + * @param {Number} f1 + * @param {Number} f2 + */ + setUniformLocationWith2f: function (location, f1, f2) { + var floats = [f1, f2]; + var updated = this._updateUniformLocation(location, floats); + if (updated) + 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} location + * @param {Number} f1 + * @param {Number} f2 + * @param {Number} f3 + */ + setUniformLocationWith3f: function (location, f1, f2, f3) { + var floats = [f1, f2, f3]; + var updated = this._updateUniformLocation(location, floats); + if (updated) + 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} location + * @param {Number} f1 + * @param {Number} f2 + * @param {Number} f3 + * @param {Number} f4 + */ + setUniformLocationWith4f: function (location, f1, f2, f3, f4) { + var floats = [f1, f2, f3, f4]; + var updated = this._updateUniformLocation(location, floats); + if (updated) + this._glContext.uniform4f(location, f1, f2, f3, f4); + }, + + /** + * calls glUniform2fv only if the values are different than the previous call for this same shader program. + * @param {WebGLUniformLocation} location + * @param {Float32Array} floatArray + * @param {Number} numberOfArrays + */ + setUniformLocationWith2fv: function (location, floatArray, numberOfArrays) { + var updated = this._updateUniformLocation(location, floatArray); + if (updated) + this._glContext.uniform2fv(location, floatArray); + }, + + /** + * calls glUniform3fv only if the values are different than the previous call for this same shader program. + * @param {WebGLUniformLocation} location + * @param {Float32Array} floatArray + * @param {Number} numberOfArrays + */ + setUniformLocationWith3fv: function (location, floatArray, numberOfArrays) { + var updated = this._updateUniformLocation(location, floatArray); + if (updated) + this._glContext.uniform3fv(location, floatArray); + }, + + /** + * calls glUniform4fv only if the values are different than the previous call for this same shader program. + * @param {WebGLUniformLocation} location + * @param {Float32Array} floatArray + * @param {Number} numberOfArrays + */ + setUniformLocationWith4fv: function (location, floatArray, numberOfArrays) { + var updated = this._updateUniformLocation(location, floatArray); + if (updated) + this._glContext.uniform4fv(location, floatArray); + }, + + /** + * calls glUniformMatrix4fv only if the values are different than the previous call for this same shader program. + * @param {WebGLUniformLocation} location + * @param {Float32Array} matrixArray + * @param {Number} numberOfMatrices + */ + setUniformLocationWithMatrix4fv: function (location, matrixArray, numberOfMatrices) { + var updated = this._updateUniformLocation(location, matrixArray); + if (updated) + 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], matrixP.mat, 1); + this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_MVMATRIX], matrixMV.mat, 1); + this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_MVPMATRIX], 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], time / 10.0, time, time * 2, time * 4); + this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_SINTIME], time / 8.0, time / 4.0, time / 2.0, Math.sin(time)); + this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_COSTIME], time / 8.0, time / 4.0, time / 2.0, Math.cos(time)); + } + + if (this._uniforms[cc.UNIFORM_RANDOM01] !== -1) + this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_RANDOM01], 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], matrixP.mat, 1); + this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_MVMATRIX], node._renderCmd._stackMatrix.mat, 1); + this.setUniformLocationWithMatrix4fv(this._uniforms[cc.UNIFORM_MVPMATRIX], 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], time / 10.0, time, time * 2, time * 4); + this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_SINTIME], time / 8.0, time / 4.0, time / 2.0, Math.sin(time)); + this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_COSTIME], time / 8.0, time / 4.0, time / 2.0, Math.cos(time)); + } + + if (this._uniforms[cc.UNIFORM_RANDOM01] !== -1) + this.setUniformLocationWith4f(this._uniforms[cc.UNIFORM_RANDOM01], 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._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_MVPMATRIX], false, + 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._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_MVPMATRIX], false, swapMat4.mat); + }, + + setUniformForModelViewAndProjectionMatrixWithMat4: function () { + this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_MVMATRIX], false, cc.modelview_matrix_stack.top.mat); + this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX], false, cc.projection_matrix_stack.top.mat); + }, + + _setUniformForMVPMatrixWithMat4: function(modelViewMatrix){ + if(!modelViewMatrix) + throw new Error("modelView matrix is undefined."); + this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_MVMATRIX], false, modelViewMatrix.mat); + this._glContext.uniformMatrix4fv(this._uniforms[cc.UNIFORM_PMATRIX], false, cc.projection_matrix_stack.top.mat); + }, + + /** + * 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; + this._uniforms.length = 0; + + // it is already deallocated by android + //ccGLDeleteProgram(m_uProgram); + this._glContext.deleteProgram(this._programObj); + this._programObj = null; + + // Purge uniform hash + for (var i = 0; i < this._hashForUniforms.length; i++) { + this._hashForUniforms[i].value = null; + this._hashForUniforms[i] = null; + } + + this._hashForUniforms.length = 0; + }, + + /** + * 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(){ + if(cc.GLProgram._highpSupported == null){ + var ctx = cc._renderContext; + 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/cocos2d/shaders/CCGLStateCache.js b/cocos2d/shaders/CCGLStateCache.js new file mode 100644 index 00000000000..f048ce03e39 --- /dev/null +++ b/cocos2d/shaders/CCGLStateCache.js @@ -0,0 +1,340 @@ +/**************************************************************************** + 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; +cc._vertexAttribPosition = false; +cc._vertexAttribColor = false; +cc._vertexAttribTexCoords = false; + +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; + cc._vertexAttribPosition = false; + cc._vertexAttribColor = false; + cc._vertexAttribTexCoords = false; + 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 = function (program) { + if (program !== cc._currentShaderProgram) { + cc._currentShaderProgram = program; + cc._renderContext.useProgram(program); + } +}; + +if(!cc.ENABLE_GL_STATE_CACHE){ + cc.glUseProgram = 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); +}; + +/** + * 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 = function (sfactor, dfactor) { + if ((sfactor !== cc._blendingSource) || (dfactor !== cc._blendingDest)) { + cc._blendingSource = sfactor; + cc._blendingDest = dfactor; + cc.setBlending(sfactor, dfactor); + } +}; + +/** + * @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); + } +}; + +/** + * @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); + } + } +}; + +if(!cc.ENABLE_GL_STATE_CACHE){ + cc.glBlendFunc = cc.setBlending; +}; + +/** + * 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; +}; + +/** + *

+ * Will enable the vertex attribs that are passed as flags.
+ * Possible flags:
+ * cc.VERTEX_ATTRIB_FLAG_POSITION
+ * cc.VERTEX_ATTRIB_FLAG_COLOR
+ * cc.VERTEX_ATTRIB_FLAG_TEX_COORDS
+ *
+ * These flags can be ORed. The flags that are not present, will be disabled. + *

+ * @function + * @param {cc.VERTEX_ATTRIB_FLAG_POSITION | cc.VERTEX_ATTRIB_FLAG_COLOR | cc.VERTEX_ATTRIB_FLAG_TEX_OORDS} flags + */ +cc.glEnableVertexAttribs = function (flags) { + /* Position */ + var ctx = cc._renderContext; + var enablePosition = ( flags & cc.VERTEX_ATTRIB_FLAG_POSITION ); + if (enablePosition !== cc._vertexAttribPosition) { + if (enablePosition) + ctx.enableVertexAttribArray(cc.VERTEX_ATTRIB_POSITION); + else + ctx.disableVertexAttribArray(cc.VERTEX_ATTRIB_POSITION); + cc._vertexAttribPosition = enablePosition; + } + + /* Color */ + var enableColor = (flags & cc.VERTEX_ATTRIB_FLAG_COLOR); + if (enableColor !== cc._vertexAttribColor) { + if (enableColor) + ctx.enableVertexAttribArray(cc.VERTEX_ATTRIB_COLOR); + else + ctx.disableVertexAttribArray(cc.VERTEX_ATTRIB_COLOR); + cc._vertexAttribColor = enableColor; + } + + /* Tex Coords */ + var enableTexCoords = (flags & cc.VERTEX_ATTRIB_FLAG_TEX_COORDS); + if (enableTexCoords !== cc._vertexAttribTexCoords) { + if (enableTexCoords) + ctx.enableVertexAttribArray(cc.VERTEX_ATTRIB_TEX_COORDS); + else + ctx.disableVertexAttribArray(cc.VERTEX_ATTRIB_TEX_COORDS); + cc._vertexAttribTexCoords = enableTexCoords; + } +}; + +/** + * 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 = 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); +}; +if (!cc.ENABLE_GL_STATE_CACHE){ + cc.glBindTexture2DN = 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); +}; + +/** + * 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/cocos2d/shaders/CCShaderCache.js b/cocos2d/shaders/CCShaderCache.js new file mode 100644 index 00000000000..f78780ddb61 --- /dev/null +++ b/cocos2d/shaders/CCShaderCache.js @@ -0,0 +1,301 @@ +/**************************************************************************** + 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_MAX: 8, + + _programs: {}, + + _init: function () { + this.loadDefaultShaders(); + return true; + }, + + _loadDefaultShader: function (program, type) { + switch (type) { + case this.TYPE_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 this.TYPE_POSITION_TEXTURECOLOR_ALPHATEST: + 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 this.TYPE_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 this.TYPE_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 this.TYPE_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 this.TYPE_POSITION_TEXTURE_A8COLOR: + 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 this.TYPE_POSITION_UCOLOR: + program.initWithVertexShaderByteArray(cc.SHADER_POSITION_UCOLOR_VERT, cc.SHADER_POSITION_UCOLOR_FRAG); + program.addAttribute("aVertex", cc.VERTEX_ATTRIB_POSITION); + break; + case this.TYPE_POSITION_LENGTH_TEXTURECOLOR: + 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 () { + // Position Texture Color shader + var program = new cc.GLProgram(); + this._loadDefaultShader(program, this.TYPE_POSITION_TEXTURECOLOR); + this._programs[cc.SHADER_POSITION_TEXTURECOLOR] = program; + this._programs["ShaderPositionTextureColor"] = program; + + // Position Texture Color alpha test + program = new cc.GLProgram(); + this._loadDefaultShader(program, this.TYPE_POSITION_TEXTURECOLOR_ALPHATEST); + this._programs[cc.SHADER_POSITION_TEXTURECOLORALPHATEST] = program; + this._programs["ShaderPositionTextureColorAlphaTest"] = program; + + // + // Position, Color shader + // + program = new cc.GLProgram(); + this._loadDefaultShader(program, this.TYPE_POSITION_COLOR); + this._programs[cc.SHADER_POSITION_COLOR] = program; + this._programs["ShaderPositionColor"] = program; + + // + // Position Texture shader + // + program = new cc.GLProgram(); + this._loadDefaultShader(program, this.TYPE_POSITION_TEXTURE); + this._programs[cc.SHADER_POSITION_TEXTURE] = program; + this._programs["ShaderPositionTexture"] = program; + + // + // Position, Texture attribs, 1 Color as uniform shader + // + program = new cc.GLProgram(); + this._loadDefaultShader(program, this.TYPE_POSITION_TEXTURE_UCOLOR); + this._programs[cc.SHADER_POSITION_TEXTURE_UCOLOR] = program; + this._programs["ShaderPositionTextureUColor"] = program; + + // + // Position Texture A8 Color shader + // + program = new cc.GLProgram(); + this._loadDefaultShader(program, this.TYPE_POSITION_TEXTURE_A8COLOR); + this._programs[cc.SHADER_POSITION_TEXTUREA8COLOR] = program; + this._programs["ShaderPositionTextureA8Color"] = program; + + // + // Position and 1 color passed as a uniform (to similate glColor4ub ) + // + program = new cc.GLProgram(); + this._loadDefaultShader(program, this.TYPE_POSITION_UCOLOR); + this._programs[cc.SHADER_POSITION_UCOLOR] = program; + this._programs["ShaderPositionUColor"] = program; + + // + // Position, Legth(TexCoords, Color (used by Draw Node basically ) + // + program = new cc.GLProgram(); + this._loadDefaultShader(program, this.TYPE_POSITION_LENGTH_TEXTURECOLOR); + this._programs[cc.SHADER_POSITION_LENGTHTEXTURECOLOR] = program; + this._programs["ShaderPositionLengthTextureColor"] = program; + }, + + /** + * 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, this.TYPE_POSITION_TEXTURECOLOR); + + // Position Texture Color alpha test + program = this.programForKey(cc.SHADER_POSITION_TEXTURECOLORALPHATEST); + program.reset(); + this._loadDefaultShader(program, this.TYPE_POSITION_TEXTURECOLOR_ALPHATEST); + + // + // Position, Color shader + // + program = this.programForKey(cc.SHADER_POSITION_COLOR); + program.reset(); + this._loadDefaultShader(program, this.TYPE_POSITION_COLOR); + + // + // Position Texture shader + // + program = this.programForKey(cc.SHADER_POSITION_TEXTURE); + program.reset(); + this._loadDefaultShader(program, this.TYPE_POSITION_TEXTURE); + + // + // Position, Texture attribs, 1 Color as uniform shader + // + program = this.programForKey(cc.SHADER_POSITION_TEXTURE_UCOLOR); + program.reset(); + this._loadDefaultShader(program, this.TYPE_POSITION_TEXTURE_UCOLOR); + + // + // Position Texture A8 Color shader + // + program = this.programForKey(cc.SHADER_POSITION_TEXTUREA8COLOR); + program.reset(); + this._loadDefaultShader(program, this.TYPE_POSITION_TEXTURE_A8COLOR); + + // + // Position and 1 color passed as a uniform (to similate glColor4ub ) + // + program = this.programForKey(cc.SHADER_POSITION_UCOLOR); + program.reset(); + this._loadDefaultShader(program, this.TYPE_POSITION_UCOLOR); + }, + + /** + * returns a GL program for a given key + * @param {String} key + */ + programForKey: function (key) { + return this._programs[key]; + }, + + /** + * returns a GL program for a shader name + * @param {String} shaderName + * @return {cc.GLProgram} + */ + getProgram: function (shaderName) { + return this._programs[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; + } +}; \ No newline at end of file diff --git a/cocos2d/shaders/CCShaders.js b/cocos2d/shaders/CCShaders.js new file mode 100644 index 00000000000..437ce589aaa --- /dev/null +++ b/cocos2d/shaders/CCShaders.js @@ -0,0 +1,280 @@ +/**************************************************************************** + 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. + ****************************************************************************/ + +//-----------------------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_MVPMatrix * a_position; \n" + + " gl_Position = (CC_PMatrix * CC_MVMatrix) * 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_MVPMatrix * a_position; \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_MVPMatrix * a_position; \n" + + " gl_Position = (CC_PMatrix * CC_MVMatrix) * a_position; \n" + + " v_fragmentColor = a_color; \n" + + " v_texCoord = a_texCoord; \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" + + "}"; \ No newline at end of file diff --git a/cocos2d/shape-nodes/CCDrawNode.js b/cocos2d/shape-nodes/CCDrawNode.js new file mode 100644 index 00000000000..7eb69935ef8 --- /dev/null +++ b/cocos2d/shape-nodes/CCDrawNode.js @@ -0,0 +1,905 @@ +/**************************************************************************** + 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. + ****************************************************************************/ + +/** + * Code copied & pasted from SpacePatrol game https://github.com/slembcke/SpacePatrol + * + * Renamed and added some changes for cocos2d + * + */ +cc.v2fzero = function () { + return {x: 0, y: 0}; +}; + +cc.v2f = function (x, y) { + return {x: x, y: y}; +}; + +cc.v2fadd = function (v0, v1) { + return cc.v2f(v0.x + v1.x, v0.y + v1.y); +}; + +cc.v2fsub = function (v0, v1) { + return cc.v2f(v0.x - v1.x, v0.y - v1.y); +}; + +cc.v2fmult = function (v, s) { + return cc.v2f(v.x * s, v.y * s); +}; + +cc.v2fperp = function (p0) { + return cc.v2f(-p0.y, p0.x); +}; + +cc.v2fneg = function (p0) { + return cc.v2f(-p0.x, -p0.y); +}; + +cc.v2fdot = function (p0, p1) { + return p0.x * p1.x + p0.y * p1.y; +}; + +cc.v2fforangle = function (_a_) { + return cc.v2f(Math.cos(_a_), Math.sin(_a_)); +}; + +cc.v2fnormalize = function (p) { + var r = cc.pNormalize(cc.p(p.x, p.y)); + return cc.v2f(r.x, r.y); +}; + +cc.__v2f = function (v) { + return cc.v2f(v.x, v.y); +}; + +cc.__t = function (v) { + return {u: v.x, v: v.y}; +}; + +/** + *

CCDrawNode
+ * Node that draws dots, segments and polygons.
+ * Faster than the "drawing primitives" since they 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.once(cc.game.EVENT_RENDERER_INITED, function () { + 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.js.mixin(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(); + }, + + /** + * draws a rectangle given the origin and destination point measured in points. + * @param {cc.Vec2} origin + * @param {cc.Vec2} 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.Vec2} 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.Vec2} origin + * @param {cc.Vec2} control + * @param {cc.Vec2} 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.Vec2} origin + * @param {cc.Vec2} control1 + * @param {cc.Vec2} control2 + * @param {cc.Vec2} 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.Vec2} 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.Vec2} from + * @param {cc.Vec2} 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} fillColor + * @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} fillColor + * @param {Number} lineWidth + * @param {cc.Color} color + */ + drawPoly: function (verts, fillColor, lineWidth, color) { + 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, color); + }, + + /** + * 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) { + + cc.js.mixin(cc.DrawNode.prototype, { + _bufferCapacity:0, + + _trianglesArrayBuffer:null, + _trianglesWebBuffer:null, + _trianglesReader:null, + + _dirty:false, + _className:"DrawNodeWebGL", + + ctor:function () { + cc.Node.prototype.ctor.call(this); + this._buffer = []; + this._blendFunc = new cc.BlendFunc(cc.SRC_ALPHA, cc.ONE_MINUS_SRC_ALPHA); + this._drawColor = new cc.WebGLColor(255,255,255,255); + + this.init(); + }, + + init:function () { + if (cc.Node.prototype.init.call(this)) { + this.shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_LENGTHTEXTURECOLOR); + this._ensureCapacity(64); + this._trianglesWebBuffer = cc._renderContext.createBuffer(); + this._dirty = true; + return true; + } + return false; + }, + + 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)]; + if(fillColor == null) + this._drawSegments(vertices, lineWidth, lineColor, true); + else + this.drawPoly(vertices, fillColor, lineWidth, lineColor); + }, + + 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, vertices = [], i, len; + 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(cc.p(j, k)); + } + if (drawLineToCenter) + vertices.push(cc.p(center.x, center.y)); + + lineWidth *= 0.5; + for (i = 0, len = vertices.length; i < len - 1; i++) + this.drawSegment(vertices[i], vertices[i + 1], lineWidth, 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)); + this._drawSegments(vertices, lineWidth, color, false); + }, + + 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)); + this._drawSegments(vertices, lineWidth, color, false); + }, + + 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.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); + } + + lineWidth *= 0.5; + for (var j = 0, len = vertices.length; j < len - 1; j++) + this.drawSegment(vertices[j], vertices[j + 1], lineWidth, color); + }, + + _render:function () { + var gl = cc._renderContext; + + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX); + gl.bindBuffer(gl.ARRAY_BUFFER, this._trianglesWebBuffer); + if (this._dirty) { + gl.bufferData(gl.ARRAY_BUFFER, this._trianglesArrayBuffer, gl.STREAM_DRAW); + this._dirty = false; + } + var triangleSize = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT; + + // vertex + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, gl.FLOAT, false, triangleSize, 0); + // color + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, gl.UNSIGNED_BYTE, true, triangleSize, 8); + // texcood + gl.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, gl.FLOAT, false, triangleSize, 12); + + gl.drawArrays(gl.TRIANGLES, 0, this._buffer.length * 3); + cc.incrementGLDraws(1); + //cc.checkGLErrorDebug(); + }, + + _ensureCapacity:function(count){ + var _t = this; + var locBuffer = _t._buffer; + if(locBuffer.length + count > _t._bufferCapacity){ + var TriangleLength = cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT; + _t._bufferCapacity += Math.max(_t._bufferCapacity, count); + //re alloc + if((locBuffer == null) || (locBuffer.length === 0)){ + //init + _t._buffer = []; + _t._trianglesArrayBuffer = new ArrayBuffer(TriangleLength * _t._bufferCapacity); + _t._trianglesReader = new Uint8Array(_t._trianglesArrayBuffer); + } else { + var newTriangles = []; + var newArrayBuffer = new ArrayBuffer(TriangleLength * _t._bufferCapacity); + for(var i = 0; i < locBuffer.length;i++){ + newTriangles[i] = new cc.V2F_C4B_T2F_Triangle(locBuffer[i].a,locBuffer[i].b,locBuffer[i].c, + newArrayBuffer, i * TriangleLength); + } + _t._trianglesReader = new Uint8Array(newArrayBuffer); + _t._trianglesArrayBuffer = newArrayBuffer; + _t._buffer = newTriangles; + } + } + }, + + drawDot:function (pos, radius, color) { + color = color || this.getDrawColor(); + if (color.a == null) + color.a = 255; + var c4bColor = {r: 0 | color.r, g: 0 | color.g, b: 0 | color.b, a: 0 | color.a}; + var a = {vertices: {x: pos.x - radius, y: pos.y - radius}, colors: c4bColor, texCoords: {u: -1.0, v: -1.0}}; + var b = {vertices: {x: pos.x - radius, y: pos.y + radius}, colors: c4bColor, texCoords: {u: -1.0, v: 1.0}}; + var c = {vertices: {x: pos.x + radius, y: pos.y + radius}, colors: c4bColor, texCoords: {u: 1.0, v: 1.0}}; + var d = {vertices: {x: pos.x + radius, y: pos.y - radius}, colors: c4bColor, texCoords: {u: 1.0, v: -1.0}}; + + this._ensureCapacity(2*3); + + this._buffer.push(new cc.V2F_C4B_T2F_Triangle(a, b, c, this._trianglesArrayBuffer, this._buffer.length * cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT)); + this._buffer.push(new cc.V2F_C4B_T2F_Triangle(a, c, d, this._trianglesArrayBuffer, this._buffer.length * cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT)); + this._dirty = true; + }, + + 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); + }, + + 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; + this._ensureCapacity(vertexCount); + + var c4bColor = {r: 0 | color.r, g: 0 | color.g, b: 0 | color.b, a: 0 | color.a}; + var a = cc.__v2f(from), b = cc.__v2f(to); + var n = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(b, a))), t = cc.v2fperp(n); + var nw = cc.v2fmult(n, radius), tw = cc.v2fmult(t, radius); + + var v0 = cc.v2fsub(b, cc.v2fadd(nw, tw)); + var v1 = cc.v2fadd(b, cc.v2fsub(nw, tw)); + var v2 = cc.v2fsub(b, nw); + var v3 = cc.v2fadd(b, nw); + var v4 = cc.v2fsub(a, nw); + var v5 = cc.v2fadd(a, nw); + var v6 = cc.v2fsub(a, cc.v2fsub(nw, tw)); + var v7 = cc.v2fadd(a, cc.v2fadd(nw, tw)); + + var TriangleLength = cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT, triangleBuffer = this._trianglesArrayBuffer, locBuffer = this._buffer; + locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v0, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(cc.v2fadd(n, t)))}, + {vertices: v1, colors: c4bColor, texCoords: cc.__t(cc.v2fsub(n, t))}, {vertices: v2, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))}, + triangleBuffer, locBuffer.length * TriangleLength)); + + locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v3, colors: c4bColor, texCoords: cc.__t(n)}, + {vertices: v1, colors: c4bColor, texCoords: cc.__t(cc.v2fsub(n, t))}, {vertices: v2, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))}, + triangleBuffer, locBuffer.length * TriangleLength)); + + locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v3, colors: c4bColor, texCoords: cc.__t(n)}, + {vertices: v4, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))}, {vertices: v2, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))}, + triangleBuffer, locBuffer.length * TriangleLength)); + + locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v3, colors: c4bColor, texCoords: cc.__t(n)}, + {vertices: v4, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))}, {vertices: v5, colors: c4bColor, texCoords: cc.__t(n)}, + triangleBuffer, locBuffer.length * TriangleLength)); + + locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v6, colors: c4bColor, texCoords: cc.__t(cc.v2fsub(t, n))}, + {vertices: v4, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))}, {vertices: v5, colors: c4bColor, texCoords: cc.__t(n)}, + triangleBuffer, locBuffer.length * TriangleLength)); + + locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v6, colors: c4bColor, texCoords: cc.__t(cc.v2fsub(t, n))}, + {vertices: v7, colors: c4bColor, texCoords: cc.__t(cc.v2fadd(n, t))}, {vertices: v5, colors: c4bColor, texCoords: cc.__t(n)}, + triangleBuffer, locBuffer.length * TriangleLength)); + this._dirty = true; + }, + + drawPoly:function (verts, fillColor, borderWidth, borderColor) { + 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 c4bFillColor = {r: 0 | fillColor.r, g: 0 | fillColor.g, b: 0 | fillColor.b, a: 0 | fillColor.a}; + var c4bBorderColor = {r: 0 | borderColor.r, g: 0 | borderColor.g, b: 0 | borderColor.b, a: 0 | borderColor.a}; + var extrude = [], i, v0, v1, v2, count = verts.length; + for (i = 0; i < count; i++) { + v0 = cc.__v2f(verts[(i - 1 + count) % count]); + v1 = cc.__v2f(verts[i]); + v2 = cc.__v2f(verts[(i + 1) % count]); + var n1 = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(v1, v0))); + var n2 = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(v2, v1))); + var offset = cc.v2fmult(cc.v2fadd(n1, n2), 1.0 / (cc.v2fdot(n1, n2) + 1.0)); + extrude[i] = {offset: offset, n: n2}; + } + var outline = (borderWidth > 0.0), triangleCount = 3 * count - 2, vertexCount = 3 * triangleCount; + this._ensureCapacity(vertexCount); + + var triangleBytesLen = cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT, trianglesBuffer = this._trianglesArrayBuffer; + var locBuffer = this._buffer; + var inset = (outline == false ? 0.5 : 0.0); + for (i = 0; i < count - 2; i++) { + v0 = cc.v2fsub(cc.__v2f(verts[0]), cc.v2fmult(extrude[0].offset, inset)); + v1 = cc.v2fsub(cc.__v2f(verts[i + 1]), cc.v2fmult(extrude[i + 1].offset, inset)); + v2 = cc.v2fsub(cc.__v2f(verts[i + 2]), cc.v2fmult(extrude[i + 2].offset, inset)); + locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v0, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())}, + {vertices: v1, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())}, {vertices: v2, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())}, + trianglesBuffer, locBuffer.length * triangleBytesLen)); + } + + for (i = 0; i < count; i++) { + var j = (i + 1) % count; + v0 = cc.__v2f(verts[i]); + v1 = cc.__v2f(verts[j]); + + var n0 = extrude[i].n; + var offset0 = extrude[i].offset; + var offset1 = extrude[j].offset; + var inner0 = outline ? cc.v2fsub(v0, cc.v2fmult(offset0, borderWidth)) : cc.v2fsub(v0, cc.v2fmult(offset0, 0.5)); + var inner1 = outline ? cc.v2fsub(v1, cc.v2fmult(offset1, borderWidth)) : cc.v2fsub(v1, cc.v2fmult(offset1, 0.5)); + var outer0 = outline ? cc.v2fadd(v0, cc.v2fmult(offset0, borderWidth)) : cc.v2fadd(v0, cc.v2fmult(offset0, 0.5)); + var outer1 = outline ? cc.v2fadd(v1, cc.v2fmult(offset1, borderWidth)) : cc.v2fadd(v1, cc.v2fmult(offset1, 0.5)); + + if (outline) { + locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))}, + {vertices: inner1, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))}, {vertices: outer1, colors: c4bBorderColor, texCoords: cc.__t(n0)}, + trianglesBuffer, locBuffer.length * triangleBytesLen)); + locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))}, + {vertices: outer0, colors: c4bBorderColor, texCoords: cc.__t(n0)}, {vertices: outer1, colors: c4bBorderColor, texCoords: cc.__t(n0)}, + trianglesBuffer, locBuffer.length * triangleBytesLen)); + } else { + locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())}, + {vertices: inner1, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())}, {vertices: outer1, colors: c4bFillColor, texCoords: cc.__t(n0)}, + trianglesBuffer, locBuffer.length * triangleBytesLen)); + locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())}, + {vertices: outer0, colors: c4bFillColor, texCoords: cc.__t(n0)}, {vertices: outer1, colors: c4bFillColor, texCoords: cc.__t(n0)}, + trianglesBuffer, locBuffer.length * triangleBytesLen)); + } + } + extrude = null; + this._dirty = true; + }, + + _drawSegments: function(verts, borderWidth, borderColor, closePoly){ + borderWidth = (borderWidth == null) ? this._lineWidth : borderWidth; + borderColor = borderColor || this._drawColor; + if(borderColor.a == null) + borderColor.a = 255; + borderWidth *= 0.5; + if (borderWidth <= 0) + return; + + var c4bBorderColor = {r: 0 | borderColor.r, g: 0 | borderColor.g, b: 0 | borderColor.b, a: 0 | borderColor.a }; + var extrude = [], i, v0, v1, v2, count = verts.length; + for (i = 0; i < count; i++) { + v0 = cc.__v2f(verts[(i - 1 + count) % count]); + v1 = cc.__v2f(verts[i]); + v2 = cc.__v2f(verts[(i + 1) % count]); + var n1 = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(v1, v0))); + var n2 = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(v2, v1))); + var offset = cc.v2fmult(cc.v2fadd(n1, n2), 1.0 / (cc.v2fdot(n1, n2) + 1.0)); + extrude[i] = {offset: offset, n: n2}; + } + + var triangleCount = 3 * count - 2, vertexCount = 3 * triangleCount; + this._ensureCapacity(vertexCount); + + var triangleBytesLen = cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT, trianglesBuffer = this._trianglesArrayBuffer; + var locBuffer = this._buffer; + var len = closePoly ? count : count - 1; + for (i = 0; i < len; i++) { + var j = (i + 1) % count; + v0 = cc.__v2f(verts[i]); + v1 = cc.__v2f(verts[j]); + + var n0 = extrude[i].n; + var offset0 = extrude[i].offset; + var offset1 = extrude[j].offset; + var inner0 = cc.v2fsub(v0, cc.v2fmult(offset0, borderWidth)); + var inner1 = cc.v2fsub(v1, cc.v2fmult(offset1, borderWidth)); + var outer0 = cc.v2fadd(v0, cc.v2fmult(offset0, borderWidth)); + var outer1 = cc.v2fadd(v1, cc.v2fmult(offset1, borderWidth)); + locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))}, + {vertices: inner1, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))}, {vertices: outer1, colors: c4bBorderColor, texCoords: cc.__t(n0)}, + trianglesBuffer, locBuffer.length * triangleBytesLen)); + locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))}, + {vertices: outer0, colors: c4bBorderColor, texCoords: cc.__t(n0)}, {vertices: outer1, colors: c4bBorderColor, texCoords: cc.__t(n0)}, + trianglesBuffer, locBuffer.length * triangleBytesLen)); + } + extrude = null; + this._dirty = true; + }, + + clear:function () { + this._buffer.length = 0; + this._dirty = true; + }, + + _createRenderCmd: function () { + return new cc.DrawNode.WebGLRenderCmd(this); + } + }); + } +}); diff --git a/cocos2d/shape-nodes/CCDrawNodeCanvasRenderCmd.js b/cocos2d/shape-nodes/CCDrawNodeCanvasRenderCmd.js new file mode 100644 index 00000000000..4cf153766ee --- /dev/null +++ b/cocos2d/shape-nodes/CCDrawNodeCanvasRenderCmd.js @@ -0,0 +1,131 @@ +/**************************************************************************** + 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){ + cc.Node.CanvasRenderCmd.call(this, 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.js.mixin( cc.DrawNode.CanvasRenderCmd.prototype, { + rendering: function (ctx, scaleX, scaleY) { + var wrapper = ctx || cc._renderContext, context = wrapper.getContext(), node = this._node; + var alpha = node._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, scaleX, scaleY) { + 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 * scaleX, -locPos.y * scaleY, locRadius * scaleX, 0, Math.PI * 2, false); + ctx.closePath(); + ctx.fill(); + }, + + _drawSegment: function (wrapper, element, scaleX, scaleY) { + 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 * scaleX, -locFrom.y * scaleY); + ctx.lineTo(locTo.x * scaleX, -locTo.y * scaleY); + ctx.stroke(); + }, + + _drawPoly: function (wrapper, element, scaleX, scaleY) { + 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 * scaleX, -firstPoint.y * scaleY); + for (var i = 1, len = locVertices.length; i < len; i++) + ctx.lineTo(locVertices[i].x * scaleX, -locVertices[i].y * scaleY); + + if (locIsClosePolygon) + ctx.closePath(); + if (locIsFill) + ctx.fill(); + if (locIsStroke) + ctx.stroke(); + } + }); + +})(); \ No newline at end of file diff --git a/cocos2d/shape-nodes/CCDrawNodeWebGLRenderCmd.js b/cocos2d/shape-nodes/CCDrawNodeWebGLRenderCmd.js new file mode 100644 index 00000000000..2c393c6cdb0 --- /dev/null +++ b/cocos2d/shape-nodes/CCDrawNodeWebGLRenderCmd.js @@ -0,0 +1,41 @@ +/**************************************************************************** + 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) { + cc.Node.WebGLRenderCmd.call(this, renderableObject); + this._needDraw = true; + }; + + 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; + cc.glBlendFunc(node._blendFunc.src, node._blendFunc.dst); + this._shaderProgram.use(); + this._shaderProgram._setUniformForMVPMatrixWithMat4(this._stackMatrix); + node._render(); + }; +})(); \ No newline at end of file diff --git a/cocos2d/text-input/CCIMEDispatcher.js b/cocos2d/text-input/CCIMEDispatcher.js new file mode 100644 index 00000000000..f3e098ef0d1 --- /dev/null +++ b/cocos2d/text-input/CCIMEDispatcher.js @@ -0,0 +1,534 @@ +/**************************************************************************** + 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 funciton .So need use the Window.prototype.prompt + var userInput; + if(window.Window && Window.prototype.prompt != prompt){ + userInput = Window.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.js.array.remove(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/cocos2d/text-input/CCTextFieldTTF.js b/cocos2d/text-input/CCTextFieldTTF.js new file mode 100644 index 00000000000..cca87939648 --- /dev/null +++ b/cocos2d/text-input/CCTextFieldTTF.js @@ -0,0 +1,500 @@ +/**************************************************************************** + 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.TextAlignment.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.js.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.js.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.TextAlignment.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); + }, + + /** + * Recursive method that visit its children and draw them. + * @param {CanvasRenderingContext2D|WebGLRenderingContext} ctx + */ + visit: function(ctx){ + this._super(ctx); + }, + + ////////////////////////////////////////////////////////////////////////// + // 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/cocos2d/tilemap/CCTGAlib.js b/cocos2d/tilemap/CCTGAlib.js new file mode 100644 index 00000000000..d5ed229a063 --- /dev/null +++ b/cocos2d/tilemap/CCTGAlib.js @@ -0,0 +1,437 @@ +/**************************************************************************** + 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/cocos2d/tilemap/CCTMXLayer.js b/cocos2d/tilemap/CCTMXLayer.js new file mode 100644 index 00000000000..e0c88cba97b --- /dev/null +++ b/cocos2d/tilemap/CCTMXLayer.js @@ -0,0 +1,914 @@ +/**************************************************************************** + 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: "", + + //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); + + 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); + }, + + /** + * Sets the untransformed size of the TMXLayer. + * @override + * @param {cc.Size|Number} size The untransformed size of the TMXLayer or The untransformed size's width of the TMXLayer. + * @param {Number} [height] The untransformed size's height of the TMXLayer. + */ + setContentSize:function (size, height) { + cc.Node.prototype.setContentSize.call(this, size, height); + this._renderCmd._updateCacheContext(size, height); + }, + + /** + * Return texture of cc.SpriteBatchNode + * @function + * @return {cc.Texture2D} + */ + getTexture: function(){ + return this._renderCmd.getTexture(); + }, + + /** + * 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; + }, + + /** + * 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) { + // XXX: is 35% a good estimate ? + var size = layerInfo._layerSize; + var totalNumberOfTiles = parseInt(size.width * size.height); + var capacity = totalNumberOfTiles * 0.35 + 1; // 35 percent is occupied ? + var texture; + if (tilesetInfo) + texture = cc.textureCache.addImage(tilesetInfo.sourceImage); + + if (this.initWithTexture(texture, capacity)) { + // layerInfo + this.layerName = layerInfo.name; + this._layerSize = size; + this.tiles = layerInfo._tiles; + this._minGID = layerInfo._minGID; + this._maxGID = layerInfo._maxGID; + this._opacity = layerInfo._opacity; + this.properties = layerInfo.properties; + this._contentScaleFactor = cc.director.getContentScaleFactor(); + + // tilesetInfo + this.tileset = tilesetInfo; + + // mapInfo + this._mapTileSize = mapInfo.getTileSize(); + this.layerOrientation = mapInfo.orientation; + + // offset (after layer orientation is set); + var offset = this._calculateLayerOffset(layerInfo.offset); + this.setPosition(cc.pointPixelsToPoints(offset)); + + this._atlasIndexArray = []; + 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; + } + return false; + }, + + /** + *

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 () { + if (this.tiles) + this.tiles = null; + + if (this._atlasIndexArray) + this._atlasIndexArray = null; + }, + + /** + *

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.Vec2|Number} pos or x + * @param {Number} [y] + * @return {cc.Sprite} + */ + getTileAt: function (pos, y) { + if(!pos) + throw new Error("cc.TMXLayer.getTileAt(): 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.getTileAt(): invalid position"); + if(!this.tiles || !this._atlasIndexArray){ + cc.log("cc.TMXLayer.getTileAt(): TMXLayer: the tiles map has been released"); + return null; + } + + var tile = null, gid = this.getTileGIDAt(pos); + + // if GID == 0, then no tile is present + if (gid === 0) + return tile; + + var z = 0 | (pos.x + pos.y * this._layerSize.width); + tile = this.getChildByTag(z); + // tile not created yet. create it + if (!tile) { + var rect = this.tileset.rectForGID(gid); + rect = cc.rectPixelsToPoints(rect); + + tile = new cc.Sprite(); + tile.initWithTexture(this.texture, rect); + tile.batchNode = this; + tile.setPosition(this.getPositionAt(pos)); + tile.vertexZ = this._vertexZForPos(pos); + tile.anchorX = 0; + tile.anchorY = 0; + tile.opacity = this._opacity; + + var indexForZ = this._atlasIndexForExistantZ(z); + this.addSpriteWithoutQuad(tile, indexForZ, 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.Vec2|Number} pos or x + * @param {Number} [y] + * @return {Number} + */ + getTileGIDAt:function (pos, y) { + if(pos == null) + throw new Error("cc.TMXLayer.getTileGIDAt(): 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.getTileGIDAt(): invalid position"); + if(!this.tiles || !this._atlasIndexArray){ + cc.log("cc.TMXLayer.getTileGIDAt(): 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_MASK) >>> 0; + }, + // XXX: deprecated + // tileGIDAt:getTileGIDAt, + + /** + * lipped tiles can be changed dynamically + * @param {cc.Vec2|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 || !this._atlasIndexArray){ + 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, + + /** + *

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.Vec2|Number} posOrX position or x + * @param {Number} flagsOrY flags or y + * @param {Number} [flags] + */ + setTileGID: function(gid, posOrX, flagsOrY, flags) { + if(!posOrX) + 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 || !this._atlasIndexArray){ + 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; + this._setNodeDirtyForCache(); + 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._insertTileForGID(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.tileset.rectForGID(gid); + rect = cc.rectPixelsToPoints(rect); + + sprite.setTextureRect(rect, false); + if (flags != null) + this._setupTileSprite(sprite, pos, gidAndFlags); + + this.tiles[z] = gidAndFlags; + } else + this._updateTileForGID(gidAndFlags, pos); + } + } + }, + + /** + * Removes a tile at given tile coordinate + * @param {cc.Vec2|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 || !this._atlasIndexArray){ + cc.log("cc.TMXLayer.removeTileAt(): TMXLayer: the tiles map has been released"); + return; + } + + var gid = this.getTileGIDAt(pos); + if (gid !== 0) { + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) + this._setNodeDirtyForCache(); + var z = 0 | (pos.x + pos.y * this._layerSize.width); + var atlasIndex = this._atlasIndexForExistantZ(z); + // remove tile from GID map + this.tiles[z] = 0; + + // remove tile from atlas position array + this._atlasIndexArray.splice(atlasIndex, 1); + + // remove it from sprites and/or texture atlas + var sprite = this.getChildByTag(z); + + if (sprite) + cc.SpriteBatchNode.prototype.removeChild.call(this, sprite, true); //this.removeChild(sprite, true); + else { + if(cc._renderType === cc.game.RENDER_TYPE_WEBGL) + this.textureAtlas.removeQuadAtIndex(atlasIndex); + + // update possible children + if (this._children) { + var locChildren = this._children; + for (var i = 0, len = locChildren.length; i < len; i++) { + var child = locChildren[i]; + if (child) { + var ai = child.atlasIndex; + if (ai >= atlasIndex) + child.atlasIndex = ai - 1; + } + } + } + } + } + }, + + /** + * Returns the position in pixels of a given tile coordinate + * @param {cc.Vec2|Number} pos position or x + * @param {Number} [y] + * @return {cc.Vec2} + */ + 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, + + /** + * Return the value for the specific property name + * @param {String} propertyName + * @return {*} + */ + getProperty:function (propertyName) { + return this.properties[propertyName]; + }, + + /** + * Creates the tiles + */ + setupTiles:function () { + // Optimization: quick hack that sets the image size on the tileset + this._renderCmd.initImageSize(); + + // Parse cocos2d properties + this._parseInternalProperties(); + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) + this._setNodeDirtyForCache(); + + var locLayerHeight = this._layerSize.height, locLayerWidth = this._layerSize.width; + for (var y = 0; y < locLayerHeight; y++) { + for (var x = 0; x < locLayerWidth; x++) { + var pos = x + locLayerWidth * y; + var gid = this.tiles[pos]; + + // XXX: gid == 0 -. empty tile + if (gid !== 0) { + this._appendTileForGID(gid, cc.p(x, y)); + // Optimization: update min and max GID rendered by the layer + this._minGID = Math.min(gid, this._minGID); + this._maxGID = Math.max(gid, this._maxGID); + } + } + } + + if (!((this._maxGID >= this.tileset.firstGid) && (this._minGID >= this.tileset.firstGid))) { + cc.log("cocos2d:TMX: Only 1 tileset per layer is supported"); + } + }, + + /** + * cc.TMXLayer doesn't support adding a cc.Sprite manually. + * @warning addChild(child); is not supported on cc.TMXLayer. Instead of setTileGID. + * @param {cc.Node} child + * @param {number} zOrder + * @param {number} tag + */ + addChild:function (child, zOrder, tag) { + cc.log("addChild: is not supported on cc.TMXLayer. Instead use setTileGID or tileAt."); + }, + + /** + * Remove child + * @param {cc.Sprite} sprite + * @param {Boolean} cleanup + */ + removeChild:function (sprite, cleanup) { + // allows removing nil objects + if (!sprite) + return; + + if(this._children.indexOf(sprite) === -1){ + cc.log("cc.TMXLayer.removeChild(): Tile does not belong to TMXLayer"); + return; + } + + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) + this._setNodeDirtyForCache(); + var atlasIndex = sprite.atlasIndex; + var zz = this._atlasIndexArray[atlasIndex]; + this.tiles[zz] = 0; + this._atlasIndexArray.splice(atlasIndex, 1); + cc.SpriteBatchNode.prototype.removeChild.call(this, sprite, cleanup); + cc.renderer.childrenOrderDirty = true; + }, + + /** + * Gets the layer name + * @return {String} + */ + getLayerName:function () { + return this.layerName; + }, + + /** + * Set the layer name + * @param {String} layerName + */ + setLayerName:function (layerName) { + this.layerName = layerName; + }, + + _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; + }, + + _appendTileForGID:function (gid, pos) { + var rect = this.tileset.rectForGID(gid); + rect = cc.rectPixelsToPoints(rect); + + var z = 0 | (pos.x + pos.y * this._layerSize.width); + var tile = this._renderCmd._reusedTileWithRect(rect); + this._setupTileSprite(tile, pos, gid); + + // optimization: + // The difference between appendTileForGID and insertTileforGID is that append is faster, since + // it appends the tile at the end of the texture atlas + var indexForZ = this._atlasIndexArray.length; + + // don't add it using the "standard" way. + this.insertQuadFromSprite(tile, indexForZ); + + // append should be after addQuadFromSprite since it modifies the quantity values + this._atlasIndexArray.splice(indexForZ, 0, z); + return tile; + }, + + _insertTileForGID:function (gid, pos) { + var rect = this.tileset.rectForGID(gid); + rect = cc.rectPixelsToPoints(rect); + + var z = 0 | (pos.x + pos.y * this._layerSize.width); + var tile = this._renderCmd._reusedTileWithRect(rect); + this._setupTileSprite(tile, pos, gid); + + // get atlas index + var indexForZ = this._atlasIndexForNewZ(z); + + // Optimization: add the quad without adding a child + this.insertQuadFromSprite(tile, indexForZ); + + // insert it into the local atlasindex array + this._atlasIndexArray.splice(indexForZ, 0, z); + // update possible children + if (this._children) { + var locChildren = this._children; + for (var i = 0, len = locChildren.length; i < len; i++) { + var child = locChildren[i]; + if (child) { + var ai = child.atlasIndex; + if (ai >= indexForZ) + child.atlasIndex = ai + 1; + } + } + } + this.tiles[z] = gid; + return tile; + }, + + _updateTileForGID:function (gid, pos) { + var rect = this.tileset.rectForGID(gid); + var locScaleFactor = this._contentScaleFactor; + rect = cc.rect(rect.x / locScaleFactor, rect.y / locScaleFactor, + rect.width / locScaleFactor, rect.height / locScaleFactor); + var z = pos.x + pos.y * this._layerSize.width; + + var tile = this._renderCmd._reusedTileWithRect(rect); + this._setupTileSprite(tile, pos, gid); + + // get atlas index + tile.atlasIndex = this._atlasIndexForExistantZ(z); + tile.dirty = true; + tile.updateTransform(); + this.tiles[z] = gid; + + return tile; + }, + + //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_POSITION_TEXTURECOLORALPHATEST); + var alphaValueLocation = cc._renderContext.getUniformLocation(this.shaderProgram.getProgram(), cc.UNIFORM_ALPHA_TEST_VALUE_S); + // NOTE: alpha test shader is hard-coded to use the equivalent of a glAlphaFunc(GL_GREATER) comparison + this.shaderProgram.use(); + this.shaderProgram.setUniformLocationWith1f(alphaValueLocation, alphaFuncValue); + } + } else + this._vertexZvalue = parseInt(vertexz, 10); + } + }, + + _setupTileSprite:function (sprite, pos, gid) { + var z = pos.x + pos.y * this._layerSize.width; + sprite.setPosition(this.getPositionAt(pos)); + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) + sprite.vertexZ = this._vertexZForPos(pos); + else + sprite.tag = z; + + sprite.anchorX = 0; + sprite.anchorY = 0; + sprite.opacity = this._opacity; + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { + sprite.rotation = 0.0; + } + + sprite.setFlippedX(false); + sprite.setFlippedY(false); + + // 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.anchorX = 0.5; + sprite.anchorY = 0.5; + sprite.x = this.getPositionAt(pos).x + sprite.width / 2; + sprite.y = this.getPositionAt(pos).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.rotation = 90; + else if (flag === cc.TMX_TILE_VERTICAL_FLAG) + sprite.rotation = 270; + else if (flag === (cc.TMX_TILE_VERTICAL_FLAG | cc.TMX_TILE_HORIZONTAL_FLAG) >>> 0) { + sprite.rotation = 90; + sprite.setFlippedX(true); + } else { + sprite.rotation = 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 (pos) { + 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 - (pos.x + pos.y)); + break; + case cc.TMX_ORIENTATION_ORTHO: + ret = -(this._layerSize.height - pos.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; + }, + + _atlasIndexForExistantZ:function (z) { + var item; + if (this._atlasIndexArray) { + var locAtlasIndexArray = this._atlasIndexArray; + for (var i = 0, len = locAtlasIndexArray.length; i < len; i++) { + item = locAtlasIndexArray[i]; + if (item === z) + break; + } + } + if(!cc.js.isNumber(item)) + cc.log("cc.TMXLayer._atlasIndexForExistantZ(): TMX atlas index not found. Shall not happen"); + return i; + }, + + _atlasIndexForNewZ:function (z) { + var locAtlasIndexArray = this._atlasIndexArray; + for (var i = 0, len = locAtlasIndexArray.length; i < len; i++) { + var val = locAtlasIndexArray[i]; + if (z < val) + break; + } + return i; + } +}); + +var _p = cc.TMXLayer.prototype; + +/** @expose */ +cc.defineGetterSetter(_p, "texture", _p.getTexture, _p.setTexture); + +// 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/cocos2d/tilemap/CCTMXLayerCanvasRenderCmd.js b/cocos2d/tilemap/CCTMXLayerCanvasRenderCmd.js new file mode 100644 index 00000000000..79a1ff197d6 --- /dev/null +++ b/cocos2d/tilemap/CCTMXLayerCanvasRenderCmd.js @@ -0,0 +1,220 @@ +/**************************************************************************** + 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){ + cc.SpriteBatchNode.CanvasRenderCmd.call(this, renderable); + this._needDraw = true; + this._realWorldTransform = {a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0}; + + var locCanvas = cc._canvas; + var tmpCanvas = document.createElement('canvas'); + tmpCanvas.width = locCanvas.width; + tmpCanvas.height = locCanvas.height; + this._cacheCanvas = tmpCanvas; + this._cacheContext = new cc.CanvasContextWrapper(this._cacheCanvas.getContext('2d')); + var tempTexture = new cc.Texture2D(); + tempTexture.initWithElement(tmpCanvas); + tempTexture.handleLoadedTexture(); + this._cacheTexture = tempTexture; + // This class uses cache, so its default cachedParent should be himself + this._cacheDirty = false; + }; + + var proto = cc.TMXLayer.CanvasRenderCmd.prototype = Object.create(cc.SpriteBatchNode.CanvasRenderCmd.prototype); + proto.constructor = cc.TMXLayer.CanvasRenderCmd; + + //set the cache dirty flag for canvas + proto._setNodeDirtyForCache = function () { + this._cacheDirty = true; + }; + + proto._renderingChildToCache = function () { + if (this._cacheDirty) { + var wrapper = this._cacheContext, + context = wrapper.getContext(), locCanvas = this._cacheCanvas; + + //wrapper.save(); + context.setTransform(1, 0, 0, 1, 0, 0); + context.clearRect(0, 0, locCanvas.width, locCanvas.height); + //reset the cache context + + var locChildren = this._node._children; + for (var i = 0, len = locChildren.length; i < len; i++) { + if (locChildren[i]){ + var selCmd = locChildren[i]._renderCmd; + if(selCmd){ + selCmd.rendering(wrapper, 1, 1); + selCmd._cacheDirty = false; + } + } + } + + //wrapper.restore(); + this._cacheDirty = false; + } + }; + + proto.rendering = function (ctx, scaleX, scaleY) { + var alpha = this._displayedOpacity / 255; + if (alpha <= 0) + return; + + var node = this._node; + this._renderingChildToCache(); + var wrapper = ctx || cc._renderContext, context = wrapper.getContext(); + wrapper.setGlobalAlpha(alpha); + + var locCacheCanvas = this._cacheCanvas; + //direct draw image by canvas drawImage + if (locCacheCanvas && locCacheCanvas.width !== 0 && locCacheCanvas.height !== 0) { + wrapper.setTransform(this._realWorldTransform, scaleX, scaleY); + var locCanvasHeight = locCacheCanvas.height * scaleY; + if (node.layerOrientation === cc.TMX_ORIENTATION_HEX) { + var halfTileSize = node._mapTileSize.height * 0.5 * scaleY; + context.drawImage(locCacheCanvas, 0, 0, locCacheCanvas.width, locCacheCanvas.height, + 0, -locCanvasHeight + halfTileSize, locCacheCanvas.width * scaleX, locCanvasHeight); + } else { + context.drawImage(locCacheCanvas, 0, 0, locCacheCanvas.width, locCacheCanvas.height, + 0, -locCanvasHeight, locCacheCanvas.width * scaleX, locCanvasHeight); + } + } + cc.g_NumberOfDraws++; + }; + + proto._updateCacheContext = function(size, height){ + var node = this._node, + locContentSize = node._contentSize, + locCanvas = this._cacheCanvas, + scaleFactor = cc.contentScaleFactor(); + locCanvas.width = 0 | (locContentSize.width * 1.5 * scaleFactor); + locCanvas.height = 0 | (locContentSize.height * 1.5 * scaleFactor); + + //todo: need change the wrapper's height + if(node.layerOrientation === cc.TMX_ORIENTATION_HEX) + this._cacheContext.setOffset(0, -node._mapTileSize.height * 0.5); //translate for hexagonal + else + this._cacheContext.setOffset(0, 0); + var locTexContentSize = this._cacheTexture._contentSize; + locTexContentSize.width = locCanvas.width; + locTexContentSize.height = locCanvas.height; + }; + + proto.getTexture = function(){ + return this._cacheTexture; + }; + + proto.visit = function(parentCmd){ + var node = this._node; + //TODO: it will implement dynamic compute child cutting automation. + var i, len, locChildren = node._children; + // quick return if not visible + if (!node._visible || !locChildren || locChildren.length === 0) + return; + + parentCmd = parentCmd || this.getParentRenderCmd(); + if (parentCmd) + this._curLevel = parentCmd._curLevel + 1; + + this._syncStatus(parentCmd); + if (this._cacheDirty) { + var wrapper = this._cacheContext, locCanvas = this._cacheCanvas, context = wrapper.getContext(), + instanceID = node.__instanceId, renderer = cc.renderer; + //begin cache + renderer._turnToCacheMode(instanceID); + + node.sortAllChildren(); + for (i = 0, len = locChildren.length; i < len; i++) { + if (locChildren[i]){ + var selCmd = locChildren[i]._renderCmd; + if(selCmd){ + selCmd.visit(this); + selCmd._cacheDirty = false; + } + } + } + + //wrapper.save(); + context.setTransform(1, 0, 0, 1, 0, 0); + context.clearRect(0, 0, locCanvas.width, locCanvas.height); + //set the wrapper's offset + + //draw to cache canvas + renderer._renderingToCacheCanvas(wrapper, instanceID); + //wrapper.restore(); //todo: it can be reserve. + this._cacheDirty = false + } + cc.renderer.pushRenderCommand(this); + this._dirtyFlag = 0; + }; + + proto.transform = function (parentCmd, recursive) { + // transform for canvas + var t = this.getNodeToParentTransform(), + worldT = this._realWorldTransform; //get the world transform + + if (parentCmd) { + var pt = parentCmd._worldTransform; + // cc.AffineTransformConcat is incorrect at get world transform + worldT.a = t.a * pt.a + t.b * pt.c; //a + worldT.b = t.a * pt.b + t.b * pt.d; //b + worldT.c = t.c * pt.a + t.d * pt.c; //c + worldT.d = t.c * pt.b + t.d * pt.d; //d + + worldT.tx = pt.a * t.tx + pt.c * t.ty + pt.tx; + worldT.ty = pt.d * t.ty + pt.ty + pt.b * t.tx; + } else { + worldT.a = t.a; + worldT.b = t.b; + worldT.c = t.c; + worldT.d = t.d; + worldT.tx = t.tx; + worldT.ty = t.ty; + } + if (recursive) { + var locChildren = this._node._children; + if (!locChildren || locChildren.length === 0) + return; + var i, len; + for (i = 0, len = locChildren.length; i < len; i++) { + locChildren[i]._renderCmd.transform(this, recursive); + } + } + }; + + proto.initImageSize = function(){ + var node = this._node; + node.tileset.imageSize = this._texture.getContentSizeInPixels(); + }; + + proto._reusedTileWithRect = function(rect){ + var node = this._node; + node._reusedTile = new cc.Sprite(); + node._reusedTile.initWithTexture(this._texture, rect, false); + node._reusedTile.batchNode = node; + node._reusedTile.parent = node; + node._reusedTile._renderCmd._cachedParent = node._renderCmd; + return node._reusedTile; + }; +})(); \ No newline at end of file diff --git a/cocos2d/tilemap/CCTMXLayerWebGLRenderCmd.js b/cocos2d/tilemap/CCTMXLayerWebGLRenderCmd.js new file mode 100644 index 00000000000..8c1906f6401 --- /dev/null +++ b/cocos2d/tilemap/CCTMXLayerWebGLRenderCmd.js @@ -0,0 +1,67 @@ +/**************************************************************************** + 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){ + cc.SpriteBatchNode.WebGLRenderCmd.call(this, renderableObject); + this._needDraw = true; + }; + + var proto = cc.TMXLayer.WebGLRenderCmd.prototype = Object.create(cc.SpriteBatchNode.WebGLRenderCmd.prototype); + proto.constructor = cc.TMXLayer.WebGLRenderCmd; + + proto._updateCacheContext = function(){}; + + proto.initImageSize = function(){ + var node = this._node; + node.tileset.imageSize = this._textureAtlas.texture.getContentSizeInPixels(); + + // By default all the tiles are aliased + // pros: + // - easier to render + // cons: + // - difficult to scale / rotate / etc. + this._textureAtlas.texture.setAliasTexParameters(); + }; + + proto._reusedTileWithRect = function(rect){ + var node = this._node; + if (!node._reusedTile) { + node._reusedTile = new cc.Sprite(); + node._reusedTile.initWithTexture(node.texture, rect, false); + node._reusedTile.batchNode = node; + } else { + // XXX HACK: Needed because if "batch node" is nil, + // then the Sprite'squad will be reset + node._reusedTile.batchNode = null; + + // Re-init the sprite + node._reusedTile.setTextureRect(rect, false); + + // restore the batch node + node._reusedTile.batchNode = node; + } + return node._reusedTile; + }; +})(); diff --git a/cocos2d/tilemap/CCTMXObjectGroup.js b/cocos2d/tilemap/CCTMXObjectGroup.js new file mode 100644 index 00000000000..6589d7c607b --- /dev/null +++ b/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.Vec2} + */ + getPositionOffset:function () { + return cc.p(this._positionOffset); + }, + + /** + * Offset position of child objects + * @param {cc.Vec2} 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/cocos2d/tilemap/CCTMXTiledMap.js b/cocos2d/tilemap/CCTMXTiledMap.js new file mode 100644 index 00000000000..fe14fa788f3 --- /dev/null +++ b/cocos2d/tilemap/CCTMXTiledMap.js @@ -0,0 +1,479 @@ +/**************************************************************************** + 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.
+ * - Embeded 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; + layer.setupTiles(); + 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/cocos2d/tilemap/CCTMXXMLParser.js b/cocos2d/tilemap/CCTMXXMLParser.js new file mode 100644 index 00000000000..de9a70090b2 --- /dev/null +++ b/cocos2d/tilemap/CCTMXXMLParser.js @@ -0,0 +1,950 @@ +/**************************************************************************** + 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 = []; + 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) { + var rect = 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; + } + switch (compression) { + case 'gzip': + layer._tiles = cc.unzipBase64AsArray(nodeValue, 4); + break; + case 'zlib': + var inflator = new Zlib.Inflate(cc.Codec.Base64.decodeAsArray(nodeValue, 1)); + layer._tiles = cc.uint8ArrayToUint32Array(inflator.decompress()); + break; + case null: + case '': + // Uncompressed + if (encoding === "base64") + layer._tiles = cc.Codec.Base64.decodeAsArray(nodeValue, 4); + else if (encoding === "csv") { + layer._tiles = []; + var csvTiles = nodeValue.split(','); + for (var csvIdx = 0; csvIdx < csvTiles.length; csvIdx++) + layer._tiles.push(parseInt(csvTiles[csvIdx])); + } else { + //XML format + var selDataTiles = data.getElementsByTagName("tile"); + layer._tiles = []; + for (var xmlIdx = 0; xmlIdx < selDataTiles.length; xmlIdx++) + layer._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; + } + + // 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/cocos2d/transitions/CCTransition.js b/cocos2d/transitions/CCTransition.js new file mode 100644 index 00000000000..949893ba2f5 --- /dev/null +++ b/cocos2d/transitions/CCTransition.js @@ -0,0 +1,1966 @@ +/**************************************************************************** + 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.onExitTransitionDidStart(); + + this._inScene.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.onExit(); + + // _inScene should not receive the onEnter callback + // only the onEnterTransitionDidFinish + this._inScene.onEnterTransitionDidFinish(); + }, + + /** + * custom cleanup + */ + cleanup:function () { + cc.Node.prototype.cleanup.call(this); + + if (this._isSendCleanupToScene) + this._outScene.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 + }); + if(cc._renderType === cc.game.RENDER_TYPE_WEBGL) + this._inScene.getCamera().restore(); + + this._outScene.attr({ + visible: false, + x: 0, + y: 0, + scale: 1.0, + rotation: 0.0 + }); + if(cc._renderType === cc.game.RENDER_TYPE_WEBGL) + this._outScene.getCamera().restore(); + + //[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 Transtion 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 incomming 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 incomming 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 incomming 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 incomming 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); +}; + +/** + * Flips the screen horizontally.
+ * The front face is the outgoing scene and the back face is the incoming scene. + * @class + * @extends cc.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} o + * @example + * var trans = new cc.TransitionFlipX(t,scene,o); + */ +cc.TransitionFlipX = cc.TransitionSceneOriented.extend(/** @lends cc.TransitionFlipX# */{ + /** + * Constructor of TransitionFlipX + * @function + * @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, o) { + cc.TransitionSceneOriented.prototype.ctor.call(this); + if(o == null) + o = cc.TRANSITION_ORIENTATION_RIGHT_OVER; + scene && this.initWithDuration(t, scene, o); + }, + + /** + * custom on enter + */ + onEnter:function () { + cc.TransitionScene.prototype.onEnter.call(this); + + var inA, outA; + this._inScene.visible = false; + + var inDeltaZ, inAngleZ, outDeltaZ, outAngleZ; + + if (this._orientation === cc.TRANSITION_ORIENTATION_RIGHT_OVER) { + inDeltaZ = 90; + inAngleZ = 270; + outDeltaZ = 90; + outAngleZ = 0; + } else { + inDeltaZ = -90; + inAngleZ = 90; + outDeltaZ = -90; + outAngleZ = 0; + } + + inA = cc.sequence( + cc.delayTime(this._duration / 2), cc.show(), + cc.orbitCamera(this._duration / 2, 1, 0, inAngleZ, inDeltaZ, 0, 0), + cc.callFunc(this.finish, this) + ); + + outA = cc.sequence( + cc.orbitCamera(this._duration / 2, 1, 0, outAngleZ, outDeltaZ, 0, 0), + cc.hide(), cc.delayTime(this._duration / 2) + ); + + this._inScene.runAction(inA); + this._outScene.runAction(outA); + } +}); + +/** + * Flips the screen horizontally.
+ * The front face is the outgoing scene and the back face is the incoming scene. + * @deprecated since v3.0,please use new cc.TransitionFlipX(t, scene,o) 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} o + * @return {cc.TransitionFlipX} + */ +cc.TransitionFlipX.create = function (t, scene, o) { + return new cc.TransitionFlipX(t, scene, o); +}; + +/** + * Flips the screen vertically.
+ * The front face is the outgoing scene and the back face is the incoming scene. + * @class + * @extends cc.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} o + * @example + * var trans = new cc.TransitionFlipY(time,scene,0); + */ +cc.TransitionFlipY = cc.TransitionSceneOriented.extend(/** @lends cc.TransitionFlipY# */{ + + /** + * Constructor of TransitionFlipY + * @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, o) { + cc.TransitionSceneOriented.prototype.ctor.call(this); + if(o == null) + o = cc.TRANSITION_ORIENTATION_UP_OVER; + scene && this.initWithDuration(t, scene, o); + }, + /** + * custom on enter + */ + onEnter:function () { + cc.TransitionScene.prototype.onEnter.call(this); + + var inA, outA; + this._inScene.visible = false; + + var inDeltaZ, inAngleZ, outDeltaZ, outAngleZ; + + if (this._orientation === cc.TRANSITION_ORIENTATION_UP_OVER) { + inDeltaZ = 90; + inAngleZ = 270; + outDeltaZ = 90; + outAngleZ = 0; + } else { + inDeltaZ = -90; + inAngleZ = 90; + outDeltaZ = -90; + outAngleZ = 0; + } + + inA = cc.sequence( + cc.delayTime(this._duration / 2), cc.show(), + cc.orbitCamera(this._duration / 2, 1, 0, inAngleZ, inDeltaZ, 90, 0), + cc.callFunc(this.finish, this) + ); + outA = cc.sequence( + cc.orbitCamera(this._duration / 2, 1, 0, outAngleZ, outDeltaZ, 90, 0), + cc.hide(), cc.delayTime(this._duration / 2) + ); + + this._inScene.runAction(inA); + this._outScene.runAction(outA); + } +}); + +/** + * Flips the screen vertically.
+ * The front face is the outgoing scene and the back face is the incoming scene. + * @deprecated since v3.0,please use new cc.TransitionFlipY(t, scene,o) 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} o + * @return {cc.TransitionFlipY} + */ +cc.TransitionFlipY.create = function (t, scene, o) { + return new cc.TransitionFlipY(t, scene, o); +}; + +/** + * Flips the screen half horizontally and half vertically.
+ * The front face is the outgoing scene and the back face is the incoming scene. + * @class + * @extends cc.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} o + * @example + * var trans = cc.TransitionFlipAngular(time,scene,o); + */ +cc.TransitionFlipAngular = cc.TransitionSceneOriented.extend(/** @lends cc.TransitionFlipAngular# */{ + /** + * Constructor of TransitionFlipAngular + * @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, o) { + cc.TransitionSceneOriented.prototype.ctor.call(this); + if(o == null) + o = cc.TRANSITION_ORIENTATION_RIGHT_OVER; + scene && this.initWithDuration(t, scene, o); + }, + /** + * custom on enter + */ + onEnter:function () { + cc.TransitionScene.prototype.onEnter.call(this); + + var inA, outA; + this._inScene.visible = false; + + var inDeltaZ, inAngleZ, outDeltaZ, outAngleZ; + + if (this._orientation === cc.TRANSITION_ORIENTATION_RIGHT_OVER) { + inDeltaZ = 90; + inAngleZ = 270; + outDeltaZ = 90; + outAngleZ = 0; + } else { + inDeltaZ = -90; + inAngleZ = 90; + outDeltaZ = -90; + outAngleZ = 0; + } + + inA = cc.sequence( + cc.delayTime(this._duration / 2), cc.show(), + cc.orbitCamera(this._duration / 2, 1, 0, inAngleZ, inDeltaZ, -45, 0), + cc.callFunc(this.finish, this) + ); + outA = cc.sequence( + cc.orbitCamera(this._duration / 2, 1, 0, outAngleZ, outDeltaZ, 45, 0), + cc.hide(), cc.delayTime(this._duration / 2) + ); + + this._inScene.runAction(inA); + this._outScene.runAction(outA); + } +}); + +/** + * Flips the screen half horizontally and half vertically.
+ * The front face is the outgoing scene and the back face is the incoming scene. + * @deprecated since v3.0,please use new new cc.TransitionFlipAngular(t, scene, o) 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} o + * @return {cc.TransitionFlipAngular} + */ +cc.TransitionFlipAngular.create = function (t, scene, o) { + return new cc.TransitionFlipAngular(t, scene, o); +}; + +/** + * Flips the screen horizontally doing a zoom out/in
+ * The front face is the outgoing scene and the back face is the incoming scene. + * @class + * @extends cc.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} o + * @example + * var trans = new cc.TransitionZoomFlipX(time,scene,o); + */ +cc.TransitionZoomFlipX = cc.TransitionSceneOriented.extend(/** @lends cc.TransitionZoomFlipX# */{ + + /** + * Constructor of TransitionZoomFlipX + * @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, o) { + cc.TransitionSceneOriented.prototype.ctor.call(this); + if(o == null) + o = cc.TRANSITION_ORIENTATION_RIGHT_OVER; + scene && this.initWithDuration(t, scene, o); + }, + /** + * custom on enter + */ + onEnter:function () { + cc.TransitionScene.prototype.onEnter.call(this); + + var inA, outA; + this._inScene.visible = false; + + var inDeltaZ, inAngleZ, outDeltaZ, outAngleZ; + + if (this._orientation === cc.TRANSITION_ORIENTATION_RIGHT_OVER) { + inDeltaZ = 90; + inAngleZ = 270; + outDeltaZ = 90; + outAngleZ = 0; + } else { + inDeltaZ = -90; + inAngleZ = 90; + outDeltaZ = -90; + outAngleZ = 0; + } + + inA = cc.sequence( + cc.delayTime(this._duration / 2), + cc.spawn( + cc.orbitCamera(this._duration / 2, 1, 0, inAngleZ, inDeltaZ, 0, 0), + cc.scaleTo(this._duration / 2, 1), cc.show()), + cc.callFunc(this.finish, this) + ); + outA = cc.sequence( + cc.spawn( + cc.orbitCamera(this._duration / 2, 1, 0, outAngleZ, outDeltaZ, 0, 0), + cc.scaleTo(this._duration / 2, 0.5)), + cc.hide(), + cc.delayTime(this._duration / 2) + ); + + this._inScene.scale = 0.5; + this._inScene.runAction(inA); + this._outScene.runAction(outA); + } +}); + +/** + * Flips the screen horizontally doing a zoom out/in
+ * The front face is the outgoing scene and the back face is the incoming scene. + * @deprecated since v3.0,please use new new cc.TransitionZoomFlipX(t, scene, o) 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} o + * @return {cc.TransitionZoomFlipX} + */ +cc.TransitionZoomFlipX.create = function (t, scene, o) { + return new cc.TransitionZoomFlipX(t, scene, o); +}; + +/** + * Flips the screen vertically doing a little zooming out/in
+ * The front face is the outgoing scene and the back face is the incoming scene. + * @class + * @extends cc.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} o + * @example + * var trans = new cc.TransitionZoomFlipY(t,scene,o); + */ +cc.TransitionZoomFlipY = cc.TransitionSceneOriented.extend(/** @lends cc.TransitionZoomFlipY# */{ + + /** + * Constructor of TransitionZoomFlipY + * @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, o) { + cc.TransitionSceneOriented.prototype.ctor.call(this); + if(o == null) + o = cc.TRANSITION_ORIENTATION_UP_OVER; + scene && this.initWithDuration(t, scene, o); + }, + /** + * custom on enter + */ + onEnter:function () { + cc.TransitionScene.prototype.onEnter.call(this); + + var inA, outA; + this._inScene.visible = false; + + var inDeltaZ, inAngleZ, outDeltaZ, outAngleZ; + + if (this._orientation === cc.TRANSITION_ORIENTATION_UP_OVER) { + inDeltaZ = 90; + inAngleZ = 270; + outDeltaZ = 90; + outAngleZ = 0; + } else { + inDeltaZ = -90; + inAngleZ = 90; + outDeltaZ = -90; + outAngleZ = 0; + } + + inA = cc.sequence( + cc.delayTime(this._duration / 2), + cc.spawn( + cc.orbitCamera(this._duration / 2, 1, 0, inAngleZ, inDeltaZ, 90, 0), + cc.scaleTo(this._duration / 2, 1), cc.show()), + cc.callFunc(this.finish, this)); + + outA = cc.sequence( + cc.spawn( + cc.orbitCamera(this._duration / 2, 1, 0, outAngleZ, outDeltaZ, 90, 0), + cc.scaleTo(this._duration / 2, 0.5)), + cc.hide(), cc.delayTime(this._duration / 2)); + + this._inScene.scale = 0.5; + this._inScene.runAction(inA); + this._outScene.runAction(outA); + } +}); + +/** + * Flips the screen vertically doing a little zooming out/in
+ * The front face is the outgoing scene and the back face is the incoming scene. + * @deprecated since v3.0,please use new new cc.TransitionZoomFlipY(t, scene, o) 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} o + * @return {cc.TransitionZoomFlipY} + */ +cc.TransitionZoomFlipY.create = function (t, scene, o) { + return new cc.TransitionZoomFlipY(t, scene, o); +}; + +/** + * Flips the screen half horizontally and half vertically doing a little zooming out/in.
+ * The front face is the outgoing scene and the back face is the incoming scene. + * @class + * @extends cc.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} o + * @example + * var trans = new cc.TransitionZoomFlipAngular(time,scene,o); + */ +cc.TransitionZoomFlipAngular = cc.TransitionSceneOriented.extend(/** @lends cc.TransitionZoomFlipAngular# */{ + + /** + * Constructor of TransitionZoomFlipAngular + * @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, o) { + cc.TransitionSceneOriented.prototype.ctor.call(this); + if(o == null) + o = cc.TRANSITION_ORIENTATION_RIGHT_OVER; + scene && this.initWithDuration(t, scene, o); + }, + /** + * custom on enter + */ + onEnter:function () { + cc.TransitionScene.prototype.onEnter.call(this); + + var inA, outA; + this._inScene.visible = false; + + var inDeltaZ, inAngleZ, outDeltaZ, outAngleZ; + if (this._orientation === cc.TRANSITION_ORIENTATION_RIGHT_OVER) { + inDeltaZ = 90; + inAngleZ = 270; + outDeltaZ = 90; + outAngleZ = 0; + } else { + inDeltaZ = -90; + inAngleZ = 90; + outDeltaZ = -90; + outAngleZ = 0; + } + + inA = cc.sequence( + cc.delayTime(this._duration / 2), + cc.spawn( + cc.orbitCamera(this._duration / 2, 1, 0, inAngleZ, inDeltaZ, -45, 0), + cc.scaleTo(this._duration / 2, 1), cc.show()), + cc.show(), + cc.callFunc(this.finish, this)); + outA = cc.sequence( + cc.spawn( + cc.orbitCamera(this._duration / 2, 1, 0, outAngleZ, outDeltaZ, 45, 0), + cc.scaleTo(this._duration / 2, 0.5)), + cc.hide(), cc.delayTime(this._duration / 2)); + + this._inScene.scale = 0.5; + this._inScene.runAction(inA); + this._outScene.runAction(outA); + } +}); + +/** + * Flips the screen half horizontally and half vertically doing a little zooming out/in.
+ * The front face is the outgoing scene and the back face is the incoming scene. + * @deprecated since v3.0,please use new new cc.TransitionZoomFlipAngular(t, scene, o) 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} o + * @return {cc.TransitionZoomFlipAngular} + */ +cc.TransitionZoomFlipAngular.create = function (t, scene, o) { + return new cc.TransitionZoomFlipAngular(t, scene, o); +}; + +/** + * 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); + }, + + /** + * stuff gets drawn here + */ + visit:function () { + cc.Node.prototype.visit.call(this); + }, + + /** + * overide draw + */ + draw:function () { + // override draw since both scenes (textures) are rendered in 1 scene + } +}); + +/** + * 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.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.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.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.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/cocos2d/transitions/CCTransitionPageTurn.js b/cocos2d/transitions/CCTransitionPageTurn.js new file mode 100644 index 00000000000..6b71fd848b9 --- /dev/null +++ b/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.onEnter(); + gridProxy.runAction( cc.sequence(action,cc.callFunc(this.finish, this),cc.stopGrid())); + } else { + gridProxy.setTarget(this._inScene); + gridProxy.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/cocos2d/transitions/CCTransitionProgress.js b/cocos2d/transitions/CCTransitionProgress.js new file mode 100644 index 00000000000..4d4a077516f --- /dev/null +++ b/cocos2d/transitions/CCTransitionProgress.js @@ -0,0 +1,458 @@ +/**************************************************************************** + 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 + }); + }, + + /** + * @override + * custom on enter + */ + 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); + }, + + /** + * @override + * custom on exit + */ + 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/docs/cocos2d/core/CCActionManager/ActionManager.js b/docs/cocos2d/core/CCActionManager/ActionManager.js new file mode 100644 index 00000000000..999d27bd1a0 --- /dev/null +++ b/docs/cocos2d/core/CCActionManager/ActionManager.js @@ -0,0 +1,2 @@ +---- +var mng = new cc.ActionManager(); diff --git a/docs/cocos2d/core/CCScheduler/schedule.js b/docs/cocos2d/core/CCScheduler/schedule.js new file mode 100644 index 00000000000..ccab42e5f16 --- /dev/null +++ b/docs/cocos2d/core/CCScheduler/schedule.js @@ -0,0 +1,3 @@ +------------------- +//register a schedule to scheduler +cc.director.getScheduler().schedule(callback, this, interval, !this._isRunning); diff --git a/docs/cocos2d/core/CCScheduler/scheduleCallbackForTarget.js b/docs/cocos2d/core/CCScheduler/scheduleCallbackForTarget.js new file mode 100644 index 00000000000..cb928db8bad --- /dev/null +++ b/docs/cocos2d/core/CCScheduler/scheduleCallbackForTarget.js @@ -0,0 +1,3 @@ +------------------- +//register a schedule to scheduler +cc.director.getScheduler().scheduleCallbackForTarget(this, function, interval, repeat, delay, !this._isRunning ); diff --git a/docs/cocos2d/core/CCScheduler/scheduleUpdateForTarget.js b/docs/cocos2d/core/CCScheduler/scheduleUpdateForTarget.js new file mode 100644 index 00000000000..3af3ee457c7 --- /dev/null +++ b/docs/cocos2d/core/CCScheduler/scheduleUpdateForTarget.js @@ -0,0 +1,3 @@ +------------------- +//register this object to scheduler +cc.director.getScheduler().scheduleUpdateForTarget(this, priority, !this._isRunning ); diff --git a/docs/cocos2d/core/CCScheduler/unscheduleCallbackForTarget.js b/docs/cocos2d/core/CCScheduler/unscheduleCallbackForTarget.js new file mode 100644 index 00000000000..af1c809c3a6 --- /dev/null +++ b/docs/cocos2d/core/CCScheduler/unscheduleCallbackForTarget.js @@ -0,0 +1,3 @@ +------------------- +//unschedule a callback of target +cc.director.getScheduler().unscheduleCallbackForTarget(function, this); diff --git a/docs/cocos2d/core/CCScheduler/unscheduleUpdateForTarget.js b/docs/cocos2d/core/CCScheduler/unscheduleUpdateForTarget.js new file mode 100644 index 00000000000..25c769d0060 --- /dev/null +++ b/docs/cocos2d/core/CCScheduler/unscheduleUpdateForTarget.js @@ -0,0 +1,3 @@ +------------------- +//unschedules the "update" method. +cc.director.getScheduler().unscheduleUpdateForTarget(this); diff --git a/docs/cocos2d/core/components/CCSpriteRenerer/initWithSpriteFrameName.js b/docs/cocos2d/core/components/CCSpriteRenerer/initWithSpriteFrameName.js new file mode 100644 index 00000000000..8681cb430c6 --- /dev/null +++ b/docs/cocos2d/core/components/CCSpriteRenerer/initWithSpriteFrameName.js @@ -0,0 +1,3 @@ +--------- +var sprite = new cc.Sprite(); +sprite.initWithSpriteFrameName("grossini_dance_01.png"); diff --git a/docs/cocos2d/core/event-manager/CCEventListener/create.js b/docs/cocos2d/core/event-manager/CCEventListener/create.js new file mode 100644 index 00000000000..534b300c525 --- /dev/null +++ b/docs/cocos2d/core/event-manager/CCEventListener/create.js @@ -0,0 +1,10 @@ +-------- +cc.EventListener.create({ + + event: cc.EventListener.TOUCH_ONE_BY_ONE, + swallowTouches: true, + onTouchBegan: function (touch, event) { + //do something + return true; + } + }); diff --git a/docs/cocos2d/core/event/_getCapturingTargets.js b/docs/cocos2d/core/event/_getCapturingTargets.js new file mode 100644 index 00000000000..96d545b0edf --- /dev/null +++ b/docs/cocos2d/core/event/_getCapturingTargets.js @@ -0,0 +1,8 @@ +---------- +Subclasses can override this method to make event propagable +```js +for (var target = this._parent; target; target = target._parent) { + if (target._capturingListeners && target._capturingListeners.has(type)) { + array.push(target); + } +} diff --git a/docs/cocos2d/core/platform/CCCommon/KEY.js b/docs/cocos2d/core/platform/CCCommon/KEY.js new file mode 100644 index 00000000000..22edc52e955 --- /dev/null +++ b/docs/cocos2d/core/platform/CCCommon/KEY.js @@ -0,0 +1,9 @@ +----- +cc.eventManager.addListener({ + event: cc.EventListener.KEYBOARD, + onKeyPressed: function(keyCode, event){ + if (cc.KEY["a"] == keyCode) { + cc.log("A is pressed"); + } + } +}, this); diff --git a/docs/cocos2d/core/platform/CCMacro/lerp.js b/docs/cocos2d/core/platform/CCMacro/lerp.js new file mode 100644 index 00000000000..db39662af27 --- /dev/null +++ b/docs/cocos2d/core/platform/CCMacro/lerp.js @@ -0,0 +1,4 @@ +---- +lerp +cc.lerp(2,10,0.5)//returns 6 +cc.lerp(2,10,0.2)//returns 3.6 diff --git a/docs/cocos2d/core/platform/attribute/attr.js b/docs/cocos2d/core/platform/attribute/attr.js new file mode 100644 index 00000000000..68b04fa5ff7 --- /dev/null +++ b/docs/cocos2d/core/platform/attribute/attr.js @@ -0,0 +1,10 @@ +----- +var myClass = function () { this.value = 0.5 }; +cc.Class.attr(myClass, 'value'); // return undefined +cc.Class.attr(myClass, 'value', {}).min = 0; // assign new attribute table + //associated with 'value', and set its min = 0 +cc.Class.attr(myClass, 'value', { // set values max and default + max: 1, + default: 0.5, +}); +cc.Class.attr(myClass, 'value'); // return { default: 0.5, min: 0, max: 1 } diff --git a/docs/cocos2d/core/platform/url/raw.js b/docs/cocos2d/core/platform/url/raw.js new file mode 100644 index 00000000000..88ec9b4c249 --- /dev/null +++ b/docs/cocos2d/core/platform/url/raw.js @@ -0,0 +1,3 @@ +--- +var url = cc.url.raw("textures/myTexture.png"); +console.log(url); // "resources/raw/textures/myTexture.png" diff --git a/docs/cocos2d/core/sprites/SpriteFrame.js b/docs/cocos2d/core/sprites/SpriteFrame.js new file mode 100644 index 00000000000..ee5830b9d69 --- /dev/null +++ b/docs/cocos2d/core/sprites/SpriteFrame.js @@ -0,0 +1,9 @@ +---------------------------------------------------- +// 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)); diff --git a/docs/cocos2d/core/sprites/addSpriteFrames.js b/docs/cocos2d/core/sprites/addSpriteFrames.js new file mode 100644 index 00000000000..44c45c4f466 --- /dev/null +++ b/docs/cocos2d/core/sprites/addSpriteFrames.js @@ -0,0 +1,4 @@ +---------------------------------------------------- +// add SpriteFrames to SpriteFrameCache With File +cc.spriteFrameCache.addSpriteFrames(s_grossiniPlist); +cc.spriteFrameCache.addSpriteFrames(s_grossiniJson); diff --git a/docs/cocos2d/core/sprites/getSpriteFrame.js b/docs/cocos2d/core/sprites/getSpriteFrame.js new file mode 100644 index 00000000000..0c37dd966e1 --- /dev/null +++ b/docs/cocos2d/core/sprites/getSpriteFrame.js @@ -0,0 +1,3 @@ +---------------------------------------------------- +//get a SpriteFrame by name +var frame = cc.spriteFrameCache.getSpriteFrame("grossini_dance_01.png"); diff --git a/docs/cocos2d/core/support/pAdd.js b/docs/cocos2d/core/support/pAdd.js new file mode 100644 index 00000000000..a1f5e8d7a3c --- /dev/null +++ b/docs/cocos2d/core/support/pAdd.js @@ -0,0 +1,4 @@ +----------------- +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)

diff --git a/docs/cocos2d/core/support/pCompOp.js b/docs/cocos2d/core/support/pCompOp.js new file mode 100644 index 00000000000..9facaccb9f9 --- /dev/null +++ b/docs/cocos2d/core/support/pCompOp.js @@ -0,0 +1,3 @@ +-------------- +//For example: let's try to take the floor of x,y +var p = cc.pCompOp(cc.p(10,10),Math.abs); diff --git a/docs/cocos2d/core/textures/TextureAtlas.js b/docs/cocos2d/core/textures/TextureAtlas.js new file mode 100644 index 00000000000..e0d4542b39d --- /dev/null +++ b/docs/cocos2d/core/textures/TextureAtlas.js @@ -0,0 +1,7 @@ +-------------------------- +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); diff --git a/docs/cocos2d/core/textures/addImage.js b/docs/cocos2d/core/textures/addImage.js new file mode 100644 index 00000000000..cd3cc39a031 --- /dev/null +++ b/docs/cocos2d/core/textures/addImage.js @@ -0,0 +1,2 @@ +---- +cc.textureCache.addImage("hello.png"); diff --git a/docs/cocos2d/core/textures/getKeyByTexture.js b/docs/cocos2d/core/textures/getKeyByTexture.js new file mode 100644 index 00000000000..6617822a860 --- /dev/null +++ b/docs/cocos2d/core/textures/getKeyByTexture.js @@ -0,0 +1,2 @@ +--------- +var key = cc.textureCache.getKeyByTexture(texture); diff --git a/docs/cocos2d/core/textures/getTextureColors.js b/docs/cocos2d/core/textures/getTextureColors.js new file mode 100644 index 00000000000..75cc1325505 --- /dev/null +++ b/docs/cocos2d/core/textures/getTextureColors.js @@ -0,0 +1,2 @@ +--------------- +var cacheTextureForColor = cc.textureCache.getTextureColors(texture); diff --git a/docs/cocos2d/core/textures/getTextureForKey.js b/docs/cocos2d/core/textures/getTextureForKey.js new file mode 100644 index 00000000000..7ce13d75dee --- /dev/null +++ b/docs/cocos2d/core/textures/getTextureForKey.js @@ -0,0 +1,2 @@ +------------------ +var key = cc.textureCache.getTextureForKey("hello.png"); diff --git a/docs/cocos2d/core/textures/initWithFile.js b/docs/cocos2d/core/textures/initWithFile.js new file mode 100644 index 00000000000..e7cfc635fa9 --- /dev/null +++ b/docs/cocos2d/core/textures/initWithFile.js @@ -0,0 +1,3 @@ +-------------------------------------------------- +var textureAtlas = new cc.TextureAtlas(); +textureAtlas.initWithTexture("hello.png", 3); diff --git a/docs/cocos2d/core/textures/initWithTexture.js b/docs/cocos2d/core/textures/initWithTexture.js new file mode 100644 index 00000000000..e1ae8a7282e --- /dev/null +++ b/docs/cocos2d/core/textures/initWithTexture.js @@ -0,0 +1,4 @@ +--------------------------- +var texture = cc.textureCache.addImage("hello.png"); +var textureAtlas = new cc.TextureAtlas(); +textureAtlas.initWithTexture(texture, 3); diff --git a/docs/cocos2d/core/textures/removeAllTextures.js b/docs/cocos2d/core/textures/removeAllTextures.js new file mode 100644 index 00000000000..e3e8bcdbebe --- /dev/null +++ b/docs/cocos2d/core/textures/removeAllTextures.js @@ -0,0 +1,2 @@ +-------- +cc.textureCache.removeAllTextures(); diff --git a/docs/cocos2d/core/textures/removeTexture.js b/docs/cocos2d/core/textures/removeTexture.js new file mode 100644 index 00000000000..e7e4782d871 --- /dev/null +++ b/docs/cocos2d/core/textures/removeTexture.js @@ -0,0 +1,2 @@ +----- +cc.textureCache.removeTexture(texture); diff --git a/docs/cocos2d/core/textures/removeTextureForKey.js b/docs/cocos2d/core/textures/removeTextureForKey.js new file mode 100644 index 00000000000..27308cb0547 --- /dev/null +++ b/docs/cocos2d/core/textures/removeTextureForKey.js @@ -0,0 +1,2 @@ +------ +cc.textureCache.removeTexture("hello.png"); diff --git a/docs/cocos2d/core/textures/textureForKey.js b/docs/cocos2d/core/textures/textureForKey.js new file mode 100644 index 00000000000..28175de1061 --- /dev/null +++ b/docs/cocos2d/core/textures/textureForKey.js @@ -0,0 +1,2 @@ +------------------ +var key = cc.textureCache.textureForKey("hello.png"); diff --git a/docs/cocos2d/core/utils/CCPath/basename.js b/docs/cocos2d/core/utils/CCPath/basename.js new file mode 100644 index 00000000000..31a3db9a876 --- /dev/null +++ b/docs/cocos2d/core/utils/CCPath/basename.js @@ -0,0 +1,6 @@ +--------------------------------- +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" diff --git a/docs/cocos2d/core/utils/CCPath/changeBasename.js b/docs/cocos2d/core/utils/CCPath/changeBasename.js new file mode 100644 index 00000000000..6188cd1ba4c --- /dev/null +++ b/docs/cocos2d/core/utils/CCPath/changeBasename.js @@ -0,0 +1,6 @@ +---------------------------------- +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" diff --git a/docs/cocos2d/core/utils/CCPath/changeExtname.js b/docs/cocos2d/core/utils/CCPath/changeExtname.js new file mode 100644 index 00000000000..8ca43f3bb25 --- /dev/null +++ b/docs/cocos2d/core/utils/CCPath/changeExtname.js @@ -0,0 +1,3 @@ +--------------------------------- +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" diff --git a/docs/cocos2d/core/utils/CCPath/dirname.js b/docs/cocos2d/core/utils/CCPath/dirname.js new file mode 100644 index 00000000000..3ed94455933 --- /dev/null +++ b/docs/cocos2d/core/utils/CCPath/dirname.js @@ -0,0 +1,9 @@ +--------------------------------- +* 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" diff --git a/docs/cocos2d/core/utils/CCPath/extname.js b/docs/cocos2d/core/utils/CCPath/extname.js new file mode 100644 index 00000000000..d8718c8af1e --- /dev/null +++ b/docs/cocos2d/core/utils/CCPath/extname.js @@ -0,0 +1,5 @@ +--------------------------- +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 diff --git a/docs/cocos2d/core/utils/CCPath/join.js b/docs/cocos2d/core/utils/CCPath/join.js new file mode 100644 index 00000000000..efd2eb1936a --- /dev/null +++ b/docs/cocos2d/core/utils/CCPath/join.js @@ -0,0 +1,6 @@ +------------------------------ +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/" diff --git a/docs/cocos2d/core/utils/node-wrapper/setPosition.js b/docs/cocos2d/core/utils/node-wrapper/setPosition.js new file mode 100644 index 00000000000..c88584e4737 --- /dev/null +++ b/docs/cocos2d/core/utils/node-wrapper/setPosition.js @@ -0,0 +1,3 @@ +--------------------------------- +var size = cc.winSize; +node.setPosition(size.width/2, size.height/2); diff --git a/docs/cocos2d/core/value-types/CCColor/color.js b/docs/cocos2d/core/value-types/CCColor/color.js new file mode 100644 index 00000000000..86c4819aa4e --- /dev/null +++ b/docs/cocos2d/core/value-types/CCColor/color.js @@ -0,0 +1,7 @@ +----------------------- +// 1. All channels seperately 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}); diff --git a/docs/cocos2d/core/value-types/CCSize/size.js b/docs/cocos2d/core/value-types/CCSize/size.js new file mode 100644 index 00000000000..d61e9014521 --- /dev/null +++ b/docs/cocos2d/core/value-types/CCSize/size.js @@ -0,0 +1,5 @@ +----- +var size1 = cc.size(); +var size2 = cc.size(100,100); +var size3 = cc.size(size2); +var size4 = cc.size({width: 100, height: 100}); diff --git a/docs/extensions/ccpool/putInPool.js b/docs/extensions/ccpool/putInPool.js new file mode 100644 index 00000000000..c60bfe58930 --- /dev/null +++ b/docs/extensions/ccpool/putInPool.js @@ -0,0 +1,5 @@ +--------------------------------- +var sp = new cc.Sprite("a.png"); +this.addChild(sp); +cc.pool.putInPool(sp); +cc.pool.getFromPool(cc.Sprite, "a.png"); diff --git a/editor/dashboard/banner.jpg b/editor/dashboard/banner.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e4f251f5f8bd2fbb7d28c07042bbc2e6098d4818 GIT binary patch literal 4915 zcmd6pXEfYjx5s~@mk@1qq72b{4bgke3`2AwLxvDtln9CVGYm0|PPB>M`w*RA5S=Iy zT?h$@D2ce9b)WUTx-agVyVl+3yx4o~@BVzxi?h}_R|{940Xic+Lp=Zl0szqU0N8oQ{zW%tX(~3}#|s;bLb6voLcoaf4ZTI61h$U`}o>AyFPS4gpR^R$duiX<<?XIpz8qs6J#W$LnA}SWI({5mVehv9`XqPf{{QO2*eqzo6?{$^=sV%?&1A`lhUH zj$2hQ&$NfWcg&z5A=2qZi#E6v-q!N5c{GB)q?($EZb|Ln%iHqj^84dzxWM`E)R{Qi zV!7J2+9?6>L4{#Q0|`fwwDjhFZHa9WQlMVpLu=`I$w5F#_wLB{ovpKOa*C2R`}g); ztiT%6sc6Q2`Udd-2}Rt2-D~9V71dcf!{qBa*l;}Uk>k$->Ly3Z9^FLv&bogBhdQ`@ zJkd+?wiTJdG4u<6c@>lO!6i{Qyoc{?2Xw`;x>%;eNR(R9wzD#A>)jO);d&DLr#LdL zAxr&!GY$gUAvr&+2om64nKTPy2&{q33_r%h|L$V>Dc`ct2<`rbL~5*GynhU0_}OMz z&>c~4DmoezK!4U9lXBlp9Z6s_)@;d=!z!2IH-`i7TLJ5aFs*mcX`{E5>wl=)v%PR^ zeYdjTs6KHDutD-XFHt_=@8HLO!y^P^FK0s43o=%WNr%4-;s${kVxaqILJGyE;APg-=rKtm>Qqu{Qh6$VJ|XR*v7?3qg1UpQei=y9J!2sFA#jm{qa-gcvIIw zyxM!?H)GsRNAWeU!qhh}a=C^$Usk7#Znz)!)lF~z)jrC`4vK!f0t85Mhcz1EVtX^t z3Q?mJ9gI?iZ4^8I<7PbiP-PEk7zDSabXV$=oGofsedrLtHPC;P!10?)?a8K(xUAoq z?lpWvu_y$g5O`Da)}Pi?Y;o>jB#h7Mp6BtqZJ+7D)U+v4?y}h6N4E)V)CR&5fF_u% z&3&5FQo1oQBt1QI!F1qTPt7Hb@Gko~iNjaqAED#BAkeY;3=jrk$ox{~h&ixuHd zmd}5xmgIYG(r2~&12IQd9d-F!a9d2sA5#Q9^3(a1waRhIAeSOU@JOdoNVR5D(iP1T zc6!yyGzyxRw0N zIzgak6sdiLmWM387aT8VTE}4Q7M&nS2@^uWs4|FEKcUY(Q&)rA+4*Km%&e9TuLmX75Cbw}H1snG!9h64<|m5NRA}DY znc?Odn|@q*L%;uaXR-0_x!-$g70a}^6HL}XZr|9ApQ__YF}Pnf;!j#z3b3#R=O2=I zm(mkNT3)&x^!6eBVkxmgq~g-|1MdpgL)`D7NNLaV=T6uwpwzQ7gZO#pH>;0ziCd$= zP{4ilfCT@c?vVW1Ju*As$*=aVdV^a8#ZPo(jBbC=yS(nM0TJja+1WSsAtm62SP;u) z!E$AP3Enya82Wtu;*e3tBGo=zu1s?=zuQxidvk!hJWDDMzI48|mGn6H3aHtI3S9xo zS3qt2^D+13liPUy7yIuJ+wqr;&-BntZF-KUabFWFZcSZav%K7jLuJ)5!6d!lU-pK( z_oREs<4U%jEEAhb62zko-7PC@G;P+FpbWJi) zAIEdqGArS3Z(FR$PkDEYpcQi0K1MX?W(f@CfFzqA89yo0@TLYEexsPRaVgUKjr4@Y zA2UjFZ3d4p(}8#{5%j2%1b^%f6_l+j4D#|Z}GI@!~qIU!rl zK!ISCj#cAIt2T+J1Sxu@$=CQ!(Q66^eL5urLdJ~Vy>*Y))f*u-WipF-NCG3V$3TU# zFcg>&DcYNVz?XSAqOxbI5-xdzN#SP+4R4h|!oKs{Ns%&-`jGS$Y5P!_h-?-+U9~N) z@<`b;;wPPEa|b-nmbf;%Q~28TPd+}0SbZkNvn&}u+f?2~ICa%kLgTD@)lSC$tA1;4HEQaY&D2z!hjy&*_EGA`s%Z%r&&`4;jATn!U+3U@u zwC^@bCDWWkp+uYHPr4bza0{|HQyGG&l845l{Aq;dSvPCdLH(roogWmC0_sA;B`px} zBN9nOjxe-2MnmpIANCioi@;^2J=Tm;t?E^$Dgyl#OILNCx~Cxc>oSDpROIvTc|T zHrzNH*y^1tJQ_Mqd%m{(UiJE}uTQ5WKYL(y2Nh+KXXN)~2Q>m0oxYDcXGv!g_axZ* z*<5SZvRe@4FKwxk=)xJy4(i&QmT`TXNL#hBL-Q*@m*%`~0Xq}(BFa%@9cpFRXWBUv z_;J~+y16B)uUK4oVs_@}Ly|UKm)Uymi2(lm4U?i0#KZau z58N^)h1E+lsRd&LNW?C&H>LH9V?PGd?CDjottrVyk795;9XK(TMUg=m=pM=WFkIAR zOwTgW)(@IygFlY0fceDtMF?yq#Yw!AYHniRg5Yvr34uowMN-o2^}rCESfQ8D2wH$r zwP_t{T1ptHHXjZ|!H)PI@RXLRTIMgAiEQOT>)=N0t;!xYX4Xda+3)$};;9g5M^5k7 z%IqaLJCNt2kS8?D+WbB7A|-)8)5mVmbIgs;c2NAb3>8(Pe3d0Mu9ro%M&pSOZvLIU5ILqo zReA^q&0c6TE+bg{aF36YF<=Nk)^ykabHqglHs@pl~ZtXD-xQ_z_I_e>b_fF8o0 zmd#ZG`+GFr-_J0b1&R8t9bdfBxqBK-sVn$;*3QiN3ZOd_Bw_FSDHMER=$2l_eJax6c|tqXyWinPAVpEUYn6bu+^ze_ zrQn1+Q3@y=-Iy@;N1hCXNmvtGxBF8<)A~V%;;)EnQIR^1!gmv`CF`~wMxjW&xBf%Z=!A010Z8~i}1pZ1lnU2l&^cv z&2D}0^q5uf!h5S|bLQKB^t<~PPh+f~Ep(r08X7fo6^bFs=hz&$9v;UY&n+d;QK6<} zs112Fyj3oJ;jpxdm3PdI<@@5lGY^6mEj>7ysv_#fySVB~mr~-InxjiA^B`Ho`zrhb zoEb(>wP}_SjiUFPUft1^`C)epp*}N!eW%nH5uBjD2$7U5I_gP%v#(}o#*qZrx8 zyP-Ka?Q5L!vA%AY<=;j&fSZUPm?!FNz;KW5B((ZXni|)4e)6g+u>>gmG~8``)iqm)KQRA2dLqV{#x;V3Iv;JX$l0+(^tGOI;QhOf1ZXGbSpiG-Q~xf-_L)WHS44mdeVx)A7@P^W7+NQv>r>fRx}mmxP~mw)X9%y_l0c{MuTv# zrmd19H~a4 zJKH`6f!b?>5i@=bKjYOt&Eh88`sa_Bq@$~Zjr+(J9OjY|L(>=+t#j`c86XQx^TB!8 zhj;jR5vIOgnIp$VpD|2T9LUpo^eNMNnNdZoH%D}!p7e`;=&+)2$MB|Ro(w191pPDN;WFcSDWIM<@>%IBRAYIFkIvrIOuW}!X)^(Ao* z^e-!PGt1%0*MF`y2l~V&;=8t+G42oFGvN6wcO~4~V6EZtY+(^lRij-rY8|7zy@7%X=wN~{9_E8U+DeVgYa&M< zP_3)7260-)NEB5_FRczZXKsW%>oPQfS{e8SlqGKTT5gCRaCfw=8q{R; zAGnVt*Zl(tIWCcQ8LYGuwXpS$olo<}1o;k1x1MZ`U+9Jp-u6^`j80JIC#0AOL5-mt zaniJYVc7x}3Bkp_2__%B@WtoFNzh^ho6>hT8B{uxiN}0^7q4R-$3`&J_tPV6mEvZA z-=fcX{>?#I?AKM&m#iOqd_QT2dJTk(+|E+ovhrIWJHkzb3}u5%glCxnUVm&!vYV;3$Fm|sfUp?M!_0nIF)>2SndSq|4zWda%UW| Xq-E1$5l0=!YY0K1wq+vKuRi`4{?Few literal 0 HcmV?d00001 diff --git a/editor/dashboard/banner.png b/editor/dashboard/banner.png new file mode 100644 index 0000000000000000000000000000000000000000..1523835f44e1c0458291f489539af0d5dd27f663 GIT binary patch literal 47339 zcmV*JKxV&*P)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z006EQNkl;jO*6AOOImGEL=pn!0EjFE1|xwHn4ApuvjJo5 z2N;u0GTAr~jmgM>5g-K0(&n7Q%J9lLyl#$sw2Z_r_?R|;bmuNeyj&nr!B|>K5;fEf= zGjF{G06-~)Wm($3lu~Tnx)m2)bP;UZ2Bj1Lkjv#*JDaxyl``A5!FPsbS)dds+C}Xg zlv37?Qcwye169sJ*tJoro!8|3V={g`er>j6%X8#RejTDOd<@umd7s7DQwql^fr5fk z3e#kLc1jMY*f(c_g14g-AV4l>0R*re2cQ5UtQ`Op9S0x;l(KUW(GG-PuL%mEX!CZU zu&g}##|zkVvWLltf$rmn@TI?fyEY$5bEL{{xc+*)>5Xrk(X$XxigofsY-1_0b} z{q=a~JKk}|p26O}e*EtK`{9<$vFim;Rc6~Z2uYHWm`pq~Q5hj5p&el5^RfFXD$8Uu zxbemt(c0SjBzf1(t!&%2?KpnynD+cS3D2~U6{e<8DmmKNi+6-RA2e}}#Cf%OOPWjm zZi>sOlr=Rq;mWJ7L@Je<@wgD%wke);8%g|iR*qMPSZ_iaI4r5NE3DneKJpQK?4uv4 z+I)C;Sl+baCn!^XnoMk5xd{cSwxLXW$(uXcMkqmWWh3{C9zzJlet_Pde@DbIxl#(x zN7!#_dm{EDvQPSV6tvR#QYd{OQqij43xy|?A^V%}B+g9;_ICAS%qasfaD3lReCfS^ z(a@c?Y11aW@4bIF<2$yqvlAcs@IPT{Y6<}G*-w84ZEbCH{^X61kK=d0`yD*j)o)u8 zAA3^iu}b`c)ZdoW-!^e~5L|ZoWmvg#75-Q^*K@Ib>oyDy4JJP_G)YELE|oAaI2eDx z>e#<>+xk^dv}rz{$Cit>z(}QLG#-X{!JYdbD4*Fp7N)rmf5Fz#F8MsQ+Od~iwe8w# zuMLkE(6B$CisFJiFb(*5K@rdZlu`si!1oWNk_`AUqHVz2vG17L2-Is5#|YTpq78+5 z9_2XdF+f3Sz^@^Y#|)S#(uQjN3Id_r<)i@OJaRwCYo~~h2bI^(lw@=PAo_6rl0`@p zrl`%Pmd2mBSz30kUcDNxe8nr|-(UX9SLR*`j*a2>zyCda&jTSO@|JZgB zhYuZ|@tCBA%i!EM<++JoinOD%;07hr>|d$D^Ga7Mf#cXU+F9u8>a2Ra^UgbuDHiT} zlIQVwzS4n_k&P=Qsy||1{`wSmFYN+T&79)t8h^Y84<22 z!d#==MQN98Y3hZpQCnJIrYT&Wj#Sd7Wir>O<4B9mG;;uD=Y={36ex1HDnO*0w&&{acq73aimfy5JK2<6nSqr{5elLp6~n8%C6=b zRYL;0d(%vCmnOSLnWC%g8Wpn1$q5|Tw;$`*uTQ*R>X8{VTu1-^&pGp9Zn=jZe6Z>& zX4R@yn&l>~K*ib#A{JuYatp1bWD8NT?6}nxXy*yEHZcnkN*m1rsDC38x5`W<%++Gg_mq?Dl%!gRI^A?(}L+f-Hkna z_RQJ1olF15Cnm7<@h9NAULEt4Jf5jksxIq~S?FiZdK?@aKwn=ksPYz7|J9PEqp=;; zUwdxI$SfQ$>R+Zc_C>znB>lGA1%Mz3K!`Gq zgi6qiP*(Dmr-b!|=<_MAnFd66vx<3<%9FJ_Z^Q^nR&zO zUk}T&@Zf_F;(-Sq!1dQ(KdaB+zP)=fK0eM@_#_g~i@UPK@0xonL8i6~e@9#f5^1lt zOK49>LhP|}|9ZN+v2U-5Ef-yM4!-|pM^>1e#I`4%fTy^%#TbN5i87~^=90$7p2gjh z#yK9RC5iWep}h~oK2XGOq4@nLj@Q}QiRP9TY3b2~h(skBDNGj5VXzFDB|uE0;2KgV zmWI^WwH64c^7oHC{4j2R{`0E#^2m`Rm_Kh`r~px6QByZShQ^dz68HtA5a}Hyk{45Fh&BKO&#EX62ZDy?xlab?cmhS1LVA za|O1kBwMz88MbWM@&^|a^gJIAJ@_Ep2*9b>m ziV~%|La3%h1u7HK??@ub6p6~0j}M6`;fNAc6(^ypf<^d~3dKf4i<~QRPy+SrQUR)C z7MWN|!tNb$i`wUyHB(+t+aKukOW*Zhz4%yg)td+O;duva79_DkC=Q zZaW7GuUgx_-ace9SzK_z1%EK_Dc|$3W5*8oz7NAN8bWARx_1r3sBa;ZsIV%{39Wms zlP*E*@4995^z>lWs>Tq(X%Tp=l*@5W$Ox2tG$rU=RaQ!FN0Rm>5t&`{coM`B3HT-l zsY^g(D|B@sj5}i{&(y963m) z3WDKE)U^07+DJDq{-6?eS?zxwsB7$9ec$J@Ve9}0zn1}-3k!~h3`BpZG${VE2K2_mTq8Ym@lJ`MarD*PmrVUh$wA+`~sP)7(Ud(AM&=p z|0f(i*~t_vk`bg~9wM~-N;R(#Y968l%CNQpQ=yo2oKV>Zko}bCR!}Tkv7+Hv%h1qJ zf92|1Yc z&sC*QE|*2Zkj0KwNs(1jkTKd#-<&|P)m zg%{#I?|Roc>!;}H>A}9e`_3wOWp>&E^#&x9_Mbn0J}$ZBk|&*?sZc0j@7}%eJg+_g z3T+I)GCn?z(a1tQ5x`O<&WO5OW7_>SQU*3}+Qd^ns&B^sPuh!o>eZkPADh&F{yOf7 zl`B`)c7IlIZARUnzxwsBar*RW0KmWe%jXl_pE@H;B=@fl3o*)i1FAc90f3gH~kH4N zdyB;obfFRm^L2?SfH8K7@5@+A1c~>OW($2IV+hK?vECtc_77uq`vNrOaxg6m=Uup| zVYZJWM~}#PIoH5xy}i8{8yigolvDzQ2_Qx-StVdhy)p*+NEp*b05qr#pb>Bo%d&9# z^l4;sIkdF2oco|c00aH~u!}`FCC2VV>IKGt)&-Vf-WN9H>iCKGfiBA>-!CzsAupbg z0Q#|uP6GI*SRIg~$WC{4A)jwTb8~am@jFKn>O*KfNHSq`^+fqdyIB4jDjyF@VmqMa zyUBB?$5RbLAtE#+Y*u!Zgwoo6%nzsLk7|Y>tj-+1{cnEr z8_g8`{1?B7+itt9a$-Al7pag1N>V?hy%+X{DB~t#d(h&8NZeNYPg|B-?NQI>Mq=foXN zdU|_hyRmWRhRa(Pvi#+`eAzPOaycxRKOc)0EkZ7riyco`L4cCB5ltqKx<*Nu;2~tk zj~&BB7hjB2Ds@gL&reQHqOY&7;y9zi*b9D%_*li~XtGei*!Vb3pFWMjp&{vhC43?8 zx-MFp*`lGjxfv}j&7hQ`si_$)&CN;zevRVmo-TYC#m;o{5=r_0mjBg(cIJ& z_n#{7pLC3Nr9#6$P8rbySJn1K1sQGpyj`6gHGTUMxPc@fPdq;`NXick0vtMY2$TFJ zF23sS?#7RQ{A0Z6_S-c!6`8Y`5TsY*gH%b_(||^NOarY-MxZEBq5yj0NYo_n^s&w^ z{MCm)jemacUm{H+046y`;Ua~3Zo^6yR68Ggm;xEQWcxK`vsk`pKDtN7d2fN!!xJd_ z1RItv!ucCEVfXeY8e*2|>gvRW7hZ_Q_%G&GHa0fKSLLLl(u>MOPT6!6VX;_5M^`6~ z965@<{=S-JtVTvhrEHAHs6@L(ixy$s+O^nl{sqYM(o0D%@q4btIN51c4 zY4@rw+F2O6u zjvvSV0|%sHo9(T+xfyF$ufatZZIRZ=bov{8oVRS*5-QnqZ0qdoWGK-Z_!C+YXrjwc zb)3S{W5;l+qhn6`n@*?E-qwbdD^{Ypxv45Rr!q*RHrz(+zIxS4%$rv^o}Z@lZ0kYg z%|{vm4<$4v(GelZL!>G{nlyww+ggJeR#Dcf79dGqlK9`Wg5Iq2`h^=ZNdZ=6kcqf9 z4<0<2c>Kx9NqqBL-^QEX^hU+aTgfs_ayK&+sp>){!W9`+)@lvHKyj55QsF_Mx&=;v z;~l5**Pr+oeBy83j;4HGb2Cfj7lib1LgCSXhP4O@fPjP)a?qJ^&Rf!oJ)M1;yW0x_ zG%Z`*@LfMNG>o(V?1h`>dFb!&uTcVLW~?&1cJIc~1fBM(W+SLN#mB zNV|OXb5!d~zQQ>OLj;D$6Ce{u>ei?Xh=XMY`upTDk^l#Gu_!A>oAt88sNfc*-JwH= zD*__^_20gRm%QW-w6(S|z)LX73>#W&ze@A1BPr_Xg6nB zjOoV?%P{3E6Cecklmf)CELhnz7Fk)G9xL!F=`1dpYgr)2u-?)*U0q#?6}ZpNGiaf| zuU`UG8e?RsaJ>pxrlYeHd-m={p-?#EC5TRSbl_A+2No?{h?{P>5zQ^lbyvI>)}N3$ zj)T^=Hnc8UbWVX~j`4C*(RNgV(`uJUI&$PF_8r)NR_17IYz#Yh?Sg5V*mU8AFio>6 z6H%oyQI&u|Vkzu-KFZ~C;+Ps|KY6}V!h4N zee5xm%jNn~G7|pvc6Y<`ymMyW#>U1lRhX(fRU-Ns85zOPe*Sa(?t$N*^*I_F8^fcI zKaP_f9eRn83Lb|>Q#%TilNIw${%GnEg3Mt5n){NuNoB0yD{pCOX^{FXuDF7L;*hB9 zTp{Tl1i0s(dvNH;;n+En&cn(gso&{sf=FM4(&j&u+&`+M=hPgXJb4m3ckN;xips?b>r{@Rf@HLL z$^Xc}z`$7wR7?jTGtII`sDlZn27sj^5Qfwbu1TzumH@Q*(-*%8k3RfR#cO%{+ux42 zz3pv@;1?MisE`(;R;#$@o_p}V_x*ixKa^{}@`@|))vtUx`8-0YB*X``zVLV-(SR}t z(WN9_eo1{sb{{;1zy8oC0K;HF6=L9$O&jpB_xu(196pNQJn{s7^XPU4o-qhOQfO*! z!bkq%^;olFnT(Ev3^F1tsYDjU>QQM!Wt>5L$3Djg#xYbVO3=;+UV3xGPryI@<3FOc zwe_sWnEHDA(9vS8RJ z%WZUQ3_Yj28&Wa=Fg`JXU*7vm3=I#T)AN%~r}31_FGq7zbAtb;euf-%TYuhpiSu^$ z!7>d2WTvq%sR2SERe&m_hFtwFQAXkuMP_N-G-|VC>a$7!8L@AYTXgyI<(Qb5Kw&C$ zlWgCy1K6SFZ#p#l;)W z$Hg1Y$Ln8q2OitK4||WD#AAC8p;)x>j*ovHuY2+H@cbLEQSt^8=9LirdbC=Gh=F@n zx3*xkXv60@8Edy(hEw}@H3*m)!lFfs&iV^yVq!co$E+TZh1#}UF5}TB9>>w6NAYCX z_Uzq@o}M1uar=v8@^?+ePY7rbV0d^K?d|Pnwf}?t163CI$=it&Cve~I?$-zl&ehHH zJUp>=D>iT1gk?*YM&5tP)ReUpWU*M3$@Mx|#WigeinSs=7?HV{9bJAe~|)cuFdOYKn5O!ys$OTEYM{RA;5hhsP7C6IR~Tfi@%# zpgD5*a8S=l54iktw(*fw{iayA#r!7YT{9TEs+ zGe(gVA@KQF*tU%nA+VhyayiCcV-Sj`U$zBLzibQs;*~GMsqS9vKXD5Cj-5i$DdA6^ zbrYKpXG+Em#GDuTTv7sFI%Gy1L>>P-8`3Pr4`Fp%%Ji^7`NNDZJTZlE?b7@Kks?iu;BuH`#pLRRlqM2z@wLIQWuf|=O7KlS~CbiJMHuGFtZt) z8XSjfwluur9UdOm8Fi>yhU(17CMPChF(g#rqSVXggg?vWGJgJxUtoA-1W(3oa&i(s z{^?IKG&H1KBdQW*20?(4k&&}DZ^Oew+PqZiuehGy{PsSn1fPuCfrAIptFh)ZP@+ls z=aigS=_-K2kjT8qzqylzsL6oRSS_u%jD#;+u|KED(q$C!vjRcFMbo-u(OO@jTTDfT zBB2hLuK1$+La&OR0FtTY@27~AEUE_}1H_S?k?qDCZ<5DoYR=;mANwf&;qU(*P2836 z`v)Gt?;dy{cAi*YgrZTDn?PSiQKqtNl2Ss7s-={I@c5VTVzy$(b*op%l1_4A#m6TI z*uFy1s(_$?A2*1Sa6>T9AVijuuuK#Ul-MOdd9a%DfdO zC*gTsRRV2Qq5t~6-{9b(Lr<#tNRk~mcmTb9z0)9o50#?tdGK6MlJ$!Z>FMb?!$HDE zK{C?>$V31xq$+Z6eEPGW!!LgE3pBU1;OqbKA6T|@NsK2pnO%l) z2Y9Dh}h$vgGcbUANw?Cmof0+cfAuAZMYyjH_^~7a$GfljAD_8 za}$T%i5llEP2D&=%$mjmH;iuvApZnZdU1^^@c@h)j z6LrPLlAf_g9(@#hYoz-;nYLUm$K0fg7uWSRTC!w`B;-C*TTgcnib_bQ8U*53zxtJ? z1fT3%CY{EWS6q=8=c|O5i7M8`ix+e9{%|v%R5eB#`k;ag$v)EA_!|+Q=xpXsBusdc z`!mq0tV1hA8%?25AE9N-mSMw&3lj4Ko0^;PzQ21f{_{Wn9UIO+AMbhZdoeLFsh4RA zl+`POL~Aw71&kCB4roI8q%iG4d@j21f)GGM^}J1RE03x_6)=wO{tV|xx<)}E9iW<9 z3fz*_^HqCCCUB^SxzgrYSC>mrD*V1URje!DEXfmf>eQ(}C?yE6suw@} z@sBYyI2gNRBOw)Gd~E!T&X?=D9CHwJXvaQ}KmPb1loA90*YmJz&z{6ui$Hz(g)T3+ ztbWoWsCveOqNLt^BPCW!Q5KXsN_uGfDkXjZI8F$RF-^`g;^=H7l6i!Wv;^c7L%@rv zF_f?aN||HJm85CqdH+II8j}NU91fPL1-d8}i;^KG7X!htfH`A=DQW?8sxrYS8rN|g zT>aFiqF5|)5R6_9Nq~7)tz3zZf9#{!v1=EO96g51FS}HZ3k8+H9SPE*qU;J_J7Hf; zUYf~qN;00OG2|q)6O(lD#tqnecZ3 zk0G1OYBJ+VZ{$suCpFd7v@{3*#rczFy?wGc1A zgr))XXoJ+%6)Hs|zk=f@AZ4%+E5dYU(2>1Tq|6Zf3 z`a|2wJfBkFxA#VeNgXrw)s=LUrx&;Z=q(N~m z|3ls1B|uL2Cy0Pypwp895m}{kh}4y&^VI%52|QC-&(#3SC`t!2sUEK!|AGt7*V@P! zU67#M6c=IL?oUc9RF97%UNyxPq0ifL;rX#~32D&+4JZR;(Gx@zAXJ2H5T}2W02^Of zWKfFZ{lgfZD$3`}0D-|%I~K2A-|$=>J$m%4Ei9dqQ-1|`<=@`D`_R+V^P~k`)GHet z9K`P*c%bG>Hssr#+fSh|#h%Y9w05rR;-^3RpFha?h;GM^A7`K;)g@ffwI6;w-;a%L zTDaDtIcT#lBhn3)%jLr24;A+%x{R;PnM|)GCvxP}Y6Y?L5*m@QM7Nw7KVFWzinP9P zGn%N!v#glf%UPy$mkQUS$eU9E8{xi0-&Y|HS510#Gfhjn4F&qsROV5<;6ckW{&rXh+Ijc-D|>aDp9PU?08CTHcI&YB2Z`i z^k&WqDfTFZ8~8XqG6~DHV5AIz&4!h?K!}0!HeZZ`zxb~z^A?v4kBs2VJ%hR|o5^Zr zh4T4)EO9<>1^AVLDK71G#ZMjGi`KtzqHXjj}KT%$SW-j-K43?>p z$av2Ab?XPqG~)iOW+94AAVA|*SqNN7%0py%dyQI%i12zwU7JBd(OCbgJ2m_0q~NvZv+HGbXJ;V5DZBM+K>w18_VS22jzGQ zD~;^4Wrjc&j!H630h}l*=U_k#3BCd0AeaQ84W;_HsO{I%vo+B)~0{ zQrM-Ew8Ghnj-XhwB~y)Qnz;GtSK<3VzZb*9Bd~1;CI_t)9b2Y0Xr-+bunZ+sf@%a4 zk#G}IIY%a6fs1>HAeG6Xb8wiIg)s~elH%4Bfo(e=h5=%v(9*U9lSBOtJs-!8AIHU( zbyh$jj^k(`7-9V-$z0dPp~Hv&pnx&;$i~LU@uQ#o1TTKki{gws{JrKlPNdY>?DmB{ zCM1z9#c>>rjg8^A_unt)>W^uTjvYUSOE11y%0$UIwVkl8g=~C$0_}_2Rj|w%hHPUF z%Q;n*MawTG{T(gKl++dpZB!_KQriYVIuuyGa%I(3(8T0qa^;6Q0W!=@7~uR1SPtH@ zB}*ffA0lx()L|i_Rt%9_Ucz!yNEXzRM`76`n%oykv3A7@tXsJpCr|h2rLY75Cs0!< zpp;?~)?W~&M+A|+hy;BFs1s0%j)4*QexR)=Ntjj$!(fTK)3FMV9XocGs!lWzBP0Y* zY}unighc=mt{*zOyMK)7zKKa$iU#S)_k*)u11bXzeToqyvnPPvY%wdO1G) z$xmV9rp;kH5y&No%hcm>P)#kcjAfd#@_PtwF*Ps>D2=tDR0p?+Tez*QRRULnAPgXj zR)GM)2ylx~kN|QDFh+btJC(q|G;^Uf%9WjI<|1)VCZkH(CUL#u#m)LcaNCX7;lF?O zOH34s3ZN;+=7)p}mz`?B2CODT~H19n~_=Td@$01Qq{!4Cp}7-+IAfEXaD zlmyG!Vgy^mi&IMv;) z0b4@wOlToW@@|zshB0U>DHPDTZfTf;(LchfGZ-q#F<)yMihGoRV#UTwrsdu_apL$H z1l{|SeX)|yK`OAJOk#xVG@B$UcP6PKx_p%TF|j)P*6fnzKV zzOhBgD!!7WoCO6mT=fX>L$p}0T~yd%VoWuB3@DY%BZ9#~oFfF}m;eF>Mp0K~R4Elh z!Z8)s5Y7it`Xc6+NFWaY68q#z4bMJ&Ab}u@$kJ&Pmpx zu)I<(AF`@YIp-E(_!w~OFQ3md;8V#4j1&xFV7TNX)4mduUB?=F zW{w>@hIQ-KoiV_{Tm)btwrxAMS@g^r(+ShE3%f#d)vYCt~6Rb@SKJ-u(_O1%= zwMyexZ9Wu`UB!JVt=tz%=UmSmJ$4N1*R7YHCJihT+1K}d%%k%{u*{Q(ItJ5}HS%{O zzax=%^s1|`#>P#XS?uTZX}spHKgGvB_(4oeOhi&7D)W(U+Hz6VmCueHJ7Sd|gay<{ zV*6#6Tq+e{<-~SwVa8&JL9FtF_&)}ap%H*gG=@lZ>o1zN042_-TEMGUER_~{EdCb- z+XSLal&DC7knu)>XsBL73KT;V6Ci}j1+iLr!Qk;i0>GdY#|OvoyTe^bFF&v0)$H+; zC(kHQ21zUDMD9w06DLlbIRH#83q|~H80^Cd*sFp$Dz#IOmvsIL{o1;9D@r9N5rpA+ z-mK0OV@C=StQ=tPzP%V9AFppLqcZhxb$qIgWubP^ofKN3gvk> z&L-h+ZMSC?*D5L;qbfkAmP<5p9JLJ+7-(pMixj{sUi}&^ko(R5_&2=tdAH$-#~w>& zwTXP1TIN*`7AKTG(vhk4QJ+yzTzB1daW`vFS&8a|-O!MhfjZ_vNNTMsB&E;Q+@AqU z>JtizBwQCLaK~-8;=qyPvfPqXqCo`)8PR7IAcGJYqXMG>N-;KN$4U? z+tD;+5!an2v#glaX6EzNW#Sx7P4V+afI*;Cmq}N?6_=^~tjgCYT%ZL0@SWojGsDINwTCd730D~1Bo&fEW_Q?T3_Y}KvT4B zdD$xakI)5T{L<-k_#VocXTmjFCjumD7u95n(JiB7YY_DVWGd|wOPh!SY2sF4bIW|( z{*pgQyvlF=$G_pF&wmc?`}Mua)tV@#lFxO@pYhrX9Tb%(7`Pd+M>0AlKv3zIB8BN@m8!qnF67tjtHT89=)GoUz}1| zXIz0unPBe?*Y|Pn-ebx=86efN5UG|$jRR(mpE!e7A+0NQ;`Hg$a}R7ua#z;UJ^sYj+GAPE#YqeZ!U#Dw^^6}e z8qYJV=9-p%ZJF}T}vx(jc* z`<1x)nYZB|Kk+HFFJ8*qF+j=0WWzg1nJ0CsD58jA~;_O9NXq~eGSR^IygMal9Y+TEe{STAqGf_$sRh? zf$_;n5F-Vo(y;PP0HyF+SD-Z5(@^05_=yv^_S$RXV2>mK$P8t+ZO6;`l;W&SM~@v1 zK{c}pbf^V*QNbR06--7#fEghXSsi0QX|hc8B}o($gjHDa8TfJ!Q;rjD{n zjmm~chB1Hsd^uK92T;K@MUhUWkk2=TV3{W)ZD`cWoReeg#Va&JHOXj=?zepT3S4>h z)2gms?*H|@c+T}t$Nj(hrG8CySkS$_y)&%*kj&Z=E$FMKP()&TK;;;sjQs^lYJ;y8 z(M<-(#4SXhCZ|wCMT=DzvJ-hh=jl|r&iDRjb zqk6s3aY?1vCpv!7@lB_~u}`JLxv4dl(f-YmWjHx8p|L)RpYjiBC5A8Ta|kR`2N0$b z7=|V=xzH-iGL`;DmA~uw9TE9muX;_*;O@l4INtfD*W#UT{BxFf2%4xsqx<&s_9lW~ z>H)~aYXgRg35xPdRBoCgiLl2cfL|&pH`$jYWGCY8&yXBDtR5ezZB#d|><^S2MJn?L z1%(<_7YPAmo}m~6->1CPl2W0ZhN!*Aj&$nrkRU|4X4YNYaNyszZSEe`Yb7E^Kt6L> zDwV29L#4V!&j?s1K|xZ)6(?0+X#Ay1qc8m=nYwQh07Q97(*##uw8y1V35SmyP6TJn z#vBC!O`MmG&Q5*&RM1K_APcDgHj0jw*f*-sL_48K5&<+EmyrZ=Izs@Oq2b|V>TOLa z>b46xu>SzXoNXlW*T&kCgwO)1x_ekQ4r1| zBVM%1lS08`C&2OEAxt@C5F?B!8GL1Zv~L){KX{6}MA?d%7;IHd3kKBfq3ZdFXiKEkHa0qnlIx0^0JH8 zBAl3*m>XbB^mp}TEEHjqYfV@v0C&Yj0U1$67*T>UO@{yO^Qy}s6TI@mr2O8f@S^6f zzLOmt@o^*slm@Z!Y+Mt@=ji0AQ*k92Ren;TMaPlL1U;akfMtw?1qg{Omc{!er2ruV zUqSW+VPzYk^n&m?hGE1P6iF`AS%gSSMdmEaXny_@~V$7d654oJB zu){DVCAd-@UP=iFIa))o3Fy%o$E{+~^6gYd2No?_B*849hl$Q;j>Hq9&dteFr;@=(wOC*bgO45IV51Db zPCo>=h&*A{+mhun&krr-OosjLxq#2-Ae9D;Ahb4B%T&x065O|Q9+_eh_59~+2sVRIJ zC{$2kCGSun0ydO{`hD0&T6WEZH5bvb<2@a?XR$Z5rMO zft>q9(3RrOq)?T8lx0O!r3a3j?qjYwgtZOjSSt&URLcUS+Li-_k%s2?pJWAi&8}r*OsPPm%Y%`o5dV91&u~+}0;g zoeIY^2Ih&lHw_~Mycnr)OjF82!)J<6$_O7n0d7h2MJQK(lm<+RQm*{qvoTGl*|Aco z@EQyQausP9e8H_Ogp*ilFiT~fNEM8SbYCma!k*o`=N2r}2uP*|PzH&sRFVveiGpef zsoXE6ElE5csSKEj0B1;r?_cra7vRXDgR?ri-~7fmuz2y}q_Cg1kVv$;D}rAX1xXd~ z3v}N;rNGF@DDJ!OJ`{^nQjvlGB!-g2Y8V{n5WZ{(F&Iy$q3**UeqykFgkt2!%3BsT zTyQ?-&1=yBQKEd3_5=xp91UL*G&+|tFb$C?MGD|V&j3IO47tiRaI${{|GV>`bcKR& zpo|2}FjzTBv6oNrk^h0c`+1~sgxe9<0AQ(U35a9suywFPYecsLw+e=bf1rVYFKKSvMEnSMY zzvZpkII>C<{)~n%!xg%C7JK`8@h@NaLUrN+H3q0EeMJ$9#X~43N_vH2O-6wxax8+7 zn8NqMu@YnGd%6WKVzXjALRO4qK+-H&ifh)a!4*$=N=3X(CX>-F@!SH-XaJW8+9eT0 zqkn*65(-a;LCD}j zl#(fiLEx0j*n8j*=Fgvpb!%25Z{@igCsf)I^t$c_*X|Dvo*~d(=tQCvIDUYDP|A^` zK+o`)jCWAVT)P@bh96siXfo?AZdegIj)Q{-4`9>AO=k=+bH=LdB%vIm1lk;RE0ta* z6TZlWES=V0?okrxs3MdvX6415@Pu>km8C@Rg@}-CP@)$3k3etb=mKJ5RR>90K|&`W7Ne& z6-4SRZN;gnWE+emd$`j@&ZwaV9mV@#)@0lblPt4jKw9pr1XQIgpA=NiM`R>9_?%bU z5gm|IRDdx8Kw~@cc8;xqV3^FQAd3TNZn^Eb`0)RIA3VMSpA8Vg6OTWROD?{I_t}=p zO$EU~0lXx#!|*YnRA+~=#n{u3*Het?(WA$(W5;#|u&_A@h7jcQO&|v0paEv#y;Qpo|ptWz_e2O&V$z#zLmBgOa^2?Gr< zOE3j9-wZ+lr|5vB)4-yYAh|qfso2o`9z1vmYgVtBQ?LxBtS(_Cu*{6^(A&h0fkd8NDIZx zWPyMKXlMYq9?VQS>>sxZ&2*Zzb8M+dMS*F@ain!?7QsB#z8ZO4j-C(ZW)4#1z*W*nurPUtng=2iiBdN5XD-NEGHc7 zk;z6rX&@byWtdzrrKBP(DJj9IlGd%GT6~(3En1dI4Jpjd+%V>$s+M((EI=7M^`o-M zWYRz?!^e~VoyjtgQ6{U5XC{N{75vgHU&HWk3*hYA~XdWUWq}5R*>G04Hwr$&{DL_M3 zVIazLsJJ){Lu<$Oqf$wVYGDRctx|$6V$=z*ij%Z{KNEB=ob@vHi3^6oUCzVuc85TxJr$L2M6ZB}Q6lgjH z@trR^*yE-2>~+Y@D-X$0X}!nf2vq4So-&I%oBMn}KMhv09q*a6O*`dYB?)ZaRa#ETH6;8ENJUr2r*Vy6#9i1A6|& zz^@yh-@`|aNJVHRw3`iC$lODaH?0}plZi6rg(S-+DHfN=6AY~hgSqoES@z=O@hvl# zV~WiHd$Fg}>>L7BD%^uv{w@i)`JW14rslC1U)co$kF{%V<}N!ID3^hh$6Sj7JmZ!_ z@@Sqf#{eHBEG;Cx00dS(AB#)PXpYqP6&S6ey#Ev}Xj=JL+Jc$Sha91)6p%95^`_G- zB|?m;X@yo|DywrQtlL>)De032i(0~I2KQe+p{!V*hsj7}oXl876WDCZ5IQNk@naEo+o zauTlZVa3v=oC+;mv@olW&3Q=mMzj(^o4b=SjO zy$05W8$x$wXBSGRJ2AOq2MSMY<@bRB_-Pi$Qfa>eg>FrZiJuDl9t06cKG9shj^l9e z#Vlb!sQ$VpgyRolh*8BQ8cmhZT%zf49JAR_@hR@&R7(ryU4K1Vo^l0pt5?F{3);Xg zqIl>qiiZwi;(-U?Pfl`W$j30{={cc6z(H&Q%l(w^&jbdbN|boBrbnU@nphbyErPVO zTM8q>BtaT&XH^V#YWrGd6ml%MM5?4R{``rsi;t(rLiY zz{+HhyX5I8{Oo8$&ynLe=(|5L=eZUG0Ws5Ws{5)%;qKr zq!J*GR0<|nbPO27K{d@SKswEV9T~u27DZ<1GAwxIE0Eu`Ir+Sc+c9tTD$Kj)T1=kq zLhskU27B*bX5A11r!Xlk6_e)_I#W~3y74_O<7_h=Rci7CeaCh*2AZC}K9rnD87oSs zJx7#3M34<6~^1Keu4=q6v=QH@R`UeJJWU?3;8-?SPLnWHG(AK&LK|rx=*-}8W z6ro#+N_^2Rm(kNVh-FLLIOzb9z#!>nSCi}oKt|z61*lel*C4>)*aRCOgUWd&(R?Uz zzlt15!vNALWHvqp_}MoaUeoU0vjn$Z;|P`3A^R9jO<47Je+P5*Y8VOb#psr5YC`KB zFGlkfSK`S3{l5s@VZLBVG4yFF4H$mp+=Qh}i21FSUpX6TCjhdX(JE#f5@_#A@$)QG ztdSY)8wIG4h%6eACdo8zDvOK;pi$}BblNrcK8@3)HwCIea$=H&&w3V`T3gZkuU}*F#=s9*P*heGijW}QNCAe*EJJj> zKm}!`5>ZTNj@09+)?s+wBo>*l8VXVz7$Z=i9&gc@w^{>YZU+@As!S~#A55D`|@1hW~4%e+)hYx<>188b$Qj~u@KBxj@qSjxf z7-Gk%PHfx0O}RWHgx3>Du27j64;2ujB1aAM4-DYIkt68pJ{^HkhqP?b6*wXBl{qmC zw6rwi(k+{@dBX(^KqWypkfoA{L6FU4(Yj~>3?miR9)rRlL&KPbpjDGRq?Euem!;D0 z7#PKOe)kw>m0<-+iA3IIKsYR)WU@du4U*3^FTM zW9dBJG$-@t&zFB6J8=?U`0|$`9G=P;rb7fMayix}n*}mCb`P2+w*oUj zz6r==u=?-*4%USi&S)G9J9eP^)1PKz=GbhUN+tFQd@l3Yr&MC#CGL6#1<%inF!)40 zEYpY|-g5KJXm4+i$$igDRG{Z(Vcr>i!aTW(-v?ALN}Kcv>N%H^FBz!&1={(-;;#Df>S?`f4j(?8TuyJ6K0y$~*?tJYI*wSKb^Jm?aa>JN zB2wu*6BuW+cM9Q|>mDFQ-gfo~Eor{-X<%;E%7aTG7 zUqb1s2zX}Rb=S{W2?9X#Q=Wq6E3Z`MNX;EID%V7;Xb{Z~Xp6Z<WbjK6vJyYZS=zZ%be_OoRWSgfW*xh6ZIT$3!}L=nn(>7x@Tj&sN{ zkw6xa=0FJHiNuB+KOz`oxJ5QFG=zsAdkhm3lhTr8<##x6ho?xGmdV_pN=X@3B8i3o zB!dBCB*2Fd*d+&#?c9Z(2M*%$&71L*i#IdiPM(2kN=^yoav3GBjMjyVc)2E49fJ}M zLLdZAu@ug)Wr6Z@cz%EYBK?DdJ$UTOYMvJY7%~}Kw6WpRR!K6$$|HOA)foN7|1z))Pd<0b9QyCMF!>9e$;u(y zi~xyo)TC*KATeoS@Z7_hlo3*omsiJ6vdA>Ez6fAUikATsU>TMS$y_hT%w3s1? zfl|pu*)4|*GQu+e9z0j>88!xijPVj4hg`O3Ug*YQfeRfjuX@0V!Qz92fiGw}1#{(M zEPLJSrnSO7a`fn&Ju5-LDxnkgA2G0O>9V;`r>K-(p%QBI&!XFIo7L0LyYBk9^AqLW9VDNN5vLrcfx>gj*E})M{FZpDJd>L9yJW4tQ?lzbSW0!{izvZ9d7c-eqB`%q^Xl$jMOh3iL-aCCQi<@A zOGy9_Zb9Z&uW1MvQ%h#9Ud=!{(uGL$zC=DWK{*Q9`R8AtED#A?*PZd4kjTHpGFFNb zPVBRR7}Z%mk<5K&V`^>QtSu<0>JBwT|Dw8D^tp~RG;sg1+K`Yr#^U4qUV>AU*AAT< zW|?|qq`r3iW_}%Y+kFS5@ z8p~obK(>m@!1Yo{EGMuHz6vMp<5!l<|t#W=?+*a924|QHKPutehYx#ZP{9 zAFjRX3S58HQ+Oo|=Du}GE-W)E!7>47EXtT!4wNzU`$c)VffB4)wiq3QW6a9q#hpwu z$AB`~tW=0O%K|Y?z{~+g3JWe;ho<>0!2A|0f9cCG`m_J3I_J@CVqya0=5kjym2JjGZClD;Sa6=5@ zKU3zLMVDWO;_lr54|AAS9!Qs2Y?tQ~o0bK*DV;$@u+0dN2MNPB05HiB;nSZZf^<3^ zmSQ3Vixw`#2oM5f)bNtR3?ut2n0QoKHixFxMYA@3GA7sBrpzy&^S}>fkwVHM&r*VA z5awNnWv`~kHnVWSLiWBgE%}}d0*2KvBWyk5yvAG;`_^RzVwQn2h`1v`EB+o->q9kJ z)t@TacJ%u|OHsJ(1-GNQrKPGJw15A8?A^D&vTKuDcQ;&r1ODZ+pTnQL5_k;6)-dPyGNr| z zUc#d1J%3s*pKaT>S6a}{_~UziSnQRA<+ILPzkYVzprn?&u>#thU{*jf^&@(c0qm7z zt)fCb|CGz+8O{lNCe>rME?U%(XS6aJpUmlyRqRU?&@)Hdk|m4f^CuRuzUNmR@3aUW z>Z~f5J((KBv=l}b2~go>KB}_KL}dve7_}W|4~h4sl+ezRIdGLhHBdk>fNAEFWum-@ z3aSBxW0&NlgbXwxsOH6Y-HmU2J6`dMSI81rkX)o;SuB9_tn%_PV`F0|l}d~qhH!Qm$rxkF z(mj%EG?l_bk37OkwrJoWWzIS8s9+hP+~U&8Y;pILTG~oje{5evf@N4=@&Eyfwj*PL zR-SP|KCx>LoRWhV-g-0Vf+U!lnnH$GI2ZS%EXPGyUl@p}1WE}EBZZchW)Op5>HKC^ zhAHjAY+`(gj$H!MK8Ro*;eab5xJb<$(k*$(bkX#ZKSBCE?}9fz-ViWh&)&Vb=4nrt zu=-gon*?Bdd`ynH__=)fa@lr9U>Tm;qk(0(wIi8P(rLyMDxNJ)+-x?r%*pjSjsx4V z;n+n8d%|&S!1Wjl3SU<_wk^{qOkS~FmTTg`j@Cu3C~?Lj0wCb=sOk5QU}ACtEo7c7 zP1oAmioN^xNsG|TH--Chi!dB#httJT23ir<=6l+?W@}8fx|h&#+!y- z5gAawu*|Aet8|DAAqadwtiDrWGtLcgOl{>BD#xIulJ@Uw$J&V5Y!*yLv5$5xGY}vA9cEB<; zfjvYecu07$s?T}3t7}%lGVbsYf~l#W#qPD{Lf@yq`VKmPVNP$*1R3<4iGcmVqk9>AuJ8^ar& zTP;}uf>A8Q9CuBK@x1ohYjN$h*TyVdi@QjpC7eVhdA*d9t^o7gEqO?JxI~_yysJf7 zA^~Uu8L zRl27-`_OX1s!*+8d zVX%w@NO3+-A~%{EaUAzz}$C&0mY}hgcC=QI(F!$ zOBo?pMjVeXfL5$n!JmcD)g~0_Oy(n(?Qg`# zhMzCr(u^0~RXeZn2S4~h%yO%0Au8aPupn`A3WaKYQyRz`gO#j6!mY)rsVVgJ_t)@u z(yD0`QFy)frC3B%Vycj!lnA4S=1OJTfB*bmluKm=rBA}Kkd=4>#X@A`;kzCJKL8;V zTh^|`qUI(=Yl@$|URw}=27XxAE(kEX=UD7gm%in#({lej`skx`25u|bMZP8p7eE(Z zbn)EyVk@{nL%OX%p)(6m*tQ+s@bIjie)QgZ<*~!_1=>rzD%(HbF5Iv|yB}@anU%S4 zT^E4@U}|k`tpS*cSbu?XZz|)6AYj)%OWXK^59s44JRvkto{uWk9cHO^)#_ENkjrJY zS3}u#!_=@U|57h|=}X`5x-QZ@2yELnT-U|)WgcADtFv9w@gnahZ*iCr`rnePlS3d#PN49|Q>e0NG52l|Cw!@Wi%l=Te%#*A0U^@!1a9C zB|AQswu7?E@4rl%#VG?HzVBgjs))|vF?JoL5_~^^7f_@#SrEg3Q*z?7sJXejgCt&WGpuNDDB9>$3UvJfyjo#c|8Czxfrsk^`?;L>d5mAI{_?yiy5Xsf09`@^Gf6;FZhp+%nQBg8^uYMR;x* zp6lX8FT5RXt*!hS^e{L$C_ObZn~xGQ=?o0RKqiv~0l|@D$8f5ngFjoGxy5nVSouD? zcT*`iwhb?!@F->DoMLPL)RC-gp}(7ZW&Ht5?-+g z&vB483@)2uC*(H$>Q%D? zq@bukc*-pZL2NCoX`d4`doJMlu=nkou{Bfb=)l0Yz8R81tDhjW;^S6Tkl6nVFW8`s zRmmyM+FUp#M?3b$3pP|Cjg9;+h{65%b@~$%=>E)SW;Bj|v54+ZeHy{|7@r@P-RpAB zys#gjx(6i@&@+9jFq6q(_3G7{WnM0qd7dyuoWod`og2(D!3^6+QKEqE_MA~pV-U{; ztkzb|Pt+7^paW@y3pOHq(VeeEQv!li4Ga$r4dKBDAJpBSii;EtxIeW#y2!PNgm=Of zo~SpdR*o-B6;LQlRtRvbE>X3>OcX2wts*yxP;0Rpn~^TlG*@&SZjZF6ug3pQvHE{f zX>!D?hB_s{FMj`sgrVVM>3N}mCv9eiQzh#45WUv<{Q-aj@!k4(q zxhq{4D3vk1^#D4)_gjn}IEB*4B$mGUtqp+zglzx80~jA4pOfd2S)Egwi|OKvE|ReH zX@g~yB~T(TCWtCaKJF1^)c>9DVB*n7rUmS9y1LNy@lPo3&{wYjj1wx$R#yX>;$E0bvSC_p74bi>`hPDclN|Lx!57mE#pWdgg1u1|at zrH)fPFGYY>e5NnU zG~O;#rCla^yo@xbr7WWyj{@mTCUQKYw5xI*G%6!uyHuK+(_EI~eTkkUm67gFwr_^_ zIV1A`SpWESNLd>Bd=r8oz}_8Owfvx8+)4(9eWU}&_1t=<$W%FV9K8oE#7(d*FV)q1a>M)Fn(bzqi$z-a^nwy%j zX6+ix{_~qgxm3pd`SWArcks}m>irYPBSb!f8SWMZA#85>@dQ9Eoy~+Y9_JO$M@Crt zlp>unSUZEDuyYpzr-YV`8<4TA3hx|UHu<~XVff$vjn&U9myq^7q(PCP0Z=Xjo`*C7 zqzOTW5Vm*;e54HnX@ek>GUN+iJVP&d-t)0~)hhXX6bh3(znIR#e54Riils}J!bqhg zU~S)lgDBdz1WAkMJRE0(9V?qbCX?oXHNxM0DJJ6!48Nxb<->>2vT-xgJU_Midpv_u zXD50-_gVNI9Z35=GL*6fU%8AlfOHTrS>OY%%f^ooWJKi_-$$CiZ!+nWRNCTQpHas^ zw5#zf+~B)-08s;^!m*Od zoDgL{5@$&uN&O>{w#u>9_DaIg`VAZL%O8EOYA#Z-SVSh1#)YDkic(`RsfaNU=1QvH zB^Nq|j8RM#rWn_z@`17fKGo5Q9!|kUVzua!>O1P=7BWRCZqF2ReUelvwD>Y9hAK>D zLDCsstThu-bEVR(y?AkQqHuL=iI!$T(K|GZ>#w*R1}~jdcD;y)Mk(G*!j-BYP&DT) zWYTFYYH7xoa-e^mf+z>9$~zsH6P;&71W{5x8P+}$eC5bW&wB-lwt(TCC&jP z&WRHkzyEijo{MZ-8;pFMc_y%nn0)*(bbt0=F!|VHp!QVgBIFe%*x#<=iVY$z(m;3b zDWH@;zx!3lX0kHRbZBr;BlDPzEuYWJ3Kt**u3N_b0|)e)o*H3!Dn#oVsjynTcormL zmyM}6Hjc61KLB@V5QGq9mnD;}7q)N5@Xvpa{;z+XftGxa*KTw}M6DRhz+*hXo*P=< z+IZ6BywPd6MK8GE0-P`Eb@DNooSb09<#lf9B4@EQfa*4&OgI{{p%O2vMBuYo_lc3HP@i6wM~I+i*__3 zv!ntQGBE~4+vctnBGXQ6+Xmr)hrDHh7>vzDD7!poX*qG?IA;q1OSeEm$tqAdMKOK` zlZlc>+;TJXc~&Z>Tn04^kW?C0b2E@m!**SnGiWt8fu;;No(uTwVYl+Ux_!}>E;K7= z0s)0RH6`N`Rz{S1DzcJU{Uw~}?#B76Rx(h8V7A%|&4f$hu6%EfdGKfwISB^X9RfxPd-Qedjwk{oU`PWz8C7+S(<{ zQZO|IGSCnB0i41lyEj~yvBUTtoXG-vF}p5Io*v;8ruYlngK4Cg3$a*~FLB8nR4n51 zi!bJjM0RW2wu7O;Vfel`i}OJ+&_93$3+Bt`^2*CE$Jf94P4>(X%KFC_fM!0=#!r-v zN|#|aw*ZFE6m%-huD7K*oDZ>>eDFa`JoFHpav9d9jp6%`QjF}}DS1K7bcT)9RDl)S z^*zQCnDKp8)`%w&SChg*$W8Ky|uL_N>K<(FNClH$KLnNJOSsL6SC-hWUT0OgV;T zIf?TUbzkTfmdHCKdOo-+3wZLnJOzNeUiT*4_p_g(^W^c0s~Fq1@zZtP`;i!$ld@EW}JU6Q&p_R-oV?C9>2BRj6MQQYJ`}ERz1Kl&^u20!69~uzD2;2(sx6>~e|6 zZiz(xf$*3D3CDDFqJY*#3y?{tamRI6qGx0R-6Lb5fe#Y!oHN0??|STVC3qkMnl6KQ zK6A+#2CN%zhyTlOH@q6&xoa0LyW~DaS3~ByE+!`?F>l^HY4P85<4ySGFYgU0 z{TfM=h_bhc-{s1khQE7mSk9`<>+QKNUug38TPT2TuT1#LesRGH9*~qei(-i9nutFPz@j>5TziNd6G z8An-TDh9$ygw$C6%|I|D#oasUG<(_Qvg~78teS(>1Xy_>ZvmDC&* z(;2o3HqwgAEku*;Th3kTrUG_3Axx9d>3tIMIZhk6=nQs`3QZARVbN8+}ph#q&p}|4MGQ|00 zo0^*Nl9#-sF@R4JFeVA?K^0`7m;)Nba{XxF^94nSE~UPTGz~atDNtnWWlV!WCYXmg zpMfMtkid?_{NV74+S=M!Sb}>r2q7378^c^sR(hVNl|%y}xb~WBVu;H`HkW9Qq_|$; zrsN7r%X{_xQub2bKgnod3?}--f4Nycv`NJp;oWTfmjOD4!*)6TvRTucLLrJhUyE4?+lf zhQ{!j|M@L!HvmbexKihCr*xLf)L3Rp0)mKO1ey)5Z|l6YYG1B@7KQi)woV5Be3cnd-v`^;Q4bh2d!;wShH@e zCj00oK7qr>j_HMWi87voZAOf1N*P0O52iCp+EFS}P*)AM2Krd4V;E7EES19JW9vrZ zgQD~jSJuLkt;ADWWqZm~o`R=bez_*=>+6GGPYgSg@1~BVrY;xSO7`c@qqSpj0ly6(xzlsMbV(PWv( zX49D8Y+=i)<=B3_1D+R1tCP@>>|4w^>q8Lsg#zjJ6>#=E!oUZO{tXMuL zPh7EB#DWD2kWQzhzFD$l2_Ad=@%j{DJ&LlLTUAmWQW;m0`Dwn+;G=^!ywD2DQPb9Z!ffV#0y(v5Ta;nZN=+e^BV2x9~mCO=*TGMgwPs5sZ_?|#fv3RE&#N* zwc(*h9*GzMr1lGt5T3KCTZ+0eQ^rN)2FkH>L-(gEfH6TheyTe(DJK(A5^yx^9&KH; z2)EvRvqrEzF*&JWQX1LPV#nDkYa%9PX6^J6PNZUqBq6gESwiqRuYx#uIV3e8Ey_Ac zu!o};2#w0r@=CG{6K_{?q(xzJj!{`|Na>$lDuw_W?h><0wnR4y@Xh*-n{myp&%$qi zeoxgg{r>mAkIOE(6qaR5S)Ng zwP0{)AbIR1NP>iArwkH~v*U0}&|u&Z!(ih_SuUYzH369n>>!XaMa#;e?S(Hy_KM4p z;cWb(l+yUI6Bzj6PhcN91k20;24Mgd*8|FBSe#v_XqTAf7%-mIV;x;6@x*1{^O(Db z5IN_R1D_}e9H+=}JM20LD2{e?V(s#!u*@7*E?I;Bg64*tT z#XM6f#+8~%!Je`iy46U*YHEUY*$o)^{ZATx26yk-gN+wns6oo(K(y)0PMkQ7O`A4J z;MnR_t8mTJpN>Z!eYC!JMD%$hgo9-`*n?XkW>XU{ZDq?svt~LC1O$#f#VtbKj_~`L zdl*d4jT?^<25{`6wEh_2g)zo33x$I{#Cf@5Fa_gDu_zUxnakltFMJ_NPKXS(OcTSy zL$K}QoQyq_app>t0b{6Ma!I`pxN=M^*%vLt6uGaizWQp| zrIG|{777InaGJ;I=&dz(5LvnwDASq4?O%`zIe3UyC z%J*^s3gBgb{$}KBlxebU8(;tCH{|)E{m^5G$%*uOl0EyGN~MwI5PIefx0&-EHKPfNR1S;2&HwHMQY&!tbX@jqxA*PlS)u?5w2X0)$e!<7QgII_&ymR zn+DCKL)TC$6*ATESBhOKV|081gfIg4aJ~W=XZ3?XOSYFuxdDai`8d(t3)f@inpU?h z!r%SrbFs2@z7m5{R@$xy>UsQy!CkqY59)a^FTNfmYfi&rDB+2%TdQ*5&g3&yC}3=C zOe0iy{_~&D$`3W{)S+=+QOz1rVE7)7sg_HCTVi00k^?vnBgM9D2IMFff#MYZ6vK}# zK6dyimDtA>AH~v9ElFRErLn%QS+xq+UvsS%BCu^6larH`S#xIN1B&kM)3NZx9WT6H zw^~IEG-esQ%%UrofifS%QYjo0b*$9!Q^!<{qhs?qu*3N%mH4qE%6FUzRZZscL!@uWq%p_-BajmNE$+=Z1s8;{Zz@^`ypY7ktEk6Oh;% z85-IxkS>*$mOhS><264urVZQ(6+Kmdw4Qqr$J*BY8JAscx&qTtPo6)7cvW3qgRfsJqYZ}hVs0ndVTZTJZZ`DlC-=hgM8 z6Ve+93dhI9s#nY*MsmKNWfV;&8CWC|2;=55K=C4rff_C_h!o>1QC#?9Rqrb%p-(0=nvH6wd$8f3OULotA#Y~=wm*N5o_6IYSKE)Ou_4^H$BrueBN`kx%D3Md-Ga5>c$>K9Z};)y#sdpHQ3PAJw=}TuO_n_1iuF z{7v+KF0%b-^W9)x2{7G?oW6^J9&4bwC@ZD2w%QpLN>4jz@!@FspEo{luTe>utR$hnnLmmDorQ2XDt3FHpP#a$LXsDcY>gHX;08B8bEw zW`ObyX*M<183k`@P#L0zKMH8qh90M<7f}bQa7|blnd&v@eS)5Y?LD$gOlTx8u2pr5 z3E1TtyRVZa`8}-@oBlBz{5_1>4;G|dT1b4|`|!|gHKLTU@@=(UeLVa3sD`V6==#_C z9qNgmYQJ}l0%9#;QqNu!J7o+UZvM5@;S=3e{%W^&tf>lX;uo}mBmgSr2{wahQ zV^d5C)`K_g+vxtytw16eeKdu-M#8wNv(x+WW`9tC#{(a5wSM`Y#l6Km!jh%G8t+@4 zDxeRHD^Yalwb5#^dlcE~EP$olilcY154XoSHXcRCRE<_oCRVR4C=A4ho%(f8PM<@p zj*tRf24&8ecI@ZoHJGRMJE}l+PkZ4N917nTsS{jg+DBr ze)eMMw5H8?4>Z0wbxBeL5r%^8NMXvtxgV{7Fa;NgR@9+NS{1gepL2iv}v5Pb(xk z4QysRt=&r%wtps=?)vjW%n9p_<-|nN7hdOMY0aml)X!;# zfr?yUm|@T;Md{-@>c9iqEncWNvw2S2glvlw$%^Zf(=%icGQda0UsC%X8Rvwg6O*mUm_yYm)R-LvGmz%1+c+0Y5Fg?xLZe(>8{RAj=zk?C^|v-TR$B=Gca=h16QkAe_5*wYgnv%tce>ezg2uMo|G_r-EDx zN+vn7y#5^M5wxXE@1N8Kb|-#|NxbAcJuSoe_RDo4l>C~Z8L{0_hI11x)E{c1N6rjI zdKF_GMID?GELkT$_iSTOMEbCZ{m09k)%#R1ZHe$LMd(vY1|y7O^==ih`|sB8tUtH5 zjHvUD3)txe&`jfveqKL0>vlAzbHXRh3#$2nMfNYr;&l%tg*B`sps0OMAT}1WDyDYv zw{o_6$)C3yPyN)&LC?`AQ{AAgsy==SaLRPc3;84`7F6f-9)k!GwOS4d_Sj-ndS!7; zHPD31CdsT$#T3^1Nig+t`=_7yUH3XqU=wR^CoQ<<^?Q9$O^?BFU{AXFM!ro#1Us2l zGJ)>?Smk0E$A~_<1iZ4xLBgExfOA%fv6~OCB&RPL-R?XZ8L)U=Lx!@59=dVM%Ss1} z8|=Xt>Kr=7-9u(Vr`%0jF~iSpJLN&=_|HnJ!uiq1=nJsA(^0o>N-*i5Xqk%pGpQcH z#GUlk0l9{|tKJcvp6e4=>Cjh8wZqMYCkXK*voEc! zJ6(8RO)B$jYfOhmWgor$0s3{e7;SZaC zzFxdx4nCG5v}pBk2=a6GwrCBLUaYhFNZ)F^SE~61uLvY>Ngpdm0$c5Pcs&`60UQ{|vSphHN3NZ|B%hOe;k&D?6Gh##M9Q zo!pgQO6(U0P^UNW#Y-v825>4XGn6U|lRQUt@43g7I_|yd`=nok0|NRTBLD^ixH3<} zYe{*gbRtQUiSJosksajt0iGjf&v)9=V(k8-g`n{0n{IR*(>*>@B{;q~j3g#N&@A&U zX5%qs>tYYtHJyB8-1^hCgh7Z?g-^?VtcxCBd2ol<1C^B1nKIE}pJPc}{0oN1KU_z+ z1oWiC(t5EPmFk`F&Wh10YQJar4z}(jC{7hcBY)HNzmxf#0H4k||9yw0qcDuBWifhy za)^)2=27zXJu8roHRyWxaF21eXU~&Awf*~Jy|RrBU2sr*U2MOgULXf&xuVj15{W@+ zWb}s7kz4`#ZR}zFX#U2rayF-;|Hs3jlO0Ii=!4|w8USh_=QA5ncN)KYX4+uO01#(M zq%N1s{ye0DpX6hpxFjPQd-ep3a=Gfh>ARSp3{ccr+FUati=BsYIwU$bvwyfInS435 ztEP~8!tlk*PJ@%wtHVV)Jk@jBY56ukAk(MQYCeYN`MpD<;iT5Vn9(iXUB+4JcS4m@ zh)CzT-99Vrlv4dPhHZqp^crlsJ9s5Uo@zYZAAuenCFoO$VPD=DK1ym-4^T9yWqi<= ztlsdje~Vf`g*&bRo}WB4oVMCAIkdV%#^KS=rrZPj2eXEp1cUVKIU2`NAP0xC13Jje z&%q_sivY3xJRxTgasM6sVugP*(+Y;7VXKobPM;NPz*!>X6eY4ZJ5X}h*uDUV&|>yp zSY>u<4;JRaO}GcRR>wf_qrpPXKCZ0Ic3!S~bu@SLUh@5VGmMQuz1$|K8T&w^M_VD* z(wV6w(K2B9IUKm?^0HiL-^FmQYMabDlehKN)mk~*GKlPNRQn?xMD@1w7HZ6HJIP!2 z=MF-h-#6w+OcPZGX|{4)&E8oa>@8drT%KP`1n!dzo4USV-sd+Q`8fhccTmMx9&5<6 z@`;xMZRhD40AnS7Q03X^T@Ly(kpRl2DtfK=jFLIr3EZcl?7H7^{N3l8+O-b?_!O@Q z@|^0re^PaAeW}k-vep0`NE>gD?%2V&Vz6U+EY%=ik;){*10c0Ai?@PiOxvk}F(49) zHbjo(2V06Ol=67LV_cw>I?~M@gPW;e`4Rz##@cO)Plr9@?eaq7LK{sn;mn#@n`hc^ zoev@p+4$I2%>UU`mqZfjKJk-V8!}n!q06#PF%pS-6R3M3hGJBsI^)8f+8RHCJskL9 zhOXTcF6a7GkkdnLVij&Mm8GkrL-@GwEV~1zW+ArJDBlNBzFSa^XTSt=48rZ$)rLV1 z5mjFtOw^0{d+hO1;R(#X+Q0(l)V&q)=oyMdS}^y~d_-eIv^Qy>g+|Ibl?_EDv989+ z4kTnoj^FY)x?l*co$n`2#n@E+s2JgrE)6lMKIAZx9=Ky6+JT_^gm)TZ_HhrZttnp} zLpt87%nC}yj&nTEPYYzHmP{)i+5Z@k>J`y>Z{xjW z1p|2SZ!1F#JT!_)ic2_4l#8p34Da$Wzu`!Nq^?$``z_Y5f0|~Iy}*{59{y(~`Nd&! zFL#kCzaY17i|H*>G{v7Q|5uCKXGJ&EKybrgP(OM1)`+H8^^X372hJj@#>uBop9lhb z?)OaF?J-n$Z9H}Al~Y74^8bo0x4&t{&6XGNUqLv2$#j^34-9d0s4nOZblA`ID%^{y zg%T2Z(i!SrNB9)T=}G(+M{^26!#F3=?K=5!q>{0HTZ3K+J1%+K;Q165+7%UtYWS1G zu?&Y}yEw%2ION3^tyP093UB9mlVeW(Vh;UfBq^>+$IE*-Tet7PMmpNI7~G@m>eW;7 zx@QQfc3_|;0eK3o%D(d>#txwjYG#4}EA|o|8Bi%ViA_O>HB)QdW9S{)bOh*itIR6WM-j3>UUzK;haAw!vhPSkL0bBhB_A~9{pVbg**po1C`0=|@Bid%3L8y?dxV*Mw8x*AdT-ro#Cj2fzL$Ur`03nEBRnj+fF`xt)D16!j^x4!!u+cDBc-qJfm+d3J<_0+8MK#l=MtYC!Mo zOV#VA$NHnMJS;Dm;y;)7%h6XG7IHAeS*KYk<8-BAH!zV8nWzgWtvE8mb0Nj7BgzWk zOUfZrXTO(#nD;K1arJWM;rjgUvE%2ld3MZF@8uWM4!^wQ1q1JF98g}lNM`KP7^eXd z0G`;MFS;ubVT)#-%u|g|<)(;kM3%$ib2R~)_|6xMtOQcC45Q+)mvVEIj^upG9bd@! z@&7yIiVDzCWf3^pNQB0m!{iMK^I*pBc-!7$@YBsST^h9RQ|D!$x24DTY5^6yJl1Ei zdLt!oU|_(U6B)!a;m>JM_gV8WVdB_O?JPz6mar==Bd+tTW#9rqBpFu-r>R3bH2ytpbih=M9KM0(`%Oc z<;$0?ylEIF-J8(=6NC$dHTZF}SHf*&gJJlR7i@e%<7Sx1)Q)`N1P`@B^aN}rRSZKz zMgq-G6534!NwjPMV&+kMvBpv%=QBu?dIj$qxC7SZB0hOwfd5uH;1`P@5h}4XNJmAK zXp$Y{8${a{WMnp#ue%lJmG?U3u_|zC#AjxSmkiiDzMiu6A+ZjZ?dp-y?ZnP(Kjq4z zd=Kco3lj*gw6;QRj9~-ex;7WQ!plqzc{yA7=5@99n81kovoc%q(oA+&3cJ$SLo5gv zSRN!~!CJpApDDkCo5eN%q8-2Lk87MpgrZTE6~YF2DtxeZCni<0T`T+4u8P5`k!c|Ta& z;r=&Dx8bc!4svVtRb9Ve(FHevG*+5fet}b%n&th{!JuCNC_)zVK6QkxJ=`rlCgHx4 zm*TI%fI8%rPH|;XqzRO^Fa>vZVW7I8IhfT>>H+IB?hG{20UxbW!?!#p=ysAS$7d)F zuodPtj&X`+r*|NRj!9e%-gw{KN`2H=_Pwk)_J@9JpO@?Dnq1o~cIK8pGs`gs)TN0t z9iK7-SX5dZc19h;uWn6>&#u$nv8af6e2?q=*CoikWN0|CI7XtabR*EBNt(%d#;PtB z@l8#m%77jd$;2hj5(0dq29^&#a8D^>$LG9cjMaZJ3SY^@LVydJkhKGTn8q4-sZ)I? zs56$?#QLJ5f-RIwJ1kE4rd{SyJQI5`nbSNlVB5sQC`8*ERCksm>f;6PMdgY5!XB)` z6F%~;qoUgSHNh2tn5%eeR->p{QKEfVziJf+N#~Y2hmlX&7J7eUDd=y?6U|^NF={C#Kcm*IY%j z;pnXCE-V7>Fc*~{4(z~jq&NN8zJR64&r@4I?1qj9z)PEJm? zyZcHb;dUOw2iv+sQ7upU*nm5kpgB7rd{yynO;2to3vVrG?$&k;I^pCdnZ8WHR~=r1 zs2?*lQ*JpU%X6u^%+pf{dZA43`KSDF<~H5V(EU^*ZuM(UVyxf060r32U{F5a$WHG= z7Gf}ntS&IF9&=ju302whuw!nUbW9R3F;J?k2i0-$lz>~=kxmqt460T0V=tQlpEnT7 zA54BP`Q_xS%t0Odkj}=Ee^r=)scAYIqDjvGoW}toC%3gC^5H0=jaMMrsQk+PZKr~j zBh24@Iso=NIt3vwcMngJ)>+W0nuqNGj~mQ}1EHnuzjllub_NRe>`s8Tqh#@wr2@<$b6I$#h8p3=N`T9F6jeEl zv?IK*b8BebzBkSwARyd{yx4bz4h3I%82;B@yj8;Rx#bH((R6cgN)I1T7RDLHW(`;U z(3d4^Gx? zcvrLHMS>It2Wx*P4p3MeodA5_)=n%u3yS{Tp`$8+HB8?N8q^Oj75 zspI4a(COsgC@N%U@)xCWjxsUuS*G(Dw&CKST^(~juN6NE`F^q3JJ2d~b@I+OVPid9 zHk;Qh0m<7fgmPYnxfdliqn5;=pWxG8i{kv%BAgbuBAZK{j}2zMA7hkZo5@q}dKNJa zPB?0Y*sP!tM+7qsJA^r9d$QllWH#NpSoY7-gK$tW(}{zkIHH=}U`>RgQWv;FduQz8 zlT5R+QjGObU5@HV&;orL#0@StSEC0oTP@eO@uCIhl&R=THwv;wY|or>Chi-$)TK?1 z`NbMWHhGufsF+y$x*!>^E#H4bW8(_7HOf(-cP~odSad;R* z)|1iFLf0<<(wWYp@f&@Nd6-B+Iq_#Uc{w0qZan^8>WST9(;nH0hRz_&MPktu^=rA) zJ{%Is!xl(3y14f%|M$nZN??5&B|;2xe#*=`qKE>4*MR-GM`B z!U=SML^0Fk0bm85&D!jY@jbp;P1*HdvP?W%td}kGuK&-Ox~;*RwrKLnq`w-d#NFJK z2Q+2esHiA0uT8nBz=`as{Mq&OWB{jQclLA2bTrQ-{cFy-h_@BRe|$?bwOl$XTIC3n zCSZz3A)hRg-Epw?I~g|Z4B!DsjA_Ln_RyzN zT<;3Xl?|6!U0ZAP*;5DX5O%=2Dz*NMYLpQCOthP%HQns;jkU9 z*`PnFQ+lFWr1iJWvV(KO8ox2eRMUT9W5yFj2I2iwTT%q#k~uxMM(5|}tWhr)f)ihG zP`P!B8;d5Dn>I<$xi0b!A67TQ)m3{a3}V5|@QIGPA0(ncV-alrqcy{)jQP-q<<=)J zQx{W41^{7CbgJbOI=aDwafSI{=H?ad|F&d}URwi#w@uTI0?S=Ng2r{$Mq5VmZWEQ2l`{+= zvuIoIH=T+!#`+EM8V9OtB_#|}mj`9$sy`7C4waSsLe!ia!+#T5$n7fg>CjF8qp`w2 zBzu;SfypxnpFL|jr|+1|vvshRHo3k&5oICy4bLw0}X&Gi^D0X1nSMbGUX-!zY@ z@>b`^^{=yVsGTGU+6IxLoN)3dxGg$dc}29S{yRk#vkl@DhKeX{;mX=Y8pGYFtH9!g zO?(VpG>3GvAfc{l;ym#>=oD2Az z2I?mio}tfuUM9CY!YEno7~BLNi+6p*zfKyYiw5!LC`e?)y5{jCGP$Mhb?9!`f#YBo zsTvtJL`_*c&5sU??>?Rip;e`1&$4`(R-w-?J}}9VBj2x20HJ@oq0q&b?K-Xx9l&^% zw-q|$xOc)w7ba^?!Gp?xsm_8$DAPkgnt@^5h=xk9?%;gG?^q^?4@hZNi6$Ms+CP0YEu zp|W5q-*ub}<*XBKaA@PHHh68lkUCs+9?QDR1I=KYy?VvP+xB09Y`$O9u_sgH`pXNyN~(P+o5DW;c*gq=xwvtKe+5U?ag_bVKH^(J7!l(uSP_ zYZ87!LV~Tdx3nXRqeXo~*NXP5FTSM669_qUg0{ z$@)4V1}jW_?2*xW2_L%+6THdPU_m4&t2>3?FGH!tyg;O+I|2nHcR9ME`7SJznJ0BZ zvN)fHBVymaZ4A7k_4D@=0R5oO`3r2q1_9)jn+)Da!F-J!6*s@UKW+36~w}r=A^Kg`ZAxN7=3|`<5jkfkY(1^9r zl$ny^5>2#6$(g{@(-8v2V{+lLQqvd=eG^&8la|&V3}gW#E*?f$Be$B& zu|q{5c0Dahk->+c0#!*}&QvaSReHGf?fw2{4U#vDTn458Q32o~pWH|zFO4(;Fjl|3 zt)Q=gQ+4nq6=)?qTM+O`abeUDizvM$MLzviRo zaO4+?MFQIR*H%?JJDXby#m>KMJUt1+0-rojj-v=mMI*8><9Zunp34{pcSL5~!1bY= zk6r_*KdL0Ox5C3u@Z5jgqEGD2nY9PV9z*%Xux*@{QI%Np^WHlU5{&Hb>^LmLdJuyTP-o*jM7Q8MAp{7f#Te;xKi7=|p`n4*JW8j6S2puHiaP5)bZ zB40yx9y7wK5FgP$>z!$-GjfW36zID?(3MS&nToQB0e;vaMTlcgK4>V7vkY#^Q7L5G zmd2tC>_i)mVBw(6L(t(U_7JZT<%SC`x?JPZ?&a5AkHNK;jU>)*UIFzTO?$}3fWPLG z`?hJp+p)8wy}Ow+^J`JxKAIqNFM#igg!cP^>WnH~-kj;%FUbf*u`WXJ4|lF;FK@A$ z|K=1?Lc(FwHf;Y4C{2g_O{742tG|q?1_h{@ug=Zv@C%8-(`IJ{>3n&>4bB@fQ{9$R z!tXjBVp@CYbSjfr@nt?K&ZegZr_MwtuzgrehcEoI$W&w9P1HPK)M9MWiz%O|m|cFI z)#1BdnFy7dd0V&hrC$)}aRg?2W|7z5js;wXqEb~MXT3ijYU|fuH1ehE7LL`5< zBF8c^L?*?cN*k(+%J4z`J;g^QwZU`ZnrRN=YC&-0Iq3`dzRa0%*W>g*)Gbx57uX3c zQ67?kfG3P<9j^(YAC02w_)X5RsS1*xyP)~3FcVE3yS9L`=NjO@--XnIC1lm(1GEEB zW2}^UD?i{KMShSkY&jikT~xiIw*z}eJ!8;F&_o2hD%$kDBfKD=`TUU0z6>&8&}h}uiSyQnT(j-IrINI z;etBmr2|U%jZ7?-{DJ#$Y~jDx2n7BhPbVNAgbM3d!+wk&z__0N^ZASM$My7B16xP@ z!NI}i34DVi<6KT#Peq)BSD&ukp0@XU<@Q;zsd2nA%XKLVK6f6u^A<5-GH3=4l&eGwqEcc~ zuh~^&mQF_>>~KRO)LDe~@{fOaS|{&Iof+d)(n|$B#anekWuQ2n2dbYxjQ5)-PbI71 zh)LoD*P!dy-(x*mAP`W=JkjkoRD*zK%IRS+?78Kud)iA%_Za$YB;(aCbws z_1YxO*$e8_H>V2(HK zaHniq;Re!`N3O`G86asj+5+Y0htp&83y5G+p#!G5xe1B2C8&f1C&$-xQ1pILxC{cF zURQb_z)KG@F*TzG3u%#_QE%A^PG#LScuS{g%I8mQp5Ly z8XId~5?7LP;i&L@avf((^*2B2p%#+o{DP*#Y~m!}_AZ$<0M(n^?qMvj5>(s&oP^v`G@#d}(Qm+k=TLKt8ApXzi+zU2Np6(hX3ntkjS z11?mX!r>Qz2NFT3RXWt4+H0sFkiNj)s}0zShE0My@5hBx&A6w?SPo3}=OV(0emkJ4 zecy9@%sIH!YZ2W*3+u-;Y~92N z`eJ%!x%NMd`4>>*TzoE%<5Lsn%|(AXGt}<3A{Qd_NR;L~m(Aw8b^d|f;IQt9{*k+` zHS2_agSWbWBmu|aeUhm)!6^PM1D0H`fY7G(#?UHXnViN`@CrIfi) zG`eh+HV1pu9ooYj+fMPs-$%}}Z`9aprJMoz|0!?k)RU7~@Z8FGsp-LnGc#0JH;A1&S?S!n(>DFWIU zt`7sdXy~rjdi&E`0wB>@NhF1fesW|;6&np%iU)igT$eV z$^A1$PD{f>hQx(zGldaS$;R*0ip{I(ID>7lY%$!F0-k-wVQ%`S!uiU6oq!K*{M|g~ z5;K^fY*gdJ)fsc-#cGUOQW_ z;%)Dz8QH>p`;Es?c4kB2YCP_;6 zMR`HB-cfbIveSN=Q_NkkP zJfmDs!y=JEe|T(>^f>8rL>8Bco%fqJuqpKagtI!U&f^)Tl+rUligOY(vN-(fz?(`| zsS6IBel1bU6rOw$rI}Zl!C1Edu&=@Q${L}g9kW=4JsuCE(NpKqW64%|K}5iSX}L7m zcaSSE5-VZ?#uQlL-XYzageq#(5oYYg-&=g%0a0S`GpUOaEAW5AuFM0oLuUl7`*L?m zLi{Lj2}3-Ub^djHdF6d!P?R7WJ;l2U<>JntD)pab{EGJGWlKav#5?-;K|E*to?#Ah zlpAL=OR_|ZxA#nIxh!q6dN?%}!b|KoOZ5e130@-;{OYaBByfoLgmC&0J88zRJCkw? zjgWXY_NF)l^CG<>+C;-?V2!~2Lg%4LERb5^Yu_0LWJwc{95EbW-o%dLq0SW2klD1u zYtw_Ok8-q+*XA*?Qx}1d@LprU1=VN1*MqCVuYf8IDwdph zRb6~6k8Q(!*ZXagtNs>`azR%|K}tsfGi~(L2)lA{%kps;Hd(RmdwgLDYxYuhk~nb% z{5x&ESZ4WgN=1W&t+!liOyyQ+`sS{j>hxsvdw0Zxa?{t~5o#TQQ!pJ6W@K7(K0gN# za8Ap`roVK4@~Gj&N6u+=p$=QVW*QvyFA-Z?TTOk{=dTe3dO$;qzt0O7Qa|o{+`Y|C zVjq|e{n_k`o(n}F8%jVYnQP}h3e;dHt5mO%iyuO$AIp6oOWfS(Sy?1i1V`KJfb4lZlh*Wda zxa=&f3UG@9@?9uUlidd-NM=`8C)(L5j{ymTWS^e-J~+PLkugWD7)K+SX;j@%vVjIm zX>H`6^c$#5^4-zQkP-A!iwq(n6RxTMt|9ZlbK2+=fGbK ze88FhiQu0nYn7raE0}QRR0wcC9W8AVTumO_biGcLphaV7(IwUJY*J41EHi?5} zHgGRG-Qy;6dUh*d#BJ~BSkBupny`mV+fQg;6!&ST{_lW*LW7J^IGqU~J3(UJznKB? z<9A097so(|FW#Kmjh)`cZ?g=JO)O3j)G+2B7WGJ7$F|=K7e#SF@#|>)nou?als@{C zrpjp0{D%hvIexqDWl(gws$@_7oVK-!66KK~miCxKs;bRrd{klurY0MqMPAJ~ic#c# z{_)q8Fj~5x(Zk=UGnq)I``ey7egJ~;pSkPjBR)DT3hU!wE|)A{s%jrdeQQ}nLfe1y zcx&)KIlARCpw_xsokS>c*G0h#5$ThtKAymy$Q4#|N4c(p1OnU+aGzO zO5qDBf)IYp(#4%V+qx0ar7y-%{+J^$H@4U0a)M^fob|)E@W@xJrGM1$y9o-&SS`{l z9i`r$0i=sruiAf!x}cbeonG%Oj!sHJb%SS%zrJtj8dL1EmZwywc!i`SC9 z-}B62<1V^ZANt{ z*T70D%Wj+Ngr59|-|8tQ#;LfuWn%HcUHm3V59$@^(Zy5x6X*O_=Geh$0wv|U;*1Gd z`nvYfqj$Yz$o;sL+o; zV0lhNO>0~%Cr4?r;@OwO3XO=?u`)UO;L$?C3~_I{_^-J~*nSM!QL<8@7h|@amVwZ3 zGnrBeQW50r)R)R2N?$&#Vdev5L@mB()?Y19v;sY>y%Ks88Hui&q*h~a5_3A3KQ0{U+Z11mZ;S<6f!>%Eej4((lB zR4e%(8h^=`pjTB^+Pi!UKJsqtzMzv$GTOHSo$m34oZ#~#C&^tZ=NcDG?ND7}4N+zc zL4DDUAKZ#5Nix#PMH*o=1EzE}Ikq)e_aqw>I36V1X}EV<-fp->H*2jse(#|Qp#QRL zJfa&2ppZ{Ojy1v+z>1fyNVt*VQN98L>{QA$b3|qwiEi-ks4#~a-S!AK+2Ji$IjD$u zeCc0yUymV>fSr`+FG+=L8++w;)OQgj!+0^BhC||%ahP^7I6SX&_0q(Us6J8@j_?O1AuwFO`*;D_Vf8lY2EBn*IiWG_dA_|78vgbL9gG(VF>*5Z-EXgBFQwe&yWJMi>nFj@yW`AtXYilrgam66T(2G z-z}$#C%1Y)P_b-WDB+b$_1pq z!y;F;@+29vx|;}53Yrp=OQ3CqbY^QDnIY(>xyHPSt=<2OT*mjkV0Sx$!QeN+ZveAu zqwQlP+uuYX;BsL4`6FjKFmV`hGag5|?o%fxCzb39T{aai2@qBrI)OiWtU+igZ+46# zhdHRF{9IO@nid|c&*})bsp`<>ImW7FZpOtikKiu8YJZ*BS1}#WEFPRkdenhs&|z*6G;2`5SjU=-JqD? z<7TXKI7c@3)?9CRl}AQW#|3nAXi&s)8i=>*oRAYyxP4@%!BJV$XFZut5As}ma@tPG z{K8^HV{j{SO<`(o1?&VTlx*m(v>|tvT_wxctN~S z){oPj?es_`N5Xratd>vmi>9yh)|-Ox7HiEf$}y}Uu@NO(B^{SjjspBzc$9@N^`^yH z@iI;;)>NOed8{x8`AODpdQwi}Ph@e4G`DlLwM);=e<_aS1|M135%=6$(amJivnvIE zjdK#pi)-~ZE9Oo;WX^Z})Ae~mT+wDh=2x%5FsE%FWoeYBqLi*Hg=D>Bh+c%`Y=}f* z=HpXMr~Ug_JNnx{Ey~5|wk=xH*c*we^0ORi#TRP#TFRyve{0-PYaUlq`Gty zFt&|xutlU~o#68FwnX@YsXbK&;I_J-3}?+Z`uJPFm(px-G3)UYGE(Os3GSvuB0mhV zOb&oA*_8H1LQu(St(iWtN80XDlT>8Hc~q)3i2^eaJo7^qf$Ahv)D9DK?L{WKapu7I ztc-|-Rwt35-2OK1ks9KyxZvP=D4{vq^2)09m<(zK*v$m$t=5cFyK|*-`z!@;1KlI`EE-*m@p3 W^{S&<;wl~l{HQ5uE7rZRjr<=3aq1iZ literal 0 HcmV?d00001 diff --git a/editor/dashboard/logo.png b/editor/dashboard/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..2fb999edbe63d894aa2015cded2daca2b0000a96 GIT binary patch literal 9390 zcmY*mY^ZH>*6FpaCetr!3i$G0tEL!a7ZAydw}5K z<#+G>?)TnQ^-NFCnRBM6YX0b|iPBV8#K9!T1ONaya3wj!-%;eRX@b%I_C=d3B!34K zcZ8x0pk{({|L+6?sbt^|0B|wItE%!kw2o#Edk|Bpw`%H6`v4(Va%;!OJwubH`vr-uYR{XatgZT~)}hn@BRS8{g$ zPg;Ky%E% z_PHN7X6;vlbv{*VIJ`Rc9ndo|abT`id1E_ZFjdlIUqWxl49n^z0^>u5VLNYHFCW5a%CJ!&ItTKu9{1^Z#sTocxp&F%;zCY zqUAH|c4eD?_Uyem-$^*>Tu5+`7xdm5 zCPC)fT3miom{o)-#FCz~=Zwcu2!$_nT&Ta?nM^p7tSz^#B_c{~z{_3`_3wN~b<(ev zCw^NCSn*rG-DT*3b^HDLDr8}^>g$p?>Bh%rNT{r``=eZB)sK@$$CRIsFZ$QTMGJ(C zGRbNzJ!{H#Rve*YRb%vdzT-BES;jAe3!=)(**QLVlpoG=6%i6ZtVkK8rlRtm*YD;` z!6=)=2q~RuLyd!jgM44wb{>K^zKdM$vX1Lg>H9o8JdR$v+ue-DSFzPMik<>wj}dB_ zNY46)j}-N)!8D&S8_U+RC$cA%-jv~VN$q=bJdcr)OZj$ESzZ0rXD7?1S7^l}#ryWx zHlNdXfh-B%X2-=k>6(sT%2X{M+YJS)O}M+V__sGFCiWP1b9dG8G05cQ@h%tU1sMVh zUiKrg^JapgcDa2|WOz0rqB|5K9@97Lu8!QYoMn-uWnSG=sI>eOhc6W4$V<%I?zZO$ z8md}aguE7BduK_kQNFUVDI>#;xXPBI@w@qOyFsXtS$p);>F4>MduUx&c9z{+ZG@`O zx+$&eN2IM(_c3tU3t*lv#p@H)1Fz5QHxIYmiX@;(5R<$Mwhm03D$lG|GtcHl2}T$g z*hxM;T+}eCq}%dn8XI#RA0IcE_5eNNk_$v>Te)asyL@+7ya!0ImG@%#EgwI~?&Lq` zj<-%tC4K$k#abGjf;E>cbAgn8t2!`a9l{UlTlrbF`n_{GM>v{c&}^A?e-xwRA!5Q} zFn;b&g5=#EAj1a*irxzDSKVC`Gj4SH*|FsJyKQ%T!NjM>uf24z19Krq%3@iV(DET- z4Y}r3R%|e>)UkX579k@fq*NVCwNZiN4}L5THOroVR(q@^EVT8ya4+d{ zXUq+GOx||-=YBhAdyKW6R2*Fl#>v{miYRfCoZ9tHweUvcXn1~wz? zmAYhyr=5BYTwRC_?%U_P1K^yQ656x-9^`u1GxovLrH81g+u6Pi^+}?A=b>V*OnU%y zWZ&GYN%`U7g7LJOCX$&!g@@I&ArLW)_85qRZ#F2J0 zV?(NGjX_;a?4ldLj__;%+a%zz2m3~PP8ZujWTc)}jY%@-znfVB!?}&q_}(4KxiUhP zt7`SkrN8t8hiJ3{@(&z}OO@l_E&N*l;=i|W85a#;W(0=d9WYQN8Fl#72l{?YnYc(! zhMd3Ygv7-VHg*~Rq9ETIeR>D`QqguIgE6m*8j;!5@v8~M%iX3fjMhhjg9iP+$_|A+ zWS+C__Q!mkc+T-LlNUhP_d*W0@PmypjhHET*;2B#?)QuRJ-pG%+D;}yX7yAUAO%3C zKS@ZPQ}ywqvTdwxmHr3+#r#!9W@e0F^mEi(eMIu`&R0G*5d&u*-)bI?gMe$CC=XBN zYyJe~r=ZS6*tP4KR`RWp4xK%P+AB3jCy9Z@N#V2@wn5KSGM=}A)<%7@GHF=!$rOK?Li{p5X@EkQmmz!8Wln(v@ z=3?WuF8<)G=B}E>aOvE(K7ZQ*IYUQFj0LRqC<+tl9?Z|pjaX$EGqts~1#4>fN#=Wa zdYY+gw`FA~)AxnrqyWIr`^4)@t>IEH880$8XW9()#G&W6VGw{`$dzLMwr3(`Xi{9! zJD$Cvjp7i-Mb$S6DBnH|Z(#lI+2SI7zjNg>!X0qsa1|#}U{7SO9-pWrz%e=n77k{B z#Ick$^6^_8E!I!ZEiuv|V-|=OG=+;-A*CH1;uz*Rq(2ptApfS(*jow}$7I*tMnaGXs5II|C z=k%^Wk^l`k)^I@jMnuT`3-Bhacqv~lYDsilQW^vzTPsEn-TKV36T9)Ix7mr$bzR0a zfrh&bWgseLP-W(Qt#VVP>~Tm~(buois#LL8n(F4J^ftIi$WM%gtt#?-|Ig44&k#3W zAu+|f`@0^_!G&;v7cWLmpU%+FwWTopBF!D>%9Q{}x2%PUB|6|~NKaxk(%$|febsVk ziNNS6lyEcm{{8B3Y`ul${C1@OSPQ)Acu-W@sgT76|g1`>504wyiAxT{<3ZK%~5E{!>`%#j;3ct1NkVNf~=t zQ4S&cB!=(r;kqggRt~Q8U{?!bw5L3(^kdDWIYrQYs8e8ypGy=wLY!8`)zp1_X=9*DQvq5+w&NxRIUgb(_z60ar}IjkZAxR4YkrRjpnf+(;TbBe0gK7P}W zqlD8f?pTQ)Dy9p9#{#|+loFLefj$f~iC0Ql)NgLg+J1j`D50Lnxsy9Vq`tvtC8r>d zMn%U}wqUrh+tSHDBm^N`!C>*aoY_~(7&vM);EdIUvb%$x#hIOPV)tu>D0+NFqmRE~ z=;9*@oHgk@Gx;J45oScM4zbvw2v68Yvnge*N)|TC8m4@D_-%PjO}YWUO@#v~dJJ3$ z_ByWDr{WI*0VPNnRN=EBoF=y;ePpCxByp*W|r%Uq_6%KNR(t&}XwS{G05MZb)w z$PZ*yricX42Ypx-u~B}?du@ugOd>nUZ9D&J=n{zUNwZeU4%F(2u*r9?ff6Uo(781< z5)%x)g&@ajpAmx*`4QyU0QqhXc1m6Pf<(r@k5ft0L-+MbNS+-G03y4crPVqKtUcbeK8r3P|EM^e7{z0#D{|UmlLVKbMrW$$IQh{ICxuX*hY=d z3fy;d{G0tYe~o=`NWa?L!j);dc9xTMB+{dYotQ(n3k##MgHGreEHVqc7#{z;d(876 zf#;GuYNK%Q6Dy;(6@@1*hg27c$z8_&LfZj@7^;zw78KpWnWrWPUItOYi;G%7>U?pgE3%%Rao{mzT<}=SoYRWqRb~6jkM*NJ!5k>|yij zlx4#>^POASUH7}CPXEfg#%T;fVl=k_^lljrfqGU49xUOpOs|D_zNJr|F#^ra2M)ho zsw&eMbkf|u&+hYaFvwzTEAZimxw;oCh7dcTJ)X!($qH8x;IVL-2?@iYjm4-r*Z^>} zDI8vZDRYjR^S3q;@za+yY|%x~ih8VZMdAeQ^o(2n2v0~7y|tQ7Wo3IiDhQOLLxG^( z$uPVp%zMCjOsO7UFj&FN@YF|jaG6(&Z;5C^4vQ>EW0KUph~f1;wIHl!gcTViG# zpW6*!LeMX^p_C#XfaP*T=L{I#LR*TpdG`46UbETJDSi0?h;w9DkU8>A{tZkiUTv*_ z?9WY-N-1oTbm*jdk=x35bS@Ej6Wn2x_4d0R0>~?6=&p}Q!WwLRC#MpPu}rJ%`-)w; zSnbSr1QCDJ7T32*ys)x&yvX2!f z{k&LOUTUc1v>q9usZoO9y46!nlJ5R_Ou<38t`)?qeWI<~@)+n*-Vfu>jil`l zvw8II+}IS;|AP+=|4O`z+L|aSE)`zaMSfH7#N8u<>XzQx+b-kiUs3+L@KY}-K8xq2 zF!~2(qE+E7Utbi;BzQ!ZW|c4*1?sT}(q(3c6Jg+~FH;x!t`#?4w(|h}jBf9)IrlS> zbg4cz@J2#$O&^2w%ZY7BQ`09_n-{j|!TPp|fM74dJ0+5jZd|i@@g!!)=I=Er6m?^W z;d3fCy-<#iA9x#0TT21Ky_Xr(2^40pVMztHR{+~*-xN~+)Z&?so8BLLFE~JpdW3&z zetmFtmSZ%#Xg*fPKB#9L%}n$HepMcp$6c8Hcu8t11CkU9t^ZsAJR63f*54<-68v%# zn$AURfb>FA^upQmLlJ~z@C5NQSrek5n z^s}%gEuP6lMRe8A&C|@$ih+tYEveIV=I*$9F-=|T-!a_Y`V6{OnFcN&2eyNxjrzqP zJrYqryl=oK7KqOe5eWa|H`5UuBYH2f7s<*OZTI))&6)dWyR;9Lb1lomA~MCvi1%0W zJL#&Oj+5V~R4*5EdssA}PR zsvC8LN@ucg)Xgl@#tRsTFwd`E@*W3b@eCvic=xTz3pyc+zpm)!z4txL-3SkgmL5%A zoG=x}TQ4TW7Iy9jEo{2SeDZa2%5BFb2ekvyfX4ViY8foUa3r0f342z6pOG>f8{=-) zF5@!lZhls^k&TZ+QVU;9`7A7hpj=r%%mnkN5j&$V8+5zuiM9StG-+8JOP>PXPPmiC zVe#8#i5{!2M#Q~m2@<|ny4tD=QDBe_zU(nzR}BdWKhqf?hUxF8Gd3ir7d?8P{zOrU z*ha-x&Mj2pmA1CP5A1$vzGnK6{2qF^^PaKZqsh|Pw-d_QiBoSDk81863&|@RH=1oViRsLNN>&be2;PN!M`dyCY!wzM6Ip*=EhWHXr1i6bfH13{`2JPEy~v6Hn*t+GVI;+ zUaM~J5i<}@#Aq}8cY`k@wb534F`>p`LCCvsBt+dIFoqmVXs{PF^wYV~+a#>iaD2Z0 zB%x6IBZsk1k~J|I>8xBRq2HC2opBrDZOduuj*dpu`ErctN+tnA8hdK+J7KrzJ2?=h zW0E3u%KTid`I1wmfaI^bOcsB098F01uI=IxC=Hg2(QlT#jNx0iy@tYQe;}XnsyQ&W zLH?B#`DKV!>YsUros>u)4)yQ4&rydE?ohL3qg!lVYt(2>xtwk60)hO>=<(H2K_)mFXVpb@z`8T6C<+ln&E}JHTmKl%DX5sIs2fbIWD1Y);>^MJbzrKt?F z!pF9pOM_ji5@jezYkV7B>mVEKIY!+RBu&bEFu$F=ddM;s*V9yA!^s^XLq69<2*UeIOggampA`U^9K^G{Y zo=o`S!1(#cD;)T1k5AA^N%S%AEv~f^PL!kgMRwS4ysEatU7lCqeN7A%ZX%5nmb&Kw z`0z)->{J3FVj$-5t~0-f*17_fd40I2s!H{C$120_*gC+f0E$|$KSH;vG2Bpx+><=0F7_;jQ9|Pv1HYfiDRHe9D2CEg=2WYa_c_- zr_!bxC~aG6thWGBtf%&jRu`a0f6U6QI*em6#E|2=SBvR0aZa7@A5O)*j*|74bVqzm z!-~g+36s%41aJ1=GB#J)(gvs2-iAG;#HZm!q1TK!I>;D#u;Bn~dxkUEqApyL@-*_& z@dW+Yb~!U6swAY{M?%1*mAE>x0hozvxY{Z{U=j2BR6#$JTf2S zr)o+uo@Oigaj01@h&e&TeOkmCguNl(Qp)RzKlZp8IB%Mu0%Lg@|y%|sil zK8KDa<(QnmhKoI6jwdA1%@=@bQ21Nuv`F@yUoyKkBzc@@)EBl`K*+ac^IT>BV07v+zm)Aq<#HRKz{CJWk;aqa8oLWYD26N>4Q}!1qwr9QfzLoI zasn8IEyq?%F?TNMtpBw3HXW?+^Yf)gm+)OiR2Q6LK^0}arCupi72e=PrU zHroGL=XfL*qnQB($d&oe15NGcuN? zxz)=Go8+|WaNu>P&fE8R!(AupBjH^2;mQ)l?|8+Fz*GKv!m*wxRZ&%Rn0>@(*Y{Ip zSfa{XV8i9bie&L8$T@LrvaJn;pry}%sgfesM!{I1@&OW)5Nss@s6q`#Kb`6#h09~o zgBsPP>^JZ;og`!v1QZ6y+*lP(Y>^!5=G75&*_;PbZS$GJ9I_$qc(Ezz%z>1@gW*Ic zP*C(e!R0kWVB|}^R{+M67@Jw>c^pjqd%~#cfS_Zna{nJnq9z!2UfV+C`x2urko2tD6xQzv$-RW5>6DhA2CZJkFWEMnbrI^|Gi;Sd_ngZJzt>E^hJLR59jl}`MTH- zWKrD(KZD^+x|QXA0Qhvtka*V|U=) zW-c|xFcJpX=2N8P5f^=k61JNOq+(5O)<-~R?e{w$jaE@Lqj_4L!h<;4;=6nB<&>y1 z??|D6kF6X_7bJxi%-Xf{w zrm!c~gsMbV8?|E5`_me8HH|oVT7Ss$`(x8;QHqP94?IuN3>|@2;a7ku*rs6+8(9p1 zmf}tvj`#dgcRYBy_u~lq+46cA_HsieOkYTrz-})eOX*_V$Tjf&7F+)u!~~xn7qX#n z8|F@{j9?SZfri^YzkA->VYBR%k{t*DQ;w1yFauheO9@!9%MU4Ana-9B&8TiY_QjC zs5w|NYL|CQJz253*fxp{-!0JCa_^1bJ?p|;U86|^rUawjr0Fwz-75x%$LNS|1M)@H zc-@?j8Xu{d%b3eVN(P-r;S@gihPk4(QP?P@jSI(}H-R!er{xWQ^|e&teF6&O0v)#{H+%Zp3U!T-k=M9=RCJoIJqfbWL0uC$NKw$o70BJqq&c+E8XHSv;)hZ&OW^W$JpV<* z22R4|MxI%QqUig_jXsFKj2rMJ1nt=dahQr-zu>7+s;ziag~4 z5tr#UDT$iCvr1(^1e`PhWnK1V9VG@0Y@wfkWnx24q=jF7@CvMqxx8IKw-X1FCe_T`!YbZl>&d^WV zUrWM{ZN~@nYz0y0QN51hUf;J{KeD<{q9yX!t|@U4!OV`XliSzG;R?M+1Dz4{(WT%e z8z~mTL18KvaR!N6$#{Fkc%NZ;XKgbs@69-&(XZuMZZx>w*7#?<<)4jw*28+Mb2vPC zmU4lnsNmLGlLomYGn2uJBj59lfo$%Eygp9z6b-4@cbYVvf7%Z{oQm*Du(;`IYEqyU zDp{76a6hdmQUM5UAu}PYgkjI6O7XXp-egnZ&m6xw`2f$D8%#yGJWa+)By}AoeL)6bidj-K;JbiENE4TG}gy)etAC*y>7=6 zA-M;>P27GW6^k)Yv(Sux!kA*~NeOt1P>7{um1^ImJ|6R~@>>m9^(Sr7^md73V@+xi zbS6CAd}8;V$fb?o%BDwua%%T@^ruP#PpRqyBiOdrdMn(JQv&-xbeNo3I+m6T=Gx~( zw3a02zgavT`BaFq8yrlirAt|x5n8EywrrV)Sk=97jA9?dcU&GE*>)3pNSPtcy+jU- zOiX18Ch^C@eYFM)ltkl}(C+OogCf*T;w#X>!mR^zRTh|orGWME28Am02&u@gmy6-y zHlgv?9uBUroxpe@d~A$bzaXz0N_x2Wd7P<1Kv!YIdpZJA{_*i=tGwec!Bhq1ujB%C z9Llut?$>#T(z)~1YcwShui$I`2RY?0uZ zyL>~Ya@XEd_qz&j%mS2?89tel$hvYUBsDSe&C>wrXqy*1Sy%k0sm|cZ3ykcQZ>?I< z-%?yC;U`V#vRJi$QT$#Pr?WjHJ#FB!CP`W2T^D;Q!1YZtp*+OVPf_M9Z+G)F{tjYw;4^hy|_lgn8^w+g?Uo$B$`|cc=>+3{fT?-*BB}%VpfOqiW&+)T8+XD zH3W4BSasV`H1lEkS}ywoh%A~^sy)=LhsC)br-GrYf$I>AUY9okcpeLOtDRS3?N=Oh zWF<;I+e#|$1(w~h`jh1yu((&R-(kdmPjeM#&?B1*yBjFbTkXI0_*$us~U0EoJ(ylws!Z5vL@}z zP^HGz1W^`~LNs-ImiDe&q3Tq{j8=_zGML?-&HnhW1Zya@IcL{;q7{6#1{}&~`Qo%% zOc+x|$-=Vx3zZhn)rj2rRxjQ6etNO~FyY?S7)=*@u92MY$F6hDaGy>7NBT z{AM$diO=$!#b&fG`}Ls#~%kFkx6#IPG{@45;bbzx%0#2A zc+fj)3c=WOK(%oYMmG~KuP5deXV&(8z{*Wv{jla2^99SzocV@}Ca=v^>UT3F>_3=| z04tfgbeu^T0-u*7eho%(^Aejc)2>`?YcfQ>FlneVH=p5>WJ)G7IYQw(UzHs;pQ_q+ zGY;AYNb^nQ`l8iPUs#HI6;a{1{aX)17MnuK^N^X8J(4Q%g(b% zbQLfHk@yM4J k^Fhmy + # do your update here +} diff --git a/editor/template/new-script.js b/editor/template/new-script.js new file mode 100644 index 00000000000..da1edcc6a02 --- /dev/null +++ b/editor/template/new-script.js @@ -0,0 +1,25 @@ +cc.Class({ + extends: cc.Component, + + properties: { + // foo: { + // default: null, + // url: cc.Texture2D, // optional, default is typeof default + // serializable: true, // optional, default is true + // visible: true, // optional, default is true + // displayName: 'Foo', // optional + // readonly: false, // optional, default is false + // }, + // ... + }, + + // use this for initialization + onLoad: function () { + + }, + + // called every frame + update: function (dt) { + + }, +}); diff --git a/extends.js b/extends.js new file mode 100644 index 00000000000..8024c9f8fa9 --- /dev/null +++ b/extends.js @@ -0,0 +1,43 @@ +/**************************************************************************** + Copyright (c) 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. + ****************************************************************************/ + +require('./cocos2d/core'); +require('./cocos2d/animation'); + +require('./cocos2d/particle/CCParticleAsset'); +require('./cocos2d/particle/CCEParticleSystem'); + +if (!(CC_EDITOR && Editor.isCoreLevel)) { + if (cc.sys.isNative) { + // TODO - add to jsb ? + function log () { + var text = cc.formatStr.apply(this, arguments); + console.log(text); + } + cc.log = log; + cc.error = log; + cc.warn = log; + cc.info = log; + } +} diff --git a/extensions/ccb-reader/CCBAnimationManager.js b/extensions/ccb-reader/CCBAnimationManager.js new file mode 100644 index 00000000000..035dc0d7608 --- /dev/null +++ b/extensions/ccb-reader/CCBAnimationManager.js @@ -0,0 +1,768 @@ +/**************************************************************************** + 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.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.js.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().getColor(); + 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]; + node.setPosition(cc._getAbsolutePosition(x,y,nType, this.getContainerSize(node.getParent()),propName)); + }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.setSkewX(x); + node.setSkewY(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.setRotationSkewX(value); + }else if(propName === "rotationY") + { + node.setRotationSkewY(value); + }else if(propName === "opacity"){ + node.setOpacity(value); + } else if(propName === "displayFrame"){ + node.setSpriteFrame(value); + } else if(propName === "color"){ + var ccColor3B = value.getColor(); + if(ccColor3B.r !== 255 || ccColor3B.g !== 255 || ccColor3B.b !== 255){ + node.setColor(ccColor3B); + } + } else if( propName === "visible"){ + value = value || false; + node.setVisible(value); + } else { + cc.log("unsupported property name is "+ propName); + } + } + } + }, + + _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: Unkown 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/extensions/ccb-reader/CCBKeyframe.js b/extensions/ccb-reader/CCBKeyframe.js new file mode 100644 index 00000000000..1458d8063a2 --- /dev/null +++ b/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/extensions/ccb-reader/CCBReader.js b/extensions/ccb-reader/CCBReader.js new file mode 100644 index 00000000000..0a7af84bbe4 --- /dev/null +++ b/extensions/ccb-reader/CCBReader.js @@ -0,0 +1,1126 @@ +/**************************************************************************** + 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; + +cc.BuilderFile = cc.Node.extend({ + _ccbFileNode:null, + + getCCBFileNode:function () { + return this._ccbFileNode; + }, + setCCBFileNode:function (node) { + this._ccbFileNode = node; + } +}); + +cc.BuilderFile.create = function () { + return new cc.BuilderFile(); +}; + +/** + * 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; + while (!this._getBit()) { + numBits++; + } + + var current = 0; + for (var a = numBits - 1; a >= 0; a--) { + if (this._getBit()) { + 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; + } + + this._alignBits(); + + return num; + }, + + readByte:function () { + var byteValue = this._data[this._currentByte]; + this._currentByte++; + return byteValue; + }, + + readBool:function () { + return (0 !== this.readByte()); + }, + + readFloat:function () { + var type = this.readByte(); + + 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; + this._checkSize(length); + + 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._readByteOnly(++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._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._readByteOnly(curByte, size) >> offsetRight) & ((1 << (diff ? 8 - offsetRight : length)) - 1); + + if (diff && offsetLeft) { + sum += (this._readByteOnly(lastByte++, size) & ((1 << offsetLeft) - 1)) << (diff-- << 3) - offsetRight; + } + + while (diff) { + sum += this._shl(this._readByteOnly(lastByte++, size), (diff-- << 3) - offsetRight); + } + + return sum; + }, + + _readByteOnly:function (i, size) { + return this._data[this._currentByte + size - i - 1]; + }, + + _shl:function (a, b) { + for (++b; --b; a = ((a %= 0x7fffffff + 1) & 0x40000000) === 0x40000000 ? a * 2 : (a - 0x40000000) * 2 + 0x7fffffff + 1); + return a; + }, + + _checkSize:function (neededBits) { + if (!(this._currentByte + Math.ceil(neededBits / 8) < this._data.length)) { + throw new Error("Index out of bound"); + } + }, + + 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.readBool(); + } else if (type === CCB_PROPTYPE_BYTE) { + value = this.readByte(); + } else if (type === CCB_PROPTYPE_COLOR3) { + var c = cc.color(this.readByte(), this.readByte(), this.readByte()); + value = cc.Color3BWapper.create(c); + } 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.readBool(); + 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, locCurrentByte = this._currentByte; + if (reverse) { + for (i = strLen - 1; i >= 0; i--) + strValue += String.fromCharCode(locData[locCurrentByte + i]); + } else { + for (i = 0; i < strLen; i++) + strValue += String.fromCharCode(locData[locCurrentByte + 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.readByte(); + var b1 = this.readByte(); + + 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 instanceof cc.BuilderFile; + if (isCCBFileNode) { + var embeddedNode = node.getCCBFileNode(); + 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.setCCBFileNode(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; + }, + + _getBit:function () { + var bit = (this._data[this._currentByte] & (1 << this._currentBit)) !== 0; + this._currentBit++; + + if (this._currentBit >= 8) { + this._currentBit = 0; + this._currentByte++; + if(this._currentByte > this._data.length) + throw new Error("out of the data bound"); + } + return bit; + }, + + _alignBits:function () { + if (this._currentBit) { + this._currentBit = 0; + this._currentByte++; + } + }, + + _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.js.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); \ No newline at end of file diff --git a/extensions/ccb-reader/CCBReaderUtil.js b/extensions/ccb-reader/CCBReaderUtil.js new file mode 100644 index 00000000000..d992f145206 --- /dev/null +++ b/extensions/ccb-reader/CCBReaderUtil.js @@ -0,0 +1,61 @@ +/**************************************************************************** + 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/extensions/ccb-reader/CCBRelativePositioning.js b/extensions/ccb-reader/CCBRelativePositioning.js new file mode 100644 index 00000000000..2b2df87befb --- /dev/null +++ b/extensions/ccb-reader/CCBRelativePositioning.js @@ -0,0 +1,90 @@ +/**************************************************************************** + 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(pt, type, containerSize, propName){ + var absPt = cc.p(0,0); + if(type === CCB_POSITIONTYPE_RELATIVE_BOTTOM_LEFT) + absPt = pt; + else if(type === CCB_POSITIONTYPE_RELATIVE_TOP_LEFT){ + absPt.x = pt.x; + absPt.y = containerSize.height - pt.y; + } else if(type === CCB_POSITIONTYPE_RELATIVE_TOP_RIGHT){ + absPt.x = containerSize.width - pt.x; + absPt.y = containerSize.height - pt.y; + } else if (type === CCB_POSITIONTYPE_RELATIVE_BOTTOM_RIGHT) { + absPt.x = containerSize.width - pt.x; + absPt.y = pt.y; + } else if (type === CCB_POSITIONTYPE_PERCENT) { + absPt.x = (containerSize.width * pt.x / 100.0); + absPt.y = (containerSize.height * pt.y / 100.0); + } else if (type === CCB_POSITIONTYPE_MULTIPLY_RESOLUTION) { + var resolutionScale = cc.BuilderReader.getResolutionScale(); + absPt.x = pt.x * resolutionScale; + absPt.y = pt.y * resolutionScale; + } + + return absPt; +}; + +cc._getAbsolutePosition = function(x, y, type, containerSize, propName){ + var absPt = 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.setScaleX(scaleX); + node.setScaleY(scaleY); +}; \ No newline at end of file diff --git a/extensions/ccb-reader/CCBSequence.js b/extensions/ccb-reader/CCBSequence.js new file mode 100644 index 00000000000..488cfc6f900 --- /dev/null +++ b/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/extensions/ccb-reader/CCBValue.js b/extensions/ccb-reader/CCBValue.js new file mode 100644 index 00000000000..85fee5782c9 --- /dev/null +++ b/extensions/ccb-reader/CCBValue.js @@ -0,0 +1,81 @@ +/**************************************************************************** + 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.Color3BWapper = cc._Class.extend({ + _color:null, + ctor:function () { + this._color = cc.color(0, 0, 0); + }, + getColor:function () { + return this._color; + } +}); + +cc.Color3BWapper.create = function (color) { + var ret = new cc.Color3BWapper(); + if (ret) { + ret._color.r = color.r; + ret._color.g = color.g; + ret._color.b = color.b; + } + return ret; +}; + +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/extensions/ccb-reader/CCControlLoader.js b/extensions/ccb-reader/CCControlLoader.js new file mode 100644 index 00000000000..901f8509ddb --- /dev/null +++ b/extensions/ccb-reader/CCControlLoader.js @@ -0,0 +1,318 @@ +/**************************************************************************** + 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) { + return cc.BuilderFile.create(); + }, + onHandlePropTypeCCBFile:function (node, parent, propertyName, ccbFileNode, ccbReader) { + if (propertyName === PROPERTY_CCBFILE) { + node.setCCBFileNode(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.setZoomOnTouchDown(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/extensions/ccb-reader/CCNodeLoader.js b/extensions/ccb-reader/CCNodeLoader.js new file mode 100644 index 00000000000..cbfb833aec0 --- /dev/null +++ b/extensions/ccb-reader/CCNodeLoader.js @@ -0,0 +1,904 @@ +/**************************************************************************** + 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, + + ctor:function(){ + this._customProperties = new cc._Dictionary(); + }, + + 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.readByte(); + if ((platform === CCB_PLATFORM_ALL) ||(platform === CCB_PLATFORM_IOS) ||(platform === CCB_PLATFORM_MAC) ) + setProp = true; + + //forward properties for sub ccb files + if(node instanceof cc.BuilderFile){ + if(node.getCCBFileNode() && isExtraProp){ + node = node.getCCBFileNode(); + //skip properties that doesn't have a value to override + var getExtraPropsNames = node.userObject; + setProp = getExtraPropsNames.indexOf(propertyName) !== -1; + } + } else if(isExtraProp && node === ccbReader.getAnimationManager().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.getAnimationManager().getContainerSize(parent); + var pt = cc._getAbsolutePosition(x,y,type,containerSize,propertyName); + node.setPosition(cc.getAbsolutePosition(pt,type,containerSize,propertyName)); //different to -x node.setPosition(pt); + + if(ccbReader.getAnimatedProperties().indexOf(propertyName) > -1){ + var baseValue = [x,y,type]; + ccbReader.getAnimationManager().setBaseValue(baseValue,node,propertyName); + } + + return pt; + }, + + parsePropTypePoint:function (node, parent, ccbReader) { + var x = ccbReader.readFloat(); + var y = ccbReader.readFloat(); + + return cc.p(x, y); + }, + + parsePropTypePointLock:function (node, parent, ccbReader) { + var x = ccbReader.readFloat(); + var y = ccbReader.readFloat(); + + return cc.p(x, y); + }, + + parsePropTypeSize:function (node, parent, ccbReader) { + var width = ccbReader.readFloat(); + var height = ccbReader.readFloat(); + + var type = ccbReader.readInt(false); + + var containerSize = ccbReader.getAnimationManager().getContainerSize(parent); + + 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; + } + + return cc.size(width, height); + }, + + 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.getAnimatedProperties().indexOf(propertyName) > -1){ + ccbReader.getAnimationManager().setBaseValue([x,y,type],node,propertyName); + } + + if (type === CCB_SCALETYPE_MULTIPLY_RESOLUTION) { + x *= cc.BuilderReader.getResolutionScale(); + y *= cc.BuilderReader.getResolutionScale(); + } + + return [x, y]; + }, + + parsePropTypeFloat:function (node, parent, ccbReader) { + return ccbReader.readFloat(); + }, + + parsePropTypeDegrees:function (node, parent, ccbReader, propertyName) { + var ret = ccbReader.readFloat(); + if(ccbReader.getAnimatedProperties().indexOf(propertyName) > -1){ + ccbReader.getAnimationManager().setBaseValue(ret,node, propertyName); + } + return ret; + }, + + 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) { + var f = ccbReader.readFloat(); + var fVar = ccbReader.readFloat(); + return [f, fVar]; + }, + + parsePropTypeCheck:function (node, parent, ccbReader, propertyName) { + var ret = ccbReader.readBool(); + if(ccbReader.getAnimatedProperties().indexOf(propertyName) > -1){ + ccbReader.getAnimationManager().setBaseValue(ret,node, propertyName); + } + return ret; + }, + + parsePropTypeSpriteFrame:function (node, parent, ccbReader, propertyName) { + var spriteSheet = ccbReader.readCachedString(); + var spriteFile = ccbReader.readCachedString(); + + var spriteFrame; + if(spriteFile != null && spriteFile.length !== 0){ + if(spriteSheet.length === 0){ + spriteFile = ccbReader.getCCBRootPath() + 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.getCCBRootPath() + spriteSheet; + //load the sprite sheet only if it is not loaded + if(ccbReader.getLoadedSpriteSheet().indexOf(spriteSheet) === -1){ + frameCache.addSpriteFrames(spriteSheet); + ccbReader.getLoadedSpriteSheet().push(spriteSheet); + } + spriteFrame = frameCache.getSpriteFrame(spriteFile); + } + if(ccbReader.getAnimatedProperties().indexOf(propertyName) > -1){ + ccbReader.getAnimationManager().setBaseValue(spriteFrame,node,propertyName); + } + } + + return spriteFrame; + }, + + parsePropTypeAnimation:function (node, parent, ccbReader) { + var animationFile = ccbReader.getCCBRootPath() + 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 != null && animation !== "") { + var animationCache = cc.animationCache; + animationCache.addAnimations(animationFile); + + ccAnimation = animationCache.getAnimation(animation); + } + return ccAnimation; + }, + + parsePropTypeTexture:function (node, parent, ccbReader) { + var spriteFile = ccbReader.getCCBRootPath() + ccbReader.readCachedString(); + + if(spriteFile !== "") + return cc.textureCache.addImage(spriteFile); + return null; + }, + + parsePropTypeByte:function (node, parent, ccbReader, propertyName) { + var ret = ccbReader.readByte(); + if(ccbReader.getAnimatedProperties().indexOf(propertyName) > -1){ + ccbReader.getAnimationManager().setBaseValue(ret,node, propertyName); + } + return ret; + }, + + parsePropTypeColor3:function (node, parent, ccbReader, propertyName) { + var red = ccbReader.readByte(); + var green = ccbReader.readByte(); + var blue = ccbReader.readByte(); + var color = {r:red, g:green, b:blue }; + if(ccbReader.getAnimatedProperties().indexOf(propertyName) > -1){ + ccbReader.getAnimationManager().setBaseValue(cc.Color3BWapper.create(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; + + var colors = []; + colors[0] = {r:red, g:green, b:blue, a:alpha}; + colors[1] = {r:redVar, g:greenVar, b:blueVar, a:alphaVar}; + + return colors; + }, + + parsePropTypeFlip:function (node, parent, ccbReader) { + var flipX = ccbReader.readBool(); + var flipY = ccbReader.readBool(); + + return [flipX, flipY]; + }, + + 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.isJSControlled()) { + if (selectorTarget === CCB_TARGETTYPE_DOCUMENTROOT) { + target = ccbReader.getAnimationManager().getRootNode(); + } else if (selectorTarget === CCB_TARGETTYPE_OWNER) { + target = ccbReader.getOwner(); + } + + 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.getCCBSelectorResolver(); + if (ccbSelectorResolver != null) + 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.isJSControlled()){ + var target = null; + if (selectorTarget === CCB_TARGETTYPE_DOCUMENTROOT) { + target = ccbReader.getAnimationManager().getRootNode(); + } else if (selectorTarget === CCB_TARGETTYPE_OWNER) { + target = ccbReader.getOwner(); + } + + if (target !== null) { + if (selectorName.length > 0) { + var selCCControlHandler = 0; + + if (target.onResolveCCBCCControlSelector) { + selCCControlHandler = target.onResolveCCBCCControlSelector(target, selectorName); + } + if (selCCControlHandler === 0) { + var ccbSelectorResolver = ccbReader.getCCBSelectorResolver(); + if (ccbSelectorResolver != null) { + selCCControlHandler = ccbSelectorResolver.onResolveCCBCCControlSelector(target, selectorName); + } + } + + if (selCCControlHandler === 0) { + cc.log("Skipping selector '" + selectorName + "' since no CCBSelectorResolver is present."); + } else { + return new BlockCCControlData(selCCControlHandler,target,controlEvents); + } + } 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.getCCBRootPath() + 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); + bytes = cc.loader.loadBinarySync(realUrl); + cc.loader.cache[ccbFileName] = bytes; + } + + myCCBReader.initWithData(bytes,ccbReader.getOwner()); + myCCBReader.getAnimationManager().setRootContainerSize(parent.getContentSize()); + myCCBReader.setAnimationManagers(ccbReader.getAnimationManagers()); + + myCCBReader.getAnimationManager().setOwner(ccbReader.getOwner()); + var ccbFileNode = myCCBReader.readFileWithCleanUp(false); + ccbReader.setAnimationManagers(myCCBReader.getAnimationManagers()); + + if(ccbFileNode && myCCBReader.getAnimationManager().getAutoPlaySequenceId() !== -1) + myCCBReader.getAnimationManager().runAnimations(myCCBReader.getAnimationManager().getAutoPlaySequenceId(),0); + + return ccbFileNode; + }, + + parsePropTypeFloatXY:function(node, parent, ccbReader){ + var x = ccbReader.readFloat(); + var y = ccbReader.readFloat(); + return [x,y]; + }, + + 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.setScaleX(scaleLock[0]); + node.setScaleY(scaleLock[1]); + } else { + ASSERT_FAIL_UNEXPECTED_PROPERTY(propertyName); + } + }, + onHandlePropTypeFloatXY: function (node, parent, propertyName, xy, ccbReader) { + if (propertyName === PROPERTY_SKEW) { + node.setSkewX(xy[0]); + node.setSkewY(xy[1]); + } 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[0]); + node[nameY](xy[1]); + } + }, + 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.setTag(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.setVisible(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/extensions/ccb-reader/CCNodeLoaderLibrary.js b/extensions/ccb-reader/CCNodeLoaderLibrary.js new file mode 100644 index 00000000000..cece42021fe --- /dev/null +++ b/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/extensions/ccb-reader/CCSpriteLoader.js b/extensions/ccb-reader/CCSpriteLoader.js new file mode 100644 index 00000000000..857b2bb025d --- /dev/null +++ b/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/extensions/ccpool/CCPool.js b/extensions/ccpool/CCPool.js new file mode 100644 index 00000000000..464f3b75431 --- /dev/null +++ b/extensions/ccpool/CCPool.js @@ -0,0 +1,141 @@ +/**************************************************************************** + 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... + *

+ * + * @class 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. + * @method putInPool + * @param {Object} obj - The need put in pool object. + * @example {@link utils/api/cocos/docs/extensions/ccpool/putInPool.js} + */ + putInPool: function (obj) { + var cid = cc.js._getClassId(obj.constructor); + if (!cid) { + return; + } + if (!this._pool[cid]) { + this._pool[cid] = []; + } + // JSB retain to avoid being auto released + obj.retain && obj.retain(); + // User implementation for disable the object + obj.unuse && obj.unuse(); + this._pool[cid].push(obj); + }, + + /** + * Check if this kind of obj has already in pool. + * @method hasObject + * @param {Object} objClass - The check object class. + * @returns {Boolean} If this kind of obj is already in pool return true,else return false. + */ + hasObject: function (objClass) { + var cid = cc.js._getClassId(objClass); + var list = this._pool[cid]; + if (!list || list.length === 0) { + return false; + } + return true; + }, + + /** + * Remove the obj if you want to delete it. + * @method removeObject + */ + removeObject: function (obj) { + var cid = cc.js._getClassId(obj.constructor); + if (cid) { + var list = this._pool[cid]; + 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. + * @method getFromPool + * @returns {*} Call the reuse function an return the obj. + */ + getFromPool: function (objClass/*,args*/) { + if (this.hasObject(objClass)) { + var cid = cc.js._getClassId(objClass); + var list = this._pool[cid]; + 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. + * @method drainAllPools + */ + 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 = {}; + } +}; \ No newline at end of file diff --git a/extensions/ccui/base-classes/CCProtectedNode.js b/extensions/ccui/base-classes/CCProtectedNode.js new file mode 100644 index 00000000000..e3b1d976f0e --- /dev/null +++ b/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 = []; + }, + + /** + *

+ * 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.onEnter(); + // prevent onEnterTransitionDidFinish to be called twice when a node is added in onEnter + if(this._isTransitionFinished) + child.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.onExitTransitionDidStart(); + child.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.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.onExitTransitionDidStart(); + child.onExit(); + } + + if (cleanup) + child.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 len = _children.length, i, j, tmp; + for(i=1; i= 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(){}, + + /** + * Stops itself and its children and protected children's all running actions and schedulers + * @override + */ + cleanup: function(){ + cc.Node.prototype.cleanup.call(this); + var locChildren = this._protectedChildren; + for(var i = 0 , len = locChildren.length; i < len; i++) + locChildren[i].cleanup(); + }, + + /** + * Calls its parent's onEnter and calls its protected children's onEnter + * @override + */ + onEnter: function(){ + cc.Node.prototype.onEnter.call(this); + var locChildren = this._protectedChildren; + for(var i = 0, len = locChildren.length;i< len;i++) + locChildren[i].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 one, e.g. Node::onEnterTransitionDidFinish() + *

+ * @override + */ + onEnterTransitionDidFinish: function(){ + cc.Node.prototype.onEnterTransitionDidFinish.call(this); + var locChildren = this._protectedChildren; + for(var i = 0, len = locChildren.length;i< len;i++) + locChildren[i].onEnterTransitionDidFinish(); + }, + + /** + * Calls its parent's onExit and calls its protected children's onExit + * @override + */ + onExit:function(){ + cc.Node.prototype.onExit.call(this); + var locChildren = this._protectedChildren; + for(var i = 0, len = locChildren.length;i< len;i++) + locChildren[i].onExit(); + }, + + /** + *

+ * Event 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. + *

+ */ + onExitTransitionDidStart: function(){ + cc.Node.prototype.onExitTransitionDidStart.call(this); + var locChildren = this._protectedChildren; + for(var i = 0, len = locChildren.length;i< len;i++) + locChildren[i].onExitTransitionDidStart(); + }, + + _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(); +}; \ No newline at end of file diff --git a/extensions/ccui/base-classes/CCProtectedNodeCanvasRenderCmd.js b/extensions/ccui/base-classes/CCProtectedNodeCanvasRenderCmd.js new file mode 100644 index 00000000000..351438ae36d --- /dev/null +++ b/extensions/ccui/base-classes/CCProtectedNodeCanvasRenderCmd.js @@ -0,0 +1,240 @@ +/**************************************************************************** + 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) { + cc.Node.CanvasRenderCmd.call(this, renderable); + this._cachedParent = null; + this._cacheDirty = false; + }; + + var proto = cc.ProtectedNode.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + cc.js.mixin(proto, cc.ProtectedNode.RenderCmd); + proto.constructor = cc.ProtectedNode.CanvasRenderCmd; + + proto.visit = function(parentCmd){ + var node = this._node; + // quick return if not visible + if (!node._visible) + return; + + //visit for canvas + var i, j; + var children = node._children, child; + var locChildren = node._children, locProtectedChildren = node._protectedChildren; + var childLen = locChildren.length, pLen = locProtectedChildren.length; + + this._syncStatus(parentCmd); + + node.sortAllChildren(); + node.sortAllProtectedChildren(); + + var pChild; + // draw children zOrder < 0 + for (i = 0; i < childLen; i++) { + child = children[i]; + if (child._localZOrder < 0) + child.visit(this); + else + break; + } + for (j = 0; j < pLen; j++) { + pChild = locProtectedChildren[j]; + if (pChild && pChild._localZOrder < 0){ + this._changeProtectedChild(pChild); + pChild.visit(this); + } + else + break; + } + + cc.renderer.pushRenderCommand(this); + + for (; i < childLen; i++) + children[i] && children[i].visit(this); + for (; j < pLen; j++){ + pChild = locProtectedChildren[j]; + if(!pChild) continue; + this._changeProtectedChild(pChild); + pChild.visit(this); + } + + this._dirtyFlag = 0; + this._cacheDirty = false; + }; + + proto.transform = function(parentCmd, recursive){ + var node = this._node; + + if(node._changePosition) + node._changePosition(); + + var t = node.getNodeToParentTransform(), worldT = this._worldTransform; + if (parentCmd) { + var pt = parentCmd._worldTransform; + // cc.AffineTransformConcat is incorrect at get world transform + worldT.a = t.a * pt.a + t.b * pt.c; //a + worldT.b = t.a * pt.b + t.b * pt.d; //b + worldT.c = t.c * pt.a + t.d * pt.c; //c + worldT.d = t.c * pt.b + t.d * pt.d; //d + + worldT.tx = pt.a * t.tx + pt.c * t.ty + pt.tx; + worldT.ty = pt.d * t.ty + pt.ty + pt.b * t.tx; + } else { + worldT.a = t.a; + worldT.b = t.b; + worldT.c = t.c; + worldT.d = t.d; + worldT.tx = t.tx; + worldT.ty = t.ty; + } + var i, len, locChildren = node._children; + if(recursive && locChildren && locChildren.length !== 0){ + for(i = 0, len = locChildren.length; i< len; i++){ + locChildren[i]._renderCmd.transform(this, recursive); + } + } + locChildren = node._protectedChildren; + if(recursive && locChildren && locChildren.length !== 0){ + for(i = 0, len = locChildren.length; i< len; i++){ + locChildren[i]._renderCmd.transform(this, recursive); + } + } + }; +})(); \ No newline at end of file diff --git a/extensions/ccui/base-classes/CCProtectedNodeWebGLRenderCmd.js b/extensions/ccui/base-classes/CCProtectedNodeWebGLRenderCmd.js new file mode 100644 index 00000000000..deb34b22270 --- /dev/null +++ b/extensions/ccui/base-classes/CCProtectedNodeWebGLRenderCmd.js @@ -0,0 +1,135 @@ +/**************************************************************************** + 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) { + cc.Node.WebGLRenderCmd.call(this, renderable); + }; + + var proto = cc.ProtectedNode.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + cc.js.mixin(proto, cc.ProtectedNode.RenderCmd); + proto.constructor = cc.ProtectedNode.WebGLRenderCmd; + + proto.visit = function(parentCmd){ + var node = this._node; + // quick return if not visible + if (!node._visible) + return; + var i, j, currentStack = cc.current_stack; + + //optimize performance for javascript + currentStack.stack.push(currentStack.top); + this._syncStatus(parentCmd); + currentStack.top = this._stackMatrix; + + var locGrid = node.grid; + if (locGrid && locGrid._active) + locGrid.beforeDraw(); + + //node.transform(node._parent && node._parent._renderCmd); + + var locChildren = node._children, locProtectedChildren = node._protectedChildren; + var childLen = locChildren.length, pLen = locProtectedChildren.length; + node.sortAllChildren(); + node.sortAllProtectedChildren(); + + var pChild; + // draw children zOrder < 0 + for (i = 0; i < childLen; i++) { + if (locChildren[i] && locChildren[i]._localZOrder < 0) + locChildren[i].visit(this); + else + break; + } + for(j = 0; j < pLen; j++){ + pChild = locProtectedChildren[j]; + if (pChild && pChild._localZOrder < 0){ + this._changeProtectedChild(pChild); + pChild.visit(this); + }else + break; + } + + cc.renderer.pushRenderCommand(this); + + // draw children zOrder >= 0 + for (; i < childLen; i++) { + locChildren[i] && locChildren[i].visit(this); + } + for (; j < pLen; j++) { + pChild = locProtectedChildren[j]; + if(!pChild) continue; + this._changeProtectedChild(pChild); + pChild.visit(this); + } + + if (locGrid && locGrid._active) + locGrid.afterDraw(node); + + this._dirtyFlag = 0; + //optimize performance for javascript + currentStack.top = currentStack.stack.pop(); + }; + + proto.transform = function(parentCmd, recursive){ + var node = this._node; + var t4x4 = this._transform4x4, stackMatrix = this._stackMatrix, + parentMatrix = parentCmd ? parentCmd._stackMatrix : cc.current_stack.top; + + // Convert 3x3 into 4x4 matrix + var trans = node.getNodeToParentTransform(); + + if(node._changePosition) + node._changePosition(); + + var t4x4Mat = t4x4.mat; + t4x4Mat[0] = trans.a; + t4x4Mat[4] = trans.c; + t4x4Mat[12] = trans.tx; + t4x4Mat[1] = trans.b; + t4x4Mat[5] = trans.d; + t4x4Mat[13] = trans.ty; + + // Update Z vertex manually + t4x4Mat[14] = node._vertexZ; + + //optimize performance for Javascript + cc.kmMat4Multiply(stackMatrix, parentMatrix, t4x4); + + var i, len, locChildren = node._children; + if(recursive && locChildren && locChildren.length !== 0){ + for(i = 0, len = locChildren.length; i< len; i++){ + locChildren[i]._renderCmd.transform(this, recursive); + } + } + locChildren = node._protectedChildren; + if(recursive && locChildren && locChildren.length !== 0){ + for(i = 0, len = locChildren.length; i< len; i++){ + locChildren[i]._renderCmd.transform(this, recursive); + } + } + }; +})(); \ No newline at end of file diff --git a/extensions/ccui/base-classes/UIScale9Sprite.js b/extensions/ccui/base-classes/UIScale9Sprite.js new file mode 100644 index 00000000000..ff2d12c9133 --- /dev/null +++ b/extensions/ccui/base-classes/UIScale9Sprite.js @@ -0,0 +1,1118 @@ +/**************************************************************************** + 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. + + 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 + ****************************************************************************/ + +EventTarget = require("../cocos2d/core/event/event-target"); + +/** + *

+ * 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.
+ * @note: it will refactor in v3.1
+ * @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# */{ + + _textureInited:false, + _spriteRect: null, + _spriteFrameRotated:false, + + //To keep backward compatibility + //this member is just used for secondary usage, instead, insetTop/Bottom/Left/right is the key value. + _derivedCapInsets:null, + + _scale9Image: null, //the original sprite + + _scale9Enabled: true, + _blendFunc:null, + + /** Original sprite's size. */ + _originalSize:null, + /** Preferred sprite's size. By default the preferred size is the original size. */ + + //if the preferredSize component is given as -1, it is ignored + _preferredSize:null, + + /** Sets the left side inset */ + _insetLeft: 0.0, + /** Sets the top side inset */ + _insetTop:0.0, + /** Sets the right side inset */ + _insetRight:0.0, + /** Sets the bottom side inset */ + _insetBottom:0.0, + + _flippedX: false, + _flippedY: false, + _isPatch9: false, + _brightState: 0, + _nonSliceSpriteAnchor: null, + _textureLoaded:false, + _renderingType:1, + + _quads:[], + _quadsDirty : true, + + ctor: function(file, rect, capInsets) { + cc.Node.prototype.ctor.call(this); + this._nonSliceSpriteAnchor = cc.p(0.5,0.5); + this._originalSize = cc.size(0,0); + this._preferredSize = cc.rect(0,0,0,0); + this._spriteRect = cc.rect(0,0,0,0); + this._blendFunc = cc.BlendFunc._disable(); + this.setAnchorPoint(cc.p(0.5,0.5)); + + if(file != undefined) + { + if(file instanceof cc.SpriteFrame) { + this.initWithSpriteFrame(file, rect); + } + else{ + var frame = cc.spriteFrameCache.getSpriteFrame(file); + if(frame){ + this.initWithSpriteFrame(frame, rect); + } + else + this.initWithFile(file,rect,capInsets); + } + } + else + { + this.init(); + } + }, + + textureLoaded:function(){ + return this._textureLoaded; + }, + + /** + * Initializes a 9-slice sprite with a texture file, a delimitation zone and + * with the specified cap insets. + * Once the sprite is created, you can then call its "setContentSize:" method + * to resize the sprite will all it's 9-slice goodness intract. + * It respects the anchorPoint too. + * + * @param file The name of the texture file. + * @param rect The rectangle that describes the sub-part of the texture that + * is the whole image. If the shape is the whole texture, set this to the + * texture's full rect. + * @param capInsets The values to use for the cap insets. + * @return True if initialize success, false otherwise. + */ + initWithFile : function(file, rect, capInsets){ + if(file instanceof cc.Rect) { + capInsets = file; + file = rect; + } + rect = rect || cc.rect(0,0,0,0); + + if(!file){ + throw new Error("ccui.Scale9Sprite.initWithFile(): file should be non-null"); + } + + var sprite = cc.Sprite.create(file,rect); + + this.init(sprite, rect, capInsets); + }, + + /** + * Initializes a 9-slice sprite with an sprite frame and with the specified + * cap insets. + * Once the sprite is created, you can then call its "setContentSize:" method + * to resize the sprite will all it's 9-slice goodness intract. + * It respects the anchorPoint too. + * + * @param spriteFrame The sprite frame object. + * @param capInsets The values to use for the cap insets. + * @return True if initializes success, false otherwise. + */ + initWithSpriteFrame : function(spriteFrame, capInsets){ + if(!spriteFrame || !spriteFrame.getTexture()) + throw new Error("ccui.Scale9Sprite.initWithSpriteFrame(): spriteFrame should be non-null and its texture should be non-null"); + + var locLoaded = spriteFrame.textureLoaded(); + + if(capInsets!== undefined) this._derivedCapInsets = capInsets; + this._textureLoaded = false; + var _onTextureLoadedCallback = function () { + var sprite = cc.Sprite.createWithSpriteFrame(spriteFrame); + this._onScale9ResourcesLoaded(sprite,spriteFrame.isRotated(),spriteFrame.getRect(),spriteFrame.getOriginalSize()); + }; + + if (!locLoaded) { + + spriteFrame.once("load", _onTextureLoadedCallback, this); + } + else { + _onTextureLoadedCallback.call(this); + } + }, + + /** + * Initializes a 9-slice sprite with an sprite frame name and with the specified + * cap insets. + * Once the sprite is created, you can then call its "setContentSize:" method + * to resize the sprite will all it's 9-slice goodness intract. + * It respects the anchorPoint too. + * + * @param spriteFrameName The sprite frame name. + * @param capInsets The values to use for the cap insets. + * @return True if initializes success, false otherwise. + */ + initWithSpriteFrameName : function(spriteFrameName, capInsets){ + if(!spriteFrameName) + throw new Error("ccui.Scale9Sprite.initWithSpriteFrameName(): spriteFrameName should be non-null"); + + var frame = cc.spriteFrameCache.getSpriteFrame(spriteFrameName); + + if (!frame) { + cc.log("ccui.Scale9Sprite.initWithSpriteFrameName(): can't find the sprite frame by spriteFrameName"); + return false; + } + + return this.initWithSpriteFrame(frame, capInsets); + }, + + /** + * @brief Initializes a 9-slice sprite with an sprite instance. + * Once the sprite is created, you can then call its "setContentSize:" method + * to resize the sprite will all it's 9-slice goodness intract. + * It respects the anchorPoint too. + * + * @param sprite The sprite instance. + * @param rect A delimitation zone. + * @param rotated Whether the sprite is rotated or not. + * @param offset The offset when slice the sprite. + * @param originalSize The original size of sprite. + * @param capInsets The values to use for the cap insets. + * @return True if initializes success, false otherwise. + */ + init : function(sprite,rect,rotated,offset,originalSize,capInsets){ + if(sprite === null) return true; + + sprite = sprite || null; + rect = rect || cc.rect(0,0,0,0); + rotated = rotated || false; + offset = offset || cc.p(0,0); + originalSize = originalSize || cc.size(rect.width, rect.height); + + if(rotated instanceof cc.Rect) + { + capInsets = arguments[2]; + rotated = false; + } + if(offset instanceof cc.Rect) + { + capInsets = arguments[3]; + offset = cc.p(0,0); + } + + this.updateWithSprite(sprite, rect, rotated, offset, originalSize, capInsets); + + return true; + }, + + /** + * 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}. + * @js NA + * @lua NA + */ + setBlendFunc : function(blendFunc){ + this._blendFunc.src = blendFunc.src; + this._blendFunc.dst = blendFunc.dst; + this._applyBlendFunc(); + }, + + /** + * Returns the blending function that is currently being used. + * + * @return A BlendFunc structure with source and destination factor which specified pixel arithmetic. + * @js NA + * @lua NA + */ + getBlendFunc : function(){ + return new cc.BlendFunc(this._blendFunc.src, this._blendFunc.dst); + }, + + /** + * Creates and returns a new sprite object with the specified cap insets. + * You use this method to add cap insets to a sprite or to change the existing + * cap insets of a sprite. In both cases, you get back a new image and the + * original sprite remains untouched. + * + * @param capInsets The values to use for the cap insets. + * @return A Scale9Sprite instance. + */ + resizableSpriteWithCapInsets : function(capInsets){ + result = new cc.Scale9Sprite(); + result.init(this._scale9Image, this._spriteRect, this._spriteFrameRotated, cc.rect(0,0,0,0), this._originalSize, capInsets); + return result; + }, + + + _onScale9ResourcesLoaded: function (sprite, rotated, rect, size) { + this._spriteFrameRotated = rotated; + this._textureLoaded = true; + var opacity = this.getOpacity(); + var color = this.getColor(); + this._updateBlendFunc(sprite.getTexture()); + this._scale9Image = sprite; + this._scale9Image.setAnchorPoint(cc.p(0, 0)); + this._scale9Image.setPosition(cc.p(0, 0)); + if (this._spriteRect.equals(cc.rect(0, 0, 0, 0))) { + this._spriteRect = cc.rect(rect); + } + if (cc.sizeEqualToSize(this._originalSize, cc.size(0, 0))) { + this._originalSize = cc.size(size); + } + if (cc.sizeEqualToSize(this._preferredSize, cc.size(0, 0))) { + this.setPreferredSize(this._originalSize); + } + + if (!this._scale9Enabled) { + this.addChild(this._scale9Image); + this._adjustScale9ImagePosition(); + } + + this._applyBlendFunc(); + this.setState(this._brightState); + if (this._textureInited) { + // Restore color and opacity + this.setOpacity(opacity); + this.setColor(color); + } + this._textureInited = true; + + if(this._derivedCapInsets) + { + this._insetLeft = this._derivedCapInsets.x; + this._insetTop = this._derivedCapInsets.y; + this._insetRight = this._originalSize.width-this._insetLeft-this._derivedCapInsets.width; + this._insetBottom = this._originalSize.height-this._insetTop-this._derivedCapInsets.height; + this._derivedCapInsets = null; + } + + this._quadsDirty = true; + }, /** + * @brief Update Scale9Sprite with a specified sprite. + * + * @param sprite A sprite pointer. + * @param rect A delimitation zone. + * @param rotated Whether the sprite is rotated or not. + * @param offset The offset when slice the sprite. + * @param originalSize The origial size of the sprite. + * @param capInsets The Values to use for the cap insets. + * @return True if update success, false otherwise. + * @js NA + */ + updateWithSprite : function(sprite,textureRect,rotated,offset,originalSize,capInsets) { + if (offset instanceof cc.Rect) { + capInsets = arguments[3]; + offset = cc.size(0, 0); + originalSize = cc.size(textureRect.width, textureRect.height); + } + + this._spriteRect = cc.rect(textureRect); + this._spriteFrameRotated = rotated; + this._originalSize = originalSize; + if(capInsets !== undefined) this._derivedCapInsets = capInsets; + this._textureLoaded = false; + + if(sprite === null){ + this._quadsDirty = true; + return true; + } + + var textureloaded = sprite.textureLoaded(); + var _onTextureLoadedCallback = function() { + //do + var textureSize = sprite.getTexture().getContentSize(); + this._onScale9ResourcesLoaded(sprite,rotated,cc.rect(0, 0, textureSize.width, textureSize.height),textureSize); + }; + + if(textureloaded) { + _onTextureLoadedCallback.call(this); + } + else { + var texture = sprite.getTexture(); + if(texture) + texture.once("load",_onTextureLoadedCallback, this); + } + + return true; + + }, + + + /** + * @brief Change inner sprite's sprite frame. + * + * @param spriteFrame A sprite frame pointer. + * @param capInsets The values to use for the cap insets. + */ + setSpriteFrame : function(spriteFrame, capInsets){ + var sprite = cc.Sprite.createWithTexture(spriteFrame.getTexture()); + this.updateWithSprite(sprite, + spriteFrame.getRect(), + spriteFrame.isRotated(), + spriteFrame.getOffset(), + spriteFrame.getOriginalSize(), + capInsets); + }, + + // overrides + setContentSize : function(size){ + if (cc.sizeEqualToSize(this._contentSize,size)) + { + return; + } + cc.Node.prototype.setContentSize.call(this, size); + this._preferredSize = size; + this._quadsDirty = true; + this._adjustScale9ImagePosition(); + }, + + setAnchorPoint : function(anchorPoint){ + cc.Node.prototype.setAnchorPoint.call(this, anchorPoint); + if (!this._scale9Enabled) + { + if (this._scale9Image !== null) + { + this._nonSliceSpriteAnchor = anchorPoint; + this._scale9Image.setAnchorPoint(anchorPoint); + this._adjustScale9ImagePosition(); + } + } + }, + + /** + * Change the state of 9-slice sprite. + * @see `State` + * @param state A enum value in State. + * @since v3.4 + */ + setState : function(state){ + this._brightState = state; + this._renderCmd.setState(state); + }, + + /** + * Query the current bright state. + * @return @see `State` + * @since v3.7 + */ + getState : function(){ + return this._brightState; + }, + + /** + * @brief Query the sprite's original size. + * + * @return Sprite size. + */ + getOriginalSize : function(){ + return cc.size(this._originalSize); + }, + + /** + * @brief Change the preferred size of Scale9Sprite. + * + * @param size A delimitation zone. + */ + setPreferredSize : function(size) + { + return this.setContentSize(size); + }, + + /** + * @brief Query the Scale9Sprite's preferred size. + * + * @return Scale9Sprite's preferred size. + */ + getPreferredSize : function(){ + return cc.size(this._preferredSize); + }, + + + setRenderingType: function(type) { + if(this._renderingType == type) return; + this._renderingType = type; + this._quadsDirty = true; + }, + + /** + * @brief Change the cap inset size. + * + * @param rect A delimitation zone. + */ + setCapInsets : function(capInsets){ + if(this._textureLoaded) { + this._quadsDirty = true; + this._insetLeft = capInsets.x; + this._insetTop = capInsets.y; + this._insetRight = this._originalSize.width - this._insetLeft - capInsets.width; + this._insetBottom = this._originalSize.height - this._insetTop - capInsets.height; + } else { + cc.log("Can not set capinset when resource is loading"); + } + }, + + /** + * @brief Query the Scale9Sprite's preferred size. + * + * @return Scale9Sprite's cap inset. + */ + getCapInsets : function(){ + + return cc.rect(this._insetLeft, this._insetTop, this._originalSize.width - this._insetLeft - this._insetRight, + this._originalSize.height - this._insetTop - this._insetBottom); + }, + + /** + * @brief Change the left sprite's cap inset. + * + * @param leftInset The values to use for the cap inset. + */ + setInsetLeft : function(insetLeft){ + this._insetLeft = insetLeft; + this._quadsDirty = true; + }, + + /** + * @brief Query the left sprite's cap inset. + * + * @return The left sprite's cap inset. + */ + getInsetLeft : function(){ + return this._insetLeft; + }, + + /** + * @brief Change the top sprite's cap inset. + * + * @param topInset The values to use for the cap inset. + */ + setInsetTop : function(insetTop){ + this._insetTop = insetTop; + this._quadsDirty = true; + }, + + /** + * @brief Query the top sprite's cap inset. + * + * @return The top sprite's cap inset. + */ + getInsetTop : function(){ + return this._insetTop; + }, + + /** + * @brief Change the right sprite's cap inset. + * + * @param rightInset The values to use for the cap inset. + */ + setInsetRight : function(insetRight){ + this._insetRight = insetRight; + this._quadsDirty = true; + }, + + /** + * @brief Query the right sprite's cap inset. + * + * @return The right sprite's cap inset. + */ + getInsetRight : function(){ + return this._insetRight; + }, + + /** + * @brief Change the bottom sprite's cap inset. + * + * @param bottomInset The values to use for the cap inset. + + */ + setInsetBottom : function(insetBottom) + { + this._insetBottom = insetBottom; + this._quadsDirty = true; + }, + + /** + * @brief Query the bottom sprite's cap inset. + * + * @return The bottom sprite's cap inset. + */ + getInsetBottom : function(){ + return this._insetBottom; + }, + + /** + * @brief Toggle 9-slice feature. + * If Scale9Sprite is 9-slice disabled, the Scale9Sprite will rendered as a normal sprite. + * @param enabled True to enable 9-slice, false otherwise. + * @js NA + */ + setScale9Enabled : function(enabled){ + if (this._scale9Enabled == enabled) { + return; + } + this._scale9Enabled = enabled; + if(this._scale9Image) { + if (!this._scale9Enabled) { + this.addChild(this._scale9Image); + this._adjustScale9ImagePosition(); + } + else { + this.removeChild(this._scale9Image); + } + + } + + this._quadsDirty = true; + }, + + /** + * @brief Query whether the Scale9Sprite is enable 9-slice or not. + * + * @return True if 9-slice is enabled, false otherwise. + * @js NA + */ + isScale9Enabled : function(){ + return this._scale9Enabled; + }, + + /** + * @brief Get the original no 9-sliced sprite + * + * @return A sprite instance. + */ + getSprite : function(){ + return this._scale9Image; + }, + + /** + * Sets whether the widget should be flipped horizontally or not. + * + * @param flippedX true if the widget should be flipped horizontally, false otherwise. + */ + setFlippedX : function(flippedX){ + var realScale = this.getScaleX(); + this._flippedX = flippedX; + 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); + * + * @return 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 flippedY true if the widget should be flipped vertically, false otherwise. + */ + setFlippedY : function(flippedY){ + var realScale = this.getScaleY(); + this._flippedY = flippedY; + 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); + * + * @return true if the widget is flipped vertically, false otherwise. + */ + isFlippedY : function(){ + return this._flippedY; + }, + + //override the setScale function of Node + 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(){ + return this.getScaleX(); + }, + + setCameraMask : function(mask,applyChildren/** = true**/){ + cc.Node.prototype.setCameraMask.call(this,mask,applyChildren); + if(this._scale9Image) + this._scale9Image.setCameraMask(mask,applyChildren); + }, + + //_updateCapInset : function() { + // var insets = cc.rect(this._insetLeft, + // this._insetTop, + // this._originalSize.width-this._insetLeft-this._insetRight, + // this._originalSize.height-this._insetTop-this._insetBottom); + // this.setCapInsets(insets); + //}, + + _adjustScale9ImagePosition : function(){ + if (this._scale9Image) + { + if (!this._scale9Enabled) { + this._scale9Image.setAnchorPoint(this._nonSliceSpriteAnchor); + this._scale9Image.setPosition(this._contentSize.width * this._scale9Image.getAnchorPoint().x, + this._contentSize.height * this._scale9Image.getAnchorPoint().y); + + } + } + }, + + _applyBlendFunc : function(){ + if(this._scale9Image != null) + this._scale9Image.setBlendFunc(this._blendFunc); + }, + + _updateBlendFunc : function(texture){ + if (! texture || ! texture.hasPremultipliedAlpha()) + { + this._blendFunc = cc.BlendFunc._alphaNonPremultiplied(); + this.setOpacityModifyRGB(false); + } + else + { + this._blendFunc = cc.BlendFunc._alphaPremultiplied(); + this.setOpacityModifyRGB(true); + } + }, + + //calculate the quads used for scale 9 rendering. + + _buildQuads : function(){ + if (this._scale9Enabled) + { + var tex = null; + if(this._scale9Image != null) + { + tex = this._scale9Image.getTexture(); + } + + if (tex == null) + { + return; + } + + var capInsets = cc.rectPointsToPixels(this.getCapInsets()); + var textureRect = cc.rectPointsToPixels(this._spriteRect); + var spriteRectSize = cc.sizePointsToPixels(this._originalSize); + + //handle .9.png + if (this._isPatch9) + { + spriteRectSize = cc.size(spriteRectSize.width - 2, spriteRectSize.height-2); + } + + if(capInsets.equals(cc.rect(0,0,0,0))) + { + capInsets = cc.rect(spriteRectSize.width/3, spriteRectSize.height/3, + spriteRectSize.width/3, spriteRectSize.height/3); + } + // + + this._calculateQuads(this._calculateUV(tex, capInsets, spriteRectSize), + this._calculateVertices(capInsets, spriteRectSize)); + } + }, + + _cleanupQuads : function(){ + this._quads = []; + }, + + _calculateUV : function(texture, capInsets, spriteRectSize){ + var atlasWidth = texture.getPixelWidth(); + var atlasHeight = texture.getPixelHeight(); + + //caculate texture coordinate + var leftWidth = 0, centerWidth = 0, rightWidth = 0; + var topHeight = 0, centerHeight = 0, bottomHeight = 0; + + if (this._spriteFrameRotated) + { + rightWidth = capInsets.y; + centerWidth = capInsets.height; + leftWidth = spriteRectSize.height - centerWidth - rightWidth; + + topHeight = capInsets.x; + centerHeight = capInsets.width; + bottomHeight = spriteRectSize.width - (topHeight + centerHeight); + } + else + { + leftWidth = capInsets.x; + centerWidth = capInsets.width; + rightWidth = spriteRectSize.width - (leftWidth + centerWidth); + + topHeight = capInsets.y; + centerHeight = capInsets.height; + bottomHeight =spriteRectSize.height - (topHeight + centerHeight); + } + + var textureRect = cc.rectPointsToPixels(this._spriteRect); + //handle .9.png + if (this._isPatch9) + { + //This magic number is used to avoiding artifact with .9.png format. + var offset = 1.3; + textureRect = cc.rect(textureRect.origin.x + offset, + textureRect.origin.y + offset, + textureRect.size.width - 2, + textureRect.size.height - 2); + } + + //uv computation should take spritesheet into account. + var u0, u1, u2, u3; + var v0, v1, v2, v3; + if (this._spriteFrameRotated) + { + u0 = textureRect.x / atlasWidth; + u1 = (leftWidth + textureRect.x) / atlasWidth; + u2 = (leftWidth + centerWidth + textureRect.x) / atlasWidth; + u3 = (textureRect.x + textureRect.height) / atlasWidth; + + v3 = textureRect.y / atlasHeight; + v2 = (topHeight + textureRect.y) / atlasHeight; + v1 = (topHeight + centerHeight + textureRect.y) / atlasHeight; + v0 = (textureRect.y + textureRect.width) / atlasHeight; + } + else + { + u0 = textureRect.x / atlasWidth; + u1 = (leftWidth + textureRect.x) / atlasWidth; + u2 = (leftWidth + centerWidth + textureRect.x) / atlasWidth; + u3 = (textureRect.x + textureRect.width) / atlasWidth; + + v0 = textureRect.y / atlasHeight; + v1 = (topHeight + textureRect.y) / atlasHeight; + v2 = (topHeight + centerHeight + textureRect.y) / atlasHeight; + v3 = (textureRect.y + textureRect.height) / atlasHeight; + } + + var uvCoordinates = []; + uvCoordinates.push(cc.p(u0,v3)); + uvCoordinates.push(cc.p(u1,v2)); + uvCoordinates.push(cc.p(u2,v1)); + uvCoordinates.push(cc.p(u3,v0)); + + return uvCoordinates; + }, + + _calculateVertices : function(capInsets,spriteRectSize){ + var leftWidth = 0, centerWidth = 0, rightWidth = 0; + var topHeight = 0, centerHeight = 0, bottomHeight = 0; + + leftWidth = capInsets.x; + centerWidth = capInsets.width; + rightWidth = spriteRectSize.width - (leftWidth + centerWidth); + + topHeight = capInsets.y; + centerHeight = capInsets.height; + bottomHeight = spriteRectSize.height - (topHeight + centerHeight); + + + leftWidth = leftWidth / cc.contentScaleFactor(); + rightWidth = rightWidth / cc.contentScaleFactor(); + topHeight = topHeight / cc.contentScaleFactor(); + bottomHeight = bottomHeight / cc.contentScaleFactor(); + var preferSize = this.getPreferredSize(); + var sizableWidth = preferSize.width - leftWidth - rightWidth; + var sizableHeight = preferSize.height - topHeight - bottomHeight; + var x0,x1,x2,x3; + var y0,y1,y2,y3; + if(sizableWidth >= 0) + { + x0 = 0; + x1 = leftWidth; + x2 = leftWidth + sizableWidth; + x3 = preferSize.width; + } + else + { + var xScale = preferSize.width / (leftWidth + rightWidth); + x0 = 0; + x1 = x2 = leftWidth * xScale; + x3 = (leftWidth + rightWidth) * xScale; + } + + if(sizableHeight >= 0) + { + y0 = 0; + y1 = bottomHeight; + y2 = bottomHeight + sizableHeight; + y3 = preferSize.height; + } + else + { + var yScale = preferSize.height / (topHeight + bottomHeight); + y0 = 0; + y1 = y2= bottomHeight * yScale; + y3 = (bottomHeight + topHeight) * yScale; + } + + var vertices = []; + vertices.push(cc.p(x0,y0)); + vertices.push(cc.p(x1,y1)); + vertices.push(cc.p(x2,y2)); + vertices.push(cc.p(x3,y3)); + + return vertices; + }, + + _onColorOpacityDirty : function() { + var color = this.getDisplayedColor(); + color.a = this.getDisplayedOpacity(); + var index; + var quadLength = this._quads.length; + for(index = 0; index < quadLength; ++index) { + //svar quad = this._quads[index]; + this._quads[index]._bl.colors = color; + this._quads[index]._br.colors = color; + this._quads[index]._tl.colors = color; + this._quads[index]._tr.colors = color; + } + }, + + _calculateQuads : function(uv, vertices){ + var color = this.getDisplayedColor(); + color.a = this.getDisplayedOpacity() ; + if(this._renderingType == ccui.Scale9Sprite.RenderingType.SIMPLE) + { + var quad = new cc.V3F_C4B_T2F_Quad(); + quad._bl.colors = color; + quad._br.colors = color; + quad._tl.colors = color; + quad._tr.colors = color; + + quad._bl.vertices = new cc.Vertex3F(vertices[0].x,vertices[0].y,0); + quad._br.vertices = new cc.Vertex3F(vertices[3].x,vertices[0].y,0); + quad._tl.vertices = new cc.Vertex3F(vertices[0].x,vertices[3].y,0); + quad._tr.vertices = new cc.Vertex3F(vertices[3].x,vertices[3].y,0); + + if (this._spriteFrameRotated) + { + quad._bl.texCoords = new cc.Tex2F(uv[0].x,uv[0].y); + quad._br.texCoords = new cc.Tex2F(uv[0].x,uv[3].y); + quad._tl.texCoords = new cc.Tex2F(uv[3].x,uv[0].y); + quad._tr.texCoords = new cc.Tex2F(uv[3].x,uv[3].y); + } + else + { + quad._bl.texCoords = new cc.Tex2F(uv[0].x,uv[0].y); + quad._br.texCoords = new cc.Tex2F(uv[3].x,uv[0].y); + quad._tl.texCoords = new cc.Tex2F(uv[0].x,uv[3].y); + quad._tr.texCoords = new cc.Tex2F(uv[3].x,uv[3].y); + + } + this._quads.push(quad); + + return; + } + + for(var j = 0; j < 3; ++j) { //row + for(var i = 0; i < 3; ++i){ //column + var quad = new cc.V3F_C4B_T2F_Quad(); + quad._bl.colors = color; + quad._br.colors = color; + quad._tl.colors = color; + quad._tr.colors = color; + + quad._bl.vertices = new cc.Vertex3F(vertices[i].x,vertices[j].y,0); + quad._br.vertices = new cc.Vertex3F(vertices[i+1].x,vertices[j].y,0); + quad._tl.vertices = new cc.Vertex3F(vertices[i].x,vertices[j+1].y,0); + quad._tr.vertices = new cc.Vertex3F(vertices[i+1].x,vertices[j+1].y,0); + + if (this._spriteFrameRotated) + { + quad._bl.texCoords = new cc.Tex2F(uv[j].x,uv[i].y); + quad._br.texCoords = new cc.Tex2F(uv[j].x,uv[i+1].y); + quad._tl.texCoords = new cc.Tex2F(uv[j+1].x,uv[i].y); + quad._tr.texCoords = new cc.Tex2F(uv[i+1].x,uv[j+1].y); + } + else + { + quad._bl.texCoords = new cc.Tex2F(uv[i].x,uv[j].y); + quad._br.texCoords = new cc.Tex2F(uv[i+1].x,uv[j].y); + quad._tl.texCoords = new cc.Tex2F(uv[i].x,uv[j+1].y); + quad._tr.texCoords = new cc.Tex2F(uv[i+1].x,uv[j+1].y); + + } + this._quads.push(quad); + } + } + }, + + _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; +EventTarget.polyfill(_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}; +ccui.Scale9Sprite.RenderingType = {SIMPLE: 0, SLICE: 1}; diff --git a/extensions/ccui/base-classes/UIScale9SpriteCanvasRenderCmd.js b/extensions/ccui/base-classes/UIScale9SpriteCanvasRenderCmd.js new file mode 100644 index 00000000000..2aa45819a90 --- /dev/null +++ b/extensions/ccui/base-classes/UIScale9SpriteCanvasRenderCmd.js @@ -0,0 +1,141 @@ +/**************************************************************************** + 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.Scale9Sprite.CanvasRenderCmd = function (renderable) { + cc.Node.CanvasRenderCmd.call(this, renderable); + this._needDraw = true; + this._state = ccui.Scale9Sprite.state.NORMAL; + this._textureToRender = null; + }; + + var proto = ccui.Scale9Sprite.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + proto.constructor = ccui.Scale9Sprite.CanvasRenderCmd; + + proto._updateDisplayOpacity = function(parentOpacity){ + cc.Node.WebGLRenderCmd.prototype._updateDisplayOpacity.call(this, parentOpacity); + var node = this._node; + var scale9Image = node._scale9Image; + if(scale9Image) { + var opacity = 255; + if (node._cascadeOpacityEnabled) opacity = this._displayedOpacity; + scale9Image._renderCmd._updateDisplayOpacity(opacity); + } + }; + + proto._updateDisplayColor = function(parentColor){ + cc.Node.WebGLRenderCmd.prototype._updateDisplayColor.call(this, parentColor); + var node = this._node; + var scale9Image = node._scale9Image; + if(scale9Image){ + var color = cc.Color.WHITE; + if(this._cascadeColorEnabled) color = node._displayedColor; + scale9Image._renderCmd._updateDisplayColor(color); + scale9Image._renderCmd._updateColor(); + } + this._textureToRender = null; + }; + + proto.setState = function(state){ + if(this._state === state) return; + + var locScale9Image = this._node._scale9Image; + if(!locScale9Image) + return; + this._state = state; + this._textureToRender = null; + }; + + proto.rendering = function (ctx, scaleX, scaleY) { + var node = this._node; + if(!node._scale9Enabled) + return; + var locDisplayOpacity = this._displayedOpacity; + var alpha = locDisplayOpacity/ 255; + var locTexture = null; + if(node.getSprite()) locTexture = node.getSprite()._texture; + if (!node._textureLoaded || locDisplayOpacity === 0) + return; + if(this._textureToRender === null){ + this._textureToRender = 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 = locTexture._generateColorTexture(color.r,color.g,color.b); + } + + var wrapper = ctx || cc._renderContext, context = wrapper.getContext(); + wrapper.setTransform(this._worldTransform, scaleX, scaleY); + wrapper.setCompositeOperation(node._blendFunc); + wrapper.setGlobalAlpha(alpha); + + if(this._textureToRender) { + if(node._quadsDirty){ + node._cleanupQuads(); + node._buildQuads(); + node._quadsDirty = false; + } + + var quads = node._quads; + for( var i = 0; i < quads.length; ++i) + { + var sx,sy,sw,sh; + var x, y, w,h; + + x = quads[i]._bl.vertices.x; + y = quads[i]._bl.vertices.y; + w = quads[i]._tr.vertices.x - quads[i]._bl.vertices.x; + h = quads[i]._tr.vertices.y - quads[i]._bl.vertices.y; + y = - y - h; + + var textureWidth = this._textureToRender.getPixelWidth(); + var textureHeight = this._textureToRender.getPixelHeight(); + + sx = quads[i]._bl.texCoords.u * textureWidth; + sy = quads[i]._bl.texCoords.v * textureHeight; + sw = (quads[i]._tr.texCoords.u - quads[i]._bl.texCoords.u) * textureWidth; + sh = (quads[i]._tr.texCoords.v - quads[i]._bl.texCoords.v) * textureHeight; + + x = x * scaleX; + y = y * scaleY; + w = w * scaleX; + h = h * scaleY; + + var image = this._textureToRender._htmlElementObj; + if (this._textureToRender._pattern !== "") { + wrapper.setFillStyle(context.createPattern(image, this._textureToRender._pattern)); + context.fillRect(x, y, w, h); + } else { + context.drawImage(image, + sx, sy, sw, sh, + x, y, w, h); + } + } + + } + cc.g_NumberOfDraws = cc.g_NumberOfDraws + 9; + } +})(); \ No newline at end of file diff --git a/extensions/ccui/base-classes/UIScale9SpriteWebGLRenderCmd.js b/extensions/ccui/base-classes/UIScale9SpriteWebGLRenderCmd.js new file mode 100644 index 00000000000..cbf1aa0d41c --- /dev/null +++ b/extensions/ccui/base-classes/UIScale9SpriteWebGLRenderCmd.js @@ -0,0 +1,180 @@ +/**************************************************************************** + 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; + ccui.Scale9Sprite.WebGLRenderCmd = function (renderable) { + cc.Node.WebGLRenderCmd.call(this, renderable); + this._needDraw = true; + this._cachedParent = null; + this._cacheDirty = false; + this._quadWebBuffer = cc._renderContext.createBuffer(); + this._colorOpacityDirty = false; + }; + + var proto = ccui.Scale9Sprite.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + proto.constructor = ccui.Scale9Sprite.WebGLRenderCmd; + + proto.rendering = function (ctx){ + var node = this._node; + if(!node._scale9Enabled) + return; + + var locTexture = null; + if(node.getSprite()) locTexture = node.getSprite()._texture; + if (!node.textureLoaded() || this._displayedOpacity === 0) + return; + var needRebuildWebBuffer = false; + if(node._quadsDirty){ + node._cleanupQuads(); + node._buildQuads(); + node._quadsDirty = false; + this._colorOpacityDirty = false; + needRebuildWebBuffer = true; + } + if(this._colorOpacityDirty) { + node._onColorOpacityDirty(); + this._colorOpacityDirty = false; + needRebuildWebBuffer = true; + } + var gl = ctx || cc._renderContext ; + + if(locTexture != null) + { + this._shaderProgram.use(); + this._shaderProgram._setUniformForMVPMatrixWithMat4(this._stackMatrix); + + cc.glBlendFunc(node._blendFunc.src, node._blendFunc.dst); + //optimize performance for javascript + cc.glBindTexture2DN(0, locTexture); // = cc.glBindTexture2D(locTexture); + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX); + + gl.bindBuffer(gl.ARRAY_BUFFER, this._quadWebBuffer); + + var quads = node._quads; + var bufferOffset = 0; + var quadsLength = quads.length; + if(quadsLength == 0) return; + if (needRebuildWebBuffer) + { + gl.bufferData(gl.ARRAY_BUFFER,quads[0].arrayBuffer.byteLength * quads.length, gl.DYNAMIC_DRAW); + for(var i = 0; i < quads.length; ++i){ + gl.bufferSubData(gl.ARRAY_BUFFER, bufferOffset,quads[i].arrayBuffer); + bufferOffset = bufferOffset + quads[i].arrayBuffer.byteLength; + } + } + bufferOffset = 0; + for(var i = 0; i < quads.length; ++i) + { + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 24, bufferOffset); //cc.VERTEX_ATTRIB_POSITION + gl.vertexAttribPointer(1, 4, gl.UNSIGNED_BYTE, true, 24, 12 + bufferOffset); //cc.VERTEX_ATTRIB_COLOR + gl.vertexAttribPointer(2, 2, gl.FLOAT, false, 24, 16 + bufferOffset); //cc.VERTEX_ATTRIB_TEX_COORDS + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + bufferOffset = bufferOffset + quads[i].arrayBuffer.byteLength; + } + + cc.g_NumberOfDraws += quads.length; + + } + + }; + + proto._updateDisplayOpacity = function(parentOpacity){ + cc.Node.WebGLRenderCmd.prototype._updateDisplayOpacity.call(this, parentOpacity); + var node = this._node; + var scale9Image = node._scale9Image; + if(scale9Image) { + var opacity = 255; + if (node._cascadeOpacityEnabled) opacity = this._displayedOpacity; + scale9Image._renderCmd._updateDisplayOpacity(opacity); + } + + this._colorOpacityDirty = true; + }; + + proto._updateDisplayColor = function(parentColor){ + cc.Node.WebGLRenderCmd.prototype._updateDisplayColor.call(this, parentColor); + var node = this._node; + var scale9Image = node._scale9Image; + if(scale9Image){ + var color = cc.Color.WHITE; + if(this._cascadeColorEnabled) color = node._displayedColor; + scale9Image._renderCmd._updateDisplayColor(color); + scale9Image._renderCmd._updateColor(); + } + + this._colorOpacityDirty = true; + }; + + proto.setState = function (state) { + var node = this._node; + var scale9Image = this._node._scale9Image; + if(!node.isScale9Enabled) + { + if (state === ccui.Scale9Sprite.state.NORMAL) { + scale9Image.setShaderProgram(cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR)); + } else if (state === ccui.Scale9Sprite.state.GRAY) { + scale9Image.setShaderProgram(ccui.Scale9Sprite.WebGLRenderCmd._getGrayShaderProgram()); + } + } + else + { + if (state === ccui.Scale9Sprite.state.NORMAL) { + node.setShaderProgram(cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR)); + } else if (state === ccui.Scale9Sprite.state.GRAY) { + node.setShaderProgram(ccui.Scale9Sprite.WebGLRenderCmd._getGrayShaderProgram()); + } + } + }; + + ccui.Scale9Sprite.WebGLRenderCmd._grayShaderProgram = null; + ccui.Scale9Sprite.WebGLRenderCmd._getGrayShaderProgram = function(){ + var grayShader = ccui.Scale9Sprite.WebGLRenderCmd._grayShaderProgram; + if(grayShader) + return grayShader; + + grayShader = new cc.GLProgram(); + grayShader.initWithVertexShaderByteArray(cc.SHADER_POSITION_TEXTURE_COLOR_VERT, ccui.Scale9Sprite.WebGLRenderCmd._grayShaderFragment); + grayShader.addAttribute(cc.ATTRIBUTE_NAME_POSITION, cc.VERTEX_ATTRIB_POSITION); + grayShader.addAttribute(cc.ATTRIBUTE_NAME_COLOR, cc.VERTEX_ATTRIB_COLOR); + grayShader.addAttribute(cc.ATTRIBUTE_NAME_TEX_COORD, cc.VERTEX_ATTRIB_TEX_COORDS); + grayShader.link(); + grayShader.updateUniforms(); + + ccui.Scale9Sprite.WebGLRenderCmd._grayShaderProgram = grayShader; + return grayShader; + }; + + ccui.Scale9Sprite.WebGLRenderCmd._grayShaderFragment = + "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" + + "}"; +})(); \ No newline at end of file diff --git a/extensions/ccui/base-classes/UIWidget.js b/extensions/ccui/base-classes/UIWidget.js new file mode 100644 index 00000000000..d46cdaa8e62 --- /dev/null +++ b/extensions/ccui/base-classes/UIWidget.js @@ -0,0 +1,2069 @@ +/**************************************************************************** + 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. + ****************************************************************************/ + +EventTarget = require("../cocos2d/core/event/event-target"); + +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; + this.init(); //TODO + }, + + /** + * 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 () { + if (cc.ProtectedNode.prototype.init.call(this)) { + this._layoutParameterDictionary = {}; + this._initRenderer(); + this.setBright(true); + + this.onFocusChanged = this.onFocusChange.bind(this); + this.onNextFocusedWidget = null; + this.setAnchorPoint(cc.p(0.5, 0.5)); + + this.ignoreContentAdaptWithSize(true); + return true; + } + return false; + }, + + /** + * 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(); + 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); + }, + + _cleanupWidget: function(){ + //clean up _touchListener + this._eventDispatcher.removeEventListener(this._touchListener); + this._touchEnabled = false; + this._touchListener = null; + + //cleanup focused widget and focus navigation controller + if (ccui.Widget._focusedWidget === this){ + ccui.Widget._focusedWidget = null; + ccui.Widget._focusNavigationController = null; + } + }, + + /** + *

+ * 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; + }, + + /** + * 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){ + var locWidth = (height === undefined) ? contentSize.width : contentSize; + var locHeight = (height === undefined) ? contentSize.height : height; + cc.Node.prototype.setContentSize.call(this, locWidth, locHeight); + + 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; + } + this._onSizeChanged(); + }, + + _setWidth: function (w) { + 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; + } + this._onSizeChanged(); + }, + _setHeight: function (h) { + 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; + } + this._onSizeChanged(); + }, + + /** + * Changes the percent that is widget's percent size + * @param {cc.Vec2} 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.Vec2} + */ + 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.Vec2} 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(); + } + } + }, + + /** + * 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, + + /** + * 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.Event.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 () {}, + + _isTargetActive: function (type) { + if (type === cc.Event.TOUCH) + return this._enabled && this._touchEnabled; + else if (type === cc.Event.FOCUS) + return this._enabled && this._focusEnabled; + else + return this._enabled; + }, + + // Store all bubbling parents that are listening to the same event in the array + _getBubblingTargets: function (type, array) { + var parent = this.getWidgetParent(); + while (parent) { + if (parent.hasEventListener(type)) { + array.push(parent); + } + parent = parent.getWidgetParent(); + } + }, + + /** + *

+ * 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 dispatchEvent
+ * 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) { + event._widgetEventType = ccui.Widget.TOUCH_BEGAN; + this.dispatchEvent(event); + } + + this._pushDownEvent(); + return true; + }, + + /** + *

+ * The callback of touch moved event.
+ * It sets the highlight state by touch, sends event to parent widget by dispatchEvent 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) { + event._widgetEventType = ccui.Widget.TOUCH_MOVED; + this.dispatchEvent(event); + } + this._moveEvent(); + }, + + /** + *

+ * The callback of touch end event + * It sends event to parent widget by dispatchEvent, + * 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) { + event._widgetEventType = ccui.Widget.TOUCH_ENDED; + this.dispatchEvent(event); + } + + 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.Vec2} 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.Vec2} 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.Vec2} 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.Vec2} 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.Vec2} 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.Vec2|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.Vec2} 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; + }, + _setYPercent: function (percent) { + if (this._usingLayoutComponent){ + var component = this._getOrCreateLayoutComponent(); + component.setPositionPercentY(percent.x); + component.refreshLayout(); + return; + } + this._positionPercent.y = percent; + }, + + /** + * Gets the percent (x,y) of the widget + * @returns {cc.Vec2} 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.Vec2} + */ + getTouchBeganPosition: function(){ + return cc.p(this._touchBeganPosition); + }, + + /** + * Gets the position of touch moved event + * @returns {cc.Vec2} + */ + getTouchMovePosition: function(){ + return cc.p(this._touchMovePosition); + }, + + /** + * Gets the position of touch end event + * @returns {cc.Vec2} + */ + 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()); + } + this._onSizeChanged(); + }, + + /*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.Vec2} 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.Vec2} 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.Vec2} 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.Vec2} 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.js.array.remove(this._nodes, node); + }, + + /** + * 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(); +}; + +EventTarget.polyfill(ccui.Widget.prototype); + +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 {string} + */ +ccui.Widget.TOUCH_BEGAN = 'widget_touch_began'; +/** + * The touch moved type of ccui.Widget's touch event + * @constant + * @type {string} + */ +ccui.Widget.TOUCH_MOVED = 'widget_touch_moved'; +/** + * The touch end type of ccui.Widget's touch event + * @constant + * @type {string} + */ +ccui.Widget.TOUCH_ENDED = 'widget_touch_ended'; +/** + * The touch canceled type of ccui.Widget's touch event + * @constant + * @type {string} + */ +ccui.Widget.TOUCH_CANCELED = 'widget_touch_canceled'; + +//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/extensions/ccui/base-classes/UIWidgetRenderCmd.js b/extensions/ccui/base-classes/UIWidgetRenderCmd.js new file mode 100644 index 00000000000..f01b12d5c98 --- /dev/null +++ b/extensions/ccui/base-classes/UIWidgetRenderCmd.js @@ -0,0 +1,97 @@ +/**************************************************************************** + 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.once(cc.game.EVENT_RENDERER_INITED, function () { + if (cc._renderType === cc.game.RENDER_TYPE_CANVAS) { + ccui.Widget.CanvasRenderCmd = function (renderable) { + cc.ProtectedNode.CanvasRenderCmd.call(this, 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; + if (node._visible) { + node._adaptRenderers(); + cc.ProtectedNode.CanvasRenderCmd.prototype.visit.call(this, parentCmd); + } + }; + + proto.transform = function (parentCmd, recursive) { + var node = this._node; + + if (node._visible) { + 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; + } + } + } + cc.ProtectedNode.CanvasRenderCmd.prototype.transform.call(this, parentCmd, recursive); + } + }; + } else { + ccui.Widget.WebGLRenderCmd = function (renderable) { + cc.ProtectedNode.WebGLRenderCmd.call(this, 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; + if (node._visible) { + node._adaptRenderers(); + cc.ProtectedNode.WebGLRenderCmd.prototype.visit.call(this, parentCmd); + } + }; + + proto.transform = function(parentCmd, recursive){ + var node = this._node; + if (node._visible) { + 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; + } + } + } + cc.ProtectedNode.WebGLRenderCmd.prototype.transform.call(this, parentCmd, recursive); + } + }; + } +}); diff --git a/extensions/ccui/layouts/UIHBox.js b/extensions/ccui/layouts/UIHBox.js new file mode 100644 index 00000000000..69657952ed1 --- /dev/null +++ b/extensions/ccui/layouts/UIHBox.js @@ -0,0 +1,80 @@ +/**************************************************************************** + 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.Type.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, size); + if(size !== undefined) + this.initWithSize(size); + else + this.init(); + }, + + /** + * Initialize a HBox. please do not call this function by yourself, you should pass the parameters to constructor to initialize it. + * @override + * @returns {boolean} + */ + init: function(){ + if(ccui.Layout.prototype.init.call(this)){ + this.setLayoutType(ccui.Layout.Type.LINEAR_HORIZONTAL); + return true; + } + return false; + }, + + /** + * Initializes a HBox with size. + * @param size + * @returns {boolean} + */ + initWithSize: function(size){ + if(this.init()){ + this.setContentSize(size); + return true; + } + return false; + } +}); + +/** + * 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/extensions/ccui/layouts/UILayout.js b/extensions/ccui/layouts/UILayout.js new file mode 100644 index 00000000000..77eb4f9fb73 --- /dev/null +++ b/extensions/ccui/layouts/UILayout.js @@ -0,0 +1,1555 @@ +/**************************************************************************** + 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.Type} 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.Type.ABSOLUTE; + this._widgetType = ccui.Widget.TYPE_CONTAINER; + this._clippingType = ccui.Layout.CLIPPING_STENCIL; + this._colorType = ccui.Layout.BG_COLOR_NONE; + + ccui.Widget.prototype.ctor.call(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.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.onExit(); + }, + + /** + * 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.Type.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.Type.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, + + /** + * override "init" method of widget. please do not call this function by yourself, you should pass the parameters to constructor to initialize it. + * @returns {boolean} + * @override + */ + init: function () { + if (ccui.Widget.prototype.init.call(this)) { + this.ignoreContentAdaptWithSize(false); + this.setContentSize(cc.size(0, 0)); + this.setAnchorPoint(0, 0); + this.onPassFocusToChild = this._findNearestChildWidgetIndex.bind(this); + return true; + } + return false; + }, + + /** + * 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; + }, + + /** + *

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

+ * @override + * @param {cc.Node.RenderCmd} [parentCmd] + */ + visit: function (parentCmd) { + if (!this._visible) + return; + this._adaptRenderers(); + this._doLayout(); + + if (this._clippingEnabled) { + switch (this._clippingType) { + case ccui.Layout.CLIPPING_STENCIL: + this._renderCmd.stencilClippingVisit(parentCmd); + break; + case ccui.Layout.CLIPPING_SCISSOR: + this._renderCmd.scissorClippingVisit(parentCmd); + break; + default: + break; + } + } else + ccui.Widget.prototype.visit.call(this, parentCmd); + }, + + /** + * 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_STENCIL: + if (able){ + this._clippingStencil = new cc.DrawNode(); + this._renderCmd.rebindStencilRendering(this._clippingStencil); + if (this._running) + this._clippingStencil.onEnter(); + this._setStencilClippingSize(this._contentSize); + } else { + if (this._running && this._clippingStencil) + this._clippingStencil.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; + if(cc._renderType === cc.game.RENDER_TYPE_CANVAS && type === ccui.Layout.CLIPPING_SCISSOR){ + cc.log("Only supports STENCIL on canvas mode."); + 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 && this._clippingType === ccui.Layout.CLIPPING_STENCIL) { + 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.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(); + var finalX = worldPos.x - (scissorWidth * this._anchorPoint.x); + var finalY = worldPos.y - (scissorHeight * this._anchorPoint.y); + var finalWidth = scissorWidth; + var finalHeight = scissorHeight; + + var leftOffset = worldPos.x - parentClippingRect.x; + if (leftOffset < 0) { + finalX = parentClippingRect.x; + finalWidth += leftOffset; + } + var rightOffset = (worldPos.x + scissorWidth) - (parentClippingRect.x + parentClippingRect.width); + if (rightOffset > 0) + finalWidth -= rightOffset; + var topOffset = (worldPos.y + scissorHeight) - (parentClippingRect.y + parentClippingRect.height); + if (topOffset > 0) + finalHeight -= topOffset; + var bottomOffset = worldPos.y - parentClippingRect.y; + if (bottomOffset < 0) { + finalY = parentClippingRect.x; + finalHeight += bottomOffset; + } + if (finalWidth < 0) + finalWidth = 0; + if (finalHeight < 0) + finalHeight = 0; + this._clippingRect.x = finalX; + this._clippingRect.y = finalY; + this._clippingRect.width = finalWidth; + this._clippingRect.height = finalHeight; + } else { + this._clippingRect.x = worldPos.x - (scissorWidth * this._anchorPoint.x); + this._clippingRect.y = worldPos.y - (scissorHeight * this._anchorPoint.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.Type.ABSOLUTE: + break; + case ccui.Layout.Type.LINEAR_HORIZONTAL: + case ccui.Layout.Type.LINEAR_VERTICAL: + var layoutParameter = locChild.getLayoutParameter(ccui.LayoutParameter.LINEAR); + if (!layoutParameter) + locChild.setLayoutParameter(new ccui.LinearLayoutParameter()); + break; + case ccui.Layout.Type.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.Vec2} 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.Vec2} + */ + 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.Type} 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.Type.LINEAR_HORIZONTAL) + layoutSize.height = layoutSize.height - layoutSize.height/widgetCount * (widgetCount-1); + + if (type === ccui.Layout.Type.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 == null) + 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.bind(this) + : this._findFarthestChildWidgetIndex.bind(this); + } else if (direction === ccui.Widget.RIGHT) { + this.onPassFocusToChild = (previousWidgetPosition.x > widgetPosition.x) ? this._findFarthestChildWidgetIndex.bind(this) + : this._findNearestChildWidgetIndex.bind(this); + }else if(direction === ccui.Widget.DOWN) { + this.onPassFocusToChild = (previousWidgetPosition.y > widgetPosition.y) ? this._findNearestChildWidgetIndex.bind(this) + : this._findFarthestChildWidgetIndex.bind(this); + }else if(direction === ccui.Widget.UP) { + this.onPassFocusToChild = (previousWidgetPosition.y < widgetPosition.y) ? this._findNearestChildWidgetIndex.bind(this) + : this._findFarthestChildWidgetIndex.bind(this); + }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.Vec2} + * @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.Type.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.Type.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.Type.LINEAR_HORIZONTAL) { + if (direction === ccui.Widget.LEFT || direction === ccui.Widget.RIGHT) + return true; + else + return this._isWidgetAncestorSupportLoopFocus(parent, direction); + } + if (layoutType === ccui.Layout.Type.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; + +/** + * Enum for layout type + * @readonly + * @enum {number} + */ +ccui.Layout.Type = cc.Enum({ + /** + * The absolute of ccui.Layout's layout type. + */ + ABSOLUTE: 0, + /** + * The vertical of ccui.Layout's layout type. + */ + LINEAR_VERTICAL: 1, + /** + * The horizontal of ccui.Layout's layout type. + */ + LINEAR_HORIZONTAL: 2, + /** + * The relative of ccui.Layout's layout type. + */ + 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; \ No newline at end of file diff --git a/extensions/ccui/layouts/UILayoutCanvasRenderCmd.js b/extensions/ccui/layouts/UILayoutCanvasRenderCmd.js new file mode 100644 index 00000000000..df53a60560a --- /dev/null +++ b/extensions/ccui/layouts/UILayoutCanvasRenderCmd.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. + ****************************************************************************/ + +(function(){ + ccui.Layout.CanvasRenderCmd = function(renderable){ + ccui.ProtectedNode.CanvasRenderCmd.call(this, renderable); + this._needDraw = false; + + this._clipElemType = false; + this._locCache = null; + this._rendererSaveCmd = new cc.CustomRenderCmd(this, this._onRenderSaveCmd); + this._rendererSaveCmdSprite = new cc.CustomRenderCmd(this, this._onRenderSaveSpriteCmd); + this._rendererClipCmd = new cc.CustomRenderCmd(this, this._onRenderClipCmd); + this._rendererRestoreCmd = new cc.CustomRenderCmd(this, this._onRenderRestoreCmd); + }; + + var proto = ccui.Layout.CanvasRenderCmd.prototype = Object.create(ccui.ProtectedNode.CanvasRenderCmd.prototype); + proto.constructor = ccui.Layout.CanvasRenderCmd; + + proto.visit = function(parentCmd){ + var node = this._node; + if (!node._visible) + return; + node._adaptRenderers(); + node._doLayout(); + + if (node._clippingEnabled) { + switch (node._clippingType) { + case ccui.Layout.CLIPPING_STENCIL: + this.stencilClippingVisit(parentCmd); + break; + case ccui.Layout.CLIPPING_SCISSOR: + this.scissorClippingVisit(parentCmd); + break; + default: + break; + } + } else + ccui.Widget.CanvasRenderCmd.prototype.visit.call(this, parentCmd); + }; + + proto._onRenderSaveCmd = function(ctx, scaleX, scaleY){ + var wrapper = ctx || cc._renderContext, context = wrapper.getContext(); + if (this._clipElemType) { + var canvas = context.canvas; + this._locCache = ccui.Layout.CanvasRenderCmd._getSharedCache(); + this._locCache.width = canvas.width; + this._locCache.height = canvas.height; + var locCacheCtx = this._locCache.getContext("2d"); + locCacheCtx.drawImage(canvas, 0, 0); + } else { + wrapper.save(); + wrapper.save(); + wrapper.setTransform(this._worldTransform, scaleX, scaleY); + } + }; + + proto._onRenderSaveSpriteCmd = function(ctx){ + var wrapper = ctx || cc._renderContext; + //var node = this._node; + if (this._clipElemType) { + wrapper.setCompositeOperation("destination-in"); + } + }; + + proto._onRenderClipCmd = function(ctx){ + var wrapper = ctx || cc._renderContext, context = wrapper.getContext(); + if (!this._clipElemType) { + wrapper.restore(); + context.clip(); + } + }; + + proto._onRenderRestoreCmd = function(ctx){ + var wrapper = ctx || cc._renderContext, context = wrapper.getContext(); + + if (this._clipElemType) { + // Redraw the cached canvas, so that the cliped area shows the background etc. + context.save(); + context.setTransform(1, 0, 0, 1, 0, 0); + context.globalCompositeOperation = "destination-over"; + context.drawImage(this._locCache, 0, 0); + context.restore(); + }else{ + wrapper.restore(); //use for restore clip operation + } + }; + + proto.rebindStencilRendering = function(stencil){ + stencil._renderCmd.rendering = this.__stencilDraw; + }; + + proto.__stencilDraw = function(ctx,scaleX, scaleY){ //Only for Canvas + var wrapper = ctx || cc._renderContext, locContext = wrapper.getContext(), buffer = this._buffer; + + for (var i = 0, bufLen = buffer.length; i < bufLen; i++) { + var element = buffer[i], vertices = element.verts; + var firstPoint = vertices[0]; + locContext.beginPath(); + locContext.moveTo(firstPoint.x * scaleX, -firstPoint.y * scaleY); + for (var j = 1, len = vertices.length; j < len; j++) + locContext.lineTo(vertices[j].x * scaleX, -vertices[j].y * scaleY); + locContext.closePath(); + } + }; + + proto.stencilClippingVisit = proto.scissorClippingVisit = function(parentCmd){ + var node = this._node; + if (!node._clippingStencil || !node._clippingStencil.isVisible()) + return; + + this._clipElemType = node._stencil instanceof cc.Sprite; + this._syncStatus(parentCmd); + + cc.renderer.pushRenderCommand(this._rendererSaveCmd); + if (this._clipElemType) { + cc.ProtectedNode.prototype.visit.call(node, parentCmd); + cc.renderer.pushRenderCommand(this._rendererSaveCmdSprite); + } + node._clippingStencil.visit(this); + + cc.renderer.pushRenderCommand(this._rendererClipCmd); + if (!this._clipElemType) { + node.sortAllChildren(); + node.sortAllProtectedChildren(); + + var children = node._children; + var j=0, locProtectChildren = node._protectedChildren, i = 0, locChild; + var iLen = children.length, jLen = locProtectChildren.length; + + for( ; i < iLen; i++ ){ + locChild = children[i]; + if ( locChild && locChild.getLocalZOrder() < 0 ) + locChild.visit(this); + else + break; + } + for( ; j < jLen; j++ ) { + locChild = locProtectChildren[j]; + if ( locChild && locChild.getLocalZOrder() < 0 ) + locChild.visit(this); + else + break; + } + for (; i < iLen; i++) + children[i].visit(this); + for (; j < jLen; j++) + locProtectChildren[j].visit(this); + cc.renderer.pushRenderCommand(this._rendererRestoreCmd); + } + this._dirtyFlag = 0; + }; + + ccui.Layout.CanvasRenderCmd._getSharedCache = function () { + return (cc.ClippingNode._sharedCache) || (cc.ClippingNode._sharedCache = document.createElement("canvas")); + }; +})(); \ No newline at end of file diff --git a/extensions/ccui/layouts/UILayoutComponent.js b/extensions/ccui/layouts/UILayoutComponent.js new file mode 100644 index 00000000000..6ff48dbfcd3 --- /dev/null +++ b/extensions/ccui/layouts/UILayoutComponent.js @@ -0,0 +1,594 @@ +/**************************************************************************** + 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) { + 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.Type.NONE: + case ccui.RelativeLayoutParameter.Type.PARENT_TOP_LEFT: + this._finalPositionX = ap.x * cs.width; + this._finalPositionY = layoutSize.height - ((1.0 - ap.y) * cs.height); + break; + case ccui.RelativeLayoutParameter.Type.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.Type.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.Type.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.Type.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.Type.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.Type.PARENT_LEFT_BOTTOM: + this._finalPositionX = ap.x * cs.width; + this._finalPositionY = ap.y * cs.height; + break; + case ccui.RelativeLayoutParameter.Type.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.Type.PARENT_RIGHT_BOTTOM: + this._finalPositionX = layoutSize.width - ((1.0 - ap.x) * cs.width); + this._finalPositionY = ap.y * cs.height; + break; + + case ccui.RelativeLayoutParameter.Type.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.Type.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.Type.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.Type.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.Type.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.Type.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.Type.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.Type.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.Type.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.Type.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.Type.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.Type.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.Type.NONE: + case ccui.RelativeLayoutParameter.Type.PARENT_TOP_LEFT: + this._finalPositionX += mg.left; + this._finalPositionY -= mg.top; + break; + case ccui.RelativeLayoutParameter.Type.PARENT_TOP_CENTER_HORIZONTAL: + this._finalPositionY -= mg.top; + break; + case ccui.RelativeLayoutParameter.Type.PARENT_TOP_RIGHT: + this._finalPositionX -= mg.right; + this._finalPositionY -= mg.top; + break; + case ccui.RelativeLayoutParameter.Type.PARENT_LEFT_CENTER_VERTICAL: + this._finalPositionX += mg.left; + break; + case ccui.RelativeLayoutParameter.Type.CENTER_IN_PARENT: + break; + case ccui.RelativeLayoutParameter.Type.PARENT_RIGHT_CENTER_VERTICAL: + this._finalPositionX -= mg.right; + break; + case ccui.RelativeLayoutParameter.Type.PARENT_LEFT_BOTTOM: + this._finalPositionX += mg.left; + this._finalPositionY += mg.bottom; + break; + case ccui.RelativeLayoutParameter.Type.PARENT_BOTTOM_CENTER_HORIZONTAL: + this._finalPositionY += mg.bottom; + break; + case ccui.RelativeLayoutParameter.Type.PARENT_RIGHT_BOTTOM: + this._finalPositionX -= mg.right; + this._finalPositionY += mg.bottom; + break; + case ccui.RelativeLayoutParameter.Type.LOCATION_ABOVE_LEFTALIGN: + this._finalPositionY += mg.bottom; + this._finalPositionX += mg.left; + break; + case ccui.RelativeLayoutParameter.Type.LOCATION_ABOVE_RIGHTALIGN: + this._finalPositionY += mg.bottom; + this._finalPositionX -= mg.right; + break; + case ccui.RelativeLayoutParameter.Type.LOCATION_ABOVE_CENTER: + this._finalPositionY += mg.bottom; + break; + case ccui.RelativeLayoutParameter.Type.LOCATION_LEFT_OF_TOPALIGN: + this._finalPositionX -= mg.right; + this._finalPositionY -= mg.top; + break; + case ccui.RelativeLayoutParameter.Type.LOCATION_LEFT_OF_BOTTOMALIGN: + this._finalPositionX -= mg.right; + this._finalPositionY += mg.bottom; + break; + case ccui.RelativeLayoutParameter.Type.LOCATION_LEFT_OF_CENTER: + this._finalPositionX -= mg.right; + break; + case ccui.RelativeLayoutParameter.Type.LOCATION_RIGHT_OF_TOPALIGN: + this._finalPositionX += mg.left; + this._finalPositionY -= mg.top; + break; + case ccui.RelativeLayoutParameter.Type.LOCATION_RIGHT_OF_BOTTOMALIGN: + this._finalPositionX += mg.left; + this._finalPositionY += mg.bottom; + break; + case ccui.RelativeLayoutParameter.Type.LOCATION_RIGHT_OF_CENTER: + this._finalPositionX += mg.left; + break; + case ccui.RelativeLayoutParameter.Type.LOCATION_BELOW_LEFTALIGN: + this._finalPositionY -= mg.top; + this._finalPositionX += mg.left; + break; + case ccui.RelativeLayoutParameter.Type.LOCATION_BELOW_RIGHTALIGN: + this._finalPositionY -= mg.top; + this._finalPositionX -= mg.right; + break; + case ccui.RelativeLayoutParameter.Type.LOCATION_BELOW_CENTER: + this._finalPositionY -= mg.top; + break; + default: + break; + } + } +}; \ No newline at end of file diff --git a/extensions/ccui/layouts/UILayoutParameter.js b/extensions/ccui/layouts/UILayoutParameter.js new file mode 100644 index 00000000000..e66580f9589 --- /dev/null +++ b/extensions/ccui/layouts/UILayoutParameter.js @@ -0,0 +1,539 @@ +/**************************************************************************** + 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.js.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.Type.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 + +/** + * Enum for Relative layout parameter RelativeAlign + * @readonly + * @enum {number} + */ +ccui.RelativeLayoutParameter.Type = cc.Enum({ + /** + * The none of ccui.RelativeLayoutParameter's relative align. + */ + NONE: 0, + /** + * The parent's top left of ccui.RelativeLayoutParameter's relative align. + */ + PARENT_TOP_LEFT: 1, + /** + * The parent's top center horizontal of ccui.RelativeLayoutParameter's relative align. + */ + PARENT_TOP_CENTER_HORIZONTAL: 2, + /** + * The parent's top right of ccui.RelativeLayoutParameter's relative align. + */ + PARENT_TOP_RIGHT: 3, + /** + * The parent's left center vertical of ccui.RelativeLayoutParameter's relative align. + */ + PARENT_LEFT_CENTER_VERTICAL: 4, + + /** + * The center in parent of ccui.RelativeLayoutParameter's relative align. + */ + CENTER_IN_PARENT: 5, + + /** + * The parent's right center vertical of ccui.RelativeLayoutParameter's relative align. + */ + PARENT_RIGHT_CENTER_VERTICAL: 6, + /** + * The parent's left bottom of ccui.RelativeLayoutParameter's relative align. + */ + PARENT_LEFT_BOTTOM: 7, + /** + * The parent's bottom center horizontal of ccui.RelativeLayoutParameter's relative align. + */ + PARENT_BOTTOM_CENTER_HORIZONTAL: 8, + /** + * The parent's right bottom of ccui.RelativeLayoutParameter's relative align. + */ + PARENT_RIGHT_BOTTOM: 9, + + /** + * The location above left align of ccui.RelativeLayoutParameter's relative align. + */ + LOCATION_ABOVE_LEFTALIGN: 10, + /** + * The location above center of ccui.RelativeLayoutParameter's relative align. + */ + LOCATION_ABOVE_CENTER: 11, + /** + * The location above right align of ccui.RelativeLayoutParameter's relative align. + */ + LOCATION_ABOVE_RIGHTALIGN: 12, + /** + * The location left of top align of ccui.RelativeLayoutParameter's relative align. + */ + LOCATION_LEFT_OF_TOPALIGN: 13, + /** + * The location left of center of ccui.RelativeLayoutParameter's relative align. + */ + LOCATION_LEFT_OF_CENTER: 14, + /** + * The location left of bottom align of ccui.RelativeLayoutParameter's relative align. + */ + LOCATION_LEFT_OF_BOTTOMALIGN: 15, + /** + * The location right of top align of ccui.RelativeLayoutParameter's relative align. + */ + LOCATION_RIGHT_OF_TOPALIGN: 16, + /** + * The location right of center of ccui.RelativeLayoutParameter's relative align. + */ + LOCATION_RIGHT_OF_CENTER: 17, + /** + * The location right of bottom align of ccui.RelativeLayoutParameter's relative align. + */ + LOCATION_RIGHT_OF_BOTTOMALIGN: 18, + /** + * The location below left align of ccui.RelativeLayoutParameter's relative align. + */ + LOCATION_BELOW_LEFTALIGN: 19, + /** + * The location below center of ccui.RelativeLayoutParameter's relative align. + */ + LOCATION_BELOW_CENTER: 20, + /** + * The location below right align of ccui.RelativeLayoutParameter's relative align. + */ + 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/extensions/ccui/layouts/UILayoutWebGLRenderCmd.js b/extensions/ccui/layouts/UILayoutWebGLRenderCmd.js new file mode 100644 index 00000000000..baf4773fca7 --- /dev/null +++ b/extensions/ccui/layouts/UILayoutWebGLRenderCmd.js @@ -0,0 +1,242 @@ +/**************************************************************************** + 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){ + ccui.ProtectedNode.WebGLRenderCmd.call(this, renderable); + this._needDraw = false; + + this._currentStencilEnabled = 0; + this._currentStencilWriteMask = 0; + this._currentStencilFunc = 0; + this._currentStencilRef = 0; + this._currentStencilValueMask = 0; + this._currentStencilFail = 0; + this._currentStencilPassDepthFail = 0; + this._currentStencilPassDepthPass = 0; + this._currentDepthWriteMask = false; + + this._mask_layer_le = 0; + + this._beforeVisitCmdStencil = new cc.CustomRenderCmd(this, this._onBeforeVisitStencil); + this._afterDrawStencilCmd = new cc.CustomRenderCmd(this, this._onAfterDrawStencil); + this._afterVisitCmdStencil = new cc.CustomRenderCmd(this, this._onAfterVisitStencil); + this._beforeVisitCmdScissor = new cc.CustomRenderCmd(this, this._onBeforeVisitScissor); + this._afterVisitCmdScissor = new cc.CustomRenderCmd(this, this._onAfterVisitScissor); + }; + + var proto = ccui.Layout.WebGLRenderCmd.prototype = Object.create(ccui.ProtectedNode.WebGLRenderCmd.prototype); + proto.constructor = ccui.Layout.WebGLRenderCmd; + + proto.visit = function(parentCmd){ + var node = this._node; + if (!node._visible) + return; + node._adaptRenderers(); + node._doLayout(); + + if (node._clippingEnabled) { + switch (node._clippingType) { + case ccui.Layout.CLIPPING_STENCIL: + this.stencilClippingVisit(parentCmd); + break; + case ccui.Layout.CLIPPING_SCISSOR: + this.scissorClippingVisit(parentCmd); + break; + default: + break; + } + } else + ccui.Widget.WebGLRenderCmd.prototype.visit.call(this, parentCmd); + }; + + 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); + this._currentStencilWriteMask = gl.getParameter(gl.STENCIL_WRITEMASK); + this._currentStencilFunc = gl.getParameter(gl.STENCIL_FUNC); + this._currentStencilRef = gl.getParameter(gl.STENCIL_REF); + this._currentStencilValueMask = gl.getParameter(gl.STENCIL_VALUE_MASK); + this._currentStencilFail = gl.getParameter(gl.STENCIL_FAIL); + this._currentStencilPassDepthFail = gl.getParameter(gl.STENCIL_PASS_DEPTH_FAIL); + this._currentStencilPassDepthPass = gl.getParameter(gl.STENCIL_PASS_DEPTH_PASS); + + gl.enable(gl.STENCIL_TEST); + + gl.stencilMask(mask_layer); + + this._currentDepthWriteMask = gl.getParameter(gl.DEPTH_WRITEMASK); + + gl.depthMask(false); + + gl.stencilFunc(gl.NEVER, mask_layer, mask_layer); + gl.stencilOp(gl.ZERO, gl.KEEP, gl.KEEP); + + // draw a fullscreen solid rectangle to clear the stencil buffer + this._drawFullScreenQuadClearStencil(); + + gl.stencilFunc(gl.NEVER, mask_layer, mask_layer); + gl.stencilOp(gl.REPLACE, gl.KEEP, gl.KEEP); + }; + + proto._onAfterDrawStencil = function(ctx){ + var gl = ctx || cc._renderContext; + gl.depthMask(this._currentDepthWriteMask); + 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; + // manually restore the stencil state + gl.stencilFunc(this._currentStencilFunc, this._currentStencilRef, this._currentStencilValueMask); + gl.stencilOp(this._currentStencilFail, this._currentStencilPassDepthFail, this._currentStencilPassDepthPass); + gl.stencilMask(this._currentStencilWriteMask); + if (!this._currentStencilEnabled) + gl.disable(gl.STENCIL_TEST); + ccui.Layout.WebGLRenderCmd._layer--; + }; + + proto._onBeforeVisitScissor = function(ctx){ + var clippingRect = this._getClippingRect(); + var gl = ctx || cc._renderContext; + gl.enable(gl.SCISSOR_TEST); + + cc.view.setScissorInPoints(clippingRect.x, clippingRect.y, clippingRect.width, clippingRect.height); + }; + + proto._onAfterVisitScissor = function(ctx){ + var gl = ctx || cc._renderContext; + gl.disable(gl.SCISSOR_TEST); + }; + + proto._drawFullScreenQuadClearStencil = function(){ + // draw a fullscreen solid rectangle to clear the stencil buffer + cc.kmGLMatrixMode(cc.KM_GL_PROJECTION); + cc.kmGLPushMatrix(); + cc.kmGLLoadIdentity(); + cc.kmGLMatrixMode(cc.KM_GL_MODELVIEW); + cc.kmGLPushMatrix(); + cc.kmGLLoadIdentity(); + cc._drawingUtil.drawSolidRect(cc.p(-1,-1), cc.p(1,1), cc.color(255, 255, 255, 255)); + cc.kmGLMatrixMode(cc.KM_GL_PROJECTION); + cc.kmGLPopMatrix(); + cc.kmGLMatrixMode(cc.KM_GL_MODELVIEW); + cc.kmGLPopMatrix(); + }; + + proto.rebindStencilRendering = function(stencil){}; + + proto.transform = function(parentCmd, recursive){ + var node = this._node; + ccui.ProtectedNode.WebGLRenderCmd.prototype.transform.call(this, 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 + cc.Node.prototype.visit.call(node, parentCmd); + return; + } + + cc.renderer.pushRenderCommand(this._beforeVisitCmdStencil); + + //optimize performance for javascript + var currentStack = cc.current_stack; + currentStack.stack.push(currentStack.top); + this._syncStatus(parentCmd); + this._dirtyFlag = 0; + currentStack.top = this._stackMatrix; + + node._clippingStencil.visit(this); + + cc.renderer.pushRenderCommand(this._afterDrawStencilCmd); + + // draw (according to the stencil test func) this node and its childs + var i = 0; // used by _children + var j = 0; // used by _protectedChildren + + node.sortAllChildren(); + node.sortAllProtectedChildren(); + var locChildren = node._children, locProtectChildren = node._protectedChildren; + var iLen = locChildren.length, jLen = locProtectChildren.length, child; + for( ; i < iLen; i++ ){ + child = locChildren[i]; + if ( child && child.getLocalZOrder() < 0 ) + child.visit(this); + else + break; + } + for( ; j < jLen; j++ ) { + child = locProtectChildren[j]; + if ( child && child.getLocalZOrder() < 0 ) + child.visit(this); + else + break; + } + + for (; i < iLen; i++) + locChildren[i].visit(this); + for (; j < jLen; j++) + locProtectChildren[j].visit(this); + + cc.renderer.pushRenderCommand(this._afterVisitCmdStencil); + + //optimize performance for javascript + currentStack.top = currentStack.stack.pop(); + }; + + proto.scissorClippingVisit = function(parentCmd){ + cc.renderer.pushRenderCommand(this._beforeVisitCmdScissor); + cc.ProtectedNode.prototype.visit.call(this._node, parentCmd); + cc.renderer.pushRenderCommand(this._afterVisitCmdScissor); + }; + + ccui.Layout.WebGLRenderCmd._layer = -1; + ccui.Layout.WebGLRenderCmd._visit_once = null; +})(); diff --git a/extensions/ccui/layouts/UIRelativeBox.js b/extensions/ccui/layouts/UIRelativeBox.js new file mode 100644 index 00000000000..14a357853ef --- /dev/null +++ b/extensions/ccui/layouts/UIRelativeBox.js @@ -0,0 +1,79 @@ +/**************************************************************************** + 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.Type.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){ + if(size) + this.initWithSize(size); + else + this.init(); + }, + + /** + * Initializes a relative box. please do not call this function by yourself, you should pass the parameters to constructor to initialize it. + * @override + * @returns {boolean} + */ + init: function(){ + if(ccui.Layout.prototype.init.call(this)){ + this.setLayoutType(ccui.Layout.Type.RELATIVE); + return true; + } + return false; + }, + + /** + * Initializes a relative box with size + * @param {cc.Size} [size] + * @returns {boolean} + */ + initWithSize: function(size){ + if(this.init()){ + this.setContentSize(size); + return true; + } + return false; + } +}); + +/** + * 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/extensions/ccui/layouts/UIVBox.js b/extensions/ccui/layouts/UIVBox.js new file mode 100644 index 00000000000..352d21bafe7 --- /dev/null +++ b/extensions/ccui/layouts/UIVBox.js @@ -0,0 +1,79 @@ +/**************************************************************************** + 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.Type.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, size); + if(size !== undefined) + this.initWithSize(size); + else + this.init(); + }, + + /** + * Initializes a VBox. please do not call this function by yourself, you should pass the parameters to constructor to initialize it. + * @override + * @returns {boolean} + */ + init: function(){ + if(ccui.Layout.prototype.init.call(this)){ + this.setLayoutType(ccui.Layout.Type.LINEAR_VERTICAL); + return true; + } + return false; + }, + + /** + * Initializes a VBox with size. + * @param {cc.Size} size + * @returns {boolean} + */ + initWithSize: function(size){ + if(this.init()){ + this.setContentSize(size); + 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/extensions/ccui/system/CocosGUI.js b/extensions/ccui/system/CocosGUI.js new file mode 100644 index 00000000000..022c32430a1 --- /dev/null +++ b/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/extensions/ccui/system/UIHelper.js b/extensions/ccui/system/UIHelper.js new file mode 100644 index 00000000000..70d82c9b845 --- /dev/null +++ b/extensions/ccui/system/UIHelper.js @@ -0,0 +1,164 @@ +/**************************************************************************** + 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); + } +}; diff --git a/extensions/ccui/uiwidgets/UIButton.js b/extensions/ccui/uiwidgets/UIButton.js new file mode 100644 index 00000000000..e857e86b68e --- /dev/null +++ b/extensions/ccui/uiwidgets/UIButton.js @@ -0,0 +1,973 @@ +/**************************************************************************** + 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 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} titleFontColor - 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# */{ + _buttonNormalRenderer: null, + _buttonClickedRenderer: null, + _buttonDisableRenderer: null, + _titleRenderer: null, + + _normalFileName: "", + _clickedFileName: "", + _disabledFileName: "", + + _prevIgnoreSize: true, + _scale9Enabled: false, + + _capInsetsNormal: null, + _capInsetsPressed: null, + _capInsetsDisabled: null, + + _normalTexType: ccui.Widget.LOCAL_TEXTURE, + _pressedTexType: ccui.Widget.LOCAL_TEXTURE, + _disabledTexType: ccui.Widget.LOCAL_TEXTURE, + + _normalTextureSize: null, + _pressedTextureSize: null, + _disabledTextureSize: null, + + pressedActionEnabled: false, + _titleColor: null, + _normalTextureScaleXInSize: 1, + _normalTextureScaleYInSize: 1, + _pressedTextureScaleXInSize: 1, + _pressedTextureScaleYInSize: 1, + + _zoomScale: 0.1, + + _normalTextureLoaded: false, + _pressedTextureLoaded: false, + _disabledTextureLoaded: false, + + _className: "Button", + _normalTextureAdaptDirty: true, + _pressedTextureAdaptDirty: true, + _disabledTextureAdaptDirty: 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._capInsetsPressed = cc.rect(0, 0, 0, 0); + this._capInsetsDisabled = cc.rect(0, 0, 0, 0); + this._normalTextureSize = cc.size(0, 0); + this._pressedTextureSize = cc.size(0, 0); + this._disabledTextureSize = cc.size(0, 0); + this._titleColor = cc.Color.WHITE; + ccui.Widget.prototype.ctor.call(this); + this.setTouchEnabled(true); + this.init(normalImage, selectedImage, disableImage, texType); + }, + + /** + * Initializes a button. please do not call this function by yourself, you should pass the parameters to constructor to initialize it. + * @param {String} normalImage + * @param {String} [selectedImage=""] + * @param {String} [disableImage=""] + * @param {Number} [texType=ccui.Widget.LOCAL_TEXTURE] + * @returns {boolean} + * @override + */ + init: function (normalImage, selectedImage,disableImage, texType) { + if (ccui.Widget.prototype.init.call(this)) { + if(normalImage === undefined) + return true; + this.loadTextures(normalImage, selectedImage,disableImage, texType); + } + return false; + }, + + _initRenderer: function () { + //todo create Scale9Sprite + this._buttonNormalRenderer = new cc.Sprite(); + this._buttonClickedRenderer = new cc.Sprite(); + this._buttonDisableRenderer = new cc.Sprite(); + this._titleRenderer = new cc.LabelTTF(""); + this._titleRenderer.setAnchorPoint(0.5, 0.5); + + this.addProtectedChild(this._buttonNormalRenderer, ccui.Button.NORMAL_RENDERER_ZORDER, -1); + this.addProtectedChild(this._buttonClickedRenderer, ccui.Button.PRESSED_RENDERER_ZORDER, -1); + this.addProtectedChild(this._buttonDisableRenderer, ccui.Button.DISABLED_RENDERER_ZORDER, -1); + this.addProtectedChild(this._titleRenderer, ccui.Button.TITLE_RENDERER_ZORDER, -1); + }, + + /** + * Sets if button is using scale9 renderer. + * @param {Boolean} able true that using scale9 renderer, false otherwise. + */ + setScale9Enabled: function (able) { + //todo create Scale9Sprite + if (this._scale9Enabled === able) + return; + + this._brightStyle = ccui.Widget.BRIGHT_STYLE_NONE; + this._scale9Enabled = able; + + this.removeProtectedChild(this._buttonNormalRenderer); + this.removeProtectedChild(this._buttonClickedRenderer); + this.removeProtectedChild(this._buttonDisableRenderer); + + if (this._scale9Enabled) { + this._buttonNormalRenderer = new ccui.Scale9Sprite(); + this._buttonClickedRenderer = new ccui.Scale9Sprite(); + this._buttonDisableRenderer = new ccui.Scale9Sprite(); + } else { + this._buttonNormalRenderer = new cc.Sprite(); + this._buttonClickedRenderer = new cc.Sprite(); + this._buttonDisableRenderer = new cc.Sprite(); + } + + this._buttonClickedRenderer.setVisible(false); + this._buttonDisableRenderer.setVisible(false); + + this.loadTextureNormal(this._normalFileName, this._normalTexType); + this.loadTexturePressed(this._clickedFileName, this._pressedTexType); + this.loadTextureDisabled(this._disabledFileName, this._disabledTexType); + + this.addProtectedChild(this._buttonNormalRenderer, ccui.Button.NORMAL_RENDERER_ZORDER, -1); + this.addProtectedChild(this._buttonClickedRenderer, ccui.Button.PRESSED_RENDERER_ZORDER, -1); + this.addProtectedChild(this._buttonDisableRenderer, ccui.Button.DISABLED_RENDERER_ZORDER, -1); + if (this._scale9Enabled) { + var ignoreBefore = this._ignoreSize; + this.ignoreContentAdaptWithSize(false); + this._prevIgnoreSize = ignoreBefore; + } else { + this.ignoreContentAdaptWithSize(this._prevIgnoreSize); + } + this.setCapInsetsNormalRenderer(this._capInsetsNormal); + this.setCapInsetsPressedRenderer(this._capInsetsPressed); + this.setCapInsetsDisabledRenderer(this._capInsetsDisabled); + this.setBright(this._bright); + + this._normalTextureAdaptDirty = true; + this._pressedTextureAdaptDirty = true; + this._disabledTextureAdaptDirty = 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 && 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); + }, + + /** + * 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 self = this; + var normalRenderer = this._buttonNormalRenderer; + if(!normalRenderer._textureLoaded){ + normalRenderer.once("load", function () { + self.loadTextureNormal(self._normalFileName, self._normalTexType); + }); + } + switch (this._normalTexType){ + case ccui.Widget.LOCAL_TEXTURE: + //SetTexture cannot load resource + normalRenderer.initWithFile(normal); + break; + case ccui.Widget.PLIST_TEXTURE: + //SetTexture cannot load resource + normalRenderer.initWithSpriteFrameName(normal); + break; + default: + break; + } + + this._normalTextureLoaded = normalRenderer._textureLoaded; + + this._normalTextureSize = this._buttonNormalRenderer.getContentSize(); + this._updateChildrenDisplayedRGBA(); + if (this._unifySize){ + if (this._scale9Enabled){ + normalRenderer.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 self = this; + var clickedRenderer = this._buttonClickedRenderer; + if(!clickedRenderer._textureLoaded){ + clickedRenderer.once("load", function () { + self.loadTexturePressed(self._clickedFileName, self._pressedTexType); + }); + } + + switch (this._pressedTexType) { + case ccui.Widget.LOCAL_TEXTURE: + //SetTexture cannot load resource + clickedRenderer.initWithFile(selected); + break; + case ccui.Widget.PLIST_TEXTURE: + //SetTexture cannot load resource + clickedRenderer.initWithSpriteFrameName(selected); + break; + default: + break; + } + + if (this._scale9Enabled) + clickedRenderer.setCapInsets(this._capInsetsPressed); + + this._pressedTextureSize = this._buttonClickedRenderer.getContentSize(); + this._updateChildrenDisplayedRGBA(); + + this._pressedTextureLoaded = true; + this._pressedTextureAdaptDirty = true; + this._findLayout(); + }, + + /** + * 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 self = this; + var disabledRenderer = this._buttonDisableRenderer; + if(!disabledRenderer._textureLoaded){ + disabledRenderer.once("load", function () { + self.loadTextureDisabled(self._disabledFileName, self._disabledTexType); + }); + } + + switch (this._disabledTexType) { + case ccui.Widget.LOCAL_TEXTURE: + //SetTexture cannot load resource + disabledRenderer.initWithFile(disabled); + break; + case ccui.Widget.PLIST_TEXTURE: + //SetTexture cannot load resource + disabledRenderer.initWithSpriteFrameName(disabled); + break; + default: + break; + } + + if (this._scale9Enabled) + disabledRenderer.setCapInsets(this._capInsetsDisabled); + + this._disabledTextureSize = this._buttonDisableRenderer.getContentSize(); + this._updateChildrenDisplayedRGBA(); + + this._disabledTextureLoaded = true; + this._disabledTextureAdaptDirty = true; + this._findLayout(); + }, + + /** + * Sets capinsets for button, if button is using scale9 renderer. + * @param {cc.Rect} capInsets + */ + setCapInsets: function (capInsets) { + this.setCapInsetsNormalRenderer(capInsets); + this.setCapInsetsPressedRenderer(capInsets); + this.setCapInsetsDisabledRenderer(capInsets); + }, + + /** + * Sets capinsets for button, if button is using scale9 renderer. + * @param {cc.Rect} capInsets + */ + setCapInsetsNormalRenderer: function (capInsets) { + if(!capInsets) + 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; + + if (!this._scale9Enabled) + return; + this._buttonNormalRenderer.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) { + if(!capInsets || !this._scale9Enabled) + return; + + var x = capInsets.x, y = capInsets.y; + var width = capInsets.width, height = capInsets.height; + + if (this._pressedTextureSize.width < width) { + x = 0; + width = 0; + } + if (this._pressedTextureSize.height < height) { + y = 0; + height = 0; + } + + var locInsets = this._capInsetsPressed; + locInsets.x = x; + locInsets.y = y; + locInsets.width = width; + locInsets.height = height; + + this._buttonClickedRenderer.setCapInsets(locInsets); + }, + + /** + * Returns pressed renderer cap insets. + * @returns {cc.Rect} + */ + getCapInsetsPressedRenderer: function () { + return cc.rect(this._capInsetsPressed); + }, + + /** + * Sets capinsets for button, if button is using scale9 renderer. + * @param {cc.Rect} capInsets + */ + setCapInsetsDisabledRenderer: function (capInsets) { + if(!capInsets || !this._scale9Enabled) + return; + + var x = capInsets.x, y = capInsets.y; + var width = capInsets.width, height = capInsets.height; + + if (this._disabledTextureSize.width < width) { + x = 0; + width = 0; + } + if (this._disabledTextureSize.height < height) { + y = 0; + height = 0; + } + + var locInsets = this._capInsetsDisabled; + locInsets.x = x; + locInsets.y = y; + locInsets.width = width; + locInsets.height = height; + + this._buttonDisableRenderer.setCapInsets(locInsets); + }, + + /** + * Returns disable renderer cap insets. + * @returns {cc.Rect} + */ + getCapInsetsDisabledRenderer: function () { + return cc.rect(this._capInsetsDisabled); + }, + + _onPressStateChangedToNormal: function () { + this._buttonNormalRenderer.setVisible(true); + this._buttonClickedRenderer.setVisible(false); + this._buttonDisableRenderer.setVisible(false); + if (this._scale9Enabled) + this._buttonNormalRenderer.setState( ccui.Scale9Sprite.state.NORMAL); + if (this._pressedTextureLoaded) { + if (this.pressedActionEnabled){ + this._buttonNormalRenderer.stopAllActions(); + this._buttonClickedRenderer.stopAllActions(); + //var zoomAction = cc.scaleTo(ccui.Button.ZOOM_ACTION_TIME_STEP, this._normalTextureScaleXInSize, this._normalTextureScaleYInSize); + //fixme: the zoomAction will run in the next frame which will cause the _buttonNormalRenderer to a wrong scale + //this._buttonNormalRenderer.runAction(zoomAction); + this._buttonNormalRenderer.setScale(this._normalTextureScaleXInSize, this._normalTextureScaleYInSize); + this._buttonClickedRenderer.setScale(this._pressedTextureScaleXInSize, this._pressedTextureScaleYInSize); + + 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._buttonNormalRenderer.stopAllActions(); + this._buttonNormalRenderer.setScale(this._normalTextureScaleXInSize, this._normalTextureScaleYInSize); + + this._titleRenderer.stopAllActions(); + if (this._scale9Enabled) + this._buttonNormalRenderer.setColor(cc.Color.WHITE); + + this._titleRenderer.setScaleX(1); + this._titleRenderer.setScaleY(1); + } + }, + + _onPressStateChangedToPressed: function () { + var locNormalRenderer = this._buttonNormalRenderer; + if (this._scale9Enabled) + locNormalRenderer.setState(ccui.Scale9Sprite.state.NORMAL); + + if (this._pressedTextureLoaded) { + locNormalRenderer.setVisible(false); + this._buttonClickedRenderer.setVisible(true); + this._buttonDisableRenderer.setVisible(false); + if (this.pressedActionEnabled) { + locNormalRenderer.stopAllActions(); + this._buttonClickedRenderer.stopAllActions(); + var zoomAction = cc.scaleTo(ccui.Button.ZOOM_ACTION_TIME_STEP, this._pressedTextureScaleXInSize + this._zoomScale, + this._pressedTextureScaleYInSize + this._zoomScale); + this._buttonClickedRenderer.runAction(zoomAction); + locNormalRenderer.setScale(this._pressedTextureScaleXInSize + this._zoomScale, this._pressedTextureScaleYInSize + this._zoomScale); + + this._titleRenderer.stopAllActions(); + this._titleRenderer.runAction(cc.scaleTo(ccui.Button.ZOOM_ACTION_TIME_STEP, 1 + this._zoomScale, 1 + this._zoomScale)); + } + } else { + locNormalRenderer.setVisible(true); + this._buttonClickedRenderer.setVisible(true); + this._buttonDisableRenderer.setVisible(false); + locNormalRenderer.stopAllActions(); + locNormalRenderer.setScale(this._normalTextureScaleXInSize + this._zoomScale, this._normalTextureScaleYInSize + this._zoomScale); + + 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._scale9Enabled) + this._buttonNormalRenderer.setState(ccui.Scale9Sprite.state.GRAY); + }else{ + this._buttonNormalRenderer.setVisible(false); + this._buttonDisableRenderer.setVisible(true); + } + + this._buttonClickedRenderer.setVisible(false); + this._buttonNormalRenderer.setScale(this._normalTextureScaleXInSize, this._normalTextureScaleYInSize); + this._buttonClickedRenderer.setScale(this._pressedTextureScaleXInSize, this._pressedTextureScaleYInSize); + }, + + _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); + this._updateTitleLocation(); + this._normalTextureAdaptDirty = true; + this._pressedTextureAdaptDirty = true; + this._disabledTextureAdaptDirty = true; + }, + + /** + * Gets the Virtual Renderer of widget. + * @returns {cc.Node} + */ + getVirtualRenderer: function () { + if (this._bright) { + switch (this._brightStyle) { + case ccui.Widget.BRIGHT_STYLE_NORMAL: + return this._buttonNormalRenderer; + case ccui.Widget.BRIGHT_STYLE_HIGH_LIGHT: + return this._buttonClickedRenderer; + default: + return null; + } + } else + return this._buttonDisableRenderer; + }, + + _normalTextureScaleChangedWithSize: function () { + if(this._ignoreSize && !this._unifySize){ + if(!this._scale9Enabled){ + this._buttonNormalRenderer.setScale(1); + this._normalTextureScaleXInSize = this._normalTextureScaleYInSize = 1; + } + }else{ + if (this._scale9Enabled){ + this._buttonNormalRenderer.setPreferredSize(this._contentSize); + this._normalTextureScaleXInSize = this._normalTextureScaleYInSize = 1; + this._buttonNormalRenderer.setScale(this._normalTextureScaleXInSize, this._normalTextureScaleYInSize); + }else{ + var textureSize = this._normalTextureSize; + if (textureSize.width <= 0 || textureSize.height <= 0) + { + this._buttonNormalRenderer.setScale(1); + return; + } + var scaleX = this._contentSize.width / textureSize.width; + var scaleY = this._contentSize.height / textureSize.height; + this._buttonNormalRenderer.setScaleX(scaleX); + this._buttonNormalRenderer.setScaleY(scaleY); + this._normalTextureScaleXInSize = scaleX; + this._normalTextureScaleYInSize = scaleY; + } + } + this._buttonNormalRenderer.setPosition(this._contentSize.width / 2, this._contentSize.height / 2); + }, + + _pressedTextureScaleChangedWithSize: function () { + if (this._ignoreSize && !this._unifySize) { + if (!this._scale9Enabled) { + this._buttonClickedRenderer.setScale(1); + this._pressedTextureScaleXInSize = this._pressedTextureScaleYInSize = 1; + } + } else { + if (this._scale9Enabled) { + this._buttonClickedRenderer.setPreferredSize(this._contentSize); + this._pressedTextureScaleXInSize = this._pressedTextureScaleYInSize = 1; + this._buttonClickedRenderer.setScale(this._pressedTextureScaleXInSize, this._pressedTextureScaleYInSize); + } else { + var textureSize = this._pressedTextureSize; + if (textureSize.width <= 0 || textureSize.height <= 0) { + this._buttonClickedRenderer.setScale(1); + return; + } + var scaleX = this._contentSize.width / textureSize.width; + var scaleY = this._contentSize.height / textureSize.height; + this._buttonClickedRenderer.setScaleX(scaleX); + this._buttonClickedRenderer.setScaleY(scaleY); + this._pressedTextureScaleXInSize = scaleX; + this._pressedTextureScaleYInSize = scaleY; + } + } + this._buttonClickedRenderer.setPosition(this._contentSize.width / 2, this._contentSize.height / 2); + }, + + _disabledTextureScaleChangedWithSize: function () { + if(this._ignoreSize && !this._unifySize){ + if (this._scale9Enabled) + this._buttonDisableRenderer.setScale(1); + }else { + if (this._scale9Enabled){ + this._buttonDisableRenderer.setScale(1); + this._buttonDisableRenderer.setPreferredSize(this._contentSize); + }else{ + var textureSize = this._disabledTextureSize; + if (textureSize.width <= 0 || textureSize.height <= 0) { + this._buttonDisableRenderer.setScale(1); + return; + } + var scaleX = this._contentSize.width / textureSize.width; + var scaleY = this._contentSize.height / textureSize.height; + this._buttonDisableRenderer.setScaleX(scaleX); + this._buttonDisableRenderer.setScaleY(scaleY); + } + } + this._buttonDisableRenderer.setPosition(this._contentSize.width / 2, this._contentSize.height / 2); + }, + + _adaptRenderers: function(){ + if (this._normalTextureAdaptDirty) { + this._normalTextureScaleChangedWithSize(); + this._normalTextureAdaptDirty = false; + } + if (this._pressedTextureAdaptDirty) { + this._pressedTextureScaleChangedWithSize(); + this._pressedTextureAdaptDirty = false; + } + if (this._disabledTextureAdaptDirty) { + this._disabledTextureScaleChangedWithSize(); + this._disabledTextureAdaptDirty = 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._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 () { + return this._titleRenderer.getString(); + }, + + /** + * Sets title color to ccui.Button. + * @param {cc.Color} color + */ + setTitleColor: function (color) { + this._titleRenderer.setFontFillColor(color); + }, + + /** + * Returns title color of ccui.Button + * @returns {cc.Color} + */ + getTitleColor: function () { + return this._titleRenderer._getFillStyle(); + }, + + /** + * Sets title fontSize to ccui.Button + * @param {cc.Size} size + */ + setTitleFontSize: function (size) { + this._titleRenderer.setFontSize(size); + this._fontSize = size; + }, + + /** + * Returns title fontSize of ccui.Button. + * @returns {Number} + */ + getTitleFontSize: function () { + return this._titleRenderer.getFontSize(); + }, + + /** + * 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._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 () { + return this._titleRenderer.getFontName(); + }, + + _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.setScale9Enabled(uiButton._scale9Enabled); + this.loadTextureNormal(uiButton._normalFileName, uiButton._normalTexType); + this.loadTexturePressed(uiButton._clickedFileName, uiButton._pressedTexType); + this.loadTextureDisabled(uiButton._disabledFileName, uiButton._disabledTexType); + this.setCapInsetsNormalRenderer(uiButton._capInsetsNormal); + this.setCapInsetsPressedRenderer(uiButton._capInsetsPressed); + this.setCapInsetsDisabledRenderer(uiButton._capInsetsDisabled); + 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; + if (this._buttonNormalRenderer !== null) + imageSize = this._buttonNormalRenderer.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/extensions/ccui/uiwidgets/UICheckBox.js b/extensions/ccui/uiwidgets/UICheckBox.js new file mode 100644 index 00000000000..d5240daaff3 --- /dev/null +++ b/extensions/ccui/uiwidgets/UICheckBox.js @@ -0,0 +1,740 @@ +/**************************************************************************** + 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} 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} + */ + 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.once("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 + +/** + * Enum for loadingBar Type + * @readonly + * @enum {number} + */ +ccui.LoadingBar.Type = cc.Enum({ + /** + * The left direction of ccui.LoadingBar. + */ + LEFT: 0, + /** + * The right direction of ccui.LoadingBar. + */ + RIGHT: 1 +}); + +/** + * The zOrder value of ccui.LoadingBar's renderer. + * @constant + * @type {number} + */ +ccui.LoadingBar.RENDERER_ZORDER = -1; \ No newline at end of file diff --git a/extensions/ccui/uiwidgets/UIRichText.js b/extensions/ccui/uiwidgets/UIRichText.js new file mode 100644 index 00000000000..fd3d57853f9 --- /dev/null +++ b/extensions/ccui/uiwidgets/UIRichText.js @@ -0,0 +1,742 @@ +/**************************************************************************** + 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 () { + this._type = 0; + this._tag = 0; + this._color = cc.color(255, 255, 255, 255); + }, + + /** + * Initializes a richElement. + * @param {Number} tag + * @param {cc.Color} color + * @param {Number} opacity + */ + init: function (tag, color, opacity) { + this._tag = tag; + this._color.r = color.r; + this._color.g = color.g; + this._color.b = color.b; + this._opacity = opacity; + 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) { + ccui.RichElement.prototype.ctor.call(this); + this._type = ccui.RichElement.TEXT; + this._text = ""; + this._fontName = ""; + this._fontSize = 0; + + if( colorOrFontDef && colorOrFontDef instanceof cc.FontDefinition) + this.initWithStringAndTextDefinition(tag, text, colorOrFontDef, opacity); + else + fontSize && this.init(tag, colorOrFontDef, opacity, text, fontName, fontSize); + }, + + /** + * Initializes a ccui.RichElementText. + * @param {Number} tag + * @param {cc.Color} color + * @param {Number} opacity + * @param {String} text + * @param {String} fontName + * @param {Number} fontSize + * @override + */ + init: function (tag, color, opacity, text, fontName, fontSize) { + ccui.RichElement.prototype.init.call(this, tag, color, opacity); + this._text = text; + this._fontName = fontName; + this._fontSize = fontSize; + }, + initWithStringAndTextDefinition: function(tag, text, fontDef, opacity){ + + ccui.RichElement.prototype.init.call(this, tag, fontDef.fillStyle, opacity); + this._fontDefinition = fontDef; + this._text = text; + this._fontName = fontDef.fontName; + this._fontSize = fontDef.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); + this._type = ccui.RichElement.IMAGE; + this._filePath = ""; + this._textureRect = cc.rect(0, 0, 0, 0); + this._textureType = 0; + + filePath !== undefined && this.init(tag, color, opacity, filePath); + }, + + /** + * Initializes a ccui.RichElementImage + * @param {Number} tag + * @param {cc.Color} color + * @param {Number} opacity + * @param {String} filePath + * @override + */ + init: function (tag, color, opacity, filePath) { + ccui.RichElement.prototype.init.call(this, tag, color, opacity); + this._filePath = filePath; + } +}); + +/** + * 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); + this._type = ccui.RichElement.CUSTOM; + this._customNode = null; + + customNode !== undefined && this.init(tag, color, opacity, customNode); + }, + + /** + * Initializes a ccui.RichElementCustomNode + * @param {Number} tag + * @param {cc.Color} color + * @param {Number} opacity + * @param {cc.Node} customNode + * @override + */ + init: function (tag, color, opacity, customNode) { + ccui.RichElement.prototype.init.call(this, tag, color, opacity); + this._customNode = customNode; + } +}); + +/** + * 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.TextAlignment.LEFT; + this._textVerticalAlignment = cc.VerticalTextAlignment.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.js.isNumber(element)) + this._richElements.splice(element, 1); + else + cc.js.array.remove(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.TextAlignment.LEFT) { + offsetX = 0; + if (this._textHorizontalAlignment === cc.TextAlignment.RIGHT) + offsetX = this._contentSize.width - nextPosX; + else if (this._textHorizontalAlignment === cc.TextAlignment.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.TextAlignment.LEFT || this._textVerticalAlignment !== cc.VerticalTextAlignment.TOP) { + offsetX = 0; + if (this._textHorizontalAlignment === cc.TextAlignment.RIGHT) + offsetX = this._contentSize.width - nextPosX; + else if (this._textHorizontalAlignment === cc.TextAlignment.CENTER) + offsetX = (this._contentSize.width - nextPosX) / 2; + + var offsetY = 0; + if (this._textVerticalAlignment === cc.VerticalTextAlignment.BOTTOM) + offsetY = this._customSize.height - newContentSizeHeight; + else if (this._textVerticalAlignment === cc.VerticalTextAlignment.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 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.Vec2} 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); +// if (ballRect.containsPoint(nsp)) { + 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.Vec2} 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); + }, + + _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); + } + }, + + _onPressStateChangedToDisabled: function () { + if (this._slidBallDisabledTextureFile){ + this._slidBallNormalRenderer.setVisible(false); + this._slidBallDisabledRenderer.setVisible(true); + } + this._slidBallNormalRenderer.setScale(this._sliderBallNormalTextureScaleX, this._sliderBallNormalTextureScaleY); + this._slidBallPressedRenderer.setVisible(false); + }, + + setZoomScale: function(scale){ + this._zoomScale = scale; + }, + + getZoomScale: function(){ + return this._zoomScale; + }, + + /** + * 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; \ No newline at end of file diff --git a/extensions/ccui/uiwidgets/UIText.js b/extensions/ccui/uiwidgets/UIText.js new file mode 100644 index 00000000000..a6033364c01 --- /dev/null +++ b/extensions/ccui/uiwidgets/UIText.js @@ -0,0 +1,515 @@ +/**************************************************************************** + 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 {cc.TextAlignment} textAlign - Horizontal Alignment of label + * @property {cc.VerticalTextAlignment} verticalAlign - Vertical Alignment of label + * @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: "Thonburi", + _fontSize: 10, + _onSelectedScaleOffset:0.5, + _labelRenderer: "", + _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); + + fontSize !== undefined && this.init(textContent, fontName, fontSize); + + }, + + /** + * Initializes a ccui.Text. Please do not call this function by yourself, you should pass the parameters to constructor to initialize it. + * @param {String} textContent + * @param {String} fontName + * @param {Number} fontSize + * @returns {boolean} + * @override + */ + init: function (textContent, fontName, fontSize) { + if (ccui.Widget.prototype.init.call(this)) { + if(arguments.length > 0){ + this.setFontName(fontName); + this.setFontSize(fontSize); + this.setString(textContent); + }else{ + this.setFontName(this._fontName); + } + return true; + } + return false; + }, + + _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._labelRenderer.setString(text); + this._updateContentSizeWithTextureSize(this._labelRenderer.getContentSize()); + 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._labelRenderer.setFontSize(size); + this._fontSize = size; + this._updateContentSizeWithTextureSize(this._labelRenderer.getContentSize()); + 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._fontName = name; + this._labelRenderer.setFontName(name); + this._updateContentSizeWithTextureSize(this._labelRenderer.getContentSize()); + this._labelRendererAdaptDirty = true; + }, + + /** + * 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._labelRenderer.setDimensions(size); + if (!this._ignoreSize){ + this._customSize = size; + } + this._updateContentSizeWithTextureSize(this._labelRenderer.getContentSize()); + this._labelRendererAdaptDirty = true; + }, + + /** + * Returns renderer's dimension. + * @returns {cc.Size} + */ + getTextAreaSize: function(){ + return this._labelRenderer.getDimensions(); + }, + + /** + * Sets Horizontal Alignment of cc.LabelTTF + * @param {cc.TextAlignment} alignment Horizontal Alignment + */ + setTextHorizontalAlignment: function (alignment) { + this._labelRenderer.setHorizontalAlignment(alignment); + this._updateContentSizeWithTextureSize(this._labelRenderer.getContentSize()); + this._labelRendererAdaptDirty = true; + }, + + /** + * Returns Horizontal Alignment of label + * @returns {cc.TextAlignment} + */ + getTextHorizontalAlignment: function () { + return this._labelRenderer.getHorizontalAlignment(); + }, + + /** + * Sets Vertical Alignment of label + * @param {cc.VerticalTextAlignment} alignment + */ + setTextVerticalAlignment: function (alignment) { + this._labelRenderer.setVerticalAlignment(alignment); + this._updateContentSizeWithTextureSize(this._labelRenderer.getContentSize()); + this._labelRendererAdaptDirty = true; + }, + + /** + * Gets text vertical alignment. + * @returns {cc.VerticalTextAlignment} + */ + 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.setDimensions(0,0); + 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 +}; \ No newline at end of file diff --git a/extensions/ccui/uiwidgets/UITextAtlas.js b/extensions/ccui/uiwidgets/UITextAtlas.js new file mode 100644 index 00000000000..bb7e0d27caa --- /dev/null +++ b/extensions/ccui/uiwidgets/UITextAtlas.js @@ -0,0 +1,229 @@ +/**************************************************************************** + 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); + 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); + }, + + /** + * 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; \ No newline at end of file diff --git a/extensions/ccui/uiwidgets/UITextBMFont.js b/extensions/ccui/uiwidgets/UITextBMFont.js new file mode 100644 index 00000000000..09d965a18ed --- /dev/null +++ b/extensions/ccui/uiwidgets/UITextBMFont.js @@ -0,0 +1,216 @@ +/**************************************************************************** + 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); + + 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.once("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) { + 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/extensions/ccui/uiwidgets/UITextField.js b/extensions/ccui/uiwidgets/UITextField.js new file mode 100644 index 00000000000..e50884f6f65 --- /dev/null +++ b/extensions/ccui/uiwidgets/UITextField.js @@ -0,0 +1,915 @@ +/**************************************************************************** + 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); + if (fontName) + this.setFontName(fontName); + if (fontSize) + this.setFontSize(fontSize); + if (placeholder) + this.setPlaceHolder(placeholder); + }, + + /** + * Initializes a ccui.TextField. Please do not call this function by yourself, you should pass the parameters to constructor to initialize it. + * @returns {boolean} + * @override + */ + init: function(){ + if(ccui.Widget.prototype.init.call(this)){ + this.setTouchEnabled(true); + return true; + } + return false; + }, + + /** + * 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.Vec2} 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.Vec2} 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/extensions/ccui/uiwidgets/UIVideoPlayer.js b/extensions/ccui/uiwidgets/UIVideoPlayer.js new file mode 100644 index 00000000000..3ba773d6aac --- /dev/null +++ b/extensions/ccui/uiwidgets/UIVideoPlayer.js @@ -0,0 +1,543 @@ +/**************************************************************************** + 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); + }, + + /** + * 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 -1) + this._items.splice(index, 1); + 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 = []; + }, + + /** + * Push back custom item into ccui.ListView. + * @param {ccui.Widget} item + * @param {Number} index + */ + insertCustomItem: function (item, index) { + this._items.splice(index, 0, item); + 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; + }, + + /** + * 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} dir + */ + setDirection: function (dir) { + switch (dir) { + case ccui.ScrollView.Dir.VERTICAL: + this.setLayoutType(ccui.Layout.Type.LINEAR_VERTICAL); + break; + case ccui.ScrollView.Dir.HORIZONTAL: + this.setLayoutType(ccui.Layout.Type.LINEAR_HORIZONTAL); + break; + case ccui.ScrollView.Dir.BOTH: + return; + default: + return; + break; + } + ccui.ScrollView.prototype.setDirection.call(this, dir); + }, + + /** + * Requests refresh list view. + */ + requestRefreshView: function () { + this._refreshViewDirty = true; + }, + + /** + * Refreshes list view. + */ + refreshView: function () { + var locItems = this._items; + for (var i = 0; i < locItems.length; i++) { + var item = locItems[i]; + item.setLocalZOrder(i); + this._remedyLayoutParameter(item); + } + this._updateInnerContainerSize(); + }, + + /** + * provides a public _doLayout function for Editor. it calls _doLayout. + */ + doLayout: function(){ + this._doLayout(); + }, + + _doLayout: function(){ + ccui.Layout.prototype._doLayout.call(this); + if (this._refreshViewDirty) { + this.refreshView(); + 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.addEventListener(selector, target); + }, + + /** + * Adds event listener to ccui.ListView. + * @param {Function} selector + * @param {Object} [target=] + */ + addEventListener: function(selector, target){ + this._listViewEventListener = target; + this._listViewEventSelector = 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._ccEventCallback) + this._ccEventCallback(this, eventEnum); + }, + + /** + * Intercept touch event, handle its child's touch event. + * @param {cc.Event} event + */ + interceptTouchEvent: function (event) { + var eventType = event._widgetEventType, + touch = event.currentTouch, + parent; + ccui.ScrollView.prototype.interceptTouchEvent.call(this, event); + if (!this._touchEnabled) + return; + + if (eventType !== ccui.Widget.TOUCH_MOVED) { + parent = event.target; + while (parent) { + if (parent && parent.getParent() === this._innerContainer) { + this._curSelectedIndex = this.getIndex(parent); + break; + } + parent = parent.getParent(); + } + if (event.target.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; + } + }, + + //v3.3 + forceDoLayout: function(){ + if (this._refreshViewDirty) { + this.refreshView(); + this._refreshViewDirty = false; + } + this._innerContainer.forceDoLayout(); + } +}); + +/** + * 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; \ No newline at end of file diff --git a/extensions/ccui/uiwidgets/scroll-widget/UIPageView.js b/extensions/ccui/uiwidgets/scroll-widget/UIPageView.js new file mode 100644 index 00000000000..de376e8219c --- /dev/null +++ b/extensions/ccui/uiwidgets/scroll-widget/UIPageView.js @@ -0,0 +1,663 @@ +/**************************************************************************** + 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.Layout + * @exmaple + * var pageView = new ccui.PageView(); + * pageView.setTouchEnabled(true); + * pageView.addPage(new ccui.Layout()); + * this.addChild(pageView); + */ +ccui.PageView = ccui.Layout.extend(/** @lends ccui.PageView# */{ + _curPageIdx: 0, + _pages: null, + _touchMoveDirection: null, + _touchStartLocation: 0, + _touchMoveStartLocation: 0, + _movePagePoint: null, + _leftBoundaryChild: null, + _rightBoundaryChild: null, + _leftBoundary: 0, + _rightBoundary: 0, + + _isAutoScrolling: false, + _autoScrollDistance: 0, + _autoScrollSpeed: 0, + _autoScrollDirection: 0, + + _childFocusCancelOffset: 0, + _pageViewEventListener: null, + _pageViewEventSelector: null, + _className:"PageView", + //v3.2 + _customScrollThreshold: 0, + _usingCustomScrollThreshold: false, + + /** + * 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.Layout.prototype.ctor.call(this); + this._pages = []; + this._touchMoveDirection = ccui.PageView.TOUCH_DIR_LEFT; + + this._movePagePoint = null; + this._leftBoundaryChild = null; + this._rightBoundaryChild = null; + + this._childFocusCancelOffset = 5; + this._pageViewEventListener = null; + this._pageViewEventSelector = null; + this.setTouchEnabled(true); + + // For propagation + this.on(cc.Event.TOUCH, this.interceptTouchEvent); + }, + + /** + * Initializes a ccui.PageView. Please do not call this function by yourself, you should pass the parameters to constructor to initialize it. + * @returns {boolean} + */ + init: function () { + if (ccui.Layout.prototype.init.call(this)) { + this.setClippingEnabled(true); + return true; + } + return false; + }, + + /** + * Calls the parent class' onEnter and schedules update function. + * @override + */ + onEnter:function(){ + ccui.Layout.prototype.onEnter.call(this); + this.scheduleUpdate(true); + }, + + /** + * Add a widget to a page of PageView. + * @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) { + if (!widget || pageIdx < 0) + return; + + var pageCount = this._getPageCount(); + if (pageIdx < 0 || pageIdx >= pageCount) { + if (forceCreate) { + if (pageIdx > pageCount) + cc.log("pageIdx is %d, it will be added as page id [%d]", pageIdx, pageCount); + var newPage = this._createPage(); + newPage.addChild(widget); + this.addPage(newPage); + } + } else { + var page = this._pages[pageIdx]; + if (page) + page.addChild(widget); + } + }, + + _createPage: function () { + var newPage = new ccui.Layout(); + newPage.setContentSize(this.getContentSize()); + return newPage; + }, + + /** + * Adds a page to ccui.PageView. + * @param {ccui.Layout} page + */ + addPage: function (page) { + if (!page || this._pages.indexOf(page) !== -1) + return; + + this.addChild(page); + this._pages.push(page); + this._doLayoutDirty = true; + }, + + /** + * Inserts a page in the specified location. + * @param {ccui.Layout} page page to be added to PageView. + * @param {Number} idx index + */ + insertPage: function (page, idx) { + if (idx < 0 || !page || this._pages.indexOf(page) !== -1) + return; + + var pageCount = this._getPageCount(); + if (idx >= pageCount) + this.addPage(page); + else { + this._pages[idx] = page; + this.addChild(page); + } + this._doLayoutDirty = true; + }, + + /** + * Removes a page from PageView. + * @param {ccui.Layout} page + */ + removePage: function (page) { + if (!page) + return; + this.removeChild(page); + var index = this._pages.indexOf(page); + if(index > -1) + this._pages.splice(index, 1); + this._doLayoutDirty = true; + }, + + /** + * Removes a page at index of PageView. + * @param {number} index + */ + removePageAtIndex: function (index) { + if (index < 0 || index >= this._pages.length) + return; + var page = this._pages[index]; + if (page) + this.removePage(page); + }, + + /** + * Removes all pages from PageView + */ + removeAllPages: function(){ + var locPages = this._pages; + for(var i = 0, len = locPages.length; i < len; i++) + this.removeChild(locPages[i]); + this._pages.length = 0; + }, + + _updateBoundaryPages: function () { + var locPages = this._pages; + if (locPages.length <= 0) { + this._leftBoundaryChild = null; + this._rightBoundaryChild = null; + return; + } + this._leftBoundaryChild = locPages[0]; + this._rightBoundaryChild = locPages[locPages.length - 1]; + }, + + _getPageCount: function(){ + return this._pages.length; + }, + + /** + * Get x position by index + * @param {number} idx + * @returns {number} + */ + _getPositionXByIndex: function (idx) { + return (this.getContentSize().width * (idx - this._curPageIdx)); + }, + + _onSizeChanged: function () { + ccui.Layout.prototype._onSizeChanged.call(this); + this._rightBoundary = this.getContentSize().width; + this._doLayoutDirty = true; + }, + + _updateAllPagesSize: function(){ + var selfSize = this.getContentSize(); + var locPages = this._pages; + for (var i = 0, len = locPages.length; i < len; i++) + locPages[i].setContentSize(selfSize); + }, + + _updateAllPagesPosition: function(){ + var pageCount = this._getPageCount(); + if (pageCount <= 0) { + this._curPageIdx = 0; + return; + } + + if (this._curPageIdx >= pageCount) + this._curPageIdx = pageCount-1; + + var pageWidth = this.getContentSize().width; + var locPages = this._pages; + for (var i=0; i< pageCount; i++) + locPages[i].setPosition(cc.p((i - this._curPageIdx) * pageWidth, 0)); + }, + + /** + * scroll PageView to index. + * @param {number} idx index of page. + */ + scrollToPage: function (idx) { + if (idx < 0 || idx >= this._pages.length) + return; + this._curPageIdx = idx; + var curPage = this._pages[idx]; + this._autoScrollDistance = -(curPage.getPosition().x); + this._autoScrollSpeed = Math.abs(this._autoScrollDistance) / 0.2; + this._autoScrollDirection = this._autoScrollDistance > 0 ? ccui.PageView.DIRECTION_RIGHT : ccui.PageView.DIRECTION_LEFT; + this._isAutoScrolling = true; + }, + + /** + * Called once per frame. Time is the number of seconds of a frame interval. + * @override + * @param {Number} dt + */ + update: function (dt) { + if (this._isAutoScrolling) + this._autoScroll(dt); + }, + + /** + * Does nothing. ccui.PageView's layout type is ccui.Layout.Type.ABSOLUTE. + * @override + * @param {Number} type + */ + setLayoutType:function(type){ + }, + + /** + * Returns the layout type of ccui.PageView. it's always ccui.Layout.Type.ABSOLUTE. + * @returns {number} + */ + getLayoutType: function(){ + return ccui.Layout.Type.ABSOLUTE; + }, + + _autoScroll: function(dt){ + var step; + switch (this._autoScrollDirection) { + case ccui.PageView.DIRECTION_LEFT: + step = this._autoScrollSpeed * dt; + if (this._autoScrollDistance + step >= 0.0) { + step = -this._autoScrollDistance; + this._autoScrollDistance = 0.0; + this._isAutoScrolling = false; + } else + this._autoScrollDistance += step; + this._scrollPages(-step); + if(!this._isAutoScrolling) + this._pageTurningEvent(); + break; + break; + case ccui.PageView.DIRECTION_RIGHT: + step = this._autoScrollSpeed * dt; + if (this._autoScrollDistance - step <= 0.0) { + step = this._autoScrollDistance; + this._autoScrollDistance = 0.0; + this._isAutoScrolling = false; + } else + this._autoScrollDistance -= step; + this._scrollPages(step); + if(!this._isAutoScrolling) + this._pageTurningEvent(); + break; + default: + break; + } + }, + + /** + * The touch moved event callback handler of ccui.PageView. + * @override + * @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.PageView. + * @override + * @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 handler of ccui.PageView. + * @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; + }, + + _doLayout: function(){ + if (!this._doLayoutDirty) + return; + + this._updateAllPagesPosition(); + this._updateAllPagesSize(); + this._updateBoundaryPages(); + this._doLayoutDirty = false; + }, + + _movePages: function (offset) { + var arrayPages = this._pages; + var length = arrayPages.length; + for (var i = 0; i < length; i++) { + var child = arrayPages[i]; + //var pos = child.getPosition(); + //child.setPosition(pos.x + offset, pos.y); + child.setPositionX(child.getPositionX() + offset); + } + }, + + _scrollPages: function (touchOffset) { + if (this._pages.length <= 0) + return false; + if (!this._leftBoundaryChild || !this._rightBoundaryChild) + return false; + + var realOffset = touchOffset; + switch (this._touchMoveDirection) { + case ccui.PageView.TOUCH_DIR_LEFT: // left + var rightBoundary = this._rightBoundaryChild.getRightBoundary(); + if (rightBoundary + touchOffset <= this._rightBoundary) { + realOffset = this._rightBoundary - rightBoundary; + this._movePages(realOffset); + return false; + } + break; + case ccui.PageView.TOUCH_DIR_RIGHT: // right + var leftBoundary = this._leftBoundaryChild.getLeftBoundary(); + if (leftBoundary + touchOffset >= this._leftBoundary) { + realOffset = this._leftBoundary - leftBoundary; + this._movePages(realOffset); + return false; + } + break; + default: + break; + } + + this._movePages(realOffset); + return true; + }, + + _handleMoveLogic: function (touch) { + var offset = touch.getLocation().x - touch.getPreviousLocation().x; + if (offset < 0) + this._touchMoveDirection = ccui.PageView.TOUCH_DIR_LEFT; + else if (offset > 0) + this._touchMoveDirection = ccui.PageView.TOUCH_DIR_RIGHT; + this._scrollPages(offset); + }, + + /** + * 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 + */ + setCustomScrollThreshold: function(threshold){ + cc.assert(threshold>0, "Invalid threshold!"); + this._customScrollThreshold = threshold; + this.setUsingCustomScrollThreshold(true); + }, + + /** + * Returns user defined scroll page threshold. + * @since v3.2 + */ + getCustomScrollThreshold: function(){ + return this._customScrollThreshold; + }, + + /** + * 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 + */ + setUsingCustomScrollThreshold: function(flag){ + this._usingCustomScrollThreshold = flag; + }, + + /** + * Queries whether we are using user defined scroll page threshold or not + */ + isUsingCustomScrollThreshold: function(){ + return this._usingCustomScrollThreshold; + }, + + _handleReleaseLogic: function (touchPoint) { + if (this._pages.length <= 0) + return; + var curPage = this._pages[this._curPageIdx]; + if (curPage) { + var curPagePos = curPage.getPosition(); + var pageCount = this._pages.length; + var curPageLocation = curPagePos.x; + var pageWidth = this.getSize().width; + if (!this._usingCustomScrollThreshold) + this._customScrollThreshold = pageWidth / 2.0; + var boundary = this._customScrollThreshold; + if (curPageLocation <= -boundary) { + if (this._curPageIdx >= pageCount - 1) + this._scrollPages(-curPageLocation); + else + this.scrollToPage(this._curPageIdx + 1); + } else if (curPageLocation >= boundary) { + if (this._curPageIdx <= 0) + this._scrollPages(-curPageLocation); + else + this.scrollToPage(this._curPageIdx - 1); + } else + this.scrollToPage(this._curPageIdx); + } + }, + + /** + * Intercept touch event, handle its child's touch event. + * @param {cc.Event} event + */ + interceptTouchEvent: function (event) { + var eventType = event._widgetEventType, + touch = event.currentTouch, + touchPoint, offset = 0; + if (!this._touchEnabled) + return; + + touchPoint = touch.getLocation(); + switch (eventType) { + case ccui.Widget.TOUCH_BEGAN: + this._touchBeganPosition.x = touchPoint.x; + this._touchBeganPosition.y = touchPoint.y; + this._isInterceptTouch = true; + break; + case ccui.Widget.TOUCH_MOVED: + this._touchMovePosition.x = touchPoint.x; + this._touchMovePosition.y = touchPoint.y; + offset = Math.abs(event.target.getTouchBeganPosition().x - touchPoint.x); + if (offset > this._childFocusCancelOffset) { + event.target.setHighlighted(false); + this._handleMoveLogic(touch); + } + break; + case ccui.Widget.TOUCH_ENDED: + case ccui.Widget.TOUCH_CANCELED: + this._touchEndPosition.x = touchPoint.x; + this._touchEndPosition.y = touchPoint.y; + this._handleReleaseLogic(touch); + if (event.target.isSwallowTouches()) + this._isInterceptTouch = false; + break; + } + }, + + _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.addEventListener(selector, target); + }, + + /** + * Adds event listener to ccui.PageView. + * @param {Function} selector + * @param {Object} [target=] + */ + addEventListener: function(selector, target){ + this._pageViewEventSelector = selector; + this._pageViewEventListener = target; + }, + + /** + * Returns current page index + * @returns {number} + */ + getCurPageIndex: function () { + return this._curPageIdx; + }, + + /** + * Returns all pages of PageView + * @returns {Array} + */ + getPages:function(){ + return this._pages; + }, + + /** + * Returns a page from PageView by index + * @param {Number} index + * @returns {ccui.Layout} + */ + getPage: function(index){ + if (index < 0 || index >= this._pages.length) + return null; + return this._pages[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.Layout.prototype._copySpecialProperties.call(this, pageView); + this._ccEventCallback = pageView._ccEventCallback; + this._pageViewEventListener = pageView._pageViewEventListener; + this._pageViewEventSelector = pageView._pageViewEventSelector; + this._usingCustomScrollThreshold = pageView._usingCustomScrollThreshold; + this._customScrollThreshold = pageView._customScrollThreshold; + } +}); +/** + * 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/extensions/ccui/uiwidgets/scroll-widget/UIScrollView.js b/extensions/ccui/uiwidgets/scroll-widget/UIScrollView.js new file mode 100644 index 00000000000..46c9356f200 --- /dev/null +++ b/extensions/ccui/uiwidgets/scroll-widget/UIScrollView.js @@ -0,0 +1,1921 @@ +/**************************************************************************** + 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} direction - Scroll direction of the scroll view + * @property {Boolean} bounceEnabled - Indicate whether bounce is enabled + * @property {Boolean} inertiaScrollEnabled - Indicate whether inertiaScroll is enabled + */ +ccui.ScrollView = ccui.Layout.extend(/** @lends ccui.ScrollView# */{ + _innerContainer: null, + direction: null, + _autoScrollDir: null, + + _topBoundary: 0, + _bottomBoundary: 0, + _leftBoundary: 0, + _rightBoundary: 0, + + _bounceTopBoundary: 0, + _bounceBottomBoundary: 0, + _bounceLeftBoundary: 0, + _bounceRightBoundary: 0, + + _autoScroll: false, + _autoScrollAddUpTime: 0, + + _autoScrollOriginalSpeed: 0, + _autoScrollAcceleration: 0, + _isAutoScrollSpeedAttenuated: false, + _needCheckAutoScrollDestination: false, + _autoScrollDestination: null, + + _bePressed: false, + _slidTime: 0, + _moveChildPoint: null, + _childFocusCancelOffset: 0, + + _leftBounceNeeded: false, + _topBounceNeeded: false, + _rightBounceNeeded: false, + _bottomBounceNeeded: false, + + bounceEnabled: false, + _bouncing: false, + _bounceDir: null, + _bounceOriginalSpeed: 0, + inertiaScrollEnabled: false, + + _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.direction = ccui.ScrollView.Dir.NONE; + this._autoScrollDir = cc.p(0, 0); + + this._autoScrollAcceleration = -1000; + this._autoScrollDestination = cc.p(0, 0); + this._slidTime = 0; + this._moveChildPoint = cc.p(0, 0); + this._childFocusCancelOffset = 5; + this._bounceDir = cc.p(0, 0); + this._bounceOriginalSpeed = 0; + this.inertiaScrollEnabled = true; + this.setTouchEnabled(true); + + // For propagation + this.on(cc.Event.TOUCH, this.interceptTouchEvent); + }, + + /** + * Initializes a ccui.ScrollView. Please do not call this function by yourself, you should pass the parameters to constructor to initialize it. + * @returns {boolean} + */ + init: function () { + if (ccui.Layout.prototype.init.call(this)) { + this.setClippingEnabled(true); + this._innerContainer.setTouchEnabled(false); + return true; + } + return false; + }, + + /** + * Calls the parent class' onEnter and schedules update function. + * @override + */ + onEnter: function () { + ccui.Layout.prototype.onEnter.call(this); + this.scheduleUpdate(true); + }, + + /** + * 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.Type.LINEAR_VERTICAL + || this.getLayoutType() === ccui.Layout.Type.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 bounceBoundaryParameterX = locSize.width / 3; + var bounceBoundaryParameterY = locSize.height / 3; + this._bounceTopBoundary = locSize.height - bounceBoundaryParameterY; + this._bounceBottomBoundary = bounceBoundaryParameterY; + this._bounceLeftBoundary = bounceBoundaryParameterX; + this._bounceRightBoundary = locSize.width - bounceBoundaryParameterX; + 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); + }, + + /** + * 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; + var locSize = this._contentSize; + var innerSizeWidth = locSize.width, innerSizeHeight = locSize.height; + var originalInnerSize = innerContainer.getContentSize(); + 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 newInnerSize, offset; + switch (this.direction) { + case ccui.ScrollView.Dir.VERTICAL: + newInnerSize = innerContainer.getContentSize(); + offset = originalInnerSize.height - newInnerSize.height; + this._scrollChildren(0, offset); + break; + case ccui.ScrollView.Dir.HORIZONTAL: + if (innerContainer.getRightBoundary() <= locSize.width) { + newInnerSize = innerContainer.getContentSize(); + offset = originalInnerSize.width - newInnerSize.width; + this._scrollChildren(offset, 0); + } + break; + case ccui.ScrollView.Dir.BOTH: + newInnerSize = innerContainer.getContentSize(); + var offsetY = originalInnerSize.height - newInnerSize.height; + var offsetX = (innerContainer.getRightBoundary() <= locSize.width) ? originalInnerSize.width - newInnerSize.width : 0; + this._scrollChildren(offsetX, offsetY); + break; + default: + break; + } + + var innerSize = innerContainer.getContentSize(); + var innerPos = innerContainer.getPosition(); + var innerAP = innerContainer.getAnchorPoint(); + if (innerContainer.getLeftBoundary() > 0.0) + innerContainer.setPosition(innerAP.x * innerSize.width, innerPos.y); + if (innerContainer.getRightBoundary() < locSize.width) + innerContainer.setPosition(locSize.width - ((1.0 - innerAP.x) * innerSize.width), innerPos.y); + if (innerPos.y > 0.0) + innerContainer.setPosition(innerPos.x, innerAP.y * innerSize.height); + if (innerContainer.getTopBoundary() < locSize.height) + innerContainer.setPosition(innerPos.x, locSize.height - (1.0 - innerAP.y) * innerSize.height); + }, + + _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); + }, + + /** + * 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) { + 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; + var childrenArray = this._innerContainer._children; + for(var i = 0; i < childrenArray.length; 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._clippingEnabled && 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); + }, + + _moveChildren: function (offsetX, offsetY) { + var locContainer = this._innerContainer; + //var pos = this._innerContainer.getPosition(); + this._moveChildPoint.x = locContainer.x + offsetX; + this._moveChildPoint.y = locContainer.y + offsetY; + this._innerContainer.setPosition(this._moveChildPoint); + if(this._clippingEnabled && this._innerContainer._children.length !== 0 ) + this.updateChildren(); + }, + + _autoScrollChildren: function (dt) { + var lastTime = this._autoScrollAddUpTime; + this._autoScrollAddUpTime += dt; + if (this._isAutoScrollSpeedAttenuated) { + var nowSpeed = this._autoScrollOriginalSpeed + this._autoScrollAcceleration * this._autoScrollAddUpTime; + if (nowSpeed <= 0) { + this._stopAutoScrollChildren(); + this._checkNeedBounce(); + } else { + var timeParam = lastTime * 2 + dt; + var offset = (this._autoScrollOriginalSpeed + this._autoScrollAcceleration * timeParam * 0.5) * dt; + var offsetX = offset * this._autoScrollDir.x; + var offsetY = offset * this._autoScrollDir.y; + if (!this._scrollChildren(offsetX, offsetY)) { + this._stopAutoScrollChildren(); + this._checkNeedBounce(); + } + } + } else { + if (this._needCheckAutoScrollDestination) { + var xOffset = this._autoScrollDir.x * dt * this._autoScrollOriginalSpeed; + var yOffset = this._autoScrollDir.y * dt * this._autoScrollOriginalSpeed; + var notDone = this._checkCustomScrollDestination(xOffset, yOffset); + var scrollCheck = this._scrollChildren(xOffset, yOffset); + if (!notDone || !scrollCheck) { + this._stopAutoScrollChildren(); + this._checkNeedBounce(); + } + } else { + if (!this._scrollChildren(this._autoScrollDir.x * dt * this._autoScrollOriginalSpeed, + this._autoScrollDir.y * dt * this._autoScrollOriginalSpeed)) { + this._stopAutoScrollChildren(); + this._checkNeedBounce(); + } + } + } + }, + + _bounceChildren: function (dt) { + var locSpeed = this._bounceOriginalSpeed; + var locBounceDir = this._bounceDir; + if (locSpeed <= 0.0) + this._stopBounceChildren(); + if (!this._bounceScrollChildren(locBounceDir.x * dt * locSpeed, locBounceDir.y * dt * locSpeed)) + this._stopBounceChildren(); + }, + + _checkNeedBounce: function () { + if (!this.bounceEnabled) + return false; + this._checkBounceBoundary(); + var locTopBounceNeeded = this._topBounceNeeded, locBottomBounceNeeded = this._bottomBounceNeeded, + locLeftBounceNeeded = this._leftBounceNeeded, locRightBounceNeeded = this._rightBounceNeeded; + + if (locTopBounceNeeded || locBottomBounceNeeded || locLeftBounceNeeded || locRightBounceNeeded) { + var scrollVector, orSpeed; + var locContentSize = this._contentSize, locInnerContainer = this._innerContainer; + if (locTopBounceNeeded && locLeftBounceNeeded) { + scrollVector = cc.pSub(cc.p(0.0, locContentSize.height), cc.p(locInnerContainer.getLeftBoundary(), locInnerContainer.getTopBoundary())); + orSpeed = cc.pLength(scrollVector) / 0.2; + this._bounceDir = cc.pNormalize(scrollVector); + this._startBounceChildren(orSpeed); + } else if (locTopBounceNeeded && locRightBounceNeeded) { + scrollVector = cc.pSub(cc.p(locContentSize.width, locContentSize.height), cc.p(locInnerContainer.getRightBoundary(), locInnerContainer.getTopBoundary())); + orSpeed = cc.pLength(scrollVector) / 0.2; + this._bounceDir = cc.pNormalize(scrollVector); + this._startBounceChildren(orSpeed); + } else if (locBottomBounceNeeded && locLeftBounceNeeded) { + scrollVector = cc.pSub(cc.p(0, 0), cc.p(locInnerContainer.getLeftBoundary(), locInnerContainer.getBottomBoundary())); + orSpeed = cc.pLength(scrollVector) / 0.2; + this._bounceDir = cc.pNormalize(scrollVector); + this._startBounceChildren(orSpeed); + } else if (locBottomBounceNeeded && locRightBounceNeeded) { + scrollVector = cc.pSub(cc.p(locContentSize.width, 0.0), cc.p(locInnerContainer.getRightBoundary(), locInnerContainer.getBottomBoundary())); + orSpeed = cc.pLength(scrollVector) / 0.2; + this._bounceDir = cc.pNormalize(scrollVector); + this._startBounceChildren(orSpeed); + } else if (locTopBounceNeeded) { + scrollVector = cc.pSub(cc.p(0, locContentSize.height), cc.p(0.0, locInnerContainer.getTopBoundary())); + orSpeed = cc.pLength(scrollVector) / 0.2; + this._bounceDir = cc.pNormalize(scrollVector); + this._startBounceChildren(orSpeed); + } else if (locBottomBounceNeeded) { + scrollVector = cc.pSub(cc.p(0, 0), cc.p(0.0, locInnerContainer.getBottomBoundary())); + orSpeed = cc.pLength(scrollVector) / 0.2; + this._bounceDir = cc.pNormalize(scrollVector); + this._startBounceChildren(orSpeed); + } else if (locLeftBounceNeeded) { + scrollVector = cc.pSub(cc.p(0, 0), cc.p(locInnerContainer.getLeftBoundary(), 0.0)); + orSpeed = cc.pLength(scrollVector) / 0.2; + this._bounceDir = cc.pNormalize(scrollVector); + this._startBounceChildren(orSpeed); + } else if (locRightBounceNeeded) { + scrollVector = cc.pSub(cc.p(locContentSize.width, 0), cc.p(locInnerContainer.getRightBoundary(), 0.0)); + orSpeed = cc.pLength(scrollVector) / 0.2; + this._bounceDir = cc.pNormalize(scrollVector); + this._startBounceChildren(orSpeed); + } + return true; + } + return false; + }, + + _checkBounceBoundary: function () { + var locContainer = this._innerContainer; + var icBottomPos = locContainer.getBottomBoundary(); + if (icBottomPos > this._bottomBoundary) { + this._scrollToBottomEvent(); + this._bottomBounceNeeded = true; + } else + this._bottomBounceNeeded = false; + + var icTopPos = locContainer.getTopBoundary(); + if (icTopPos < this._topBoundary) { + this._scrollToTopEvent(); + this._topBounceNeeded = true; + } else + this._topBounceNeeded = false; + + var icRightPos = locContainer.getRightBoundary(); + if (icRightPos < this._rightBoundary) { + this._scrollToRightEvent(); + this._rightBounceNeeded = true; + } else + this._rightBounceNeeded = false; + + var icLeftPos = locContainer.getLeftBoundary(); + if (icLeftPos > this._leftBoundary) { + this._scrollToLeftEvent(); + this._leftBounceNeeded = true; + } else + this._leftBounceNeeded = false; + }, + + _startBounceChildren: function (v) { + this._bounceOriginalSpeed = v; + this._bouncing = true; + }, + + _stopBounceChildren: function () { + this._bouncing = false; + this._bounceOriginalSpeed = 0.0; + this._leftBounceNeeded = false; + this._rightBounceNeeded = false; + this._topBounceNeeded = false; + this._bottomBounceNeeded = false; + }, + + _startAutoScrollChildrenWithOriginalSpeed: function (dir, v, attenuated, acceleration) { + this._stopAutoScrollChildren(); + this._autoScrollDir.x = dir.x; + this._autoScrollDir.y = dir.y; + this._isAutoScrollSpeedAttenuated = attenuated; + this._autoScrollOriginalSpeed = v; + this._autoScroll = true; + this._autoScrollAcceleration = acceleration; + }, + + _startAutoScrollChildrenWithDestination: function (des, time, attenuated) { + this._needCheckAutoScrollDestination = false; + this._autoScrollDestination = des; + var dis = cc.pSub(des, this._innerContainer.getPosition()); + var dir = cc.pNormalize(dis); + var orSpeed = 0.0; + var acceleration = -1000.0; + var disLength = cc.pLength(dis); + if (attenuated) { + acceleration = -(2 * disLength) / (time * time); + orSpeed = 2 * disLength / time; + } else { + this._needCheckAutoScrollDestination = true; + orSpeed = disLength / time; + } + this._startAutoScrollChildrenWithOriginalSpeed(dir, orSpeed, attenuated, acceleration); + }, + + _jumpToDestination: function (dstX, dstY) { + if (dstX.x !== undefined) { + dstY = dstX.y; + dstX = dstX.x; + } + var finalOffsetX = dstX; + var finalOffsetY = dstY; + switch (this.direction) { + case ccui.ScrollView.Dir.VERTICAL: + if (dstY <= 0) + finalOffsetY = Math.max(dstY, this._contentSize.height - this._innerContainer.getContentSize().height); + break; + case ccui.ScrollView.Dir.HORIZONTAL: + if (dstX <= 0) + finalOffsetX = Math.max(dstX, this._contentSize.width - this._innerContainer.getContentSize().width); + break; + case ccui.ScrollView.Dir.BOTH: + if (dstY <= 0) + finalOffsetY = Math.max(dstY, this._contentSize.height - this._innerContainer.getContentSize().height); + if (dstX <= 0) + finalOffsetX = Math.max(dstX, this._contentSize.width - this._innerContainer.getContentSize().width); + break; + default: + break; + } + this._innerContainer.setPosition(finalOffsetX, finalOffsetY); + }, + + _stopAutoScrollChildren: function () { + this._autoScroll = false; + this._autoScrollOriginalSpeed = 0; + this._autoScrollAddUpTime = 0; + }, + + _bounceScrollChildren: function (touchOffsetX, touchOffsetY) { + var scrollEnabled = true; + var realOffsetX, realOffsetY, icRightPos, icTopPos, icBottomPos; + var locContainer = this._innerContainer; + if (touchOffsetX > 0.0 && touchOffsetY > 0.0){ //first quadrant //bounce to top-right + realOffsetX = touchOffsetX; + realOffsetY = touchOffsetY; + icRightPos = locContainer.getRightBoundary(); + if (icRightPos + realOffsetX >= this._rightBoundary) { + realOffsetX = this._rightBoundary - icRightPos; + this._bounceRightEvent(); + scrollEnabled = false; + } + icTopPos = locContainer.getTopBoundary(); + if (icTopPos + touchOffsetY >= this._topBoundary) { + realOffsetY = this._topBoundary - icTopPos; + this._bounceTopEvent(); + scrollEnabled = false; + } + this._moveChildren(realOffsetX, realOffsetY); + } else if (touchOffsetX < 0.0 && touchOffsetY > 0.0){ //second quadrant //bounce to top-left + realOffsetX = touchOffsetX; + realOffsetY = touchOffsetY; + icLefrPos = locContainer.getLeftBoundary(); + if (icLefrPos + realOffsetX <= this._leftBoundary) { + realOffsetX = this._leftBoundary - icLefrPos; + this._bounceLeftEvent(); + scrollEnabled = false; + } + icTopPos = locContainer.getTopBoundary(); + if (icTopPos + touchOffsetY >= this._topBoundary) { + realOffsetY = this._topBoundary - icTopPos; + this._bounceTopEvent(); + scrollEnabled = false; + } + this._moveChildren(realOffsetX, realOffsetY); + }else if (touchOffsetX < 0.0 && touchOffsetY < 0.0){ //third quadrant //bounce to bottom-left + realOffsetX = touchOffsetX; + realOffsetY = touchOffsetY; + var icLefrPos = locContainer.getLeftBoundary(); + if (icLefrPos + realOffsetX <= this._leftBoundary) { + realOffsetX = this._leftBoundary - icLefrPos; + this._bounceLeftEvent(); + scrollEnabled = false; + } + icBottomPos = locContainer.getBottomBoundary(); + if (icBottomPos + touchOffsetY <= this._bottomBoundary) { + realOffsetY = this._bottomBoundary - icBottomPos; + this._bounceBottomEvent(); + scrollEnabled = false; + } + this._moveChildren(realOffsetX, realOffsetY); + } else if (touchOffsetX > 0.0 && touchOffsetY < 0.0){ //forth quadrant //bounce to bottom-right + realOffsetX = touchOffsetX; + realOffsetY = touchOffsetY; + icRightPos = locContainer.getRightBoundary(); + if (icRightPos + realOffsetX >= this._rightBoundary) { + realOffsetX = this._rightBoundary - icRightPos; + this._bounceRightEvent(); + scrollEnabled = false; + } + icBottomPos = locContainer.getBottomBoundary(); + if (icBottomPos + touchOffsetY <= this._bottomBoundary) { + realOffsetY = this._bottomBoundary - icBottomPos; + this._bounceBottomEvent(); + scrollEnabled = false; + } + this._moveChildren(realOffsetX, realOffsetY); + } else if (touchOffsetX === 0.0 && touchOffsetY > 0.0){ // bounce to top + realOffsetY = touchOffsetY; + icTopPos = locContainer.getTopBoundary(); + if (icTopPos + touchOffsetY >= this._topBoundary) { + realOffsetY = this._topBoundary - icTopPos; + this._bounceTopEvent(); + scrollEnabled = false; + } + this._moveChildren(0.0, realOffsetY); + } else if (touchOffsetX === 0.0 && touchOffsetY < 0.0) {//bounce to bottom + realOffsetY = touchOffsetY; + icBottomPos = locContainer.getBottomBoundary(); + if (icBottomPos + touchOffsetY <= this._bottomBoundary) { + realOffsetY = this._bottomBoundary - icBottomPos; + this._bounceBottomEvent(); + scrollEnabled = false; + } + this._moveChildren(0.0, realOffsetY); + } else if (touchOffsetX > 0.0 && touchOffsetY === 0.0){ //bounce to right + realOffsetX = touchOffsetX; + icRightPos = locContainer.getRightBoundary(); + if (icRightPos + realOffsetX >= this._rightBoundary) { + realOffsetX = this._rightBoundary - icRightPos; + this._bounceRightEvent(); + scrollEnabled = false; + } + this._moveChildren(realOffsetX, 0.0); + }else if (touchOffsetX < 0.0 && touchOffsetY === 0.0){ //bounce to left + realOffsetX = touchOffsetX; + var icLeftPos = locContainer.getLeftBoundary(); + if (icLeftPos + realOffsetX <= this._leftBoundary) { + realOffsetX = this._leftBoundary - icLeftPos; + this._bounceLeftEvent(); + scrollEnabled = false; + } + this._moveChildren(realOffsetX, 0.0); + } + return scrollEnabled; + }, + + _checkCustomScrollDestination: function (touchOffsetX, touchOffsetY) { + var scrollEnabled = true; + var icBottomPos, icLeftPos, icRightPos, icTopPos; + var locContainer = this._innerContainer, locDestination = this._autoScrollDestination; + switch (this.direction) { + case ccui.ScrollView.Dir.VERTICAL: + if (this._autoScrollDir.y > 0) { + icBottomPos = locContainer.getBottomBoundary(); + if (icBottomPos + touchOffsetY >= locDestination.y) { + touchOffsetY = locDestination.y - icBottomPos; + scrollEnabled = false; + } + } else { + icBottomPos = locContainer.getBottomBoundary(); + if (icBottomPos + touchOffsetY <= locDestination.y) { + touchOffsetY = locDestination.y - icBottomPos; + scrollEnabled = false; + } + } + break; + case ccui.ScrollView.Dir.HORIZONTAL: + if (this._autoScrollDir.x > 0) { + icLeftPos = locContainer.getLeftBoundary(); + if (icLeftPos + touchOffsetX >= locDestination.x) { + touchOffsetX = locDestination.x - icLeftPos; + scrollEnabled = false; + } + } else { + icLeftPos = locContainer.getLeftBoundary(); + if (icLeftPos + touchOffsetX <= locDestination.x) { + touchOffsetX = locDestination.x - icLeftPos; + scrollEnabled = false; + } + } + break; + case ccui.ScrollView.Dir.BOTH: + if (touchOffsetX > 0.0 && touchOffsetY > 0.0){ // up right + icLeftPos = locContainer.getLeftBoundary(); + if (icLeftPos + touchOffsetX >= locDestination.x) { + touchOffsetX = locDestination.x - icLeftPos; + scrollEnabled = false; + } + icBottomPos = locContainer.getBottomBoundary(); + if (icBottomPos + touchOffsetY >= locDestination.y) { + touchOffsetY = locDestination.y - icBottomPos; + scrollEnabled = false; + } + } else if (touchOffsetX < 0.0 && touchOffsetY > 0.0){ // up left + icRightPos = locContainer.getRightBoundary(); + if (icRightPos + touchOffsetX <= locDestination.x) { + touchOffsetX = locDestination.x - icRightPos; + scrollEnabled = false; + } + icBottomPos = locContainer.getBottomBoundary(); + if (icBottomPos + touchOffsetY >= locDestination.y) { + touchOffsetY = locDestination.y - icBottomPos; + scrollEnabled = false; + } + } else if (touchOffsetX < 0.0 && touchOffsetY < 0.0){ // down left + icRightPos = locContainer.getRightBoundary(); + if (icRightPos + touchOffsetX <= locDestination.x) { + touchOffsetX = locDestination.x - icRightPos; + scrollEnabled = false; + } + icTopPos = locContainer.getTopBoundary(); + if (icTopPos + touchOffsetY <= locDestination.y) { + touchOffsetY = locDestination.y - icTopPos; + scrollEnabled = false; + } + } else if (touchOffsetX > 0.0 && touchOffsetY < 0.0){ // down right + icLeftPos = locContainer.getLeftBoundary(); + if (icLeftPos + touchOffsetX >= locDestination.x) { + touchOffsetX = locDestination.x - icLeftPos; + scrollEnabled = false; + } + icTopPos = locContainer.getTopBoundary(); + if (icTopPos + touchOffsetY <= locDestination.y) { + touchOffsetY = locDestination.y - icTopPos; + scrollEnabled = false; + } + } else if (touchOffsetX === 0.0 && touchOffsetY > 0.0){ // up + icBottomPos = locContainer.getBottomBoundary(); + if (icBottomPos + touchOffsetY >= locDestination.y) { + touchOffsetY = locDestination.y - icBottomPos; + scrollEnabled = false; + } + } else if (touchOffsetX < 0.0 && touchOffsetY === 0.0){ // left + icRightPos = locContainer.getRightBoundary(); + if (icRightPos + touchOffsetX <= locDestination.x) { + touchOffsetX = locDestination.x - icRightPos; + scrollEnabled = false; + } + } else if (touchOffsetX === 0.0 && touchOffsetY < 0.0){ // down + icTopPos = locContainer.getTopBoundary(); + if (icTopPos + touchOffsetY <= locDestination.y) { + touchOffsetY = locDestination.y - icTopPos; + scrollEnabled = false; + } + } else if (touchOffsetX > 0.0 && touchOffsetY === 0.0){ // right + icLeftPos = locContainer.getLeftBoundary(); + if (icLeftPos + touchOffsetX >= locDestination.x) { + touchOffsetX = locDestination.x - icLeftPos; + scrollEnabled = false; + } + } + break; + default: + break; + } + return scrollEnabled; + }, + + _scrollChildren: function (touchOffsetX, touchOffsetY) { + var scrollEnabled = true; + this._scrollingEvent(); + switch (this.direction) { + case ccui.ScrollView.Dir.VERTICAL: // vertical + scrollEnabled = this._scrollChildrenVertical(touchOffsetX, touchOffsetY); + break; + case ccui.ScrollView.Dir.HORIZONTAL: // horizontal + scrollEnabled = this._scrollChildrenHorizontal(touchOffsetX, touchOffsetY); + break; + case ccui.ScrollView.Dir.BOTH: + scrollEnabled = this._scrollChildrenBoth(touchOffsetX, touchOffsetY); + break; + default: + break; + } + return scrollEnabled; + }, + + _scrollChildrenVertical: function(touchOffsetX, touchOffsetY){ + var realOffset = touchOffsetY; + var scrollEnabled = true; + var icBottomPos, icTopPos, locContainer = this._innerContainer; + if (this.bounceEnabled) { + icBottomPos = locContainer.getBottomBoundary(); + if (icBottomPos + touchOffsetY >= this._bounceBottomBoundary) { + realOffset = this._bounceBottomBoundary - icBottomPos; + this._scrollToBottomEvent(); + scrollEnabled = false; + } + icTopPos = locContainer.getTopBoundary(); + if (icTopPos + touchOffsetY <= this._bounceTopBoundary) { + realOffset = this._bounceTopBoundary - icTopPos; + this._scrollToTopEvent(); + scrollEnabled = false; + + } + } else { + icBottomPos = locContainer.getBottomBoundary(); + if (icBottomPos + touchOffsetY >= this._bottomBoundary){ + realOffset = this._bottomBoundary - icBottomPos; + this._scrollToBottomEvent(); + scrollEnabled = false; + } + icTopPos = locContainer.getTopBoundary(); + if (icTopPos + touchOffsetY <= this._topBoundary) { + realOffset = this._topBoundary - icTopPos; + this._scrollToTopEvent(); + scrollEnabled = false; + } + } + this._moveChildren(0.0, realOffset); + return scrollEnabled; + }, + + _scrollChildrenHorizontal: function(touchOffsetX, touchOffestY){ + var scrollEnabled = true; + var realOffset = touchOffsetX; + var icRightPos, icLeftPos, locContainer = this._innerContainer; + if (this.bounceEnabled){ + icRightPos = locContainer.getRightBoundary(); + if (icRightPos + touchOffsetX <= this._bounceRightBoundary) { + realOffset = this._bounceRightBoundary - icRightPos; + this._scrollToRightEvent(); + scrollEnabled = false; + } + icLeftPos = locContainer.getLeftBoundary(); + if (icLeftPos + touchOffsetX >= this._bounceLeftBoundary) { + realOffset = this._bounceLeftBoundary - icLeftPos; + this._scrollToLeftEvent(); + scrollEnabled = false; + } + } else { + icRightPos = locContainer.getRightBoundary(); + if (icRightPos + touchOffsetX <= this._rightBoundary) { + realOffset = this._rightBoundary - icRightPos; + this._scrollToRightEvent(); + scrollEnabled = false; + } + icLeftPos = locContainer.getLeftBoundary(); + if (icLeftPos + touchOffsetX >= this._leftBoundary) { + realOffset = this._leftBoundary - icLeftPos; + this._scrollToLeftEvent(); + scrollEnabled = false; + } + } + this._moveChildren(realOffset, 0.0); + return scrollEnabled; + }, + + _scrollChildrenBoth: function (touchOffsetX, touchOffsetY) { + var scrollEnabled = true; + var realOffsetX = touchOffsetX; + var realOffsetY = touchOffsetY; + var icLeftPos, icBottomPos, icRightPos, icTopPos; + var locContainer = this._innerContainer; + if (this.bounceEnabled) { + if (touchOffsetX > 0.0 && touchOffsetY > 0.0) { // up right + icLeftPos = locContainer.getLeftBoundary(); + if (icLeftPos + touchOffsetX >= this._bounceLeftBoundary) { + realOffsetX = this._bounceLeftBoundary - icLeftPos; + this._scrollToLeftEvent(); + scrollEnabled = false; + } + icBottomPos = locContainer.getBottomBoundary(); + if (icBottomPos + touchOffsetY >= this._bounceBottomBoundary) { + realOffsetY = this._bounceBottomBoundary - icBottomPos; + this._scrollToBottomEvent(); + scrollEnabled = false; + } + } else if (touchOffsetX < 0.0 && touchOffsetY > 0.0) { // up left + icRightPos = locContainer.getRightBoundary(); + if (icRightPos + touchOffsetX <= this._bounceRightBoundary) { + realOffsetX = this._bounceRightBoundary - icRightPos; + this._scrollToRightEvent(); + scrollEnabled = false; + } + icBottomPos = locContainer.getBottomBoundary(); + if (icBottomPos + touchOffsetY >= this._bounceBottomBoundary) { + realOffsetY = this._bounceBottomBoundary - icBottomPos; + this._scrollToBottomEvent(); + scrollEnabled = false; + } + } else if (touchOffsetX < 0.0 && touchOffsetY < 0.0) { // down left + icRightPos = locContainer.getRightBoundary(); + if (icRightPos + touchOffsetX <= this._bounceRightBoundary) { + realOffsetX = this._bounceRightBoundary - icRightPos; + this._scrollToRightEvent(); + scrollEnabled = false; + } + icTopPos = locContainer.getTopBoundary(); + if (icTopPos + touchOffsetY <= this._bounceTopBoundary) { + realOffsetY = this._bounceTopBoundary - icTopPos; + this._scrollToTopEvent(); + scrollEnabled = false; + } + } else if (touchOffsetX > 0.0 && touchOffsetY < 0.0){ // down right + icLeftPos = locContainer.getLeftBoundary(); + if (icLeftPos + touchOffsetX >= this._bounceLeftBoundary) { + realOffsetX = this._bounceLeftBoundary - icLeftPos; + this._scrollToLeftEvent(); + scrollEnabled = false; + } + icTopPos = locContainer.getTopBoundary(); + if (icTopPos + touchOffsetY <= this._bounceTopBoundary) { + realOffsetY = this._bounceTopBoundary - icTopPos; + this._scrollToTopEvent(); + scrollEnabled = false; + } + } else if (touchOffsetX === 0.0 && touchOffsetY > 0.0){ // up + icBottomPos = locContainer.getBottomBoundary(); + if (icBottomPos + touchOffsetY >= this._bounceBottomBoundary) { + realOffsetY = this._bounceBottomBoundary - icBottomPos; + this._scrollToBottomEvent(); + scrollEnabled = false; + } + } else if (touchOffsetX < 0.0 && touchOffsetY === 0.0){ // left + icRightPos = locContainer.getRightBoundary(); + if (icRightPos + touchOffsetX <= this._bounceRightBoundary) { + realOffsetX = this._bounceRightBoundary - icRightPos; + this._scrollToRightEvent(); + scrollEnabled = false; + } + } else if (touchOffsetX === 0.0 && touchOffsetY < 0.0){ // down + icTopPos = locContainer.getTopBoundary(); + if (icTopPos + touchOffsetY <= this._bounceTopBoundary) { + realOffsetY = this._bounceTopBoundary - icTopPos; + this._scrollToTopEvent(); + scrollEnabled = false; + } + } else if (touchOffsetX > 0.0 && touchOffsetY === 0.0){ // right + icLeftPos = locContainer.getLeftBoundary(); + if (icLeftPos + touchOffsetX >= this._bounceLeftBoundary) { + realOffsetX = this._bounceLeftBoundary - icLeftPos; + this._scrollToLeftEvent(); + scrollEnabled = false; + } + } + } else { + if (touchOffsetX > 0.0 && touchOffsetY > 0.0){ // up right + icLeftPos = locContainer.getLeftBoundary(); + if (icLeftPos + touchOffsetX >= this._leftBoundary) { + realOffsetX = this._leftBoundary - icLeftPos; + this._scrollToLeftEvent(); + scrollEnabled = false; + } + icBottomPos = locContainer.getBottomBoundary(); + if (icBottomPos + touchOffsetY >= this._bottomBoundary) { + realOffsetY = this._bottomBoundary - icBottomPos; + this._scrollToBottomEvent(); + scrollEnabled = false; + } + } else if (touchOffsetX < 0.0 && touchOffsetY > 0.0){ // up left + icRightPos = locContainer.getRightBoundary(); + if (icRightPos + touchOffsetX <= this._rightBoundary) { + realOffsetX = this._rightBoundary - icRightPos; + this._scrollToRightEvent(); + scrollEnabled = false; + } + icBottomPos = locContainer.getBottomBoundary(); + if (icBottomPos + touchOffsetY >= this._bottomBoundary) { + realOffsetY = this._bottomBoundary - icBottomPos; + this._scrollToBottomEvent(); + scrollEnabled = false; + } + } else if (touchOffsetX < 0.0 && touchOffsetY < 0.0){ // down left + icRightPos = locContainer.getRightBoundary(); + if (icRightPos + touchOffsetX <= this._rightBoundary) { + realOffsetX = this._rightBoundary - icRightPos; + this._scrollToRightEvent(); + scrollEnabled = false; + } + icTopPos = locContainer.getTopBoundary(); + if (icTopPos + touchOffsetY <= this._topBoundary) { + realOffsetY = this._topBoundary - icTopPos; + this._scrollToTopEvent(); + scrollEnabled = false; + } + } else if (touchOffsetX > 0.0 && touchOffsetY < 0.0){ // down right + icLeftPos = locContainer.getLeftBoundary(); + if (icLeftPos + touchOffsetX >= this._leftBoundary) { + realOffsetX = this._leftBoundary - icLeftPos; + this._scrollToLeftEvent(); + scrollEnabled = false; + } + icTopPos = this._innerContainer.getTopBoundary(); + if (icTopPos + touchOffsetY <= this._topBoundary) { + realOffsetY = this._topBoundary - icTopPos; + this._scrollToTopEvent(); + scrollEnabled = false; + } + } else if (touchOffsetX === 0.0 && touchOffsetY > 0.0) { // up + icBottomPos = this._innerContainer.getBottomBoundary(); + if (icBottomPos + touchOffsetY >= this._bottomBoundary) { + realOffsetY = this._bottomBoundary - icBottomPos; + this._scrollToBottomEvent(); + scrollEnabled = false; + } + } else if (touchOffsetX < 0.0 && touchOffsetY === 0.0){ // left + icRightPos = this._innerContainer.getRightBoundary(); + if (icRightPos + touchOffsetX <= this._rightBoundary) { + realOffsetX = this._rightBoundary - icRightPos; + this._scrollToRightEvent(); + scrollEnabled = false; + } + } else if (touchOffsetX === 0.0 && touchOffsetY < 0.0){ // down + icTopPos = this._innerContainer.getTopBoundary(); + if (icTopPos + touchOffsetY <= this._topBoundary) { + realOffsetY = this._topBoundary - icTopPos; + this._scrollToTopEvent(); + scrollEnabled = false; + } + } else if (touchOffsetX > 0.0 && touchOffsetY === 0.0){ // right + icLeftPos = this._innerContainer.getLeftBoundary(); + if (icLeftPos + touchOffsetX >= this._leftBoundary) { + realOffsetX = this._leftBoundary - icLeftPos; + this._scrollToLeftEvent(); + scrollEnabled = false; + } + } + } + this._moveChildren(realOffsetX, realOffsetY); + return scrollEnabled; + }, + + /** + * Scroll inner container to bottom boundary of ScrollView. + * @param {Number} time + * @param {Boolean} attenuated + */ + scrollToBottom: function (time, attenuated) { + this._startAutoScrollChildrenWithDestination(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._startAutoScrollChildrenWithDestination( + 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._startAutoScrollChildrenWithDestination(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._startAutoScrollChildrenWithDestination( + 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._startAutoScrollChildrenWithDestination(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._startAutoScrollChildrenWithDestination(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._startAutoScrollChildrenWithDestination(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._startAutoScrollChildrenWithDestination(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._startAutoScrollChildrenWithDestination(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._startAutoScrollChildrenWithDestination(cc.p(-(percent * w / 100), this._innerContainer.getPositionY()), time, attenuated); + }, + + /** + * Scroll inner container to both direction percent position of ScrollView. + * @param {cc.Vec2} 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._startAutoScrollChildrenWithDestination(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.Vec2} 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); + }, + + _startRecordSlidAction: function () { + if (this._autoScroll) + this._stopAutoScrollChildren(); + if (this._bouncing) + this._stopBounceChildren(); + this._slidTime = 0.0; + }, + + _endRecordSlidAction: function () { + if (!this._checkNeedBounce() && this.inertiaScrollEnabled) { + if (this._slidTime <= 0.016) + return; + var totalDis = 0, dir; + var touchEndPositionInNodeSpace = this.convertToNodeSpace(this._touchEndPosition); + var touchBeganPositionInNodeSpace = this.convertToNodeSpace(this._touchBeganPosition); + switch (this.direction) { + case ccui.ScrollView.Dir.VERTICAL : + totalDis = touchEndPositionInNodeSpace.y - touchBeganPositionInNodeSpace.y; + dir = (totalDis < 0) ? ccui.ScrollView.SCROLLDIR_DOWN : ccui.ScrollView.SCROLLDIR_UP; + break; + case ccui.ScrollView.Dir.HORIZONTAL: + totalDis = touchEndPositionInNodeSpace.x - touchBeganPositionInNodeSpace.x; + dir = totalDis < 0 ? ccui.ScrollView.SCROLLDIR_LEFT : ccui.ScrollView.SCROLLDIR_RIGHT; + break; + case ccui.ScrollView.Dir.BOTH : + var subVector = cc.pSub(touchEndPositionInNodeSpace, touchBeganPositionInNodeSpace); + totalDis = cc.pLength(subVector); + dir = cc.pNormalize(subVector); + break; + default: + dir = cc.p(0,0); + break; + } + var orSpeed = Math.min(Math.abs(totalDis) / (this._slidTime), ccui.ScrollView.AUTO_SCROLL_MAX_SPEED); + this._startAutoScrollChildrenWithOriginalSpeed(dir, orSpeed, true, -1000); + this._slidTime = 0; + } + }, + + _handlePressLogic: function (touch) { + this._startRecordSlidAction(); + this._bePressed = true; + }, + + _handleMoveLogic: function (touch) { + var touchPositionInNodeSpace = this.convertToNodeSpace(touch.getLocation()), + previousTouchPositionInNodeSpace = this.convertToNodeSpace(touch.getPreviousLocation()); + var delta = cc.pSub(touchPositionInNodeSpace, previousTouchPositionInNodeSpace); + switch (this.direction) { + case ccui.ScrollView.Dir.VERTICAL: // vertical + this._scrollChildren(0.0, delta.y); + break; + case ccui.ScrollView.Dir.HORIZONTAL: // horizontal + this._scrollChildren(delta.x, 0); + break; + case ccui.ScrollView.Dir.BOTH: // both + this._scrollChildren(delta.x, delta.y); + break; + default: + break; + } + }, + + _handleReleaseLogic: function (touch) { + this._endRecordSlidAction(); + this._bePressed = false; + }, + + /** + * 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._autoScroll) + this._autoScrollChildren(dt); + if (this._bouncing) + this._bounceChildren(dt); + this._recordSlidTime(dt); + }, + + _recordSlidTime: function (dt) { + if (this._bePressed) + this._slidTime += dt; + }, + + /** + * Intercept touch event, handle its child's touch event. + * @override + * @param {cc.Event} event + */ + interceptTouchEvent: function (event) { + var eventType = event._widgetEventType, + touch = event.currentTouch, + touchPoint, offset; + if (!this._touchEnabled) + return; + + touchPoint = touch.getLocation(); + switch (eventType) { + 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: + offset = cc.pLength(cc.pSub(event.target.getTouchBeganPosition(), touchPoint)); + this._touchMovePosition.x = touchPoint.x; + this._touchMovePosition.y = touchPoint.y; + if (offset > this._childFocusCancelOffset) { + event.target.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 (event.target.isSwallowTouches()) + this._isInterceptTouch = false; + break; + } + }, + + _scrollToTopEvent: function () { + if(this._scrollViewEventSelector){ + if (this._scrollViewEventListener) + this._scrollViewEventSelector.call(this._scrollViewEventListener, this, ccui.ScrollView.Event.SCROLL_TO_TOP); + else + this._scrollViewEventSelector(this, ccui.ScrollView.Event.SCROLL_TO_TOP); + } + if(this._ccEventCallback) + this._ccEventCallback(this, ccui.ScrollView.Event.SCROLL_TO_TOP); + }, + + _scrollToBottomEvent: function () { + if(this._scrollViewEventSelector){ + if (this._scrollViewEventListener) + this._scrollViewEventSelector.call(this._scrollViewEventListener, this, ccui.ScrollView.Event.SCROLL_TO_BOTTOM); + else + this._scrollViewEventSelector(this, ccui.ScrollView.Event.SCROLL_TO_BOTTOM); + } + if(this._ccEventCallback) + this._ccEventCallback(this, ccui.ScrollView.Event.SCROLL_TO_BOTTOM); + }, + + _scrollToLeftEvent: function () { + if(this._scrollViewEventSelector){ + if (this._scrollViewEventListener) + this._scrollViewEventSelector.call(this._scrollViewEventListener, this, ccui.ScrollView.Event.SCROLL_TO_LEFT); + else + this._scrollViewEventSelector(this, ccui.ScrollView.Event.SCROLL_TO_LEFT); + } + if(this._ccEventCallback) + this._ccEventCallback(this, ccui.ScrollView.Event.SCROLL_TO_LEFT); + }, + + _scrollToRightEvent: function () { + if(this._scrollViewEventSelector){ + if (this._scrollViewEventListener) + this._scrollViewEventSelector.call(this._scrollViewEventListener, this, ccui.ScrollView.Event.SCROLL_TO_RIGHT); + else + this._scrollViewEventSelector(this, ccui.ScrollView.Event.SCROLL_TO_RIGHT); + } + if(this._ccEventCallback) + this._ccEventCallback(this, ccui.ScrollView.Event.SCROLL_TO_RIGHT); + }, + + _scrollingEvent: function () { + if(this._scrollViewEventSelector){ + if (this._scrollViewEventListener) + this._scrollViewEventSelector.call(this._scrollViewEventListener, this, ccui.ScrollView.Event.SCROLLING); + else + this._scrollViewEventSelector(this, ccui.ScrollView.Event.SCROLLING); + } + if(this._ccEventCallback) + this._ccEventCallback(this, ccui.ScrollView.Event.SCROLLING); + }, + + _bounceTopEvent: function () { + if(this._scrollViewEventSelector){ + if (this._scrollViewEventListener) + this._scrollViewEventSelector.call(this._scrollViewEventListener, this, ccui.ScrollView.Event.BOUNCE_TOP); + else + this._scrollViewEventSelector(this, ccui.ScrollView.Event.BOUNCE_TOP); + } + if(this._ccEventCallback) + this._ccEventCallback(this, ccui.ScrollView.Event.BOUNCE_TOP); + }, + + _bounceBottomEvent: function () { + if(this._scrollViewEventSelector){ + if (this._scrollViewEventListener) + this._scrollViewEventSelector.call(this._scrollViewEventListener, this, ccui.ScrollView.Event.BOUNCE_BOTTOM); + else + this._scrollViewEventSelector(this, ccui.ScrollView.Event.BOUNCE_BOTTOM); + } + if(this._ccEventCallback) + this._ccEventCallback(this, ccui.ScrollView.Event.BOUNCE_BOTTOM); + }, + + _bounceLeftEvent: function () { + if(this._scrollViewEventSelector){ + if (this._scrollViewEventListener) + this._scrollViewEventSelector.call(this._scrollViewEventListener, this, ccui.ScrollView.Event.BOUNCE_LEFT); + else + this._scrollViewEventSelector(this, ccui.ScrollView.Event.BOUNCE_LEFT); + } + if(this._ccEventCallback) + this._ccEventCallback(this, ccui.ScrollView.Event.BOUNCE_LEFT); + }, + + _bounceRightEvent: function () { + if(this._scrollViewEventSelector){ + if (this._scrollViewEventListener) + this._scrollViewEventSelector.call(this._scrollViewEventListener, this, ccui.ScrollView.Event.BOUNCE_RIGHT); + else + this._scrollViewEventSelector(this, ccui.ScrollView.Event.BOUNCE_RIGHT); + } + if(this._ccEventCallback) + this._ccEventCallback(this, ccui.ScrollView.Event.BOUNCE_RIGHT); + }, + + /** + * 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.addEventListener(selector, target); + }, + + /** + * Adds callback function called ScrollView event triggered + * @param {Function} selector + * @param {Object} [target=] + */ + addEventListener: function(selector, target){ + this._scrollViewEventSelector = selector; + this._scrollViewEventListener = target; + }, + + /** + * Changes scroll direction of ScrollView. + * @param {ccui.ScrollView.Dir} dir + * Direction::VERTICAL means vertical scroll, Direction::HORIZONTAL means horizontal scroll + */ + setDirection: function (dir) { + this.direction = dir; + }, + + /** + * Returns scroll direction of ScrollView. + * @returns {ccui.ScrollView.Dir} + */ + 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; + }, + + /** + * 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.Type} type + */ + setLayoutType: function (type) { + this._innerContainer.setLayoutType(type); + }, + + /** + * Returns the layout type of ccui.ScrollView. + * @returns {ccui.Layout.Type} + */ + 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.setDirection(scrollView.direction); + this.setBounceEnabled(scrollView.bounceEnabled); + this.setInertiaScrollEnabled(scrollView.inertiaScrollEnabled); + this._scrollViewEventListener = scrollView._scrollViewEventListener; + this._scrollViewEventSelector = scrollView._scrollViewEventSelector; + this._ccEventCallback = scrollView._ccEventCallback; + } + }, + + /** + * 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); + +_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 + +/** + * Enum for ScrollView direction + * @readonly + * @enum {number} + */ +ccui.ScrollView.Dir = cc.Enum({ + /** + * The none flag of ccui.ScrollView's direction. + */ + NONE: 0, + /** + * The vertical flag of ccui.ScrollView's direction. + */ + VERTICAL: 1, + /** + * The horizontal flag of ccui.ScrollView's direction. + */ + HORIZONTAL: 2, + /** + * The both flag of ccui.ScrollView's direction. + */ + BOTH: 3 +}); + +/** + * The event types of ccui.ScrollView. + * @readonly + * @enum {number} + */ +ccui.ScrollView.Event = cc.Enum({ + /** + * The flag scroll to top of ccui.ScrollView's event. + */ + SCROLL_TO_TOP: 0, + /** + * The flag scroll to bottom of ccui.ScrollView's event. + */ + SCROLL_TO_BOTTOM: 1, + /** + * The flag scroll to left of ccui.ScrollView's event. + */ + SCROLL_TO_LEFT: 2, + /** + * The flag scroll to right of ccui.ScrollView's event. + */ + SCROLL_TO_RIGHT: 3, + /** + * The scrolling flag of ccui.ScrollView's event. + */ + SCROLLING: 4, + /** + * The flag bounce top of ccui.ScrollView's event. + */ + BOUNCE_TOP: 5, + /** + * The flag bounce bottom of ccui.ScrollView's event. + */ + BOUNCE_BOTTOM: 6, + /** + * The flag bounce left of ccui.ScrollView's event. + */ + BOUNCE_LEFT: 7, + /** + * The flag bounce right of ccui.ScrollView's event. + */ + BOUNCE_RIGHT: 8 +}); + +/** + * The auto scroll max speed of ccui.ScrollView. + * @constant + * @type {number} + */ +ccui.ScrollView.AUTO_SCROLL_MAX_SPEED = 1000; + +/** + * @ignore + */ +ccui.ScrollView.SCROLLDIR_UP = cc.p(0, 1); +ccui.ScrollView.SCROLLDIR_DOWN = cc.p(0, -1); +ccui.ScrollView.SCROLLDIR_LEFT = cc.p(-1, 0); +ccui.ScrollView.SCROLLDIR_RIGHT = cc.p(1, 0); diff --git a/extensions/ccui/uiwidgets/scroll-widget/UIScrollViewCanvasRenderCmd.js b/extensions/ccui/uiwidgets/scroll-widget/UIScrollViewCanvasRenderCmd.js new file mode 100644 index 00000000000..9cc0ed6cc62 --- /dev/null +++ b/extensions/ccui/uiwidgets/scroll-widget/UIScrollViewCanvasRenderCmd.js @@ -0,0 +1,43 @@ +(function(){ + if(!ccui.ProtectedNode.CanvasRenderCmd) + return; + ccui.ScrollView.CanvasRenderCmd = function(renderable){ + ccui.Layout.CanvasRenderCmd.call(this, 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.visit = function(parentCmd) { + var node = this._node; + if (!node._visible) + return; + cc.renderer.pushRenderCommand(this); + var currentID = node.__instanceId; + cc.renderer._turnToCacheMode(currentID); + + ccui.Layout.CanvasRenderCmd.prototype.visit.call(this, parentCmd); + this._dirtyFlag = 0; + cc.renderer._turnToNormalMode(); + }; + + proto.rendering = function (ctx) { + var currentID = this._node.__instanceId; + var locCmds = cc.renderer._cacheToCanvasCmds[currentID], i, len, + scaleX = cc.view.getScaleX(), + scaleY = cc.view.getScaleY(); + var context = ctx || cc._renderContext; + context.computeRealOffsetY(); + + 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); + } + }; +})(); \ No newline at end of file diff --git a/extensions/ccui/uiwidgets/scroll-widget/UIScrollViewWebGLRenderCmd.js b/extensions/ccui/uiwidgets/scroll-widget/UIScrollViewWebGLRenderCmd.js new file mode 100644 index 00000000000..2448972b7b7 --- /dev/null +++ b/extensions/ccui/uiwidgets/scroll-widget/UIScrollViewWebGLRenderCmd.js @@ -0,0 +1,44 @@ + +(function(){ + if(!ccui.ProtectedNode.WebGLRenderCmd) + return; + ccui.ScrollView.WebGLRenderCmd = function(renderable){ + ccui.Layout.WebGLRenderCmd.call(this, 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.visit = function(parentCmd) { + var node = this._node; + if (!node._visible) + return; + var currentID = this._node.__instanceId; + + cc.renderer.pushRenderCommand(this); + cc.renderer._turnToCacheMode(currentID); + + ccui.Layout.WebGLRenderCmd.prototype.visit.call(this, parentCmd); + + this._dirtyFlag = 0; + cc.renderer._turnToNormalMode(); + }; + + proto.rendering = function(ctx){ + var currentID = this._node.__instanceId; + var locCmds = cc.renderer._cacheToBufferCmds[currentID], + i, + len; + var context = ctx || cc._renderContext; + 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); + } + } +})(); \ No newline at end of file diff --git a/extensions/cocostudio/CocoStudio.js b/extensions/cocostudio/CocoStudio.js new file mode 100644 index 00000000000..07804f52831 --- /dev/null +++ b/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/extensions/cocostudio/action/CCActionFrame.js b/extensions/cocostudio/action/CCActionFrame.js new file mode 100644 index 00000000000..401cd4a6e04 --- /dev/null +++ b/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 === null) + 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 === null) + return true; + return this._action.isDone(); + } +}); diff --git a/extensions/cocostudio/action/CCActionObject.js b/extensions/cocostudio/action/CCActionObject.js new file mode 100644 index 00000000000..d87e8f9e715 --- /dev/null +++ b/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.js.array.remove(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); + } + } + } +}); diff --git a/extensions/cocostudio/armature/CCArmature.js b/extensions/cocostudio/armature/CCArmature.js new file mode 100644 index 00000000000..00c368bad48 --- /dev/null +++ b/extensions/cocostudio/armature/CCArmature.js @@ -0,0 +1,578 @@ +/**************************************************************************** + 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, + _textureAtlas: 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); + }, + + /** + * Initializes a CCArmature with the specified name and CCBone + * @param {String} [name] + * @param {ccs.Bone} [parentBone] + * @return {Boolean} + */ + init: function (name, parentBone) { + cc.Node.prototype.init.call(this); + 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; + }, + + 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.js.array.remove(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.js.array.remove(parentBone.getChildren(), bone); + bone.setParentBone(null); + } + + if (parentName) { + var boneParent = this._boneDic[parentName]; + if (boneParent) { + boneParent.addChildBone(bone); + cc.js.array.remove(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 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/extensions/cocostudio/armature/CCArmatureCanvasRenderCmd.js b/extensions/cocostudio/armature/CCArmatureCanvasRenderCmd.js new file mode 100644 index 00000000000..f1842056517 --- /dev/null +++ b/extensions/cocostudio/armature/CCArmatureCanvasRenderCmd.js @@ -0,0 +1,190 @@ +/**************************************************************************** + 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){ + cc.Node.CanvasRenderCmd.call(this, renderableObject); + this._needDraw = true; + + this._realAnchorPointInPoints = new cc.Vec2(0,0); + this._startRenderCmd = new cc.CustomRenderCmd(this, this._startCmdCallback); + this._RestoreRenderCmd = new cc.CustomRenderCmd(this, this._RestoreCmdCallback); + }; + + var proto = ccs.Armature.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + cc.js.mixin(proto, ccs.Armature.RenderCmd); + 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){ + ccs.Node.CanvasRenderCmd.prototype.transform.call(this, parentCmd, recursive); + + var locChildren = this._node._children; + for (var i = 0, len = locChildren.length; i< len; i++) { + var selBone = locChildren[i]; + if (selBone && selBone.getDisplayRenderNode) { + var selNode = selBone.getDisplayRenderNode(); + if (selNode && selNode._renderCmd){ + var cmd = selNode._renderCmd; + cmd.transform(null); //must be null, use transform in armature mode + + //update displayNode's color and opacity, because skin didn't call visit() + var parentColor = selBone._renderCmd._displayedColor, parentOpacity = selBone._renderCmd._displayedOpacity; + var flags = cc.Node._dirtyFlags, locFlag = cmd._dirtyFlag; + var colorDirty = locFlag & flags.colorDirty, + opacityDirty = locFlag & flags.opacityDirty; + if(colorDirty) + cmd._updateDisplayColor(parentColor); + if(opacityDirty) + cmd._updateDisplayOpacity(parentOpacity); + } + } + } + }; + + proto._RestoreCmdCallback = function(wrapper){ + this._cacheDirty = false; + //wrapper.restore(); + wrapper._switchToArmatureMode(false); + wrapper.restore(); + }; + + proto.initShaderCache = function(){}; + proto.setShaderProgram = function(){}; + proto.updateChildPosition = function(ctx, dis){ + //dis.visit(ctx); + 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; + + switch (selBone.getDisplayRenderNodeType()) { + case ccs.DISPLAY_TYPE_SPRITE: + if(selNode instanceof ccs.Skin) + this.updateChildPosition(ctx, selNode, selBone, alphaPremultiplied, alphaNonPremultipled); + break; + case ccs.DISPLAY_TYPE_ARMATURE: + selNode._renderCmd.rendering(ctx, scaleX, scaleY); + break; + default: + selNode.visit(this); + break; + } + } else if(selBone instanceof cc.Node) { + this._visitNormalChild(selBone); + //selBone.visit(this); + } + } + }; + + proto._visitNormalChild = function(childNode){ + if(childNode == null) + 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._renderCmd.visit(cmd); + else + break; + } + cc.renderer.pushRenderCommand(cmd); + for (; i < len; i++) + children[i]._renderCmd.visit(cmd); + } 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.updateStatus(parentCmd); + node.sortAllChildren(); + + cc.renderer.pushRenderCommand(this._startRenderCmd); + this.rendering(); + cc.renderer.pushRenderCommand(this._RestoreRenderCmd); + + this._cacheDirty = false; + }; +})(); \ No newline at end of file diff --git a/extensions/cocostudio/armature/CCArmatureWebGLRenderCmd.js b/extensions/cocostudio/armature/CCArmatureWebGLRenderCmd.js new file mode 100644 index 00000000000..baffdc30810 --- /dev/null +++ b/extensions/cocostudio/armature/CCArmatureWebGLRenderCmd.js @@ -0,0 +1,167 @@ +/**************************************************************************** + 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){ + cc.Node.WebGLRenderCmd.call(this, renderableObject); + this._needDraw = true; + + this._realAnchorPointInPoints = new cc.Vec2(0,0); + }; + + var proto = ccs.Armature.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + cc.js.mixin(proto, ccs.Armature.RenderCmd); + proto.constructor = ccs.Armature.WebGLRenderCmd; + + proto.rendering = function (ctx, dontChangeMatrix) { + var node = this._node; + + if(!dontChangeMatrix){ + cc.kmGLMatrixMode(cc.KM_GL_MODELVIEW); + cc.kmGLPushMatrix(); + cc.kmGLLoadMatrix(this._stackMatrix); + } + + 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; + selNode.setShaderProgram(this._shaderProgram); + switch (selBone.getDisplayRenderNodeType()) { + case ccs.DISPLAY_TYPE_SPRITE: + if (selNode instanceof ccs.Skin) { + this._updateColorAndOpacity(selNode._renderCmd, selBone); //because skin didn't call visit() + selNode.updateTransform(); + + var func = selBone.getBlendFunc(); + if (func.src !== alphaPremultiplied.src || func.dst !== alphaPremultiplied.dst) + selNode.setBlendFunc(selBone.getBlendFunc()); + else { + if ((node._blendFunc.src === alphaPremultiplied.src && node._blendFunc.dst === alphaPremultiplied.dst) + && !selNode.getTexture().hasPremultipliedAlpha()) + selNode.setBlendFunc(alphaNonPremultipled); + else + selNode.setBlendFunc(node._blendFunc); + } + selNode._renderCmd.rendering(ctx); + } + break; + case ccs.DISPLAY_TYPE_ARMATURE: + selNode._renderCmd.rendering(ctx, true); + break; + default: + selNode._renderCmd.transform(); + selNode._renderCmd.rendering(ctx); + break; + } + } else if (selBone instanceof cc.Node) { + selBone.setShaderProgram(this._shaderProgram); + selBone._renderCmd.transform(); + if(selBone._renderCmd.rendering) + selBone._renderCmd.rendering(ctx); + } + } + if(!dontChangeMatrix) + cc.kmGLPopMatrix(); + }; + + proto.initShaderCache = function(){ + this._shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR); + }; + + proto.setShaderProgram = function(shaderProgram){ + this._shaderProgram = 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.updateChildPosition = function(ctx, dis, selBone, alphaPremultiplied, alphaNonPremultipled){ + var node = this._node; + dis.updateTransform(); + + var func = selBone.getBlendFunc(); + if (func.src !== alphaPremultiplied.src || func.dst !== alphaPremultiplied.dst) + dis.setBlendFunc(selBone.getBlendFunc()); + else { + if ((node._blendFunc.src === alphaPremultiplied.src && node_blendFunc.dst === alphaPremultiplied.dst) + && !dis.getTexture().hasPremultipliedAlpha()) + dis.setBlendFunc(alphaNonPremultipled); + else + dis.setBlendFunc(node._blendFunc); + } + dis.rendering(ctx); + }; + + proto.updateStatus = function () { + var flags = cc.Node._dirtyFlags, locFlag = this._dirtyFlag; + var colorDirty = locFlag & flags.colorDirty, + opacityDirty = locFlag & flags.opacityDirty; + if(colorDirty) + this._updateDisplayColor(); + + if(opacityDirty) + this._updateDisplayOpacity(); + + if(colorDirty || opacityDirty) + this._updateColor(); + + //update the transform every visit, needn't dirty flag, + this.transform(this.getParentRenderCmd(), true); + }; + + proto.visit = function(parentCmd){ + var node = this._node; + // quick return if not visible. children won't be drawn. + if (!node._visible) + return; + + var currentStack = cc.current_stack; + currentStack.stack.push(currentStack.top); + this.updateStatus(parentCmd); + currentStack.top = this._stackMatrix; + + node.sortAllChildren(); + cc.renderer.pushRenderCommand(this); + + this._dirtyFlag = 0; + currentStack.top = currentStack.stack.pop(); + }; +})(); \ No newline at end of file diff --git a/extensions/cocostudio/armature/CCBone.js b/extensions/cocostudio/armature/CCBone.js new file mode 100644 index 00000000000..d0cf3bac0cd --- /dev/null +++ b/extensions/cocostudio/armature/CCBone.js @@ -0,0 +1,713 @@ +/**************************************************************************** + 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) + this._worldTransform = cc.affineTransformConcat(this._worldTransform, this._armature.getNodeToParentTransform()); //TODO TransformConcat + } + + ccs.displayFactory.updateDisplay(this, delta, this._boneTransformDirty || this._armature.getArmatureTransformDirty()); + for(var i=0; i= 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 + * 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(cc.color( node._tweenData.r, node._tweenData.g, node._tweenData.g)); + display.setOpacity(node._tweenData.a); + displayCmd._syncDisplayColor(this._displayedColor); + displayCmd._syncDisplayOpacity(this._displayedOpacity); + displayCmd._updateColor(); + } + } +}; + +(function(){ + ccs.Bone.CanvasRenderCmd = function(renderable){ + cc.Node.CanvasRenderCmd.call(this, renderable); + this._needDraw = false; + }; + + var proto = ccs.Bone.CanvasRenderCmd.prototype = Object.create(cc.Node.CanvasRenderCmd.prototype); + cc.js.mixin(proto, ccs.Bone.RenderCmd); + proto.constructor = ccs.Bone.CanvasRenderCmd; +})(); + +(function(){ + if(!cc.Node.WebGLRenderCmd) + return; + ccs.Bone.WebGLRenderCmd = function(renderable){ + cc.Node.WebGLRenderCmd.call(this, renderable); + this._needDraw = false; + }; + + var proto = ccs.Bone.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + cc.js.mixin(proto, ccs.Bone.RenderCmd); + proto.constructor = ccs.Bone.WebGLRenderCmd; +})(); diff --git a/extensions/cocostudio/armature/animation/CCArmatureAnimation.js b/extensions/cocostudio/armature/animation/CCArmatureAnimation.js new file mode 100644 index 00000000000..870ba7ed0f7 --- /dev/null +++ b/extensions/cocostudio/armature/animation/CCArmatureAnimation.js @@ -0,0 +1,669 @@ +/**************************************************************************** + 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); + } 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/extensions/cocostudio/armature/animation/CCProcessBase.js b/extensions/cocostudio/armature/animation/CCProcessBase.js new file mode 100644 index 00000000000..a4707d38543 --- /dev/null +++ b/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/extensions/cocostudio/armature/animation/CCTween.js b/extensions/cocostudio/armature/animation/CCTween.js new file mode 100644 index 00000000000..5706d1d5951 --- /dev/null +++ b/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/extensions/cocostudio/armature/datas/CCDatas.js b/extensions/cocostudio/armature/datas/CCDatas.js new file mode 100644 index 00000000000..79c0de812a5 --- /dev/null +++ b/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.Vec2} p + */ +ccs.ContourData.prototype.addVertex = function(p){ + //var v = new ccs.ContourVertex2(p.x, p.y); //ccs.ContourVertex2 is same as cc.Vec2, 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/extensions/cocostudio/armature/display/CCBatchNode.js b/extensions/cocostudio/armature/display/CCBatchNode.js new file mode 100644 index 00000000000..f20c2388509 --- /dev/null +++ b/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/extensions/cocostudio/armature/display/CCDecorativeDisplay.js b/extensions/cocostudio/armature/display/CCDecorativeDisplay.js new file mode 100644 index 00000000000..f7a7c3d4bf9 --- /dev/null +++ b/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/extensions/cocostudio/armature/display/CCDisplayFactory.js b/extensions/cocostudio/armature/display/CCDisplayFactory.js new file mode 100644 index 00000000000..da8f8601df8 --- /dev/null +++ b/extensions/cocostudio/armature/display/CCDisplayFactory.js @@ -0,0 +1,217 @@ +/**************************************************************************** + 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.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/extensions/cocostudio/armature/display/CCDisplayManager.js b/extensions/cocostudio/armature/display/CCDisplayManager.js new file mode 100644 index 00000000000..5c31dd15a30 --- /dev/null +++ b/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.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.Vec2|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); +}; \ No newline at end of file diff --git a/extensions/cocostudio/armature/display/CCSkin.js b/extensions/cocostudio/armature/display/CCSkin.js new file mode 100644 index 00000000000..4553cf568b4 --- /dev/null +++ b/extensions/cocostudio/armature/display/CCSkin.js @@ -0,0 +1,215 @@ +/**************************************************************************** + 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); + + var localTransform = this.getNodeToParentTransform ? this.getNodeToParentTransform() : this.nodeToParentTransform(); + var skinTransform = this._skinTransform; + skinTransform.a = localTransform.a; + skinTransform.b = localTransform.b; + skinTransform.c = localTransform.c; + skinTransform.d = localTransform.d; + skinTransform.tx = localTransform.tx; + skinTransform.ty = localTransform.ty; + this.updateArmatureTransform(); + }, + + /** + * 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.updateArmatureTransform(); + }, + + /** + * 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/extensions/cocostudio/armature/display/CCSkinCanvasRenderCmd.js b/extensions/cocostudio/armature/display/CCSkinCanvasRenderCmd.js new file mode 100644 index 00000000000..4cde024fc8e --- /dev/null +++ b/extensions/cocostudio/armature/display/CCSkinCanvasRenderCmd.js @@ -0,0 +1,58 @@ +/**************************************************************************** + 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 = { + updateArmatureTransform: function () { + var node = this._node; + this._transform = cc.affineTransformConcat( + node._skinTransform, + node.bone.getNodeToArmatureTransform() + ); + this._dirtyFlag = this._dirtyFlag & cc.Node._dirtyFlags.transformDirty ^ this._dirtyFlag; + }, + + 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){ + cc.Sprite.CanvasRenderCmd.call(this, renderable); + this._needDraw = true; + }; + + var proto = ccs.Skin.CanvasRenderCmd.prototype = Object.create(cc.Sprite.CanvasRenderCmd.prototype); + cc.js.mixin(proto, ccs.Skin.RenderCmd); + proto.constructor = ccs.Skin.CanvasRenderCmd; +})(); diff --git a/extensions/cocostudio/armature/display/CCSkinWebGLRenderCmd.js b/extensions/cocostudio/armature/display/CCSkinWebGLRenderCmd.js new file mode 100644 index 00000000000..f3d9e56f826 --- /dev/null +++ b/extensions/cocostudio/armature/display/CCSkinWebGLRenderCmd.js @@ -0,0 +1,146 @@ +/**************************************************************************** + 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.WebGLRenderCmd = function(renderable){ + cc.Sprite.WebGLRenderCmd.call(this, renderable); + }; + + var proto = ccs.Skin.WebGLRenderCmd.prototype = Object.create(cc.Sprite.WebGLRenderCmd.prototype); + cc.js.mixin(proto, ccs.Skin.RenderCmd); + proto.constructor = ccs.Skin.WebGLRenderCmd; + + proto.updateTransform = function(){ + var node = this._node; + var locQuad = this._quad; + // If it is not visible, or one of its ancestors is not visible, then do nothing: + if( !node._visible) + locQuad.br.vertices = locQuad.tl.vertices = locQuad.tr.vertices = locQuad.bl.vertices = {x: 0, y: 0, z: 0}; + else { + // + // calculate the Quad based on the Affine Matrix + // + var transform = this.getNodeToParentTransform(); //this._transform; + var size = node._rect; + + var x1 = node._offsetPosition.x, y1 = node._offsetPosition.y; + + var x2 = x1 + size.width, y2 = y1 + size.height; + var x = transform.tx, y = transform.ty; + + var cr = transform.a, sr = transform.b; + var cr2 = transform.d, sr2 = -transform.c; + var ax = x1 * cr - y1 * sr2 + x; + var ay = x1 * sr + y1 * cr2 + y; + + var bx = x2 * cr - y1 * sr2 + x; + var by = x2 * sr + y1 * cr2 + y; + + var cx = x2 * cr - y2 * sr2 + x; + var cy = x2 * sr + y2 * cr2 + y; + + var dx = x1 * cr - y2 * sr2 + x; + var dy = x1 * sr + y2 * cr2 + y; + + var locVertexZ = node._vertexZ; + if(!cc.SPRITEBATCHNODE_RENDER_SUBPIXEL) { + ax = 0 | ax; + ay = 0 | ay; + bx = 0 | bx; + by = 0 | by; + cx = 0 | cx; + cy = 0 | cy; + dx = 0 | dx; + dy = 0 | dy; + } + this.SET_VERTEX3F(locQuad.bl.vertices,ax, ay,locVertexZ); + this.SET_VERTEX3F(locQuad.br.vertices,bx, by,locVertexZ); + this.SET_VERTEX3F(locQuad.tl.vertices,dx, dy,locVertexZ); + this.SET_VERTEX3F(locQuad.tr.vertices,cx, cy,locVertexZ); + } + + // MARMALADE CHANGE: ADDED CHECK FOR nullptr, TO PERMIT SPRITES WITH NO BATCH NODE / TEXTURE ATLAS + if (node.textureAtlas) + node.textureAtlas.updateQuad(locQuad, node.textureAtlas.getTotalQuads()); + this._quadDirty = true; + }; + + proto.SET_VERTEX3F = function(_v_, _x_, _y_, _z_){ + (_v_).x = (_x_); + (_v_).y = (_y_); + (_v_).z = (_z_); + }; + + proto.rendering = function(ctx){ + var node = this._node; + if (!node._textureLoaded) + return; + + var gl = ctx || cc._renderContext, locTexture = node._texture; + if (locTexture && locTexture._textureLoaded) { + this._shaderProgram.use(); + this._shaderProgram.setUniformForModelViewAndProjectionMatrixWithMat4(); + + cc.glBlendFunc(node._blendFunc.src, node._blendFunc.dst); + //optimize performance for javascript + cc.glBindTexture2DN(0, locTexture); // = cc.glBindTexture2D(locTexture); + cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX); + + gl.bindBuffer(gl.ARRAY_BUFFER, this._quadWebBuffer); + if (this._quadDirty) { + gl.bufferData(gl.ARRAY_BUFFER, this._quad.arrayBuffer, gl.DYNAMIC_DRAW); + this._quadDirty = false; + } + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 24, 0); //cc.VERTEX_ATTRIB_POSITION + gl.vertexAttribPointer(1, 4, gl.UNSIGNED_BYTE, true, 24, 12); //cc.VERTEX_ATTRIB_COLOR + gl.vertexAttribPointer(2, 2, gl.FLOAT, false, 24, 16); //cc.VERTEX_ATTRIB_TEX_COORDS + + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + } + + cc.g_NumberOfDraws++; + if (cc.SPRITE_DEBUG_DRAW === 0 && !node._showNode) + return; + + if (cc.SPRITE_DEBUG_DRAW === 1 || node._showNode) { + // draw bounding box + var locQuad = this._quad; + var verticesG1 = [ + cc.p(locQuad.tl.vertices.x, locQuad.tl.vertices.y), + cc.p(locQuad.bl.vertices.x, locQuad.bl.vertices.y), + cc.p(locQuad.br.vertices.x, locQuad.br.vertices.y), + cc.p(locQuad.tr.vertices.x, locQuad.tr.vertices.y) + ]; + cc._drawingUtil.drawPoly(verticesG1, 4, true); + } else if (cc.SPRITE_DEBUG_DRAW === 2) { + // draw texture box + var drawRectG2 = node.getTextureRect(); + var offsetPixG2 = node.getOffsetPosition(); + var verticesG2 = [cc.p(offsetPixG2.x, offsetPixG2.y), cc.p(offsetPixG2.x + drawRectG2.width, offsetPixG2.y), + cc.p(offsetPixG2.x + drawRectG2.width, offsetPixG2.y + drawRectG2.height), cc.p(offsetPixG2.x, offsetPixG2.y + drawRectG2.height)]; + cc._drawingUtil.drawPoly(verticesG2, 4, true); + } // CC_SPRITE_DEBUG_DRAW + }; +})(); \ No newline at end of file diff --git a/extensions/cocostudio/armature/physics/CCColliderDetector.js b/extensions/cocostudio/armature/physics/CCColliderDetector.js new file mode 100644 index 00000000000..73e58374d6c --- /dev/null +++ b/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); +}; diff --git a/extensions/cocostudio/armature/utils/CCArmatureDataManager.js b/extensions/cocostudio/armature/utils/CCArmatureDataManager.js new file mode 100644 index 00000000000..89f06fb2d06 --- /dev/null +++ b/extensions/cocostudio/armature/utils/CCArmatureDataManager.js @@ -0,0 +1,325 @@ +/**************************************************************************** + 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 Relative data 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() { + this._animationDatas = {}; + this._armatureDatas = {}; + this._textureDatas = {}; + ccs.spriteFrameCacheHelper.clear(); + ccs.dataReaderHelper.clear(); + } +}; \ No newline at end of file diff --git a/extensions/cocostudio/armature/utils/CCArmatureDefine.js b/extensions/cocostudio/armature/utils/CCArmatureDefine.js new file mode 100644 index 00000000000..a7db1b92031 --- /dev/null +++ b/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/extensions/cocostudio/armature/utils/CCDataReaderHelper.js b/extensions/cocostudio/armature/utils/CCDataReaderHelper.js new file mode 100644 index 00000000000..77cd0333c98 --- /dev/null +++ b/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.js.array.remove(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"); + + 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.js.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); + } +}; diff --git a/extensions/cocostudio/armature/utils/CCSpriteFrameCacheHelper.js b/extensions/cocostudio/armature/utils/CCSpriteFrameCacheHelper.js new file mode 100644 index 00000000000..a0496e96a76 --- /dev/null +++ b/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/extensions/cocostudio/armature/utils/CCTransformHelp.js b/extensions/cocostudio/armature/utils/CCTransformHelp.js new file mode 100644 index 00000000000..0d6228d5cd6 --- /dev/null +++ b/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/extensions/cocostudio/armature/utils/CCTweenFunction.js b/extensions/cocostudio/armature/utils/CCTweenFunction.js new file mode 100644 index 00000000000..7ced7fad96f --- /dev/null +++ b/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/extensions/cocostudio/armature/utils/CCUtilMath.js b/extensions/cocostudio/armature/utils/CCUtilMath.js new file mode 100644 index 00000000000..082ec35b121 --- /dev/null +++ b/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/extensions/cocostudio/components/CCComAttribute.js b/extensions/cocostudio/components/CCComAttribute.js new file mode 100644 index 00000000000..303cd08ff65 --- /dev/null +++ b/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/extensions/cocostudio/components/CCComAudio.js b/extensions/cocostudio/components/CCComAudio.js new file mode 100644 index 00000000000..80bf953c5d1 --- /dev/null +++ b/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/extensions/cocostudio/components/CCComController.js b/extensions/cocostudio/components/CCComController.js new file mode 100644 index 00000000000..9fa5e82f610 --- /dev/null +++ b/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/extensions/cocostudio/components/CCComRender.js b/extensions/cocostudio/components/CCComRender.js new file mode 100644 index 00000000000..85343a383c6 --- /dev/null +++ b/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/extensions/cocostudio/components/CCComponent.js b/extensions/cocostudio/components/CCComponent.js new file mode 100644 index 00000000000..4adf508fb59 --- /dev/null +++ b/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/extensions/cocostudio/components/CCComponentContainer.js b/extensions/cocostudio/components/CCComponentContainer.js new file mode 100644 index 00000000000..20c8a882884 --- /dev/null +++ b/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/extensions/cocostudio/loader/load.js b/extensions/cocostudio/loader/load.js new file mode 100644 index 00000000000..05e3c0aac1e --- /dev/null +++ b/extensions/cocostudio/loader/load.js @@ -0,0 +1,274 @@ +/**************************************************************************** + 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 = {}; + +/** + * 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= 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(); + } + }; +})(); \ No newline at end of file diff --git a/extensions/cocostudio/loader/parsers/scene-1.x.js b/extensions/cocostudio/loader/parsers/scene-1.x.js new file mode 100644 index 00000000000..57f34c28a36 --- /dev/null +++ b/extensions/cocostudio/loader/parsers/scene-1.x.js @@ -0,0 +1,262 @@ +/**************************************************************************** + 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.js.isUndefined(json["x"]))?0:json["x"]; + var y = (cc.js.isUndefined(json["y"]))?0:json["y"]; + node.setPosition(x, y); + + var bVisible = Boolean((cc.js.isUndefined(json["visible"]))?1:json["visible"]); + node.setVisible(bVisible); + + var nTag = (cc.js.isUndefined(json["objecttag"]))?-1:json["objecttag"]; + node.setTag(nTag); + + var nZorder = (cc.js.isUndefined(json["zorder"]))?0:json["zorder"]; + node.setLocalZOrder(nZorder); + + var fScaleX = (cc.js.isUndefined(json["scalex"]))?1:json["scalex"]; + var fScaleY = (cc.js.isUndefined(json["scaley"]))?1:json["scaley"]; + node.setScaleX(fScaleX); + node.setScaleY(fScaleY); + + var fRotationZ = (cc.js.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/extensions/cocostudio/loader/parsers/timelineParser-1.x.js b/extensions/cocostudio/loader/parsers/timelineParser-1.x.js new file mode 100644 index 00000000000..c9c3efbab51 --- /dev/null +++ b/extensions/cocostudio/loader/parsers/timelineParser-1.x.js @@ -0,0 +1,292 @@ +/**************************************************************************** + 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", "*", parser); + +})(ccs._load, ccs._parser); diff --git a/extensions/cocostudio/loader/parsers/timelineParser-2.x.js b/extensions/cocostudio/loader/parsers/timelineParser-2.x.js new file mode 100644 index 00000000000..2d649372eac --- /dev/null +++ b/extensions/cocostudio/loader/parsers/timelineParser-2.x.js @@ -0,0 +1,1401 @@ +/**************************************************************************** + 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"]; + 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"]; + 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); + + return widget; + }; + + /** + * TextBMFont + * @param json + * @param resourcePath + * @returns {ccui.TextBMFont} + */ + parser.initTextBMFont = function(json, resourcePath){ + + var widget = new ccui.TextBMFont(); + this.widgetAttributes(widget, json); + + var text = json["LabelText"]; + widget.setString(text); + + 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); + }); + 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"] || 0, blendFunc["Dst"] || 0)); + + 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); + + +})(ccs._load, ccs._parser); diff --git a/extensions/cocostudio/loader/parsers/uiParser-1.x.js b/extensions/cocostudio/loader/parsers/uiParser-1.x.js new file mode 100644 index 00000000000..c14763e875b --- /dev/null +++ b/extensions/cocostudio/loader/parsers/uiParser-1.x.js @@ -0,0 +1,698 @@ +/**************************************************************************** + 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({ + + 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); + }, + + deferred: function(json, resourcePath, node, file){ + if(node){ + ccs.actionManager.initWithDictionary(file, json["animation"], node); + node.setContentSize(cc.size(json["designWidth"], json["designHeight"])); + } + } + + }); + var parser = new Parser(); + + + parser.generalAttributes = function(widget, options){ + var ignoreSizeExsit = options["ignoreSize"]; + if(ignoreSizeExsit != null) + widget.ignoreContentAdaptWithSize(ignoreSizeExsit); + + if (options["sizeType"]) + { + widget.setSizeType(options["sizeType"]); + } + + if (options["positionType"]) + { + widget.setPositionType(options["positionType"]); + } + + widget.setSizePercent(cc.p(options["sizePercentX"], options["sizePercentY"])); + widget.setPositionPercent(cc.p(options["positionPercentX"], options["positionPercentY"])); + + /* 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"]; + h = options["height"]; + } + widget.setContentSize(w, h); + + widget.setTag(options["tag"]); + widget.setActionTag(options["actiontag"]); + widget.setTouchEnabled(options["touchAble"]); + var name = options["name"]; + var widgetName = name ? name : "default"; + widget.setName(widgetName); + + var x = options["x"]; + var y = options["y"]; + widget.setPosition(x, y); + + var sx = options["scaleX"]!=null ? options["scaleX"] : 1; + widget.setScaleX(sx); + + var sy = options["scaleY"]!=null ? options["scaleY"] : 1; + widget.setScaleY(sy); + + var rt = options["rotation"] || 0; + widget.setRotation(rt); + + var vb = options["visible"] || false; + if(vb != null) + widget.setVisible(vb); + widget.setLocalZOrder(options["ZOrder"]); + + var layout = options["layoutParameter"]; + if(layout != null){ + var layoutParameterDic = options["layoutParameter"]; + var paramType = layoutParameterDic["type"]; + var parameter = null; + + switch(paramType){ + case 0: + break; + case 1: + parameter = new ccui.LinearLayoutParameter(); + var gravity = layoutParameterDic["gravity"]; + parameter.setGravity(gravity); + break; + case 2: + parameter = new ccui.RelativeLayoutParameter(); + var rParameter = parameter; + var relativeName = layoutParameterDic["relativeName"]; + rParameter.setRelativeName(relativeName); + var relativeToName = layoutParameterDic["relativeToName"]; + rParameter.setRelativeToWidgetName(relativeToName); + var align = layoutParameterDic["align"]; + rParameter.setAlign(align); + break; + default: + break; + } + if(parameter != null){ + var mgl = layoutParameterDic["marginLeft"]||0; + var mgt = layoutParameterDic["marginTop"]||0; + var mgr = layoutParameterDic["marginRight"]||0; + var mgb = layoutParameterDic["marginDown"]||0; + parameter.setMargin(mgl, mgt, mgr, mgb); + widget.setLayoutParameter(parameter); + } + } + }; + + parser.colorAttributes = function(widget, options){ + var op = options["opacity"]; + 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"]); + }; + + parser.anchorPointAttributes = function(widget, options){ + var isAnchorPointXExists = options["anchorPointX"]; + var anchorPointXInFile; + if (isAnchorPointXExists != null) + anchorPointXInFile = options["anchorPointX"]; + else + anchorPointXInFile = widget.getAnchorPoint().x; + + var isAnchorPointYExists = options["anchorPointY"]; + var anchorPointYInFile; + if (isAnchorPointYExists != null) + anchorPointYInFile = options["anchorPointY"]; + else + anchorPointYInFile = widget.getAnchorPoint().y; + + if (isAnchorPointXExists != null || isAnchorPointYExists != null) + widget.setAnchorPoint(cc.p(anchorPointXInFile, anchorPointYInFile)); + }; + + parser.parseChild = function(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) + widget.addPage(child); + else { + if(widget instanceof ccui.ListView){ + widget.pushBackCustomItem(child); + } else { + if(!(widget instanceof ccui.Layout)) { + 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)); + } + var AnchorPointIn = widget.getAnchorPointInPoints(); + child.setPosition(cc.p(child.getPositionX() + AnchorPointIn.x, child.getPositionY() + AnchorPointIn.y)); + } + widget.addChild(child); + } + } + } + } + }; + + 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"]; + h = options["height"]; + } + widget.setSize(cc.size(w, h)); + + widget.setClippingEnabled(options["clipAble"]); + + var backGroundScale9Enable = options["backGroundScale9Enable"]; + widget.setBackGroundImageScale9Enabled(backGroundScale9Enable); + var cr = options["bgColorR"]; + var cg = options["bgColorG"]; + var cb = options["bgColorB"]; + + var scr = options["bgStartColorR"]; + var scg = options["bgStartColorG"]; + var scb = options["bgStartColorB"]; + + var ecr = options["bgEndColorR"]; + var ecg = options["bgEndColorG"]; + var ecb = options["bgEndColorB"]; + + var bgcv1 = options["vectorX"]; + var bgcv2 = options["vectorY"]; + widget.setBackGroundColorVector(cc.p(bgcv1, bgcv2)); + + var co = options["bgColorOpacity"]; + + var colorType = options["colorType"]; + 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"], imageFileNameDic["path"], function(path, type){ + widget.setBackGroundImage(path, type); + }); + } + + if (backGroundScale9Enable){ + var cx = options["capInsetsX"]; + var cy = options["capInsetsY"]; + var cw = options["capInsetsWidth"]; + var ch = 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"], normalDic["path"], function(path, type){ + button.loadTextureNormal(path, type); + }); + var pressedDic = options["pressedData"]; + getPath(resourcePath, pressedDic["resourceType"], pressedDic["path"], function(path, type){ + button.loadTexturePressed(path, type); + }); + var disabledDic = options["disabledData"]; + getPath(resourcePath, disabledDic["resourceType"], disabledDic["path"], function(path, type){ + button.loadTextureDisabled(path, type); + }); + if (scale9Enable) { + var cx = options["capInsetsX"]; + var cy = options["capInsetsY"]; + var cw = options["capInsetsWidth"]; + var ch = options["capInsetsHeight"]; + + button.setCapInsets(cc.rect(cx, cy, cw, ch)); + var sw = options["scale9Width"]; + var sh = options["scale9Height"]; + if (sw != null && sh != null) + button.setSize(cc.size(sw, sh)); + } + var text = options["text"]; + if (text != null) + 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"], 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"], 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"], 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"], 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"]; + var sh = options["scale9Height"]; + if (sw && sh) + { + var swf = options["scale9Width"]; + var shf = options["scale9Height"]; + widget.setSize(cc.size(swf, shf)); + } + + var cx = options["capInsetsX"]; + var cy = options["capInsetsY"]; + var cw = options["capInsetsWidth"]; + var ch = 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"]; + 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"]; + 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"]; + 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"]; + var ah = options["areaHeight"]; + if (aw != null && ah != null){ + var size = cc.size(options["areaWidth"], options["areaHeight"]); + widget.setTextAreaSize(size); + } + var ha = options["hAlignment"]; + if (ha != null){ + widget.setTextHorizontalAlignment(options["hAlignment"]); + } + var va = options["vAlignment"]; + if (va != null){ + widget.setTextVerticalAlignment(options["vAlignment"]); + } + }; + /** + * ListView parser (UIListView) + */ + parser.ListViewAttributes = function(widget, options, resoutcePath){ + parser.ScrollViewAttributes(widget, options,resoutcePath); + var direction = options["direction"]; + widget.setDirection(direction); + var gravity = options["gravity"]; + widget.setGravity(gravity); + var itemMargin = options["itemMargin"]; + widget.setItemsMargin(itemMargin); + }; + /** + * LoadingBar parser (UILoadingBar) + */ + parser.LoadingBarAttributes = function(widget, options, resourcePath){ + var imageFileNameDic = options["textureData"]; + getPath(resourcePath, imageFileNameDic["resourceType"], imageFileNameDic["path"], function(path, type){ + widget.loadTexture(path, type); + }); + + var scale9Enable = options["scale9Enable"]; + widget.setScale9Enabled(scale9Enable); + + if (scale9Enable){ + var cx = options["capInsetsX"]; + var cy = options["capInsetsY"]; + var cw = options["capInsetsWidth"]; + var ch = options["capInsetsHeight"]; + + widget.setCapInsets(cc.rect(cx, cy, cw, ch)); + + var width = options["width"]; + var height = options["height"]; + widget.setSize(cc.size(width, height)); + } + + widget.setDirection(options["direction"]); + widget.setPercent(options["percent"]); + }; + /** + * 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"]; + 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"], 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"], disabledDic["path"], function(path, type){ + slider.loadSlidBallTextureDisabled(path, type); + }); + + var progressBarDic = options["progressBarData"]; + getPath(resourcePath, progressBarDic["resourceType"], 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["fontSize1"]; + 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"]; + var tsh = options["touchSizeHeight"]; + if(tsw!=null && tsh!=null) + widget.setTouchSize(tsw, tsh); + + var dw = options["width"]; + var dh = options["height"]; + 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"]; + var ah = options["areaHeight"]; + if(aw && ah){ + var size = cc.size(aw, ah); + widget.setTextAreaSize(size); + } + var ha = options["hAlignment"]; + if(ha) + widget.setTextHorizontalAlignment(ha); + var va = options["vAlignment"]; + if(va) + widget.setTextVerticalAlignment(va); + }; + + var register = [ + {name: "Panel", object: ccui.Layout, handle: parser.LayoutAttributes}, + {name: "Button", object: ccui.Button, handle: parser.ButtonAttributes}, + {name: "CheckBox", object: ccui.CheckBox, handle: parser.CheckBoxAttributes}, + {name: "ImageView", object: ccui.ImageView, handle: parser.ImageViewAttributes}, + {name: "LabelAtlas", object: ccui.TextAtlas, handle: parser.TextAtlasAttributes}, + {name: "LabelBMFont", object: ccui.TextBMFont, handle: parser.TextBMFontAttributes}, + {name: "Label", object: ccui.Text, handle: parser.TextAttributes}, + {name: "ListView", object: ccui.ListView, handle: parser.ListViewAttributes}, + {name: "LoadingBar", object: ccui.LoadingBar, handle: parser.LoadingBarAttributes}, + {name: "PageView", object: ccui.PageView, handle: parser.PageViewAttributes}, + {name: "ScrollView", object: ccui.ScrollView, handle: parser.ScrollViewAttributes}, + {name: "Slider", object: ccui.Slider, handle: parser.SliderAttributes}, + {name: "TextField", object: ccui.TextField, handle: parser.TextFieldAttributes} + ]; + + register.forEach(function(item){ + parser.registerParser(item.name, function(options, resourcePath){ + var widget = new item.object; + var uiOptions = options["options"]; + parser.generalAttributes(widget, uiOptions); + item.handle(widget, uiOptions, resourcePath); + parser.colorAttributes(widget, uiOptions); + parser.anchorPointAttributes(widget, uiOptions); + parser.parseChild.call(this, widget, options, resourcePath); + return widget; + }); + }); + + load.registerParser("ccui", "*", parser); + +})(ccs._load, ccs._parser); \ No newline at end of file diff --git a/extensions/cocostudio/timeline/ActionTimeline.js b/extensions/cocostudio/timeline/ActionTimeline.js new file mode 100644 index 00000000000..5467f9198b1 --- /dev/null +++ b/extensions/cocostudio/timeline/ActionTimeline.js @@ -0,0 +1,541 @@ +/**************************************************************************** + 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= 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.js.array.remove(this._timelineMap[tag], timeline); + cc.js.array.remove(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 = 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 _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(); + }, + + 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.js.array.remove(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._renderCmd.visit(cmd); + else + break; + } + for (; i < len; i++) + children[i]._renderCmd.visit(cmd); + } + 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.js.array.remove(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 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= 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/extensions/cocostudio/trigger/ObjectFactory.js b/extensions/cocostudio/trigger/ObjectFactory.js new file mode 100644 index 00000000000..29c21c8c8d2 --- /dev/null +++ b/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.js.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/extensions/cocostudio/trigger/TriggerBase.js b/extensions/cocostudio/trigger/TriggerBase.js new file mode 100644 index 00000000000..4c773e44768 --- /dev/null +++ b/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/extensions/cocostudio/trigger/TriggerMng.js b/extensions/cocostudio/trigger/TriggerMng.js new file mode 100644 index 00000000000..e0167a1ab69 --- /dev/null +++ b/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/extensions/cocostudio/trigger/TriggerObj.js b/extensions/cocostudio/trigger/TriggerObj.js new file mode 100644 index 00000000000..6a8e3c1226a --- /dev/null +++ b/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/extensions/editbox/CCEditBox.js b/extensions/editbox/CCEditBox.js new file mode 100644 index 00000000000..bc4678ea242 --- /dev/null +++ b/extensions/editbox/CCEditBox.js @@ -0,0 +1,688 @@ +/**************************************************************************** + Copyright (c) 2011-2012 cocos2d-x.org + Copyright (c) 2013-2014 Chukong Technologies Inc. + Copyright (c) 2012 James Chen + + 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 keyboard return types + * @readonly + * @enum {number} + */ +cc.KeyboardReturnType = cc.Enum({ + DEFAULT: 0, + DONE: 1, + SEND: 2, + SEARCH: 3, + GO: 4 +}); + +/** + * The EditBox's InputMode defines the type of text that the user is allowed to enter + * @readonly + * @enum {number} + * @memberof cc.EditBox + */ +var InputMode = cc.Enum({ + + ANY: 0, + + /** + * The user is allowed to enter an e-mail address. + */ + EMAILADDR: 1, + + /** + * The user is allowed to enter an integer value. + */ + NUMERIC: 2, + + /** + * The user is allowed to enter a phone number. + */ + PHONENUMBER: 3, + + /** + * The user is allowed to enter a URL. + */ + URL: 4, + + /** + * The user is allowed to enter a real number value. + * This extends kEditBoxInputModeNumeric by allowing a decimal point. + */ + DECIMAL: 5, + + /** + * The user is allowed to enter any text, except for line breaks. + */ + SINGLELINE: 6 +}); + +/** + * Enum for the EditBox's input flags + * @readonly + * @enum {number} + * @memberof cc.EditBox + */ +var InputFlag = cc.Enum({ + /** + * Indicates that the text entered is confidential data that should be + * obscured whenever possible. This implies EDIT_BOX_INPUT_FLAG_SENSITIVE. + */ + 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. + */ + SENSITIVE: 1, + + /** + * This flag is a hint to the implementation that during text editing, + * the initial letter of each word should be capitalized. + */ + 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. + */ + INITIAL_CAPS_SENTENCE: 3, + + /** + * Capitalize all characters automatically. + */ + 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.ControlButton + * + * @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 {cc.EditBox.InputFlag} inputFlag - <@writeonly> Input flag of edit box, one of the cc.EditBox.InputFlag constants. e.g.cc.EditBox.InputFlag..PASSWORD + * @property {Object} delegate - <@writeonly> Delegate of edit box + * @property {cc.EditBox.InputMode} inputMode - <@writeonly> Input mode of the edit box. Value should be one of the cc.EditBox.InputMode constants. + * @property {Number} returnType - <@writeonly> Return type of edit box, value should be one of the KeyboardReturnType constants. + * + */ +cc.EditBox = cc.ControlButton.extend({ + _domInputSprite: null, + + _delegate: null, + _editBoxInputMode: InputMode.ANY, + _editBoxInputFlag: InputFlag.SENSITIVE, + _keyboardReturnType: cc.KeyboardReturnType.DEFAULT, + + _text: "", + _placeholderText: "", + _textColor: null, + _placeholderColor: null, + _maxLength: 50, + _adjustHeight: 18, + + _edTxt: null, + _edFontSize: 14, + _edFontName: "Arial", + + _placeholderFontName: "", + _placeholderFontSize: 14, + + _tooltip: false, + _className: "EditBox", + + /** + * constructor of cc.EditBox + * @param {cc.Size} size + * @param {cc.Scale9Sprite} normal9SpriteBg + * @param {cc.Scale9Sprite} press9SpriteBg + * @param {cc.Scale9Sprite} disabled9SpriteBg + */ + ctor: function (size, normal9SpriteBg, press9SpriteBg, disabled9SpriteBg) { + cc.ControlButton.prototype.ctor.call(this); + + this._textColor = cc.Color.WHITE; + this._placeholderColor = cc.Color.GRAY; + this.setContentSize(size); + var tmpDOMSprite = this._domInputSprite = new cc.Sprite(); + tmpDOMSprite.draw = function () {}; //redefine draw function + this.addChild(tmpDOMSprite); + var selfPointer = this; + var tmpEdTxt = this._edTxt = document.createElement("input"); + tmpEdTxt.type = "text"; + tmpEdTxt.style.fontSize = this._edFontSize + "px"; + tmpEdTxt.style.color = "#000000"; + tmpEdTxt.style.border = 0; + tmpEdTxt.style.background = "transparent"; + //tmpEdTxt.style.paddingLeft = "2px"; + tmpEdTxt.style.width = "100%"; + tmpEdTxt.style.height = "100%"; + tmpEdTxt.style.active = 0; + tmpEdTxt.style.outline = "medium"; + tmpEdTxt.style.padding = "0"; + var onCanvasClick = function() { tmpEdTxt.blur();}; + + // TODO the event listener will be remove when EditBox removes from parent. + tmpEdTxt.addEventListener("input", function () { + if (selfPointer._delegate && selfPointer._delegate.editBoxTextChanged) + selfPointer._delegate.editBoxTextChanged(selfPointer, this.value); + }); + tmpEdTxt.addEventListener("keypress", function (e) { + if (e.keyCode === cc.KEY.enter) { + e.stopPropagation(); + e.preventDefault(); + if (selfPointer._delegate && selfPointer._delegate.editBoxReturn) + selfPointer._delegate.editBoxReturn(selfPointer); + cc._canvas.focus(); + } + }); + tmpEdTxt.addEventListener("focus", function () { + if (this.value === selfPointer._placeholderText) { + this.value = ""; + this.style.fontSize = selfPointer._edFontSize + "px"; + this.style.color = cc.colorToHex(selfPointer._textColor); + if (selfPointer._editBoxInputFlag === InputFlag.PASSWORD) + selfPointer._edTxt.type = "password"; + else + selfPointer._edTxt.type = "text"; + } + if (selfPointer._delegate && selfPointer._delegate.editBoxEditingDidBegin) + selfPointer._delegate.editBoxEditingDidBegin(selfPointer); + cc._canvas.addEventListener("click", onCanvasClick); + }); + tmpEdTxt.addEventListener("blur", function () { + if (this.value === "") { + this.value = selfPointer._placeholderText; + this.style.fontSize = selfPointer._placeholderFontSize + "px"; + this.style.color = cc.colorToHex(selfPointer._placeholderColor); + selfPointer._edTxt.type = "text"; + } + if (selfPointer._delegate && selfPointer._delegate.editBoxEditingDidEnd) + selfPointer._delegate.editBoxEditingDidEnd(selfPointer); + cc._canvas.removeEventListener('click', onCanvasClick); + }); + + cc.DOM.convert(tmpDOMSprite); + tmpDOMSprite.dom.appendChild(tmpEdTxt); + tmpDOMSprite.dom.showTooltipDiv = false; + tmpDOMSprite.dom.style.width = (size.width - 6) + "px"; + tmpDOMSprite.dom.style.height = (size.height - 6) + "px"; + + //this._domInputSprite.dom.style.borderWidth = "1px"; + //this._domInputSprite.dom.style.borderStyle = "solid"; + //this._domInputSprite.dom.style.borderRadius = "8px"; + tmpDOMSprite.canvas.remove(); + + if (this.initWithSizeAndBackgroundSprite(size, normal9SpriteBg)) { + if (press9SpriteBg) + this.setBackgroundSpriteForState(press9SpriteBg, cc.CONTROL_STATE_HIGHLIGHTED); + if (disabled9SpriteBg) + this.setBackgroundSpriteForState(disabled9SpriteBg, cc.CONTROL_STATE_DISABLED); + } + }, + + /** + * Set the font. + * @param {String} fontName The font name. + * @param {Number} fontSize The font size. + */ + setFont: function (fontName, fontSize) { + this._edFontSize = fontSize; + this._edFontName = fontName; + this._setFontToEditBox(); + }, + + _setFont: function (fontStyle) { + var res = cc.LabelTTF._fontStyleRE.exec(fontStyle); + if (res) { + this._edFontSize = parseInt(res[1]); + this._edFontName = res[2]; + this._setFontToEditBox(); + } + }, + + /** + * set fontName + * @param {String} fontName + */ + setFontName: function (fontName) { + this._edFontName = fontName; + this._setFontToEditBox(); + }, + + /** + * set fontSize + * @param {Number} fontSize + */ + setFontSize: function (fontSize) { + this._edFontSize = fontSize; + this._setFontToEditBox(); + }, + + _setFontToEditBox: function () { + if (this._edTxt.value !== this._placeholderText) { + this._edTxt.style.fontFamily = this._edFontName; + this._edTxt.style.fontSize = this._edFontSize + "px"; + if (this._editBoxInputFlag === InputFlag.PASSWORD) + this._edTxt.type = "password"; + else + this._edTxt.type = "text"; + } + }, + + /** + * Set the text entered in the edit box. + * @deprecated + * @param {string} text The given text. + */ + setText: function (text) { + cc.log("Please use the setString"); + this.setString(text); + }, + + /** + * Set the text entered in the edit box. + * @param {string} text The given text. + */ + setString: function (text) { + if (text != null) { + if (text === "") { + this._edTxt.value = this._placeholderText; + this._edTxt.style.color = cc.colorToHex(this._placeholderColor); + this._edTxt.type = "text"; + } else { + this._edTxt.value = text; + this._edTxt.style.color = cc.colorToHex(this._textColor); + if (this._editBoxInputFlag === InputFlag.PASSWORD) + this._edTxt.type = "password"; + else + this._edTxt.type = "text"; + } + } + }, + + /** + * Set the font color of the widget's text. + * @param {cc.Color} color + */ + setFontColor: function (color) { + this._textColor = color; + if (this._edTxt.value !== this._placeholderText) { + this._edTxt.style.color = cc.colorToHex(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) && maxLength > 0) { + this._maxLength = maxLength; + this._edTxt.maxLength = maxLength; + } + }, + + /** + * Gets the maximum input length of the edit box. + * @return {Number} Maximum input length. + */ + getMaxLength: function () { + return this._maxLength; + }, + + /** + * Set 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) { + var oldPlaceholderText = this._placeholderText; + this._placeholderText = text; + if (this._edTxt.value === oldPlaceholderText) { + this._edTxt.value = text; + this._edTxt.style.color = cc.colorToHex(this._placeholderColor); + this._setPlaceholderFontToEditText(); + } + } + }, + + /** + * Set the placeholder's font. + * @param {String} fontName + * @param {Number} fontSize + */ + setPlaceholderFont: function (fontName, fontSize) { + this._placeholderFontName = fontName; + this._placeholderFontSize = fontSize; + this._setPlaceholderFontToEditText(); + }, + _setPlaceholderFont: function (fontStyle) { + var res = cc.LabelTTF._fontStyleRE.exec(fontStyle); + if (res) { + this._placeholderFontName = res[2]; + this._placeholderFontSize = parseInt(res[1]); + this._setPlaceholderFontToEditText(); + } + }, + + /** + * Set the placeholder's fontName. + * @param {String} fontName + */ + setPlaceholderFontName: function (fontName) { + this._placeholderFontName = fontName; + this._setPlaceholderFontToEditText(); + }, + + /** + * Set the placeholder's fontSize. + * @param {Number} fontSize + */ + setPlaceholderFontSize: function (fontSize) { + this._placeholderFontSize = fontSize; + this._setPlaceholderFontToEditText(); + }, + + _setPlaceholderFontToEditText: function () { + if (this._edTxt.value === this._placeholderText) { + this._edTxt.style.fontFamily = this._placeholderFontName; + this._edTxt.style.fontSize = this._placeholderFontSize + "px"; + this._edTxt.type = "text"; + } + }, + + /** + * Set the font color of the placeholder text when the edit box is empty. + * @param {cc.Color} color + */ + setPlaceholderFontColor: function (color) { + this._placeholderColor = color; + if (this._edTxt.value === this._placeholderText) { + this._edTxt.style.color = cc.colorToHex(color); + } + }, + + /** + * Set the input flags that are to be applied to the edit box. + * @param {cc.EditBox.InputFlag} inputFlag - One of the cc.EditBox.InputFlag constants. + * e.g.cc.EditBox.InputFlag..PASSWORD + */ + setInputFlag: function (inputFlag) { + this._editBoxInputFlag = inputFlag; + if ((this._edTxt.value !== this._placeholderText) && (inputFlag === InputFlag.PASSWORD)) + this._edTxt.type = "password"; + else + this._edTxt.type = "text"; + }, + + /** + * Gets the input string of the edit box. + * @deprecated + * @return {string} + */ + getText: function () { + cc.log("Please use the getString"); + return this._edTxt.value; + }, + + /** + * Gets the input string of the edit box. + * @return {string} + */ + getString: function () { + if(this._edTxt.value === this._placeholderText) + return ""; + return this._edTxt.value; + }, + + /** + * Init edit box with specified size. + * @param {cc.Size} size + * @param {cc.Color | cc.Scale9Sprite} normal9SpriteBg + */ + initWithSizeAndBackgroundSprite: function (size, normal9SpriteBg) { + if (this.initWithBackgroundSprite(normal9SpriteBg)) { + this._domInputSprite.x = 3; + this._domInputSprite.y = 3; + + this.setZoomOnTouchDown(false); + this.setPreferredSize(size); + this.x = 0; + this.y = 0; + this._addTargetWithActionForControlEvent(this, this.touchDownAction, cc.CONTROL_EVENT_TOUCH_UP_INSIDE); + return true; + } + return false; + }, + + /* override functions */ + /** + * Set the delegate for edit box. + * @param {cc.EditBoxDelegate} delegate + */ + setDelegate: function (delegate) { + this._delegate = delegate; + }, + + /** + * Get a text in the edit box that acts as a placeholder when an + * edit box is empty. + * @return {String} + */ + getPlaceHolder: function () { + return this._placeholderText; + }, + + /** + * Set the input mode of the edit box. + * @param {Number} inputMode One of the EditBoxInputMode constants. + */ + setInputMode: function (inputMode) { + this._editBoxInputMode = inputMode; + }, + + /** + * Set 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; + }, + + keyboardWillShow: function (info) { + var rectTracked = cc.EditBox.getRect(this); + // some adjustment for margin between the keyboard and the edit box. + rectTracked.y -= 4; + // if the keyboard area doesn't intersect with the tracking node area, nothing needs to be done. + if (!rectTracked.intersectsRect(info.end)) { + cc.log("needn't to adjust view layout."); + return; + } + + // assume keyboard at the bottom of screen, calculate the vertical adjustment. + this._adjustHeight = info.end.getMaxY() - rectTracked.getMinY(); + // CCLOG("CCEditBox:needAdjustVerticalPosition(%f)", m_fAdjustHeight); + + //callback + }, + keyboardDidShow: function (info) { + }, + keyboardWillHide: function (info) { + //if (m_pEditBoxImpl != NULL) { + // m_pEditBoxImpl->doAnimationWhenKeyboardMove(info.duration, -m_fAdjustHeight); + //} + }, + keyboardDidHide: function (info) { + }, + + touchDownAction: function (sender, controlEvent) { + //this._editBoxImpl.openKeyboard(); + }, + + /** + * @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; + +/** + * get the rect of a node in world coordinate frame + * @function + * @param {cc.Node} node + * @return {cc.Rect} + */ +cc.EditBox.getRect = function (node) { + var contentSize = node.getContentSize(); + var rect = cc.rect(0, 0, contentSize.width, contentSize.height); + return cc.rectApplyAffineTransform(rect, node.getNodeToWorldTransform()); +}; + +/** + * 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); +}; + +cc.EditBox.InputMode = InputMode; +cc.EditBox.InputFlag = InputFlag; diff --git a/extensions/editbox/CCdomNode.js b/extensions/editbox/CCdomNode.js new file mode 100644 index 00000000000..7bce23c2683 --- /dev/null +++ b/extensions/editbox/CCdomNode.js @@ -0,0 +1,657 @@ +/**************************************************************************** + 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 DOM object + * @namespace + * @name cc.DOM + */ +cc.DOM = {}; + +/** + * @function + * @private + * @param node + */ +cc.DOM._addMethods = function (node) { + for (var funcs in cc.DOM.methods) { + node[funcs] = cc.DOM.methods[funcs]; + } + + // Redefine getter setter + cc.defineGetterSetter(node, "x", node.getPositionX, node.setPositionX); + cc.defineGetterSetter(node, "y", node.getPositionY, node.setPositionY); + cc.defineGetterSetter(node, "width", node._getWidth, node._setWidth); + cc.defineGetterSetter(node, "height", node._getHeight, node._setHeight); + cc.defineGetterSetter(node, "anchorX", node._getAnchorX, node._setAnchorX); + cc.defineGetterSetter(node, "anchorY", node._getAnchorY, node._setAnchorY); + cc.defineGetterSetter(node, "scale", node.getScale, node.setScale); + cc.defineGetterSetter(node, "scaleX", node.getScaleX, node.setScaleX); + cc.defineGetterSetter(node, "scaleY", node.getScaleY, node.setScaleY); + cc.defineGetterSetter(node, "rotation", node.getRotation, node.setRotation); + cc.defineGetterSetter(node, "skewX", node.getSkewX, node.setSkewX); + cc.defineGetterSetter(node, "skewY", node.getSkewY, node.setSkewY); + cc.defineGetterSetter(node, "visible", node.isVisible, node.setVisible); + cc.defineGetterSetter(node, "parent", node.getParent, node.setParent); + cc.defineGetterSetter(node, "opacity", node.getOpacity, node.setOpacity); +}; +cc.DOM.methods = /** @lends cc.DOM# */{ + /** + * Replace the set position of ccNode + * @param {cc.Vec2|Number} x + * @param {Number} y + */ + setPosition:function (x, y) { + if (y === undefined) { + this._position.x = x.x; + this._position.y = x.y; + } else { + this._position.x = x; + this._position.y = y; + } + this.setNodeDirty(); + this.dom.translates(this._position.x, -this._position.y); + }, + /** + * replace set Position Y of ccNode + * @param {Number} y + */ + setPositionY:function (y) { + this._position.y = y; + this.setNodeDirty(); + this.dom.translates(this._position.x, -this._position.y); + }, + + /** + * replace set Position X of ccNode + * @param {Number} x + */ + setPositionX:function (x) { + this._position.x = x; + this.setNodeDirty(); + this.dom.translates(this._position.x, -this._position.y); + }, + + /** + * replace set Scale of ccNode + * @param {object|Number} scale + * @param {Number} scaleY + */ + setScale:function (scale, scaleY) { + //save dirty region when before change + //this._addDirtyRegionToDirector(this.getBoundingBoxToWorld()); + + this._scaleX = scale; + this._scaleY = scaleY || scale; + + //save dirty region when after changed + //this._addDirtyRegionToDirector(this.getBoundingBoxToWorld()); + this.setNodeDirty(); + this.dom.resize(this._scaleX, this._scaleY); + }, + + /** + * replace set Scale X of ccNode + * @param {Number} x + */ + setScaleX:function (x) { + this._scaleX = x; + this.setNodeDirty(); + this.dom.resize(this._scaleX, this._scaleY); + }, + + /** + * replace set Scale Y of ccNode + * @param {Number} y + */ + setScaleY:function (y) { + this._scaleY = y; + this.setNodeDirty(); + this.dom.resize(this._scaleX, this._scaleY); + }, + + /** + * replace set anchorpoint of ccNode + * @param {cc.Vec2|Number} point The anchor point of node or The anchor point.x of node. + * @param {Number} [y] The anchor point.y of node. + */ + setAnchorPoint:function (point, y) { + var cmd = this._renderCmd; + + var locAnchorPoint = this._anchorPoint; + if (y === undefined) { + locAnchorPoint.x = point.x; + locAnchorPoint.y = point.y; + } else { + locAnchorPoint.x = point; + locAnchorPoint.y = y; + } + var locAPP = cmd._anchorPointInPoints, locSize = this._contentSize; + locAPP.x = locSize.width * locAnchorPoint.x; + locAPP.y = locSize.height * locAnchorPoint.y; + + this.dom.style[cc.$.pfx + 'TransformOrigin'] = '' + locAPP.x + 'px ' + -locAPP.y + 'px'; + if (this.ignoreAnchor) { + this.dom.style.marginLeft = 0; + this.dom.style.marginBottom = 0; + } else { + this.dom.style.marginLeft = (this.isToggler) ? 0 : -locAPP.x + 'px'; + this.dom.style.marginBottom = -locAPP.y + 'px'; + } + this.setNodeDirty(); + }, + + /** + * replace set anchorpoint x of ccNode + * @param {Number} x The anchor x of node. + */ + _setAnchorX:function (x) { + var locAnchorPoint = this._anchorPoint; + var cmd = this._renderCmd; + + if (x === locAnchorPoint.x) + return; + locAnchorPoint.x = x; + + var locAPP = cmd._anchorPointInPoints, locSize = this._contentSize; + locAPP.x = locSize.width * locAnchorPoint.x; + + this.dom.style[cc.$.pfx + 'TransformOrigin'] = '' + locAPP.x + 'px ' + -locAPP.y + 'px'; + if (this.ignoreAnchor) { + this.dom.style.marginLeft = 0; + this.dom.style.marginBottom = 0; + } else { + this.dom.style.marginLeft = (this.isToggler) ? 0 : -locAPP.x + 'px'; + } + this.setNodeDirty(); + }, + + /** + * replace set anchorpoint y of ccNode + * @param {Number} y The anchor y of node. + */ + _setAnchorY:function (y) { + var locAnchorPoint = this._anchorPoint; + var cmd = this._renderCmd; + + if (y === locAnchorPoint.y) + return; + locAnchorPoint.y = y; + + var locAPP = cmd._anchorPointInPoints, locSize = this._contentSize; + locAPP.y = locSize.height * locAnchorPoint.y; + + this.dom.style[cc.$.pfx + 'TransformOrigin'] = '' + locAPP.x + 'px ' + -locAPP.y + 'px'; + if (this.ignoreAnchor) { + this.dom.style.marginLeft = 0; + this.dom.style.marginBottom = 0; + } else { + this.dom.style.marginBottom = -locAPP.y + 'px'; + } + this.setNodeDirty(); + }, + + /** + * replace set ContentSize of ccNode + * @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 cmd = this._renderCmd; + + var locContentSize = this._contentSize; + if (height === undefined) { + locContentSize.width = size.width; + locContentSize.height = size.height; + } else { + locContentSize.width = size; + locContentSize.height = height; + } + var locAPP = cmd._anchorPointInPoints, locAnchorPoint = this._anchorPoint; + locAPP.x = locContentSize.width * locAnchorPoint.x; + locAPP.y = locContentSize.height * locAnchorPoint.y; + this.dom.width = locContentSize.width; + this.dom.height = locContentSize.height; + this.setAnchorPoint(this.getAnchorPoint()); + if (this.canvas) { + this.canvas.width = locContentSize.width; + this.canvas.height = locContentSize.height; + } + this.setNodeDirty(); + this.redraw(); + }, + + /** + * replace set width of ccNode + * @param {Number} width The untransformed size's width of the node. + */ + _setWidth:function (width) { + var locContentSize = this._contentSize; + var cmd = this._renderCmd; + if (width === locContentSize.width) + return; + locContentSize.width = width; + + var locAPP = cmd._anchorPointInPoints, locAnchorPoint = this._anchorPoint; + locAPP.x = locContentSize.width * locAnchorPoint.x; + this.dom.width = locContentSize.width; + this.anchorX = locAnchorPoint.x; + if (this.canvas) { + this.canvas.width = locContentSize.width; + } + this.setNodeDirty(); + this.redraw(); + }, + + /** + * replace set height of ccNode + * @param {Number} height The untransformed size's height of the node. + */ + _setHeight:function (height) { + var locContentSize = this._contentSize; + var cmd = this._renderCmd; + if (height === locContentSize.height) + return; + locContentSize.height = height; + + var locAPP = cmd._anchorPointInPoints, locAnchorPoint = this._anchorPoint; + locAPP.y = locContentSize.height * locAnchorPoint.y; + this.dom.height = locContentSize.height; + this.anchorY = locAnchorPoint.y; + if (this.canvas) { + this.canvas.height = locContentSize.height; + } + this.setNodeDirty(); + this.redraw(); + }, + + /** + * replace set Rotation of ccNode + * @param {Number} newRotation + */ + setRotation:function (newRotation) { + if (this._rotation === newRotation) + return; + + this._rotationX = this._rotationY = newRotation; + this.setNodeDirty(); + this.dom.rotate(newRotation); + }, + + /** + * replace set SkewX of ccNode + * @param {Number} x + */ + setSkewX:function (x) { + this._skewX = x; + this.setNodeDirty(); + this.dom.setSkew(this._skewX, this._skewY); + }, + + /** + * replace set SkewY of ccNode + * @param {Number} y + */ + setSkewY:function (y) { + this._skewY = y; + this.setNodeDirty(); + this.dom.setSkew(this._skewX, this._skewY); + }, + + /** + * replace set Visible of ccNode + * @param {Boolean} x + */ + setVisible:function (x) { + this._visible = x; + this.setNodeDirty(); + if (this.dom) + this.dom.style.display = (x) ? 'block' : 'none'; + }, + _setLocalZOrder:function (z) { + this._localZOrder = z + this.setNodeDirty(); + if (this.dom) + this.dom.zIndex = z; + }, + + /** + * replace set Parent of ccNode + * @param {cc.Node} p + */ + setParent:function (p) { + this._parent = p; + + if (p !== null) { + p.setAnchorPoint(p.getAnchorPoint()); + this.setNodeDirty(); + cc.DOM.parentDOM(this); + } + }, + + /** + * replace resume Schedule and actions of ccNode + */ + resume:function () { + this.getScheduler().resumeTarget(this); + this.getActionManager().resumeTarget(this); + cc.eventManager.resumeTarget(this); + //if dom does not have parent, but node has no parent and its running + if (this.dom && !this.dom.parentNode) { + if (!this.getParent()) { + if(this.dom.id === ""){ + cc.DOM._createEGLViewDiv(this); + }else{ + this.dom.appendTo(cc.container); + } + } else { + cc.DOM.parentDOM(this); + } + } + if (this.dom) + this.dom.style.visibility = "visible"; + }, + + /** + * replace pause Schedule and Actions of ccNode + */ + pause:function () { + this.getScheduler().pauseTarget(this); + this.getActionManager().pauseTarget(this); + cc.eventManager.pauseTarget(this); + if (this.dom) { + this.dom.style.visibility = 'hidden'; + } + }, + + /** + * replace clean up of ccNode + */ + cleanup:function () { + // actions + this.stopAllActions(); + this.unscheduleAllCallbacks(); + + // timers + this._arrayMakeObjectsPerformSelector(this._children, cc.Node._stateCallbackType.cleanup); + if (this.dom) { + this.dom.remove(); + } + }, + /** + * replace remove from parent and clean up of ccNode + */ + removeFromParentAndCleanup:function () { + this.dom.remove(); + }, + setOpacity:function (o) { + this._opacity = o; + this.dom.style.opacity = o / 255; + }, + /** + * refresh/updates the DOM element + */ + redraw:function () { + if (this.isSprite) { + var tmp = this._children; + this._children = []; + cc.Sprite.prototype.visit.call(this, this.ctx); + this._children = tmp; + } + else { + cc.Sprite.prototype.visit.call(this, this.ctx); + } + } +}; + +cc.DOM._resetEGLViewDiv = function(){ + var div = cc.$("#EGLViewDiv"); + if(div){ + var view = cc.view; + var designSize = view.getDesignResolutionSize(); + var viewPortRect = view.getViewPortRect(); + var screenSize = view.getFrameSize(); + var pixelRatio = view.getDevicePixelRatio(); + var designSizeWidth = designSize.width, designSizeHeight = designSize.height; + if((designSize.width === 0) && (designSize.height === 0)){ + designSizeWidth = screenSize.width; + designSizeHeight = screenSize.height; + } + + var viewPortWidth = viewPortRect.width/pixelRatio; + if((viewPortRect.width === 0) && (viewPortRect.height === 0)){ + viewPortWidth = screenSize.width; + } + + div.style.position = 'absolute'; + //x.dom.style.display='block'; + div.style.width = designSizeWidth + "px"; + div.style.maxHeight = designSizeHeight + "px"; + div.style.margin = 0; + + div.resize(view.getScaleX()/pixelRatio, view.getScaleY()/pixelRatio); + if (view.getResolutionPolicy() === view._rpNoBorder) { + div.style.left = (view.getFrameSize().width - designSizeWidth)/2 + "px"; + div.style.bottom = (view.getFrameSize().height - designSizeHeight*view.getScaleY()/pixelRatio)/2 + "px"; + } + else { + div.style.left = (designSizeWidth*view.getScaleX()/pixelRatio - designSizeWidth) / 2 + "px"; + div.style.bottom = "0px"; + } + } +}; + +/** + * @function + * @private + * @param x + * @return {Boolean} + */ +cc.DOM.parentDOM = function (x) { + var p = x.getParent(); + //if has parent, parent need to have dom too + if (!p || !x.dom) + return false; + if (!p.dom) { + cc.DOM.placeHolder(p); + p.setParent = cc.DOM.methods.setParent; + } + //if parent have dom, attach self to parent + x.dom.appendTo(p.dom); + p.setAnchorPoint(p.getAnchorPoint()); + + if (p.getParent()) { + cc.DOM.parentDOM(p); + } else { + //parent has no more parent, if its running, then add it to the container + if (p.isRunning()) { + //find EGLView div + var eglViewDiv = cc.$("#EGLViewDiv"); + if (eglViewDiv) { + p.dom.appendTo(eglViewDiv); + } else { + cc.DOM._createEGLViewDiv(p); + } + } + } + return true; +}; + +cc.DOM._createEGLViewDiv = function(p){ + var div = cc.$("#EGLViewDiv"); + if(!div){ + div = cc.$new("div"); + div.id = "EGLViewDiv"; + } + + var view = cc.view; + var designSize = view.getDesignResolutionSize(); + var viewPortRect = view.getViewPortRect(); + var screenSize = view.getFrameSize(); + var pixelRatio = view.getDevicePixelRatio(); + var designSizeWidth = designSize.width, designSizeHeight = designSize.height; + if ((designSize.width === 0) && (designSize.height === 0)) { + designSizeWidth = screenSize.width; + designSizeHeight = screenSize.height; + } + + var viewPortWidth = viewPortRect.width/pixelRatio; + if ((viewPortRect.width === 0) && (viewPortRect.height === 0)) { + viewPortWidth = screenSize.width; + } + + div.style.position = 'absolute'; + //x.dom.style.display='block'; + div.style.width = designSizeWidth + "px"; + div.style.maxHeight = designSizeHeight + "px"; + div.style.margin = 0; + + div.resize(view.getScaleX()/pixelRatio, view.getScaleY()/pixelRatio); + if (view.getResolutionPolicy() === view._rpNoBorder) { + div.style.left = (screenSize.width - designSizeWidth)/2 + "px"; + div.style.bottom = (screenSize.height - designSizeHeight*view.getScaleY()/pixelRatio)/2 + "px"; + } + else { + div.style.left = (designSizeWidth*view.getScaleX()/pixelRatio - designSizeWidth) / 2 + "px"; + div.style.bottom = "0px"; + } + + p.dom.appendTo(div); + div.appendTo(cc.container); +}; + +/** + * @function + * @private + * @param x + */ +cc.DOM.setTransform = function (x) { + if (x.ctx) { + x.ctx.translate(x.getAnchorPointInPoints().x, x.getAnchorPointInPoints().y); + if (x.isSprite) { + var tmp = x._children; + x._children = []; + cc.Sprite.prototype.visit.call(x); + x._children = tmp; + } + else { + cc.Sprite.prototype.visit.call(x); + } + } + if (x.dom) { + x.dom.position.x = x.getPositionX(); + x.dom.position.y = -x.getPositionY(); + x.dom.rotation = x.getRotation(); + x.dom.scale = {x:x.getScaleX(), y:x.getScaleY()}; + x.dom.skew = {x:x.getSkewX(), y:x.getSkewY()}; + if (x.setAnchorPoint) + x.setAnchorPoint(x.getAnchorPoint()); + x.dom.transforms(); + } + +}; + +/** + * @function + * @private + * @param x + */ +cc.DOM.forSprite = function (x) { + x.dom = cc.$new('div'); + x.canvas = cc.$new('canvas'); + var locContentSize = x.getContentSize(); + x.canvas.width = locContentSize.width; + x.canvas.height = locContentSize.height; + x.dom.style.position = 'absolute'; + x.dom.style.bottom = 0; + x.ctx = x.canvas.getContext('2d'); + x.dom.appendChild(x.canvas); + if (x.getParent()) { + cc.DOM.parentDOM(x); + } + x.isSprite = true; +}; + +/** + * This creates divs for parent Nodes that are related to the current node + * @function + * @private + * @param x + */ +cc.DOM.placeHolder = function (x) { + //creating a placeholder dom to simulate other ccNode in the hierachy + x.dom = cc.$new('div'); + x.placeholder = true; + x.dom.style.position = 'absolute'; + x.dom.style.bottom = 0; + //x.dom.style.display='block'; + x.dom.style.width = (x.getContentSize().width || cc.director.getWinSize().width) + "px"; + x.dom.style.maxHeight = (x.getContentSize().height || cc.director.getWinSize().height) + "px"; + x.dom.style.margin = 0; + cc.DOM.setTransform(x); + x.dom.transforms(); + cc.DOM._addMethods(x); + //x.dom.style.border = 'red 1px dotted'; +}; + +/** + * Converts cc.Sprite or cc.MenuItem to DOM elements
+ * It currently only supports cc.Sprite and cc.MenuItem + * @function + * @param {cc.Sprite|cc.MenuItem|Array} nodeObject + * @example + * // example + * cc.DOM.convert(Sprite1, Sprite2, Menuitem); + * + * var myDOMElements = [Sprite1, Sprite2, MenuItem]; + * cc.DOM.convert(myDOMElements); + */ +cc.DOM.convert = function (nodeObject) { + //if passing by list, make it an array + if (arguments.length > 1) { + cc.DOM.convert(arguments); + return; + } else if (arguments.length === 1 && !arguments[0].length) { + cc.DOM.convert([arguments[0]]); + return; + } + var args = arguments[0]; + for (var i = 0; i < args.length; i++) { + //first check if its sprite + if (args[i] instanceof cc.Sprite) { + // create a canvas + if (!args[i].dom) + cc.DOM.forSprite(args[i]); + } else { + cc.log('DOM converter only supports sprite and menuitems yet'); + } + cc.DOM._addMethods(args[i]); + args[i].visit = function () { + }; + args[i].transform = function () { + }; + cc.DOM.setTransform(args[i]); + args[i].setVisible(args[i].isVisible()); + } +}; diff --git a/extensions/gui/control-extension/CCControl.js b/extensions/gui/control-extension/CCControl.js new file mode 100644 index 00000000000..7ca024d33cd --- /dev/null +++ b/extensions/gui/control-extension/CCControl.js @@ -0,0 +1,381 @@ +/** + * 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 () { + if (cc.Layer.prototype.init.call(this)) { + // 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; + } else + return false; + }, + + 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.js.array.remove(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/extensions/gui/control-extension/CCControlButton.js b/extensions/gui/control-extension/CCControlButton.js new file mode 100644 index 00000000000..e571e81f083 --- /dev/null +++ b/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 prefered 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 prefered 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/extensions/gui/control-extension/CCControlColourPicker.js b/extensions/gui/control-extension/CCControlColourPicker.js new file mode 100644 index 00000000000..6cdfd6bcc8c --- /dev/null +++ b/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/extensions/gui/control-extension/CCControlHuePicker.js b/extensions/gui/control-extension/CCControlHuePicker.js new file mode 100644 index 00000000000..58db95f4acf --- /dev/null +++ b/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.Vec2} 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.Vec2} 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/extensions/gui/control-extension/CCControlPotentiometer.js b/extensions/gui/control-extension/CCControlPotentiometer.js new file mode 100644 index 00000000000..298c7074ab7 --- /dev/null +++ b/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.Vec2} 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.Vec2} point1 + * @param {cc.Vec2} 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.Vec2} beginLineA + * @param {cc.Vec2} endLineA + * @param {cc.Vec2} beginLineB + * @param {cc.Vec2} 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/extensions/gui/control-extension/CCControlSaturationBrightnessPicker.js b/extensions/gui/control-extension/CCControlSaturationBrightnessPicker.js new file mode 100644 index 00000000000..bd635c342ed --- /dev/null +++ b/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.Vec2} 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.Vec2} 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.Vec2} 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/extensions/gui/control-extension/CCControlSlider.js b/extensions/gui/control-extension/CCControlSlider.js new file mode 100644 index 00000000000..c9947394a4b --- /dev/null +++ b/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/extensions/gui/control-extension/CCControlStepper.js b/extensions/gui/control-extension/CCControlStepper.js new file mode 100644 index 00000000000..9ddec8e12bb --- /dev/null +++ b/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.TextAlignment.CENTER, cc.VerticalTextAlignment.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.TextAlignment.CENTER, cc.VerticalTextAlignment.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/extensions/gui/control-extension/CCControlSwitch.js b/extensions/gui/control-extension/CCControlSwitch.js new file mode 100644 index 00000000000..2bf1f5d296f --- /dev/null +++ b/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.Vec2} onPos - The position of slider when switch is on + * @property {cc.Vec2} offPos - The position of slider when switch is off + * @property {cc.Texture2D} maskTexture - The texture of the mask + * @property {cc.Vec2} texturePos - The position of the texture + * @property {cc.Vec2} 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/extensions/gui/control-extension/CCControlUtils.js b/extensions/gui/control-extension/CCControlUtils.js new file mode 100644 index 00000000000..5e803bd56a4 --- /dev/null +++ b/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/extensions/gui/control-extension/CCInvocation.js b/extensions/gui/control-extension/CCInvocation.js new file mode 100644 index 00000000000..2a5d477b873 --- /dev/null +++ b/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.js.isString(this._action)) { + this._target[this._action](sender, this._controlEvent); + } else{ + this._action.call(this._target, sender, this._controlEvent); + } + } + } +}); + diff --git a/extensions/gui/control-extension/CCMenuPassive.js b/extensions/gui/control-extension/CCMenuPassive.js new file mode 100644 index 00000000000..82a3fd32fe2 --- /dev/null +++ b/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/extensions/gui/scrollview/CCScrollView.js b/extensions/gui/scrollview/CCScrollView.js new file mode 100644 index 00000000000..9326a436438 --- /dev/null +++ b/extensions/gui/scrollview/CCScrollView.js @@ -0,0 +1,826 @@ +/**************************************************************************** + 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.Vec2} minOffset - <@readonly> The current container's minimum offset + * @property {cc.Vec2} 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; + }, + + /** + * Sets a new content offset. It ignores max/min offset. It just sets what's given. (just like UIKit's UIScrollView) + * + * @param {cc.Vec2} 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.Vec2} 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.Vec2} 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.Vec2} 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; + }, + + visit:function (parentCmd) { + // quick return if not visible + if (!this.isVisible()) + return; + + this._renderCmd.visit(parentCmd); + }, + + 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); +}; \ No newline at end of file diff --git a/extensions/gui/scrollview/CCScrollViewCanvasRenderCmd.js b/extensions/gui/scrollview/CCScrollViewCanvasRenderCmd.js new file mode 100644 index 00000000000..3fdc92851f6 --- /dev/null +++ b/extensions/gui/scrollview/CCScrollViewCanvasRenderCmd.js @@ -0,0 +1,81 @@ +/**************************************************************************** + 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){ + cc.Layer.CanvasRenderCmd.call(this, renderable); + this._needDraw = false; + + this.startCmd = new cc.CustomRenderCmd(this, this._startCmd); + this.endCmd = new cc.CustomRenderCmd(this, this._endCmd); + }; + + 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) * scaleX; + var getHeight = (node._viewSize.height * locScaleY) * scaleY; + + context.beginPath(); + context.rect(0, 0, getWidth, -getHeight); + context.closePath(); + context.clip(); + } + }; + + proto._endCmd = function(wrapper){ + wrapper = wrapper || cc._renderContext; + wrapper.restore(); + }; + + proto.visit = function(parentCmd){ + var node = this._node; + if (!node._visible) return; + + var i, locChildren = node._children, childrenLen; + + this.transform(parentCmd); + cc.renderer.pushRenderCommand(this.startCmd); + + if (locChildren && locChildren.length > 0) { + childrenLen = locChildren.length; + node.sortAllChildren(); + for (i = 0; i < childrenLen; i++) { + locChildren[i]._renderCmd.visit(this); + } + } + cc.renderer.pushRenderCommand(this.endCmd); + }; +})(); diff --git a/extensions/gui/scrollview/CCScrollViewWebGLRenderCmd.js b/extensions/gui/scrollview/CCScrollViewWebGLRenderCmd.js new file mode 100644 index 00000000000..7098f1a8254 --- /dev/null +++ b/extensions/gui/scrollview/CCScrollViewWebGLRenderCmd.js @@ -0,0 +1,109 @@ +/**************************************************************************** + 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){ + cc.Layer.WebGLRenderCmd.call(this, 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); + } + }; + + proto.visit = function(parendCmd){ + var node = this._node; + if (!node._visible) return; + + var i, locChildren = node._children, selChild, childrenLen; + + cc.kmGLPushMatrix(); + + this.transform(parendCmd); + + if (node._clippingToBounds) { + cc.renderer.pushRenderCommand(this.startCmd); + } + + if (locChildren && locChildren.length > 0) { + childrenLen = locChildren.length; + // draw children zOrder < 0 + for (i = 0; i < childrenLen; i++) { + selChild = locChildren[i]; + if (selChild && selChild._localZOrder < 0) + selChild._renderCmd.visit(); + else + break; + } + + // draw children zOrder >= 0 + for (; i < childrenLen; i++) + locChildren[i]._renderCmd.visit(); + } + + if (node._clippingToBounds) { + cc.renderer.pushRenderCommand(this.endCmd); + } + + this._dirtyFlag = 0; + cc.kmGLPopMatrix(); + }; +})(); \ No newline at end of file diff --git a/extensions/gui/scrollview/CCSorting.js b/extensions/gui/scrollview/CCSorting.js new file mode 100644 index 00000000000..79020181718 --- /dev/null +++ b/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/extensions/gui/scrollview/CCTableView.js b/extensions/gui/scrollview/CCTableView.js new file mode 100644 index 00000000000..7b7f2c28468 --- /dev/null +++ b/extensions/gui/scrollview/CCTableView.js @@ -0,0 +1,720 @@ +/**************************************************************************** + 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.js.array.remove(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.js.array.remove(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); + }, + + /** + * 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/extensions/runtime/CCLoaderLayer.js b/extensions/runtime/CCLoaderLayer.js new file mode 100644 index 00000000000..bfbe12ea3c3 --- /dev/null +++ b/extensions/runtime/CCLoaderLayer.js @@ -0,0 +1,974 @@ +/**************************************************************************** + 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); + 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); + } + } + self._callback.call(this._target, !status.isFailed); + }); + }, + _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 () { + if (cc.director.getRunningScene()) { + loaderLayer.updateGroup(groupname, preloadCb, target); + loaderLayer._addToScene(); + loaderLayer._preloadSource(); + } else { + cc.log("Current scene is null we can't start preload"); + } + }; + + if (res_engine_loaded) { + callPreload(); + return; + } + + // 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._confg; +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; + +})(); diff --git a/extensions/spine/CCSkeleton.js b/extensions/spine/CCSkeleton.js new file mode 100644 index 00000000000..9555725a9d8 --- /dev/null +++ b/extensions/spine/CCSkeleton.js @@ -0,0 +1,407 @@ +/**************************************************************************** + 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 || {}; + +/** + * The vertex index of spine. + * @constant + * @type {{X1: number, Y1: number, X2: number, Y2: number, X3: number, Y3: number, X4: number, Y4: number}} + */ +sp.VERTEX_INDEX = { + X1: 0, + Y1: 1, + X2: 2, + Y2: 3, + X3: 4, + Y3: 5, + X4: 6, + Y4: 7 +}; + +/** + * The attachment type of spine. It contains three type: REGION(0), BOUNDING_BOX(1), MESH(2) and SKINNED_MESH. + * @constant + * @type {{REGION: number, BOUNDING_BOX: number, REGION_SEQUENCE: number, MESH: number}} + */ +sp.ATTACHMENT_TYPE = { + REGION: 0, + BOUNDING_BOX: 1, + MESH: 2, + SKINNED_MESH:3 +}; + +/** + *

+ * 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, + _blendFunc: 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); + this._blendFunc = {src: cc.BLEND_SRC, dst: cc.BLEND_DST}; + + 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.setOpacityModifyRGB(true); + this._blendFunc.src = cc.ONE; + this._blendFunc.dst = cc.ONE_MINUS_SRC_ALPHA; + this.scheduleUpdate(); + }, + + /** + * 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 {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.js.isString(argSkeletonFile)) { + if (cc.js.isString(argAtlasFile)) { + var data = cc.loader.getRes(argAtlasFile); + sp._atlasLoader.setAtlasFile(argAtlasFile); + atlas = new spine.Atlas(data, 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 = sp.VERTEX_INDEX; + + for (var i = 0, slotCount = slots.length; i < slotCount; ++i) { + var slot = slots[i]; + if (!slot.attachment || slot.attachment.type != sp.ATTACHMENT_TYPE.REGION) + continue; + var attachment = slot.attachment; + this._computeRegionAttachmentWorldVertices(attachment, slot.bone.skeleton.x, slot.bone.skeleton.y, slot.bone, vertices); + minX = Math.min(minX, vertices[VERTEX.X1] * scaleX, vertices[VERTEX.X4] * scaleX, vertices[VERTEX.X2] * scaleX, vertices[VERTEX.X3] * scaleX); + minY = Math.min(minY, vertices[VERTEX.Y1] * scaleY, vertices[VERTEX.Y4] * scaleY, vertices[VERTEX.Y2] * scaleY, vertices[VERTEX.Y3] * scaleY); + maxX = Math.max(maxX, vertices[VERTEX.X1] * scaleX, vertices[VERTEX.X4] * scaleX, vertices[VERTEX.X2] * scaleX, vertices[VERTEX.X3] * scaleX); + maxY = Math.max(maxY, vertices[VERTEX.Y1] * scaleY, vertices[VERTEX.Y4] * scaleY, vertices[VERTEX.Y2] * scaleY, vertices[VERTEX.Y3] * scaleY); + } + var position = this.getPosition(); + return cc.rect(position.x + minX, position.y + minY, maxX - minX, maxY - minY); + }, + + _computeRegionAttachmentWorldVertices : function(self, x, y, bone, vertices){ + var offset = self.offset, vertexIndex = sp.VERTEX_INDEX; + x += bone.worldX; + y += bone.worldY; + vertices[vertexIndex.X1] = offset[vertexIndex.X1] * bone.m00 + offset[vertexIndex.Y1] * bone.m01 + x; + vertices[vertexIndex.Y1] = offset[vertexIndex.X1] * bone.m10 + offset[vertexIndex.Y1] * bone.m11 + y; + vertices[vertexIndex.X2] = offset[vertexIndex.X2] * bone.m00 + offset[vertexIndex.Y2] * bone.m01 + x; + vertices[vertexIndex.Y2] = offset[vertexIndex.X2] * bone.m10 + offset[vertexIndex.Y2] * bone.m11 + y; + vertices[vertexIndex.X3] = offset[vertexIndex.X3] * bone.m00 + offset[vertexIndex.Y3] * bone.m01 + x; + vertices[vertexIndex.Y3] = offset[vertexIndex.X3] * bone.m10 + offset[vertexIndex.Y3] * bone.m11 + y; + vertices[vertexIndex.X4] = offset[vertexIndex.X4] * bone.m00 + offset[vertexIndex.Y4] * bone.m01 + x; + vertices[vertexIndex.Y4] = offset[vertexIndex.X4] * bone.m10 + offset[vertexIndex.Y4] * bone.m11 + y; + }, + + /** + * 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 {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 {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 {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 {spine.RegionAttachment|spine.BoundingBoxAttachment} + */ + getAttachment: function (slotName, attachmentName) { + return this._skeleton.getAttachmentBySlotName(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 + */ + setOpacityModifyRGB: function (alpha) { + this._premultipliedAlpha = alpha; + }, + + /** + * Returns whether to enable premultiplied alpha. + * @returns {boolean} + */ + isOpacityModifyRGB: function () { + return this._premultipliedAlpha; + }, + + /** + * Sets skeleton data to sp.Skeleton. + * @param {spine.SkeletonData} skeletonData + * @param {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 {spine.RegionAttachment|spine.BoundingBoxAttachment} regionAttachment + * @returns {cc.Node} + */ + getTextureAtlas: function (regionAttachment) { + return regionAttachment.rendererObject.page.rendererObject; + }, + + /** + * Returns the blendFunc of sp.Skeleton. + * @returns {cc.BlendFunc} + */ + getBlendFunc: function () { + return this._blendFunc; + }, + + /** + * Sets the blendFunc of sp.Skeleton. + * @param {cc.BlendFunc|Number} 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; + } + }, + + /** + * 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); + } +}); + +/** + * 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); +}; \ No newline at end of file diff --git a/extensions/spine/CCSkeletonAnimation.js b/extensions/spine/CCSkeletonAnimation.js new file mode 100644 index 00000000000..54b0c17d4db --- /dev/null +++ b/extensions/spine/CCSkeletonAnimation.js @@ -0,0 +1,348 @@ +/**************************************************************************** + 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. + ****************************************************************************/ + +/** + * @ignore + */ +sp._atlasPage_createTexture_webGL = function (self, path) { + var texture = cc.textureCache.addImage(path); + self.rendererObject = new cc.TextureAtlas(texture, 128); + self.width = texture.getPixelWidth(); + self.height = texture.getPixelHeight(); +}; + +sp._atlasPage_createTexture_canvas = function(self, path) { + self._texture = cc.textureCache.addImage(path); +}; + +sp._atlasPage_disposeTexture = function (self) { + self.rendererObject.release(); +}; + +sp._atlasLoader = { + spAtlasFile:null, + setAtlasFile:function(spAtlasFile){ + this.spAtlasFile = spAtlasFile; + }, + load:function(page, line, spAtlas){ + var texturePath = cc.path.join(cc.path.dirname(this.spAtlasFile), line); + if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) + sp._atlasPage_createTexture_webGL(page,texturePath); + else + sp._atlasPage_createTexture_canvas(page,texturePath); + }, + 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, + END: 1, + COMPLETE: 2, + EVENT: 3 +}; + +sp.TrackEntryListeners = function(startListener, endListener, completeListener, eventListener){ + this.startListener = startListener || null; + this.endListener = endListener || null; + this.completeListener = completeListener || null; + this.eventListener = eventListener || null; +}; + +sp.TrackEntryListeners.getListeners = function(entry){ + if(!entry.rendererObject){ + entry.rendererObject = new sp.TrackEntryListeners(); + entry.listener = sp.trackEntryCallback; + } + return entry.rendererObject; +}; + +sp.trackEntryCallback = function(state, trackIndex, type, event, loopCount) { + state.rendererObject.onTrackEntryEvent(trackIndex, type, event, loopCount); +}; + +/** + * 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, + _target: null, + _callback: null, + + _ownsAnimationStateData: false, + _startListener: null, + _endListener: null, + _completeListener: null, + _eventListener: 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 {spine.AnimationStateData} stateData + */ + setAnimationStateData: function (stateData) { + var state = new spine.AnimationState(stateData); + state.rendererObject = this; + state.onStart = this._onAnimationStateStart.bind(this); + state.onComplete = this._onAnimationStateComplete.bind(this); + state.onEnd = this._onAnimationStateEnd.bind(this); + state.onEvent = this._onAnimationStateEvent.bind(this); + 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.setMixByName(fromAnimation, toAnimation, duration); + }, + + /** + * Sets event listener of sp.SkeletonAnimation. + * @param {Object} target + * @param {Function} callback + */ + setAnimationListener: function (target, callback) { + this._target = target; + this._callback = callback; + }, + + /** + * Set the current animation. Any queued animations are cleared. + * @param {Number} trackIndex + * @param {String} name + * @param {Boolean} loop + * @returns {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.setAnimation(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 {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.addAnimation(trackIndex, animation, loop, delay); + }, + + /** + * Returns track entry by trackIndex. + * @param trackIndex + * @returns {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._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._startListener = listener; + }, + + /** + * Set the end event listener. + * @param {function} listener + */ + setEndListener: function(listener) { + this._endListener = listener; + }, + + setCompleteListener: function(listener) { + this._completeListener = listener; + }, + + setEventListener: function(listener){ + this._eventListener = listener; + }, + + setTrackStartListener: function(entry, listener){ + sp.TrackEntryListeners.getListeners(entry).startListener = listener; + }, + + setTrackEndListener: function(entry, listener){ + sp.TrackEntryListeners.getListeners(entry).endListener = listener; + }, + + setTrackCompleteListener: function(entry, listener){ + sp.TrackEntryListeners.getListeners(entry).completeListener = listener; + }, + + setTrackEventListener: function(entry, listener){ + sp.TrackEntryListeners.getListeners(entry).eventListener = listener; + }, + + onTrackEntryEvent: function(traceIndex, type, event, loopCount){ + var entry = this._state.getCurrent(traceIndex); + if(!entry.rendererObject) + return; + var listeners = entry.rendererObject; + switch (type){ + case sp.ANIMATION_EVENT_TYPE.START: + if(listeners.startListener) + listeners.startListener(traceIndex); + break; + case sp.ANIMATION_EVENT_TYPE.END: + if(listeners.endListener) + listeners.endListener(traceIndex); + break; + case sp.ANIMATION_EVENT_TYPE.COMPLETE: + if(listeners.completeListener) + listeners.completeListener(traceIndex, loopCount); + break; + case sp.ANIMATION_EVENT_TYPE.EVENT: + if(listeners.eventListener) + listeners.eventListener(traceIndex, event); + break; + } + }, + + onAnimationStateEvent: function(trackIndex, type, event, loopCount) { + switch(type){ + case sp.ANIMATION_EVENT_TYPE.START: + if(this._startListener) + this._startListener(trackIndex); + break; + case sp.ANIMATION_EVENT_TYPE.END: + if(this._endListener) + this._endListener(trackIndex); + break; + case sp.ANIMATION_EVENT_TYPE.COMPLETE: + if(this._completeListener) + this._completeListener(trackIndex, loopCount); + break; + case sp.ANIMATION_EVENT_TYPE.EVENT: + if(this._eventListener) + this._eventListener(trackIndex, event); + break; + } + }, + + getState: function(){ + return this._state; + }, + + _onAnimationStateStart: function (trackIndex) { + this._animationStateCallback(trackIndex, sp.ANIMATION_EVENT_TYPE.START, null, 0); + }, + _onAnimationStateEnd: function (trackIndex) { + this._animationStateCallback(trackIndex, sp.ANIMATION_EVENT_TYPE.END, null, 0); + }, + _onAnimationStateComplete: function (trackIndex, count) { + this._animationStateCallback(trackIndex, sp.ANIMATION_EVENT_TYPE.COMPLETE, null, count); + }, + _onAnimationStateEvent: function (trackIndex, event) { + this._animationStateCallback(trackIndex, sp.ANIMATION_EVENT_TYPE.EVENT, event, 0); + }, + _animationStateCallback: function (trackIndex, type, event, loopCount) { + this.onAnimationStateEvent(trackIndex, type, event, loopCount); + if (this._target && this._callback) { + this._callback.call(this._target, this, trackIndex, type, event, loopCount) + } + } +}); + +/** + * 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.create = function (skeletonDataFile, atlasFile/* or atlas*/, scale) { + return new sp.SkeletonAnimation(skeletonDataFile, atlasFile, scale); +}; \ No newline at end of file diff --git a/extensions/spine/CCSkeletonCanvasRenderCmd.js b/extensions/spine/CCSkeletonCanvasRenderCmd.js new file mode 100644 index 00000000000..1b4f873cd1b --- /dev/null +++ b/extensions/spine/CCSkeletonCanvasRenderCmd.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(){ + sp.Skeleton.CanvasRenderCmd = function(renderableObject){ + cc.Node.CanvasRenderCmd.call(this, 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.type != sp.ATTACHMENT_TYPE.REGION) + 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.m00 + bone.worldX; + var y = bone.data.length * bone.m10 + bone.worldY; + drawingUtil.drawLine( + {x: bone.worldX, y: bone.worldY}, + {x: x, y: 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({x: bone.worldX, y: bone.worldY}); + if (i === 0) + drawingUtil.setDrawColor(0, 255, 0, 255); + } + } + }; + + proto._updateRegionAttachmentSlot = function(attachment, slot, points) { + if(!points) + return; + + var vertices = {}, VERTEX = sp.VERTEX_INDEX, bone = slot.bone; + attachment.computeVertices(bone.skeleton.x, bone.skeleton.y, bone, vertices); + points.length = 0; + points.push(cc.p(vertices[VERTEX.X1], vertices[VERTEX.Y1])); + points.push(cc.p(vertices[VERTEX.X4], vertices[VERTEX.Y4])); + points.push(cc.p(vertices[VERTEX.X3], vertices[VERTEX.Y3])); + points.push(cc.p(vertices[VERTEX.X2], vertices[VERTEX.Y2])); + }; + + 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.rendererObject.name; + sprite = this._createSprite(slot, attachment); + slot.currentSprite = sprite; + slot.currentSpriteName = spriteName; + slotNode.addChild(sprite); + } else if(attachment instanceof spine.MeshAttachment){ + //todo for mesh + } + } + }; + + proto._createSprite = function(slot, attachment){ + var rendererObject = attachment.rendererObject; + var texture = rendererObject.page._texture; + var rect = new cc.Rect(rendererObject.x, rendererObject.y, rendererObject.width, rendererObject.height); + var sprite = new cc.Sprite(); + sprite.initWithTexture(rendererObject.page._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); + + slot.sprites = slot.sprites || {}; + slot.sprites[rendererObject.name] = sprite; + + return sprite; + }; + + proto._updateChild = function(){ + var locSkeleton = this._node._skeleton, slots = locSkeleton.slots; + var i, n, selSprite; + + 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; + } + var type = attachment.type; + if (type === spine.AttachmentType.region){ + if(attachment.rendererObject){ + if(!slot.currentSpriteName || slot.currentSpriteName !== attachment.name){ + var spriteName = attachment.rendererObject.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; + slotNode.setPosition(bone.worldX + attachment.x * bone.m00 + attachment.y * bone.m01, + bone.worldY + attachment.x * bone.m10 + attachment.y * bone.m11); + slotNode.setScale(bone.worldScaleX, bone.worldScaleY); + + //set the color and opacity + selSprite = slot.currentSprite; + selSprite._flippedX = bone.worldFlipX; + selSprite._flippedY = bone.worldFlipY; + if(selSprite._flippedY || selSprite._flippedX){ + slotNode.setRotation(bone.worldRotation); + selSprite.setRotation(attachment.rotation); + }else{ + slotNode.setRotation(-bone.worldRotation); + selSprite.setRotation(-attachment.rotation); + } + + //hack for sprite + selSprite._renderCmd._displayedOpacity = 0 | (locSkeleton.a * slot.a * 255); + var r = 0 | (locSkeleton.r * slot.r * 255), g = 0 | (locSkeleton.g * slot.g * 255), b = 0 | (locSkeleton.b * slot.b * 255); + selSprite.setColor(cc.color(r,g,b)); + selSprite._renderCmd._updateColor(); + } else if (type === spine.AttachmentType.skinnedmesh) { + //todo for mesh + } else { + slotNode.setVisible(false); + continue; + } + slotNode.setVisible(true); + } + }; +})(); \ No newline at end of file diff --git a/extensions/spine/CCSkeletonWebGLRenderCmd.js b/extensions/spine/CCSkeletonWebGLRenderCmd.js new file mode 100644 index 00000000000..b00b3c44c55 --- /dev/null +++ b/extensions/spine/CCSkeletonWebGLRenderCmd.js @@ -0,0 +1,260 @@ +/**************************************************************************** + 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(){ + sp.Skeleton.WebGLRenderCmd = function (renderableObject) { + cc.Node.WebGLRenderCmd.call(this, renderableObject); + this._needDraw = true; + this.setShaderProgram(cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR)); + this._tmpQuad = new cc.V3F_C4B_T2F_Quad(); + }; + + var proto = sp.Skeleton.WebGLRenderCmd.prototype = Object.create(cc.Node.WebGLRenderCmd.prototype); + proto.constructor = sp.Skeleton.WebGLRenderCmd; + + proto.rendering = function (ctx) { + var node = this._node, tmpQuad = this._tmpQuad; + var color = node.getColor(), locSkeleton = node._skeleton; + + var blendMode, textureAtlas, attachment, slot, i, n; + var locBlendFunc = node._blendFunc; + var premultiAlpha = node._premultipliedAlpha; + + this._shaderProgram.use(); + this._shaderProgram._setUniformForMVPMatrixWithMat4(this._stackMatrix); +// cc.glBlendFunc(this._blendFunc.src, this._blendFunc.dst); + locSkeleton.r = color.r / 255; + locSkeleton.g = color.g / 255; + locSkeleton.b = color.b / 255; + locSkeleton.a = node.getOpacity() / 255; + if (premultiAlpha) { + locSkeleton.r *= locSkeleton.a; + locSkeleton.g *= locSkeleton.a; + locSkeleton.b *= locSkeleton.a; + } + + //for (i = 0, n = locSkeleton.slots.length; i < n; i++) { + for (i = 0, n = locSkeleton.drawOrder.length; i < n; i++) { + slot = locSkeleton.drawOrder[i]; + if (!slot.attachment) + continue; + attachment = slot.attachment; + + switch(slot.attachment.type) { + case sp.ATTACHMENT_TYPE.REGION: + this._updateRegionAttachmentQuad(attachment, slot, tmpQuad, premultiAlpha); + break; + case sp.ATTACHMENT_TYPE.MESH: + this._updateMeshAttachmentQuad(attachment, slot, tmpQuad, premultiAlpha); + break; + case sp.ATTACHMENT_TYPE.SKINNED_MESH: + break; + default: + continue; + } + + var regionTextureAtlas = node.getTextureAtlas(attachment); + + if (slot.data.blendMode != blendMode) { + if (textureAtlas) { + textureAtlas.drawQuads(); + textureAtlas.removeAllQuads(); + } + blendMode = slot.data.blendMode; + switch (blendMode) { + case spine.BlendMode.additive: + cc.glBlendFunc(premultiAlpha ? cc.ONE : cc.SRC_ALPHA, cc.ONE); + break; + case spine.BlendMode.multiply: + cc.glBlendFunc(cc.DST_COLOR, cc.ONE_MINUS_SRC_ALPHA); + break; + case spine.BlendMode.screen: + cc.glBlendFunc(cc.ONE, cc.ONE_MINUS_SRC_COLOR); + break; + default: + cc.glBlendFunc(locBlendFunc.src, locBlendFunc.dst); + } + } else if (regionTextureAtlas != textureAtlas && textureAtlas) { + textureAtlas.drawQuads(); + textureAtlas.removeAllQuads(); + } + textureAtlas = regionTextureAtlas; + + var quadCount = textureAtlas.getTotalQuads(); + if (textureAtlas.getCapacity() == quadCount) { + textureAtlas.drawQuads(); + textureAtlas.removeAllQuads(); + if (!textureAtlas.resizeCapacity(textureAtlas.getCapacity() * 2)) + return; + } + + textureAtlas.updateQuad(tmpQuad, quadCount); + } + + if (textureAtlas) { + textureAtlas.drawQuads(); + textureAtlas.removeAllQuads(); + } + + if (node._debugBones || node._debugSlots) { + cc.kmGLMatrixMode(cc.KM_GL_MODELVIEW); + //cc.kmGLPushMatrixWitMat4(this._stackMatrix); + cc.current_stack.stack.push(cc.current_stack.top); + cc.current_stack.top = this._stackMatrix; + var drawingUtil = cc._drawingUtil; + + if (node._debugSlots) { + // Slots. + drawingUtil.setDrawColor(0, 0, 255, 255); + drawingUtil.setLineWidth(1); + + for (i = 0, n = locSkeleton.slots.length; i < n; i++) { + slot = locSkeleton.drawOrder[i]; + if (!slot.attachment || slot.attachment.type != sp.ATTACHMENT_TYPE.REGION) + continue; + attachment = slot.attachment; + this._updateRegionAttachmentQuad(attachment, slot, tmpQuad); + + var points = []; + points.push(cc.p(tmpQuad.bl.vertices.x, tmpQuad.bl.vertices.y)); + points.push(cc.p(tmpQuad.br.vertices.x, tmpQuad.br.vertices.y)); + points.push(cc.p(tmpQuad.tr.vertices.x, tmpQuad.tr.vertices.y)); + points.push(cc.p(tmpQuad.tl.vertices.x, tmpQuad.tl.vertices.y)); + + 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.m00 + bone.worldX; + var y = bone.data.length * bone.m10 + 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(); + } + }; + + proto._createChildFormSkeletonData = function(){}; + + proto._updateChild = function(){}; + + proto._updateRegionAttachmentQuad = function(self, slot, quad, premultipliedAlpha) { + var vertices = {}; + self.computeVertices(slot.bone.skeleton.x, slot.bone.skeleton.y, slot.bone, vertices); + var r = slot.bone.skeleton.r * slot.r * 255; + var g = slot.bone.skeleton.g * slot.g * 255; + var b = slot.bone.skeleton.b * slot.b * 255; + var normalizedAlpha = slot.bone.skeleton.a * slot.a; + + if (premultipliedAlpha) { + r *= normalizedAlpha; + g *= normalizedAlpha; + b *= normalizedAlpha; + } + var a = normalizedAlpha * 255; + + quad.bl.colors.r = quad.tl.colors.r = quad.tr.colors.r = quad.br.colors.r = r; + quad.bl.colors.g = quad.tl.colors.g = quad.tr.colors.g = quad.br.colors.g = g; + quad.bl.colors.b = quad.tl.colors.b = quad.tr.colors.b = quad.br.colors.b = b; + quad.bl.colors.a = quad.tl.colors.a = quad.tr.colors.a = quad.br.colors.a = a; + + var VERTEX = sp.VERTEX_INDEX; + quad.bl.vertices.x = vertices[VERTEX.X1]; + quad.bl.vertices.y = vertices[VERTEX.Y1]; + quad.tl.vertices.x = vertices[VERTEX.X2]; + quad.tl.vertices.y = vertices[VERTEX.Y2]; + quad.tr.vertices.x = vertices[VERTEX.X3]; + quad.tr.vertices.y = vertices[VERTEX.Y3]; + quad.br.vertices.x = vertices[VERTEX.X4]; + quad.br.vertices.y = vertices[VERTEX.Y4]; + + quad.bl.texCoords.u = self.uvs[VERTEX.X1]; + quad.bl.texCoords.v = self.uvs[VERTEX.Y1]; + quad.tl.texCoords.u = self.uvs[VERTEX.X2]; + quad.tl.texCoords.v = self.uvs[VERTEX.Y2]; + quad.tr.texCoords.u = self.uvs[VERTEX.X3]; + quad.tr.texCoords.v = self.uvs[VERTEX.Y3]; + quad.br.texCoords.u = self.uvs[VERTEX.X4]; + quad.br.texCoords.v = self.uvs[VERTEX.Y4]; + }; + + proto._updateMeshAttachmentQuad = function(self, slot, quad, premultipliedAlpha) { + var vertices = {}; + self.computeWorldVertices(slot.bone.x, slot.bone.y, slot, vertices); + var r = slot.bone.skeleton.r * slot.r * 255; + var g = slot.bone.skeleton.g * slot.g * 255; + var b = slot.bone.skeleton.b * slot.b * 255; + var normalizedAlpha = slot.bone.skeleton.a * slot.a; + if (premultipliedAlpha) { + r *= normalizedAlpha; + g *= normalizedAlpha; + b *= normalizedAlpha; + } + var a = normalizedAlpha * 255; + + quad.bl.colors.r = quad.tl.colors.r = quad.tr.colors.r = quad.br.colors.r = r; + quad.bl.colors.g = quad.tl.colors.g = quad.tr.colors.g = quad.br.colors.g = g; + quad.bl.colors.b = quad.tl.colors.b = quad.tr.colors.b = quad.br.colors.b = b; + quad.bl.colors.a = quad.tl.colors.a = quad.tr.colors.a = quad.br.colors.a = a; + + var VERTEX = sp.VERTEX_INDEX; + quad.bl.vertices.x = vertices[VERTEX.X1]; + quad.bl.vertices.y = vertices[VERTEX.Y1]; + quad.tl.vertices.x = vertices[VERTEX.X2]; + quad.tl.vertices.y = vertices[VERTEX.Y2]; + quad.tr.vertices.x = vertices[VERTEX.X3]; + quad.tr.vertices.y = vertices[VERTEX.Y3]; + quad.br.vertices.x = vertices[VERTEX.X4]; + quad.br.vertices.y = vertices[VERTEX.Y4]; + + quad.bl.texCoords.u = self.uvs[VERTEX.X1]; + quad.bl.texCoords.v = self.uvs[VERTEX.Y1]; + quad.tl.texCoords.u = self.uvs[VERTEX.X2]; + quad.tl.texCoords.v = self.uvs[VERTEX.Y2]; + quad.tr.texCoords.u = self.uvs[VERTEX.X3]; + quad.tr.texCoords.v = self.uvs[VERTEX.Y3]; + quad.br.texCoords.u = self.uvs[VERTEX.X4]; + quad.br.texCoords.v = self.uvs[VERTEX.Y4]; + }; +})(); \ No newline at end of file diff --git a/extensions/spine/Spine.js b/extensions/spine/Spine.js new file mode 100644 index 00000000000..32af69d94e2 --- /dev/null +++ b/extensions/spine/Spine.js @@ -0,0 +1,2636 @@ +/****************************************************************************** + * Spine Runtimes Software License + * Version 2.3 + * + * Copyright (c) 2013-2015, 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 (the "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 otherwise create derivative works, improvements of the + * Software or develop new applications using the Software 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; 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. + *****************************************************************************/ + +var spine = { + radDeg: 180 / Math.PI, + degRad: Math.PI / 180, + temp: [], + Float32Array: (typeof(Float32Array) === 'undefined') ? Array : Float32Array, + Uint16Array: (typeof(Uint16Array) === 'undefined') ? Array : Uint16Array +}; + +spine.BoneData = function (name, parent) { + this.name = name; + this.parent = parent; +}; +spine.BoneData.prototype = { + length: 0, + x: 0, y: 0, + rotation: 0, + scaleX: 1, scaleY: 1, + inheritScale: true, + inheritRotation: true, + flipX: false, flipY: false +}; + +spine.BlendMode = { + normal: 0, + additive: 1, + multiply: 2, + screen: 3 +}; + +spine.SlotData = function (name, boneData) { + this.name = name; + this.boneData = boneData; +}; +spine.SlotData.prototype = { + r: 1, g: 1, b: 1, a: 1, + attachmentName: null, + blendMode: spine.BlendMode.normal +}; + +spine.IkConstraintData = function (name) { + this.name = name; + this.bones = []; +}; +spine.IkConstraintData.prototype = { + target: null, + bendDirection: 1, + mix: 1 +}; + +spine.Bone = function (boneData, skeleton, parent) { + this.data = boneData; + this.skeleton = skeleton; + this.parent = parent; + this.setToSetupPose(); +}; +spine.Bone.yDown = false; +spine.Bone.prototype = { + x: 0, y: 0, + rotation: 0, rotationIK: 0, + scaleX: 1, scaleY: 1, + flipX: false, flipY: false, + m00: 0, m01: 0, worldX: 0, // a b x + m10: 0, m11: 0, worldY: 0, // c d y + worldRotation: 0, + worldScaleX: 1, worldScaleY: 1, + worldFlipX: false, worldFlipY: false, + updateWorldTransform: function () { + var parent = this.parent; + if (parent) { + this.worldX = this.x * parent.m00 + this.y * parent.m01 + parent.worldX; + this.worldY = this.x * parent.m10 + this.y * parent.m11 + parent.worldY; + if (this.data.inheritScale) { + this.worldScaleX = parent.worldScaleX * this.scaleX; + this.worldScaleY = parent.worldScaleY * this.scaleY; + } else { + this.worldScaleX = this.scaleX; + this.worldScaleY = this.scaleY; + } + this.worldRotation = this.data.inheritRotation ? (parent.worldRotation + this.rotationIK) : this.rotationIK; + this.worldFlipX = parent.worldFlipX != this.flipX; + this.worldFlipY = parent.worldFlipY != this.flipY; + } else { + var skeletonFlipX = this.skeleton.flipX, skeletonFlipY = this.skeleton.flipY; + this.worldX = skeletonFlipX ? -this.x : this.x; + this.worldY = (skeletonFlipY != spine.Bone.yDown) ? -this.y : this.y; + this.worldScaleX = this.scaleX; + this.worldScaleY = this.scaleY; + this.worldRotation = this.rotationIK; + this.worldFlipX = skeletonFlipX != this.flipX; + this.worldFlipY = skeletonFlipY != this.flipY; + } + var radians = this.worldRotation * spine.degRad; + var cos = Math.cos(radians); + var sin = Math.sin(radians); + if (this.worldFlipX) { + this.m00 = -cos * this.worldScaleX; + this.m01 = sin * this.worldScaleY; + } else { + this.m00 = cos * this.worldScaleX; + this.m01 = -sin * this.worldScaleY; + } + if (this.worldFlipY != spine.Bone.yDown) { + this.m10 = -sin * this.worldScaleX; + this.m11 = -cos * this.worldScaleY; + } else { + this.m10 = sin * this.worldScaleX; + this.m11 = cos * this.worldScaleY; + } + }, + setToSetupPose: function () { + var data = this.data; + this.x = data.x; + this.y = data.y; + this.rotation = data.rotation; + this.rotationIK = this.rotation; + this.scaleX = data.scaleX; + this.scaleY = data.scaleY; + this.flipX = data.flipX; + this.flipY = data.flipY; + }, + worldToLocal: function (world) { + var dx = world[0] - this.worldX, dy = world[1] - this.worldY; + var m00 = this.m00, m10 = this.m10, m01 = this.m01, m11 = this.m11; + if (this.worldFlipX != (this.worldFlipY != spine.Bone.yDown)) { + m00 = -m00; + m11 = -m11; + } + var invDet = 1 / (m00 * m11 - m01 * m10); + world[0] = dx * m00 * invDet - dy * m01 * invDet; + world[1] = dy * m11 * invDet - dx * m10 * invDet; + }, + localToWorld: function (local) { + var localX = local[0], localY = local[1]; + local[0] = localX * this.m00 + localY * this.m01 + this.worldX; + local[1] = localX * this.m10 + localY * this.m11 + this.worldY; + } +}; + +spine.Slot = function (slotData, bone) { + this.data = slotData; + this.bone = bone; + this.setToSetupPose(); +}; +spine.Slot.prototype = { + r: 1, g: 1, b: 1, a: 1, + _attachmentTime: 0, + attachment: null, + attachmentVertices: [], + setAttachment: function (attachment) { + this.attachment = attachment; + this._attachmentTime = this.bone.skeleton.time; + this.attachmentVertices.length = 0; + }, + setAttachmentTime: function (time) { + this._attachmentTime = this.bone.skeleton.time - time; + }, + getAttachmentTime: function () { + return this.bone.skeleton.time - this._attachmentTime; + }, + setToSetupPose: function () { + var data = this.data; + this.r = data.r; + this.g = data.g; + this.b = data.b; + this.a = data.a; + + var slotDatas = this.bone.skeleton.data.slots; + for (var i = 0, n = slotDatas.length; i < n; i++) { + if (slotDatas[i] == data) { + this.setAttachment(!data.attachmentName ? null : this.bone.skeleton.getAttachmentBySlotIndex(i, data.attachmentName)); + break; + } + } + } +}; + +spine.IkConstraint = function (data, skeleton) { + this.data = data; + this.mix = data.mix; + this.bendDirection = data.bendDirection; + + this.bones = []; + for (var i = 0, n = data.bones.length; i < n; i++) + this.bones.push(skeleton.findBone(data.bones[i].name)); + this.target = skeleton.findBone(data.target.name); +}; +spine.IkConstraint.prototype = { + apply: function () { + var target = this.target; + var bones = this.bones; + switch (bones.length) { + case 1: + spine.IkConstraint.apply1(bones[0], target.worldX, target.worldY, this.mix); + break; + case 2: + spine.IkConstraint.apply2(bones[0], bones[1], target.worldX, target.worldY, this.bendDirection, this.mix); + break; + } + } +}; +/** Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified in the world + * coordinate system. */ +spine.IkConstraint.apply1 = function (bone, targetX, targetY, alpha) { + var parentRotation = (!bone.data.inheritRotation || !bone.parent) ? 0 : bone.parent.worldRotation; + var rotation = bone.rotation; + var rotationIK = Math.atan2(targetY - bone.worldY, targetX - bone.worldX) * spine.radDeg; + if (bone.worldFlipX != (bone.worldFlipY != spine.Bone.yDown)) rotationIK = -rotationIK; + rotationIK -= parentRotation; + bone.rotationIK = rotation + (rotationIK - rotation) * alpha; +}; +/** Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as possible. The + * target is specified in the world coordinate system. + * @param child Any descendant bone of the parent. */ +spine.IkConstraint.apply2 = function (parent, child, targetX, targetY, bendDirection, alpha) { + var childRotation = child.rotation, parentRotation = parent.rotation; + if (!alpha) { + child.rotationIK = childRotation; + parent.rotationIK = parentRotation; + return; + } + var positionX, positionY, tempPosition = spine.temp; + var parentParent = parent.parent; + if (parentParent) { + tempPosition[0] = targetX; + tempPosition[1] = targetY; + parentParent.worldToLocal(tempPosition); + targetX = (tempPosition[0] - parent.x) * parentParent.worldScaleX; + targetY = (tempPosition[1] - parent.y) * parentParent.worldScaleY; + } else { + targetX -= parent.x; + targetY -= parent.y; + } + if (child.parent == parent) { + positionX = child.x; + positionY = child.y; + } else { + tempPosition[0] = child.x; + tempPosition[1] = child.y; + child.parent.localToWorld(tempPosition); + parent.worldToLocal(tempPosition); + positionX = tempPosition[0]; + positionY = tempPosition[1]; + } + var childX = positionX * parent.worldScaleX, childY = positionY * parent.worldScaleY; + var offset = Math.atan2(childY, childX); + var len1 = Math.sqrt(childX * childX + childY * childY), len2 = child.data.length * child.worldScaleX; + // Based on code by Ryan Juckett with permission: Copyright (c) 2008-2009 Ryan Juckett, http://www.ryanjuckett.com/ + var cosDenom = 2 * len1 * len2; + if (cosDenom < 0.0001) { + child.rotationIK = childRotation + (Math.atan2(targetY, targetX) * spine.radDeg - parentRotation - childRotation) * alpha; + return; + } + var cos = (targetX * targetX + targetY * targetY - len1 * len1 - len2 * len2) / cosDenom; + if (cos < -1) + cos = -1; + else if (cos > 1) + cos = 1; + var childAngle = Math.acos(cos) * bendDirection; + var adjacent = len1 + len2 * cos, opposite = len2 * Math.sin(childAngle); + var parentAngle = Math.atan2(targetY * adjacent - targetX * opposite, targetX * adjacent + targetY * opposite); + var rotation = (parentAngle - offset) * spine.radDeg - parentRotation; + if (rotation > 180) + rotation -= 360; + else if (rotation < -180) // + rotation += 360; + parent.rotationIK = parentRotation + rotation * alpha; + rotation = (childAngle + offset) * spine.radDeg - childRotation; + if (rotation > 180) + rotation -= 360; + else if (rotation < -180) // + rotation += 360; + child.rotationIK = childRotation + (rotation + parent.worldRotation - child.parent.worldRotation) * alpha; +}; + +spine.Skin = function (name) { + this.name = name; + this.attachments = {}; +}; +spine.Skin.prototype = { + addAttachment: function (slotIndex, name, attachment) { + this.attachments[slotIndex + ":" + name] = attachment; + }, + getAttachment: function (slotIndex, name) { + return this.attachments[slotIndex + ":" + name]; + }, + _attachAll: function (skeleton, oldSkin) { + for (var key in oldSkin.attachments) { + var colon = key.indexOf(":"); + var slotIndex = parseInt(key.substring(0, colon)); + var name = key.substring(colon + 1); + var slot = skeleton.slots[slotIndex]; + if (slot.attachment && slot.attachment.name == name) { + var attachment = this.getAttachment(slotIndex, name); + if (attachment) slot.setAttachment(attachment); + } + } + } +}; + +spine.Animation = function (name, timelines, duration) { + this.name = name; + this.timelines = timelines; + this.duration = duration; +}; +spine.Animation.prototype = { + apply: function (skeleton, lastTime, time, loop, events) { + if (loop && this.duration != 0) { + time %= this.duration; + lastTime %= this.duration; + } + var timelines = this.timelines; + for (var i = 0, n = timelines.length; i < n; i++) + timelines[i].apply(skeleton, lastTime, time, events, 1); + }, + mix: function (skeleton, lastTime, time, loop, events, alpha) { + if (loop && this.duration != 0) { + time %= this.duration; + 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); + } +}; +spine.Animation.binarySearch = function (values, target, step) { + var low = 0; + var high = Math.floor(values.length / step) - 2; + if (!high) 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; + } +}; +spine.Animation.binarySearch1 = function (values, target) { + var low = 0; + var high = values.length - 2; + if (!high) return 1; + var current = high >>> 1; + while (true) { + if (values[current + 1] <= target) + low = current + 1; + else + high = current; + if (low == high) return low + 1; + current = (low + high) >>> 1; + } +}; +spine.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; +}; + +spine.Curves = function (frameCount) { + this.curves = []; // type, x, y, ... + //this.curves.length = (frameCount - 1) * 19/*BEZIER_SIZE*/; +}; +spine.Curves.prototype = { + setLinear: function (frameIndex) { + this.curves[frameIndex * 19/*BEZIER_SIZE*/] = 0/*LINEAR*/; + }, + setStepped: function (frameIndex) { + this.curves[frameIndex * 19/*BEZIER_SIZE*/] = 1/*STEPPED*/; + }, + /** Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next. + * cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of + * the difference between the keyframe's values. */ + setCurve: function (frameIndex, cx1, cy1, cx2, cy2) { + var subdiv1 = 1 / 10/*BEZIER_SEGMENTS*/, subdiv2 = subdiv1 * subdiv1, subdiv3 = subdiv2 * subdiv1; + var pre1 = 3 * subdiv1, pre2 = 3 * subdiv2, pre4 = 6 * subdiv2, pre5 = 6 * subdiv3; + var tmp1x = -cx1 * 2 + cx2, tmp1y = -cy1 * 2 + cy2, tmp2x = (cx1 - cx2) * 3 + 1, tmp2y = (cy1 - cy2) * 3 + 1; + var dfx = cx1 * pre1 + tmp1x * pre2 + tmp2x * subdiv3, dfy = cy1 * pre1 + tmp1y * pre2 + tmp2y * subdiv3; + var ddfx = tmp1x * pre4 + tmp2x * pre5, ddfy = tmp1y * pre4 + tmp2y * pre5; + var dddfx = tmp2x * pre5, dddfy = tmp2y * pre5; + + var i = frameIndex * 19/*BEZIER_SIZE*/; + var curves = this.curves; + curves[i++] = 2/*BEZIER*/; + + var x = dfx, y = dfy; + for (var n = i + 19/*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; + } + }, + getCurvePercent: function (frameIndex, percent) { + percent = percent < 0 ? 0 : (percent > 1 ? 1 : percent); + var curves = this.curves; + var i = frameIndex * 19/*BEZIER_SIZE*/; + var type = curves[i]; + if (type === 0/*LINEAR*/) return percent; + if (type == 1/*STEPPED*/) return 0; + i++; + var x = 0; + for (var start = i, n = i + 19/*BEZIER_SIZE*/ - 1; i < n; i += 2) { + x = curves[i]; + if (x >= percent) { + var prevX, prevY; + 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); // Last point is 1,1. + } +}; + +spine.RotateTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, angle, ... + this.frames.length = frameCount * 2; +}; +spine.RotateTimeline.prototype = { + boneIndex: 0, + getFrameCount: function () { + return this.frames.length / 2; + }, + setFrame: function (frameIndex, time, angle) { + frameIndex *= 2; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = angle; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var bone = skeleton.bones[this.boneIndex]; + + if (time >= frames[frames.length - 2]) { // Time is after last frame. + var amount = bone.data.rotation + frames[frames.length - 1] - bone.rotation; + while (amount > 180) + amount -= 360; + while (amount < -180) + amount += 360; + bone.rotation += amount * alpha; + return; + } + + // Interpolate between the previous frame and the current frame. + var frameIndex = spine.Animation.binarySearch(frames, time, 2); + var prevFrameValue = frames[frameIndex - 1]; + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex - 2/*PREV_FRAME_TIME*/] - frameTime); + percent = this.curves.getCurvePercent(frameIndex / 2 - 1, percent); + + var amount = frames[frameIndex + 1/*FRAME_VALUE*/] - prevFrameValue; + while (amount > 180) + amount -= 360; + while (amount < -180) + amount += 360; + amount = bone.data.rotation + (prevFrameValue + amount * percent) - bone.rotation; + while (amount > 180) + amount -= 360; + while (amount < -180) + amount += 360; + bone.rotation += amount * alpha; + } +}; + +spine.TranslateTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, x, y, ... + this.frames.length = frameCount * 3; +}; +spine.TranslateTimeline.prototype = { + boneIndex: 0, + getFrameCount: function () { + return this.frames.length / 3; + }, + setFrame: function (frameIndex, time, x, y) { + frameIndex *= 3; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = x; + this.frames[frameIndex + 2] = y; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var bone = skeleton.bones[this.boneIndex]; + + if (time >= frames[frames.length - 3]) { // Time is after last frame. + bone.x += (bone.data.x + frames[frames.length - 2] - bone.x) * alpha; + bone.y += (bone.data.y + frames[frames.length - 1] - bone.y) * alpha; + return; + } + + // Interpolate between the previous frame and the current frame. + var frameIndex = spine.Animation.binarySearch(frames, time, 3); + var prevFrameX = frames[frameIndex - 2]; + var prevFrameY = frames[frameIndex - 1]; + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*PREV_FRAME_TIME*/] - frameTime); + percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + + bone.x += (bone.data.x + prevFrameX + (frames[frameIndex + 1/*FRAME_X*/] - prevFrameX) * percent - bone.x) * alpha; + bone.y += (bone.data.y + prevFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - prevFrameY) * percent - bone.y) * alpha; + } +}; + +spine.ScaleTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, x, y, ... + this.frames.length = frameCount * 3; +}; +spine.ScaleTimeline.prototype = { + boneIndex: 0, + getFrameCount: function () { + return this.frames.length / 3; + }, + setFrame: function (frameIndex, time, x, y) { + frameIndex *= 3; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = x; + this.frames[frameIndex + 2] = y; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var bone = skeleton.bones[this.boneIndex]; + + if (time >= frames[frames.length - 3]) { // Time is after last frame. + bone.scaleX += (bone.data.scaleX * frames[frames.length - 2] - bone.scaleX) * alpha; + bone.scaleY += (bone.data.scaleY * frames[frames.length - 1] - bone.scaleY) * alpha; + return; + } + + // Interpolate between the previous frame and the current frame. + var frameIndex = spine.Animation.binarySearch(frames, time, 3); + var prevFrameX = frames[frameIndex - 2]; + var prevFrameY = frames[frameIndex - 1]; + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*PREV_FRAME_TIME*/] - frameTime); + percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + + bone.scaleX += (bone.data.scaleX * (prevFrameX + (frames[frameIndex + 1/*FRAME_X*/] - prevFrameX) * percent) - bone.scaleX) * alpha; + bone.scaleY += (bone.data.scaleY * (prevFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - prevFrameY) * percent) - bone.scaleY) * alpha; + } +}; + +spine.ColorTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, r, g, b, a, ... + this.frames.length = frameCount * 5; +}; +spine.ColorTimeline.prototype = { + slotIndex: 0, + getFrameCount: function () { + return this.frames.length / 5; + }, + setFrame: function (frameIndex, time, r, g, b, a) { + frameIndex *= 5; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = r; + this.frames[frameIndex + 2] = g; + this.frames[frameIndex + 3] = b; + this.frames[frameIndex + 4] = a; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var r, g, b, a; + if (time >= frames[frames.length - 5]) { + // Time is after last frame. + var i = frames.length - 1; + r = frames[i - 3]; + g = frames[i - 2]; + b = frames[i - 1]; + a = frames[i]; + } else { + // Interpolate between the previous frame and the current frame. + var frameIndex = spine.Animation.binarySearch(frames, time, 5); + var prevFrameR = frames[frameIndex - 4]; + var prevFrameG = frames[frameIndex - 3]; + var prevFrameB = frames[frameIndex - 2]; + var prevFrameA = frames[frameIndex - 1]; + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex - 5/*PREV_FRAME_TIME*/] - frameTime); + percent = this.curves.getCurvePercent(frameIndex / 5 - 1, percent); + + r = prevFrameR + (frames[frameIndex + 1/*FRAME_R*/] - prevFrameR) * percent; + g = prevFrameG + (frames[frameIndex + 2/*FRAME_G*/] - prevFrameG) * percent; + b = prevFrameB + (frames[frameIndex + 3/*FRAME_B*/] - prevFrameB) * percent; + a = prevFrameA + (frames[frameIndex + 4/*FRAME_A*/] - prevFrameA) * percent; + } + var slot = skeleton.slots[this.slotIndex]; + if (alpha < 1) { + slot.r += (r - slot.r) * alpha; + slot.g += (g - slot.g) * alpha; + slot.b += (b - slot.b) * alpha; + slot.a += (a - slot.a) * alpha; + } else { + slot.r = r; + slot.g = g; + slot.b = b; + slot.a = a; + } + } +}; + +spine.AttachmentTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, ... + this.frames.length = frameCount; + this.attachmentNames = []; + this.attachmentNames.length = frameCount; +}; +spine.AttachmentTimeline.prototype = { + slotIndex: 0, + getFrameCount: function () { + return this.frames.length; + }, + setFrame: function (frameIndex, time, attachmentName) { + this.frames[frameIndex] = time; + this.attachmentNames[frameIndex] = attachmentName; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var frames = this.frames; + if (time < frames[0]) { + if (lastTime > time) this.apply(skeleton, lastTime, Number.MAX_VALUE, null, 0); + return; + } else if (lastTime > time) // + lastTime = -1; + + var frameIndex = time >= frames[frames.length - 1] ? frames.length - 1 : spine.Animation.binarySearch1(frames, time) - 1; + if (frames[frameIndex] < lastTime) return; + + var attachmentName = this.attachmentNames[frameIndex]; + skeleton.slots[this.slotIndex].setAttachment( + !attachmentName ? null : skeleton.getAttachmentBySlotIndex(this.slotIndex, attachmentName)); + } +}; + +spine.EventTimeline = function (frameCount) { + this.frames = []; // time, ... + this.frames.length = frameCount; + this.events = []; + this.events.length = frameCount; +}; +spine.EventTimeline.prototype = { + getFrameCount: function () { + return this.frames.length; + }, + setFrame: function (frameIndex, time, event) { + this.frames[frameIndex] = time; + this.events[frameIndex] = event; + }, + /** Fires events for frames > lastTime and <= time. */ + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + if (!firedEvents) return; + + var frames = this.frames; + var frameCount = frames.length; + + if (lastTime > time) { // Fire events after last time for looped animations. + this.apply(skeleton, lastTime, Number.MAX_VALUE, firedEvents, alpha); + lastTime = -1; + } else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame. + return; + if (time < frames[0]) return; // Time is before first frame. + + var frameIndex; + if (lastTime < frames[0]) + frameIndex = 0; + else { + frameIndex = spine.Animation.binarySearch1(frames, lastTime); + var frame = frames[frameIndex]; + while (frameIndex > 0) { // Fire multiple events with the same frame. + if (frames[frameIndex - 1] != frame) break; + frameIndex--; + } + } + var events = this.events; + for (; frameIndex < frameCount && time >= frames[frameIndex]; frameIndex++) + firedEvents.push(events[frameIndex]); + } +}; + +spine.DrawOrderTimeline = function (frameCount) { + this.frames = []; // time, ... + this.frames.length = frameCount; + this.drawOrders = []; + this.drawOrders.length = frameCount; +}; +spine.DrawOrderTimeline.prototype = { + getFrameCount: function () { + return this.frames.length; + }, + setFrame: function (frameIndex, time, drawOrder) { + this.frames[frameIndex] = time; + this.drawOrders[frameIndex] = drawOrder; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var frameIndex; + if (time >= frames[frames.length - 1]) // Time is after last frame. + frameIndex = frames.length - 1; + else + frameIndex = spine.Animation.binarySearch1(frames, time) - 1; + + var drawOrder = skeleton.drawOrder; + var slots = skeleton.slots; + var drawOrderToSetupIndex = this.drawOrders[frameIndex]; + if (!drawOrderToSetupIndex) { + for (var i = 0, n = slots.length; i < n; i++) + drawOrder[i] = slots[i]; + } else { + for (var i = 0, n = drawOrderToSetupIndex.length; i < n; i++) + drawOrder[i] = skeleton.slots[drawOrderToSetupIndex[i]]; + } + + } +}; + +spine.FfdTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; + this.frames.length = frameCount; + this.frameVertices = []; + this.frameVertices.length = frameCount; +}; +spine.FfdTimeline.prototype = { + slotIndex: 0, + attachment: 0, + getFrameCount: function () { + return this.frames.length; + }, + setFrame: function (frameIndex, time, vertices) { + this.frames[frameIndex] = time; + this.frameVertices[frameIndex] = vertices; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var slot = skeleton.slots[this.slotIndex]; + if (slot.attachment != this.attachment) return; + + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var frameVertices = this.frameVertices; + var vertexCount = frameVertices[0].length; + + var vertices = slot.attachmentVertices; + if (vertices.length != vertexCount) alpha = 1; + vertices.length = vertexCount; + + if (time >= frames[frames.length - 1]) { // Time is after last frame. + var lastVertices = frameVertices[frames.length - 1]; + if (alpha < 1) { + for (var i = 0; i < vertexCount; i++) + vertices[i] += (lastVertices[i] - vertices[i]) * alpha; + } else { + for (var i = 0; i < vertexCount; i++) + vertices[i] = lastVertices[i]; + } + return; + } + + // Interpolate between the previous frame and the current frame. + var frameIndex = spine.Animation.binarySearch1(frames, time); + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex - 1] - frameTime); + percent = this.curves.getCurvePercent(frameIndex - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent)); + + var prevVertices = frameVertices[frameIndex - 1]; + var nextVertices = frameVertices[frameIndex]; + + if (alpha < 1) { + for (var i = 0; i < vertexCount; i++) { + var prev = prevVertices[i]; + vertices[i] += (prev + (nextVertices[i] - prev) * percent - vertices[i]) * alpha; + } + } else { + for (var i = 0; i < vertexCount; i++) { + var prev = prevVertices[i]; + vertices[i] = prev + (nextVertices[i] - prev) * percent; + } + } + } +}; + +spine.IkConstraintTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, mix, bendDirection, ... + this.frames.length = frameCount * 3; +}; +spine.IkConstraintTimeline.prototype = { + ikConstraintIndex: 0, + getFrameCount: function () { + return this.frames.length / 3; + }, + setFrame: function (frameIndex, time, mix, bendDirection) { + frameIndex *= 3; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = mix; + this.frames[frameIndex + 2] = bendDirection; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var frames = this.frames; + if (time < frames[0]) return; // Time is before first frame. + + var ikConstraint = skeleton.ikConstraints[this.ikConstraintIndex]; + + if (time >= frames[frames.length - 3]) { // Time is after last frame. + ikConstraint.mix += (frames[frames.length - 2] - ikConstraint.mix) * alpha; + ikConstraint.bendDirection = frames[frames.length - 1]; + return; + } + + // Interpolate between the previous frame and the current frame. + var frameIndex = spine.Animation.binarySearch(frames, time, 3); + var prevFrameMix = frames[frameIndex + -2/*PREV_FRAME_MIX*/]; + var frameTime = frames[frameIndex]; + var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*PREV_FRAME_TIME*/] - frameTime); + percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); + + var mix = prevFrameMix + (frames[frameIndex + 1/*FRAME_MIX*/] - prevFrameMix) * percent; + ikConstraint.mix += (mix - ikConstraint.mix) * alpha; + ikConstraint.bendDirection = frames[frameIndex + -1/*PREV_FRAME_BEND_DIRECTION*/]; + } +}; + +spine.FlipXTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, flip, ... + this.frames.length = frameCount * 2; +}; +spine.FlipXTimeline.prototype = { + boneIndex: 0, + getFrameCount: function () { + return this.frames.length / 2; + }, + setFrame: function (frameIndex, time, flip) { + frameIndex *= 2; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = flip ? 1 : 0; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var frames = this.frames; + if (time < frames[0]) { + if (lastTime > time) this.apply(skeleton, lastTime, Number.MAX_VALUE, null, 0); + return; + } else if (lastTime > time) // + lastTime = -1; + var frameIndex = (time >= frames[frames.length - 2] ? frames.length : spine.Animation.binarySearch(frames, time, 2)) - 2; + if (frames[frameIndex] < lastTime) return; + skeleton.bones[this.boneIndex].flipX = frames[frameIndex + 1] != 0; + } +}; + +spine.FlipYTimeline = function (frameCount) { + this.curves = new spine.Curves(frameCount); + this.frames = []; // time, flip, ... + this.frames.length = frameCount * 2; +}; +spine.FlipYTimeline.prototype = { + boneIndex: 0, + getFrameCount: function () { + return this.frames.length / 2; + }, + setFrame: function (frameIndex, time, flip) { + frameIndex *= 2; + this.frames[frameIndex] = time; + this.frames[frameIndex + 1] = flip ? 1 : 0; + }, + apply: function (skeleton, lastTime, time, firedEvents, alpha) { + var frames = this.frames; + if (time < frames[0]) { + if (lastTime > time) this.apply(skeleton, lastTime, Number.MAX_VALUE, null, 0); + return; + } else if (lastTime > time) // + lastTime = -1; + var frameIndex = (time >= frames[frames.length - 2] ? frames.length : spine.Animation.binarySearch(frames, time, 2)) - 2; + if (frames[frameIndex] < lastTime) return; + skeleton.bones[this.boneIndex].flipY = frames[frameIndex + 1] != 0; + } +}; + +spine.SkeletonData = function () { + this.bones = []; + this.slots = []; + this.skins = []; + this.events = []; + this.animations = []; + this.ikConstraints = []; +}; +spine.SkeletonData.prototype = { + name: null, + defaultSkin: null, + width: 0, height: 0, + version: null, hash: null, + /** @return May be null. */ + findBone: function (boneName) { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + if (bones[i].name == boneName) return bones[i]; + return null; + }, + /** @return -1 if the bone was not found. */ + findBoneIndex: function (boneName) { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + if (bones[i].name == boneName) return i; + return -1; + }, + /** @return May be null. */ + findSlot: function (slotName) { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) { + if (slots[i].name == slotName) return slot[i]; + } + return null; + }, + /** @return -1 if the bone was not found. */ + findSlotIndex: function (slotName) { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) + if (slots[i].name == slotName) return i; + return -1; + }, + /** @return May be null. */ + findSkin: function (skinName) { + var skins = this.skins; + for (var i = 0, n = skins.length; i < n; i++) + if (skins[i].name == skinName) return skins[i]; + return null; + }, + /** @return May be null. */ + findEvent: function (eventName) { + var events = this.events; + for (var i = 0, n = events.length; i < n; i++) + if (events[i].name == eventName) return events[i]; + return null; + }, + /** @return May be null. */ + findAnimation: function (animationName) { + var animations = this.animations; + for (var i = 0, n = animations.length; i < n; i++) + if (animations[i].name == animationName) return animations[i]; + return null; + }, + /** @return May be null. */ + findIkConstraint: function (ikConstraintName) { + var ikConstraints = this.ikConstraints; + for (var i = 0, n = ikConstraints.length; i < n; i++) + if (ikConstraints[i].name == ikConstraintName) return ikConstraints[i]; + return null; + } +}; + +spine.Skeleton = function (skeletonData) { + this.data = skeletonData; + + this.bones = []; + for (var i = 0, n = skeletonData.bones.length; i < n; i++) { + var boneData = skeletonData.bones[i]; + var parent = !boneData.parent ? null : this.bones[skeletonData.bones.indexOf(boneData.parent)]; + this.bones.push(new spine.Bone(boneData, this, parent)); + } + + this.slots = []; + this.drawOrder = []; + for (var i = 0, n = skeletonData.slots.length; i < n; i++) { + var slotData = skeletonData.slots[i]; + var bone = this.bones[skeletonData.bones.indexOf(slotData.boneData)]; + var slot = new spine.Slot(slotData, bone); + this.slots.push(slot); + this.drawOrder.push(slot); + } + + this.ikConstraints = []; + for (var i = 0, n = skeletonData.ikConstraints.length; i < n; i++) + this.ikConstraints.push(new spine.IkConstraint(skeletonData.ikConstraints[i], this)); + + this.boneCache = []; + this.updateCache(); +}; +spine.Skeleton.prototype = { + x: 0, y: 0, + skin: null, + r: 1, g: 1, b: 1, a: 1, + time: 0, + flipX: false, flipY: false, + /** Caches information about bones and IK constraints. Must be called if bones or IK constraints are added or removed. */ + updateCache: function () { + var ikConstraints = this.ikConstraints; + var ikConstraintsCount = ikConstraints.length; + + var arrayCount = ikConstraintsCount + 1; + var boneCache = this.boneCache; + if (boneCache.length > arrayCount) boneCache.length = arrayCount; + for (var i = 0, n = boneCache.length; i < n; i++) + boneCache[i].length = 0; + while (boneCache.length < arrayCount) + boneCache[boneCache.length] = []; + + var nonIkBones = boneCache[0]; + var bones = this.bones; + + outer: + for (var i = 0, n = bones.length; i < n; i++) { + var bone = bones[i]; + var current = bone; + do { + for (var ii = 0; ii < ikConstraintsCount; ii++) { + var ikConstraint = ikConstraints[ii]; + var parent = ikConstraint.bones[0]; + var child= ikConstraint.bones[ikConstraint.bones.length - 1]; + while (true) { + if (current == child) { + boneCache[ii].push(bone); + boneCache[ii + 1].push(bone); + continue outer; + } + if (child == parent) break; + child = child.parent; + } + } + current = current.parent; + } while (current); + nonIkBones[nonIkBones.length] = bone; + } + }, + /** Updates the world transform for each bone. */ + updateWorldTransform: function () { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) { + var bone = bones[i]; + bone.rotationIK = bone.rotation; + } + var i = 0, last = this.boneCache.length - 1; + while (true) { + var cacheBones = this.boneCache[i]; + for (var ii = 0, nn = cacheBones.length; ii < nn; ii++) + cacheBones[ii].updateWorldTransform(); + if (i == last) break; + this.ikConstraints[i].apply(); + i++; + } + }, + /** Sets the bones and slots to their setup pose values. */ + setToSetupPose: function () { + this.setBonesToSetupPose(); + this.setSlotsToSetupPose(); + }, + 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 ikConstraint = ikConstraints[i]; + ikConstraint.bendDirection = ikConstraint.data.bendDirection; + ikConstraint.mix = ikConstraint.data.mix; + } + }, + setSlotsToSetupPose: function () { + var slots = this.slots; + var drawOrder = this.drawOrder; + for (var i = 0, n = slots.length; i < n; i++) { + drawOrder[i] = slots[i]; + slots[i].setToSetupPose(i); + } + }, + /** @return May return null. */ + getRootBone: function () { + return this.bones.length ? this.bones[0] : null; + }, + /** @return May be null. */ + findBone: function (boneName) { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + if (bones[i].data.name == boneName) return bones[i]; + return null; + }, + /** @return -1 if the bone was not found. */ + findBoneIndex: function (boneName) { + var bones = this.bones; + for (var i = 0, n = bones.length; i < n; i++) + if (bones[i].data.name == boneName) return i; + return -1; + }, + /** @return May be null. */ + findSlot: function (slotName) { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) + if (slots[i].data.name == slotName) return slots[i]; + return null; + }, + /** @return -1 if the bone was not found. */ + findSlotIndex: function (slotName) { + var slots = this.slots; + for (var i = 0, n = slots.length; i < n; i++) + if (slots[i].data.name == slotName) return i; + return -1; + }, + setSkinByName: function (skinName) { + var skin = this.data.findSkin(skinName); + if (!skin) throw new Error("Skin not found: " + skinName); + this.setSkin(skin); + }, + /** Sets the skin used to look up attachments before looking in the {@link SkeletonData#getDefaultSkin() default skin}. + * Attachments from the new skin are attached if the corresponding attachment from the old skin was attached. If there was + * no old skin, each slot's setup mode attachment is attached from the new skin. + * @param newSkin May be null. */ + setSkin: function (newSkin) { + if (newSkin) { + if (this.skin) + 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 = slot.data.attachmentName; + if (name) { + var attachment = newSkin.getAttachment(i, name); + if (attachment) slot.setAttachment(attachment); + } + } + } + } + this.skin = newSkin; + }, + /** @return May be null. */ + getAttachmentBySlotName: function (slotName, attachmentName) { + return this.getAttachmentBySlotIndex(this.data.findSlotIndex(slotName), attachmentName); + }, + /** @return May be null. */ + getAttachmentBySlotIndex: function (slotIndex, attachmentName) { + if (this.skin) { + var attachment = this.skin.getAttachment(slotIndex, attachmentName); + if (attachment) return attachment; + } + if (this.data.defaultSkin) return this.data.defaultSkin.getAttachment(slotIndex, attachmentName); + return null; + }, + /** @param attachmentName May be null. */ + setAttachment: function (slotName, attachmentName) { + 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) { + attachment = this.getAttachmentBySlotIndex(i, attachmentName); + if (!attachment) throw new Error("Attachment not found: " + attachmentName + ", for slot: " + slotName); + } + slot.setAttachment(attachment); + return; + } + } + throw new Error("Slot not found: " + slotName); + }, + /** @return May be null. */ + findIkConstraint: function (ikConstraintName) { + var ikConstraints = this.ikConstraints; + for (var i = 0, n = ikConstraints.length; i < n; i++) + if (ikConstraints[i].data.name == ikConstraintName) return ikConstraints[i]; + return null; + }, + update: function (delta) { + this.time += delta; + } +}; + +spine.EventData = function (name) { + this.name = name; +}; +spine.EventData.prototype = { + intValue: 0, + floatValue: 0, + stringValue: null +}; + +spine.Event = function (data) { + this.data = data; +}; +spine.Event.prototype = { + intValue: 0, + floatValue: 0, + stringValue: null +}; + +spine.AttachmentType = { + region: 0, + boundingbox: 1, + mesh: 2, + skinnedmesh: 3 +}; + +spine.RegionAttachment = function (name) { + this.name = name; + this.offset = []; + this.offset.length = 8; + this.uvs = []; + this.uvs.length = 8; +}; +spine.RegionAttachment.prototype = { + type: spine.AttachmentType.region, + x: 0, y: 0, + rotation: 0, + scaleX: 1, scaleY: 1, + width: 0, height: 0, + r: 1, g: 1, b: 1, a: 1, + path: null, + rendererObject: null, + regionOffsetX: 0, regionOffsetY: 0, + regionWidth: 0, regionHeight: 0, + regionOriginalWidth: 0, regionOriginalHeight: 0, + setUVs: function (u, v, u2, v2, rotate) { + var uvs = this.uvs; + if (rotate) { + uvs[2/*X2*/] = u; + uvs[3/*Y2*/] = v2; + uvs[4/*X3*/] = u; + uvs[5/*Y3*/] = v; + uvs[6/*X4*/] = u2; + uvs[7/*Y4*/] = v; + uvs[0/*X1*/] = u2; + uvs[1/*Y1*/] = v2; + } else { + uvs[0/*X1*/] = u; + uvs[1/*Y1*/] = v2; + uvs[2/*X2*/] = u; + uvs[3/*Y2*/] = v; + uvs[4/*X3*/] = u2; + uvs[5/*Y3*/] = v; + uvs[6/*X4*/] = u2; + uvs[7/*Y4*/] = v2; + } + }, + updateOffset: function () { + var regionScaleX = this.width / this.regionOriginalWidth * this.scaleX; + var regionScaleY = this.height / this.regionOriginalHeight * this.scaleY; + var localX = -this.width / 2 * this.scaleX + this.regionOffsetX * regionScaleX; + var localY = -this.height / 2 * this.scaleY + this.regionOffsetY * regionScaleY; + var localX2 = localX + this.regionWidth * regionScaleX; + var localY2 = localY + this.regionHeight * regionScaleY; + var radians = this.rotation * spine.degRad; + 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[0/*X1*/] = localXCos - localYSin; + offset[1/*Y1*/] = localYCos + localXSin; + offset[2/*X2*/] = localXCos - localY2Sin; + offset[3/*Y2*/] = localY2Cos + localXSin; + offset[4/*X3*/] = localX2Cos - localY2Sin; + offset[5/*Y3*/] = localY2Cos + localX2Sin; + offset[6/*X4*/] = localX2Cos - localYSin; + offset[7/*Y4*/] = localYCos + localX2Sin; + }, + computeVertices: function (x, y, bone, vertices) { + x += bone.worldX; + y += bone.worldY; + var m00 = bone.m00, m01 = bone.m01, m10 = bone.m10, m11 = bone.m11; + var offset = this.offset; + vertices[0/*X1*/] = offset[0/*X1*/] * m00 + offset[1/*Y1*/] * m01 + x; + vertices[1/*Y1*/] = offset[0/*X1*/] * m10 + offset[1/*Y1*/] * m11 + y; + vertices[2/*X2*/] = offset[2/*X2*/] * m00 + offset[3/*Y2*/] * m01 + x; + vertices[3/*Y2*/] = offset[2/*X2*/] * m10 + offset[3/*Y2*/] * m11 + y; + vertices[4/*X3*/] = offset[4/*X3*/] * m00 + offset[5/*X3*/] * m01 + x; + vertices[5/*X3*/] = offset[4/*X3*/] * m10 + offset[5/*X3*/] * m11 + y; + vertices[6/*X4*/] = offset[6/*X4*/] * m00 + offset[7/*Y4*/] * m01 + x; + vertices[7/*Y4*/] = offset[6/*X4*/] * m10 + offset[7/*Y4*/] * m11 + y; + } +}; + +spine.MeshAttachment = function (name) { + this.name = name; +}; +spine.MeshAttachment.prototype = { + type: spine.AttachmentType.mesh, + vertices: null, + uvs: null, + regionUVs: null, + triangles: null, + hullLength: 0, + r: 1, g: 1, b: 1, a: 1, + path: null, + rendererObject: null, + regionU: 0, regionV: 0, regionU2: 0, regionV2: 0, regionRotate: false, + regionOffsetX: 0, regionOffsetY: 0, + regionWidth: 0, regionHeight: 0, + regionOriginalWidth: 0, regionOriginalHeight: 0, + edges: null, + width: 0, height: 0, + updateUVs: function () { + var width = this.regionU2 - this.regionU, height = this.regionV2 - this.regionV; + var n = this.regionUVs.length; + if (!this.uvs || this.uvs.length != n) { + this.uvs = new spine.Float32Array(n); + } + if (this.regionRotate) { + for (var i = 0; i < n; i += 2) { + this.uvs[i] = this.regionU + this.regionUVs[i + 1] * width; + this.uvs[i + 1] = this.regionV + height - this.regionUVs[i] * height; + } + } else { + for (var i = 0; i < n; i += 2) { + this.uvs[i] = this.regionU + this.regionUVs[i] * width; + this.uvs[i + 1] = this.regionV + this.regionUVs[i + 1] * height; + } + } + }, + computeWorldVertices: function (x, y, slot, worldVertices) { + var bone = slot.bone; + x += bone.worldX; + y += bone.worldY; + var m00 = bone.m00, m01 = bone.m01, m10 = bone.m10, m11 = bone.m11; + var vertices = this.vertices; + var verticesCount = vertices.length; + if (slot.attachmentVertices.length == verticesCount) vertices = slot.attachmentVertices; + for (var i = 0; i < verticesCount; i += 2) { + var vx = vertices[i]; + var vy = vertices[i + 1]; + worldVertices[i] = vx * m00 + vy * m01 + x; + worldVertices[i + 1] = vx * m10 + vy * m11 + y; + } + } +}; + +spine.SkinnedMeshAttachment = function (name) { + this.name = name; +}; +spine.SkinnedMeshAttachment.prototype = { + type: spine.AttachmentType.skinnedmesh, + bones: null, + weights: null, + uvs: null, + regionUVs: null, + triangles: null, + hullLength: 0, + r: 1, g: 1, b: 1, a: 1, + path: null, + rendererObject: null, + regionU: 0, regionV: 0, regionU2: 0, regionV2: 0, regionRotate: false, + regionOffsetX: 0, regionOffsetY: 0, + regionWidth: 0, regionHeight: 0, + regionOriginalWidth: 0, regionOriginalHeight: 0, + edges: null, + width: 0, height: 0, + updateUVs: function (u, v, u2, v2, rotate) { + var width = this.regionU2 - this.regionU, height = this.regionV2 - this.regionV; + var n = this.regionUVs.length; + if (!this.uvs || this.uvs.length != n) { + this.uvs = new spine.Float32Array(n); + } + if (this.regionRotate) { + for (var i = 0; i < n; i += 2) { + this.uvs[i] = this.regionU + this.regionUVs[i + 1] * width; + this.uvs[i + 1] = this.regionV + height - this.regionUVs[i] * height; + } + } else { + for (var i = 0; i < n; i += 2) { + this.uvs[i] = this.regionU + this.regionUVs[i] * width; + this.uvs[i + 1] = this.regionV + this.regionUVs[i + 1] * height; + } + } + }, + computeWorldVertices: function (x, y, slot, worldVertices) { + var skeletonBones = slot.bone.skeleton.bones; + var weights = this.weights; + var bones = this.bones; + + var w = 0, v = 0, b = 0, f = 0, n = bones.length, nn; + var wx, wy, bone, vx, vy, weight; + if (!slot.attachmentVertices.length) { + for (; v < n; w += 2) { + wx = 0; + wy = 0; + nn = bones[v++] + v; + for (; v < nn; v++, b += 3) { + bone = skeletonBones[bones[v]]; + vx = weights[b]; + vy = weights[b + 1]; + weight = weights[b + 2]; + wx += (vx * bone.m00 + vy * bone.m01 + bone.worldX) * weight; + wy += (vx * bone.m10 + vy * bone.m11 + bone.worldY) * weight; + } + worldVertices[w] = wx + x; + worldVertices[w + 1] = wy + y; + } + } else { + var ffd = slot.attachmentVertices; + for (; v < n; w += 2) { + wx = 0; + wy = 0; + nn = bones[v++] + v; + for (; v < nn; v++, b += 3, f += 2) { + bone = skeletonBones[bones[v]]; + vx = weights[b] + ffd[f]; + vy = weights[b + 1] + ffd[f + 1]; + weight = weights[b + 2]; + wx += (vx * bone.m00 + vy * bone.m01 + bone.worldX) * weight; + wy += (vx * bone.m10 + vy * bone.m11 + bone.worldY) * weight; + } + worldVertices[w] = wx + x; + worldVertices[w + 1] = wy + y; + } + } + } +}; + +spine.BoundingBoxAttachment = function (name) { + this.name = name; + this.vertices = []; +}; +spine.BoundingBoxAttachment.prototype = { + type: spine.AttachmentType.boundingbox, + computeWorldVertices: function (x, y, bone, worldVertices) { + x += bone.worldX; + y += bone.worldY; + var m00 = bone.m00, m01 = bone.m01, m10 = bone.m10, m11 = bone.m11; + var vertices = this.vertices; + for (var i = 0, n = vertices.length; i < n; i += 2) { + var px = vertices[i]; + var py = vertices[i + 1]; + worldVertices[i] = px * m00 + py * m01 + x; + worldVertices[i + 1] = px * m10 + py * m11 + y; + } + } +}; + +spine.AnimationStateData = function (skeletonData) { + this.skeletonData = skeletonData; + this.animationToMixTime = {}; +}; +spine.AnimationStateData.prototype = { + defaultMix: 0, + setMixByName: function (fromName, toName, duration) { + var from = this.skeletonData.findAnimation(fromName); + if (!from) throw new Error("Animation not found: " + fromName); + var to = this.skeletonData.findAnimation(toName); + if (!to) throw new Error("Animation not found: " + toName); + this.setMix(from, to, duration); + }, + setMix: function (from, to, duration) { + this.animationToMixTime[from.name + ":" + to.name] = duration; + }, + getMix: function (from, to) { + var key = from.name + ":" + to.name; + return this.animationToMixTime.hasOwnProperty(key) ? this.animationToMixTime[key] : this.defaultMix; + } +}; + +spine.TrackEntry = function () {}; +spine.TrackEntry.prototype = { + next: null, previous: null, + animation: null, + loop: false, + delay: 0, time: 0, lastTime: -1, endTime: 0, + timeScale: 1, + mixTime: 0, mixDuration: 0, mix: 1, + onStart: null, onEnd: null, onComplete: null, onEvent: null +}; + +spine.AnimationState = function (stateData) { + this.data = stateData; + this.tracks = []; + this.events = []; +}; +spine.AnimationState.prototype = { + onStart: null, + onEnd: null, + onComplete: null, + onEvent: null, + timeScale: 1, + update: function (delta) { + delta *= this.timeScale; + for (var i = 0; i < this.tracks.length; i++) { + var current = this.tracks[i]; + if (!current) continue; + + current.time += delta * current.timeScale; + if (current.previous) { + var previousDelta = delta * current.previous.timeScale; + current.previous.time += previousDelta; + current.mixTime += previousDelta; + } + + var next = current.next; + if (next) { + next.time = current.lastTime - next.delay; + if (next.time >= 0) this.setCurrent(i, next); + } else { + // End non-looping animation when it reaches its end time and there is no next entry. + if (!current.loop && current.lastTime >= current.endTime) this.clearTrack(i); + } + } + }, + apply: function (skeleton) { + for (var i = 0; i < this.tracks.length; i++) { + var current = this.tracks[i]; + if (!current) continue; + + this.events.length = 0; + + var time = current.time; + var lastTime = current.lastTime; + var endTime = current.endTime; + var loop = current.loop; + if (!loop && time > endTime) time = endTime; + + var previous = current.previous; + if (!previous) { + if (current.mix == 1) + current.animation.apply(skeleton, current.lastTime, time, loop, this.events); + else + current.animation.mix(skeleton, current.lastTime, time, loop, this.events, current.mix); + } else { + var previousTime = previous.time; + if (!previous.loop && previousTime > previous.endTime) previousTime = previous.endTime; + previous.animation.apply(skeleton, previousTime, previousTime, previous.loop, null); + + var alpha = current.mixTime / current.mixDuration * current.mix; + if (alpha >= 1) { + alpha = 1; + current.previous = null; + } + current.animation.mix(skeleton, current.lastTime, time, loop, this.events, alpha); + } + + for (var ii = 0, nn = this.events.length; ii < nn; ii++) { + var event = this.events[ii]; + if (current.onEvent) current.onEvent(i, event); + if (this.onEvent) this.onEvent(i, event); + } + + // Check if completed the animation or a loop iteration. + if (loop ? (lastTime % endTime > time % endTime) : (lastTime < endTime && time >= endTime)) { + var count = Math.floor(time / endTime); + if (current.onComplete) current.onComplete(i, count); + if (this.onComplete) this.onComplete(i, count); + } + + current.lastTime = current.time; + } + }, + clearTracks: function () { + for (var i = 0, n = this.tracks.length; i < n; i++) + this.clearTrack(i); + this.tracks.length = 0; + }, + clearTrack: function (trackIndex) { + if (trackIndex >= this.tracks.length) return; + var current = this.tracks[trackIndex]; + if (!current) return; + + if (current.onEnd) current.onEnd(trackIndex); + if (this.onEnd) this.onEnd(trackIndex); + + this.tracks[trackIndex] = null; + }, + _expandToIndex: function (index) { + if (index < this.tracks.length) return this.tracks[index]; + while (index >= this.tracks.length) + this.tracks.push(null); + return null; + }, + setCurrent: function (index, entry) { + var current = this._expandToIndex(index); + if (current) { + var previous = current.previous; + current.previous = null; + + if (current.onEnd) current.onEnd(index); + if (this.onEnd) this.onEnd(index); + + entry.mixDuration = this.data.getMix(current.animation, entry.animation); + if (entry.mixDuration > 0) { + entry.mixTime = 0; + // If a mix is in progress, mix from the closest animation. + if (previous && current.mixTime / current.mixDuration < 0.5) + entry.previous = previous; + else + entry.previous = current; + } + } + + this.tracks[index] = entry; + + if (entry.onStart) entry.onStart(index); + if (this.onStart) this.onStart(index); + }, + setAnimationByName: function (trackIndex, animationName, loop) { + var animation = this.data.skeletonData.findAnimation(animationName); + if (!animation) throw new Error("Animation not found: " + animationName); + return this.setAnimation(trackIndex, animation, loop); + }, + /** Set the current animation. Any queued animations are cleared. */ + setAnimation: function (trackIndex, animation, loop) { + var entry = new spine.TrackEntry(); + entry.animation = animation; + entry.loop = loop; + entry.endTime = animation.duration; + this.setCurrent(trackIndex, entry); + return entry; + }, + addAnimationByName: function (trackIndex, animationName, loop, delay) { + var animation = this.data.skeletonData.findAnimation(animationName); + if (!animation) throw new Error("Animation not found: " + animationName); + return this.addAnimation(trackIndex, animation, loop, delay); + }, + /** Adds an animation to be played delay seconds after the current or last queued animation. + * @param delay May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay. */ + addAnimation: function (trackIndex, animation, loop, delay) { + var entry = new spine.TrackEntry(); + entry.animation = animation; + entry.loop = loop; + entry.endTime = animation.duration; + + var last = this._expandToIndex(trackIndex); + if (last) { + while (last.next) + last = last.next; + last.next = entry; + } else + this.tracks[trackIndex] = entry; + + if (delay <= 0) { + if (last) + delay += last.endTime - this.data.getMix(last.animation, animation); + else + delay = 0; + } + entry.delay = delay; + + return entry; + }, + /** May be null. */ + getCurrent: function (trackIndex) { + if (trackIndex >= this.tracks.length) return null; + return this.tracks[trackIndex]; + } +}; + +spine.SkeletonJson = function (attachmentLoader) { + this.attachmentLoader = attachmentLoader; +}; +spine.SkeletonJson.prototype = { + scale: 1, + readSkeletonData: function (root, name) { + var skeletonData = new spine.SkeletonData(); + skeletonData.name = name; + + // Skeleton. + var skeletonMap = root["skeleton"]; + if (skeletonMap) { + skeletonData.hash = skeletonMap["hash"]; + skeletonData.version = skeletonMap["spine"]; + skeletonData.width = skeletonMap["width"] || 0; + skeletonData.height = skeletonMap["height"] || 0; + } + + // Bones. + var bones = root["bones"]; + for (var i = 0, n = bones.length; i < n; i++) { + var boneMap = bones[i]; + var parent = null; + if (boneMap["parent"]) { + parent = skeletonData.findBone(boneMap["parent"]); + if (!parent) throw new Error("Parent bone not found: " + boneMap["parent"]); + } + var boneData = new spine.BoneData(boneMap["name"], parent); + boneData.length = (boneMap["length"] || 0) * this.scale; + boneData.x = (boneMap["x"] || 0) * this.scale; + boneData.y = (boneMap["y"] || 0) * this.scale; + boneData.rotation = (boneMap["rotation"] || 0); + boneData.scaleX = boneMap.hasOwnProperty("scaleX") ? boneMap["scaleX"] : 1; + boneData.scaleY = boneMap.hasOwnProperty("scaleY") ? boneMap["scaleY"] : 1; + boneData.inheritScale = boneMap.hasOwnProperty("inheritScale") ? boneMap["inheritScale"] : true; + boneData.inheritRotation = boneMap.hasOwnProperty("inheritRotation") ? boneMap["inheritRotation"] : true; + skeletonData.bones.push(boneData); + } + + // IK constraints. + var ik = root["ik"]; + if (ik) { + for (var i = 0, n = ik.length; i < n; i++) { + var ikMap = ik[i]; + var ikConstraintData = new spine.IkConstraintData(ikMap["name"]); + + var bones = ikMap["bones"]; + for (var ii = 0, nn = bones.length; ii < nn; ii++) { + var bone = skeletonData.findBone(bones[ii]); + if (!bone) throw new Error("IK bone not found: " + bones[ii]); + ikConstraintData.bones.push(bone); + } + + ikConstraintData.target = skeletonData.findBone(ikMap["target"]); + if (!ikConstraintData.target) throw new Error("Target bone not found: " + ikMap["target"]); + + ikConstraintData.bendDirection = (!ikMap.hasOwnProperty("bendPositive") || ikMap["bendPositive"]) ? 1 : -1; + ikConstraintData.mix = ikMap.hasOwnProperty("mix") ? ikMap["mix"] : 1; + + skeletonData.ikConstraints.push(ikConstraintData); + } + } + + // Slots. + var slots = root["slots"]; + for (var i = 0, n = slots.length; i < n; i++) { + var slotMap = slots[i]; + var boneData = skeletonData.findBone(slotMap["bone"]); + if (!boneData) throw new Error("Slot bone not found: " + slotMap["bone"]); + var slotData = new spine.SlotData(slotMap["name"], boneData); + + var color = slotMap["color"]; + if (color) { + slotData.r = this.toColor(color, 0); + slotData.g = this.toColor(color, 1); + slotData.b = this.toColor(color, 2); + slotData.a = this.toColor(color, 3); + } + + slotData.attachmentName = slotMap["attachment"]; + slotData.blendMode = spine.AttachmentType[slotMap["blend"] || "normal"]; + + skeletonData.slots.push(slotData); + } + + // Skins. + var skins = root["skins"]; + for (var skinName in skins) { + if (!skins.hasOwnProperty(skinName)) continue; + var skinMap = skins[skinName]; + var skin = new spine.Skin(skinName); + for (var slotName in skinMap) { + if (!skinMap.hasOwnProperty(slotName)) continue; + var slotIndex = skeletonData.findSlotIndex(slotName); + var slotEntry = skinMap[slotName]; + for (var attachmentName in slotEntry) { + if (!slotEntry.hasOwnProperty(attachmentName)) continue; + var attachment = this.readAttachment(skin, attachmentName, slotEntry[attachmentName]); + if (attachment) skin.addAttachment(slotIndex, attachmentName, attachment); + } + } + skeletonData.skins.push(skin); + if (skin.name == "default") skeletonData.defaultSkin = skin; + } + + // Events. + var events = root["events"]; + for (var eventName in events) { + if (!events.hasOwnProperty(eventName)) continue; + var eventMap = events[eventName]; + var eventData = new spine.EventData(eventName); + eventData.intValue = eventMap["int"] || 0; + eventData.floatValue = eventMap["float"] || 0; + eventData.stringValue = eventMap["string"] || null; + skeletonData.events.push(eventData); + } + + // Animations. + var animations = root["animations"]; + for (var animationName in animations) { + if (!animations.hasOwnProperty(animationName)) continue; + this.readAnimation(animationName, animations[animationName], skeletonData); + } + + return skeletonData; + }, + readAttachment: function (skin, name, map) { + name = map["name"] || name; + + var type = spine.AttachmentType[map["type"] || "region"]; + var path = map["path"] || name; + + var scale = this.scale; + if (type == spine.AttachmentType.region) { + var region = this.attachmentLoader.newRegionAttachment(skin, name, path); + if (!region) return null; + region.path = path; + region.x = (map["x"] || 0) * scale; + region.y = (map["y"] || 0) * scale; + region.scaleX = map.hasOwnProperty("scaleX") ? map["scaleX"] : 1; + region.scaleY = map.hasOwnProperty("scaleY") ? map["scaleY"] : 1; + region.rotation = map["rotation"] || 0; + region.width = (map["width"] || 0) * scale; + region.height = (map["height"] || 0) * scale; + + var color = map["color"]; + if (color) { + region.r = this.toColor(color, 0); + region.g = this.toColor(color, 1); + region.b = this.toColor(color, 2); + region.a = this.toColor(color, 3); + } + + region.updateOffset(); + return region; + } else if (type == spine.AttachmentType.mesh) { + var mesh = this.attachmentLoader.newMeshAttachment(skin, name, path); + if (!mesh) return null; + mesh.path = path; + mesh.vertices = this.getFloatArray(map, "vertices", scale); + mesh.triangles = this.getIntArray(map, "triangles"); + mesh.regionUVs = this.getFloatArray(map, "uvs", 1); + mesh.updateUVs(); + + color = map["color"]; + if (color) { + mesh.r = this.toColor(color, 0); + mesh.g = this.toColor(color, 1); + mesh.b = this.toColor(color, 2); + mesh.a = this.toColor(color, 3); + } + + mesh.hullLength = (map["hull"] || 0) * 2; + if (map["edges"]) mesh.edges = this.getIntArray(map, "edges"); + mesh.width = (map["width"] || 0) * scale; + mesh.height = (map["height"] || 0) * scale; + return mesh; + } else if (type == spine.AttachmentType.skinnedmesh) { + var mesh = this.attachmentLoader.newSkinnedMeshAttachment(skin, name, path); + if (!mesh) return null; + mesh.path = path; + + var uvs = this.getFloatArray(map, "uvs", 1); + var vertices = this.getFloatArray(map, "vertices", 1); + var weights = []; + var bones = []; + for (var i = 0, n = vertices.length; i < n; ) { + var boneCount = vertices[i++] | 0; + bones[bones.length] = boneCount; + for (var nn = i + boneCount * 4; i < nn; ) { + bones[bones.length] = vertices[i]; + weights[weights.length] = vertices[i + 1] * scale; + weights[weights.length] = vertices[i + 2] * scale; + weights[weights.length] = vertices[i + 3]; + i += 4; + } + } + mesh.bones = bones; + mesh.weights = weights; + mesh.triangles = this.getIntArray(map, "triangles"); + mesh.regionUVs = uvs; + mesh.updateUVs(); + + color = map["color"]; + if (color) { + mesh.r = this.toColor(color, 0); + mesh.g = this.toColor(color, 1); + mesh.b = this.toColor(color, 2); + mesh.a = this.toColor(color, 3); + } + + mesh.hullLength = (map["hull"] || 0) * 2; + if (map["edges"]) mesh.edges = this.getIntArray(map, "edges"); + mesh.width = (map["width"] || 0) * scale; + mesh.height = (map["height"] || 0) * scale; + return mesh; + } else if (type == spine.AttachmentType.boundingbox) { + var attachment = this.attachmentLoader.newBoundingBoxAttachment(skin, name); + var vertices = map["vertices"]; + for (var i = 0, n = vertices.length; i < n; i++) + attachment.vertices.push(vertices[i] * scale); + return attachment; + } + throw new Error("Unknown attachment type: " + type); + }, + readAnimation: function (name, map, skeletonData) { + var timelines = []; + var duration = 0; + + var slots = map["slots"]; + for (var slotName in slots) { + if (!slots.hasOwnProperty(slotName)) continue; + var slotMap = slots[slotName]; + var slotIndex = skeletonData.findSlotIndex(slotName); + + for (var timelineName in slotMap) { + if (!slotMap.hasOwnProperty(timelineName)) continue; + var values = slotMap[timelineName]; + if (timelineName == "color") { + var timeline = new spine.ColorTimeline(values.length); + timeline.slotIndex = slotIndex; + + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; + var color = valueMap["color"]; + var r = this.toColor(color, 0); + var g = this.toColor(color, 1); + var b = this.toColor(color, 2); + var a = this.toColor(color, 3); + timeline.setFrame(frameIndex, valueMap["time"], r, g, b, a); + this.readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 5 - 5]); + + } else if (timelineName == "attachment") { + var timeline = new spine.AttachmentTimeline(values.length); + timeline.slotIndex = slotIndex; + + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; + timeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + + } else + throw new Error("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"); + } + } + + var bones = map["bones"]; + for (var boneName in bones) { + if (!bones.hasOwnProperty(boneName)) continue; + var boneIndex = skeletonData.findBoneIndex(boneName); + if (boneIndex == -1) throw new Error("Bone not found: " + boneName); + var boneMap = bones[boneName]; + + for (var timelineName in boneMap) { + if (!boneMap.hasOwnProperty(timelineName)) continue; + var values = boneMap[timelineName]; + if (timelineName == "rotate") { + var timeline = new spine.RotateTimeline(values.length); + timeline.boneIndex = boneIndex; + + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; + timeline.setFrame(frameIndex, valueMap["time"], valueMap["angle"]); + this.readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 2 - 2]); + + } else if (timelineName == "translate" || timelineName == "scale") { + var timeline; + var timelineScale = 1; + if (timelineName == "scale") + timeline = new spine.ScaleTimeline(values.length); + else { + timeline = new spine.TranslateTimeline(values.length); + timelineScale = this.scale; + } + timeline.boneIndex = boneIndex; + + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; + var x = (valueMap["x"] || 0) * timelineScale; + var y = (valueMap["y"] || 0) * timelineScale; + timeline.setFrame(frameIndex, valueMap["time"], x, y); + this.readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); + + } else if (timelineName == "flipX" || timelineName == "flipY") { + var x = timelineName == "flipX"; + var timeline = x ? new spine.FlipXTimeline(values.length) : new spine.FlipYTimeline(values.length); + timeline.boneIndex = boneIndex; + + var field = x ? "x" : "y"; + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; + timeline.setFrame(frameIndex, valueMap["time"], valueMap[field] || false); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 2 - 2]); + } else + throw new Error("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"); + } + } + + var ikMap = map["ik"]; + for (var ikConstraintName in ikMap) { + if (!ikMap.hasOwnProperty(ikConstraintName)) continue; + var ikConstraint = skeletonData.findIkConstraint(ikConstraintName); + var values = ikMap[ikConstraintName]; + var timeline = new spine.IkConstraintTimeline(values.length); + timeline.ikConstraintIndex = skeletonData.ikConstraints.indexOf(ikConstraint); + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; + var mix = valueMap.hasOwnProperty("mix") ? valueMap["mix"] : 1; + var bendDirection = (!valueMap.hasOwnProperty("bendPositive") || valueMap["bendPositive"]) ? 1 : -1; + timeline.setFrame(frameIndex, valueMap["time"], mix, bendDirection); + this.readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.frameCount * 3 - 3]); + } + + var ffd = map["ffd"]; + for (var skinName in ffd) { + var skin = skeletonData.findSkin(skinName); + var slotMap = ffd[skinName]; + for (slotName in slotMap) { + var slotIndex = skeletonData.findSlotIndex(slotName); + var meshMap = slotMap[slotName]; + for (var meshName in meshMap) { + var values = meshMap[meshName]; + var timeline = new spine.FfdTimeline(values.length); + var attachment = skin.getAttachment(slotIndex, meshName); + if (!attachment) throw new Error("FFD attachment not found: " + meshName); + timeline.slotIndex = slotIndex; + timeline.attachment = attachment; + + var isMesh = attachment.type == spine.AttachmentType.mesh; + var vertexCount; + if (isMesh) + vertexCount = attachment.vertices.length; + else + vertexCount = attachment.weights.length / 3 * 2; + + var frameIndex = 0; + for (var i = 0, n = values.length; i < n; i++) { + var valueMap = values[i]; + var vertices; + if (!valueMap["vertices"]) { + if (isMesh) + vertices = attachment.vertices; + else { + vertices = []; + vertices.length = vertexCount; + } + } else { + var verticesValue = valueMap["vertices"]; + var vertices = []; + vertices.length = vertexCount; + var start = valueMap["offset"] || 0; + var nn = verticesValue.length; + if (this.scale == 1) { + for (var ii = 0; ii < nn; ii++) + vertices[ii + start] = verticesValue[ii]; + } else { + for (var ii = 0; ii < nn; ii++) + vertices[ii + start] = verticesValue[ii] * this.scale; + } + if (isMesh) { + var meshVertices = attachment.vertices; + for (var ii = 0, nn = vertices.length; ii < nn; ii++) + vertices[ii] += meshVertices[ii]; + } + } + + timeline.setFrame(frameIndex, valueMap["time"], vertices); + this.readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines[timelines.length] = timeline; + duration = Math.max(duration, timeline.frames[timeline.frameCount - 1]); + } + } + } + + var drawOrderValues = map["drawOrder"]; + if (!drawOrderValues) drawOrderValues = map["draworder"]; + if (drawOrderValues) { + var timeline = new spine.DrawOrderTimeline(drawOrderValues.length); + var slotCount = skeletonData.slots.length; + var frameIndex = 0; + for (var i = 0, n = drawOrderValues.length; i < n; i++) { + var drawOrderMap = drawOrderValues[i]; + var drawOrder = null; + if (drawOrderMap["offsets"]) { + drawOrder = []; + drawOrder.length = slotCount; + for (var ii = slotCount - 1; ii >= 0; ii--) + drawOrder[ii] = -1; + var offsets = drawOrderMap["offsets"]; + var unchanged = []; + unchanged.length = slotCount - offsets.length; + var originalIndex = 0, unchangedIndex = 0; + for (var ii = 0, nn = offsets.length; ii < nn; ii++) { + var offsetMap = offsets[ii]; + var slotIndex = skeletonData.findSlotIndex(offsetMap["slot"]); + if (slotIndex == -1) throw new Error("Slot not found: " + offsetMap["slot"]); + // Collect unchanged items. + while (originalIndex != slotIndex) + unchanged[unchangedIndex++] = originalIndex++; + // Set changed items. + drawOrder[originalIndex + offsetMap["offset"]] = originalIndex++; + } + // Collect remaining unchanged items. + while (originalIndex < slotCount) + unchanged[unchangedIndex++] = originalIndex++; + // Fill in unchanged items. + for (var ii = slotCount - 1; ii >= 0; ii--) + if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex]; + } + timeline.setFrame(frameIndex++, drawOrderMap["time"], drawOrder); + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } + + var events = map["events"]; + if (events) { + var timeline = new spine.EventTimeline(events.length); + var frameIndex = 0; + for (var i = 0, n = events.length; i < n; i++) { + var eventMap = events[i]; + var eventData = skeletonData.findEvent(eventMap["name"]); + if (!eventData) throw new Error("Event not found: " + eventMap["name"]); + var event = new spine.Event(eventData); + event.intValue = eventMap.hasOwnProperty("int") ? eventMap["int"] : eventData.intValue; + event.floatValue = eventMap.hasOwnProperty("float") ? eventMap["float"] : eventData.floatValue; + event.stringValue = eventMap.hasOwnProperty("string") ? eventMap["string"] : eventData.stringValue; + timeline.setFrame(frameIndex++, eventMap["time"], event); + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); + } + + skeletonData.animations.push(new spine.Animation(name, timelines, duration)); + }, + readCurve: function (timeline, frameIndex, valueMap) { + var curve = valueMap["curve"]; + if (!curve) + timeline.curves.setLinear(frameIndex); + else if (curve == "stepped") + timeline.curves.setStepped(frameIndex); + else if (curve instanceof Array) + timeline.curves.setCurve(frameIndex, curve[0], curve[1], curve[2], curve[3]); + }, + toColor: function (hexString, colorIndex) { + if (hexString.length != 8) throw new Error("Color hexidecimal length must be 8, recieved: " + hexString); + return parseInt(hexString.substring(colorIndex * 2, (colorIndex * 2) + 2), 16) / 255; + }, + getFloatArray: function (map, name, scale) { + var list = map[name]; + var values = new spine.Float32Array(list.length); + var i = 0, n = list.length; + if (scale == 1) { + for (; i < n; i++) + values[i] = list[i]; + } else { + for (; i < n; i++) + values[i] = list[i] * scale; + } + return values; + }, + getIntArray: function (map, name) { + var list = map[name]; + var values = new spine.Uint16Array(list.length); + for (var i = 0, n = list.length; i < n; i++) + values[i] = list[i] | 0; + return values; + } +}; + +spine.Atlas = function (atlasText, textureLoader) { + this.textureLoader = textureLoader; + this.pages = []; + this.regions = []; + + var reader = new spine.AtlasReader(atlasText); + var tuple = []; + tuple.length = 4; + var page = null; + while (true) { + var line = reader.readLine(); + if (line === null) break; + line = reader.trim(line); + if (!line.length) + page = null; + else if (!page) { + page = new spine.AtlasPage(); + page.name = line; + + if (reader.readTuple(tuple) == 2) { // size is only optional for an atlas packed with an old TexturePacker. + page.width = parseInt(tuple[0]); + page.height = parseInt(tuple[1]); + reader.readTuple(tuple); + } + page.format = spine.Atlas.Format[tuple[0]]; + + reader.readTuple(tuple); + page.minFilter = spine.Atlas.TextureFilter[tuple[0]]; + page.magFilter = spine.Atlas.TextureFilter[tuple[1]]; + + var direction = reader.readValue(); + page.uWrap = spine.Atlas.TextureWrap.clampToEdge; + page.vWrap = spine.Atlas.TextureWrap.clampToEdge; + if (direction == "x") + page.uWrap = spine.Atlas.TextureWrap.repeat; + else if (direction == "y") + page.vWrap = spine.Atlas.TextureWrap.repeat; + else if (direction == "xy") + page.uWrap = page.vWrap = spine.Atlas.TextureWrap.repeat; + + textureLoader.load(page, line, this); + + this.pages.push(page); + + } else { + var region = new spine.AtlasRegion(); + 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) { // split is optional + region.splits = [parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3])]; + + if (reader.readTuple(tuple) == 4) { // pad is optional, but only present with splits + region.pads = [parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3])]; + + 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()); + + this.regions.push(region); + } + } +}; +spine.Atlas.prototype = { + findRegion: function (name) { + var regions = this.regions; + for (var i = 0, n = regions.length; i < n; i++) + if (regions[i].name == name) return regions[i]; + return null; + }, + dispose: function () { + var pages = this.pages; + for (var i = 0, n = pages.length; i < n; i++) + this.textureLoader.unload(pages[i].rendererObject); + }, + updateUVs: function (page) { + var regions = this.regions; + for (var i = 0, n = regions.length; i < n; i++) { + var region = regions[i]; + if (region.page != page) continue; + region.u = region.x / page.width; + region.v = region.y / page.height; + if (region.rotate) { + region.u2 = (region.x + region.height) / page.width; + region.v2 = (region.y + region.width) / page.height; + } else { + region.u2 = (region.x + region.width) / page.width; + region.v2 = (region.y + region.height) / page.height; + } + } + } +}; + +spine.Atlas.Format = { + alpha: 0, + intensity: 1, + luminanceAlpha: 2, + rgb565: 3, + rgba4444: 4, + rgb888: 5, + rgba8888: 6 +}; + +spine.Atlas.TextureFilter = { + nearest: 0, + linear: 1, + mipMap: 2, + mipMapNearestNearest: 3, + mipMapLinearNearest: 4, + mipMapNearestLinear: 5, + mipMapLinearLinear: 6 +}; + +spine.Atlas.TextureWrap = { + mirroredRepeat: 0, + clampToEdge: 1, + repeat: 2 +}; + +spine.AtlasPage = function () {}; +spine.AtlasPage.prototype = { + name: null, + format: null, + minFilter: null, + magFilter: null, + uWrap: null, + vWrap: null, + rendererObject: null, + width: 0, + height: 0 +}; + +spine.AtlasRegion = function () {}; +spine.AtlasRegion.prototype = { + page: null, + name: null, + x: 0, y: 0, + width: 0, height: 0, + u: 0, v: 0, u2: 0, v2: 0, + offsetX: 0, offsetY: 0, + originalWidth: 0, originalHeight: 0, + index: 0, + rotate: false, + splits: null, + pads: null +}; + +spine.AtlasReader = function (text) { + this.lines = text.split(/\r\n|\r|\n/); +}; +spine.AtlasReader.prototype = { + index: 0, + trim: function (value) { + return value.replace(/^\s+|\s+$/g, ""); + }, + readLine: function () { + if (this.index >= this.lines.length) return null; + return this.lines[this.index++]; + }, + readValue: function () { + var line = this.readLine(); + var colon = line.indexOf(":"); + if (colon == -1) throw new Error("Invalid line: " + line); + return this.trim(line.substring(colon + 1)); + }, + /** Returns the number of tuple values read (1, 2 or 4). */ + 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] = this.trim(line.substr(lastMatch, comma - lastMatch)); + lastMatch = comma + 1; + } + tuple[i] = this.trim(line.substring(lastMatch)); + return i + 1; + } +}; + +spine.AtlasAttachmentLoader = function (atlas) { + this.atlas = atlas; +}; +spine.AtlasAttachmentLoader.prototype = { + newRegionAttachment: function (skin, name, path) { + var region = this.atlas.findRegion(path); + if (!region) throw new Error("Region not found in atlas: " + path + " (region attachment: " + name + ")"); + var attachment = new spine.RegionAttachment(name); + attachment.rendererObject = region; + attachment.setUVs(region.u, region.v, region.u2, region.v2, region.rotate); + attachment.regionOffsetX = region.offsetX; + attachment.regionOffsetY = region.offsetY; + attachment.regionWidth = region.width; + attachment.regionHeight = region.height; + attachment.regionOriginalWidth = region.originalWidth; + attachment.regionOriginalHeight = region.originalHeight; + return attachment; + }, + newMeshAttachment: function (skin, name, path) { + var region = this.atlas.findRegion(path); + if (!region) throw new Error("Region not found in atlas: " + path + " (mesh attachment: " + name + ")"); + var attachment = new spine.MeshAttachment(name); + attachment.rendererObject = region; + attachment.regionU = region.u; + attachment.regionV = region.v; + attachment.regionU2 = region.u2; + attachment.regionV2 = region.v2; + attachment.regionRotate = region.rotate; + attachment.regionOffsetX = region.offsetX; + attachment.regionOffsetY = region.offsetY; + attachment.regionWidth = region.width; + attachment.regionHeight = region.height; + attachment.regionOriginalWidth = region.originalWidth; + attachment.regionOriginalHeight = region.originalHeight; + return attachment; + }, + newSkinnedMeshAttachment: function (skin, name, path) { + var region = this.atlas.findRegion(path); + if (!region) throw new Error("Region not found in atlas: " + path + " (skinned mesh attachment: " + name + ")"); + var attachment = new spine.SkinnedMeshAttachment(name); + attachment.rendererObject = region; + attachment.regionU = region.u; + attachment.regionV = region.v; + attachment.regionU2 = region.u2; + attachment.regionV2 = region.v2; + attachment.regionRotate = region.rotate; + attachment.regionOffsetX = region.offsetX; + attachment.regionOffsetY = region.offsetY; + attachment.regionWidth = region.width; + attachment.regionHeight = region.height; + attachment.regionOriginalWidth = region.originalWidth; + attachment.regionOriginalHeight = region.originalHeight; + return attachment; + }, + newBoundingBoxAttachment: function (skin, name) { + return new spine.BoundingBoxAttachment(name); + } +}; + +spine.SkeletonBounds = function () { + this.polygonPool = []; + this.polygons = []; + this.boundingBoxes = []; +}; +spine.SkeletonBounds.prototype = { + minX: 0, minY: 0, maxX: 0, maxY: 0, + update: function (skeleton, updateAabb) { + var slots = skeleton.slots; + var slotCount = slots.length; + var x = skeleton.x, y = skeleton.y; + var boundingBoxes = this.boundingBoxes; + var polygonPool = this.polygonPool; + var polygons = this.polygons; + + boundingBoxes.length = 0; + for (var i = 0, n = polygons.length; i < n; i++) + polygonPool.push(polygons[i]); + polygons.length = 0; + + for (var i = 0; i < slotCount; i++) { + var slot = slots[i]; + var boundingBox = slot.attachment; + if (boundingBox.type != spine.AttachmentType.boundingbox) continue; + boundingBoxes.push(boundingBox); + + var poolCount = polygonPool.length, polygon; + if (poolCount > 0) { + polygon = polygonPool[poolCount - 1]; + polygonPool.splice(poolCount - 1, 1); + } else + polygon = []; + polygons.push(polygon); + + polygon.length = boundingBox.vertices.length; + boundingBox.computeWorldVertices(x, y, slot.bone, polygon); + } + + if (updateAabb) this.aabbCompute(); + }, + aabbCompute: function () { + var polygons = this.polygons; + var minX = Number.MAX_VALUE, minY = Number.MAX_VALUE, maxX = Number.MIN_VALUE, maxY = Number.MIN_VALUE; + for (var i = 0, n = polygons.length; i < n; i++) { + var vertices = polygons[i]; + for (var ii = 0, nn = vertices.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; + }, + /** Returns true if the axis aligned bounding box contains the point. */ + aabbContainsPoint: function (x, y) { + return x >= this.minX && x <= this.maxX && y >= this.minY && y <= this.maxY; + }, + /** Returns true if the axis aligned bounding box intersects the line segment. */ + aabbIntersectsSegment: function (x1, y1, x2, y2) { + var minX = this.minX, minY = this.minY, maxX = this.maxX, 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; + }, + /** Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds. */ + aabbIntersectsSkeleton: function (bounds) { + return this.minX < bounds.maxX && this.maxX > bounds.minX && this.minY < bounds.maxY && this.maxY > bounds.minY; + }, + /** Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more + * efficient to only call this method if {@link #aabbContainsPoint(float, float)} returns true. */ + containsPoint: function (x, y) { + var polygons = this.polygons; + for (var i = 0, n = polygons.length; i < n; i++) + if (this.polygonContainsPoint(polygons[i], x, y)) return this.boundingBoxes[i]; + return null; + }, + /** Returns the first bounding box attachment that contains the line segment, or null. When doing many checks, it is usually + * more efficient to only call this method if {@link #aabbIntersectsSegment(float, float, float, float)} returns true. */ + intersectsSegment: function (x1, y1, x2, y2) { + var polygons = this.polygons; + for (var i = 0, n = polygons.length; i < n; i++) + if (polygons[i].intersectsSegment(x1, y1, x2, y2)) return this.boundingBoxes[i]; + return null; + }, + /** Returns true if the polygon contains the point. */ + polygonContainsPoint: function (polygon, x, y) { + var nn = polygon.length; + var prevIndex = nn - 2; + var inside = false; + for (var ii = 0; ii < nn; ii += 2) { + var vertexY = polygon[ii + 1]; + var prevY = polygon[prevIndex + 1]; + if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) { + var vertexX = polygon[ii]; + if (vertexX + (y - vertexY) / (prevY - vertexY) * (polygon[prevIndex] - vertexX) < x) inside = !inside; + } + prevIndex = ii; + } + return inside; + }, + /** Returns true if the polygon contains the line segment. */ + polygonIntersectsSegment: function (polygon, x1, y1, x2, y2) { + var nn = polygon.length; + var width12 = x1 - x2, height12 = y1 - y2; + var det1 = x1 * y2 - y1 * x2; + var x3 = polygon[nn - 2], y3 = polygon[nn - 1]; + for (var ii = 0; ii < nn; ii += 2) { + var x4 = polygon[ii], y4 = polygon[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; + }, + getPolygon: function (attachment) { + var index = this.boundingBoxes.indexOf(attachment); + return index == -1 ? null : this.polygons[index]; + }, + getWidth: function () { + return this.maxX - this.minX; + }, + getHeight: function () { + return this.maxY - this.minY; + } +}; diff --git a/external/box2d/box2d.js b/external/box2d/box2d.js new file mode 100644 index 00000000000..55fc3b047eb --- /dev/null +++ b/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 seperation = 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); + seperation = (pointB.x - pointA.x) * this.m_axis.x + (pointB.y - pointA.y) * this.m_axis.y; + return seperation; + } + 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); + seperation = (pointB.x - pointA.x) * normal.x + (pointB.y - pointA.y) * normal.y; + return seperation; + } + 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); + seperation = (pointA.x - pointB.x) * normal.x + (pointA.y - pointB.y) * normal.y; + return seperation; + } + 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/external/chipmunk/chipmunk.js b/external/chipmunk/chipmunk.js new file mode 100644 index 00000000000..3be84bfc61d --- /dev/null +++ b/external/chipmunk/chipmunk.js @@ -0,0 +1,6196 @@ +(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 occured. + 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; + +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 occured. + 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.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; }; + +/// 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); +}; + +/// 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 persistant 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/external/gaf/GAFBoot.js b/external/gaf/GAFBoot.js new file mode 100644 index 00000000000..0baccc7ecd8 --- /dev/null +++ b/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/external/gaf/GAFMacros.js b/external/gaf/GAFMacros.js new file mode 100644 index 00000000000..ef34cc8f5db --- /dev/null +++ b/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/external/gaf/Library/GAFAsset.js b/external/gaf/Library/GAFAsset.js new file mode 100644 index 00000000000..c66fddde391 --- /dev/null +++ b/external/gaf/Library/GAFAsset.js @@ -0,0 +1,428 @@ +EventTarget = require("../cocos2d/core/event/event-target"); + +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; + } + } + }, + + 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.emit("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.emit("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; +}; + +EventTarget.polyfill(gaf.Asset.prototype); diff --git a/external/gaf/Library/GAFAssetPreload.js b/external/gaf/Library/GAFAssetPreload.js new file mode 100644 index 00000000000..8514e09f2da --- /dev/null +++ b/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/external/gaf/Library/GAFAtlasLoader.js b/external/gaf/Library/GAFAtlasLoader.js new file mode 100644 index 00000000000..152bba0788d --- /dev/null +++ b/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/external/gaf/Library/GAFDataReader.js b/external/gaf/Library/GAFDataReader.js new file mode 100644 index 00000000000..3affbe13356 --- /dev/null +++ b/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/external/gaf/Library/GAFLoader.js b/external/gaf/Library/GAFLoader.js new file mode 100644 index 00000000000..3e40c339478 --- /dev/null +++ b/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/external/gaf/Library/GAFMask.js b/external/gaf/Library/GAFMask.js new file mode 100644 index 00000000000..34ca20681c9 --- /dev/null +++ b/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/external/gaf/Library/GAFMaskProto.js b/external/gaf/Library/GAFMaskProto.js new file mode 100644 index 00000000000..6074fd12792 --- /dev/null +++ b/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/external/gaf/Library/GAFObject.js b/external/gaf/Library/GAFObject.js new file mode 100644 index 00000000000..7d5375abe5c --- /dev/null +++ b/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/external/gaf/Library/GAFShaderManager.js b/external/gaf/Library/GAFShaderManager.js new file mode 100644 index 00000000000..49b7ffba0f8 --- /dev/null +++ b/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/external/gaf/Library/GAFShaders.js b/external/gaf/Library/GAFShaders.js new file mode 100644 index 00000000000..7d915372856 --- /dev/null +++ b/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/external/gaf/Library/GAFSprite.js b/external/gaf/Library/GAFSprite.js new file mode 100644 index 00000000000..2387bf92df2 --- /dev/null +++ b/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/external/gaf/Library/GAFSpriteCanvasRenderCmd.js b/external/gaf/Library/GAFSpriteCanvasRenderCmd.js new file mode 100644 index 00000000000..bb807cc220c --- /dev/null +++ b/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/external/gaf/Library/GAFSpriteProto.js b/external/gaf/Library/GAFSpriteProto.js new file mode 100644 index 00000000000..6e33fa7d5f9 --- /dev/null +++ b/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/external/gaf/Library/GAFSpriteWebGLRenderCmd.js b/external/gaf/Library/GAFSpriteWebGLRenderCmd.js new file mode 100644 index 00000000000..eabe25b0c97 --- /dev/null +++ b/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/external/gaf/Library/GAFTags.js b/external/gaf/Library/GAFTags.js new file mode 100644 index 00000000000..09f8186538d --- /dev/null +++ b/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/external/gaf/Library/GAFTextField.js b/external/gaf/Library/GAFTextField.js new file mode 100644 index 00000000000..04f9744920c --- /dev/null +++ b/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/external/gaf/Library/GAFTimeLine.js b/external/gaf/Library/GAFTimeLine.js new file mode 100644 index 00000000000..ef9387aa468 --- /dev/null +++ b/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/external/gaf/Library/GAFTimeLineProto.js b/external/gaf/Library/GAFTimeLineProto.js new file mode 100644 index 00000000000..9d78b219d9b --- /dev/null +++ b/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/external/gaf/gaf_viewer.css b/external/gaf/gaf_viewer.css new file mode 100644 index 00000000000..60933161d2e --- /dev/null +++ b/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/external/gaf/gaf_viewer.html b/external/gaf/gaf_viewer.html new file mode 100644 index 00000000000..2c66aeb05f7 --- /dev/null +++ b/external/gaf/gaf_viewer.html @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + +
Drop GAF here
+ + + + + + \ No newline at end of file diff --git a/external/gaf/gaf_viewer.js b/external/gaf/gaf_viewer.js new file mode 100644 index 00000000000..db5bdbea7f0 --- /dev/null +++ b/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 compatiblity. 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/external/pluginx/Plugin.js b/external/pluginx/Plugin.js new file mode 100644 index 00000000000..1a2aaced03f --- /dev/null +++ b/external/pluginx/Plugin.js @@ -0,0 +1,254 @@ +/**************************************************************************** + 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; + } + + var config = cc.game.config.plugin || {}; + + //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){ + 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/external/pluginx/platform/facebook.js b/external/pluginx/platform/facebook.js new file mode 100644 index 00000000000..190115951a3 --- /dev/null +++ b/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/external/pluginx/platform/facebook_sdk.js b/external/pluginx/platform/facebook_sdk.js new file mode 100644 index 00000000000..bc050683238 --- /dev/null +++ b/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