From 9cd57b731ab1c666d4a1cb725538fdc137763d12 Mon Sep 17 00:00:00 2001 From: Starla Insigna Date: Sat, 30 Jul 2011 11:19:14 -0400 Subject: Initial commit (version 0.2.1) --- libs/cocos2d/CCNode.m | 921 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 921 insertions(+) create mode 100755 libs/cocos2d/CCNode.m (limited to 'libs/cocos2d/CCNode.m') diff --git a/libs/cocos2d/CCNode.m b/libs/cocos2d/CCNode.m new file mode 100755 index 0000000..569cb22 --- /dev/null +++ b/libs/cocos2d/CCNode.m @@ -0,0 +1,921 @@ +/* + * cocos2d for iPhone: http://www.cocos2d-iphone.org + * + * Copyright (c) 2009 Valentin Milea + * + * Copyright (c) 2008-2010 Ricardo Quesada + * Copyright (c) 2011 Zynga 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. + */ + +#import "CCNode.h" +#import "CCGrid.h" +#import "CCDirector.h" +#import "CCActionManager.h" +#import "CCCamera.h" +#import "CCScheduler.h" +#import "ccConfig.h" +#import "ccMacros.h" +#import "Support/CGPointExtension.h" +#import "Support/ccCArray.h" +#import "Support/TransformUtils.h" +#import "ccMacros.h" + +#import +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED +#import "Platforms/iOS/CCDirectorIOS.h" +#endif + + +#if CC_COCOSNODE_RENDER_SUBPIXEL +#define RENDER_IN_SUBPIXEL +#else +#define RENDER_IN_SUBPIXEL (NSInteger) +#endif + +@interface CCNode () +// lazy allocs +-(void) childrenAlloc; +// helper that reorder a child +-(void) insertChild:(CCNode*)child z:(NSInteger)z; +// used internally to alter the zOrder variable. DON'T call this method manually +-(void) _setZOrder:(NSInteger) z; +-(void) detachChild:(CCNode *)child cleanup:(BOOL)doCleanup; +@end + +@implementation CCNode + +@synthesize children = children_; +@synthesize visible = visible_; +@synthesize parent = parent_; +@synthesize grid = grid_; +@synthesize zOrder = zOrder_; +@synthesize tag = tag_; +@synthesize vertexZ = vertexZ_; +@synthesize isRunning = isRunning_; +@synthesize userData = userData_; + +#pragma mark CCNode - Transform related properties + +@synthesize rotation = rotation_, scaleX = scaleX_, scaleY = scaleY_; +@synthesize skewX = skewX_, skewY = skewY_; +@synthesize position = position_, positionInPixels = positionInPixels_; +@synthesize anchorPoint = anchorPoint_, anchorPointInPixels = anchorPointInPixels_; +@synthesize contentSize = contentSize_, contentSizeInPixels = contentSizeInPixels_; +@synthesize isRelativeAnchorPoint = isRelativeAnchorPoint_; + +// getters synthesized, setters explicit + +-(void) setSkewX:(float)newSkewX +{ + skewX_ = newSkewX; + isTransformDirty_ = isInverseDirty_ = YES; +#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX + isTransformGLDirty_ = YES; +#endif +} + +-(void) setSkewY:(float)newSkewY +{ + skewY_ = newSkewY; + isTransformDirty_ = isInverseDirty_ = YES; +#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX + isTransformGLDirty_ = YES; +#endif +} + +-(void) setRotation: (float)newRotation +{ + rotation_ = newRotation; + isTransformDirty_ = isInverseDirty_ = YES; +#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX + isTransformGLDirty_ = YES; +#endif +} + +-(void) setScaleX: (float)newScaleX +{ + scaleX_ = newScaleX; + isTransformDirty_ = isInverseDirty_ = YES; +#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX + isTransformGLDirty_ = YES; +#endif +} + +-(void) setScaleY: (float)newScaleY +{ + scaleY_ = newScaleY; + isTransformDirty_ = isInverseDirty_ = YES; +#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX + isTransformGLDirty_ = YES; +#endif +} + +-(void) setPosition: (CGPoint)newPosition +{ + position_ = newPosition; + if( CC_CONTENT_SCALE_FACTOR() == 1 ) + positionInPixels_ = position_; + else + positionInPixels_ = ccpMult( newPosition, CC_CONTENT_SCALE_FACTOR() ); + + isTransformDirty_ = isInverseDirty_ = YES; +#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX + isTransformGLDirty_ = YES; +#endif +} + +-(void) setPositionInPixels:(CGPoint)newPosition +{ + positionInPixels_ = newPosition; + + if( CC_CONTENT_SCALE_FACTOR() == 1 ) + position_ = positionInPixels_; + else + position_ = ccpMult( newPosition, 1/CC_CONTENT_SCALE_FACTOR() ); + + isTransformDirty_ = isInverseDirty_ = YES; +#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX + isTransformGLDirty_ = YES; +#endif +} + +-(void) setIsRelativeAnchorPoint: (BOOL)newValue +{ + isRelativeAnchorPoint_ = newValue; + isTransformDirty_ = isInverseDirty_ = YES; +#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX + isTransformGLDirty_ = YES; +#endif +} + +-(void) setAnchorPoint:(CGPoint)point +{ + if( ! CGPointEqualToPoint(point, anchorPoint_) ) { + anchorPoint_ = point; + anchorPointInPixels_ = ccp( contentSizeInPixels_.width * anchorPoint_.x, contentSizeInPixels_.height * anchorPoint_.y ); + isTransformDirty_ = isInverseDirty_ = YES; +#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX + isTransformGLDirty_ = YES; +#endif + } +} + +-(void) setContentSize:(CGSize)size +{ + if( ! CGSizeEqualToSize(size, contentSize_) ) { + contentSize_ = size; + + if( CC_CONTENT_SCALE_FACTOR() == 1 ) + contentSizeInPixels_ = contentSize_; + else + contentSizeInPixels_ = CGSizeMake( size.width * CC_CONTENT_SCALE_FACTOR(), size.height * CC_CONTENT_SCALE_FACTOR() ); + + anchorPointInPixels_ = ccp( contentSizeInPixels_.width * anchorPoint_.x, contentSizeInPixels_.height * anchorPoint_.y ); + isTransformDirty_ = isInverseDirty_ = YES; +#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX + isTransformGLDirty_ = YES; +#endif + } +} + +-(void) setContentSizeInPixels:(CGSize)size +{ + if( ! CGSizeEqualToSize(size, contentSizeInPixels_) ) { + contentSizeInPixels_ = size; + + if( CC_CONTENT_SCALE_FACTOR() == 1 ) + contentSize_ = contentSizeInPixels_; + else + contentSize_ = CGSizeMake( size.width / CC_CONTENT_SCALE_FACTOR(), size.height / CC_CONTENT_SCALE_FACTOR() ); + + anchorPointInPixels_ = ccp( contentSizeInPixels_.width * anchorPoint_.x, contentSizeInPixels_.height * anchorPoint_.y ); + isTransformDirty_ = isInverseDirty_ = YES; +#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX + isTransformGLDirty_ = YES; +#endif + } +} + +- (CGRect) boundingBox +{ + CGRect ret = [self boundingBoxInPixels]; + return CC_RECT_PIXELS_TO_POINTS( ret ); +} + +- (CGRect) boundingBoxInPixels +{ + CGRect rect = CGRectMake(0, 0, contentSizeInPixels_.width, contentSizeInPixels_.height); + return CGRectApplyAffineTransform(rect, [self nodeToParentTransform]); +} + +-(void) setVertexZ:(float)vertexZ +{ + vertexZ_ = vertexZ * CC_CONTENT_SCALE_FACTOR(); +} + +-(float) scale +{ + NSAssert( scaleX_ == scaleY_, @"CCNode#scale. ScaleX != ScaleY. Don't know which one to return"); + return scaleX_; +} + +-(void) setScale:(float) s +{ + scaleX_ = scaleY_ = s; + isTransformDirty_ = isInverseDirty_ = YES; +#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX + isTransformGLDirty_ = YES; +#endif +} + +#pragma mark CCNode - Init & cleanup + ++(id) node +{ + return [[[self alloc] init] autorelease]; +} + +-(id) init +{ + if ((self=[super init]) ) { + + isRunning_ = NO; + + skewX_ = skewY_ = 0.0f; + rotation_ = 0.0f; + scaleX_ = scaleY_ = 1.0f; + positionInPixels_ = position_ = CGPointZero; + anchorPointInPixels_ = anchorPoint_ = CGPointZero; + contentSizeInPixels_ = contentSize_ = CGSizeZero; + + + // "whole screen" objects. like Scenes and Layers, should set isRelativeAnchorPoint to NO + isRelativeAnchorPoint_ = YES; + + isTransformDirty_ = isInverseDirty_ = YES; +#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX + isTransformGLDirty_ = YES; +#endif + + vertexZ_ = 0; + + grid_ = nil; + + visible_ = YES; + + tag_ = kCCNodeTagInvalid; + + zOrder_ = 0; + + // lazy alloc + camera_ = nil; + + // children (lazy allocs) + children_ = nil; + + // userData is always inited as nil + userData_ = nil; + + //initialize parent to nil + parent_ = nil; + } + + return self; +} + +- (void)cleanup +{ + // actions + [self stopAllActions]; + [self unscheduleAllSelectors]; + + // timers + [children_ makeObjectsPerformSelector:@selector(cleanup)]; +} + +- (NSString*) description +{ + return [NSString stringWithFormat:@"<%@ = %08X | Tag = %i>", [self class], self, tag_]; +} + +- (void) dealloc +{ + CCLOGINFO( @"cocos2d: deallocing %@", self); + + // attributes + [camera_ release]; + + [grid_ release]; + + // children + CCNode *child; + CCARRAY_FOREACH(children_, child) + child.parent = nil; + + [children_ release]; + + [super dealloc]; +} + +#pragma mark CCNode Composition + +-(void) childrenAlloc +{ + children_ = [[CCArray alloc] initWithCapacity:4]; +} + +// camera: lazy alloc +-(CCCamera*) camera +{ + if( ! camera_ ) { + camera_ = [[CCCamera alloc] init]; + + // by default, center camera at the Sprite's anchor point + // [camera_ setCenterX:anchorPointInPixels_.x centerY:anchorPointInPixels_.y centerZ:0]; + // [camera_ setEyeX:anchorPointInPixels_.x eyeY:anchorPointInPixels_.y eyeZ:1]; + + // [camera_ setCenterX:0 centerY:0 centerZ:0]; + // [camera_ setEyeX:0 eyeY:0 eyeZ:1]; + + } + + return camera_; +} + +-(CCNode*) getChildByTag:(NSInteger) aTag +{ + NSAssert( aTag != kCCNodeTagInvalid, @"Invalid tag"); + + CCNode *node; + CCARRAY_FOREACH(children_, node){ + if( node.tag == aTag ) + return node; + } + // not found + return nil; +} + +/* "add" logic MUST only be on this method + * If a class want's to extend the 'addChild' behaviour it only needs + * to override this method + */ +-(void) addChild: (CCNode*) child z:(NSInteger)z tag:(NSInteger) aTag +{ + NSAssert( child != nil, @"Argument must be non-nil"); + NSAssert( child.parent == nil, @"child already added. It can't be added again"); + + if( ! children_ ) + [self childrenAlloc]; + + [self insertChild:child z:z]; + + child.tag = aTag; + + [child setParent: self]; + + if( isRunning_ ) { + [child onEnter]; + [child onEnterTransitionDidFinish]; + } +} + +-(void) addChild: (CCNode*) child z:(NSInteger)z +{ + NSAssert( child != nil, @"Argument must be non-nil"); + [self addChild:child z:z tag:child.tag]; +} + +-(void) addChild: (CCNode*) child +{ + NSAssert( child != nil, @"Argument must be non-nil"); + [self addChild:child z:child.zOrder tag:child.tag]; +} + +-(void) removeFromParentAndCleanup:(BOOL)cleanup +{ + [parent_ removeChild:self cleanup:cleanup]; +} + +/* "remove" logic MUST only be on this method + * If a class want's to extend the 'removeChild' behavior it only needs + * to override this method + */ +-(void) removeChild: (CCNode*)child cleanup:(BOOL)cleanup +{ + // explicit nil handling + if (child == nil) + return; + + if ( [children_ containsObject:child] ) + [self detachChild:child cleanup:cleanup]; +} + +-(void) removeChildByTag:(NSInteger)aTag cleanup:(BOOL)cleanup +{ + NSAssert( aTag != kCCNodeTagInvalid, @"Invalid tag"); + + CCNode *child = [self getChildByTag:aTag]; + + if (child == nil) + CCLOG(@"cocos2d: removeChildByTag: child not found!"); + else + [self removeChild:child cleanup:cleanup]; +} + +-(void) removeAllChildrenWithCleanup:(BOOL)cleanup +{ + // not using detachChild improves speed here + CCNode *c; + CCARRAY_FOREACH(children_, c) + { + // IMPORTANT: + // -1st do onExit + // -2nd cleanup + if (isRunning_) + [c onExit]; + + if (cleanup) + [c cleanup]; + + // set parent nil at the end (issue #476) + [c setParent:nil]; + } + + [children_ removeAllObjects]; +} + +-(void) detachChild:(CCNode *)child cleanup:(BOOL)doCleanup +{ + // IMPORTANT: + // -1st do onExit + // -2nd cleanup + if (isRunning_) + [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 (doCleanup) + [child cleanup]; + + // set parent nil at the end (issue #476) + [child setParent:nil]; + + [children_ removeObject:child]; +} + +// used internally to alter the zOrder variable. DON'T call this method manually +-(void) _setZOrder:(NSInteger) z +{ + zOrder_ = z; +} + +// helper used by reorderChild & add +-(void) insertChild:(CCNode*)child z:(NSInteger)z +{ + NSUInteger index=0; + CCNode *a = [children_ lastObject]; + + // quick comparison to improve performance + if (!a || a.zOrder <= z) + [children_ addObject:child]; + + else + { + CCARRAY_FOREACH(children_, a) { + if ( a.zOrder > z ) { + [children_ insertObject:child atIndex:index]; + break; + } + index++; + } + } + + [child _setZOrder:z]; +} + +-(void) reorderChild:(CCNode*) child z:(NSInteger)z +{ + NSAssert( child != nil, @"Child must be non-nil"); + + [child retain]; + [children_ removeObject:child]; + + [self insertChild:child z:z]; + + [child release]; +} + +#pragma mark CCNode Draw + +-(void) draw +{ + // override me + // Only use this function to draw your staff. + // DON'T draw your stuff outside this method +} + +-(void) visit +{ + // quick return if not visible + if (!visible_) + return; + + glPushMatrix(); + + if ( grid_ && grid_.active) { + [grid_ beforeDraw]; + [self transformAncestors]; + } + + [self transform]; + + if(children_) { + ccArray *arrayData = children_->data; + NSUInteger i = 0; + + // draw children zOrder < 0 + for( ; i < arrayData->num; i++ ) { + CCNode *child = arrayData->arr[i]; + if ( [child zOrder] < 0 ) + [child visit]; + else + break; + } + + // self draw + [self draw]; + + // draw children zOrder >= 0 + for( ; i < arrayData->num; i++ ) { + CCNode *child = arrayData->arr[i]; + [child visit]; + } + + } else + [self draw]; + + if ( grid_ && grid_.active) + [grid_ afterDraw:self]; + + glPopMatrix(); +} + +#pragma mark CCNode - Transformations + +-(void) transformAncestors +{ + if( parent_ ) { + [parent_ transformAncestors]; + [parent_ transform]; + } +} + +-(void) transform +{ + // transformations + +#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX + // BEGIN alternative -- using cached transform + // + if( isTransformGLDirty_ ) { + CGAffineTransform t = [self nodeToParentTransform]; + CGAffineToGL(&t, transformGL_); + isTransformGLDirty_ = NO; + } + + glMultMatrixf(transformGL_); + if( vertexZ_ ) + glTranslatef(0, 0, vertexZ_); + + // XXX: Expensive calls. Camera should be integrated into the cached affine matrix + if ( camera_ && !(grid_ && grid_.active) ) + { + BOOL translate = (anchorPointInPixels_.x != 0.0f || anchorPointInPixels_.y != 0.0f); + + if( translate ) + ccglTranslate(RENDER_IN_SUBPIXEL(anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(anchorPointInPixels_.y), 0); + + [camera_ locate]; + + if( translate ) + ccglTranslate(RENDER_IN_SUBPIXEL(-anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(-anchorPointInPixels_.y), 0); + } + + + // END alternative + +#else + // BEGIN original implementation + // + // translate + if ( isRelativeAnchorPoint_ && (anchorPointInPixels_.x != 0 || anchorPointInPixels_.y != 0 ) ) + glTranslatef( RENDER_IN_SUBPIXEL(-anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(-anchorPointInPixels_.y), 0); + + if (anchorPointInPixels_.x != 0 || anchorPointInPixels_.y != 0) + glTranslatef( RENDER_IN_SUBPIXEL(positionInPixels_.x + anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(positionInPixels_.y + anchorPointInPixels_.y), vertexZ_); + else if ( positionInPixels_.x !=0 || positionInPixels_.y !=0 || vertexZ_ != 0) + glTranslatef( RENDER_IN_SUBPIXEL(positionInPixels_.x), RENDER_IN_SUBPIXEL(positionInPixels_.y), vertexZ_ ); + + // rotate + if (rotation_ != 0.0f ) + glRotatef( -rotation_, 0.0f, 0.0f, 1.0f ); + + // skew + if ( (skewX_ != 0.0f) || (skewY_ != 0.0f) ) { + CGAffineTransform skewMatrix = CGAffineTransformMake( 1.0f, tanf(CC_DEGREES_TO_RADIANS(skewY_)), tanf(CC_DEGREES_TO_RADIANS(skewX_)), 1.0f, 0.0f, 0.0f ); + GLfloat glMatrix[16]; + CGAffineToGL(&skewMatrix, glMatrix); + glMultMatrixf(glMatrix); + } + + // scale + if (scaleX_ != 1.0f || scaleY_ != 1.0f) + glScalef( scaleX_, scaleY_, 1.0f ); + + if ( camera_ && !(grid_ && grid_.active) ) + [camera_ locate]; + + // restore and re-position point + if (anchorPointInPixels_.x != 0.0f || anchorPointInPixels_.y != 0.0f) + glTranslatef(RENDER_IN_SUBPIXEL(-anchorPointInPixels_.x), RENDER_IN_SUBPIXEL(-anchorPointInPixels_.y), 0); + + // + // END original implementation +#endif + +} + +#pragma mark CCNode SceneManagement + +-(void) onEnter +{ + [children_ makeObjectsPerformSelector:@selector(onEnter)]; + [self resumeSchedulerAndActions]; + + isRunning_ = YES; +} + +-(void) onEnterTransitionDidFinish +{ + [children_ makeObjectsPerformSelector:@selector(onEnterTransitionDidFinish)]; +} + +-(void) onExit +{ + [self pauseSchedulerAndActions]; + isRunning_ = NO; + + [children_ makeObjectsPerformSelector:@selector(onExit)]; +} + +#pragma mark CCNode Actions + +-(CCAction*) runAction:(CCAction*) action +{ + NSAssert( action != nil, @"Argument must be non-nil"); + + [[CCActionManager sharedManager] addAction:action target:self paused:!isRunning_]; + return action; +} + +-(void) stopAllActions +{ + [[CCActionManager sharedManager] removeAllActionsFromTarget:self]; +} + +-(void) stopAction: (CCAction*) action +{ + [[CCActionManager sharedManager] removeAction:action]; +} + +-(void) stopActionByTag:(NSInteger)aTag +{ + NSAssert( aTag != kCCActionTagInvalid, @"Invalid tag"); + [[CCActionManager sharedManager] removeActionByTag:aTag target:self]; +} + +-(CCAction*) getActionByTag:(NSInteger) aTag +{ + NSAssert( aTag != kCCActionTagInvalid, @"Invalid tag"); + return [[CCActionManager sharedManager] getActionByTag:aTag target:self]; +} + +-(NSUInteger) numberOfRunningActions +{ + return [[CCActionManager sharedManager] numberOfRunningActionsInTarget:self]; +} + +#pragma mark CCNode - Scheduler + +-(void) scheduleUpdate +{ + [self scheduleUpdateWithPriority:0]; +} + +-(void) scheduleUpdateWithPriority:(NSInteger)priority +{ + [[CCScheduler sharedScheduler] scheduleUpdateForTarget:self priority:priority paused:!isRunning_]; +} + +-(void) unscheduleUpdate +{ + [[CCScheduler sharedScheduler] unscheduleUpdateForTarget:self]; +} + +-(void) schedule:(SEL)selector +{ + [self schedule:selector interval:0]; +} + +-(void) schedule:(SEL)selector interval:(ccTime)interval +{ + NSAssert( selector != nil, @"Argument must be non-nil"); + NSAssert( interval >=0, @"Arguemnt must be positive"); + + [[CCScheduler sharedScheduler] scheduleSelector:selector forTarget:self interval:interval paused:!isRunning_]; +} + +-(void) unschedule:(SEL)selector +{ + // explicit nil handling + if (selector == nil) + return; + + [[CCScheduler sharedScheduler] unscheduleSelector:selector forTarget:self]; +} + +-(void) unscheduleAllSelectors +{ + [[CCScheduler sharedScheduler] unscheduleAllSelectorsForTarget:self]; +} +- (void) resumeSchedulerAndActions +{ + [[CCScheduler sharedScheduler] resumeTarget:self]; + [[CCActionManager sharedManager] resumeTarget:self]; +} + +- (void) pauseSchedulerAndActions +{ + [[CCScheduler sharedScheduler] pauseTarget:self]; + [[CCActionManager sharedManager] pauseTarget:self]; +} + +#pragma mark CCNode Transform + +- (CGAffineTransform)nodeToParentTransform +{ + if ( isTransformDirty_ ) { + + transform_ = CGAffineTransformIdentity; + + if ( !isRelativeAnchorPoint_ && !CGPointEqualToPoint(anchorPointInPixels_, CGPointZero) ) + transform_ = CGAffineTransformTranslate(transform_, anchorPointInPixels_.x, anchorPointInPixels_.y); + + if( ! CGPointEqualToPoint(positionInPixels_, CGPointZero) ) + transform_ = CGAffineTransformTranslate(transform_, positionInPixels_.x, positionInPixels_.y); + + if( rotation_ != 0 ) + transform_ = CGAffineTransformRotate(transform_, -CC_DEGREES_TO_RADIANS(rotation_)); + + if( skewX_ != 0 || skewY_ != 0 ) { + // create a skewed coordinate system + CGAffineTransform skew = CGAffineTransformMake(1.0f, tanf(CC_DEGREES_TO_RADIANS(skewY_)), tanf(CC_DEGREES_TO_RADIANS(skewX_)), 1.0f, 0.0f, 0.0f); + // apply the skew to the transform + transform_ = CGAffineTransformConcat(skew, transform_); + } + + if( ! (scaleX_ == 1 && scaleY_ == 1) ) + transform_ = CGAffineTransformScale(transform_, scaleX_, scaleY_); + + if( ! CGPointEqualToPoint(anchorPointInPixels_, CGPointZero) ) + transform_ = CGAffineTransformTranslate(transform_, -anchorPointInPixels_.x, -anchorPointInPixels_.y); + + isTransformDirty_ = NO; + } + + return transform_; +} + +- (CGAffineTransform)parentToNodeTransform +{ + if ( isInverseDirty_ ) { + inverse_ = CGAffineTransformInvert([self nodeToParentTransform]); + isInverseDirty_ = NO; + } + + return inverse_; +} + +- (CGAffineTransform)nodeToWorldTransform +{ + CGAffineTransform t = [self nodeToParentTransform]; + + for (CCNode *p = parent_; p != nil; p = p.parent) + t = CGAffineTransformConcat(t, [p nodeToParentTransform]); + + return t; +} + +- (CGAffineTransform)worldToNodeTransform +{ + return CGAffineTransformInvert([self nodeToWorldTransform]); +} + +- (CGPoint)convertToNodeSpace:(CGPoint)worldPoint +{ + CGPoint ret; + if( CC_CONTENT_SCALE_FACTOR() == 1 ) + ret = CGPointApplyAffineTransform(worldPoint, [self worldToNodeTransform]); + else { + ret = ccpMult( worldPoint, CC_CONTENT_SCALE_FACTOR() ); + ret = CGPointApplyAffineTransform(ret, [self worldToNodeTransform]); + ret = ccpMult( ret, 1/CC_CONTENT_SCALE_FACTOR() ); + } + + return ret; +} + +- (CGPoint)convertToWorldSpace:(CGPoint)nodePoint +{ + CGPoint ret; + if( CC_CONTENT_SCALE_FACTOR() == 1 ) + ret = CGPointApplyAffineTransform(nodePoint, [self nodeToWorldTransform]); + else { + ret = ccpMult( nodePoint, CC_CONTENT_SCALE_FACTOR() ); + ret = CGPointApplyAffineTransform(ret, [self nodeToWorldTransform]); + ret = ccpMult( ret, 1/CC_CONTENT_SCALE_FACTOR() ); + } + + return ret; +} + +- (CGPoint)convertToNodeSpaceAR:(CGPoint)worldPoint +{ + CGPoint nodePoint = [self convertToNodeSpace:worldPoint]; + CGPoint anchorInPoints; + if( CC_CONTENT_SCALE_FACTOR() == 1 ) + anchorInPoints = anchorPointInPixels_; + else + anchorInPoints = ccpMult( anchorPointInPixels_, 1/CC_CONTENT_SCALE_FACTOR() ); + + return ccpSub(nodePoint, anchorInPoints); +} + +- (CGPoint)convertToWorldSpaceAR:(CGPoint)nodePoint +{ + CGPoint anchorInPoints; + if( CC_CONTENT_SCALE_FACTOR() == 1 ) + anchorInPoints = anchorPointInPixels_; + else + anchorInPoints = ccpMult( anchorPointInPixels_, 1/CC_CONTENT_SCALE_FACTOR() ); + + nodePoint = ccpAdd(nodePoint, anchorInPoints); + return [self convertToWorldSpace:nodePoint]; +} + +- (CGPoint)convertToWindowSpace:(CGPoint)nodePoint +{ + CGPoint worldPoint = [self convertToWorldSpace:nodePoint]; + return [[CCDirector sharedDirector] convertToUI:worldPoint]; +} + +// convenience methods which take a UITouch instead of CGPoint + +#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED + +- (CGPoint)convertTouchToNodeSpace:(UITouch *)touch +{ + CGPoint point = [touch locationInView: [touch view]]; + point = [[CCDirector sharedDirector] convertToGL: point]; + return [self convertToNodeSpace:point]; +} + +- (CGPoint)convertTouchToNodeSpaceAR:(UITouch *)touch +{ + CGPoint point = [touch locationInView: [touch view]]; + point = [[CCDirector sharedDirector] convertToGL: point]; + return [self convertToNodeSpaceAR:point]; +} + +#endif // __IPHONE_OS_VERSION_MAX_ALLOWED + + +@end -- cgit 1.4.1