Reset Darwin target(s)

* Unfortunately cocos2d doesn't support what I'd like to do with a Mac target at this point, maybe sometime...
This commit is contained in:
Aaron Culliney 2014-06-21 20:37:48 -07:00
parent 3a3219234f
commit a69d8685eb
376 changed files with 0 additions and 99147 deletions

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>org.deadc0de.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -1,9 +0,0 @@
//
// Prefix header
//
// The contents of this file are implicitly included at the beginning of every source file.
//
#ifdef __OBJC__
#import <Cocoa/Cocoa.h>
#endif

View File

@ -1,46 +0,0 @@
//
// Apple2Mac_Tests.m
// Apple2Mac Tests
//
// Created by Aaron Culliney on 6/21/14.
// Copyright (c) 2014 deadc0de.org. All rights reserved.
//
#import <XCTest/XCTest.h>
@interface Apple2Mac_CPUTests : XCTestCase
@end
@implementation Apple2Mac_CPUTests
- (void)setUp
{
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown
{
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
extern int test_cpu(int, char **);
- (void)testCPU
{
char *argv[] = {
"-f",
NULL
};
int argc = 0;
for (char **p = &argv[0]; *p != NULL; p++) {
++argc;
}
int val = test_cpu(argc, argv);
XCTAssertEqual(val, 0);
}
@end

View File

@ -1,2 +0,0 @@
/* Localized versions of Info.plist keys */

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:Apple2Mac.xcodeproj">
</FileRef>
</Workspace>

View File

@ -1,18 +0,0 @@
//
// AppDelegate.h
// Apple2Mac
//
// Created by Aaron Culliney on 6/16/14.
// Copyright deadc0de.org 2014. All rights reserved.
//
#import "cocos2d.h"
@interface Apple2MacAppDelegate : NSObject <NSApplicationDelegate>
@property (nonatomic, weak) IBOutlet NSWindow *window;
@property (nonatomic, weak) IBOutlet CCGLView *glView;
- (IBAction)toggleFullScreen:(id)sender;
@end

View File

@ -1,56 +0,0 @@
//
// AppDelegate.m
// Apple2Mac
//
// Created by Aaron Culliney on 6/16/14.
// Copyright deadc0de.org 2014. All rights reserved.
//
#import "AppDelegate.h"
#import "HelloWorldLayer.h"
@implementation Apple2MacAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
CCDirectorMac *director = (CCDirectorMac*) [CCDirector sharedDirector];
// enable FPS and SPF
[director setDisplayStats:YES];
// connect the OpenGL view with the director
[director setView:_glView];
// EXPERIMENTAL stuff.
// 'Effects' don't work correctly when autoscale is turned on.
// Use kCCDirectorResize_NoScale if you don't want auto-scaling.
[director setResizeMode:kCCDirectorResize_AutoScale];
// Enable "moving" mouse event. Default no.
[_window setAcceptsMouseMovedEvents:NO];
// Center main window
[_window center];
[director runWithScene:[HelloWorldLayer scene]];
}
- (BOOL) applicationShouldTerminateAfterLastWindowClosed: (NSApplication *) theApplication
{
return YES;
}
- (void)dealloc
{
[[CCDirector sharedDirector] end];
}
#pragma mark AppDelegate - IBActions
- (IBAction)toggleFullScreen: (id)sender
{
CCDirectorMac *director = (CCDirectorMac*) [CCDirector sharedDirector];
[director setFullScreen: ! [director isFullScreen] ];
}
@end

View File

@ -1,16 +0,0 @@
//
// DarwinVideo.h
// Apple2Mac
//
// Created by Aaron Culliney on 6/21/14.
// Copyright 2014 deadc0de.org. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "cocos2d.h"
@interface DarwinVideo : CCNode {
}
@end

View File

@ -1,28 +0,0 @@
//
// DarwinVideo.m
// Apple2Mac
//
// Created by Aaron Culliney on 6/21/14.
// Copyright 2014 deadc0de.org All rights reserved.
//
#import "DarwinVideo.h"
void video_driver_init(void)
{
// no-op
}
void video_driver_shutdown(void)
{
// no-op
}
void video_sync(int block)
{
// TODO ...
}
@implementation DarwinVideo
@end

View File

@ -1,19 +0,0 @@
//
// HelloWorldLayer.h
// Apple2Mac
//
// Created by Aaron Culliney on 6/16/14.
// Copyright deadc0de.org 2014. All rights reserved.
//
// When you import this file, you import all the cocos2d classes
#import "cocos2d.h"
// HelloWorldLayer
@interface HelloWorldLayer : CCNode
// returns a CCScene that contains the HelloWorldLayer as the only child
+(CCScene *) scene;
@end

View File

@ -1,64 +0,0 @@
//
// HelloWorldLayer.m
// Apple2Mac
//
// Created by Aaron Culliney on 6/16/14.
// Copyright deadc0de.org 2014. All rights reserved.
//
#include "common.h"
// Import the interfaces
#import "HelloWorldLayer.h"
// HelloWorldLayer implementation
@implementation HelloWorldLayer
+ (CCScene *)scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorldLayer *layer = [HelloWorldLayer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
// on "init" you need to initialize your instance
- (id)init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init]) ) {
// create and initialize a Label
CCLabelTTF *label = [CCLabelTTF labelWithString:@"Hello World" fontName:@"Marker Felt" fontSize:64];
// ask director for the window size
CGSize size = [[CCDirector sharedDirector] viewSize];
// position the label on the center of the screen
label.position = ccp( size.width /2 , size.height/2 );
// add the label as a child to this Layer
[self addChild: label];
#warning TODO FIXME ...
#if 0
// initialize emulator
c_initialize_firsttime();
// spin off CPU thread
pthread_create(&cpu_thread_id, NULL, (void *) &cpu_thread, (void *)NULL);
#endif
}
return self;
}
@end

View File

@ -1,121 +0,0 @@
/*
* cocos2d for iPhone: http://www.cocos2d-iphone.org
*
* Copyright (c) 2013 Apportable 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 <Foundation/Foundation.h>
#import "cocos2d.h"
#import "CCBSequenceProperty.h"
@class CCBSequence;
#pragma mark Animation Manager Delegate
/**
* The animation manager delegate receives callbacks when animation sequences finishes playing.
*/
@protocol CCBAnimationManagerDelegate <NSObject>
/**
* Called when an animation sequence has finished playing.
* @param name The name of the sequence that just finished playing.
*/
- (void) completedAnimationSequenceNamed:(NSString*)name;
@end
#pragma mark Animation Manager
/**
* The animation manager plays back animations, usually created by a tool such as SpriteBuilder. Any animation can have an arbitrary number of sequences (timelines) which each have keyframes for different properties.
*/
@interface CCAnimationManager : NSObject <CCSchedulerTarget>
{
NSMutableDictionary* _nodeSequences;
NSMutableDictionary* _baseValues;
NSInteger _animationManagerId;
CCBSequence* _runningSequence;
CCBSequence* _lastSequence;
void (^block)(id sender);
CCScheduler* _scheduler;
NSMutableArray* _currentActions;
BOOL _loop;
}
/// If set to true the animation manager will run on a fixed time step, this is required to run animations toghether with physics.
@property (nonatomic,assign) bool fixedTimestep;
/// The animation manager delegate receives updates about the animations currently being played.
@property (nonatomic,weak) NSObject<CCBAnimationManagerDelegate>* delegate;
/// The name of the currently running sequence (timeline).
@property (unsafe_unretained, nonatomic,readonly) NSString* runningSequenceName;
/// The name of the last completed sequence (timeline).
@property (nonatomic,readonly) NSString* lastCompletedSequenceName;
/// Playback speed, default is 1 and corresponds to the normal playback speed. Use this property for fast forward or slow motion playback.
@property (nonatomic,assign) float playbackSpeed;
/// Set to true to pause the animation currently being run.
@property (nonatomic,assign) bool paused;
/**
* Plays an animation sequence (timeline) by its name.
* @param name The name of the sequence to play.
*/
- (void) runAnimationsForSequenceNamed:(NSString*)name;
/**
* Plays an animation sequence (timeline) by its name, tweens smoothly to the new sequence.
* @param name The name of the sequence to play.
* @param tweenDuration Time to tween to the new sequence.
*/
- (void) runAnimationsForSequenceNamed:(NSString*)name tweenDuration:(float)tweenDuration;
/**
* Sets a block to be called when an animation sequence has finished playing.
* @param b The block to call.
*/
-(void) setCompletedAnimationCallbackBlock:(void(^)(id sender))b;
#pragma mark Time Controls
//Renamed to jumpToSequenceNamed:time
//- (void)timeSeekForSequenceNamed:(NSString*)name time:(float)time __attribute__((deprecated));
/**
* Jumps to a specific time in a specific sequence (timeline).
* @param name The name of the sequence to jump to.
* @param time The time in the sequence.
*/
- (void)jumpToSequenceNamed:(NSString*)name time:(float)time;
@end

View File

@ -1,943 +0,0 @@
/*
* cocos2d for iPhone: http://www.cocos2d-iphone.org
*
* Copyright (c) 2013 Apportable 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 "CCAnimationManager.h"
#import "CCAnimationManager_Private.h"
#import "CCBKeyframe.h"
#import "CCBSequence.h"
#import "CCBSequenceProperty.h"
#import "CCBReader.h"
#import "CCBKeyframe.h"
#import "OALSimpleAudio.h"
#import <objc/runtime.h>
#import "CCDirector_Private.h"
#import "CCBReader_Private.h"
#import "CCActionManager.h"
// Unique Manager ID
static NSInteger ccbAnimationManagerID = 0;
@implementation CCAnimationManager
- (id)init {
self = [super init];
if (!self) return NULL;
_animationManagerId = ccbAnimationManagerID;
ccbAnimationManagerID++;
_sequences = [[NSMutableArray alloc] init];
_nodeSequences = [[NSMutableDictionary alloc] init];
_baseValues = [[NSMutableDictionary alloc] init];
// Scheduler
_scheduler = [[CCDirector sharedDirector] scheduler];
[_scheduler scheduleTarget:self];
[_scheduler setPaused:NO target:self];
// Current Sequence Actions
_currentActions = [[NSMutableArray alloc] init];
_playbackSpeed = 1.0f;
_paused = NO;
_lastSequence = nil;
_fixedTimestep = NO;
_loop = NO;
return self;
}
- (NSInteger)priority {
return 0;
}
- (CGSize)containerSize:(CCNode*)node {
if (node) {
return node.contentSize;
} else {
return _rootContainerSize;
}
}
- (void)addNode:(CCNode*)node andSequences:(NSDictionary*)seq {
NSValue* nodePtr = [NSValue valueWithPointer:(__bridge const void *)(node)];
[_nodeSequences setObject:seq forKey:nodePtr];
}
- (id)seqForNode:(CCNode*)node {
NSValue* nodePtr = [NSValue valueWithPointer:(__bridge const void *)(node)];
return [_nodeSequences objectForKey:nodePtr];
}
- (void)moveAnimationsFromNode:(CCNode*)fromNode toNode:(CCNode*)toNode {
NSValue* fromNodePtr = [NSValue valueWithPointer:(__bridge const void *)(fromNode)];
NSValue* toNodePtr = [NSValue valueWithPointer:(__bridge const void *)(toNode)];
// Move base values
id baseValue = [_baseValues objectForKey:fromNodePtr];
if (baseValue) {
[_baseValues setObject:baseValue forKey:toNodePtr];
[_baseValues removeObjectForKey:fromNodePtr];
}
// Move keyframes
NSDictionary* seqs = [_nodeSequences objectForKey:fromNodePtr];
if (seqs) {
[_nodeSequences setObject:seqs forKey:toNodePtr];
[_nodeSequences removeObjectForKey:fromNodePtr];
}
}
- (void)setBaseValue:(id)value forNode:(CCNode*)node propertyName:(NSString*)propName {
NSValue* nodePtr = [NSValue valueWithPointer:(__bridge const void *)(node)];
NSMutableDictionary* props = [_baseValues objectForKey:nodePtr];
if (!props) {
props = [NSMutableDictionary dictionary];
[_baseValues setObject:props forKey:nodePtr];
}
[props setObject:value forKey:propName];
}
- (id)baseValueForNode:(CCNode*) node propertyName:(NSString*) propName {
NSValue* nodePtr = [NSValue valueWithPointer:(__bridge const void *)(node)];
NSMutableDictionary* props = [_baseValues objectForKey:nodePtr];
return [props objectForKey:propName];
}
- (int)sequenceIdForSequenceNamed:(NSString*)name {
for (CCBSequence* seq in _sequences) {
if ([seq.name isEqualToString:name]) {
return seq.sequenceId;
}
}
return -1;
}
- (CCBSequence*)sequenceFromSequenceId:(int)seqId {
for (CCBSequence* seq in _sequences)
{
if (seq.sequenceId == seqId) return seq;
}
return NULL;
}
- (CCActionInterval*)actionFromKeyframe0:(CCBKeyframe*)kf0 andKeyframe1:(CCBKeyframe*)kf1 propertyName:(NSString*)name node:(CCNode*)node {
float duration = kf1.time - kf0.time;
if ([name isEqualToString:@"rotation"]) {
return [CCActionRotateTo actionWithDuration:duration angle:[kf1.value floatValue] simple:YES];
} else if ([name isEqualToString:@"position"]) {
id value = kf1.value;
// Get relative position
float x = [[value objectAtIndex:0] floatValue];
float y = [[value objectAtIndex:1] floatValue];
return [CCActionMoveTo actionWithDuration:duration position:ccp(x,y)];
} else if ([name isEqualToString:@"scale"]) {
id value = kf1.value;
// Get relative scale
float x = [[value objectAtIndex:0] floatValue];
float y = [[value objectAtIndex:1] floatValue];
return [CCActionScaleTo actionWithDuration:duration scaleX:x scaleY:y];
} else if ([name isEqualToString:@"skew"]) {
id value = kf1.value;
float x = [[value objectAtIndex:0] floatValue];
float y = [[value objectAtIndex:1] floatValue];
return [CCActionSkewTo actionWithDuration:duration skewX:x skewY:y];
} else if ([name isEqualToString:@"rotationalSkewX"]) {
return [CCActionRotateTo actionWithDuration:duration angleX:[kf1.value floatValue]];
} else if ([name isEqualToString:@"rotationalSkewY"]) {
return [CCActionRotateTo actionWithDuration:duration angleY:[kf1.value floatValue]];
} else if ([name isEqualToString:@"opacity"]) {
return [CCActionFadeTo actionWithDuration:duration opacity:[kf1.value intValue]];
} else if ([name isEqualToString:@"color"]) {
CCColor* color = kf1.value;
return [CCActionTintTo actionWithDuration:duration color:color];
} else if ([name isEqualToString:@"visible"]) {
if ([kf1.value boolValue]) {
return [CCActionSequence actionOne:[CCActionDelay actionWithDuration:duration] two:[CCActionShow action]];
} else {
return [CCActionSequence actionOne:[CCActionDelay actionWithDuration:duration] two:[CCActionHide action]];
}
} else if ([name isEqualToString:@"spriteFrame"]) {
return [CCActionSequence actionOne:[CCActionDelay actionWithDuration:duration] two:[CCActionSpriteFrame actionWithSpriteFrame:kf1.value]];
} else {
CCLOG(@"CCBReader: Failed to create animation for property: %@", name);
}
return NULL;
}
- (void)setAnimatedProperty:(NSString*)name forNode:(CCNode*)node toValue:(id)value tweenDuration:(float) tweenDuration {
if (tweenDuration > 0) {
// Create a fake keyframe to generate the action from
CCBKeyframe* kf1 = [[CCBKeyframe alloc] init];
kf1.value = value;
kf1.time = tweenDuration;
kf1.easingType = kCCBKeyframeEasingLinear;
CCActionInterval* tweenAction = [self actionFromKeyframe0:NULL andKeyframe1:kf1 propertyName:name node:node];
tweenAction.tag = (int)_animationManagerId;
[tweenAction startWithTarget:node];
[_currentActions addObject:tweenAction];
} else {
if ([name isEqualToString:@"position"]) {
// Get relative position
float x = [[value objectAtIndex:0] floatValue];
float y = [[value objectAtIndex:1] floatValue];
#ifdef __CC_PLATFORM_IOS
[node setValue:[NSValue valueWithCGPoint:ccp(x,y)] forKey:name];
#elif defined (__CC_PLATFORM_MAC)
[node setValue:[NSValue valueWithPoint:ccp(x,y)] forKey:name];
#endif
} else if ([name isEqualToString:@"scale"]) {
// Get relative scale
float x = [[value objectAtIndex:0] floatValue];
float y = [[value objectAtIndex:1] floatValue];
[node setValue:[NSNumber numberWithFloat:x] forKey:[name stringByAppendingString:@"X"]];
[node setValue:[NSNumber numberWithFloat:y] forKey:[name stringByAppendingString:@"Y"]];
} else if ([name isEqualToString:@"skew"]) {
node.skewX = [[value objectAtIndex:0] floatValue];
node.skewY = [[value objectAtIndex:1] floatValue];
} else if ([name isEqualToString:@"spriteFrame"]) {
[(CCSprite*)node setSpriteFrame:value];
} else {
[node setValue:value forKey:name];
}
}
}
- (void)setKeyFrameForNode:(CCNode*)node sequenceProperty:(CCBSequenceProperty*)seqProp tweenDuration:(float)tweenDuration keyFrame:(int)kf {
NSArray* keyframes = [seqProp keyframes];
if ([keyframes count] == 0) {
// No Animation, Set Base Value
id baseValue = [self baseValueForNode:node propertyName:seqProp.name];
NSAssert1(baseValue, @"No baseValue found for property (%@)", seqProp.name);
[self setAnimatedProperty:seqProp.name forNode:node toValue:baseValue tweenDuration:tweenDuration];
} else {
// Use Specified KeyFrame
CCBKeyframe* keyframe = [keyframes objectAtIndex:kf];
[self setAnimatedProperty:seqProp.name forNode:node toValue:keyframe.value tweenDuration:tweenDuration];
}
}
- (CCActionInterval*)easeAction:(CCActionInterval*) action easingType:(int)easingType easingOpt:(float) easingOpt
{
if ([action isKindOfClass:[CCActionSequence class]]) return action;
if (easingType == kCCBKeyframeEasingLinear)
{
return action;
}
else if (easingType == kCCBKeyframeEasingInstant)
{
return [CCActionEaseInstant actionWithAction:action];
}
else if (easingType == kCCBKeyframeEasingCubicIn)
{
return [CCActionEaseIn actionWithAction:action rate:easingOpt];
}
else if (easingType == kCCBKeyframeEasingCubicOut)
{
return [CCActionEaseOut actionWithAction:action rate:easingOpt];
}
else if (easingType == kCCBKeyframeEasingCubicInOut)
{
return [CCActionEaseInOut actionWithAction:action rate:easingOpt];
}
else if (easingType == kCCBKeyframeEasingBackIn)
{
return [CCActionEaseBackIn actionWithAction:action];
}
else if (easingType == kCCBKeyframeEasingBackOut)
{
return [CCActionEaseBackOut actionWithAction:action];
}
else if (easingType == kCCBKeyframeEasingBackInOut)
{
return [CCActionEaseBackInOut actionWithAction:action];
}
else if (easingType == kCCBKeyframeEasingBounceIn)
{
return [CCActionEaseBounceIn actionWithAction:action];
}
else if (easingType == kCCBKeyframeEasingBounceOut)
{
return [CCActionEaseBounceOut actionWithAction:action];
}
else if (easingType == kCCBKeyframeEasingBounceInOut)
{
return [CCActionEaseBounceInOut actionWithAction:action];
}
else if (easingType == kCCBKeyframeEasingElasticIn)
{
return [CCActionEaseElasticIn actionWithAction:action period:easingOpt];
}
else if (easingType == kCCBKeyframeEasingElasticOut)
{
return [CCActionEaseElasticOut actionWithAction:action period:easingOpt];
}
else if (easingType == kCCBKeyframeEasingElasticInOut)
{
return [CCActionEaseElasticInOut actionWithAction:action period:easingOpt];
}
else
{
NSLog(@"CCBReader: Unkown easing type %d", easingType);
return action;
}
}
- (void)runActionsForNode:(CCNode*)node sequenceProperty:(CCBSequenceProperty*)seqProp tweenDuration:(float)tweenDuration startKeyFrame:(int)startFrame {
// Grab Key Frames / Count
NSArray* keyframes = [seqProp keyframes];
int numKeyframes = (int)keyframes.count;
// Nothing to do - No Keyframes
if(numKeyframes<1) return;
// Action Sequence Builder
NSMutableArray* actions = [NSMutableArray array];
int endFrame = startFrame+1;
if(endFrame==numKeyframes || endFrame<0)
return;
// First Frame
CCBKeyframe* kf0 = [keyframes objectAtIndex:startFrame];
// Initial Tween Required
if(startFrame==0) {
float timeFirst = kf0.time + tweenDuration;
// Handle Tween
if (timeFirst > 0) {
[actions addObject:[CCActionDelay actionWithDuration:timeFirst]];
}
}
// Build Actions
CCActionSequence* actionSeq = [self createActionForNode:node sequenceProperty:seqProp beginKeyFrame:startFrame endKeyFrame:endFrame];
if(actionSeq) {
[actions addObject:actionSeq];
}
// Next Sequence
CCActionCallBlock* nextKeyFrameBlock = [CCActionCallBlock actionWithBlock:^{
[self runActionsForNode:node sequenceProperty:seqProp tweenDuration:0 startKeyFrame:endFrame];
}];
[actions addObject:nextKeyFrameBlock];
CCActionSequence* seq = [CCActionSequence actionWithArray:actions];
seq.tag = _animationManagerId;
[seq startWithTarget:node];
if(kf0.time>0 || _loop) { // Ensure Sync
[seq step:0];
[seq step:_runningSequence.time-kf0.time];
}
[_currentActions addObject:seq];
}
- (id)actionForCallbackChannel:(CCBSequenceProperty*) channel {
float lastKeyframeTime = 0;
NSMutableArray* actions = [NSMutableArray array];
for (CCBKeyframe* keyframe in channel.keyframes) {
float timeSinceLastKeyframe = keyframe.time - lastKeyframeTime;
lastKeyframeTime = keyframe.time;
if (timeSinceLastKeyframe > 0) {
[actions addObject:[CCActionDelay actionWithDuration:timeSinceLastKeyframe]];
}
NSString* selectorName = [keyframe.value objectAtIndex:0];
int selectorTarget = [[keyframe.value objectAtIndex:1] intValue];
// Callback through obj-c
id target = NULL;
if (selectorTarget == kCCBTargetTypeDocumentRoot) target = self.rootNode;
else if (selectorTarget == kCCBTargetTypeOwner) target = _owner;
SEL selector = NSSelectorFromString(selectorName);
if (target && selector) {
[actions addObject:[CCActionCallFunc actionWithTarget:target selector:selector]];
}
}
if (!actions.count) return NULL;
return [CCActionSequence actionWithArray:actions];
}
- (id)actionForSoundChannel:(CCBSequenceProperty*) channel {
float lastKeyframeTime = 0;
NSMutableArray* actions = [NSMutableArray array];
for (CCBKeyframe* keyframe in channel.keyframes) {
float timeSinceLastKeyframe = keyframe.time - lastKeyframeTime;
lastKeyframeTime = keyframe.time;
if (timeSinceLastKeyframe > 0) {
[actions addObject:[CCActionDelay actionWithDuration:timeSinceLastKeyframe]];
}
NSString* soundFile = [keyframe.value objectAtIndex:0];
float pitch = [[keyframe.value objectAtIndex:1] floatValue];
float pan = [[keyframe.value objectAtIndex:2] floatValue];
float gain = [[keyframe.value objectAtIndex:3] floatValue];
[actions addObject:[CCActionSoundEffect actionWithSoundFile:soundFile pitch:pitch pan:pan gain:gain]];
}
if (!actions.count) return NULL;
return [CCActionSequence actionWithArray:actions];
}
- (void)runAnimationsForSequenceId:(int)seqId tweenDuration:(float) tweenDuration {
NSAssert(seqId != -1, @"Sequence id %d couldn't be found",seqId);
_paused = YES;
[self clearAllActions];
// Contains all Sequence Propertys / Keyframe
for (NSValue* nodePtr in _nodeSequences) {
CCNode* node = [nodePtr pointerValue];
NSDictionary* seqs = [_nodeSequences objectForKey:nodePtr];
NSDictionary* seqNodeProps = [seqs objectForKey:[NSNumber numberWithInt:seqId]];
NSMutableSet* seqNodePropNames = [NSMutableSet set];
if(_lastSequence.sequenceId!=seqId) {
_loop = NO;
// Reset the nodes that may have been changed by other timelines
NSDictionary* nodeBaseValues = [_baseValues objectForKey:nodePtr];
for (NSString* propName in nodeBaseValues) {
if (![seqNodePropNames containsObject:propName]) {
id value = [nodeBaseValues objectForKey:propName];
if (value) {
[self setAnimatedProperty:propName forNode:node toValue:value tweenDuration:tweenDuration];
}
}
}
}
// Reset nodes that have sequence node properties, build first keyframe action sequence.
for (NSString* propName in seqNodeProps) {
CCBSequenceProperty* seqProp = [seqNodeProps objectForKey:propName];
[seqNodePropNames addObject:propName];
// Reset Node State to First KeyFrame
[self setKeyFrameForNode:node sequenceProperty:seqProp tweenDuration:tweenDuration keyFrame:0];
// Build First Key Frame Sequence
[self runActionsForNode:node sequenceProperty:seqProp tweenDuration:tweenDuration startKeyFrame:0];
}
}
[self addSequenceCallBacks:seqId tweenDuration:tweenDuration startTime:0];
// Set the running scene
_runningSequence = [self sequenceFromSequenceId:seqId];
_runningSequence.time = 0.0f;
_paused = NO;
}
- (void)runAnimationsForSequenceNamed:(NSString*)name tweenDuration:(float)tweenDuration {
int seqId = [self sequenceIdForSequenceNamed:name];
[self runAnimationsForSequenceId:seqId tweenDuration:tweenDuration];
}
- (void)runAnimationsForSequenceNamed:(NSString*)name {
[self runAnimationsForSequenceNamed:name tweenDuration:0];
}
- (void)addSequenceCallBacks:(int)seqId tweenDuration:(float)tweenDuration startTime:(float)time {
// End of Sequence Callback
CCBSequence* seq = [self sequenceFromSequenceId:seqId];
CCActionSequence* completeAction = [CCActionSequence
actionOne:[CCActionDelay actionWithDuration:seq.duration+tweenDuration-time]
two:[CCActionCallFunc actionWithTarget:self selector:@selector(sequenceCompleted)]];
completeAction.tag = (int)_animationManagerId;
[completeAction startWithTarget:self.rootNode];
[_currentActions addObject:completeAction];
// Playback callbacks and sounds
if (seq.callbackChannel) {
// Build sound actions for channel
CCAction* action = [self actionForCallbackChannel:seq.callbackChannel];
if (action) {
action.tag = (int)_animationManagerId;
[action startWithTarget:self.rootNode];
[_currentActions addObject:action];
}
}
if (seq.soundChannel) {
// Build sound actions for channel
CCAction* action = [self actionForSoundChannel:seq.soundChannel];
if (action) {
action.tag = (int)_animationManagerId;
[action startWithTarget:self.rootNode];
[_currentActions addObject:action];
}
}
}
- (void)sequenceCompleted {
// Save last completed sequence
if (_lastCompletedSequenceName != _runningSequence.name) {
_lastCompletedSequenceName = [_runningSequence.name copy];
_lastSequence = _runningSequence;
}
// Play next sequence
int nextSeqId = _runningSequence.chainedSequenceId;
// Repeat Same Sequence
if(nextSeqId!=-1&& nextSeqId==_runningSequence.sequenceId) {
_loop = YES;
}
_runningSequence = NULL;
// Callbacks
[_delegate completedAnimationSequenceNamed:_lastCompletedSequenceName];
if (block) block(self);
// Run next sequence if callbacks did not start a new sequence
if (_runningSequence == NULL && nextSeqId != -1) {
[self runAnimationsForSequenceId:nextSeqId tweenDuration:0];
}
}
- (NSString*)runningSequenceName {
return _runningSequence.name;
}
- (void)setCompletedAnimationCallbackBlock:(void(^)(id sender))b {
block = [b copy];
}
- (void)cleanup {
[_scheduler setPaused:YES target:self];
[_scheduler unscheduleTarget:self];
[self clearAllActions];
}
- (void)dealloc {
_rootNode = NULL;
}
- (void)debug {
CCLOG(@"baseValues: %@", _baseValues);
CCLOG(@"nodeSequences: %@", _nodeSequences);
}
- (void)setPlaybackSpeed:(float)playbackSpeed {
// Backward Motion (Backwards)
if(_playbackSpeed>0 && playbackSpeed<0 && _runningSequence) {
[self timeSeekStaticForSequenceId:_runningSequence.sequenceId time:_runningSequence.time];
}
// Forward Motion
if(_playbackSpeed<0 && playbackSpeed>0 && _runningSequence) {
[self timeSeekForSequenceId:_runningSequence.sequenceId time:_runningSequence.time];
}
_playbackSpeed = playbackSpeed;
}
- (void)timeSeekStaticForSequenceId:(int)seqId time:(float)time {
NSAssert(seqId != -1, @"Sequence id %d couldn't be found",seqId);
// Reverse Loop Hack
if(_playbackSpeed<0 && time<0 && _runningSequence.chainedSequenceId==_runningSequence.sequenceId) {
time = _runningSequence.duration;
}
[self clearAllActions];
// Contains all Sequence Propertys / Keyframe
for (NSValue* nodePtr in _nodeSequences) {
CCNode* node = [nodePtr pointerValue];
NSDictionary* seqs = [_nodeSequences objectForKey:nodePtr];
NSDictionary* seqNodeProps = [seqs objectForKey:[NSNumber numberWithInt:seqId]];
// Reset Nodes, Create Actions
NSMutableSet* seqNodePropNames = [NSMutableSet set];
// Reset the nodes that may have been changed by other timelines
NSDictionary* nodeBaseValues = [_baseValues objectForKey:nodePtr];
for (NSString* propName in nodeBaseValues) {
if (![seqNodePropNames containsObject:propName]) {
id value = [nodeBaseValues objectForKey:propName];
if (value) {
[self setAnimatedProperty:propName forNode:node toValue:value tweenDuration:0];
}
}
}
for (NSString* propName in seqNodeProps) {
CCBSequenceProperty* seqProp = [seqNodeProps objectForKey:propName];
NSMutableArray* keyFrames = [self findFrames:time sequenceProperty:seqProp];
// No KeyFrames Found
if([keyFrames count]==0) {
continue;
}
// Last Sequence KeyFrame Ended Before Seek Time / Set State
if([keyFrames count]==1) {
[self setKeyFrameForNode:node sequenceProperty:seqProp tweenDuration:0 keyFrame:[[keyFrames objectAtIndex:0] intValue]];
continue;
}
// Set Initial State First Key Frame
[self setKeyFrameForNode:node sequenceProperty:seqProp tweenDuration:0 keyFrame:[[keyFrames objectAtIndex:0] intValue]];
CCBKeyframe* currentKeyFrame = [seqProp.keyframes objectAtIndex:[[keyFrames objectAtIndex:0] unsignedIntegerValue]];
float timeFoward = time - currentKeyFrame.time;
// Create Action Sequence
CCActionSequence* action = [self createActionForNode:node
sequenceProperty:seqProp
beginKeyFrame:[[keyFrames objectAtIndex:0] intValue]
endKeyFrame:[[keyFrames objectAtIndex:1] intValue]];
// Fast forward to time point
[action startWithTarget:node];
[action step:0]; // First Tick
[action step:timeFoward];
}
}
_runningSequence = [self sequenceFromSequenceId:seqId];
_runningSequence.time = time;
}
- (void)timeSeekForSequenceNamed:(NSString*)name time:(float)time {
[self jumpToSequenceNamed:name time:time];
}
- (void)jumpToSequenceNamed:(NSString *)name time:(float)time {
int seqId = [self sequenceIdForSequenceNamed:name];
[self timeSeekForSequenceId:seqId time:time];
}
- (void) timeSeekForSequenceId:(int)seqId time:(float)time {
NSAssert(seqId != -1, @"Sequence id %d couldn't be found",seqId);
[self clearAllActions];
// Contains all Sequence Propertys / Keyframe
for (NSValue* nodePtr in _nodeSequences) {
CCNode* node = [nodePtr pointerValue];
NSDictionary* seqs = [_nodeSequences objectForKey:nodePtr];
NSDictionary* seqNodeProps = [seqs objectForKey:[NSNumber numberWithInt:seqId]];
// Reset Nodes, Create Actions
NSMutableSet* seqNodePropNames = [NSMutableSet set];
// Reset the nodes that may have been changed by other timelines
NSDictionary* nodeBaseValues = [_baseValues objectForKey:nodePtr];
for (NSString* propName in nodeBaseValues) {
if (![seqNodePropNames containsObject:propName]) {
id value = [nodeBaseValues objectForKey:propName];
if (value) {
[self setAnimatedProperty:propName forNode:node toValue:value tweenDuration:0];
}
}
}
for (NSString* propName in seqNodeProps) {
CCBSequenceProperty* seqProp = [seqNodeProps objectForKey:propName];
NSMutableArray* keyFrames = [self findFrames:time sequenceProperty:seqProp];
// No KeyFrames Found
if([keyFrames count]==0) {
continue;
}
// Last Sequence KeyFrame Ended Before Seek Time / Set State
if([keyFrames count]==1) {
[self setKeyFrameForNode:node sequenceProperty:seqProp tweenDuration:0 keyFrame:[[keyFrames objectAtIndex:0] intValue]];
continue;
}
// Set Initial State First Key Frame
[self setKeyFrameForNode:node sequenceProperty:seqProp tweenDuration:0 keyFrame:[[keyFrames objectAtIndex:0] intValue]];
CCBKeyframe* currentKeyFrame = [seqProp.keyframes objectAtIndex:[[keyFrames objectAtIndex:0] unsignedIntegerValue]];
float timeFoward = time - currentKeyFrame.time;
// Create Action Sequence
CCActionSequence* action = [self createActionForNode:node
sequenceProperty:seqProp
beginKeyFrame:[[keyFrames objectAtIndex:0] intValue]
endKeyFrame:[[keyFrames objectAtIndex:1] intValue]];
// Next Sequence
CCActionCallBlock* nextKeyFrameBlock = [CCActionCallBlock actionWithBlock:^{
[self runActionsForNode:node sequenceProperty:seqProp tweenDuration:0 startKeyFrame:[[keyFrames objectAtIndex:1] intValue]];
}];
CCActionSequence* animSequence = [CCActionSequence actions:action, nextKeyFrameBlock,nil];
// Fast forward to time point
[animSequence setTag:_animationManagerId];
[animSequence startWithTarget:node];
[animSequence step:0]; // First Tick
[animSequence step:timeFoward];
[_currentActions addObject:animSequence];
}
}
[self addSequenceCallBacks:seqId tweenDuration:0 startTime:time];
_runningSequence = [self sequenceFromSequenceId:seqId];
_runningSequence.time = time;
}
- (NSMutableArray*)findFrames:(float)time sequenceProperty:(CCBSequenceProperty*) seqProp{
NSMutableArray* result = [[NSMutableArray alloc] init];
CCBKeyframe* startKeyFrame = [seqProp.keyframes objectAtIndex:0];
CCBKeyframe* endKeyFrame = [seqProp.keyframes objectAtIndex:0];
NSUInteger frameCount = [seqProp.keyframes count];
// Find KeyFrames
int i;
for (i = 0; i < frameCount; i++) {
CCBKeyframe* currentKey = [seqProp.keyframes objectAtIndex:i];
if (currentKey.time>time) {
endKeyFrame = currentKey;
// Add KeyFrames
[result addObject:[NSNumber numberWithUnsignedInteger:[seqProp.keyframes indexOfObject:startKeyFrame]]];
[result addObject:[NSNumber numberWithUnsignedInteger:[seqProp.keyframes indexOfObject:endKeyFrame]]];
break;
}
startKeyFrame = [seqProp.keyframes objectAtIndex:i];
}
// No Frames
if([result count]==0) {
[result addObject:[NSNumber numberWithInteger:(i-1)]];
}
return result;
}
- (CCActionSequence*)createActionForNode:(CCNode*)node sequenceProperty:(CCBSequenceProperty*)seqProp beginKeyFrame:(int)beginKeyFrame endKeyFrame:(int)endKeyFrame
{
NSArray* keyframes = [seqProp keyframes];
CCBKeyframe* startKF = [keyframes objectAtIndex:beginKeyFrame];
CCBKeyframe* endKF = [keyframes objectAtIndex:endKeyFrame];
CCActionSequence* seq = nil;
// Check Keyframe Cache
if(endKF.frameActions) {
seq = [endKF.frameActions copy];
} else {
// Build Animation Actions
NSMutableArray* actions = [[NSMutableArray alloc] init];
CCActionInterval* action = [self actionFromKeyframe0:startKF andKeyframe1:endKF propertyName:seqProp.name node:node];
if (action) {
// Apply Easing
action = [self easeAction:action easingType:startKF.easingType easingOpt:startKF.easingOpt];
[actions addObject:action];
// Cache
seq = [CCActionSequence actionWithArray:actions];
seq.tag = _animationManagerId;
endKF.frameActions = [seq copy];
}
}
return seq;
}
-(void)fixedUpdate:(CCTime)delta {
if(self.fixedTimestep) {
[self updateInternal:delta];
}
}
- (void)update:(CCTime)delta {
if(!self.fixedTimestep) {
[self updateInternal:delta];
}
}
- (void)updateInternal:(CCTime)delta {
if(_paused) return;
float step = delta*_playbackSpeed;
if(_playbackSpeed<0) {
[self timeSeekStaticForSequenceId:_runningSequence.sequenceId time:_runningSequence.time+step];
return;
}
if(_currentActions.count==0) return;
CCAction *action;
NSArray *actionsCopy = [_currentActions copy];
for(action in actionsCopy) {
[action step:step];
if([action isDone]) {
[_currentActions removeObject:action];
}
}
_runningSequence.time+=step;
}
- (void)clearAllActions {
if(!_currentActions.count) return;
for(CCAction *action in _currentActions) {
[action stop];
}
[_currentActions removeAllObjects];
}
#pragma mark Simple Sequence Builder
- (void)addKeyFramesForSequenceNamed:(NSString*)name propertyType:(CCBSequencePropertyType)propertyType frameArray:(NSArray*)frameArray node:(CCNode *)node loop:(BOOL)loop {
int seqId = (int)[self.sequences count];
// Create New Sequence
CCBSequence* sequence = [[CCBSequence alloc] init];
[sequence setName:name];
[sequence setSequenceId:seqId];
[self.sequences addObject:sequence];
// Repeat Sequence (Loop)
if(loop) {
[sequence setChainedSequenceId:seqId];
}
NSString *propertyName = [CCBSequenceProperty getPropertyNameFromTypeId:propertyType];
NSAssert(propertyName != nil, @"Property type %d couldn't be found",(int)propertyType);
// Create Sequence Property
CCBSequenceProperty* sequenceProperty = [[CCBSequenceProperty alloc] init];
[sequenceProperty setName:propertyName];
[sequenceProperty setType:propertyType];
// Keyframe total time
float duration = 0.0f;
for(NSDictionary* frameDict in frameArray) {
// Create KeyFrame
CCBKeyframe* newFrame = [[CCBKeyframe alloc] init];
[newFrame setTime:[[frameDict valueForKey:@"time"] floatValue]];
[newFrame setValue:[CCSpriteFrame frameWithImageNamed:[frameDict objectForKey:@"value"]]];
[sequenceProperty.keyframes addObject:newFrame];
duration=newFrame.time;
}
// Set Sequence Duration
sequence.duration = duration;
NSMutableDictionary* seqs = [NSMutableDictionary dictionary];
NSMutableDictionary* seqNodeProps = [NSMutableDictionary dictionary];
[seqNodeProps setObject:sequenceProperty forKey:sequenceProperty.name];
[seqs setObject:seqNodeProps forKey:[NSNumber numberWithInt:seqId]];
NSMutableDictionary* seqNode = [self seqForNode:node];
if(seqNode) {
[seqNode setObject:seqNodeProps forKey:[NSNumber numberWithInt:seqId]];
} else {
[self addNode:node andSequences:seqs];
}
}
@end

View File

@ -1,59 +0,0 @@
/*
* cocos2d for iPhone: http://www.cocos2d-iphone.org
*
* Copyright (c) 2013 Apportable 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.
*/
@interface CCAnimationManager ()
// Sequence Array
@property (nonatomic,readonly) NSMutableArray* sequences;
// Auto play sequence id.
@property (nonatomic,assign) int autoPlaySequenceId;
// Base node.
@property (nonatomic,unsafe_unretained) CCNode* rootNode;
// (CCB) Optional owner
@property (nonatomic,unsafe_unretained) id owner;
// (CCB) Resolution and default container size.
@property (nonatomic,assign) CGSize rootContainerSize;
// (CCB) Node Management
- (CGSize) containerSize:(CCNode*)node;
- (void) addNode:(CCNode*)node andSequences:(NSDictionary*)seq;
- (void) moveAnimationsFromNode:(CCNode*)fromNode toNode:(CCNode*)toNode;
// Reset node state.
- (void) setBaseValue:(id)value forNode:(CCNode*)node propertyName:(NSString*)propName;
- (void) runAnimationsForSequenceId:(int)seqId tweenDuration:(float) tweenDuration;
- (void)timeSeekForSequenceId:(int)seqId time:(float)time;
#pragma mark Simple Sequence Builder
- (void)addKeyFramesForSequenceNamed:(NSString*)name propertyType:(CCBSequencePropertyType)propertyType frameArray:(NSArray*)frameArray node:(CCNode *)node loop:(BOOL)loop;
@end

View File

@ -1,36 +0,0 @@
/*
* SpriteBuilder: http://www.spritebuilder.org
*
* Copyright (c) 2012 Zynga Inc.
* Copyright (c) 2013 Apportable 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 <Foundation/Foundation.h>
#import "cocos2d.h"
#import "CCAnimationManager.h"
__attribute__ ((deprecated))
@interface CCBAnimationManager : CCAnimationManager
{
}
@end

View File

@ -1,31 +0,0 @@
/*
* SpriteBuilder: http://www.spritebuilder.org
*
* Copyright (c) 2012 Zynga Inc.
* Copyright (c) 2013 Apportable 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 "CCBAnimationManager.h"
@implementation CCBAnimationManager
@end

View File

@ -1,38 +0,0 @@
/*
* SpriteBuilder: http://www.spritebuilder.org
*
* Copyright (c) 2012 Zynga Inc.
* Copyright (c) 2013 Apportable 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 <Foundation/Foundation.h>
@class CCActionSequence;
@interface CCBKeyframe : NSObject
@property (nonatomic,strong) id value;
@property (nonatomic,assign) float time;
@property (nonatomic,assign) int easingType;
@property (nonatomic,assign) float easingOpt;
@property (nonatomic,strong) CCActionSequence* frameActions;
@end

View File

@ -1,53 +0,0 @@
/*
* SpriteBuilder: http://www.spritebuilder.org
*
* Copyright (c) 2012 Zynga Inc.
* Copyright (c) 2013 Apportable 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 "CCBKeyframe.h"
@implementation CCBKeyframe
- (id)init
{
self = [super init];
if (!self) return NULL;
// Defaults
_value = nil;
_time = 0.0f;
_easingType = 0;
_easingOpt = 0.0f;
_frameActions = nil;
return self;
}
- (NSString *) description {
NSString *description = [NSString localizedStringWithFormat:@"[CCAnimationKeyframe] value: %@, time: %f, easingType: %d, easingOpt: %f", self.value, self.time,self.easingType,self.easingOpt];
return description;
}
@end

View File

@ -1,41 +0,0 @@
/*
* SpriteBuilder: http://www.spritebuilder.org
*
* Copyright (c) 2013 Apportable 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 <Foundation/Foundation.h>
#define CCBLocalize(key) \
[[CCBLocalizationManager sharedManager] localizedStringForKey:(key)]
@interface CCBLocalizationManager : NSObject
{
NSMutableDictionary* _translations;
}
@property (nonatomic,readonly) NSDictionary* translations;
+ (id)sharedManager;
- (NSString*) localizedStringForKey:(NSString*)key;
@end

View File

@ -1,111 +0,0 @@
/*
* SpriteBuilder: http://www.spritebuilder.org
*
* Copyright (c) 2013 Apportable 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 "CCBLocalizationManager.h"
#import "CCBReader.h"
@implementation CCBLocalizationManager
@synthesize translations = _translations;
+ (id)sharedManager
{
static dispatch_once_t pred;
static CCBLocalizationManager *loc = nil;
dispatch_once(&pred, ^{
loc = [[self alloc] init];
});
return loc;
}
- (id) init
{
self = [super init];
if (!self) return NULL;
[self loadStringsFile:@"Strings.ccbLang"];
return self;
}
- (void) loadStringsFile:(NSString*) file
{
// Load default localization dictionary
NSString* path = [[CCFileUtils sharedFileUtils] fullPathForFilename:file];
// Load strings file
NSDictionary* ser = [NSDictionary dictionaryWithContentsOfFile:path];
// Check that format of file is correct
NSAssert([[ser objectForKey:@"fileType"] isEqualToString:@"SpriteBuilderTranslations"], @"Invalid file format for SpriteBuilder localizations");
// Check that file version is correct
NSAssert([[ser objectForKey:@"fileVersion"] intValue] == 1, @"Translation file version is incompatible with this reader");
// Load available languages
NSArray* languages = [ser objectForKey:@"activeLanguages"];
// Determine which language to use
NSString* userLanguage = NULL;
NSArray* preferredLangs = [NSLocale preferredLanguages];
for (NSString* preferredLang in preferredLangs)
{
if ([languages containsObject:preferredLang])
{
userLanguage = preferredLang;
break;
}
}
// Create dictionary for translations
_translations = [[NSMutableDictionary alloc] init];
// Load translations
if (userLanguage != NULL)
{
NSArray* translations = [ser objectForKey:@"translations"];
for (NSDictionary* translation in translations)
{
NSString* key = [translation objectForKey:@"key"];
NSString* value = [(NSDictionary*)[translation objectForKey:@"translations"] objectForKey:userLanguage];
if (key != NULL && value != NULL)
{
[_translations setObject:value forKey:key];
}
}
}
}
- (NSString*) localizedStringForKey:(NSString*)key
{
NSString* localizedString = [_translations objectForKey:key];
if (!localizedString) localizedString = key;
return localizedString;
}
@end

View File

@ -1,173 +0,0 @@
/*
* SpriteBuilder: http://www.spritebuilder.org
*
* Copyright (c) 2012 Zynga Inc.
* Copyright (c) 2013 Apportable 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 <Foundation/Foundation.h>
#if CCB_SPRITEKIT_READER
#import "CCBSpriteKitCompatibility.h"
#else
#import "cocos2d.h"
#endif
@class CCAnimationManager;
/**
The CCBReader loads node graphs created by SpriteBuilder (or other editors using the same format). If you are using SpriteBuilder it's strongly recommended that you set up the CCFileUtils using the configureCCFileUtils method or use the Xcode project file created by SpriteBuilder.
Load your SpriteBuilder files using the load: method, if you are loading a scene use the loadAsScene: method, which will wrap the loaded node graph in a CCScene object.
You can optionally pass an owner object to the CCBReader. If you have assigned member variables in your ccb-files to the owner, they will be set on the owner object when the file is loaded.
When all loading is complete, the didLoadFromCCB method will be called on all loaded nodes (if it has been implemented).
If you are using animations a CCAnimationManager will be assigned to all ccb-file root node's animationManager property. The top CCAnimationManager is also assigned to the CCBReader's animationManager property.
*/
@interface CCBReader : NSObject
{
NSData* data;
unsigned char* bytes;
int currentByte;
int currentBit;
NSMutableArray* stringCache;
NSMutableSet* loadedSpriteSheets;
id owner;
CCAnimationManager* animationManager;
NSMutableDictionary* actionManagers;
NSMutableSet* animatedProps;
NSMutableDictionary* nodeMapping;//Maps UUID -> Node
}
/// -----------------------------------------------------------------------
/// @name Setup
/// -----------------------------------------------------------------------
/**
* Call this method to configure the CCFileUtils to work correctly with SpriteBuilder. It will setup search paths for the resources to use with the current device and resolution. It assumes that the SpriteBuilder resources has been published to a directory named Published-iOS that has been added as a blue folder in Xcode.
*/
+ (void) configureCCFileUtils;
/// -----------------------------------------------------------------------
/// @name Instantiation
/// -----------------------------------------------------------------------
/**
* Creates a new CCBReader.
*
* @return A new CCBReader.
*/
+ (CCBReader*) reader;
/// -----------------------------------------------------------------------
/// @name Loading Files
/// -----------------------------------------------------------------------
/**
* Loads a ccbi-file with the specified name. Using the extension is optional, e.g. both MyNodeGraph and MyNodeGraph.ccbi will work.
*
* @param file Name of the file to load.
*
* @return The loaded node graph.
*/
- (CCNode*) load:(NSString*) file;
/**
* Loads a ccbi-file with the specified name and owner. Using the extension is optional, e.g. both MyNodeGraph and MyNodeGraph.ccbi will work.
*
* @param file Name of the file to load.
* @param owner The owner object used to load the file.
*
* @return The loaded node graph.
*/
- (CCNode*) load:(NSString*) file owner:(id)owner;
/**
* Loads a ccbi-file from the provided NSData object. This method is useful if you load ccbi-files from the internet. If you are not using the owner variable, pass NULL.
*
* @param data Data object to load the ccbi-file from.
* @param owner The owner object used to load the file, or NULL if not used.
*
* @return The loaded node graph.
*/
- (CCNode*) loadWithData:(NSData*) data owner:(id)owner;
/**
* Loads a ccbi-file with the specified name. Using the extension is optional, e.g. both MyNodeGraph and MyNodeGraph.ccbi will work.
*
* @param file Name of the file to load.
*
* @return The loaded node graph.
*/
+ (CCNode*) load:(NSString*) file;
/**
* Loads a ccbi-file with the specified name and owner. Using the extension is optional, e.g. both MyNodeGraph and MyNodeGraph.ccbi will work.
*
* @param file Name of the file to load.
* @param owner The owner object used to load the file.
*
* @return The loaded node graph.
*/
+ (CCNode*) load:(NSString*) file owner:(id)owner;
/**
* Loads a ccbi-file with the specified name and wraps it in a CCScene node. Using the extension is optional, e.g. both MyNodeGraph and MyNodeGraph.ccbi will work.
*
* @param file Name of the file to load.
*
* @return The loaded node graph.
*/
+ (CCScene*) loadAsScene:(NSString*) file;
/**
* Loads a ccbi-file with the specified name and owner and wraps it in a CCScene node. Using the extension is optional, e.g. both MyNodeGraph and MyNodeGraph.ccbi will work.
*
* @param file Name of the file to load.
* @param owner The owner object used to load the file.
*
* @return The loaded node graph.
*/
+ (CCScene*) loadAsScene:(NSString *)file owner:(id)owner;
/// -----------------------------------------------------------------------
/// @name Animations
/// -----------------------------------------------------------------------
/**
* Once a ccb-file has been loaded, the animationManager property will be set to contain the top level CCAnimationManager
*/
@property (nonatomic,strong) CCAnimationManager* animationManager;
// Internal use: override methods for Sprite Kit Reader subclass
+(void) setSceneSize:(CGSize)sceneSize;
-(CCNode*) nodeFromClassName:(NSString*)nodeClassName;
-(CCScene*) createScene;
-(void) readerDidSetSpriteFrame:(CCSpriteFrame*)spriteFrame node:(CCNode*)node;
@end

File diff suppressed because it is too large Load Diff

View File

@ -1,94 +0,0 @@
//
// CCBReader_Private.h
// cocos2d-ios
//
// Created by Viktor on 11/13/13.
//
//
#import "CCBReader.h"
#define kCCBVersion 10
enum {
kCCBPropTypePosition = 0,
kCCBPropTypeSize,
kCCBPropTypePoint,
kCCBPropTypePointLock,
kCCBPropTypeScaleLock,
kCCBPropTypeDegrees,
kCCBPropTypeInteger,
kCCBPropTypeFloat,
kCCBPropTypeFloatVar,
kCCBPropTypeCheck,
kCCBPropTypeSpriteFrame,
kCCBPropTypeTexture,
kCCBPropTypeByte,
kCCBPropTypeColor3,
kCCBPropTypeColor4FVar,
kCCBPropTypeFlip,
kCCBPropTypeBlendmode,
kCCBPropTypeFntFile,
kCCBPropTypeText,
kCCBPropTypeFontTTF,
kCCBPropTypeIntegerLabeled,
kCCBPropTypeBlock,
kCCBPropTypeAnimation,
kCCBPropTypeCCBFile,
kCCBPropTypeString,
kCCBPropTypeBlockCCControl,
kCCBPropTypeFloatScale,
kCCBPropTypeFloatXY,
kCCBPropTypeColor4,
kCCBPropTypeNodeReference,
kCCBPropTypeFloatCheck,
};
enum {
kCCBFloat0 = 0,
kCCBFloat1,
kCCBFloatMinus1,
kCCBFloat05,
kCCBFloatInteger,
kCCBFloatFull
};
enum {
kCCBPlatformAll = 0,
kCCBPlatformIOS,
kCCBPlatformMac
};
enum {
kCCBTargetTypeNone = 0,
kCCBTargetTypeDocumentRoot = 1,
kCCBTargetTypeOwner = 2,
};
enum
{
kCCBKeyframeEasingInstant,
kCCBKeyframeEasingLinear,
kCCBKeyframeEasingCubicIn,
kCCBKeyframeEasingCubicOut,
kCCBKeyframeEasingCubicInOut,
kCCBKeyframeEasingElasticIn,
kCCBKeyframeEasingElasticOut,
kCCBKeyframeEasingElasticInOut,
kCCBKeyframeEasingBounceIn,
kCCBKeyframeEasingBounceOut,
kCCBKeyframeEasingBounceInOut,
kCCBKeyframeEasingBackIn,
kCCBKeyframeEasingBackOut,
kCCBKeyframeEasingBackInOut,
};
@interface CCBReader ()
@end

View File

@ -1,43 +0,0 @@
/*
* SpriteBuilder: http://www.spritebuilder.org
*
* Copyright (c) 2012 Zynga Inc.
* Copyright (c) 2013 Apportable 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 <Foundation/Foundation.h>
@class CCBSequenceProperty;
@interface CCBSequence : NSObject
@property (nonatomic,assign) float duration;
@property (nonatomic,assign) float time;
@property (nonatomic,copy) NSString* name;
@property (nonatomic,assign) int sequenceId;
@property (nonatomic,assign) int chainedSequenceId;
@property (nonatomic,assign) int loops; // @todo
// Channels
@property (nonatomic,strong) CCBSequenceProperty* callbackChannel;
@property (nonatomic,strong) CCBSequenceProperty* soundChannel;
@end

View File

@ -1,40 +0,0 @@
/*
* SpriteBuilder: http://www.spritebuilder.org
*
* Copyright (c) 2012 Zynga Inc.
* Copyright (c) 2013 Apportable 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 <Foundation/Foundation.h>
#import "CCBReader_Private.h"
typedef NS_ENUM(NSInteger, CCBSequencePropertyType) {
CCBSequencePropertyTypeSpriteFrame = kCCBPropTypeSpriteFrame
};
@interface CCBSequenceProperty : NSObject
@property (nonatomic,strong) NSString* name;
@property (nonatomic,assign) int type;
@property (nonatomic,readonly) NSMutableArray* keyframes;
+ (NSString *)getPropertyNameFromTypeId:(CCBSequencePropertyType)propertyType;
@end

View File

@ -1,59 +0,0 @@
/*
* SpriteBuilder: http://www.spritebuilder.org
*
* Copyright (c) 2012 Zynga Inc.
* Copyright (c) 2013 Apportable 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 "CCBSequenceProperty.h"
@implementation CCBSequenceProperty
- (id) init
{
self = [super init];
if (!self) return NULL;
_name = @"";
_type = 0;
_keyframes = [[NSMutableArray alloc] init];
return self;
}
- (NSString *)description {
NSString *description = [NSString localizedStringWithFormat:@"[CCAnimationSequenceProperty] Name: %@, Type: %d, Keyframes: %@", self.name, self.type, self.keyframes];
return description;
}
+ (NSString *)getPropertyNameFromTypeId:(CCBSequencePropertyType)propertyType {
switch(propertyType) {
case CCBSequencePropertyTypeSpriteFrame:
return @"spriteFrame";
break;
}
return nil;
}
@end

View File

@ -1,50 +0,0 @@
/*
* SpriteBuilder: http://www.spritebuilder.org
*
* Copyright (c) 2012 Zynga Inc.
* Copyright (c) 2013 Apportable 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 "CCBSequence.h"
@implementation CCBSequence
- (id)init
{
self = [super init];
if (!self) return NULL;
// Defaults
_duration = 0.0f;
_time = 0.0f;
_name = @"";
_sequenceId = -1;
_chainedSequenceId = -1;
_loops = 0;
_callbackChannel = nil;
_soundChannel = nil;
return self;
}
@end

View File

@ -1,34 +0,0 @@
/*
* CocosBuilder: http://www.spritebuilder.org
*
* Copyright (c) 2012 Zynga Inc.
* Copyright (c) 2013 Apportable 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.
*/
// Unless this macro is defined in Build Settings CCBReader will assume it is working with cocos2d-iphone
#ifndef CCB_SPRITEKIT_READER
#define CCB_SPRITEKIT_READER 0
#endif
#import "CCBReader.h"
#import "CCBAnimationManager.h"
#import "CCAnimationManager.h"
#import "CCBLocalizationManager.h"

View File

@ -1,245 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef CHIPMUNK_H
#define CHIPMUNK_H
#ifdef _MSC_VER
#define _USE_MATH_DEFINES
#endif
#include <stdlib.h>
#include <math.h>
#ifdef __WIN32__
// For alloca().
#include <malloc.h>
#else
#include <alloca.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
// NUKE
#ifndef CP_ALLOW_PRIVATE_ACCESS
#define CP_ALLOW_PRIVATE_ACCESS 0
#endif
#if CP_ALLOW_PRIVATE_ACCESS == 1
#define CP_PRIVATE(__symbol__) __symbol__
#else
#define CP_PRIVATE(__symbol__) __symbol__##_private
#endif
void cpMessage(const char *condition, const char *file, int line, int isError, int isHardError, const char *message, ...);
#ifdef NDEBUG
#define cpAssertWarn(__condition__, ...)
#else
#define cpAssertWarn(__condition__, ...) if(!(__condition__)) cpMessage(#__condition__, __FILE__, __LINE__, 0, 0, __VA_ARGS__)
#endif
#ifdef NDEBUG
#define cpAssertSoft(__condition__, ...)
#else
#define cpAssertSoft(__condition__, ...) if(!(__condition__)){cpMessage(#__condition__, __FILE__, __LINE__, 1, 0, __VA_ARGS__), abort();}
#endif
// Hard assertions are used in situations where the program definitely will crash anyway, and the reason is inexpensive to detect.
#define cpAssertHard(__condition__, ...) if(!(__condition__)){cpMessage(#__condition__, __FILE__, __LINE__, 1, 1, __VA_ARGS__); abort();}
#include "chipmunk_types.h"
/// @defgroup misc Misc
/// @{
/// Allocated size for various Chipmunk buffers
#ifndef CP_BUFFER_BYTES
#define CP_BUFFER_BYTES (32*1024)
#endif
#ifndef cpcalloc
/// Chipmunk calloc() alias.
#define cpcalloc calloc
#endif
#ifndef cprealloc
/// Chipmunk realloc() alias.
#define cprealloc realloc
#endif
#ifndef cpfree
/// Chipmunk free() alias.
#define cpfree free
#endif
typedef struct cpArray cpArray;
typedef struct cpHashSet cpHashSet;
typedef struct cpBody cpBody;
typedef struct cpShape cpShape;
typedef struct cpCircleShape cpCircleShape;
typedef struct cpSegmentShape cpSegmentShape;
typedef struct cpPolyShape cpPolyShape;
typedef struct cpConstraint cpConstraint;
typedef struct cpPinJoint cpPinJoint;
typedef struct cpSlideJoint cpSlideJoint;
typedef struct cpPivotJoint cpPivotJoint;
typedef struct cpGrooveJoint cpGrooveJoint;
typedef struct cpDampedSpring cpDampedSpring;
typedef struct cpDampedRotarySpring cpDampedRotarySpring;
typedef struct cpRotaryLimitJoint cpRotaryLimitJoint;
typedef struct cpRatchetJoint cpRatchetJoint;
typedef struct cpGearJoint cpGearJoint;
typedef struct cpSimpleMotorJoint cpSimpleMotorJoint;
typedef struct cpCollisionHandler cpCollisionHandler;
typedef struct cpContactPointSet cpContactPointSet;
typedef struct cpArbiter cpArbiter;
typedef struct cpSpace cpSpace;
#include "cpVect.h"
#include "cpBB.h"
#include "cpTransform.h"
#include "cpSpatialIndex.h"
#include "cpBody.h"
#include "cpShape.h"
#include "cpPolyShape.h"
#include "cpArbiter.h"
#include "cpConstraint.h"
#include "cpSpace.h"
// Chipmunk 7.0.0
#define CP_VERSION_MAJOR 7
#define CP_VERSION_MINOR 0
#define CP_VERSION_RELEASE 0
/// Version string.
extern const char *cpVersionString;
/// Calculate the moment of inertia for a circle.
/// @c r1 and @c r2 are the inner and outer diameters. A solid circle has an inner diameter of 0.
cpFloat cpMomentForCircle(cpFloat m, cpFloat r1, cpFloat r2, cpVect offset);
/// Calculate area of a hollow circle.
/// @c r1 and @c r2 are the inner and outer diameters. A solid circle has an inner diameter of 0.
cpFloat cpAreaForCircle(cpFloat r1, cpFloat r2);
/// Calculate the moment of inertia for a line segment.
/// Beveling radius is not supported.
cpFloat cpMomentForSegment(cpFloat m, cpVect a, cpVect b, cpFloat radius);
/// Calculate the area of a fattened (capsule shaped) line segment.
cpFloat cpAreaForSegment(cpVect a, cpVect b, cpFloat radius);
/// Calculate the moment of inertia for a solid polygon shape assuming it's center of gravity is at it's centroid. The offset is added to each vertex.
cpFloat cpMomentForPoly(cpFloat m, int count, const cpVect *verts, cpVect offset, cpFloat radius);
/// Calculate the signed area of a polygon. A Clockwise winding gives positive area.
/// This is probably backwards from what you expect, but matches Chipmunk's the winding for poly shapes.
cpFloat cpAreaForPoly(const int count, const cpVect *verts, cpFloat radius);
/// Calculate the natural centroid of a polygon.
cpVect cpCentroidForPoly(const int count, const cpVect *verts);
/// Calculate the moment of inertia for a solid box.
cpFloat cpMomentForBox(cpFloat m, cpFloat width, cpFloat height);
/// Calculate the moment of inertia for a solid box.
cpFloat cpMomentForBox2(cpFloat m, cpBB box);
/// Calculate the convex hull of a given set of points. Returns the count of points in the hull.
/// @c result must be a pointer to a @c cpVect array with at least @c count elements. If @c verts == @c result, then @c verts will be reduced inplace.
/// @c first is an optional pointer to an integer to store where the first vertex in the hull came from (i.e. verts[first] == result[0])
/// @c tol is the allowed amount to shrink the hull when simplifying it. A tolerance of 0.0 creates an exact hull.
int cpConvexHull(int count, const cpVect *verts, cpVect *result, int *first, cpFloat tol);
#ifdef _MSC_VER
#include "malloc.h"
#endif
/// Convenience macro to work with cpConvexHull.
/// @c count and @c verts is the input array passed to cpConvexHull().
/// @c count_var and @c verts_var are the names of the variables the macro creates to store the result.
/// The output vertex array is allocated on the stack using alloca() so it will be freed automatically, but cannot be returned from the current scope.
#define CP_CONVEX_HULL(__count__, __verts__, __count_var__, __verts_var__) \
cpVect *__verts_var__ = (cpVect *)alloca(__count__*sizeof(cpVect)); \
int __count_var__ = cpConvexHull(__count__, __verts__, __verts_var__, NULL, 0.0); \
/// Returns the closest point on the line segment ab, to the point p.
static inline cpVect
cpClosetPointOnSegment(const cpVect p, const cpVect a, const cpVect b)
{
cpVect delta = cpvsub(a, b);
cpFloat t = cpfclamp01(cpvdot(delta, cpvsub(p, b))/cpvlengthsq(delta));
return cpvadd(b, cpvmult(delta, t));
}
#if defined(__has_extension)
#if __has_extension(blocks)
// Define alternate block based alternatives for a few of the callback heavy functions.
// Collision handlers are post-step callbacks are not included to avoid memory management issues.
// If you want to use blocks for those and are aware of how to correctly manage the memory, the implementation is trivial.
void cpSpaceEachBody_b(cpSpace *space, void (^block)(cpBody *body));
void cpSpaceEachShape_b(cpSpace *space, void (^block)(cpShape *shape));
void cpSpaceEachConstraint_b(cpSpace *space, void (^block)(cpConstraint *constraint));
void cpBodyEachShape_b(cpBody *body, void (^block)(cpShape *shape));
void cpBodyEachConstraint_b(cpBody *body, void (^block)(cpConstraint *constraint));
void cpBodyEachArbiter_b(cpBody *body, void (^block)(cpArbiter *arbiter));
typedef void (^cpSpacePointQueryBlock)(cpShape *shape, cpVect point, cpFloat distance, cpVect gradient);
void cpSpacePointQuery_b(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpSpacePointQueryBlock block);
typedef void (^cpSpaceSegmentQueryBlock)(cpShape *shape, cpVect point, cpVect normal, cpFloat alpha);
void cpSpaceSegmentQuery_b(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSpaceSegmentQueryBlock block);
typedef void (^cpSpaceBBQueryBlock)(cpShape *shape);
void cpSpaceBBQuery_b(cpSpace *space, cpBB bb, cpShapeFilter filter, cpSpaceBBQueryBlock block);
typedef void (^cpSpaceShapeQueryBlock)(cpShape *shape, cpContactPointSet *points);
cpBool cpSpaceShapeQuery_b(cpSpace *space, cpShape *shape, cpSpaceShapeQueryBlock block);
#endif
#endif
//@}
#ifdef __cplusplus
}
static inline cpVect operator *(const cpVect v, const cpFloat s){return cpvmult(v, s);}
static inline cpVect operator +(const cpVect v1, const cpVect v2){return cpvadd(v1, v2);}
static inline cpVect operator -(const cpVect v1, const cpVect v2){return cpvsub(v1, v2);}
static inline cpBool operator ==(const cpVect v1, const cpVect v2){return cpveql(v1, v2);}
static inline cpVect operator -(const cpVect v){return cpvneg(v);}
#endif
#endif

View File

@ -1,100 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifdef CHIPMUNK_FFI
// Create non static inlined copies of Chipmunk functions, useful for working with dynamic FFIs
// This file should only be included in chipmunk.c
// TODO: get rid of the reliance on static inlines.
// They make a mess for FFIs.
#ifdef _MSC_VER
#if _MSC_VER >= 1600
#define MAKE_REF(name) decltype(name) *_##name = name
#else
#define MAKE_REF(name)
#endif
#else
#define MAKE_REF(name) __typeof__(name) *_##name = name
#endif
#define MAKE_PROPERTIES_REF(struct, property) \
MAKE_REF(struct##Get##property); MAKE_REF(struct##Set##property)
MAKE_REF(cpv); // makes a variable named _cpv that contains the function pointer for cpv()
MAKE_REF(cpveql);
MAKE_REF(cpvadd);
MAKE_REF(cpvneg);
MAKE_REF(cpvsub);
MAKE_REF(cpvmult);
MAKE_REF(cpvdot);
MAKE_REF(cpvcross);
MAKE_REF(cpvperp);
MAKE_REF(cpvrperp);
MAKE_REF(cpvproject);
MAKE_REF(cpvforangle);
MAKE_REF(cpvtoangle);
MAKE_REF(cpvrotate);
MAKE_REF(cpvunrotate);
MAKE_REF(cpvlengthsq);
MAKE_REF(cpvlength);
MAKE_REF(cpvlerp);
MAKE_REF(cpvnormalize);
MAKE_REF(cpvclamp);
MAKE_REF(cpvlerpconst);
MAKE_REF(cpvdist);
MAKE_REF(cpvdistsq);
MAKE_REF(cpvnear);
MAKE_REF(cpfmax);
MAKE_REF(cpfmin);
MAKE_REF(cpfabs);
MAKE_REF(cpfclamp);
MAKE_REF(cpflerp);
MAKE_REF(cpflerpconst);
MAKE_REF(cpBBNew);
MAKE_REF(cpBBNewForCircle);
MAKE_REF(cpBBIntersects);
MAKE_REF(cpBBContainsBB);
MAKE_REF(cpBBContainsVect);
MAKE_REF(cpBBMerge);
MAKE_REF(cpBBExpand);
MAKE_REF(cpBBArea);
MAKE_REF(cpBBMergedArea);
MAKE_REF(cpBBSegmentQuery);
MAKE_REF(cpBBIntersectsSegment);
MAKE_REF(cpBBClampVect);
MAKE_REF(cpSpatialIndexDestroy);
MAKE_REF(cpSpatialIndexCount);
MAKE_REF(cpSpatialIndexEach);
MAKE_REF(cpSpatialIndexContains);
MAKE_REF(cpSpatialIndexInsert);
MAKE_REF(cpSpatialIndexRemove);
MAKE_REF(cpSpatialIndexReindex);
MAKE_REF(cpSpatialIndexReindexObject);
MAKE_REF(cpSpatialIndexSegmentQuery);
MAKE_REF(cpSpatialIndexQuery);
MAKE_REF(cpSpatialIndexReindexQuery);
#endif

View File

@ -1,765 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifdef CHIPMUNK_H
#error Cannot include chipmunk_private.h after chipmunk.h.
#endif
#ifndef CHIPMUNK_PRIVATE_H
#define CHIPMUNK_PRIVATE_H
#define CP_ALLOW_PRIVATE_ACCESS 1
#include "chipmunk.h"
#define CP_HASH_COEF (3344921057ul)
#define CP_HASH_PAIR(A, B) ((cpHashValue)(A)*CP_HASH_COEF ^ (cpHashValue)(B)*CP_HASH_COEF)
// TODO: Eww. Magic numbers.
#define MAGIC_EPSILON 1e-5
//MARK: cpArray
struct cpArray {
int num, max;
void **arr;
};
cpArray *cpArrayNew(int size);
void cpArrayFree(cpArray *arr);
void cpArrayPush(cpArray *arr, void *object);
void *cpArrayPop(cpArray *arr);
void cpArrayDeleteObj(cpArray *arr, void *obj);
cpBool cpArrayContains(cpArray *arr, void *ptr);
void cpArrayFreeEach(cpArray *arr, void (freeFunc)(void*));
//MARK: cpHashSet
typedef cpBool (*cpHashSetEqlFunc)(void *ptr, void *elt);
typedef void *(*cpHashSetTransFunc)(void *ptr, void *data);
cpHashSet *cpHashSetNew(int size, cpHashSetEqlFunc eqlFunc);
void cpHashSetSetDefaultValue(cpHashSet *set, void *default_value);
void cpHashSetFree(cpHashSet *set);
int cpHashSetCount(cpHashSet *set);
void *cpHashSetInsert(cpHashSet *set, cpHashValue hash, void *ptr, cpHashSetTransFunc trans, void *data);
void *cpHashSetRemove(cpHashSet *set, cpHashValue hash, void *ptr);
void *cpHashSetFind(cpHashSet *set, cpHashValue hash, void *ptr);
typedef void (*cpHashSetIteratorFunc)(void *elt, void *data);
void cpHashSetEach(cpHashSet *set, cpHashSetIteratorFunc func, void *data);
typedef cpBool (*cpHashSetFilterFunc)(void *elt, void *data);
void cpHashSetFilter(cpHashSet *set, cpHashSetFilterFunc func, void *data);
//MARK: Bodies
struct cpBody {
// Integration functions
cpBodyVelocityFunc velocity_func;
cpBodyPositionFunc position_func;
// mass and it's inverse
cpFloat m;
cpFloat m_inv;
// moment of inertia and it's inverse
cpFloat i;
cpFloat i_inv;
// center of gravity
cpVect cog;
// position, velocity, force
cpVect p;
cpVect v;
cpVect f;
// Angle, angular velocity, torque (radians)
cpFloat a;
cpFloat w;
cpFloat t;
cpTransform transform;
cpDataPointer userData;
// "pseudo-velocities" used for eliminating overlap.
// Erin Catto has some papers that talk about what these are.
cpVect v_bias;
cpFloat w_bias;
cpSpace *space;
cpShape *shapeList;
cpArbiter *arbiterList;
cpConstraint *constraintList;
struct {
cpBody *root;
cpBody *next;
cpFloat idleTime;
} sleeping;
};
void cpBodyAddShape(cpBody *body, cpShape *shape);
void cpBodyRemoveShape(cpBody *body, cpShape *shape);
//void cpBodyAccumulateMassForShape(cpBody *body, cpShape *shape);
void cpBodyAccumulateMassFromShapes(cpBody *body);
void cpBodyRemoveConstraint(cpBody *body, cpConstraint *constraint);
//MARK: Spatial Index Functions
cpSpatialIndex *cpSpatialIndexInit(cpSpatialIndex *index, cpSpatialIndexClass *klass, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);
//MARK: Arbiters
enum cpArbiterState {
// Arbiter is active and its the first collision.
CP_ARBITER_STATE_FIRST_COLLISION,
// Arbiter is active and its not the first collision.
CP_ARBITER_STATE_NORMAL,
// Collision has been explicitly ignored.
// Either by returning false from a begin collision handler or calling cpArbiterIgnore().
CP_ARBITER_STATE_IGNORE,
// Collison is no longer active. A space will cache an arbiter for up to cpSpace.collisionPersistence more steps.
CP_ARBITER_STATE_CACHED,
// Collison arbiter is invalid because one of the shapes was removed.
CP_ARBITER_STATE_INVALIDATED,
};
struct cpArbiterThread {
struct cpArbiter *next, *prev;
};
struct cpContact {
cpVect r1, r2;
cpFloat nMass, tMass;
cpFloat bounce; // TODO: look for an alternate bounce solution.
cpFloat jnAcc, jtAcc, jBias;
cpFloat bias;
cpHashValue hash;
};
struct cpCollisionInfo {
const cpShape *a, *b;
cpCollisionID id;
cpVect n;
int count;
// TODO Should this be a unique struct type?
struct cpContact *arr;
};
struct cpArbiter {
cpFloat e;
cpFloat u;
cpVect surface_vr;
cpDataPointer data;
const cpShape *a, *b;
cpBody *body_a, *body_b;
struct cpArbiterThread thread_a, thread_b;
int count;
struct cpContact *contacts;
cpVect n;
// Regular, wildcard A and wildcard B collision handlers.
cpCollisionHandler *handler, *handlerA, *handlerB;
cpBool swapped;
cpTimestamp stamp;
enum cpArbiterState state;
};
cpArbiter* cpArbiterInit(cpArbiter *arb, cpShape *a, cpShape *b);
static inline struct cpArbiterThread *
cpArbiterThreadForBody(cpArbiter *arb, cpBody *body)
{
return (arb->body_a == body ? &arb->thread_a : &arb->thread_b);
}
void cpArbiterUnthread(cpArbiter *arb);
void cpArbiterUpdate(cpArbiter *arb, struct cpCollisionInfo *info, cpSpace *space);
void cpArbiterPreStep(cpArbiter *arb, cpFloat dt, cpFloat bias, cpFloat slop);
void cpArbiterApplyCachedImpulse(cpArbiter *arb, cpFloat dt_coef);
void cpArbiterApplyImpulse(cpArbiter *arb);
//MARK: Shapes/Collisions
struct cpShapeMassInfo {
cpFloat m;
cpFloat i;
cpVect cog;
cpFloat area;
};
typedef enum cpShapeType{
CP_CIRCLE_SHAPE,
CP_SEGMENT_SHAPE,
CP_POLY_SHAPE,
CP_NUM_SHAPES
} cpShapeType;
typedef cpBB (*cpShapeCacheDataImpl)(cpShape *shape, cpTransform transform);
typedef void (*cpShapeDestroyImpl)(cpShape *shape);
typedef void (*cpShapePointQueryImpl)(const cpShape *shape, cpVect p, cpPointQueryInfo *info);
typedef void (*cpShapeSegmentQueryImpl)(const cpShape *shape, cpVect a, cpVect b, cpFloat radius, cpSegmentQueryInfo *info);
typedef struct cpShapeClass cpShapeClass;
struct cpShapeClass {
cpShapeType type;
cpShapeCacheDataImpl cacheData;
cpShapeDestroyImpl destroy;
cpShapePointQueryImpl pointQuery;
cpShapeSegmentQueryImpl segmentQuery;
};
struct cpShape {
const cpShapeClass *klass;
cpSpace *space;
cpBody *body;
struct cpShapeMassInfo massInfo;
cpBB bb;
cpBool sensor;
cpFloat e;
cpFloat u;
cpVect surfaceV;
cpDataPointer userData;
cpCollisionType type;
cpShapeFilter filter;
cpShape *next;
cpShape *prev;
cpHashValue hashid;
};
struct cpCircleShape {
cpShape shape;
cpVect c, tc;
cpFloat r;
};
struct cpSegmentShape {
cpShape shape;
cpVect a, b, n;
cpVect ta, tb, tn;
cpFloat r;
cpVect a_tangent, b_tangent;
};
struct cpSplittingPlane {
cpVect v0, n;
};
#define CP_POLY_SHAPE_INLINE_ALLOC 6
struct cpPolyShape {
cpShape shape;
cpFloat r;
int count;
// The untransformed planes are appended at the end of the transformed planes.
struct cpSplittingPlane *planes;
// Allocate a small number of splitting planes internally for simple poly.
struct cpSplittingPlane _planes[2*CP_POLY_SHAPE_INLINE_ALLOC];
};
cpShape *cpShapeInit(cpShape *shape, const cpShapeClass *klass, cpBody *body, struct cpShapeMassInfo massInfo);
static inline cpBool
cpShapeActive(cpShape *shape)
{
// checks if the shape is added to a shape list.
// TODO could this just check the space now?
return (shape->prev || (shape->body && shape->body->shapeList == shape));
}
// Note: This function returns contact points with r1/r2 in absolute coordinates, not body relative.
struct cpCollisionInfo cpCollide(const cpShape *a, const cpShape *b, cpCollisionID id, struct cpContact *contacts);
static inline void
CircleSegmentQuery(cpShape *shape, cpVect center, cpFloat r1, cpVect a, cpVect b, cpFloat r2, cpSegmentQueryInfo *info)
{
cpVect da = cpvsub(a, center);
cpVect db = cpvsub(b, center);
cpFloat rsum = r1 + r2;
cpFloat qa = cpvdot(da, da) - 2.0f*cpvdot(da, db) + cpvdot(db, db);
cpFloat qb = cpvdot(da, db) - cpvdot(da, da);
cpFloat det = qb*qb - qa*(cpvdot(da, da) - rsum*rsum);
if(det >= 0.0f){
cpFloat t = (-qb - cpfsqrt(det))/(qa);
if(0.0f<= t && t <= 1.0f){
cpVect n = cpvnormalize(cpvlerp(da, db, t));
info->shape = shape;
info->point = cpvsub(cpvlerp(a, b, t), cpvmult(n, r2));
info->normal = n;
info->alpha = t;
}
}
}
static inline cpBool
cpShapeFilterReject(cpShapeFilter a, cpShapeFilter b)
{
// Reject the collision if:
return (
// They are in the same non-zero group.
(a.group != 0 && a.group == b.group) ||
// One of the category/mask combinations fails.
(a.categories & b.mask) == 0 ||
(b.categories & a.mask) == 0
);
}
void cpLoopIndexes(const cpVect *verts, int count, int *start, int *end);
//MARK: Constraints
// TODO naming conventions here
typedef void (*cpConstraintPreStepImpl)(cpConstraint *constraint, cpFloat dt);
typedef void (*cpConstraintApplyCachedImpulseImpl)(cpConstraint *constraint, cpFloat dt_coef);
typedef void (*cpConstraintApplyImpulseImpl)(cpConstraint *constraint, cpFloat dt);
typedef cpFloat (*cpConstraintGetImpulseImpl)(cpConstraint *constraint);
typedef struct cpConstraintClass {
cpConstraintPreStepImpl preStep;
cpConstraintApplyCachedImpulseImpl applyCachedImpulse;
cpConstraintApplyImpulseImpl applyImpulse;
cpConstraintGetImpulseImpl getImpulse;
} cpConstraintClass;
struct cpConstraint {
const cpConstraintClass *klass;
cpSpace *space;
cpBody *a, *b;
cpConstraint *next_a, *next_b;
cpFloat maxForce;
cpFloat errorBias;
cpFloat maxBias;
cpBool collideBodies;
cpConstraintPreSolveFunc preSolve;
cpConstraintPostSolveFunc postSolve;
cpDataPointer userData;
};
struct cpPinJoint {
cpConstraint constraint;
cpVect anchorA, anchorB;
cpFloat dist;
cpVect r1, r2;
cpVect n;
cpFloat nMass;
cpFloat jnAcc;
cpFloat bias;
};
struct cpSlideJoint {
cpConstraint constraint;
cpVect anchorA, anchorB;
cpFloat min, max;
cpVect r1, r2;
cpVect n;
cpFloat nMass;
cpFloat jnAcc;
cpFloat bias;
};
struct cpPivotJoint {
cpConstraint constraint;
cpVect anchorA, anchorB;
cpVect r1, r2;
cpMat2x2 k;
cpVect jAcc;
cpVect bias;
};
struct cpGrooveJoint {
cpConstraint constraint;
cpVect grv_n, grv_a, grv_b;
cpVect anchorB;
cpVect grv_tn;
cpFloat clamp;
cpVect r1, r2;
cpMat2x2 k;
cpVect jAcc;
cpVect bias;
};
struct cpDampedSpring {
cpConstraint constraint;
cpVect anchorA, anchorB;
cpFloat restLength;
cpFloat stiffness;
cpFloat damping;
cpDampedSpringForceFunc springForceFunc;
cpFloat target_vrn;
cpFloat v_coef;
cpVect r1, r2;
cpFloat nMass;
cpVect n;
cpFloat jAcc;
};
struct cpDampedRotarySpring {
cpConstraint constraint;
cpFloat restAngle;
cpFloat stiffness;
cpFloat damping;
cpDampedRotarySpringTorqueFunc springTorqueFunc;
cpFloat target_wrn;
cpFloat w_coef;
cpFloat iSum;
cpFloat jAcc;
};
struct cpRotaryLimitJoint {
cpConstraint constraint;
cpFloat min, max;
cpFloat iSum;
cpFloat bias;
cpFloat jAcc;
};
struct cpRatchetJoint {
cpConstraint constraint;
cpFloat angle, phase, ratchet;
cpFloat iSum;
cpFloat bias;
cpFloat jAcc;
};
struct cpGearJoint {
cpConstraint constraint;
cpFloat phase, ratio;
cpFloat ratio_inv;
cpFloat iSum;
cpFloat bias;
cpFloat jAcc;
};
struct cpSimpleMotor {
cpConstraint constraint;
cpFloat rate;
cpFloat iSum;
cpFloat jAcc;
};
void cpConstraintInit(cpConstraint *constraint, const struct cpConstraintClass *klass, cpBody *a, cpBody *b);
static inline void
cpConstraintActivateBodies(cpConstraint *constraint)
{
cpBody *a = constraint->a; cpBodyActivate(a);
cpBody *b = constraint->b; cpBodyActivate(b);
}
static inline cpVect
relative_velocity(cpBody *a, cpBody *b, cpVect r1, cpVect r2){
cpVect v1_sum = cpvadd(a->CP_PRIVATE(v), cpvmult(cpvperp(r1), a->CP_PRIVATE(w)));
cpVect v2_sum = cpvadd(b->CP_PRIVATE(v), cpvmult(cpvperp(r2), b->CP_PRIVATE(w)));
return cpvsub(v2_sum, v1_sum);
}
static inline cpFloat
normal_relative_velocity(cpBody *a, cpBody *b, cpVect r1, cpVect r2, cpVect n){
return cpvdot(relative_velocity(a, b, r1, r2), n);
}
static inline void
apply_impulse(cpBody *body, cpVect j, cpVect r){
body->CP_PRIVATE(v) = cpvadd(body->CP_PRIVATE(v), cpvmult(j, body->CP_PRIVATE(m_inv)));
body->CP_PRIVATE(w) += body->CP_PRIVATE(i_inv)*cpvcross(r, j);
}
static inline void
apply_impulses(cpBody *a , cpBody *b, cpVect r1, cpVect r2, cpVect j)
{
apply_impulse(a, cpvneg(j), r1);
apply_impulse(b, j, r2);
}
static inline void
apply_bias_impulse(cpBody *body, cpVect j, cpVect r)
{
body->CP_PRIVATE(v_bias) = cpvadd(body->CP_PRIVATE(v_bias), cpvmult(j, body->CP_PRIVATE(m_inv)));
body->CP_PRIVATE(w_bias) += body->CP_PRIVATE(i_inv)*cpvcross(r, j);
}
static inline void
apply_bias_impulses(cpBody *a , cpBody *b, cpVect r1, cpVect r2, cpVect j)
{
apply_bias_impulse(a, cpvneg(j), r1);
apply_bias_impulse(b, j, r2);
}
static inline cpFloat
k_scalar_body(cpBody *body, cpVect r, cpVect n)
{
cpFloat rcn = cpvcross(r, n);
return body->CP_PRIVATE(m_inv) + body->CP_PRIVATE(i_inv)*rcn*rcn;
}
static inline cpFloat
k_scalar(cpBody *a, cpBody *b, cpVect r1, cpVect r2, cpVect n)
{
cpFloat value = k_scalar_body(a, r1, n) + k_scalar_body(b, r2, n);
cpAssertSoft(value != 0.0, "Unsolvable collision or constraint.");
return value;
}
static inline cpMat2x2
k_tensor(cpBody *a, cpBody *b, cpVect r1, cpVect r2)
{
cpFloat m_sum = a->CP_PRIVATE(m_inv) + b->CP_PRIVATE(m_inv);
// start with Identity*m_sum
cpFloat k11 = m_sum, k12 = 0.0f;
cpFloat k21 = 0.0f, k22 = m_sum;
// add the influence from r1
cpFloat a_i_inv = a->CP_PRIVATE(i_inv);
cpFloat r1xsq = r1.x * r1.x * a_i_inv;
cpFloat r1ysq = r1.y * r1.y * a_i_inv;
cpFloat r1nxy = -r1.x * r1.y * a_i_inv;
k11 += r1ysq; k12 += r1nxy;
k21 += r1nxy; k22 += r1xsq;
// add the influnce from r2
cpFloat b_i_inv = b->CP_PRIVATE(i_inv);
cpFloat r2xsq = r2.x * r2.x * b_i_inv;
cpFloat r2ysq = r2.y * r2.y * b_i_inv;
cpFloat r2nxy = -r2.x * r2.y * b_i_inv;
k11 += r2ysq; k12 += r2nxy;
k21 += r2nxy; k22 += r2xsq;
// invert
cpFloat det = k11*k22 - k12*k21;
cpAssertSoft(det != 0.0, "Unsolvable constraint.");
cpFloat det_inv = 1.0f/det;
return cpMat2x2New(
k22*det_inv, -k12*det_inv,
-k21*det_inv, k11*det_inv
);
}
static inline cpFloat
bias_coef(cpFloat errorBias, cpFloat dt)
{
return 1.0f - cpfpow(errorBias, dt);
}
//MARK: Spaces
struct cpSpace {
int iterations;
cpVect gravity;
cpFloat damping;
cpFloat idleSpeedThreshold;
cpFloat sleepTimeThreshold;
cpFloat collisionSlop;
cpFloat collisionBias;
cpTimestamp collisionPersistence;
cpDataPointer userData;
cpTimestamp stamp;
cpFloat curr_dt;
cpArray *dynamicBodies;
cpArray *staticBodies;
cpArray *rousedBodies;
cpArray *sleepingComponents;
cpHashValue shapeIDCounter;
cpSpatialIndex *staticShapes;
cpSpatialIndex *dynamicShapes;
cpArray *constraints;
cpArray *arbiters;
cpContactBufferHeader *contactBuffersHead;
cpHashSet *cachedArbiters;
cpArray *pooledArbiters;
cpArray *allocatedBuffers;
unsigned int locked;
cpBool usesWildcards;
cpHashSet *collisionHandlers;
cpCollisionHandler defaultHandler;
cpBool skipPostStep;
cpArray *postStepCallbacks;
cpBody *staticBody;
cpBody _staticBody;
};
#define cpAssertSpaceUnlocked(space) \
cpAssertHard(!space->locked, \
"This operation cannot be done safely during a call to cpSpaceStep() or during a query. " \
"Put these calls into a post-step callback." \
);
void cpSpaceSetStaticBody(cpSpace *space, cpBody *body);
extern cpCollisionHandler cpCollisionHandlerDoNothing;
void cpSpaceProcessComponents(cpSpace *space, cpFloat dt);
void cpSpacePushFreshContactBuffer(cpSpace *space);
struct cpContact *cpContactBufferGetArray(cpSpace *space);
void cpSpacePushContacts(cpSpace *space, int count);
typedef struct cpPostStepCallback {
cpPostStepFunc func;
void *key;
void *data;
} cpPostStepCallback;
cpPostStepCallback *cpSpaceGetPostStepCallback(cpSpace *space, void *key);
cpBool cpSpaceArbiterSetFilter(cpArbiter *arb, cpSpace *space);
void cpSpaceFilterArbiters(cpSpace *space, cpBody *body, cpShape *filter);
void cpSpaceActivateBody(cpSpace *space, cpBody *body);
void cpSpaceLock(cpSpace *space);
void cpSpaceUnlock(cpSpace *space, cpBool runPostStep);
static inline void
cpSpaceUncacheArbiter(cpSpace *space, cpArbiter *arb)
{
const cpShape *a = arb->a, *b = arb->b;
const cpShape *shape_pair[] = {a, b};
cpHashValue arbHashID = CP_HASH_PAIR((cpHashValue)a, (cpHashValue)b);
cpHashSetRemove(space->cachedArbiters, arbHashID, shape_pair);
cpArrayDeleteObj(space->arbiters, arb);
}
static inline cpArray *
cpSpaceArrayForBodyType(cpSpace *space, cpBodyType type)
{
return (type == CP_BODY_TYPE_STATIC ? space->staticBodies : space->dynamicBodies);
}
void cpShapeUpdateFunc(cpShape *shape, void *unused);
cpCollisionID cpSpaceCollideShapes(cpShape *a, cpShape *b, cpCollisionID id, cpSpace *space);
//MARK: Foreach loops
static inline cpConstraint *
cpConstraintNext(cpConstraint *node, cpBody *body)
{
return (node->a == body ? node->next_a : node->next_b);
}
#define CP_BODY_FOREACH_CONSTRAINT(bdy, var)\
for(cpConstraint *var = bdy->constraintList; var; var = cpConstraintNext(var, bdy))
static inline cpArbiter *
cpArbiterNext(cpArbiter *node, cpBody *body)
{
return (node->body_a == body ? node->thread_a.next : node->thread_b.next);
}
#define CP_BODY_FOREACH_ARBITER(bdy, var)\
for(cpArbiter *var = bdy->arbiterList; var; var = cpArbiterNext(var, bdy))
#define CP_BODY_FOREACH_SHAPE(body, var)\
for(cpShape *var = body->shapeList; var; var = var->next)
#define CP_BODY_FOREACH_COMPONENT(root, var)\
for(cpBody *var = root; var; var = var->sleeping.next)
#endif

View File

@ -1,269 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef CHIPMUNK_TYPES_H
#define CHIPMUNK_TYPES_H
#include <stdint.h>
#include <float.h>
#include <math.h>
#ifdef __APPLE__
#include "TargetConditionals.h"
#endif
// Use CGTypes by default on iOS and Mac.
// Also enables usage of doubles on 64 bit.
// Performance is usually very comparable when the CPU cache is well utilised.
#if (TARGET_OS_IPHONE || TARGET_OS_MAC) && (!defined CP_USE_CGTYPES)
#define CP_USE_CGTYPES 1
#endif
#if CP_USE_CGTYPES
#if TARGET_OS_IPHONE
#import <CoreGraphics/CGGeometry.h>
#import <CoreGraphics/CGAffineTransform.h>
#elif TARGET_OS_MAC
#include <ApplicationServices/ApplicationServices.h>
#endif
#if defined(__LP64__) && __LP64__
#define CP_USE_DOUBLES 1
#else
#define CP_USE_DOUBLES 0
#endif
#endif
#ifndef CP_USE_DOUBLES
// Use doubles by default for higher precision.
#define CP_USE_DOUBLES 1
#endif
/// @defgroup basicTypes Basic Types
/// Most of these types can be configured at compile time.
/// @{
#if CP_USE_DOUBLES
/// Chipmunk's floating point type.
/// Can be reconfigured at compile time.
typedef double cpFloat;
#define cpfsqrt sqrt
#define cpfsin sin
#define cpfcos cos
#define cpfacos acos
#define cpfatan2 atan2
#define cpfmod fmod
#define cpfexp exp
#define cpfpow pow
#define cpffloor floor
#define cpfceil ceil
#define CPFLOAT_MIN DBL_MIN
#else
typedef float cpFloat;
#define cpfsqrt sqrtf
#define cpfsin sinf
#define cpfcos cosf
#define cpfacos acosf
#define cpfatan2 atan2f
#define cpfmod fmodf
#define cpfexp expf
#define cpfpow powf
#define cpffloor floorf
#define cpfceil ceilf
#define CPFLOAT_MIN FLT_MIN
#endif
#ifndef INFINITY
#ifdef _MSC_VER
union MSVC_EVIL_FLOAT_HACK
{
unsigned __int8 Bytes[4];
float Value;
};
static union MSVC_EVIL_FLOAT_HACK INFINITY_HACK = {{0x00, 0x00, 0x80, 0x7F}};
#define INFINITY (INFINITY_HACK.Value)
#endif
#ifdef __GNUC__
#define INFINITY (__builtin_inf())
#endif
#ifndef INFINITY
#define INFINITY (1e1000)
#endif
#endif
#ifndef M_PI
#define M_PI 3.14159265358979323846264338327950288
#endif
#ifndef M_E
#define M_E 2.71828182845904523536028747135266250
#endif
/// Return the max of two cpFloats.
static inline cpFloat cpfmax(cpFloat a, cpFloat b)
{
return (a > b) ? a : b;
}
/// Return the min of two cpFloats.
static inline cpFloat cpfmin(cpFloat a, cpFloat b)
{
return (a < b) ? a : b;
}
/// Return the absolute value of a cpFloat.
static inline cpFloat cpfabs(cpFloat f)
{
return (f < 0) ? -f : f;
}
/// Clamp @c f to be between @c min and @c max.
static inline cpFloat cpfclamp(cpFloat f, cpFloat min, cpFloat max)
{
return cpfmin(cpfmax(f, min), max);
}
/// Clamp @c f to be between 0 and 1.
static inline cpFloat cpfclamp01(cpFloat f)
{
return cpfmax(0.0f, cpfmin(f, 1.0f));
}
/// Linearly interpolate (or extrapolate) between @c f1 and @c f2 by @c t percent.
static inline cpFloat cpflerp(cpFloat f1, cpFloat f2, cpFloat t)
{
return f1*(1.0f - t) + f2*t;
}
/// Linearly interpolate from @c f1 to @c f2 by no more than @c d.
static inline cpFloat cpflerpconst(cpFloat f1, cpFloat f2, cpFloat d)
{
return f1 + cpfclamp(f2 - f1, -d, d);
}
/// Hash value type.
typedef uintptr_t cpHashValue;
/// Type used internally to cache colliding object info for cpCollideShapes().
/// Should be at least 32 bits.
typedef uint32_t cpCollisionID;
// Oh C, how we love to define our own boolean types to get compiler compatibility
/// Chipmunk's boolean type.
#ifdef CP_BOOL_TYPE
typedef CP_BOOL_TYPE cpBool;
#else
typedef unsigned char cpBool;
#endif
#ifndef cpTrue
/// true value.
#define cpTrue 1
#endif
#ifndef cpFalse
/// false value.
#define cpFalse 0
#endif
#ifdef CP_DATA_POINTER_TYPE
typedef CP_DATA_POINTER_TYPE cpDataPointer;
#else
/// Type used for user data pointers.
typedef void * cpDataPointer;
#endif
#ifdef CP_COLLISION_TYPE_TYPE
typedef CP_COLLISION_TYPE_TYPE cpCollisionType;
#else
/// Type used for cpSpace.collision_type.
typedef uintptr_t cpCollisionType;
#endif
#ifdef CP_GROUP_TYPE
typedef CP_GROUP_TYPE cpGroup;
#else
/// Type used for cpShape.group.
typedef uintptr_t cpGroup;
#endif
#ifdef CP_BITMASK_TYPE
typedef CP_BITMASK_TYPE cpLayers;
#else
/// Type used for cpShapeFilter category and mask.
typedef unsigned int cpBitmask;
#endif
#ifdef CP_TIMESTAMP_TYPE
typedef CP_TIMESTAMP_TYPE cpTimestamp;
#else
/// Type used for various timestamps in Chipmunk.
typedef unsigned int cpTimestamp;
#endif
#ifndef CP_NO_GROUP
/// Value for cpShape.group signifying that a shape is in no group.
#define CP_NO_GROUP ((cpGroup)0)
#endif
#ifndef CP_ALL_CATEGORIES
/// Value for cpShape.layers signifying that a shape is in every layer.
#define CP_ALL_CATEGORIES (~(cpBitmask)0)
#endif
#ifndef CP_WILDCARD_COLLISION_TYPE
/// cpCollisionType value internally reserved for hashing wildcard handlers.
#define CP_WILDCARD_COLLISION_TYPE (~(cpCollisionType)0)
#endif
/// @}
// CGPoints are structurally the same, and allow
// easy interoperability with other Cocoa libraries
#if CP_USE_CGTYPES
typedef CGPoint cpVect;
#else
/// Chipmunk's 2D vector type.
/// @addtogroup cpVect
typedef struct cpVect{cpFloat x,y;} cpVect;
#endif
#if CP_USE_CGTYPES
typedef CGAffineTransform cpTransform;
#else
/// Column major affine transform.
typedef struct cpTransform {
cpFloat a, b, c, d, tx, ty;
} cpTransform;
#endif
// NUKE
typedef struct cpMat2x2 {
// Row major [[a, b][c d]]
cpFloat a, b, c, d;
} cpMat2x2;
#endif

View File

@ -1,66 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 header defines a number of "unsafe" operations on Chipmunk objects.
* In this case "unsafe" is referring to operations which may reduce the
* physical accuracy or numerical stability of the simulation, but will not
* cause crashes.
*
* The prime example is mutating collision shapes. Chipmunk does not support
* this directly. Mutating shapes using this API will caused objects in contact
* to be pushed apart using Chipmunk's overlap solver, but not using real
* persistent velocities. Probably not what you meant, but perhaps close enough.
*/
/// @defgroup unsafe Chipmunk Unsafe Shape Operations
/// These functions are used for mutating collision shapes.
/// Chipmunk does not have any way to get velocity information on changing shapes,
/// so the results will be unrealistic. You must explicity include the chipmunk_unsafe.h header to use them.
/// @{
#ifndef CHIPMUNK_UNSAFE_H
#define CHIPMUNK_UNSAFE_H
#ifdef __cplusplus
extern "C" {
#endif
/// Set the radius of a circle shape.
void cpCircleShapeSetRadius(cpShape *shape, cpFloat radius);
/// Set the offset of a circle shape.
void cpCircleShapeSetOffset(cpShape *shape, cpVect offset);
/// Set the endpoints of a segment shape.
void cpSegmentShapeSetEndpoints(cpShape *shape, cpVect a, cpVect b);
/// Set the radius of a segment shape.
void cpSegmentShapeSetRadius(cpShape *shape, cpFloat radius);
/// Set the vertexes of a poly shape.
void cpPolyShapeSetVerts(cpShape *shape, int count, cpVect *verts, cpTransform transform);
void cpPolyShapeSetVertsRaw(cpShape *shape, int count, cpVect *verts);
/// Set the radius of a poly shape.
void cpPolyShapeSetRadius(cpShape *shape, cpFloat radius);
#ifdef __cplusplus
}
#endif
#endif
/// @}

View File

@ -1,126 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 cpArbiter cpArbiter
/// The cpArbiter struct controls pairs of colliding shapes.
/// They are also used in conjuction with collision handler callbacks
/// allowing you to retrieve information on the collision and control it.
/// @{
#define CP_MAX_CONTACTS_PER_ARBITER 2
// TODO: Document
cpFloat cpArbiterGetRestitution(const cpArbiter *arb);
void cpArbiterSetRestitution(cpArbiter *arb, cpFloat restitution);
cpFloat cpArbiterGetFriction(const cpArbiter *arb);
void cpArbiterSetFriction(cpArbiter *arb, cpFloat friction);
// Get the relative surface velocity of the two shapes in contact.
cpVect cpArbiterGetSurfaceVelocity(cpArbiter *arb);
// Override the relative surface velocity of the two shapes in contact.
// By default this is calculated to be the difference of the two
// surface velocities clamped to the tangent plane.
void cpArbiterSetSurfaceVelocity(cpArbiter *arb, cpVect vr);
cpDataPointer cpArbiterGetUserData(const cpArbiter *arb);
void cpArbiterSetUserData(cpArbiter *arb, cpDataPointer userData);
/// Calculate the total impulse including the friction that was applied by this arbiter.
/// This function should only be called from a post-solve, post-step or cpBodyEachArbiter callback.
cpVect cpArbiterTotalImpulse(const cpArbiter *arb);
/// Calculate the amount of energy lost in a collision including static, but not dynamic friction.
/// This function should only be called from a post-solve, post-step or cpBodyEachArbiter callback.
cpFloat cpArbiterTotalKE(const cpArbiter *arb);
cpBool cpArbiterIgnore(cpArbiter *arb);
/// Return the colliding shapes involved for this arbiter.
/// The order of their cpSpace.collision_type values will match
/// the order set when the collision handler was registered.
void cpArbiterGetShapes(const cpArbiter *arb, cpShape **a, cpShape **b);
/// A macro shortcut for defining and retrieving the shapes from an arbiter.
#define CP_ARBITER_GET_SHAPES(__arb__, __a__, __b__) cpShape *__a__, *__b__; cpArbiterGetShapes(__arb__, &__a__, &__b__);
/// Return the colliding bodies involved for this arbiter.
/// The order of the cpSpace.collision_type the bodies are associated with values will match
/// the order set when the collision handler was registered.
void cpArbiterGetBodies(const cpArbiter *arb, cpBody **a, cpBody **b);
/// A macro shortcut for defining and retrieving the bodies from an arbiter.
#define CP_ARBITER_GET_BODIES(__arb__, __a__, __b__) cpBody *__a__, *__b__; cpArbiterGetBodies(__arb__, &__a__, &__b__);
/// A struct that wraps up the important collision data for an arbiter.
struct cpContactPointSet {
/// The number of contact points in the set.
int count;
/// The normal of the collision.
cpVect normal;
/// The array of contact points.
struct {
/// The position of the contact on the surface of each shape.
cpVect pointA, pointB;
/// Penetration distance of the two shapes. Overlapping means it will be negative.
/// This value is calculated as cpvdot(cpvsub(point2, point1), normal) and is ignored by cpArbiterSetContactPointSet().
cpFloat distance;
} points[CP_MAX_CONTACTS_PER_ARBITER];
};
/// Return a contact set from an arbiter.
cpContactPointSet cpArbiterGetContactPointSet(const cpArbiter *arb);
/// Replace the contact point set for an arbiter.
/// This can be a very powerful feature, but use it with caution!
void cpArbiterSetContactPointSet(cpArbiter *arb, cpContactPointSet *set);
/// Returns true if this is the first step a pair of objects started colliding.
cpBool cpArbiterIsFirstContact(const cpArbiter *arb);
/// Returns true if in separate callback due to a shape being removed from the space.
cpBool cpArbiterIsRemoval(const cpArbiter *arb);
/// Get the number of contact points for this arbiter.
int cpArbiterGetCount(const cpArbiter *arb);
/// Get the normal of the collision.
cpVect cpArbiterGetNormal(const cpArbiter *arb);
/// Get the position of the @c ith contact point on the surface of the first shape.
cpVect cpArbiterGetPointA(const cpArbiter *arb, int i);
/// Get the position of the @c ith contact point on the surface of the second shape.
cpVect cpArbiterGetPointB(const cpArbiter *arb, int i);
/// Get the depth of the @c ith contact point.
cpFloat cpArbiterGetDepth(const cpArbiter *arb, int i);
cpBool cpArbiterCallWildcardBeginA(cpArbiter *arb, cpSpace *space);
cpBool cpArbiterCallWildcardBeginB(cpArbiter *arb, cpSpace *space);
cpBool cpArbiterCallWildcardPreSolveA(cpArbiter *arb, cpSpace *space);
cpBool cpArbiterCallWildcardPreSolveB(cpArbiter *arb, cpSpace *space);
void cpArbiterCallWildcardPostSolveA(cpArbiter *arb, cpSpace *space);
void cpArbiterCallWildcardPostSolveB(cpArbiter *arb, cpSpace *space);
void cpArbiterCallWildcardSeparateA(cpArbiter *arb, cpSpace *space);
void cpArbiterCallWildcardSeparateB(cpArbiter *arb, cpSpace *space);
/// @}

View File

@ -1,169 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef CHIPMUNK_BB_H
#define CHIPMUNK_BB_H
#include "chipmunk_types.h"
#include "cpVect.h"
/// @defgroup cpBBB cpBB
/// Chipmunk's axis-aligned 2D bounding box type along with a few handy routines.
/// @{
/// Chipmunk's axis-aligned 2D bounding box type. (left, bottom, right, top)
typedef struct cpBB{
cpFloat l, b, r ,t;
} cpBB;
/// Convenience constructor for cpBB structs.
static inline cpBB cpBBNew(const cpFloat l, const cpFloat b, const cpFloat r, const cpFloat t)
{
cpBB bb = {l, b, r, t};
return bb;
}
/// Constructs a cpBB centered on a point with the given extents (half sizes).
static inline cpBB
cpBBNewForExtents(const cpVect c, const cpFloat hw, const cpFloat hh)
{
return cpBBNew(c.x - hw, c.y - hh, c.x + hw, c.y + hh);
}
/// Constructs a cpBB for a circle with the given position and radius.
static inline cpBB cpBBNewForCircle(const cpVect p, const cpFloat r)
{
return cpBBNewForExtents(p, r, r);
}
/// Returns true if @c a and @c b intersect.
static inline cpBool cpBBIntersects(const cpBB a, const cpBB b)
{
return (a.l <= b.r && b.l <= a.r && a.b <= b.t && b.b <= a.t);
}
/// Returns true if @c other lies completely within @c bb.
static inline cpBool cpBBContainsBB(const cpBB bb, const cpBB 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.
static inline cpBool cpBBContainsVect(const cpBB bb, const cpVect v)
{
return (bb.l <= v.x && bb.r >= v.x && bb.b <= v.y && bb.t >= v.y);
}
/// Returns a bounding box that holds both bounding boxes.
static inline cpBB cpBBMerge(const cpBB a, const cpBB b){
return cpBBNew(
cpfmin(a.l, b.l),
cpfmin(a.b, b.b),
cpfmax(a.r, b.r),
cpfmax(a.t, b.t)
);
}
/// Returns a bounding box that holds both @c bb and @c v.
static inline cpBB cpBBExpand(const cpBB bb, const cpVect v){
return cpBBNew(
cpfmin(bb.l, v.x),
cpfmin(bb.b, v.y),
cpfmax(bb.r, v.x),
cpfmax(bb.t, v.y)
);
}
/// Returns the center of a bounding box.
static inline cpVect
cpBBCenter(cpBB bb)
{
return cpvlerp(cpv(bb.l, bb.b), cpv(bb.r, bb.t), 0.5f);
}
/// Returns the area of the bounding box.
static inline cpFloat cpBBArea(cpBB 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.
static inline cpFloat cpBBMergedArea(cpBB a, cpBB b)
{
return (cpfmax(a.r, b.r) - cpfmin(a.l, b.l))*(cpfmax(a.t, b.t) - cpfmin(a.b, b.b));
}
/// Returns the fraction along the segment query the cpBB is hit. Returns INFINITY if it doesn't hit.
static inline cpFloat cpBBSegmentQuery(cpBB bb, cpVect a, cpVect b)
{
cpFloat idx = 1.0f/(b.x - a.x);
cpFloat tx1 = (bb.l == a.x ? -INFINITY : (bb.l - a.x)*idx);
cpFloat tx2 = (bb.r == a.x ? INFINITY : (bb.r - a.x)*idx);
cpFloat txmin = cpfmin(tx1, tx2);
cpFloat txmax = cpfmax(tx1, tx2);
cpFloat idy = 1.0f/(b.y - a.y);
cpFloat ty1 = (bb.b == a.y ? -INFINITY : (bb.b - a.y)*idy);
cpFloat ty2 = (bb.t == a.y ? INFINITY : (bb.t - a.y)*idy);
cpFloat tymin = cpfmin(ty1, ty2);
cpFloat tymax = cpfmax(ty1, ty2);
if(tymin <= txmax && txmin <= tymax){
cpFloat min = cpfmax(txmin, tymin);
cpFloat max = cpfmin(txmax, tymax);
if(0.0 <= max && min <= 1.0) return cpfmax(min, 0.0);
}
return INFINITY;
}
/// Return true if the bounding box intersects the line segment with ends @c a and @c b.
static inline cpBool cpBBIntersectsSegment(cpBB bb, cpVect a, cpVect b)
{
return (cpBBSegmentQuery(bb, a, b) != INFINITY);
}
/// Clamp a vector to a bounding box.
static inline cpVect
cpBBClampVect(const cpBB bb, const cpVect v)
{
return cpv(cpfclamp(v.x, bb.l, bb.r), cpfclamp(v.y, bb.b, bb.t));
}
/// Wrap a vector to a bounding box.
static inline cpVect
cpBBWrapVect(const cpBB bb, const cpVect v)
{
cpFloat dx = cpfabs(bb.r - bb.l);
cpFloat modx = cpfmod(v.x - bb.l, dx);
cpFloat x = (modx > 0.0f) ? modx : modx + dx;
cpFloat dy = cpfabs(bb.t - bb.b);
cpFloat mody = cpfmod(v.y - bb.b, dy);
cpFloat y = (mody > 0.0f) ? mody : mody + dy;
return cpv(x + bb.l, y + bb.b);
}
///@}
#endif

View File

@ -1,180 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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.
/// @{
typedef enum cpBodyType {
CP_BODY_TYPE_DYNAMIC,
CP_BODY_TYPE_KINEMATIC,
CP_BODY_TYPE_STATIC,
} cpBodyType;
/// Rigid body velocity update function type.
typedef void (*cpBodyVelocityFunc)(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt);
/// Rigid body position update function type.
typedef void (*cpBodyPositionFunc)(cpBody *body, cpFloat dt);
/// Allocate a cpBody.
cpBody* cpBodyAlloc(void);
/// Initialize a cpBody.
cpBody* cpBodyInit(cpBody *body, cpFloat mass, cpFloat moment);
/// Allocate and initialize a cpBody.
cpBody* cpBodyNew(cpFloat mass, cpFloat moment);
/// Allocate and initialize a cpBody, and set it as a kinematic body.
cpBody* cpBodyNewKinematic(void);
/// Allocate and initialize a cpBody, and set it as a static body.
cpBody* cpBodyNewStatic(void);
/// Destroy a cpBody.
void cpBodyDestroy(cpBody *body);
/// Destroy and free a cpBody.
void cpBodyFree(cpBody *body);
// Defined in cpSpace.c
/// Wake up a sleeping or idle body.
void cpBodyActivate(cpBody *body);
/// Wake up any sleeping or idle bodies touching a static body.
void cpBodyActivateStatic(cpBody *body, cpShape *filter);
/// Force a body to fall asleep immediately.
void cpBodySleep(cpBody *body);
/// Force a body to fall asleep immediately along with other bodies in a group.
void cpBodySleepWithGroup(cpBody *body, cpBody *group);
/// Returns true if the body is sleeping.
cpBool cpBodyIsSleeping(const cpBody *body);
/// Get the type of the body.
cpBodyType cpBodyGetType(cpBody *body);
/// Set the type of the body.
void cpBodySetType(cpBody *body, cpBodyType type);
/// Get the space this body is added to.
cpSpace* cpBodyGetSpace(const cpBody *body);
/// Get the mass of the body.
cpFloat cpBodyGetMass(const cpBody *body);
/// Set the mass of the body.
void cpBodySetMass(cpBody *body, cpFloat m);
/// Get the moment of inertia of the body.
cpFloat cpBodyGetMoment(const cpBody *body);
/// Set the moment of inertia of the body.
void cpBodySetMoment(cpBody *body, cpFloat i);
/// Set the position of a body.
cpVect cpBodyGetPosition(const cpBody *body);
/// Set the position of the body.
void cpBodySetPosition(cpBody *body, cpVect pos);
/// Get the offset of the center of gravity in body local coordinates.
cpVect cpBodyGetCenterOfGravity(const cpBody *body);
/// Set the offset of the center of gravity in body local coordinates.
void cpBodySetCenterOfGravity(cpBody *body, cpVect cog);
/// Get the velocity of the body.
cpVect cpBodyGetVelocity(const cpBody *body);
/// Set the velocity of the body.
void cpBodySetVelocity(cpBody *body, cpVect velocity);
/// Get the force applied to the body for the next time step.
cpVect cpBodyGetForce(const cpBody *body);
/// Set the force applied to the body for the next time step.
void cpBodySetForce(cpBody *body, cpVect force);
/// Get the angle of the body.
cpFloat cpBodyGetAngle(const cpBody *body);
/// Set the angle of a body.
void cpBodySetAngle(cpBody *body, cpFloat a);
/// Get the angular velocity of the body.
cpFloat cpBodyGetAngularVelocity(const cpBody *body);
/// Set the angular velocity of the body.
void cpBodySetAngularVelocity(cpBody *body, cpFloat angularVelocity);
/// Get the torque applied to the body for the next time step.
cpFloat cpBodyGetTorque(const cpBody *body);
/// Set the torque applied to the body for the next time step.
void cpBodySetTorque(cpBody *body, cpFloat torque);
/// Get the rotation vector of the body. (The x basis vector of it's transform.)
cpVect cpBodyGetRotation(const cpBody *body);
/// Get the user data pointer assigned to the body.
cpDataPointer cpBodyGetUserData(const cpBody *body);
/// Set the user data pointer assigned to the body.
void cpBodySetUserData(cpBody *body, cpDataPointer userData);
/// Set the callback used to update a body's velocity.
void cpBodySetVelocityUpdateFunc(cpBody *body, cpBodyVelocityFunc velocityFunc);
/// Set the callback used to update a body's position.
/// NOTE: It's not generally recommended to override this.
void cpBodySetPositionUpdateFunc(cpBody *body, cpBodyPositionFunc positionFunc);
/// Default velocity integration function..
void cpBodyUpdateVelocity(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt);
/// Default position integration function.
void cpBodyUpdatePosition(cpBody *body, cpFloat dt);
/// Convert body relative/local coordinates to absolute/world coordinates.
cpVect cpBodyLocalToWorld(const cpBody *body, const cpVect point);
/// Convert body absolute/world coordinates to relative/local coordinates.
cpVect cpBodyWorldToLocal(const cpBody *body, const cpVect point);
/// Apply a force to a body. Both the force and point are expressed in world coordinates.
void cpBodyApplyForceAtWorldPoint(cpBody *body, cpVect force, cpVect point);
/// Apply a force to a body. Both the force and point are expressed in body local coordinates.
void cpBodyApplyForceAtLocalPoint(cpBody *body, cpVect force, cpVect point);
/// Apply an impulse to a body. Both the impulse and point are expressed in world coordinates.
void cpBodyApplyImpulseAtWorldPoint(cpBody *body, cpVect impulse, cpVect point);
/// Apply an impulse to a body. Both the impulse and point are expressed in body local coordinates.
void cpBodyApplyImpulseAtLocalPoint(cpBody *body, cpVect impulse, cpVect point);
/// Get the velocity on a body (in world units) at a point on the body in world coordinates.
cpVect cpBodyGetVelocityAtWorldPoint(const cpBody *body, cpVect point);
/// Get the velocity on a body (in world units) at a point on the body in local coordinates.
cpVect cpBodyGetVelocityAtLocalPoint(const cpBody *body, cpVect point);
/// Get the amount of kinetic energy contained by the body.
cpFloat cpBodyKineticEnergy(const cpBody *body);
/// Body/shape iterator callback function type.
typedef void (*cpBodyShapeIteratorFunc)(cpBody *body, cpShape *shape, void *data);
/// Call @c func once for each shape attached to @c body and added to the space.
void cpBodyEachShape(cpBody *body, cpBodyShapeIteratorFunc func, void *data);
/// Body/constraint iterator callback function type.
typedef void (*cpBodyConstraintIteratorFunc)(cpBody *body, cpConstraint *constraint, void *data);
/// Call @c func once for each constraint attached to @c body and added to the space.
void cpBodyEachConstraint(cpBody *body, cpBodyConstraintIteratorFunc func, void *data);
/// Body/arbiter iterator callback function type.
typedef void (*cpBodyArbiterIteratorFunc)(cpBody *body, cpArbiter *arbiter, void *data);
/// Call @c func once for each arbiter that is currently active on the body.
void cpBodyEachArbiter(cpBody *body, cpBodyArbiterIteratorFunc func, void *data);
///@}

View File

@ -1,95 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 cpConstraint cpConstraint
/// @{
/// Callback function type that gets called before solving a joint.
typedef void (*cpConstraintPreSolveFunc)(cpConstraint *constraint, cpSpace *space);
/// Callback function type that gets called after solving a joint.
typedef void (*cpConstraintPostSolveFunc)(cpConstraint *constraint, cpSpace *space);
/// Destroy a constraint.
void cpConstraintDestroy(cpConstraint *constraint);
/// Destroy and free a constraint.
void cpConstraintFree(cpConstraint *constraint);
/// Get the cpSpace this constraint is added to.
cpSpace* cpConstraintGetSpace(const cpConstraint *constraint);
/// Get the first body the constraint is attached to.
cpBody* cpConstraintGetBodyA(const cpConstraint *constraint);
/// Get the second body the constraint is attached to.
cpBody* cpConstraintGetBodyB(const cpConstraint *constraint);
/// Get the maximum force that this constraint is allowed to use.
cpFloat cpConstraintGetMaxForce(const cpConstraint *constraint);
/// Set the maximum force that this constraint is allowed to use. (defaults to INFINITY)
void cpConstraintSetMaxForce(cpConstraint *constraint, cpFloat maxForce);
/// Get rate at which joint error is corrected.
cpFloat cpConstraintGetErrorBias(const cpConstraint *constraint);
/// Set rate at which joint error is corrected.
/// Defaults to pow(1.0 - 0.1, 60.0) meaning that it will
/// correct 10% of the error every 1/60th of a second.
void cpConstraintSetErrorBias(cpConstraint *constraint, cpFloat errorBias);
/// Get the maximum rate at which joint error is corrected.
cpFloat cpConstraintGetMaxBias(const cpConstraint *constraint);
/// Set the maximum rate at which joint error is corrected. (defaults to INFINITY)
void cpConstraintSetMaxBias(cpConstraint *constraint, cpFloat maxBias);
/// Get if the two bodies connected by the constraint are allowed to collide or not.
cpBool cpConstraintGetCollideBodies(const cpConstraint *constraint);
/// Set if the two bodies connected by the constraint are allowed to collide or not. (defaults to cpFalse)
void cpConstraintSetCollideBodies(cpConstraint *constraint, cpBool collideBodies);
/// Get the pre-solve function that is called before the solver runs.
cpConstraintPreSolveFunc cpConstraintGetPreSolveFunc(const cpConstraint *constraint);
/// Set the pre-solve function that is called before the solver runs.
void cpConstraintSetPreSolveFunc(cpConstraint *constraint, cpConstraintPreSolveFunc preSolveFunc);
/// Get the post-solve function that is called before the solver runs.
cpConstraintPostSolveFunc cpConstraintGetPostSolveFunc(const cpConstraint *constraint);
/// Set the post-solve function that is called before the solver runs.
void cpConstraintSetPostSolveFunc(cpConstraint *constraint, cpConstraintPostSolveFunc postSolveFunc);
/// Get the user definable data pointer for this constraint
cpDataPointer cpConstraintGetUserData(const cpConstraint *constraint);
/// Set the user definable data pointer for this constraint
void cpConstraintSetUserData(cpConstraint *constraint, cpDataPointer userData);
/// Get the last impulse applied by this constraint.
cpFloat cpConstraintGetImpulse(cpConstraint *constraint);
#include "cpPinJoint.h"
#include "cpSlideJoint.h"
#include "cpPivotJoint.h"
#include "cpGrooveJoint.h"
#include "cpDampedSpring.h"
#include "cpDampedRotarySpring.h"
#include "cpRotaryLimitJoint.h"
#include "cpRatchetJoint.h"
#include "cpGearJoint.h"
#include "cpSimpleMotor.h"
///@}

View File

@ -1,58 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 cpDampedRotarySpring cpDampedRotarySpring
/// @{
/// Check if a constraint is a damped rotary springs.
cpBool cpConstraintIsDampedRotarySpring(const cpConstraint *constraint);
/// Function type used for damped rotary spring force callbacks.
typedef cpFloat (*cpDampedRotarySpringTorqueFunc)(struct cpConstraint *spring, cpFloat relativeAngle);
/// Allocate a damped rotary spring.
cpDampedRotarySpring* cpDampedRotarySpringAlloc(void);
/// Initialize a damped rotary spring.
cpDampedRotarySpring* cpDampedRotarySpringInit(cpDampedRotarySpring *joint, cpBody *a, cpBody *b, cpFloat restAngle, cpFloat stiffness, cpFloat damping);
/// Allocate and initialize a damped rotary spring.
cpConstraint* cpDampedRotarySpringNew(cpBody *a, cpBody *b, cpFloat restAngle, cpFloat stiffness, cpFloat damping);
/// Get the rest length of the spring.
cpFloat cpDampedRotarySpringGetRestAngle(const cpConstraint *constraint);
/// Set the rest length of the spring.
void cpDampedRotarySpringSetRestAngle(cpConstraint *constraint, cpFloat restAngle);
/// Get the stiffness of the spring in force/distance.
cpFloat cpDampedRotarySpringGetStiffness(const cpConstraint *constraint);
/// Set the stiffness of the spring in force/distance.
void cpDampedRotarySpringSetStiffness(cpConstraint *constraint, cpFloat stiffness);
/// Get the damping of the spring.
cpFloat cpDampedRotarySpringGetDamping(const cpConstraint *constraint);
/// Set the damping of the spring.
void cpDampedRotarySpringSetDamping(cpConstraint *constraint, cpFloat damping);
/// Get the damping of the spring.
cpDampedRotarySpringTorqueFunc cpDampedRotarySpringGetSpringTorqueFunc(const cpConstraint *constraint);
/// Set the damping of the spring.
void cpDampedRotarySpringSetSpringTorqueFunc(cpConstraint *constraint, cpDampedRotarySpringTorqueFunc springTorqueFunc);
/// @}

View File

@ -1,68 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 cpDampedSpring cpDampedSpring
/// @{
/// Check if a constraint is a slide joint.
cpBool cpConstraintIsDampedSpring(const cpConstraint *constraint);
/// Function type used for damped spring force callbacks.
typedef cpFloat (*cpDampedSpringForceFunc)(cpConstraint *spring, cpFloat dist);
/// Allocate a damped spring.
cpDampedSpring* cpDampedSpringAlloc(void);
/// Initialize a damped spring.
cpDampedSpring* cpDampedSpringInit(cpDampedSpring *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat restLength, cpFloat stiffness, cpFloat damping);
/// Allocate and initialize a damped spring.
cpConstraint* cpDampedSpringNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat restLength, cpFloat stiffness, cpFloat damping);
/// Get the location of the first anchor relative to the first body.
cpVect cpDampedSpringGetAnchorA(const cpConstraint *constraint);
/// Set the location of the first anchor relative to the first body.
void cpDampedSpringSetAnchorA(cpConstraint *constraint, cpVect anchorA);
/// Get the location of the second anchor relative to the second body.
cpVect cpDampedSpringGetAnchorB(const cpConstraint *constraint);
/// Set the location of the second anchor relative to the second body.
void cpDampedSpringSetAnchorB(cpConstraint *constraint, cpVect anchorB);
/// Get the rest length of the spring.
cpFloat cpDampedSpringGetRestLength(const cpConstraint *constraint);
/// Set the rest length of the spring.
void cpDampedSpringSetRestLength(cpConstraint *constraint, cpFloat restLength);
/// Get the stiffness of the spring in force/distance.
cpFloat cpDampedSpringGetStiffness(const cpConstraint *constraint);
/// Set the stiffness of the spring in force/distance.
void cpDampedSpringSetStiffness(cpConstraint *constraint, cpFloat stiffness);
/// Get the damping of the spring.
cpFloat cpDampedSpringGetDamping(const cpConstraint *constraint);
/// Set the damping of the spring.
void cpDampedSpringSetDamping(cpConstraint *constraint, cpFloat damping);
/// Get the damping of the spring.
cpDampedSpringForceFunc cpDampedSpringGetSpringForceFunc(const cpConstraint *constraint);
/// Set the damping of the spring.
void cpDampedSpringSetSpringForceFunc(cpConstraint *constraint, cpDampedSpringForceFunc springForceFunc);
/// @}

View File

@ -1,45 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 cpGearJoint cpGearJoint
/// @{
/// Check if a constraint is a damped rotary springs.
cpBool cpConstraintIsGearJoint(const cpConstraint *constraint);
/// Allocate a gear joint.
cpGearJoint* cpGearJointAlloc(void);
/// Initialize a gear joint.
cpGearJoint* cpGearJointInit(cpGearJoint *joint, cpBody *a, cpBody *b, cpFloat phase, cpFloat ratio);
/// Allocate and initialize a gear joint.
cpConstraint* cpGearJointNew(cpBody *a, cpBody *b, cpFloat phase, cpFloat ratio);
/// Get the phase offset of the gears.
cpFloat cpGearJointGetPhase(const cpConstraint *constraint);
/// Set the phase offset of the gears.
void cpGearJointSetPhase(cpConstraint *constraint, cpFloat phase);
/// Get the angular distance of each ratchet.
cpFloat cpGearJointGetRatio(const cpConstraint *constraint);
/// Set the ratio of a gear joint.
void cpGearJointSetRatio(cpConstraint *constraint, cpFloat ratio);
/// @}

View File

@ -1,50 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 cpGrooveJoint cpGrooveJoint
/// @{
/// Check if a constraint is a slide joint.
cpBool cpConstraintIsGrooveJoint(const cpConstraint *constraint);
/// Allocate a groove joint.
cpGrooveJoint* cpGrooveJointAlloc(void);
/// Initialize a groove joint.
cpGrooveJoint* cpGrooveJointInit(cpGrooveJoint *joint, cpBody *a, cpBody *b, cpVect groove_a, cpVect groove_b, cpVect anchorB);
/// Allocate and initialize a groove joint.
cpConstraint* cpGrooveJointNew(cpBody *a, cpBody *b, cpVect groove_a, cpVect groove_b, cpVect anchorB);
/// Get the first endpoint of the groove relative to the first body.
cpVect cpGrooveJointGetGrooveA(const cpConstraint *constraint);
/// Set the first endpoint of the groove relative to the first body.
void cpGrooveJointSetGrooveA(cpConstraint *constraint, cpVect grooveA);
/// Get the first endpoint of the groove relative to the first body.
cpVect cpGrooveJointGetGrooveB(const cpConstraint *constraint);
/// Set the first endpoint of the groove relative to the first body.
void cpGrooveJointSetGrooveB(cpConstraint *constraint, cpVect grooveB);
/// Get the location of the second anchor relative to the second body.
cpVect cpGrooveJointGetAnchorB(const cpConstraint *constraint);
/// Set the location of the second anchor relative to the second body.
void cpGrooveJointSetAnchorB(cpConstraint *constraint, cpVect anchorB);
/// @}

View File

@ -1,50 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 cpPinJoint cpPinJoint
/// @{
/// Check if a constraint is a pin joint.
cpBool cpConstraintIsPinJoint(const cpConstraint *constraint);
/// Allocate a pin joint.
cpPinJoint* cpPinJointAlloc(void);
/// Initialize a pin joint.
cpPinJoint* cpPinJointInit(cpPinJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB);
/// Allocate and initialize a pin joint.
cpConstraint* cpPinJointNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB);
/// Get the location of the first anchor relative to the first body.
cpVect cpPinJointGetAnchorA(const cpConstraint *constraint);
/// Set the location of the first anchor relative to the first body.
void cpPinJointSetAnchorA(cpConstraint *constraint, cpVect anchorA);
/// Get the location of the second anchor relative to the second body.
cpVect cpPinJointGetAnchorB(const cpConstraint *constraint);
/// Set the location of the second anchor relative to the second body.
void cpPinJointSetAnchorB(cpConstraint *constraint, cpVect anchorB);
/// Get the distance the joint will maintain between the two anchors.
cpFloat cpPinJointGetDist(const cpConstraint *constraint);
/// Set the distance the joint will maintain between the two anchors.
void cpPinJointSetDist(cpConstraint *constraint, cpFloat dist);
///@}

View File

@ -1,47 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 cpPivotJoint cpPivotJoint
/// @{
/// Check if a constraint is a slide joint.
cpBool cpConstraintIsPivotJoint(const cpConstraint *constraint);
/// Allocate a pivot joint
cpPivotJoint* cpPivotJointAlloc(void);
/// Initialize a pivot joint.
cpPivotJoint* cpPivotJointInit(cpPivotJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB);
/// Allocate and initialize a pivot joint.
cpConstraint* cpPivotJointNew(cpBody *a, cpBody *b, cpVect pivot);
/// Allocate and initialize a pivot joint with specific anchors.
cpConstraint* cpPivotJointNew2(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB);
/// Get the location of the first anchor relative to the first body.
cpVect cpPivotJointGetAnchorA(const cpConstraint *constraint);
/// Set the location of the first anchor relative to the first body.
void cpPivotJointSetAnchorA(cpConstraint *constraint, cpVect anchorA);
/// Get the location of the second anchor relative to the second body.
cpVect cpPivotJointGetAnchorB(const cpConstraint *constraint);
/// Set the location of the second anchor relative to the second body.
void cpPivotJointSetAnchorB(cpConstraint *constraint, cpVect anchorB);
/// @}

View File

@ -1,52 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 cpPolyShape cpPolyShape
/// @{
/// Allocate a polygon shape.
cpPolyShape* cpPolyShapeAlloc(void);
/// Initialize a polygon shape with rounded corners.
/// A convex hull will be created from the vertexes.
cpPolyShape* cpPolyShapeInit(cpPolyShape *poly, cpBody *body, int count, const cpVect *verts, cpTransform transform, cpFloat radius);
cpPolyShape* cpPolyShapeInitRaw(cpPolyShape *poly, cpBody *body, int count, const cpVect *verts, cpFloat radius);
/// Allocate and initialize a polygon shape with rounded corners.
/// A convex hull will be created from the vertexes.
cpShape* cpPolyShapeNew(cpBody *body, int count, const cpVect *verts, cpTransform transform, cpFloat radius);
cpShape* cpPolyShapeNewRaw(cpBody *body, int count, const cpVect *verts, cpFloat radius);
/// Initialize a box shaped polygon shape with rounded corners.
cpPolyShape* cpBoxShapeInit(cpPolyShape *poly, cpBody *body, cpFloat width, cpFloat height, cpFloat radius);
/// Initialize an offset box shaped polygon shape with rounded corners.
cpPolyShape* cpBoxShapeInit2(cpPolyShape *poly, cpBody *body, cpBB box, cpFloat radius);
/// Allocate and initialize a box shaped polygon shape.
cpShape* cpBoxShapeNew(cpBody *body, cpFloat width, cpFloat height, cpFloat radius);
/// Allocate and initialize an offset box shaped polygon shape.
cpShape* cpBoxShapeNew2(cpBody *body, cpBB box, cpFloat radius);
/// Get the number of verts in a polygon shape.
int cpPolyShapeGetCount(const cpShape *shape);
/// Get the @c ith vertex of a polygon shape.
cpVect cpPolyShapeGetVert(const cpShape *shape, int index);
/// Get the radius of a polygon shape.
cpFloat cpPolyShapeGetRadius(const cpShape *shape);
/// @}

View File

@ -1,50 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 cpRatchetJoint cpRatchetJoint
/// @{
/// Check if a constraint is a damped rotary springs.
cpBool cpConstraintIsRatchetJoint(const cpConstraint *constraint);
/// Allocate a ratchet joint.
cpRatchetJoint* cpRatchetJointAlloc(void);
/// Initialize a ratched joint.
cpRatchetJoint* cpRatchetJointInit(cpRatchetJoint *joint, cpBody *a, cpBody *b, cpFloat phase, cpFloat ratchet);
/// Allocate and initialize a ratchet joint.
cpConstraint* cpRatchetJointNew(cpBody *a, cpBody *b, cpFloat phase, cpFloat ratchet);
/// Get the angle of the current ratchet tooth.
cpFloat cpRatchetJointGetAngle(const cpConstraint *constraint);
/// Set the angle of the current ratchet tooth.
void cpRatchetJointSetAngle(cpConstraint *constraint, cpFloat angle);
/// Get the phase offset of the ratchet.
cpFloat cpRatchetJointGetPhase(const cpConstraint *constraint);
/// Get the phase offset of the ratchet.
void cpRatchetJointSetPhase(cpConstraint *constraint, cpFloat phase);
/// Get the angular distance of each ratchet.
cpFloat cpRatchetJointGetRatchet(const cpConstraint *constraint);
/// Set the angular distance of each ratchet.
void cpRatchetJointSetRatchet(cpConstraint *constraint, cpFloat ratchet);
/// @}

View File

@ -1,45 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 cpRotaryLimitJoint cpRotaryLimitJoint
/// @{
/// Check if a constraint is a damped rotary springs.
cpBool cpConstraintIsRotaryLimitJoint(const cpConstraint *constraint);
/// Allocate a damped rotary limit joint.
cpRotaryLimitJoint* cpRotaryLimitJointAlloc(void);
/// Initialize a damped rotary limit joint.
cpRotaryLimitJoint* cpRotaryLimitJointInit(cpRotaryLimitJoint *joint, cpBody *a, cpBody *b, cpFloat min, cpFloat max);
/// Allocate and initialize a damped rotary limit joint.
cpConstraint* cpRotaryLimitJointNew(cpBody *a, cpBody *b, cpFloat min, cpFloat max);
/// Get the minimum distance the joint will maintain between the two anchors.
cpFloat cpRotaryLimitJointGetMin(const cpConstraint *constraint);
/// Set the minimum distance the joint will maintain between the two anchors.
void cpRotaryLimitJointSetMin(cpConstraint *constraint, cpFloat min);
/// Get the maximum distance the joint will maintain between the two anchors.
cpFloat cpRotaryLimitJointGetMax(const cpConstraint *constraint);
/// Set the maximum distance the joint will maintain between the two anchors.
void cpRotaryLimitJointSetMax(cpConstraint *constraint, cpFloat max);
/// @}

View File

@ -1,189 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 cpShape cpShape
/// The cpShape struct defines the shape of a rigid body.
/// @{
/// Nearest point query info struct.
typedef struct cpPointQueryInfo {
/// The nearest shape, NULL if no shape was within range.
const cpShape *shape;
/// The closest point on the shape's surface. (in world space coordinates)
cpVect point;
/// The distance to the point. The distance is negative if the point is inside the shape.
cpFloat distance;
/// The gradient of the signed distance function.
/// The same as info.p/info.d, but accurate even for very small values of info.d.
cpVect gradient;
} cpPointQueryInfo;
/// Segment query info struct.
typedef struct cpSegmentQueryInfo {
/// The shape that was hit, NULL if no collision occured.
const cpShape *shape;
/// The point of impact.
cpVect point;
/// The normal of the surface hit.
cpVect normal;
/// The normalized distance along the query segment in the range [0, 1].
cpFloat alpha;
} cpSegmentQueryInfo;
typedef struct cpShapeFilter {
cpGroup group;
cpBitmask categories;
cpBitmask mask;
} cpShapeFilter;
static const cpShapeFilter CP_SHAPE_FILTER_ALL = {CP_NO_GROUP, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES};
static const cpShapeFilter CP_SHAPE_FILTER_NONE = {CP_NO_GROUP, ~CP_ALL_CATEGORIES, ~CP_ALL_CATEGORIES};
static inline cpShapeFilter
cpShapeFilterNew(cpGroup group, cpBitmask categories, cpBitmask mask)
{
cpShapeFilter filter = {group, categories, mask};
return filter;
}
/// Destroy a shape.
void cpShapeDestroy(cpShape *shape);
/// Destroy and Free a shape.
void cpShapeFree(cpShape *shape);
/// Update, cache and return the bounding box of a shape based on the body it's attached to.
cpBB cpShapeCacheBB(cpShape *shape);
/// Update, cache and return the bounding box of a shape with an explicit transformation.
cpBB cpShapeUpdate(cpShape *shape, cpTransform transform);
/// Perform a nearest point query. It finds the closest point on the surface of shape to a specific point.
/// The value returned is the distance between the points. A negative distance means the point is inside the shape.
cpFloat cpShapePointQuery(const cpShape *shape, cpVect p, cpPointQueryInfo *out);
/// Perform a segment query against a shape. @c info must be a pointer to a valid cpSegmentQueryInfo structure.
cpBool cpShapeSegmentQuery(const cpShape *shape, cpVect a, cpVect b, cpFloat radius, cpSegmentQueryInfo *info);
/// Return contact information about two shapes.
cpContactPointSet cpShapesCollide(const cpShape *a, const cpShape *b);
/// The cpSpace this body is added to.
cpSpace* cpShapeGetSpace(const cpShape *shape);
/// The cpBody this shape is connected to.
cpBody* cpShapeGetBody(const cpShape *shape);
/// Set the cpBody this shape is connected to.
/// Can only be used if the shape is not currently added to a space.
void cpShapeSetBody(cpShape *shape, cpBody *body);
/// Get the mass of the shape if you are having Chipmunk calculate mass properties for you.
cpFloat cpShapeGetMass(cpShape *shape);
/// Set the mass of this shape to have Chipmunk calculate mass properties for you.
void cpShapeSetMass(cpShape *shape, cpFloat mass);
/// Get the density of the shape if you are having Chipmunk calculate mass properties for you.
cpFloat cpShapeGetDensity(cpShape *shape);
/// Set the density of this shape to have Chipmunk calculate mass properties for you.
void cpShapeSetDensity(cpShape *shape, cpFloat density);
/// Get the calculated moment of inertia for this shape.
cpFloat cpShapeGetMoment(cpShape *shape);
/// Get the calculated area of this shape.
cpFloat cpShapeGetArea(cpShape *shape);
/// Get the centroid of this shape.
cpVect cpShapeGetCenterOfGravity(cpShape *shape);
/// Get the bounding box that contains the shape given it's current position and angle.
cpBB cpShapeGetBB(const cpShape *shape);
/// Get if the shape is set to be a sensor or not.
cpBool cpShapeGetSensor(const cpShape *shape);
/// Set if the shape is a sensor or not.
void cpShapeSetSensor(cpShape *shape, cpBool sensor);
/// Get the elasticity of this shape.
cpFloat cpShapeGetElasticity(const cpShape *shape);
/// Set the elasticity of this shape.
void cpShapeSetElasticity(cpShape *shape, cpFloat elasticity);
/// Get the friction of this shape.
cpFloat cpShapeGetFriction(const cpShape *shape);
/// Set the friction of this shape.
void cpShapeSetFriction(cpShape *shape, cpFloat friction);
/// Get the surface velocity of this shape.
cpVect cpShapeGetSurfaceVelocity(const cpShape *shape);
/// Set the surface velocity of this shape.
void cpShapeSetSurfaceVelocity(cpShape *shape, cpVect surfaceVelocity);
/// Get the user definable data pointer of this shape.
cpDataPointer cpShapeGetUserData(const cpShape *shape);
/// Set the user definable data pointer of this shape.
void cpShapeSetUserData(cpShape *shape, cpDataPointer userData);
/// Set the collision type of this shape.
cpCollisionType cpShapeGetCollisionType(const cpShape *shape);
/// Get the collision type of this shape.
void cpShapeSetCollisionType(cpShape *shape, cpCollisionType collisionType);
/// Get the collision filtering parameters of this shape.
cpShapeFilter cpShapeGetFilter(const cpShape *shape);
/// Set the collision filtering parameters of this shape.
void cpShapeSetFilter(cpShape *shape, cpShapeFilter filter);
/// @}
/// @defgroup cpCircleShape cpCircleShape
/// Allocate a circle shape.
cpCircleShape* cpCircleShapeAlloc(void);
/// Initialize a circle shape.
cpCircleShape* cpCircleShapeInit(cpCircleShape *circle, cpBody *body, cpFloat radius, cpVect offset);
/// Allocate and initialize a circle shape.
cpShape* cpCircleShapeNew(cpBody *body, cpFloat radius, cpVect offset);
/// Get the offset of a circle shape.
cpVect cpCircleShapeGetOffset(const cpShape *shape);
/// Get the radius of a circle shape.
cpFloat cpCircleShapeGetRadius(const cpShape *shape);
/// @}
/// @defgroup cpSegmentShape cpSegmentShape
/// Allocate a segment shape.
cpSegmentShape* cpSegmentShapeAlloc(void);
/// Initialize a segment shape.
cpSegmentShape* cpSegmentShapeInit(cpSegmentShape *seg, cpBody *body, cpVect a, cpVect b, cpFloat radius);
/// Allocate and initialize a segment shape.
cpShape* cpSegmentShapeNew(cpBody *body, cpVect a, cpVect b, cpFloat radius);
/// Let Chipmunk know about the geometry of adjacent segments to avoid colliding with endcaps.
void cpSegmentShapeSetNeighbors(cpShape *shape, cpVect prev, cpVect next);
/// Get the first endpoint of a segment shape.
cpVect cpSegmentShapeGetA(const cpShape *shape);
/// Get the second endpoint of a segment shape.
cpVect cpSegmentShapeGetB(const cpShape *shape);
/// Get the normal of a segment shape.
cpVect cpSegmentShapeGetNormal(const cpShape *shape);
/// Get the first endpoint of a segment shape.
cpFloat cpSegmentShapeGetRadius(const cpShape *shape);
/// @}

View File

@ -1,43 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 cpSimpleMotor cpSimpleMotor
/// @{
/// Opaque struct type for damped rotary springs.
typedef struct cpSimpleMotor cpSimpleMotor;
/// Check if a constraint is a damped rotary springs.
cpBool cpConstraintIsSimpleMotor(const cpConstraint *constraint);
/// Allocate a simple motor.
cpSimpleMotor* cpSimpleMotorAlloc(void);
/// initialize a simple motor.
cpSimpleMotor* cpSimpleMotorInit(cpSimpleMotor *joint, cpBody *a, cpBody *b, cpFloat rate);
/// Allocate and initialize a simple motor.
cpConstraint* cpSimpleMotorNew(cpBody *a, cpBody *b, cpFloat rate);
/// Get the rate of the motor.
cpFloat cpSimpleMotorGetRate(const cpConstraint *constraint);
/// Set the rate of the motor.
void cpSimpleMotorSetRate(cpConstraint *constraint, cpFloat rate);
/// @}

View File

@ -1,55 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 cpSlideJoint cpSlideJoint
/// @{
/// Check if a constraint is a slide joint.
cpBool cpConstraintIsSlideJoint(const cpConstraint *constraint);
/// Allocate a slide joint.
cpSlideJoint* cpSlideJointAlloc(void);
/// Initialize a slide joint.
cpSlideJoint* cpSlideJointInit(cpSlideJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat min, cpFloat max);
/// Allocate and initialize a slide joint.
cpConstraint* cpSlideJointNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat min, cpFloat max);
/// Get the location of the first anchor relative to the first body.
cpVect cpSlideJointGetAnchorA(const cpConstraint *constraint);
/// Set the location of the first anchor relative to the first body.
void cpSlideJointSetAnchorA(cpConstraint *constraint, cpVect anchorA);
/// Get the location of the second anchor relative to the second body.
cpVect cpSlideJointGetAnchorB(const cpConstraint *constraint);
/// Set the location of the second anchor relative to the second body.
void cpSlideJointSetAnchorB(cpConstraint *constraint, cpVect anchorB);
/// Get the minimum distance the joint will maintain between the two anchors.
cpFloat cpSlideJointGetMin(const cpConstraint *constraint);
/// Set the minimum distance the joint will maintain between the two anchors.
void cpSlideJointSetMin(cpConstraint *constraint, cpFloat min);
/// Get the maximum distance the joint will maintain between the two anchors.
cpFloat cpSlideJointGetMax(const cpConstraint *constraint);
/// Set the maximum distance the joint will maintain between the two anchors.
void cpSlideJointSetMax(cpConstraint *constraint, cpFloat max);
/// @}

View File

@ -1,284 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 cpSpace cpSpace
/// @{
//MARK: Definitions
typedef struct cpContactBufferHeader cpContactBufferHeader;
typedef void (*cpSpaceArbiterApplyImpulseFunc)(cpArbiter *arb);
/// Collision begin event function callback type.
/// Returning false from a begin callback causes the collision to be ignored until
/// the the separate callback is called when the objects stop colliding.
typedef cpBool (*cpCollisionBeginFunc)(cpArbiter *arb, cpSpace *space, cpDataPointer userData);
/// Collision pre-solve event function callback type.
/// Returning false from a pre-step callback causes the collision to be ignored until the next step.
typedef cpBool (*cpCollisionPreSolveFunc)(cpArbiter *arb, cpSpace *space, cpDataPointer userData);
/// Collision post-solve event function callback type.
typedef void (*cpCollisionPostSolveFunc)(cpArbiter *arb, cpSpace *space, cpDataPointer userData);
/// Collision separate event function callback type.
typedef void (*cpCollisionSeparateFunc)(cpArbiter *arb, cpSpace *space, cpDataPointer userData);
struct cpCollisionHandler {
const cpCollisionType typeA, typeB;
cpCollisionBeginFunc beginFunc;
cpCollisionPreSolveFunc preSolveFunc;
cpCollisionPostSolveFunc postSolveFunc;
cpCollisionSeparateFunc separateFunc;
cpDataPointer userData;
};
// TODO: Make timestep a parameter?
//MARK: Memory and Initialization
/// Allocate a cpSpace.
cpSpace* cpSpaceAlloc(void);
/// Initialize a cpSpace.
cpSpace* cpSpaceInit(cpSpace *space);
/// Allocate and initialize a cpSpace.
cpSpace* cpSpaceNew(void);
/// Destroy a cpSpace.
void cpSpaceDestroy(cpSpace *space);
/// Destroy and free a cpSpace.
void cpSpaceFree(cpSpace *space);
//MARK: Properties
/// Number of iterations to use in the impulse solver to solve contacts and other constraints.
int cpSpaceGetIterations(const cpSpace *space);
void cpSpaceSetIterations(cpSpace *space, int iterations);
/// Gravity to pass to rigid bodies when integrating velocity.
cpVect cpSpaceGetGravity(const cpSpace *space);
void cpSpaceSetGravity(cpSpace *space, cpVect gravity);
/// 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.
cpFloat cpSpaceGetDamping(const cpSpace *space);
void cpSpaceSetDamping(cpSpace *space, cpFloat damping);
/// 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.
cpFloat cpSpaceGetIdleSpeedThreshold(const cpSpace *space);
void cpSpaceSetIdleSpeedThreshold(cpSpace *space, cpFloat idleSpeedThreshold);
/// 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.
cpFloat cpSpaceGetSleepTimeThreshold(const cpSpace *space);
void cpSpaceSetSleepTimeThreshold(cpSpace *space, cpFloat sleepTimeThreshold);
/// 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.
cpFloat cpSpaceGetCollisionSlop(const cpSpace *space);
void cpSpaceSetCollisionSlop(cpSpace *space, cpFloat collisionSlop);
/// 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.
cpFloat cpSpaceGetCollisionBias(const cpSpace *space);
void cpSpaceSetCollisionBias(cpSpace *space, cpFloat collisionBias);
/// Number of frames that contact information should persist.
/// Defaults to 3. There is probably never a reason to change this value.
cpTimestamp cpSpaceGetCollisionPersistence(const cpSpace *space);
void cpSpaceSetCollisionPersistence(cpSpace *space, cpTimestamp collisionPersistence);
/// User definable data pointer.
/// Generally this points to your game's controller or game state
/// class so you can access it when given a cpSpace reference in a callback.
cpDataPointer cpSpaceGetUserData(const cpSpace *space);
void cpSpaceSetUserData(cpSpace *space, cpDataPointer userData);
/// The Space provided static body for a given cpSpace.
/// This is merely provided for convenience and you are not required to use it.
cpBody* cpSpaceGetStaticBody(const cpSpace *space);
/// Returns the current (or most recent) time step used with the given space.
/// Useful from callbacks if your time step is not a compile-time global.
cpFloat cpSpaceGetCurrentTimeStep(const cpSpace *space);
/// returns true from inside a callback when objects cannot be added/removed.
cpBool cpSpaceIsLocked(cpSpace *space);
//MARK: Collision Handlers
cpCollisionHandler *cpSpaceAddDefaultCollisionHandler(cpSpace *space);
cpCollisionHandler *cpSpaceAddCollisionHandler(cpSpace *space, cpCollisionType a, cpCollisionType b);
cpCollisionHandler *cpSpaceAddWildcardHandler(cpSpace *space, cpCollisionType type);
//MARK: Add/Remove objects
/// Add a collision shape to the simulation.
/// If the shape is attached to a static body, it will be added as a static shape.
cpShape* cpSpaceAddShape(cpSpace *space, cpShape *shape);
/// Add a rigid body to the simulation.
cpBody* cpSpaceAddBody(cpSpace *space, cpBody *body);
/// Add a constraint to the simulation.
cpConstraint* cpSpaceAddConstraint(cpSpace *space, cpConstraint *constraint);
/// Remove a collision shape from the simulation.
void cpSpaceRemoveShape(cpSpace *space, cpShape *shape);
/// Remove a rigid body from the simulation.
void cpSpaceRemoveBody(cpSpace *space, cpBody *body);
/// Remove a constraint from the simulation.
void cpSpaceRemoveConstraint(cpSpace *space, cpConstraint *constraint);
/// Test if a collision shape has been added to the space.
cpBool cpSpaceContainsShape(cpSpace *space, cpShape *shape);
/// Test if a rigid body has been added to the space.
cpBool cpSpaceContainsBody(cpSpace *space, cpBody *body);
/// Test if a constraint has been added to the space.
cpBool cpSpaceContainsConstraint(cpSpace *space, cpConstraint *constraint);
//MARK: Post-Step Callbacks
/// Post Step callback function type.
typedef void (*cpPostStepFunc)(cpSpace *space, void *key, void *data);
/// Schedule a post-step callback to be called when cpSpaceStep() finishes.
/// You can only register one callback per unique value for @c key.
/// Returns true only if @c key has never been scheduled before.
/// It's possible to pass @c NULL for @c func if you only want to mark @c key as being used.
cpBool cpSpaceAddPostStepCallback(cpSpace *space, cpPostStepFunc func, void *key, void *data);
//MARK: Queries
// TODO: Queries and iterators should take a cpSpace parametery.
// TODO: They should also be abortable.
/// Nearest point query callback function type.
typedef void (*cpSpacePointQueryFunc)(cpShape *shape, cpVect point, cpFloat distance, cpVect gradient, void *data);
/// Query the space at a point and call @c func for each shape found.
void cpSpacePointQuery(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpSpacePointQueryFunc func, void *data);
/// Query the space at a point and return the nearest shape found. Returns NULL if no shapes were found.
cpShape *cpSpacePointQueryNearest(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpPointQueryInfo *out);
/// Segment query callback function type.
typedef void (*cpSpaceSegmentQueryFunc)(cpShape *shape, cpVect point, cpVect normal, cpFloat alpha, void *data);
/// Perform a directed line segment query (like a raycast) against the space calling @c func for each shape intersected.
void cpSpaceSegmentQuery(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSpaceSegmentQueryFunc func, void *data);
/// Perform a directed line segment query (like a raycast) against the space and return the first shape hit. Returns NULL if no shapes were hit.
cpShape *cpSpaceSegmentQueryFirst(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSegmentQueryInfo *out);
/// Rectangle Query callback function type.
typedef void (*cpSpaceBBQueryFunc)(cpShape *shape, void *data);
/// Perform a fast rectangle query on the space calling @c func for each shape found.
/// Only the shape's bounding boxes are checked for overlap, not their full shape.
void cpSpaceBBQuery(cpSpace *space, cpBB bb, cpShapeFilter filter, cpSpaceBBQueryFunc func, void *data);
/// Shape query callback function type.
typedef void (*cpSpaceShapeQueryFunc)(cpShape *shape, cpContactPointSet *points, void *data);
/// Query a space for any shapes overlapping the given shape and call @c func for each shape found.
cpBool cpSpaceShapeQuery(cpSpace *space, cpShape *shape, cpSpaceShapeQueryFunc func, void *data);
//MARK: Iteration
/// Space/body iterator callback function type.
typedef void (*cpSpaceBodyIteratorFunc)(cpBody *body, void *data);
/// Call @c func for each body in the space.
void cpSpaceEachBody(cpSpace *space, cpSpaceBodyIteratorFunc func, void *data);
/// Space/body iterator callback function type.
typedef void (*cpSpaceShapeIteratorFunc)(cpShape *shape, void *data);
/// Call @c func for each shape in the space.
void cpSpaceEachShape(cpSpace *space, cpSpaceShapeIteratorFunc func, void *data);
/// Space/constraint iterator callback function type.
typedef void (*cpSpaceConstraintIteratorFunc)(cpConstraint *constraint, void *data);
/// Call @c func for each shape in the space.
void cpSpaceEachConstraint(cpSpace *space, cpSpaceConstraintIteratorFunc func, void *data);
//MARK: Indexing
/// Update the collision detection info for the static shapes in the space.
void cpSpaceReindexStatic(cpSpace *space);
/// Update the collision detection data for a specific shape in the space.
void cpSpaceReindexShape(cpSpace *space, cpShape *shape);
/// Update the collision detection data for all shapes attached to a body.
void cpSpaceReindexShapesForBody(cpSpace *space, cpBody *body);
/// Switch the space to use a spatial has as it's spatial index.
void cpSpaceUseSpatialHash(cpSpace *space, cpFloat dim, int count);
//MARK: Time Stepping
/// Step the space forward in time by @c dt.
void cpSpaceStep(cpSpace *space, cpFloat dt);
//MARK: Debug API
#ifndef CP_SPACE_DISABLE_DEBUG_API
typedef struct cpSpaceDebugColor {
float r, g, b, a;
} cpSpaceDebugColor;
typedef void (*cpSpaceDebugDrawCircleImpl)(cpVect pos, cpFloat angle, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer *data);
typedef void (*cpSpaceDebugDrawSegmentImpl)(cpVect a, cpVect b, cpSpaceDebugColor color, cpDataPointer *data);
typedef void (*cpSpaceDebugDrawFatSegmentImpl)(cpVect a, cpVect b, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer *data);
typedef void (*cpSpaceDebugDrawPolygonImpl)(int count, const cpVect *verts, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer *data);
typedef void (*cpSpaceDebugDrawDotImpl)(cpFloat size, cpVect pos, cpSpaceDebugColor color, cpDataPointer *data);
typedef cpSpaceDebugColor (*cpSpaceDebugDrawColorForShapeImpl)(cpShape *shape, cpDataPointer *data);
typedef enum cpSpaceDebugDrawFlags {
CP_SPACE_DEBUG_DRAW_SHAPES = 1<<0,
CP_SPACE_DEBUG_DRAW_CONSTRAINTS = 1<<1,
CP_SPACE_DEBUG_DRAW_COLLISION_POINTS = 1<<2,
} cpSpaceDebugDrawFlags;
typedef struct cpSpaceDebugDrawOptions {
cpSpaceDebugDrawCircleImpl drawCircle;
cpSpaceDebugDrawSegmentImpl drawSegment;
cpSpaceDebugDrawFatSegmentImpl drawFatSegment;
cpSpaceDebugDrawPolygonImpl drawPolygon;
cpSpaceDebugDrawDotImpl drawDot;
cpSpaceDebugDrawFlags flags;
cpSpaceDebugColor shapeOutlineColor;
cpSpaceDebugDrawColorForShapeImpl colorForShape;
cpSpaceDebugColor constraintColor;
cpSpaceDebugColor collisionPointColor;
cpDataPointer data;
} cpSpaceDebugDrawOptions;
void cpSpaceDebugDraw(cpSpace *space, cpSpaceDebugDrawOptions *options);
#endif
/// @}

View File

@ -1,227 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 cpShape structs.
It works by using @c void 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 meanns you shouldn't be reading any of the struct fields.
@{
*/
//MARK: Spatial Index
/// Spatial index bounding box callback function type.
/// The spatial index calls this function and passes you a pointer to an object you added
/// when it needs to get the bounding box associated with that object.
typedef cpBB (*cpSpatialIndexBBFunc)(void *obj);
/// Spatial index/object iterator callback function type.
typedef void (*cpSpatialIndexIteratorFunc)(void *obj, void *data);
/// Spatial query callback function type.
typedef cpCollisionID (*cpSpatialIndexQueryFunc)(void *obj1, void *obj2, cpCollisionID id, void *data);
/// Spatial segment query callback function type.
typedef cpFloat (*cpSpatialIndexSegmentQueryFunc)(void *obj1, void *obj2, void *data);
typedef struct cpSpatialIndexClass cpSpatialIndexClass;
typedef struct cpSpatialIndex cpSpatialIndex;
/// @private
struct cpSpatialIndex {
cpSpatialIndexClass *klass;
cpSpatialIndexBBFunc bbfunc;
cpSpatialIndex *staticIndex, *dynamicIndex;
};
//MARK: Spatial Hash
typedef struct cpSpaceHash cpSpaceHash;
/// Allocate a spatial hash.
cpSpaceHash* cpSpaceHashAlloc(void);
/// Initialize a spatial hash.
cpSpatialIndex* cpSpaceHashInit(cpSpaceHash *hash, cpFloat celldim, int numcells, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);
/// Allocate and initialize a spatial hash.
cpSpatialIndex* cpSpaceHashNew(cpFloat celldim, int cells, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);
/// Change the cell dimensions and table size of the spatial hash to tune it.
/// The cell dimensions should roughly match the average size of your objects
/// and the table size should be ~10 larger than the number of objects inserted.
/// Some trial and error is required to find the optimum numbers for efficiency.
void cpSpaceHashResize(cpSpaceHash *hash, cpFloat celldim, int numcells);
//MARK: AABB Tree
typedef struct cpBBTree cpBBTree;
/// Allocate a bounding box tree.
cpBBTree* cpBBTreeAlloc(void);
/// Initialize a bounding box tree.
cpSpatialIndex* cpBBTreeInit(cpBBTree *tree, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);
/// Allocate and initialize a bounding box tree.
cpSpatialIndex* cpBBTreeNew(cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);
/// Perform a static top down optimization of the tree.
void cpBBTreeOptimize(cpSpatialIndex *index);
/// Bounding box tree velocity callback function.
/// This function should return an estimate for the object's velocity.
typedef cpVect (*cpBBTreeVelocityFunc)(void *obj);
/// Set the velocity function for the bounding box tree to enable temporal coherence.
void cpBBTreeSetVelocityFunc(cpSpatialIndex *index, cpBBTreeVelocityFunc func);
//MARK: Single Axis Sweep
typedef struct cpSweep1D cpSweep1D;
/// Allocate a 1D sort and sweep broadphase.
cpSweep1D* cpSweep1DAlloc(void);
/// Initialize a 1D sort and sweep broadphase.
cpSpatialIndex* cpSweep1DInit(cpSweep1D *sweep, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);
/// Allocate and initialize a 1D sort and sweep broadphase.
cpSpatialIndex* cpSweep1DNew(cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex);
//MARK: Spatial Index Implementation
typedef void (*cpSpatialIndexDestroyImpl)(cpSpatialIndex *index);
typedef int (*cpSpatialIndexCountImpl)(cpSpatialIndex *index);
typedef void (*cpSpatialIndexEachImpl)(cpSpatialIndex *index, cpSpatialIndexIteratorFunc func, void *data);
typedef cpBool (*cpSpatialIndexContainsImpl)(cpSpatialIndex *index, void *obj, cpHashValue hashid);
typedef void (*cpSpatialIndexInsertImpl)(cpSpatialIndex *index, void *obj, cpHashValue hashid);
typedef void (*cpSpatialIndexRemoveImpl)(cpSpatialIndex *index, void *obj, cpHashValue hashid);
typedef void (*cpSpatialIndexReindexImpl)(cpSpatialIndex *index);
typedef void (*cpSpatialIndexReindexObjectImpl)(cpSpatialIndex *index, void *obj, cpHashValue hashid);
typedef void (*cpSpatialIndexReindexQueryImpl)(cpSpatialIndex *index, cpSpatialIndexQueryFunc func, void *data);
typedef void (*cpSpatialIndexQueryImpl)(cpSpatialIndex *index, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data);
typedef void (*cpSpatialIndexSegmentQueryImpl)(cpSpatialIndex *index, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data);
struct cpSpatialIndexClass {
cpSpatialIndexDestroyImpl destroy;
cpSpatialIndexCountImpl count;
cpSpatialIndexEachImpl each;
cpSpatialIndexContainsImpl contains;
cpSpatialIndexInsertImpl insert;
cpSpatialIndexRemoveImpl remove;
cpSpatialIndexReindexImpl reindex;
cpSpatialIndexReindexObjectImpl reindexObject;
cpSpatialIndexReindexQueryImpl reindexQuery;
cpSpatialIndexQueryImpl query;
cpSpatialIndexSegmentQueryImpl segmentQuery;
};
/// Destroy and free a spatial index.
void cpSpatialIndexFree(cpSpatialIndex *index);
/// Collide the objects in @c dynamicIndex against the objects in @c staticIndex using the query callback function.
void cpSpatialIndexCollideStatic(cpSpatialIndex *dynamicIndex, cpSpatialIndex *staticIndex, cpSpatialIndexQueryFunc func, void *data);
/// Destroy a spatial index.
static inline void cpSpatialIndexDestroy(cpSpatialIndex *index)
{
if(index->klass) index->klass->destroy(index);
}
/// Get the number of objects in the spatial index.
static inline int cpSpatialIndexCount(cpSpatialIndex *index)
{
return index->klass->count(index);
}
/// Iterate the objects in the spatial index. @c func will be called once for each object.
static inline void cpSpatialIndexEach(cpSpatialIndex *index, cpSpatialIndexIteratorFunc func, void *data)
{
index->klass->each(index, func, data);
}
/// Returns true if the spatial index contains the given object.
/// Most spatial indexes use hashed storage, so you must provide a hash value too.
static inline cpBool cpSpatialIndexContains(cpSpatialIndex *index, void *obj, cpHashValue hashid)
{
return index->klass->contains(index, obj, hashid);
}
/// Add an object to a spatial index.
/// Most spatial indexes use hashed storage, so you must provide a hash value too.
static inline void cpSpatialIndexInsert(cpSpatialIndex *index, void *obj, cpHashValue hashid)
{
index->klass->insert(index, obj, hashid);
}
/// Remove an object from a spatial index.
/// Most spatial indexes use hashed storage, so you must provide a hash value too.
static inline void cpSpatialIndexRemove(cpSpatialIndex *index, void *obj, cpHashValue hashid)
{
index->klass->remove(index, obj, hashid);
}
/// Perform a full reindex of a spatial index.
static inline void cpSpatialIndexReindex(cpSpatialIndex *index)
{
index->klass->reindex(index);
}
/// Reindex a single object in the spatial index.
static inline void cpSpatialIndexReindexObject(cpSpatialIndex *index, void *obj, cpHashValue hashid)
{
index->klass->reindexObject(index, obj, hashid);
}
/// Perform a rectangle query against the spatial index, calling @c func for each potential match.
static inline void cpSpatialIndexQuery(cpSpatialIndex *index, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data)
{
index->klass->query(index, obj, bb, func, data);
}
/// Perform a segment query against the spatial index, calling @c func for each potential match.
static inline void cpSpatialIndexSegmentQuery(cpSpatialIndex *index, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data)
{
index->klass->segmentQuery(index, obj, a, b, t_exit, func, data);
}
/// 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.
static inline void cpSpatialIndexReindexQuery(cpSpatialIndex *index, cpSpatialIndexQueryFunc func, void *data)
{
index->klass->reindexQuery(index, func, data);
}
///@}

View File

@ -1,197 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef CHIPMUNK_TRANSFORM_H
#define CHIPMUNK_TRANSFORM_H
#include "chipmunk_types.h"
#include "cpVect.h"
#include "cpBB.h"
/// Identity transform matrix.
static const cpTransform cpTransformIdentity = {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f};
/// Construct a new transform matrix.
/// (a, b) is the x basis vector.
/// (c, d) is the y basis vector.
/// (tx, ty) is the translation.
static inline cpTransform
cpTransformNew(cpFloat a, cpFloat b, cpFloat c, cpFloat d, cpFloat tx, cpFloat ty)
{
cpTransform t = {a, b, c, d, tx, ty};
return t;
}
/// Construct a new transform matrix in transposed order.
static inline cpTransform
cpTransformNewTranspose(cpFloat a, cpFloat c, cpFloat tx, cpFloat b, cpFloat d, cpFloat ty)
{
cpTransform t = {a, b, c, d, tx, ty};
return t;
}
/// Get the inverse of a transform matrix.
static inline cpTransform
cpTransformInverse(cpTransform t)
{
cpFloat inv_det = 1.0/(t.a*t.d - t.c*t.b);
return cpTransformNewTranspose(
t.d*inv_det, -t.c*inv_det, (t.c*t.ty - t.tx*t.d)*inv_det,
-t.b*inv_det, t.a*inv_det, (t.tx*t.b - t.a*t.ty)*inv_det
);
}
/// Multiply two transformation matrices.
static inline cpTransform
cpTransformMult(cpTransform t1, cpTransform t2)
{
return cpTransformNewTranspose(
t1.a*t2.a + t1.c*t2.b, t1.a*t2.c + t1.c*t2.d, t1.a*t2.tx + t1.c*t2.ty + t1.tx,
t1.b*t2.a + t1.d*t2.b, t1.b*t2.c + t1.d*t2.d, t1.b*t2.tx + t1.d*t2.ty + t1.ty
);
}
/// Transform an absolute point. (i.e. a vertex)
static inline cpVect
cpTransformPoint(cpTransform t, cpVect p)
{
return cpv(t.a*p.x + t.c*p.y + t.tx, t.b*p.x + t.d*p.y + t.ty);
}
/// Transform a vector (i.e. a normal)
static inline cpVect
cpTransformVect(cpTransform t, cpVect v)
{
return cpv(t.a*v.x + t.c*v.y, t.b*v.x + t.d*v.y);
}
/// Transform a cpBB.
static inline cpBB
cpTransformbBB(cpTransform t, cpBB bb)
{
cpVect center = cpBBCenter(bb);
cpFloat hw = (bb.r - bb.l)*0.5;
cpFloat hh = (bb.t - bb.b)*0.5;
cpFloat a = t.a*hw, b = t.c*hh, d = t.b*hw, e = t.d*hh;
cpFloat hw_max = cpfmax(cpfabs(a + b), cpfabs(a - b));
cpFloat hh_max = cpfmax(cpfabs(d + e), cpfabs(d - e));
return cpBBNewForExtents(cpTransformPoint(t, center), hw_max, hh_max);
}
/// Create a transation matrix.
static inline cpTransform
cpTransformTranslate(cpVect translate)
{
return cpTransformNewTranspose(
1.0, 0.0, translate.x,
0.0, 1.0, translate.y
);
}
/// Create a scale matrix.
static inline cpTransform
cpTransformScale(cpFloat scaleX, cpFloat scaleY)
{
return cpTransformNewTranspose(
scaleX, 0.0, 0.0,
0.0, scaleY, 0.0
);
}
/// Create a rotation matrix.
static inline cpTransform
cpTransformRotate(cpFloat radians)
{
cpVect rot = cpvforangle(radians);
return cpTransformNewTranspose(
rot.x, -rot.y, 0.0,
rot.y, rot.x, 0.0
);
}
/// Create a rigid transformation matrix. (transation + rotation)
static inline cpTransform
cpTransformRigid(cpVect translate, cpFloat radians)
{
cpVect rot = cpvforangle(radians);
return cpTransformNewTranspose(
rot.x, -rot.y, translate.x,
rot.y, rot.x, translate.y
);
}
/// Fast inverse of a rigid transformation matrix.
static inline cpTransform
cpTransformRigidInverse(cpTransform t)
{
return cpTransformNewTranspose(
t.d, -t.c, (t.c*t.ty - t.tx*t.d),
-t.b, t.a, (t.tx*t.b - t.a*t.ty)
);
}
// Miscelaneous (but useful) transformation matrices.
static inline cpTransform
cpTransformWrap(cpTransform outer, cpTransform inner)
{
return cpTransformMult(cpTransformInverse(outer), cpTransformMult(inner, outer));
}
static inline cpTransform
cpTransformWrapInverse(cpTransform outer, cpTransform inner)
{
return cpTransformMult(outer, cpTransformMult(inner, cpTransformInverse(outer)));
}
static inline cpTransform
cpTransformOrtho(cpBB bb)
{
return cpTransformNewTranspose(
2.0/(bb.r - bb.l), 0.0, -(bb.r + bb.l)/(bb.r - bb.l),
0.0, 2.0/(bb.t - bb.b), -(bb.t + bb.b)/(bb.t - bb.b)
);
}
static inline cpTransform
cpTransformBoneScale(cpVect v0, cpVect v1)
{
cpVect d = cpvsub(v1, v0);
return cpTransformNewTranspose(
d.x, -d.y, v0.x,
d.y, d.x, v0.y
);
}
static inline cpTransform
cpTransformAxialScale(cpVect axis, cpVect pivot, cpFloat scale)
{
cpFloat A = axis.x*axis.y*(scale - 1.0);
cpFloat B = cpvdot(axis, pivot)*(1.0 - scale);
return cpTransformNewTranspose(
scale*axis.x*axis.x + axis.y*axis.y, A, axis.x*B,
A, axis.x*axis.x + scale*axis.y*axis.y, axis.y*B
);
}
#endif

View File

@ -1,230 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef CHIPMUNK_VECT_H
#define CHIPMUNK_VECT_H
#include "chipmunk_types.h"
/// @defgroup cpVect cpVect
/// Chipmunk's 2D vector type along with a handy 2D vector math lib.
/// @{
/// Constant for the zero vector.
static const cpVect cpvzero = {0.0f,0.0f};
/// Convenience constructor for cpVect structs.
static inline cpVect cpv(const cpFloat x, const cpFloat y)
{
cpVect v = {x, y};
return v;
}
/// Check if two vectors are equal. (Be careful when comparing floating point numbers!)
static inline cpBool cpveql(const cpVect v1, const cpVect v2)
{
return (v1.x == v2.x && v1.y == v2.y);
}
/// Add two vectors
static inline cpVect cpvadd(const cpVect v1, const cpVect v2)
{
return cpv(v1.x + v2.x, v1.y + v2.y);
}
/// Subtract two vectors.
static inline cpVect cpvsub(const cpVect v1, const cpVect v2)
{
return cpv(v1.x - v2.x, v1.y - v2.y);
}
/// Negate a vector.
static inline cpVect cpvneg(const cpVect v)
{
return cpv(-v.x, -v.y);
}
/// Scalar multiplication.
static inline cpVect cpvmult(const cpVect v, const cpFloat s)
{
return cpv(v.x*s, v.y*s);
}
/// Vector dot product.
static inline cpFloat cpvdot(const cpVect v1, const cpVect v2)
{
return v1.x*v2.x + v1.y*v2.y;
}
/// 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.
static inline cpFloat cpvcross(const cpVect v1, const cpVect v2)
{
return v1.x*v2.y - v1.y*v2.x;
}
/// Returns a perpendicular vector. (90 degree rotation)
static inline cpVect cpvperp(const cpVect v)
{
return cpv(-v.y, v.x);
}
/// Returns a perpendicular vector. (-90 degree rotation)
static inline cpVect cpvrperp(const cpVect v)
{
return cpv(v.y, -v.x);
}
/// Returns the vector projection of v1 onto v2.
static inline cpVect cpvproject(const cpVect v1, const cpVect v2)
{
return cpvmult(v2, cpvdot(v1, v2)/cpvdot(v2, v2));
}
/// Returns the unit length vector for the given angle (in radians).
static inline cpVect cpvforangle(const cpFloat a)
{
return cpv(cpfcos(a), cpfsin(a));
}
/// Returns the angular direction v is pointing in (in radians).
static inline cpFloat cpvtoangle(const cpVect v)
{
return cpfatan2(v.y, v.x);
}
/// Uses complex number multiplication to rotate v1 by v2. Scaling will occur if v1 is not a unit vector.
static inline cpVect cpvrotate(const cpVect v1, const cpVect v2)
{
return cpv(v1.x*v2.x - v1.y*v2.y, v1.x*v2.y + v1.y*v2.x);
}
/// Inverse of cpvrotate().
static inline cpVect cpvunrotate(const cpVect v1, const cpVect v2)
{
return cpv(v1.x*v2.x + v1.y*v2.y, v1.y*v2.x - v1.x*v2.y);
}
/// Returns the squared length of v. Faster than cpvlength() when you only need to compare lengths.
static inline cpFloat cpvlengthsq(const cpVect v)
{
return cpvdot(v, v);
}
/// Returns the length of v.
static inline cpFloat cpvlength(const cpVect v)
{
return cpfsqrt(cpvdot(v, v));
}
/// Linearly interpolate between v1 and v2.
static inline cpVect cpvlerp(const cpVect v1, const cpVect v2, const cpFloat t)
{
return cpvadd(cpvmult(v1, 1.0f - t), cpvmult(v2, t));
}
/// Returns a normalized copy of v.
static inline cpVect cpvnormalize(const cpVect v)
{
// Neat trick I saw somewhere to avoid div/0.
return cpvmult(v, 1.0f/(cpvlength(v) + CPFLOAT_MIN));
}
/// Spherical linearly interpolate between v1 and v2.
static inline cpVect
cpvslerp(const cpVect v1, const cpVect v2, const cpFloat t)
{
cpFloat dot = cpvdot(cpvnormalize(v1), cpvnormalize(v2));
cpFloat omega = cpfacos(cpfclamp(dot, -1.0f, 1.0f));
if(omega < 1e-3){
// If the angle between two vectors is very small, lerp instead to avoid precision issues.
return cpvlerp(v1, v2, t);
} else {
cpFloat denom = 1.0f/cpfsin(omega);
return cpvadd(cpvmult(v1, cpfsin((1.0f - t)*omega)*denom), cpvmult(v2, cpfsin(t*omega)*denom));
}
}
/// Spherical linearly interpolate between v1 towards v2 by no more than angle a radians
static inline cpVect
cpvslerpconst(const cpVect v1, const cpVect v2, const cpFloat a)
{
cpFloat dot = cpvdot(cpvnormalize(v1), cpvnormalize(v2));
cpFloat omega = cpfacos(cpfclamp(dot, -1.0f, 1.0f));
return cpvslerp(v1, v2, cpfmin(a, omega)/omega);
}
/// Clamp v to length len.
static inline cpVect cpvclamp(const cpVect v, const cpFloat len)
{
return (cpvdot(v,v) > len*len) ? cpvmult(cpvnormalize(v), len) : v;
}
/// Linearly interpolate between v1 towards v2 by distance d.
static inline cpVect cpvlerpconst(cpVect v1, cpVect v2, cpFloat d)
{
return cpvadd(v1, cpvclamp(cpvsub(v2, v1), d));
}
/// Returns the distance between v1 and v2.
static inline cpFloat cpvdist(const cpVect v1, const cpVect v2)
{
return cpvlength(cpvsub(v1, v2));
}
/// Returns the squared distance between v1 and v2. Faster than cpvdist() when you only need to compare distances.
static inline cpFloat cpvdistsq(const cpVect v1, const cpVect v2)
{
return cpvlengthsq(cpvsub(v1, v2));
}
/// Returns true if the distance between v1 and v2 is less than dist.
static inline cpBool cpvnear(const cpVect v1, const cpVect v2, const cpFloat dist)
{
return cpvdistsq(v1, v2) < dist*dist;
}
/// @}
/// @defgroup cpMat2x2 cpMat2x2
/// 2x2 matrix type used for tensors and such.
/// @{
// NUKE
static inline cpMat2x2
cpMat2x2New(cpFloat a, cpFloat b, cpFloat c, cpFloat d)
{
cpMat2x2 m = {a, b, c, d};
return m;
}
static inline cpVect
cpMat2x2Transform(cpMat2x2 m, cpVect v)
{
return cpv(v.x*m.a + v.y*m.b, v.x*m.c + v.y*m.d);
}
///@}
#endif

View File

@ -1,318 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include "chipmunk/chipmunk_private.h"
void
cpMessage(const char *condition, const char *file, int line, int isError, int isHardError, const char *message, ...)
{
fprintf(stderr, (isError ? "Aborting due to Chipmunk error: " : "Chipmunk warning: "));
va_list vargs;
va_start(vargs, message); {
vfprintf(stderr, message, vargs);
fprintf(stderr, "\n");
} va_end(vargs);
fprintf(stderr, "\tFailed condition: %s\n", condition);
fprintf(stderr, "\tSource:%s:%d\n", file, line);
}
#define STR(s) #s
#define XSTR(s) STR(s)
const char *cpVersionString = XSTR(CP_VERSION_MAJOR)"."XSTR(CP_VERSION_MINOR)"."XSTR(CP_VERSION_RELEASE);
//MARK: Misc Functions
cpFloat
cpMomentForCircle(cpFloat m, cpFloat r1, cpFloat r2, cpVect offset)
{
return m*(0.5f*(r1*r1 + r2*r2) + cpvlengthsq(offset));
}
cpFloat
cpAreaForCircle(cpFloat r1, cpFloat r2)
{
return (cpFloat)M_PI*cpfabs(r1*r1 - r2*r2);
}
cpFloat
cpMomentForSegment(cpFloat m, cpVect a, cpVect b, cpFloat r)
{
cpVect offset = cpvlerp(a, b, 0.5f);
// This approximates the shape as a box for rounded segments, but it's quite close.
cpFloat length = cpvdist(b, a) + 2.0f*r;
return m*((length*length + 4.0f*r*r)/12.0f + cpvlengthsq(offset));
}
cpFloat
cpAreaForSegment(cpVect a, cpVect b, cpFloat r)
{
return r*((cpFloat)M_PI*r + 2.0f*cpvdist(a, b));
}
cpFloat
cpMomentForPoly(cpFloat m, const int count, const cpVect *verts, cpVect offset, cpFloat r)
{
// TODO account for radius.
if(count == 2) return cpMomentForSegment(m, verts[0], verts[1], 0.0f);
cpFloat sum1 = 0.0f;
cpFloat sum2 = 0.0f;
for(int i=0; i<count; i++){
cpVect v1 = cpvadd(verts[i], offset);
cpVect v2 = cpvadd(verts[(i+1)%count], offset);
cpFloat a = cpvcross(v2, v1);
cpFloat b = cpvdot(v1, v1) + cpvdot(v1, v2) + cpvdot(v2, v2);
sum1 += a*b;
sum2 += a;
}
return (m*sum1)/(6.0f*sum2);
}
cpFloat
cpAreaForPoly(const int count, const cpVect *verts, cpFloat r)
{
cpFloat area = 0.0f;
cpFloat perimeter = 0.0f;
for(int i=0; i<count; i++){
cpVect v1 = verts[i];
cpVect v2 = verts[(i+1)%count];
area += cpvcross(v1, v2);
perimeter += cpvdist(v1, v2);
}
return r*(M_PI*cpfabs(r) + perimeter) + area/2.0f;
}
cpVect
cpCentroidForPoly(const int count, const cpVect *verts)
{
cpFloat sum = 0.0f;
cpVect vsum = cpvzero;
for(int i=0; i<count; i++){
cpVect v1 = verts[i];
cpVect v2 = verts[(i+1)%count];
cpFloat cross = cpvcross(v1, v2);
sum += cross;
vsum = cpvadd(vsum, cpvmult(cpvadd(v1, v2), cross));
}
return cpvmult(vsum, 1.0f/(3.0f*sum));
}
//void
//cpRecenterPoly(const int count, cpVect *verts){
// cpVect centroid = cpCentroidForPoly(count, verts);
//
// for(int i=0; i<count; i++){
// verts[i] = cpvsub(verts[i], centroid);
// }
//}
cpFloat
cpMomentForBox(cpFloat m, cpFloat width, cpFloat height)
{
return m*(width*width + height*height)/12.0f;
}
cpFloat
cpMomentForBox2(cpFloat m, cpBB box)
{
cpFloat width = box.r - box.l;
cpFloat height = box.t - box.b;
cpVect offset = cpvmult(cpv(box.l + box.r, box.b + box.t), 0.5f);
// TODO: NaN when offset is 0 and m is INFINITY
return cpMomentForBox(m, width, height) + m*cpvlengthsq(offset);
}
//MARK: Quick Hull
void
cpLoopIndexes(const cpVect *verts, int count, int *start, int *end)
{
(*start) = (*end) = 0;
cpVect min = verts[0];
cpVect max = min;
for(int i=1; i<count; i++){
cpVect v = verts[i];
if(v.x < min.x || (v.x == min.x && v.y < min.y)){
min = v;
(*start) = i;
} else if(v.x > max.x || (v.x == max.x && v.y > max.y)){
max = v;
(*end) = i;
}
}
}
#define SWAP(__A__, __B__) {cpVect __TMP__ = __A__; __A__ = __B__; __B__ = __TMP__;}
static int
QHullPartition(cpVect *verts, int count, cpVect a, cpVect b, cpFloat tol)
{
if(count == 0) return 0;
cpFloat max = 0;
int pivot = 0;
cpVect delta = cpvsub(b, a);
cpFloat valueTol = tol*cpvlength(delta);
int head = 0;
for(int tail = count-1; head <= tail;){
cpFloat value = cpvcross(cpvsub(verts[head], a), delta);
if(value > valueTol){
if(value > max){
max = value;
pivot = head;
}
head++;
} else {
SWAP(verts[head], verts[tail]);
tail--;
}
}
// move the new pivot to the front if it's not already there.
if(pivot != 0) SWAP(verts[0], verts[pivot]);
return head;
}
static int
QHullReduce(cpFloat tol, cpVect *verts, int count, cpVect a, cpVect pivot, cpVect b, cpVect *result)
{
if(count < 0){
return 0;
} else if(count == 0) {
result[0] = pivot;
return 1;
} else {
int left_count = QHullPartition(verts, count, a, pivot, tol);
int index = QHullReduce(tol, verts + 1, left_count - 1, a, verts[0], pivot, result);
result[index++] = pivot;
int right_count = QHullPartition(verts + left_count, count - left_count, pivot, b, tol);
return index + QHullReduce(tol, verts + left_count + 1, right_count - 1, pivot, verts[left_count], b, result + 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.
int
cpConvexHull(int count, const cpVect *verts, cpVect *result, int *first, cpFloat tol)
{
if(verts != result){
// Copy the line vertexes into the empty part of the result polyline to use as a scratch buffer.
memcpy(result, verts, count*sizeof(cpVect));
}
// Degenerate case, all points are the same.
int start, end;
cpLoopIndexes(verts, count, &start, &end);
if(start == end){
if(first) (*first) = 0;
return 1;
}
SWAP(result[0], result[start]);
SWAP(result[1], result[end == 0 ? start : end]);
cpVect a = result[0];
cpVect b = result[1];
if(first) (*first) = start;
return QHullReduce(tol, result + 2, count - 2, a, b, a, result + 1) + 1;
}
//MARK: Alternate Block Iterators
#if defined(__has_extension)
#if __has_extension(blocks)
static void IteratorFunc(void *ptr, void (^block)(void *ptr)){block(ptr);}
void cpSpaceEachBody_b(cpSpace *space, void (^block)(cpBody *body)){
cpSpaceEachBody(space, (cpSpaceBodyIteratorFunc)IteratorFunc, block);
}
void cpSpaceEachShape_b(cpSpace *space, void (^block)(cpShape *shape)){
cpSpaceEachShape(space, (cpSpaceShapeIteratorFunc)IteratorFunc, block);
}
void cpSpaceEachConstraint_b(cpSpace *space, void (^block)(cpConstraint *constraint)){
cpSpaceEachConstraint(space, (cpSpaceConstraintIteratorFunc)IteratorFunc, block);
}
static void BodyIteratorFunc(cpBody *body, void *ptr, void (^block)(void *ptr)){block(ptr);}
void cpBodyEachShape_b(cpBody *body, void (^block)(cpShape *shape)){
cpBodyEachShape(body, (cpBodyShapeIteratorFunc)BodyIteratorFunc, block);
}
void cpBodyEachConstraint_b(cpBody *body, void (^block)(cpConstraint *constraint)){
cpBodyEachConstraint(body, (cpBodyConstraintIteratorFunc)BodyIteratorFunc, block);
}
void cpBodyEachArbiter_b(cpBody *body, void (^block)(cpArbiter *arbiter)){
cpBodyEachArbiter(body, (cpBodyArbiterIteratorFunc)BodyIteratorFunc, block);
}
static void PointQueryIteratorFunc(cpShape *shape, cpVect p, cpFloat d, cpVect g, cpSpacePointQueryBlock block){block(shape, p, d, g);}
void cpSpacePointQuery_b(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpSpacePointQueryBlock block){
cpSpacePointQuery(space, point, maxDistance, filter, (cpSpacePointQueryFunc)PointQueryIteratorFunc, block);
}
static void SegmentQueryIteratorFunc(cpShape *shape, cpVect p, cpVect n, cpFloat t, cpSpaceSegmentQueryBlock block){block(shape, p, n, t);}
void cpSpaceSegmentQuery_b(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSpaceSegmentQueryBlock block){
cpSpaceSegmentQuery(space, start, end, radius, filter, (cpSpaceSegmentQueryFunc)SegmentQueryIteratorFunc, block);
}
void cpSpaceBBQuery_b(cpSpace *space, cpBB bb, cpShapeFilter filter, cpSpaceBBQueryBlock block){
cpSpaceBBQuery(space, bb, filter, (cpSpaceBBQueryFunc)IteratorFunc, block);
}
static void ShapeQueryIteratorFunc(cpShape *shape, cpContactPointSet *points, cpSpaceShapeQueryBlock block){block(shape, points);}
cpBool cpSpaceShapeQuery_b(cpSpace *space, cpShape *shape, cpSpaceShapeQueryBlock block){
return cpSpaceShapeQuery(space, shape, (cpSpaceShapeQueryFunc)ShapeQueryIteratorFunc, block);
}
#endif
#endif
#include "chipmunk_ffi.h"

View File

@ -1,496 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "chipmunk/chipmunk_private.h"
// TODO: make this generic so I can reuse it for constraints also.
static inline void
unthreadHelper(cpArbiter *arb, cpBody *body)
{
struct cpArbiterThread *thread = cpArbiterThreadForBody(arb, body);
cpArbiter *prev = thread->prev;
cpArbiter *next = thread->next;
if(prev){
cpArbiterThreadForBody(prev, body)->next = next;
} else if(body->arbiterList == arb) {
// IFF prev is NULL and body->arbiterList == arb, is arb at the head of the list.
// This function may be called for an arbiter that was never in a list.
// In that case, we need to protect it from wiping out the body->arbiterList pointer.
body->arbiterList = next;
}
if(next) cpArbiterThreadForBody(next, body)->prev = prev;
thread->prev = NULL;
thread->next = NULL;
}
void
cpArbiterUnthread(cpArbiter *arb)
{
unthreadHelper(arb, arb->body_a);
unthreadHelper(arb, arb->body_b);
}
cpBool cpArbiterIsFirstContact(const cpArbiter *arb)
{
return arb->state == CP_ARBITER_STATE_FIRST_COLLISION;
}
cpBool cpArbiterIsRemoval(const cpArbiter *arb)
{
return arb->state == CP_ARBITER_STATE_INVALIDATED;
}
int cpArbiterGetCount(const cpArbiter *arb)
{
// Return 0 contacts if we are in a separate callback.
return (arb->state < CP_ARBITER_STATE_CACHED ? arb->count : 0);
}
cpVect
cpArbiterGetNormal(const cpArbiter *arb)
{
return cpvmult(arb->n, arb->swapped ? -1.0f : 1.0);
}
cpVect
cpArbiterGetPointA(const cpArbiter *arb, int i)
{
cpAssertHard(0 <= i && i < cpArbiterGetCount(arb), "Index error: The specified contact index is invalid for this arbiter");
return cpvadd(arb->body_a->p, arb->contacts[i].r1);
}
cpVect
cpArbiterGetPointB(const cpArbiter *arb, int i)
{
cpAssertHard(0 <= i && i < cpArbiterGetCount(arb), "Index error: The specified contact index is invalid for this arbiter");
return cpvadd(arb->body_b->p, arb->contacts[i].r2);
}
cpFloat
cpArbiterGetDepth(const cpArbiter *arb, int i)
{
cpAssertHard(0 <= i && i < cpArbiterGetCount(arb), "Index error: The specified contact index is invalid for this arbiter");
struct cpContact *con = &arb->contacts[i];
return cpvdot(cpvadd(cpvsub(con->r2, con->r1), cpvsub(arb->body_b->p, arb->body_a->p)), arb->n);
}
cpContactPointSet
cpArbiterGetContactPointSet(const cpArbiter *arb)
{
cpContactPointSet set;
set.count = cpArbiterGetCount(arb);
cpBool swapped = arb->swapped;
cpVect n = arb->n;
set.normal = (swapped ? cpvneg(n) : n);
for(int i=0; i<set.count; i++){
// Contact points are relative to body CoGs;
cpVect p1 = cpvadd(arb->body_a->p, arb->contacts[i].r1);
cpVect p2 = cpvadd(arb->body_b->p, arb->contacts[i].r2);
set.points[i].pointA = (swapped ? p2 : p1);
set.points[i].pointB = (swapped ? p1 : p2);
set.points[i].distance = cpvdot(cpvsub(p2, p1), n);
}
return set;
}
void
cpArbiterSetContactPointSet(cpArbiter *arb, cpContactPointSet *set)
{
int count = set->count;
cpAssertHard(count == arb->count, "The number of contact points cannot be changed.");
cpBool swapped = arb->swapped;
arb->n = (swapped ? cpvneg(set->normal) : set->normal);
for(int i=0; i<count; i++){
// Convert back to CoG relative offsets.
cpVect p1 = set->points[i].pointA;
cpVect p2 = set->points[i].pointB;
arb->contacts[i].r1 = cpvsub(swapped ? p2 : p1, arb->body_a->p);
arb->contacts[i].r2 = cpvsub(swapped ? p1 : p2, arb->body_b->p);
}
}
cpVect
cpArbiterTotalImpulse(const cpArbiter *arb)
{
struct cpContact *contacts = arb->contacts;
cpVect n = arb->n;
cpVect sum = cpvzero;
for(int i=0, count=cpArbiterGetCount(arb); i<count; i++){
struct cpContact *con = &contacts[i];
sum = cpvadd(sum, cpvrotate(n, cpv(con->jnAcc, con->jtAcc)));
}
return (arb->swapped ? sum : cpvneg(sum));
return cpvzero;
}
cpFloat
cpArbiterTotalKE(const cpArbiter *arb)
{
cpFloat eCoef = (1 - arb->e)/(1 + arb->e);
cpFloat sum = 0.0;
struct cpContact *contacts = arb->contacts;
for(int i=0, count=cpArbiterGetCount(arb); i<count; i++){
struct cpContact *con = &contacts[i];
cpFloat jnAcc = con->jnAcc;
cpFloat jtAcc = con->jtAcc;
sum += eCoef*jnAcc*jnAcc/con->nMass + jtAcc*jtAcc/con->tMass;
}
return sum;
}
cpBool
cpArbiterIgnore(cpArbiter *arb)
{
arb->state = CP_ARBITER_STATE_IGNORE;
return cpFalse;
}
cpFloat
cpArbiterGetRestitution(const cpArbiter *arb)
{
return arb->e;
}
void
cpArbiterSetRestitution(cpArbiter *arb, cpFloat restitution)
{
arb->e = restitution;
}
cpFloat
cpArbiterGetFriction(const cpArbiter *arb)
{
return arb->u;
}
void
cpArbiterSetFriction(cpArbiter *arb, cpFloat friction)
{
arb->u = friction;
}
cpVect
cpArbiterGetSurfaceVelocity(cpArbiter *arb)
{
return cpvmult(arb->surface_vr, arb->swapped ? -1.0f : 1.0);
}
void
cpArbiterSetSurfaceVelocity(cpArbiter *arb, cpVect vr)
{
arb->surface_vr = cpvmult(vr, arb->swapped ? -1.0f : 1.0);
}
cpDataPointer
cpArbiterGetUserData(const cpArbiter *arb)
{
return arb->data;
}
void
cpArbiterSetUserData(cpArbiter *arb, cpDataPointer userData)
{
arb->data = userData;
}
void
cpArbiterGetShapes(const cpArbiter *arb, cpShape **a, cpShape **b)
{
if(arb->swapped){
(*a) = (cpShape *)arb->b, (*b) = (cpShape *)arb->a;
} else {
(*a) = (cpShape *)arb->a, (*b) = (cpShape *)arb->b;
}
}
void cpArbiterGetBodies(const cpArbiter *arb, cpBody **a, cpBody **b)
{
CP_ARBITER_GET_SHAPES(arb, shape_a, shape_b);
(*a) = shape_a->body;
(*b) = shape_b->body;
}
cpBool
cpArbiterCallWildcardBeginA(cpArbiter *arb, cpSpace *space)
{
cpCollisionHandler *handler = arb->handlerA;
return handler->beginFunc(arb, space, handler->userData);
}
cpBool
cpArbiterCallWildcardBeginB(cpArbiter *arb, cpSpace *space)
{
cpCollisionHandler *handler = arb->handlerB;
arb->swapped = !arb->swapped;
cpBool retval = handler->beginFunc(arb, space, handler->userData);
arb->swapped = !arb->swapped;
return retval;
}
cpBool
cpArbiterCallWildcardPreSolveA(cpArbiter *arb, cpSpace *space)
{
cpCollisionHandler *handler = arb->handlerA;
return handler->preSolveFunc(arb, space, handler->userData);
}
cpBool
cpArbiterCallWildcardPreSolveB(cpArbiter *arb, cpSpace *space)
{
cpCollisionHandler *handler = arb->handlerB;
arb->swapped = !arb->swapped;
cpBool retval = handler->preSolveFunc(arb, space, handler->userData);
arb->swapped = !arb->swapped;
return retval;
}
void
cpArbiterCallWildcardPostSolveA(cpArbiter *arb, cpSpace *space)
{
cpCollisionHandler *handler = arb->handlerA;
handler->postSolveFunc(arb, space, handler->userData);
}
void
cpArbiterCallWildcardPostSolveB(cpArbiter *arb, cpSpace *space)
{
cpCollisionHandler *handler = arb->handlerB;
arb->swapped = !arb->swapped;
handler->postSolveFunc(arb, space, handler->userData);
arb->swapped = !arb->swapped;
}
void
cpArbiterCallWildcardSeparateA(cpArbiter *arb, cpSpace *space)
{
cpCollisionHandler *handler = arb->handlerA;
handler->separateFunc(arb, space, handler->userData);
}
void
cpArbiterCallWildcardSeparateB(cpArbiter *arb, cpSpace *space)
{
cpCollisionHandler *handler = arb->handlerB;
arb->swapped = !arb->swapped;
handler->separateFunc(arb, space, handler->userData);
arb->swapped = !arb->swapped;
}
cpArbiter*
cpArbiterInit(cpArbiter *arb, cpShape *a, cpShape *b)
{
arb->handler = NULL;
arb->swapped = cpFalse;
arb->handler = NULL;
arb->handlerA = NULL;
arb->handlerB = NULL;
arb->e = 0.0f;
arb->u = 0.0f;
arb->surface_vr = cpvzero;
arb->count = 0;
arb->contacts = NULL;
arb->a = a; arb->body_a = a->body;
arb->b = b; arb->body_b = b->body;
arb->thread_a.next = NULL;
arb->thread_b.next = NULL;
arb->thread_a.prev = NULL;
arb->thread_b.prev = NULL;
arb->stamp = 0;
arb->state = CP_ARBITER_STATE_FIRST_COLLISION;
arb->data = NULL;
return arb;
}
static inline cpCollisionHandler *
cpSpaceLookupHandler(cpSpace *space, cpCollisionType a, cpCollisionType b, cpCollisionHandler *defaultValue)
{
cpCollisionType types[] = {a, b};
cpCollisionHandler *handler = (cpCollisionHandler *)cpHashSetFind(space->collisionHandlers, CP_HASH_PAIR(a, b), types);
return (handler ? handler : defaultValue);
}
void
cpArbiterUpdate(cpArbiter *arb, struct cpCollisionInfo *info, cpSpace *space)
{
const cpShape *a = info->a, *b = info->b;
// For collisions between two similar primitive types, the order could have been swapped since the last frame.
arb->a = a; arb->body_a = a->body;
arb->b = b; arb->body_b = b->body;
// Iterate over the possible pairs to look for hash value matches.
for(int i=0; i<info->count; i++){
struct cpContact *con = &info->arr[i];
// r1 and r2 store absolute offsets at init time.
// Need to convert them to relative offsets.
con->r1 = cpvsub(con->r1, a->body->p);
con->r2 = cpvsub(con->r2, b->body->p);
// Cached impulses are not zeroed at init time.
con->jnAcc = con->jtAcc = 0.0f;
for(int j=0; j<arb->count; j++){
struct cpContact *old = &arb->contacts[j];
// This could trigger false positives, but is fairly unlikely nor serious if it does.
if(con->hash == old->hash){
// Copy the persistant contact information.
con->jnAcc = old->jnAcc;
con->jtAcc = old->jtAcc;
}
}
}
arb->contacts = info->arr;
arb->count = info->count;
arb->n = info->n;
arb->e = a->e * b->e;
arb->u = a->u * b->u;
cpVect surface_vr = cpvsub(b->surfaceV, a->surfaceV);
arb->surface_vr = cpvsub(surface_vr, cpvmult(info->n, cpvdot(surface_vr, info->n)));
cpCollisionType typeA = info->a->type, typeB = info->b->type;
cpCollisionHandler *defaultHandler = &space->defaultHandler;
cpCollisionHandler *handler = arb->handler = cpSpaceLookupHandler(space, typeA, typeB, defaultHandler);
// Check if the types match, but don't swap for a default handler which use the wildcard for type A.
cpBool swapped = arb->swapped = (typeA != handler->typeA && handler->typeA != CP_WILDCARD_COLLISION_TYPE);
if(handler != defaultHandler || space->usesWildcards){
// The order of the main handler swaps the wildcard handlers too. Uffda.
arb->handlerA = cpSpaceLookupHandler(space, (swapped ? typeB : typeA), CP_WILDCARD_COLLISION_TYPE, &cpCollisionHandlerDoNothing);
arb->handlerB = cpSpaceLookupHandler(space, (swapped ? typeA : typeB), CP_WILDCARD_COLLISION_TYPE, &cpCollisionHandlerDoNothing);
}
// mark it as new if it's been cached
if(arb->state == CP_ARBITER_STATE_CACHED) arb->state = CP_ARBITER_STATE_FIRST_COLLISION;
}
void
cpArbiterPreStep(cpArbiter *arb, cpFloat dt, cpFloat slop, cpFloat bias)
{
cpBody *a = arb->body_a;
cpBody *b = arb->body_b;
cpVect n = arb->n;
cpVect body_delta = cpvsub(b->p, a->p);
for(int i=0; i<arb->count; i++){
struct cpContact *con = &arb->contacts[i];
// Calculate the mass normal and mass tangent.
con->nMass = 1.0f/k_scalar(a, b, con->r1, con->r2, n);
con->tMass = 1.0f/k_scalar(a, b, con->r1, con->r2, cpvperp(n));
// Calculate the target bias velocity.
cpFloat dist = cpvdot(cpvadd(cpvsub(con->r2, con->r1), body_delta), n);
con->bias = -bias*cpfmin(0.0f, dist + slop)/dt;
con->jBias = 0.0f;
// Calculate the target bounce velocity.
con->bounce = normal_relative_velocity(a, b, con->r1, con->r2, n)*arb->e;
}
}
void
cpArbiterApplyCachedImpulse(cpArbiter *arb, cpFloat dt_coef)
{
if(cpArbiterIsFirstContact(arb)) return;
cpBody *a = arb->body_a;
cpBody *b = arb->body_b;
cpVect n = arb->n;
for(int i=0; i<arb->count; i++){
struct cpContact *con = &arb->contacts[i];
cpVect j = cpvrotate(n, cpv(con->jnAcc, con->jtAcc));
apply_impulses(a, b, con->r1, con->r2, cpvmult(j, dt_coef));
}
}
// TODO: is it worth splitting velocity/position correction?
void
cpArbiterApplyImpulse(cpArbiter *arb)
{
cpBody *a = arb->body_a;
cpBody *b = arb->body_b;
cpVect n = arb->n;
cpVect surface_vr = arb->surface_vr;
cpFloat friction = arb->u;
for(int i=0; i<arb->count; i++){
struct cpContact *con = &arb->contacts[i];
cpFloat nMass = con->nMass;
cpVect r1 = con->r1;
cpVect r2 = con->r2;
cpVect vb1 = cpvadd(a->v_bias, cpvmult(cpvperp(r1), a->w_bias));
cpVect vb2 = cpvadd(b->v_bias, cpvmult(cpvperp(r2), b->w_bias));
cpVect vr = cpvadd(relative_velocity(a, b, r1, r2), surface_vr);
cpFloat vbn = cpvdot(cpvsub(vb2, vb1), n);
cpFloat vrn = cpvdot(vr, n);
cpFloat vrt = cpvdot(vr, cpvperp(n));
cpFloat jbn = (con->bias - vbn)*nMass;
cpFloat jbnOld = con->jBias;
con->jBias = cpfmax(jbnOld + jbn, 0.0f);
cpFloat jn = -(con->bounce + vrn)*nMass;
cpFloat jnOld = con->jnAcc;
con->jnAcc = cpfmax(jnOld + jn, 0.0f);
cpFloat jtMax = friction*con->jnAcc;
cpFloat jt = -vrt*con->tMass;
cpFloat jtOld = con->jtAcc;
con->jtAcc = cpfclamp(jtOld + jt, -jtMax, jtMax);
apply_bias_impulses(a, b, r1, r2, cpvmult(n, con->jBias - jbnOld));
apply_impulses(a, b, r1, r2, cpvrotate(n, cpv(con->jnAcc - jnOld, con->jtAcc - jtOld)));
}
}

View File

@ -1,101 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <string.h>
#include "chipmunk/chipmunk_private.h"
cpArray *
cpArrayNew(int size)
{
cpArray *arr = (cpArray *)cpcalloc(1, sizeof(cpArray));
arr->num = 0;
arr->max = (size ? size : 4);
arr->arr = (void **)cpcalloc(arr->max, sizeof(void*));
return arr;
}
void
cpArrayFree(cpArray *arr)
{
if(arr){
cpfree(arr->arr);
arr->arr = NULL;
cpfree(arr);
}
}
void
cpArrayPush(cpArray *arr, void *object)
{
if(arr->num == arr->max){
arr->max *= 2;
arr->arr = (void **)cprealloc(arr->arr, arr->max*sizeof(void*));
}
arr->arr[arr->num] = object;
arr->num++;
}
void *
cpArrayPop(cpArray *arr)
{
arr->num--;
void *value = arr->arr[arr->num];
arr->arr[arr->num] = NULL;
return value;
}
void
cpArrayDeleteObj(cpArray *arr, void *obj)
{
for(int i=0; i<arr->num; i++){
if(arr->arr[i] == obj){
arr->num--;
arr->arr[i] = arr->arr[arr->num];
arr->arr[arr->num] = NULL;
return;
}
}
}
void
cpArrayFreeEach(cpArray *arr, void (freeFunc)(void*))
{
for(int i=0; i<arr->num; i++) freeFunc(arr->arr[i]);
}
cpBool
cpArrayContains(cpArray *arr, void *ptr)
{
for(int i=0; i<arr->num; i++)
if(arr->arr[i] == ptr) return cpTrue;
return cpFalse;
}

View File

@ -1,896 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "stdlib.h"
#include "stdio.h"
#include "chipmunk/chipmunk_private.h"
static inline cpSpatialIndexClass *Klass();
typedef struct Node Node;
typedef struct Pair Pair;
struct cpBBTree {
cpSpatialIndex spatialIndex;
cpBBTreeVelocityFunc velocityFunc;
cpHashSet *leaves;
Node *root;
Node *pooledNodes;
Pair *pooledPairs;
cpArray *allocatedBuffers;
cpTimestamp stamp;
};
struct Node {
void *obj;
cpBB bb;
Node *parent;
union {
// Internal nodes
struct { Node *a, *b; } children;
// Leaves
struct {
cpTimestamp stamp;
Pair *pairs;
} leaf;
} node;
};
// Can't use anonymous unions and still get good x-compiler compatability
#define A node.children.a
#define B node.children.b
#define STAMP node.leaf.stamp
#define PAIRS node.leaf.pairs
typedef struct Thread {
Pair *prev;
Node *leaf;
Pair *next;
} Thread;
struct Pair {
Thread a, b;
cpCollisionID id;
};
//MARK: Misc Functions
static inline cpBB
GetBB(cpBBTree *tree, void *obj)
{
cpBB bb = tree->spatialIndex.bbfunc(obj);
cpBBTreeVelocityFunc velocityFunc = tree->velocityFunc;
if(velocityFunc){
cpFloat coef = 0.1f;
cpFloat x = (bb.r - bb.l)*coef;
cpFloat y = (bb.t - bb.b)*coef;
cpVect v = cpvmult(velocityFunc(obj), 0.1f);
return cpBBNew(bb.l + cpfmin(-x, v.x), bb.b + cpfmin(-y, v.y), bb.r + cpfmax(x, v.x), bb.t + cpfmax(y, v.y));
} else {
return bb;
}
}
static inline cpBBTree *
GetTree(cpSpatialIndex *index)
{
return (index && index->klass == Klass() ? (cpBBTree *)index : NULL);
}
static inline Node *
GetRootIfTree(cpSpatialIndex *index){
return (index && index->klass == Klass() ? ((cpBBTree *)index)->root : NULL);
}
static inline cpBBTree *
GetMasterTree(cpBBTree *tree)
{
cpBBTree *dynamicTree = GetTree(tree->spatialIndex.dynamicIndex);
return (dynamicTree ? dynamicTree : tree);
}
static inline void
IncrementStamp(cpBBTree *tree)
{
cpBBTree *dynamicTree = GetTree(tree->spatialIndex.dynamicIndex);
if(dynamicTree){
dynamicTree->stamp++;
} else {
tree->stamp++;
}
}
//MARK: Pair/Thread Functions
static void
PairRecycle(cpBBTree *tree, Pair *pair)
{
// Share the pool of the master tree.
// TODO: would be lovely to move the pairs stuff into an external data structure.
tree = GetMasterTree(tree);
pair->a.next = tree->pooledPairs;
tree->pooledPairs = pair;
}
static Pair *
PairFromPool(cpBBTree *tree)
{
// Share the pool of the master tree.
// TODO: would be lovely to move the pairs stuff into an external data structure.
tree = GetMasterTree(tree);
Pair *pair = tree->pooledPairs;
if(pair){
tree->pooledPairs = pair->a.next;
return pair;
} else {
// Pool is exhausted, make more
int count = CP_BUFFER_BYTES/sizeof(Pair);
cpAssertHard(count, "Internal Error: Buffer size is too small.");
Pair *buffer = (Pair *)cpcalloc(1, CP_BUFFER_BYTES);
cpArrayPush(tree->allocatedBuffers, buffer);
// push all but the first one, return the first instead
for(int i=1; i<count; i++) PairRecycle(tree, buffer + i);
return buffer;
}
}
static inline void
ThreadUnlink(Thread thread)
{
Pair *next = thread.next;
Pair *prev = thread.prev;
if(next){
if(next->a.leaf == thread.leaf) next->a.prev = prev; else next->b.prev = prev;
}
if(prev){
if(prev->a.leaf == thread.leaf) prev->a.next = next; else prev->b.next = next;
} else {
thread.leaf->PAIRS = next;
}
}
static void
PairsClear(Node *leaf, cpBBTree *tree)
{
Pair *pair = leaf->PAIRS;
leaf->PAIRS = NULL;
while(pair){
if(pair->a.leaf == leaf){
Pair *next = pair->a.next;
ThreadUnlink(pair->b);
PairRecycle(tree, pair);
pair = next;
} else {
Pair *next = pair->b.next;
ThreadUnlink(pair->a);
PairRecycle(tree, pair);
pair = next;
}
}
}
static void
PairInsert(Node *a, Node *b, cpBBTree *tree)
{
Pair *nextA = a->PAIRS, *nextB = b->PAIRS;
Pair *pair = PairFromPool(tree);
Pair temp = {{NULL, a, nextA},{NULL, b, nextB}, 0};
a->PAIRS = b->PAIRS = pair;
*pair = temp;
if(nextA){
if(nextA->a.leaf == a) nextA->a.prev = pair; else nextA->b.prev = pair;
}
if(nextB){
if(nextB->a.leaf == b) nextB->a.prev = pair; else nextB->b.prev = pair;
}
}
//MARK: Node Functions
static void
NodeRecycle(cpBBTree *tree, Node *node)
{
node->parent = tree->pooledNodes;
tree->pooledNodes = node;
}
static Node *
NodeFromPool(cpBBTree *tree)
{
Node *node = tree->pooledNodes;
if(node){
tree->pooledNodes = node->parent;
return node;
} else {
// Pool is exhausted, make more
int count = CP_BUFFER_BYTES/sizeof(Node);
cpAssertHard(count, "Internal Error: Buffer size is too small.");
Node *buffer = (Node *)cpcalloc(1, CP_BUFFER_BYTES);
cpArrayPush(tree->allocatedBuffers, buffer);
// push all but the first one, return the first instead
for(int i=1; i<count; i++) NodeRecycle(tree, buffer + i);
return buffer;
}
}
static inline void
NodeSetA(Node *node, Node *value)
{
node->A = value;
value->parent = node;
}
static inline void
NodeSetB(Node *node, Node *value)
{
node->B = value;
value->parent = node;
}
static Node *
NodeNew(cpBBTree *tree, Node *a, Node *b)
{
Node *node = NodeFromPool(tree);
node->obj = NULL;
node->bb = cpBBMerge(a->bb, b->bb);
node->parent = NULL;
NodeSetA(node, a);
NodeSetB(node, b);
return node;
}
static inline cpBool
NodeIsLeaf(Node *node)
{
return (node->obj != NULL);
}
static inline Node *
NodeOther(Node *node, Node *child)
{
return (node->A == child ? node->B : node->A);
}
static inline void
NodeReplaceChild(Node *parent, Node *child, Node *value, cpBBTree *tree)
{
cpAssertSoft(!NodeIsLeaf(parent), "Internal Error: Cannot replace child of a leaf.");
cpAssertSoft(child == parent->A || child == parent->B, "Internal Error: Node is not a child of parent.");
if(parent->A == child){
NodeRecycle(tree, parent->A);
NodeSetA(parent, value);
} else {
NodeRecycle(tree, parent->B);
NodeSetB(parent, value);
}
for(Node *node=parent; node; node = node->parent){
node->bb = cpBBMerge(node->A->bb, node->B->bb);
}
}
//MARK: Subtree Functions
static inline cpFloat
cpBBProximity(cpBB a, cpBB b)
{
return cpfabs(a.l + a.r - b.l - b.r) + cpfabs(a.b + a.t - b.b - b.t);
}
static Node *
SubtreeInsert(Node *subtree, Node *leaf, cpBBTree *tree)
{
if(subtree == NULL){
return leaf;
} else if(NodeIsLeaf(subtree)){
return NodeNew(tree, leaf, subtree);
} else {
cpFloat cost_a = cpBBArea(subtree->B->bb) + cpBBMergedArea(subtree->A->bb, leaf->bb);
cpFloat cost_b = cpBBArea(subtree->A->bb) + cpBBMergedArea(subtree->B->bb, leaf->bb);
if(cost_a == cost_b){
cost_a = cpBBProximity(subtree->A->bb, leaf->bb);
cost_b = cpBBProximity(subtree->B->bb, leaf->bb);
}
if(cost_b < cost_a){
NodeSetB(subtree, SubtreeInsert(subtree->B, leaf, tree));
} else {
NodeSetA(subtree, SubtreeInsert(subtree->A, leaf, tree));
}
subtree->bb = cpBBMerge(subtree->bb, leaf->bb);
return subtree;
}
}
static void
SubtreeQuery(Node *subtree, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data)
{
if(cpBBIntersects(subtree->bb, bb)){
if(NodeIsLeaf(subtree)){
func(obj, subtree->obj, 0, data);
} else {
SubtreeQuery(subtree->A, obj, bb, func, data);
SubtreeQuery(subtree->B, obj, bb, func, data);
}
}
}
static cpFloat
SubtreeSegmentQuery(Node *subtree, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data)
{
if(NodeIsLeaf(subtree)){
return func(obj, subtree->obj, data);
} else {
cpFloat t_a = cpBBSegmentQuery(subtree->A->bb, a, b);
cpFloat t_b = cpBBSegmentQuery(subtree->B->bb, a, b);
if(t_a < t_b){
if(t_a < t_exit) t_exit = cpfmin(t_exit, SubtreeSegmentQuery(subtree->A, obj, a, b, t_exit, func, data));
if(t_b < t_exit) t_exit = cpfmin(t_exit, SubtreeSegmentQuery(subtree->B, obj, a, b, t_exit, func, data));
} else {
if(t_b < t_exit) t_exit = cpfmin(t_exit, SubtreeSegmentQuery(subtree->B, obj, a, b, t_exit, func, data));
if(t_a < t_exit) t_exit = cpfmin(t_exit, SubtreeSegmentQuery(subtree->A, obj, a, b, t_exit, func, data));
}
return t_exit;
}
}
static void
SubtreeRecycle(cpBBTree *tree, Node *node)
{
if(!NodeIsLeaf(node)){
SubtreeRecycle(tree, node->A);
SubtreeRecycle(tree, node->B);
NodeRecycle(tree, node);
}
}
static inline Node *
SubtreeRemove(Node *subtree, Node *leaf, cpBBTree *tree)
{
if(leaf == subtree){
return NULL;
} else {
Node *parent = leaf->parent;
if(parent == subtree){
Node *other = NodeOther(subtree, leaf);
other->parent = subtree->parent;
NodeRecycle(tree, subtree);
return other;
} else {
NodeReplaceChild(parent->parent, parent, NodeOther(parent, leaf), tree);
return subtree;
}
}
}
//MARK: Marking Functions
typedef struct MarkContext {
cpBBTree *tree;
Node *staticRoot;
cpSpatialIndexQueryFunc func;
void *data;
} MarkContext;
static void
MarkLeafQuery(Node *subtree, Node *leaf, cpBool left, MarkContext *context)
{
if(cpBBIntersects(leaf->bb, subtree->bb)){
if(NodeIsLeaf(subtree)){
if(left){
PairInsert(leaf, subtree, context->tree);
} else {
if(subtree->STAMP < leaf->STAMP) PairInsert(subtree, leaf, context->tree);
context->func(leaf->obj, subtree->obj, 0, context->data);
}
} else {
MarkLeafQuery(subtree->A, leaf, left, context);
MarkLeafQuery(subtree->B, leaf, left, context);
}
}
}
static void
MarkLeaf(Node *leaf, MarkContext *context)
{
cpBBTree *tree = context->tree;
if(leaf->STAMP == GetMasterTree(tree)->stamp){
Node *staticRoot = context->staticRoot;
if(staticRoot) MarkLeafQuery(staticRoot, leaf, cpFalse, context);
for(Node *node = leaf; node->parent; node = node->parent){
if(node == node->parent->A){
MarkLeafQuery(node->parent->B, leaf, cpTrue, context);
} else {
MarkLeafQuery(node->parent->A, leaf, cpFalse, context);
}
}
} else {
Pair *pair = leaf->PAIRS;
while(pair){
if(leaf == pair->b.leaf){
pair->id = context->func(pair->a.leaf->obj, leaf->obj, pair->id, context->data);
pair = pair->b.next;
} else {
pair = pair->a.next;
}
}
}
}
static void
MarkSubtree(Node *subtree, MarkContext *context)
{
if(NodeIsLeaf(subtree)){
MarkLeaf(subtree, context);
} else {
MarkSubtree(subtree->A, context);
MarkSubtree(subtree->B, context); // TODO: Force TCO here?
}
}
//MARK: Leaf Functions
static Node *
LeafNew(cpBBTree *tree, void *obj, cpBB bb)
{
Node *node = NodeFromPool(tree);
node->obj = obj;
node->bb = GetBB(tree, obj);
node->parent = NULL;
node->STAMP = 0;
node->PAIRS = NULL;
return node;
}
static cpBool
LeafUpdate(Node *leaf, cpBBTree *tree)
{
Node *root = tree->root;
cpBB bb = tree->spatialIndex.bbfunc(leaf->obj);
if(!cpBBContainsBB(leaf->bb, bb)){
leaf->bb = GetBB(tree, leaf->obj);
root = SubtreeRemove(root, leaf, tree);
tree->root = SubtreeInsert(root, leaf, tree);
PairsClear(leaf, tree);
leaf->STAMP = GetMasterTree(tree)->stamp;
return cpTrue;
} else {
return cpFalse;
}
}
static cpCollisionID VoidQueryFunc(void *obj1, void *obj2, cpCollisionID id, void *data){return id;}
static void
LeafAddPairs(Node *leaf, cpBBTree *tree)
{
cpSpatialIndex *dynamicIndex = tree->spatialIndex.dynamicIndex;
if(dynamicIndex){
Node *dynamicRoot = GetRootIfTree(dynamicIndex);
if(dynamicRoot){
cpBBTree *dynamicTree = GetTree(dynamicIndex);
MarkContext context = {dynamicTree, NULL, NULL, NULL};
MarkLeafQuery(dynamicRoot, leaf, cpTrue, &context);
}
} else {
Node *staticRoot = GetRootIfTree(tree->spatialIndex.staticIndex);
MarkContext context = {tree, staticRoot, VoidQueryFunc, NULL};
MarkLeaf(leaf, &context);
}
}
//MARK: Memory Management Functions
cpBBTree *
cpBBTreeAlloc(void)
{
return (cpBBTree *)cpcalloc(1, sizeof(cpBBTree));
}
static int
leafSetEql(void *obj, Node *node)
{
return (obj == node->obj);
}
static void *
leafSetTrans(void *obj, cpBBTree *tree)
{
return LeafNew(tree, obj, tree->spatialIndex.bbfunc(obj));
}
cpSpatialIndex *
cpBBTreeInit(cpBBTree *tree, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex)
{
cpSpatialIndexInit((cpSpatialIndex *)tree, Klass(), bbfunc, staticIndex);
tree->velocityFunc = NULL;
tree->leaves = cpHashSetNew(0, (cpHashSetEqlFunc)leafSetEql);
tree->root = NULL;
tree->pooledNodes = NULL;
tree->allocatedBuffers = cpArrayNew(0);
tree->stamp = 0;
return (cpSpatialIndex *)tree;
}
void
cpBBTreeSetVelocityFunc(cpSpatialIndex *index, cpBBTreeVelocityFunc func)
{
if(index->klass != Klass()){
cpAssertWarn(cpFalse, "Ignoring cpBBTreeSetVelocityFunc() call to non-tree spatial index.");
return;
}
((cpBBTree *)index)->velocityFunc = func;
}
cpSpatialIndex *
cpBBTreeNew(cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex)
{
return cpBBTreeInit(cpBBTreeAlloc(), bbfunc, staticIndex);
}
static void
cpBBTreeDestroy(cpBBTree *tree)
{
cpHashSetFree(tree->leaves);
if(tree->allocatedBuffers) cpArrayFreeEach(tree->allocatedBuffers, cpfree);
cpArrayFree(tree->allocatedBuffers);
}
//MARK: Insert/Remove
static void
cpBBTreeInsert(cpBBTree *tree, void *obj, cpHashValue hashid)
{
Node *leaf = (Node *)cpHashSetInsert(tree->leaves, hashid, obj, (cpHashSetTransFunc)leafSetTrans, tree);
Node *root = tree->root;
tree->root = SubtreeInsert(root, leaf, tree);
leaf->STAMP = GetMasterTree(tree)->stamp;
LeafAddPairs(leaf, tree);
IncrementStamp(tree);
}
static void
cpBBTreeRemove(cpBBTree *tree, void *obj, cpHashValue hashid)
{
Node *leaf = (Node *)cpHashSetRemove(tree->leaves, hashid, obj);
tree->root = SubtreeRemove(tree->root, leaf, tree);
PairsClear(leaf, tree);
NodeRecycle(tree, leaf);
}
static cpBool
cpBBTreeContains(cpBBTree *tree, void *obj, cpHashValue hashid)
{
return (cpHashSetFind(tree->leaves, hashid, obj) != NULL);
}
//MARK: Reindex
static void LeafUpdateWrap(Node *leaf, cpBBTree *tree) {LeafUpdate(leaf, tree);}
static void
cpBBTreeReindexQuery(cpBBTree *tree, cpSpatialIndexQueryFunc func, void *data)
{
if(!tree->root) return;
// LeafUpdate() may modify tree->root. Don't cache it.
cpHashSetEach(tree->leaves, (cpHashSetIteratorFunc)LeafUpdateWrap, tree);
cpSpatialIndex *staticIndex = tree->spatialIndex.staticIndex;
Node *staticRoot = (staticIndex && staticIndex->klass == Klass() ? ((cpBBTree *)staticIndex)->root : NULL);
MarkContext context = {tree, staticRoot, func, data};
MarkSubtree(tree->root, &context);
if(staticIndex && !staticRoot) cpSpatialIndexCollideStatic((cpSpatialIndex *)tree, staticIndex, func, data);
IncrementStamp(tree);
}
static void
cpBBTreeReindex(cpBBTree *tree)
{
cpBBTreeReindexQuery(tree, VoidQueryFunc, NULL);
}
static void
cpBBTreeReindexObject(cpBBTree *tree, void *obj, cpHashValue hashid)
{
Node *leaf = (Node *)cpHashSetFind(tree->leaves, hashid, obj);
if(leaf){
if(LeafUpdate(leaf, tree)) LeafAddPairs(leaf, tree);
IncrementStamp(tree);
}
}
//MARK: Query
static void
cpBBTreeSegmentQuery(cpBBTree *tree, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data)
{
Node *root = tree->root;
if(root) SubtreeSegmentQuery(root, obj, a, b, t_exit, func, data);
}
static void
cpBBTreeQuery(cpBBTree *tree, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data)
{
if(tree->root) SubtreeQuery(tree->root, obj, bb, func, data);
}
//MARK: Misc
static int
cpBBTreeCount(cpBBTree *tree)
{
return cpHashSetCount(tree->leaves);
}
typedef struct eachContext {
cpSpatialIndexIteratorFunc func;
void *data;
} eachContext;
static void each_helper(Node *node, eachContext *context){context->func(node->obj, context->data);}
static void
cpBBTreeEach(cpBBTree *tree, cpSpatialIndexIteratorFunc func, void *data)
{
eachContext context = {func, data};
cpHashSetEach(tree->leaves, (cpHashSetIteratorFunc)each_helper, &context);
}
static cpSpatialIndexClass klass = {
(cpSpatialIndexDestroyImpl)cpBBTreeDestroy,
(cpSpatialIndexCountImpl)cpBBTreeCount,
(cpSpatialIndexEachImpl)cpBBTreeEach,
(cpSpatialIndexContainsImpl)cpBBTreeContains,
(cpSpatialIndexInsertImpl)cpBBTreeInsert,
(cpSpatialIndexRemoveImpl)cpBBTreeRemove,
(cpSpatialIndexReindexImpl)cpBBTreeReindex,
(cpSpatialIndexReindexObjectImpl)cpBBTreeReindexObject,
(cpSpatialIndexReindexQueryImpl)cpBBTreeReindexQuery,
(cpSpatialIndexQueryImpl)cpBBTreeQuery,
(cpSpatialIndexSegmentQueryImpl)cpBBTreeSegmentQuery,
};
static inline cpSpatialIndexClass *Klass(){return &klass;}
//MARK: Tree Optimization
static int
cpfcompare(const cpFloat *a, const cpFloat *b){
return (*a < *b ? -1 : (*b < *a ? 1 : 0));
}
static void
fillNodeArray(Node *node, Node ***cursor){
(**cursor) = node;
(*cursor)++;
}
static Node *
partitionNodes(cpBBTree *tree, Node **nodes, int count)
{
if(count == 1){
return nodes[0];
} else if(count == 2) {
return NodeNew(tree, nodes[0], nodes[1]);
}
// Find the AABB for these nodes
cpBB bb = nodes[0]->bb;
for(int i=1; i<count; i++) bb = cpBBMerge(bb, nodes[i]->bb);
// Split it on it's longest axis
cpBool splitWidth = (bb.r - bb.l > bb.t - bb.b);
// Sort the bounds and use the median as the splitting point
cpFloat *bounds = (cpFloat *)cpcalloc(count*2, sizeof(cpFloat));
if(splitWidth){
for(int i=0; i<count; i++){
bounds[2*i + 0] = nodes[i]->bb.l;
bounds[2*i + 1] = nodes[i]->bb.r;
}
} else {
for(int i=0; i<count; i++){
bounds[2*i + 0] = nodes[i]->bb.b;
bounds[2*i + 1] = nodes[i]->bb.t;
}
}
qsort(bounds, count*2, sizeof(cpFloat), (int (*)(const void *, const void *))cpfcompare);
cpFloat split = (bounds[count - 1] + bounds[count])*0.5f; // use the medain as the split
cpfree(bounds);
// Generate the child BBs
cpBB a = bb, b = bb;
if(splitWidth) a.r = b.l = split; else a.t = b.b = split;
// Partition the nodes
int right = count;
for(int left=0; left < right;){
Node *node = nodes[left];
if(cpBBMergedArea(node->bb, b) < cpBBMergedArea(node->bb, a)){
// if(cpBBProximity(node->bb, b) < cpBBProximity(node->bb, a)){
right--;
nodes[left] = nodes[right];
nodes[right] = node;
} else {
left++;
}
}
if(right == count){
Node *node = NULL;
for(int i=0; i<count; i++) node = SubtreeInsert(node, nodes[i], tree);
return node;
}
// Recurse and build the node!
return NodeNew(tree,
partitionNodes(tree, nodes, right),
partitionNodes(tree, nodes + right, count - right)
);
}
//static void
//cpBBTreeOptimizeIncremental(cpBBTree *tree, int passes)
//{
// for(int i=0; i<passes; i++){
// Node *root = tree->root;
// Node *node = root;
// int bit = 0;
// unsigned int path = tree->opath;
//
// while(!NodeIsLeaf(node)){
// node = (path&(1<<bit) ? node->a : node->b);
// bit = (bit + 1)&(sizeof(unsigned int)*8 - 1);
// }
//
// root = subtreeRemove(root, node, tree);
// tree->root = subtreeInsert(root, node, tree);
// }
//}
void
cpBBTreeOptimize(cpSpatialIndex *index)
{
if(index->klass != &klass){
cpAssertWarn(cpFalse, "Ignoring cpBBTreeOptimize() call to non-tree spatial index.");
return;
}
cpBBTree *tree = (cpBBTree *)index;
Node *root = tree->root;
if(!root) return;
int count = cpBBTreeCount(tree);
Node **nodes = (Node **)cpcalloc(count, sizeof(Node *));
Node **cursor = nodes;
cpHashSetEach(tree->leaves, (cpHashSetIteratorFunc)fillNodeArray, &cursor);
SubtreeRecycle(tree, root);
tree->root = partitionNodes(tree, nodes, count);
cpfree(nodes);
}
//MARK: Debug Draw
//#define CP_BBTREE_DEBUG_DRAW
#ifdef CP_BBTREE_DEBUG_DRAW
#include "OpenGL/gl.h"
#include "OpenGL/glu.h"
#include <GLUT/glut.h>
static void
NodeRender(Node *node, int depth)
{
if(!NodeIsLeaf(node) && depth <= 10){
NodeRender(node->a, depth + 1);
NodeRender(node->b, depth + 1);
}
cpBB bb = node->bb;
// GLfloat v = depth/2.0f;
// glColor3f(1.0f - v, v, 0.0f);
glLineWidth(cpfmax(5.0f - depth, 1.0f));
glBegin(GL_LINES); {
glVertex2f(bb.l, bb.b);
glVertex2f(bb.l, bb.t);
glVertex2f(bb.l, bb.t);
glVertex2f(bb.r, bb.t);
glVertex2f(bb.r, bb.t);
glVertex2f(bb.r, bb.b);
glVertex2f(bb.r, bb.b);
glVertex2f(bb.l, bb.b);
}; glEnd();
}
void
cpBBTreeRenderDebug(cpSpatialIndex *index){
if(index->klass != &klass){
cpAssertWarn(cpFalse, "Ignoring cpBBTreeRenderDebug() call to non-tree spatial index.");
return;
}
cpBBTree *tree = (cpBBTree *)index;
if(tree->root) NodeRender(tree->root, 0);
}
#endif

View File

@ -1,626 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <float.h>
#include <stdarg.h>
#include "chipmunk/chipmunk_private.h"
cpBody*
cpBodyAlloc(void)
{
return (cpBody *)cpcalloc(1, sizeof(cpBody));
}
cpBody *
cpBodyInit(cpBody *body, cpFloat mass, cpFloat moment)
{
body->space = NULL;
body->shapeList = NULL;
body->arbiterList = NULL;
body->constraintList = NULL;
body->velocity_func = cpBodyUpdateVelocity;
body->position_func = cpBodyUpdatePosition;
body->sleeping.root = NULL;
body->sleeping.next = NULL;
body->sleeping.idleTime = 0.0f;
body->p = cpvzero;
body->v = cpvzero;
body->f = cpvzero;
body->w = 0.0f;
body->t = 0.0f;
body->v_bias = cpvzero;
body->w_bias = 0.0f;
body->userData = NULL;
// Setters must be called after full initialization so the sanity checks don't assert on garbage data.
cpBodySetMass(body, mass);
cpBodySetMoment(body, moment);
cpBodySetAngle(body, 0.0f);
return body;
}
cpBody*
cpBodyNew(cpFloat mass, cpFloat moment)
{
return cpBodyInit(cpBodyAlloc(), mass, moment);
}
cpBody*
cpBodyNewKinematic()
{
cpBody *body = cpBodyNew(0.0f, 0.0f);
cpBodySetType(body, CP_BODY_TYPE_KINEMATIC);
return body;
}
cpBody*
cpBodyNewStatic()
{
cpBody *body = cpBodyNew(0.0f, 0.0f);
cpBodySetType(body, CP_BODY_TYPE_STATIC);
return body;
}
void cpBodyDestroy(cpBody *body){}
void
cpBodyFree(cpBody *body)
{
if(body){
cpBodyDestroy(body);
cpfree(body);
}
}
#ifdef NDEBUG
#define cpAssertSaneBody(body)
#else
static void cpv_assert_nan(cpVect v, char *message){cpAssertHard(v.x == v.x && v.y == v.y, message);}
static void cpv_assert_infinite(cpVect v, char *message){cpAssertHard(cpfabs(v.x) != INFINITY && cpfabs(v.y) != INFINITY, message);}
static void cpv_assert_sane(cpVect v, char *message){cpv_assert_nan(v, message); cpv_assert_infinite(v, message);}
static void
cpBodySanityCheck(const cpBody *body)
{
cpAssertHard(body->m == body->m && body->m_inv == body->m_inv, "Body's mass is NaN.");
cpAssertHard(body->i == body->i && body->i_inv == body->i_inv, "Body's moment is NaN.");
cpAssertHard(body->m >= 0.0f, "Body's mass is negative.");
cpAssertHard(body->i >= 0.0f, "Body's moment is negative.");
cpv_assert_sane(body->p, "Body's position is invalid.");
cpv_assert_sane(body->v, "Body's velocity is invalid.");
cpv_assert_sane(body->f, "Body's force is invalid.");
cpAssertHard(body->a == body->a && cpfabs(body->a) != INFINITY, "Body's angle is invalid.");
cpAssertHard(body->w == body->w && cpfabs(body->w) != INFINITY, "Body's angular velocity is invalid.");
cpAssertHard(body->t == body->t && cpfabs(body->t) != INFINITY, "Body's torque is invalid.");
}
#define cpAssertSaneBody(body) cpBodySanityCheck(body)
#endif
cpBool
cpBodyIsSleeping(const cpBody *body)
{
return (body->sleeping.root != ((cpBody*)0));
}
cpBodyType
cpBodyGetType(cpBody *body)
{
if(body->sleeping.idleTime == INFINITY){
return CP_BODY_TYPE_STATIC;
} else if(body->m == INFINITY){
return CP_BODY_TYPE_KINEMATIC;
} else {
return CP_BODY_TYPE_DYNAMIC;
}
}
void
cpBodySetType(cpBody *body, cpBodyType type)
{
cpBodyType oldType = cpBodyGetType(body);
if(oldType == type) return;
// Static bodies have their idle timers set to infinity.
// Non-static bodies should have their idle timer reset.
body->sleeping.idleTime = (type == CP_BODY_TYPE_STATIC ? INFINITY : 0.0f);
if(type == CP_BODY_TYPE_DYNAMIC){
body->m = body->i = 0.0f;
body->m_inv = body->i_inv = INFINITY;
cpBodyAccumulateMassFromShapes(body);
} else {
body->m = body->i = INFINITY;
body->m_inv = body->i_inv = 0.0f;
body->v = cpvzero;
body->w = 0.0f;
}
// If the body is added to a space already, we'll need to update some space data structures.
cpSpace *space = cpBodyGetSpace(body);
if(space != NULL){
cpAssertSpaceUnlocked(space);
if(oldType == CP_BODY_TYPE_STATIC){
// TODO This is probably not necessary
// cpBodyActivateStatic(body, NULL);
} else {
cpBodyActivate(body);
}
// Move the bodies to the correct array.
cpArray *fromArray = cpSpaceArrayForBodyType(space, oldType);
cpArray *toArray = cpSpaceArrayForBodyType(space, type);
if(fromArray != toArray){
cpArrayDeleteObj(fromArray, body);
cpArrayPush(toArray, body);
}
// Move the body's shapes to the correct spatial index.
cpSpatialIndex *fromIndex = (oldType == CP_BODY_TYPE_STATIC ? space->staticShapes : space->dynamicShapes);
cpSpatialIndex *toIndex = (type == CP_BODY_TYPE_STATIC ? space->staticShapes : space->dynamicShapes);
if(fromIndex != toIndex){
CP_BODY_FOREACH_SHAPE(body, shape){
cpSpatialIndexRemove(fromIndex, shape, shape->hashid);
cpSpatialIndexInsert(toIndex, shape, shape->hashid);
}
}
}
}
// Should *only* be called when shapes with mass info are modified, added or removed.
void
cpBodyAccumulateMassFromShapes(cpBody *body)
{
if(body == NULL || cpBodyGetType(body) != CP_BODY_TYPE_DYNAMIC) return;
// Reset the body's mass data.
body->m = body->i = 0.0f;
body->cog = cpvzero;
// Cache the position to realign it at the end.
cpVect pos = cpBodyGetPosition(body);
// Accumulate mass from shapes.
CP_BODY_FOREACH_SHAPE(body, shape){
struct cpShapeMassInfo *info = &shape->massInfo;
cpFloat m = info->m;
if(m > 0.0f){
cpFloat msum = body->m + m;
body->i += m*info->i + cpvdistsq(body->cog, info->cog)*(m*body->m)/msum;
body->cog = cpvlerp(body->cog, info->cog, m/msum);
body->m = msum;
}
}
// Recalculate the inverses.
body->m_inv = 1.0f/body->m;
body->i_inv = 1.0f/body->i;
// Realign the body since the CoG has probably moved.
cpBodySetPosition(body, pos);
cpAssertSaneBody(body);
}
cpSpace *
cpBodyGetSpace(const cpBody *body)
{
return body->space;
}
cpFloat
cpBodyGetMass(const cpBody *body)
{
return body->m;
}
void
cpBodySetMass(cpBody *body, cpFloat mass)
{
cpAssertHard(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC, "You cannot set the mass of kinematic or static bodies.");
cpAssertHard(0.0f <= mass && mass < INFINITY, "Mass must be positive and finite.");
cpBodyActivate(body);
body->m = mass;
body->m_inv = 1.0f/mass;
cpAssertSaneBody(body);
}
cpFloat
cpBodyGetMoment(const cpBody *body)
{
return body->i;
}
void
cpBodySetMoment(cpBody *body, cpFloat moment)
{
cpAssertHard(moment >= 0.0f, "Moment of Inertia must be positive.");
cpBodyActivate(body);
body->i = moment;
body->i_inv = 1.0f/moment;
cpAssertSaneBody(body);
}
cpVect
cpBodyGetRotation(const cpBody *body)
{
return cpv(body->transform.a, body->transform.b);
}
void
cpBodyAddShape(cpBody *body, cpShape *shape)
{
cpShape *next = body->shapeList;
if(next) next->prev = shape;
shape->next = next;
body->shapeList = shape;
if(shape->massInfo.m > 0.0f){
cpBodyAccumulateMassFromShapes(body);
}
}
void
cpBodyRemoveShape(cpBody *body, cpShape *shape)
{
cpShape *prev = shape->prev;
cpShape *next = shape->next;
if(prev){
prev->next = next;
} else {
body->shapeList = next;
}
if(next){
next->prev = prev;
}
shape->prev = NULL;
shape->next = NULL;
if(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC && shape->massInfo.m > 0.0f){
cpBodyAccumulateMassFromShapes(body);
}
}
static cpConstraint *
filterConstraints(cpConstraint *node, cpBody *body, cpConstraint *filter)
{
if(node == filter){
return cpConstraintNext(node, 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;
}
void
cpBodyRemoveConstraint(cpBody *body, cpConstraint *constraint)
{
body->constraintList = filterConstraints(body->constraintList, body, constraint);
}
// 'p' is the position of the CoG
static void
SetTransform(cpBody *body, cpVect p, cpFloat a)
{
cpVect rot = cpvforangle(a);
cpVect c = body->cog;
body->transform = cpTransformNewTranspose(
rot.x, -rot.y, p.x - (c.x*rot.x - c.y*rot.y),
rot.y, rot.x, p.y - (c.x*rot.y + c.y*rot.x)
);
}
static inline cpFloat
SetAngle(cpBody *body, cpFloat a)
{
body->a = a;
cpAssertSaneBody(body);
return a;
}
cpVect
cpBodyGetPosition(const cpBody *body)
{
return cpTransformPoint(body->transform, cpvzero);
}
void
cpBodySetPosition(cpBody *body, cpVect position)
{
cpBodyActivate(body);
cpVect p = body->p = cpvadd(cpTransformVect(body->transform, body->cog), position);
cpAssertSaneBody(body);
SetTransform(body, p, body->a);
}
cpVect
cpBodyGetCenterOfGravity(const cpBody *body)
{
return body->cog;
}
void
cpBodySetCenterOfGravity(cpBody *body, cpVect cog)
{
cpBodyActivate(body);
body->cog = cog;
cpAssertSaneBody(body);
}
cpVect
cpBodyGetVelocity(const cpBody *body)
{
return body->v;
}
void
cpBodySetVelocity(cpBody *body, cpVect velocity)
{
cpBodyActivate(body);
body->v = velocity;
cpAssertSaneBody(body);
}
cpVect
cpBodyGetForce(const cpBody *body)
{
return body->f;
}
void
cpBodySetForce(cpBody *body, cpVect force)
{
cpBodyActivate(body);
body->f = force;
cpAssertSaneBody(body);
}
cpFloat
cpBodyGetAngle(const cpBody *body)
{
return body->a;
}
void
cpBodySetAngle(cpBody *body, cpFloat angle)
{
cpBodyActivate(body);
SetAngle(body, angle);
SetTransform(body, body->p, angle);
}
cpFloat
cpBodyGetAngularVelocity(const cpBody *body)
{
return body->w;
}
void
cpBodySetAngularVelocity(cpBody *body, cpFloat angularVelocity)
{
cpBodyActivate(body);
body->w = angularVelocity;
cpAssertSaneBody(body);
}
cpFloat
cpBodyGetTorque(const cpBody *body)
{
return body->t;
}
void
cpBodySetTorque(cpBody *body, cpFloat torque)
{
cpBodyActivate(body);
body->t = torque;
cpAssertSaneBody(body);
}
cpDataPointer
cpBodyGetUserData(const cpBody *body)
{
return body->userData;
}
void
cpBodySetUserData(cpBody *body, cpDataPointer userData)
{
body->userData = userData;
}
void
cpBodySetVelocityUpdateFunc(cpBody *body, cpBodyVelocityFunc velocityFunc)
{
body->velocity_func = velocityFunc;
}
void
cpBodySetPositionUpdateFunc(cpBody *body, cpBodyPositionFunc positionFunc)
{
body->position_func = positionFunc;
}
void
cpBodyUpdateVelocity(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt)
{
// Skip kinematic bodies.
if(cpBodyGetType(body) == CP_BODY_TYPE_KINEMATIC) return;
cpAssertSoft(body->m > 0.0f && body->i > 0.0f, "Body's mass and moment must be positive to simulate. (Mass: %f Moment: %f)", body->m, body->i);
body->v = cpvadd(cpvmult(body->v, damping), cpvmult(cpvadd(gravity, cpvmult(body->f, body->m_inv)), dt));
body->w = body->w*damping + body->t*body->i_inv*dt;
// Reset forces.
body->f = cpvzero;
body->t = 0.0f;
cpAssertSaneBody(body);
}
void
cpBodyUpdatePosition(cpBody *body, cpFloat dt)
{
cpVect p = body->p = cpvadd(body->p, cpvmult(cpvadd(body->v, body->v_bias), dt));
cpFloat a = SetAngle(body, body->a + (body->w + body->w_bias)*dt);
SetTransform(body, p, a);
body->v_bias = cpvzero;
body->w_bias = 0.0f;
cpAssertSaneBody(body);
}
cpVect
cpBodyLocalToWorld(const cpBody *body, const cpVect point)
{
return cpTransformPoint(body->transform, point);
}
cpVect
cpBodyWorldToLocal(const cpBody *body, const cpVect point)
{
return cpTransformPoint(cpTransformRigidInverse(body->transform), point);
}
void
cpBodyApplyForceAtWorldPoint(cpBody *body, cpVect force, cpVect point)
{
cpBodyActivate(body);
body->f = cpvadd(body->f, force);
cpVect r = cpvsub(point, cpTransformPoint(body->transform, body->cog));
body->t += cpvcross(r, force);
}
void
cpBodyApplyForceAtLocalPoint(cpBody *body, cpVect force, cpVect point)
{
cpBodyApplyForceAtWorldPoint(body, cpTransformVect(body->transform, force), cpTransformPoint(body->transform, point));
}
void
cpBodyApplyImpulseAtWorldPoint(cpBody *body, cpVect impulse, cpVect point)
{
cpBodyActivate(body);
cpVect r = cpvsub(point, cpTransformPoint(body->transform, body->cog));
apply_impulse(body, impulse, r);
}
void
cpBodyApplyImpulseAtLocalPoint(cpBody *body, cpVect impulse, cpVect point)
{
cpBodyApplyImpulseAtWorldPoint(body, cpTransformVect(body->transform, impulse), cpTransformPoint(body->transform, point));
}
cpVect
cpBodyGetVelocityAtLocalPoint(const cpBody *body, cpVect point)
{
cpVect r = cpTransformVect(body->transform, cpvsub(point, body->cog));
return cpvadd(body->v, cpvmult(cpvperp(r), body->w));
}
cpVect
cpBodyGetVelocityAtWorldPoint(const cpBody *body, cpVect point)
{
cpVect r = cpvsub(point, cpTransformPoint(body->transform, body->cog));
return cpvadd(body->v, cpvmult(cpvperp(r), body->w));
}
cpFloat
cpBodyKineticEnergy(const cpBody *body)
{
// Need to do some fudging to avoid NaNs
cpFloat vsq = cpvdot(body->v, body->v);
cpFloat wsq = body->w*body->w;
return (vsq ? vsq*body->m : 0.0f) + (wsq ? wsq*body->i : 0.0f);
}
void
cpBodyEachShape(cpBody *body, cpBodyShapeIteratorFunc func, void *data)
{
cpShape *shape = body->shapeList;
while(shape){
cpShape *next = shape->next;
func(body, shape, data);
shape = next;
}
}
void
cpBodyEachConstraint(cpBody *body, cpBodyConstraintIteratorFunc func, void *data)
{
cpConstraint *constraint = body->constraintList;
while(constraint){
cpConstraint *next = cpConstraintNext(constraint, body);
func(body, constraint, data);
constraint = next;
}
}
void
cpBodyEachArbiter(cpBody *body, cpBodyArbiterIteratorFunc func, void *data)
{
cpArbiter *arb = body->arbiterList;
while(arb){
cpArbiter *next = cpArbiterNext(arb, body);
cpBool swapped = arb->swapped; {
arb->swapped = (body == arb->body_b);
func(body, arb, data);
} arb->swapped = swapped;
arb = next;
}
}

View File

@ -1,734 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdio.h>
#include <string.h>
#include "chipmunk/chipmunk_private.h"
#if DEBUG && 0
#include "ChipmunkDemo.h"
#define DRAW_ALL 0
#define DRAW_GJK (0 || DRAW_ALL)
#define DRAW_EPA (0 || DRAW_ALL)
#define DRAW_CLOSEST (0 || DRAW_ALL)
#define DRAW_CLIP (0 || DRAW_ALL)
#define PRINT_LOG 0
#endif
#define ENABLE_CACHING 1
#define MAX_GJK_ITERATIONS 30
#define MAX_EPA_ITERATIONS 30
#define WARN_GJK_ITERATIONS 20
#define WARN_EPA_ITERATIONS 20
static inline void
cpCollisionInfoPushContact(struct cpCollisionInfo *info, cpVect p1, cpVect p2, cpHashValue hash)
{
cpAssertSoft(info->count <= CP_MAX_CONTACTS_PER_ARBITER, "Internal error: Tried to push too many contacts.");
struct cpContact *con = &info->arr[info->count];
con->r1 = p1;
con->r2 = p2;
con->hash = hash;
info->count++;
}
//MARK: Support Points and Edges:
// Support points are the maximal points on a shape's perimeter along a certain axis.
// The GJK and EPA algorithms use support points to iteratively sample the surface of the two shapes' minkowski difference.
static inline int
PolySupportPointIndex(const int count, const struct cpSplittingPlane *planes, const cpVect n)
{
cpFloat max = -INFINITY;
int index = 0;
for(int i=0; i<count; i++){
cpVect v = planes[i].v0;
cpFloat d = cpvdot(v, n);
if(d > max){
max = d;
index = i;
}
}
return index;
}
struct SupportPoint {
cpVect p;
// Save an index of the point so it can be cheaply looked up as a starting point for the next frame.
cpCollisionID index;
};
static inline struct SupportPoint
SupportPointNew(cpVect p, cpCollisionID index)
{
struct SupportPoint point = {p, index};
return point;
}
typedef struct SupportPoint (*SupportPointFunc)(const cpShape *shape, const cpVect n);
static inline struct SupportPoint
CircleSupportPoint(const cpCircleShape *circle, const cpVect n)
{
return SupportPointNew(circle->tc, 0);
}
static inline struct SupportPoint
SegmentSupportPoint(const cpSegmentShape *seg, const cpVect n)
{
if(cpvdot(seg->ta, n) > cpvdot(seg->tb, n)){
return SupportPointNew(seg->ta, 0);
} else {
return SupportPointNew(seg->tb, 1);
}
}
static inline struct SupportPoint
PolySupportPoint(const cpPolyShape *poly, const cpVect n)
{
const struct cpSplittingPlane *planes = poly->planes;
int i = PolySupportPointIndex(poly->count, planes, n);
return SupportPointNew(planes[i].v0, i);
}
// A point on the surface of two shape's minkowski difference.
struct MinkowskiPoint {
// Cache the two original support points.
cpVect a, b;
// b - a
cpVect ab;
// Concatenate the two support point indexes.
cpCollisionID id;
};
static inline struct MinkowskiPoint
MinkowskiPointNew(const struct SupportPoint a, const struct SupportPoint b)
{
struct MinkowskiPoint point = {a.p, b.p, cpvsub(b.p, a.p), (a.index & 0xFF)<<8 | (b.index & 0xFF)};
return point;
}
struct SupportContext {
const cpShape *shape1, *shape2;
SupportPointFunc func1, func2;
};
// Calculate the maximal point on the minkowski difference of two shapes along a particular axis.
static inline struct MinkowskiPoint
Support(const struct SupportContext *ctx, const cpVect n)
{
struct SupportPoint a = ctx->func1(ctx->shape1, cpvneg(n));
struct SupportPoint b = ctx->func2(ctx->shape2, n);
return MinkowskiPointNew(a, b);
}
struct EdgePoint {
cpVect p;
// Keep a hash value for Chipmunk's collision hashing mechanism.
cpHashValue hash;
};
// Support edges are the edges of a polygon or segment shape that are in contact.
struct Edge {
struct EdgePoint a, b;
cpFloat r;
cpVect n;
};
static struct Edge
SupportEdgeForPoly(const cpPolyShape *poly, const cpVect n)
{
int count = poly->count;
int i1 = PolySupportPointIndex(poly->count, poly->planes, n);
// TODO: get rid of mod eventually, very expensive on ARM
int i0 = (i1 - 1 + count)%count;
int i2 = (i1 + 1)%count;
const struct cpSplittingPlane *planes = poly->planes;
cpHashValue hashid = poly->shape.hashid;
if(cpvdot(n, planes[i1].n) > cpvdot(n, planes[i2].n)){
struct Edge edge = {{planes[i0].v0, CP_HASH_PAIR(hashid, i0)}, {planes[i1].v0, CP_HASH_PAIR(hashid, i1)}, poly->r, planes[i1].n};
return edge;
} else {
struct Edge edge = {{planes[i1].v0, CP_HASH_PAIR(hashid, i1)}, {planes[i2].v0, CP_HASH_PAIR(hashid, i2)}, poly->r, planes[i2].n};
return edge;
}
}
static struct Edge
SupportEdgeForSegment(const cpSegmentShape *seg, const cpVect n)
{
cpHashValue hashid = seg->shape.hashid;
if(cpvdot(seg->tn, n) > 0.0){
struct Edge edge = {{seg->ta, CP_HASH_PAIR(hashid, 0)}, {seg->tb, CP_HASH_PAIR(hashid, 1)}, seg->r, seg->tn};
return edge;
} else {
struct Edge edge = {{seg->tb, CP_HASH_PAIR(hashid, 1)}, {seg->ta, CP_HASH_PAIR(hashid, 0)}, seg->r, cpvneg(seg->tn)};
return edge;
}
}
// Find the closest p(t) to (0, 0) where p(t) = a*(1-t)/2 + b*(1+t)/2
// The range for t is [-1, 1] to avoid floating point issues if the parameters are swapped.
static inline cpFloat
ClosestT(const cpVect a, const cpVect b)
{
cpVect delta = cpvsub(b, a);
return -cpfclamp(cpvdot(delta, cpvadd(a, b))/cpvlengthsq(delta), -1.0f, 1.0f);
}
// Basically the same as cpvlerp(), except t = [-1, 1]
static inline cpVect
LerpT(const cpVect a, const cpVect b, const cpFloat t)
{
cpFloat ht = 0.5f*t;
return cpvadd(cpvmult(a, 0.5f - ht), cpvmult(b, 0.5f + ht));
}
// Closest points on the surface of two shapes.
struct ClosestPoints {
// Surface points in absolute coordinates.
cpVect a, b;
// Minimum separating axis of the two shapes.
cpVect n;
// Signed distance between the points.
cpFloat d;
// Concatenation of the id's of the minkoski points.
cpCollisionID id;
};
// Calculate the closest points on two shapes given the closest edge on their minkowski difference to (0, 0)
static inline struct ClosestPoints
ClosestPointsNew(const struct MinkowskiPoint v0, const struct MinkowskiPoint v1)
{
// Find the closest p(t) on the minkowski difference to (0, 0)
cpFloat t = ClosestT(v0.ab, v1.ab);
cpVect p = LerpT(v0.ab, v1.ab, t);
// Interpolate the original support points using the same 't' value as above.
// This gives you the closest surface points in absolute coordinates. NEAT!
cpVect pa = LerpT(v0.a, v1.a, t);
cpVect pb = LerpT(v0.b, v1.b, t);
cpCollisionID id = (v0.id & 0xFFFF)<<16 | (v1.id & 0xFFFF);
// First try calculating the MSA from the minkowski difference edge.
// This gives us a nice, accurate MSA when the surfaces are close together.
cpVect delta = cpvsub(v1.ab, v0.ab);
cpVect n = cpvnormalize(cpvrperp(delta));
cpFloat d = cpvdot(n, p);
if(d <= 0.0f || (-1.0f < t && t < 1.0f)){
// If the shapes are overlapping, or we have a regular vertex/edge collision, we are done.
struct ClosestPoints points = {pa, pb, n, d, id};
return points;
} else {
// Vertex/vertex collisions need special treatment since the MSA won't be shared with an axis of the minkowski difference.
cpFloat d = cpvlength(p);
cpVect n = cpvmult(p, 1.0f/(d + CPFLOAT_MIN));
struct ClosestPoints points = {pa, pb, n, d, id};
return points;
}
}
//MARK: EPA Functions
static inline cpFloat
ClosestDist(const cpVect v0,const cpVect v1)
{
return cpvlengthsq(LerpT(v0, v1, ClosestT(v0, v1)));
}
static inline cpBool
CheckArea(cpVect v1, cpVect v2)
{
return (v1.x*v2.y) > (v1.y*v2.x);
}
// Recursive implementation of the EPA loop.
// Each recursion adds a point to the convex hull until it's known that we have the closest point on the surface.
static struct ClosestPoints
EPARecurse(const struct SupportContext *ctx, const int count, const struct MinkowskiPoint *hull, const int iteration)
{
int mini = 0;
cpFloat minDist = INFINITY;
// TODO: precalculate this when building the hull and save a step.
// Find the closest segment hull[i] and hull[i + 1] to (0, 0)
for(int j=0, i=count-1; j<count; i=j, j++){
cpFloat d = ClosestDist(hull[i].ab, hull[j].ab);
if(d < minDist){
minDist = d;
mini = i;
}
}
struct MinkowskiPoint v0 = hull[mini];
struct MinkowskiPoint v1 = hull[(mini + 1)%count];
cpAssertSoft(!cpveql(v0.ab, v1.ab), "Internal Error: EPA vertexes are the same (%d and %d)", mini, (mini + 1)%count);
// Check if there is a point on the minkowski difference beyond this edge.
struct MinkowskiPoint p = Support(ctx, cpvperp(cpvsub(v1.ab, v0.ab)));
#if DRAW_EPA
cpVect verts[count];
for(int i=0; i<count; i++) verts[i] = hull[i].ab;
ChipmunkDebugDrawPolygon(count, verts, 0.0, RGBAColor(1, 1, 0, 1), RGBAColor(1, 1, 0, 0.25));
ChipmunkDebugDrawSegment(v0.ab, v1.ab, RGBAColor(1, 0, 0, 1));
ChipmunkDebugDrawDot(5, p.ab, LAColor(1, 1));
#endif
if(CheckArea(cpvsub(v1.ab, v0.ab), cpvadd(cpvsub(p.ab, v0.ab), cpvsub(p.ab, v1.ab))) && iteration < MAX_EPA_ITERATIONS){
// Rebuild the convex hull by inserting p.
struct MinkowskiPoint *hull2 = (struct MinkowskiPoint *)alloca((count + 1)*sizeof(struct MinkowskiPoint));
int count2 = 1;
hull2[0] = p;
for(int i=0; i<count; i++){
int index = (mini + 1 + i)%count;
cpVect h0 = hull2[count2 - 1].ab;
cpVect h1 = hull[index].ab;
cpVect h2 = (i + 1 < count ? hull[(index + 1)%count] : p).ab;
if(CheckArea(cpvsub(h2, h0), cpvadd(cpvsub(h1, h0), cpvsub(h1, h2)))){
hull2[count2] = hull[index];
count2++;
}
}
return EPARecurse(ctx, count2, hull2, iteration + 1);
} else {
// Could not find a new point to insert, so we have found the closest edge of the minkowski difference.
cpAssertWarn(iteration < WARN_EPA_ITERATIONS, "High EPA iterations: %d", iteration);
return ClosestPointsNew(v0, v1);
}
}
// Find the closest points on the surface of two overlapping shapes using the EPA algorithm.
// EPA is called from GJK when two shapes overlap.
// This is moderately expensive step! Avoid it by adding radii to your shapes so their inner polygons won't overlap.
static struct ClosestPoints
EPA(const struct SupportContext *ctx, const struct MinkowskiPoint v0, const struct MinkowskiPoint v1, const struct MinkowskiPoint v2)
{
// TODO: allocate a NxM array here and do an in place convex hull reduction in EPARecurse
struct MinkowskiPoint hull[3] = {v0, v1, v2};
return EPARecurse(ctx, 3, hull, 1);
}
//MARK: GJK Functions.
// Recursive implementatino of the GJK loop.
static inline struct ClosestPoints
GJKRecurse(const struct SupportContext *ctx, const struct MinkowskiPoint v0, const struct MinkowskiPoint v1, const int iteration)
{
if(iteration > MAX_GJK_ITERATIONS){
cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK iterations: %d", iteration);
return ClosestPointsNew(v0, v1);
}
cpVect delta = cpvsub(v1.ab, v0.ab);
if(CheckArea(delta, cpvadd(v0.ab, v1.ab))){
// Origin is behind axis. Flip and try again.
return GJKRecurse(ctx, v1, v0, iteration);
} else {
cpFloat t = ClosestT(v0.ab, v1.ab);
cpVect n = (-1.0f < t && t < 1.0f ? cpvperp(delta) : cpvneg(LerpT(v0.ab, v1.ab, t)));
struct MinkowskiPoint p = Support(ctx, n);
#if DRAW_GJK
ChipmunkDebugDrawSegment(v0.ab, v1.ab, RGBAColor(1, 1, 1, 1));
cpVect c = cpvlerp(v0.ab, v1.ab, 0.5);
ChipmunkDebugDrawSegment(c, cpvadd(c, cpvmult(cpvnormalize(n), 5.0)), RGBAColor(1, 0, 0, 1));
ChipmunkDebugDrawDot(5.0, p.ab, LAColor(1, 1));
#endif
if(
CheckArea(cpvsub(v1.ab, p.ab), cpvadd(v1.ab, p.ab)) &&
CheckArea(cpvadd(v0.ab, p.ab), cpvsub(v0.ab, p.ab))
){
// The triangle v0, p, v1 contains the origin. Use EPA to find the MSA.
cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK->EPA iterations: %d", iteration);
return EPA(ctx, v0, p, v1);
} else {
if(cpvdot(p.ab, n) <= cpfmax(cpvdot(v0.ab, n), cpvdot(v1.ab, n))){
// The edge v0, v1 that we already have is the closest to (0, 0) since p was not closer.
cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK iterations: %d", iteration);
return ClosestPointsNew(v0, v1);
} else {
// p was closer to the origin than our existing edge.
// Need to figure out which existing point to drop.
if(ClosestDist(v0.ab, p.ab) < ClosestDist(p.ab, v1.ab)){
return GJKRecurse(ctx, v0, p, iteration + 1);
} else {
return GJKRecurse(ctx, p, v1, iteration + 1);
}
}
}
}
}
// Get a SupportPoint from a cached shape and index.
static struct SupportPoint
ShapePoint(const cpShape *shape, const int i)
{
switch(shape->klass->type){
case CP_CIRCLE_SHAPE: {
return SupportPointNew(((cpCircleShape *)shape)->tc, 0);
} case CP_SEGMENT_SHAPE: {
cpSegmentShape *seg = (cpSegmentShape *)shape;
return SupportPointNew(i == 0 ? seg->ta : seg->tb, i);
} case CP_POLY_SHAPE: {
cpPolyShape *poly = (cpPolyShape *)shape;
// Poly shapes may change vertex count.
int index = (i < poly->count ? i : 0);
return SupportPointNew(poly->planes[index].v0, index);
} default: {
return SupportPointNew(cpvzero, 0);
}
}
}
// Find the closest points between two shapes using the GJK algorithm.
static struct ClosestPoints
GJK(const struct SupportContext *ctx, cpCollisionID *id)
{
#if DRAW_GJK || DRAW_EPA
int count1 = 1;
int count2 = 1;
switch(ctx->shape1->klass->type){
case CP_SEGMENT_SHAPE: count1 = 2; break;
case CP_POLY_SHAPE: count1 = ((cpPolyShape *)ctx->shape1)->count; break;
default: break;
}
switch(ctx->shape2->klass->type){
case CP_SEGMENT_SHAPE: count1 = 2; break;
case CP_POLY_SHAPE: count2 = ((cpPolyShape *)ctx->shape2)->count; break;
default: break;
}
// draw the minkowski difference origin
cpVect origin = cpvzero;
ChipmunkDebugDrawDot(5.0, origin, RGBAColor(1,0,0,1));
int mdiffCount = count1*count2;
cpVect *mdiffVerts = alloca(mdiffCount*sizeof(cpVect));
for(int i=0; i<count1; i++){
for(int j=0; j<count2; j++){
cpVect v = cpvsub(ShapePoint(ctx->shape2, j).p, ShapePoint(ctx->shape1, i).p);
mdiffVerts[i*count2 + j] = v;
ChipmunkDebugDrawDot(2.0, v, RGBAColor(1, 0, 0, 1));
}
}
cpVect *hullVerts = alloca(mdiffCount*sizeof(cpVect));
int hullCount = cpConvexHull(mdiffCount, mdiffVerts, hullVerts, NULL, 0.0);
ChipmunkDebugDrawPolygon(hullCount, hullVerts, 0.0, RGBAColor(1, 0, 0, 1), RGBAColor(1, 0, 0, 0.25));
#endif
struct MinkowskiPoint v0, v1;
if(*id && ENABLE_CACHING){
// Use the minkowski points from the last frame as a starting point using the cached indexes.
v0 = MinkowskiPointNew(ShapePoint(ctx->shape1, (*id>>24)&0xFF), ShapePoint(ctx->shape2, (*id>>16)&0xFF));
v1 = MinkowskiPointNew(ShapePoint(ctx->shape1, (*id>> 8)&0xFF), ShapePoint(ctx->shape2, (*id )&0xFF));
} else {
// No cached indexes, use the shapes' bounding box centers as a guess for a starting axis.
cpVect axis = cpvperp(cpvsub(cpBBCenter(ctx->shape1->bb), cpBBCenter(ctx->shape2->bb)));
v0 = Support(ctx, axis);
v1 = Support(ctx, cpvneg(axis));
}
struct ClosestPoints points = GJKRecurse(ctx, v0, v1, 1);
*id = points.id;
return points;
}
//MARK: Contact Clipping
// Given two support edges, find contact point pairs on their surfaces.
static inline void
ContactPoints(const struct Edge e1, const struct Edge e2, const struct ClosestPoints points, struct cpCollisionInfo *info)
{
cpFloat mindist = e1.r + e2.r;
if(points.d <= mindist){
#ifdef DRAW_CLIP
ChipmunkDebugDrawFatSegment(e1.a.p, e1.b.p, e1.r, RGBAColor(0, 1, 0, 1), LAColor(0, 0));
ChipmunkDebugDrawFatSegment(e2.a.p, e2.b.p, e2.r, RGBAColor(1, 0, 0, 1), LAColor(0, 0));
#endif
cpVect n = info->n = points.n;
// Distances along the axis parallel to n
cpFloat d_e1_a = cpvcross(e1.a.p, n);
cpFloat d_e1_b = cpvcross(e1.b.p, n);
cpFloat d_e2_a = cpvcross(e2.a.p, n);
cpFloat d_e2_b = cpvcross(e2.b.p, n);
cpFloat e1_denom = 1.0f/(d_e1_b - d_e1_a);
cpFloat e2_denom = 1.0f/(d_e2_b - d_e2_a);
// Project the endpoints of the two edges onto the opposing edge, clamping them as necessary.
// Compare the projected points to the collision normal to see if the shapes overlap there.
{
cpVect p1 = cpvadd(cpvmult(n, e1.r), cpvlerp(e1.a.p, e1.b.p, cpfclamp01((d_e2_b - d_e1_a)*e1_denom)));
cpVect p2 = cpvadd(cpvmult(n, -e2.r), cpvlerp(e2.a.p, e2.b.p, cpfclamp01((d_e1_a - d_e2_a)*e2_denom)));
cpFloat dist = cpvdot(cpvsub(p2, p1), n);
if(dist <= 0.0f){
cpHashValue hash_1a2b = CP_HASH_PAIR(e1.a.hash, e2.b.hash);
cpCollisionInfoPushContact(info, p1, p2, hash_1a2b);
}
}{
cpVect p1 = cpvadd(cpvmult(n, e1.r), cpvlerp(e1.a.p, e1.b.p, cpfclamp01((d_e2_a - d_e1_a)*e1_denom)));
cpVect p2 = cpvadd(cpvmult(n, -e2.r), cpvlerp(e2.a.p, e2.b.p, cpfclamp01((d_e1_b - d_e2_a)*e2_denom)));
cpFloat dist = cpvdot(cpvsub(p2, p1), n);
if(dist <= 0.0f){
cpHashValue hash_1b2a = CP_HASH_PAIR(e1.b.hash, e2.a.hash);
cpCollisionInfoPushContact(info, p1, p2, hash_1b2a);
}
}
}
}
//MARK: Collision Functions
typedef void (*CollisionFunc)(const cpShape *a, const cpShape *b, struct cpCollisionInfo *info);
// Collide circle shapes.
static void
CircleToCircle(const cpCircleShape *c1, const cpCircleShape *c2, struct cpCollisionInfo *info)
{
cpFloat mindist = c1->r + c2->r;
cpVect delta = cpvsub(c2->tc, c1->tc);
cpFloat distsq = cpvlengthsq(delta);
if(distsq < mindist*mindist){
cpFloat dist = cpfsqrt(distsq);
cpVect n = info->n = (dist ? cpvmult(delta, 1.0f/dist) : cpv(1.0f, 0.0f));
cpCollisionInfoPushContact(info, cpvadd(c1->tc, cpvmult(n, c1->r)), cpvadd(c2->tc, cpvmult(n, -c2->r)), 0);
}
}
static void
CircleToSegment(const cpCircleShape *circle, const cpSegmentShape *segment, struct cpCollisionInfo *info)
{
cpVect seg_a = segment->ta;
cpVect seg_b = segment->tb;
cpVect center = circle->tc;
// Find the closest point on the segment to the circle.
cpVect seg_delta = cpvsub(seg_b, seg_a);
cpFloat closest_t = cpfclamp01(cpvdot(seg_delta, cpvsub(center, seg_a))/cpvlengthsq(seg_delta));
cpVect closest = cpvadd(seg_a, cpvmult(seg_delta, closest_t));
// Compare the radii of the two shapes to see if they are colliding.
cpFloat mindist = circle->r + segment->r;
cpVect delta = cpvsub(closest, center);
cpFloat distsq = cpvlengthsq(delta);
if(distsq < mindist*mindist){
cpFloat dist = cpfsqrt(distsq);
// Handle coincident shapes as gracefully as possible.
cpVect n = info->n = (dist ? cpvmult(delta, 1.0f/dist) : segment->tn);
// Reject endcap collisions if tangents are provided.
cpVect rot = cpBodyGetRotation(segment->shape.body);
if(
(closest_t != 0.0f || cpvdot(n, cpvrotate(segment->a_tangent, rot)) >= 0.0) &&
(closest_t != 1.0f || cpvdot(n, cpvrotate(segment->b_tangent, rot)) >= 0.0)
){
cpCollisionInfoPushContact(info, cpvadd(center, cpvmult(n, circle->r)), cpvadd(closest, cpvmult(n, -segment->r)), 0);
}
}
}
static void
SegmentToSegment(const cpSegmentShape *seg1, const cpSegmentShape *seg2, struct cpCollisionInfo *info)
{
struct SupportContext context = {(cpShape *)seg1, (cpShape *)seg2, (SupportPointFunc)SegmentSupportPoint, (SupportPointFunc)SegmentSupportPoint};
struct ClosestPoints points = GJK(&context, &info->id);
#if DRAW_CLOSEST
#if PRINT_LOG
// ChipmunkDemoPrintString("Distance: %.2f\n", points.d);
#endif
ChipmunkDebugDrawDot(6.0, points.a, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawDot(6.0, points.b, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1));
#endif
cpVect n = points.n;
cpVect rot1 = cpBodyGetRotation(seg1->shape.body);
cpVect rot2 = cpBodyGetRotation(seg2->shape.body);
// If the closest points are nearer than the sum of the radii...
if(
points.d <= (seg1->r + seg2->r) &&
(
// Reject endcap collisions if tangents are provided.
(!cpveql(points.a, seg1->ta) || cpvdot(n, cpvrotate(seg1->a_tangent, rot1)) <= 0.0) &&
(!cpveql(points.a, seg1->tb) || cpvdot(n, cpvrotate(seg1->b_tangent, rot1)) <= 0.0) &&
(!cpveql(points.b, seg2->ta) || cpvdot(n, cpvrotate(seg2->a_tangent, rot2)) >= 0.0) &&
(!cpveql(points.b, seg2->tb) || cpvdot(n, cpvrotate(seg2->b_tangent, rot2)) >= 0.0)
)
){
ContactPoints(SupportEdgeForSegment(seg1, n), SupportEdgeForSegment(seg2, cpvneg(n)), points, info);
}
}
static void
PolyToPoly(const cpPolyShape *poly1, const cpPolyShape *poly2, struct cpCollisionInfo *info)
{
struct SupportContext context = {(cpShape *)poly1, (cpShape *)poly2, (SupportPointFunc)PolySupportPoint, (SupportPointFunc)PolySupportPoint};
struct ClosestPoints points = GJK(&context, &info->id);
#if DRAW_CLOSEST
#if PRINT_LOG
// ChipmunkDemoPrintString("Distance: %.2f\n", points.d);
#endif
ChipmunkDebugDrawDot(3.0, points.a, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawDot(3.0, points.b, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1));
#endif
// If the closest points are nearer than the sum of the radii...
if(points.d - poly1->r - poly2->r <= 0.0){
ContactPoints(SupportEdgeForPoly(poly1, points.n), SupportEdgeForPoly(poly2, cpvneg(points.n)), points, info);
}
}
static void
SegmentToPoly(const cpSegmentShape *seg, const cpPolyShape *poly, struct cpCollisionInfo *info)
{
struct SupportContext context = {(cpShape *)seg, (cpShape *)poly, (SupportPointFunc)SegmentSupportPoint, (SupportPointFunc)PolySupportPoint};
struct ClosestPoints points = GJK(&context, &info->id);
#if DRAW_CLOSEST
#if PRINT_LOG
// ChipmunkDemoPrintString("Distance: %.2f\n", points.d);
#endif
ChipmunkDebugDrawDot(3.0, points.a, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawDot(3.0, points.b, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1));
#endif
cpVect n = points.n;
cpVect rot = cpBodyGetRotation(seg->shape.body);
if(
// If the closest points are nearer than the sum of the radii...
points.d - seg->r - poly->r <= 0.0 &&
(
// Reject endcap collisions if tangents are provided.
(!cpveql(points.a, seg->ta) || cpvdot(n, cpvrotate(seg->a_tangent, rot)) <= 0.0) &&
(!cpveql(points.a, seg->tb) || cpvdot(n, cpvrotate(seg->b_tangent, rot)) <= 0.0)
)
){
ContactPoints(SupportEdgeForSegment(seg, n), SupportEdgeForPoly(poly, cpvneg(n)), points, info);
}
}
static void
CircleToPoly(const cpCircleShape *circle, const cpPolyShape *poly, struct cpCollisionInfo *info)
{
struct SupportContext context = {(cpShape *)circle, (cpShape *)poly, (SupportPointFunc)CircleSupportPoint, (SupportPointFunc)PolySupportPoint};
struct ClosestPoints points = GJK(&context, &info->id);
#if DRAW_CLOSEST
ChipmunkDebugDrawDot(3.0, points.a, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawDot(3.0, points.b, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1));
ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1));
#endif
// If the closest points are nearer than the sum of the radii...
if(points.d <= circle->r + poly->r){
cpVect n = info->n = points.n;
cpCollisionInfoPushContact(info, cpvadd(points.a, cpvmult(n, circle->r)), cpvadd(points.b, cpvmult(n, poly->r)), 0);
}
}
static void
CollisionError(const cpShape *circle, const cpShape *poly, struct cpCollisionInfo *info)
{
cpAssertHard(cpFalse, "Internal Error: Shape types are not sorted.");
}
static const CollisionFunc BuiltinCollisionFuncs[9] = {
(CollisionFunc)CircleToCircle,
CollisionError,
CollisionError,
(CollisionFunc)CircleToSegment,
(CollisionFunc)SegmentToSegment,
CollisionError,
(CollisionFunc)CircleToPoly,
(CollisionFunc)SegmentToPoly,
(CollisionFunc)PolyToPoly,
};
static const CollisionFunc *CollisionFuncs = BuiltinCollisionFuncs;
struct cpCollisionInfo
cpCollide(const cpShape *a, const cpShape *b, cpCollisionID id, struct cpContact *contacts)
{
struct cpCollisionInfo info = {a, b, id, cpvzero, 0, contacts};
// Make sure the shape types are in order.
if(a->klass->type > b->klass->type){
info.a = b;
info.b = a;
}
CollisionFuncs[info.a->klass->type + info.b->klass->type*CP_NUM_SHAPES](info.a, info.b, &info);
// if(0){
// for(int i=0; i<info.count; i++){
// cpVect r1 = info.arr[i].r1;
// cpVect r2 = info.arr[i].r2;
// cpVect mid = cpvlerp(r1, r2, 0.5f);
//
// ChipmunkDebugDrawSegment(r1, mid, RGBAColor(1, 0, 0, 1));
// ChipmunkDebugDrawSegment(r2, mid, RGBAColor(0, 0, 1, 1));
// }
// }
return info;
}

View File

@ -1,173 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "chipmunk/chipmunk_private.h"
// TODO: Comment me!
void cpConstraintDestroy(cpConstraint *constraint){}
void
cpConstraintFree(cpConstraint *constraint)
{
if(constraint){
cpConstraintDestroy(constraint);
cpfree(constraint);
}
}
void
cpConstraintInit(cpConstraint *constraint, const cpConstraintClass *klass, cpBody *a, cpBody *b)
{
constraint->klass = klass;
constraint->a = a;
constraint->b = b;
constraint->space = NULL;
constraint->next_a = NULL;
constraint->next_b = NULL;
constraint->maxForce = (cpFloat)INFINITY;
constraint->errorBias = cpfpow(1.0f - 0.1f, 60.0f);
constraint->maxBias = (cpFloat)INFINITY;
constraint->collideBodies = cpTrue;
constraint->preSolve = NULL;
constraint->postSolve = NULL;
}
cpSpace *
cpConstraintGetSpace(const cpConstraint *constraint)
{
return constraint->space;
}
cpBody *
cpConstraintGetBodyA(const cpConstraint *constraint)
{
return constraint->a;
}
cpBody *
cpConstraintGetBodyB(const cpConstraint *constraint)
{
return constraint->b;
}
cpFloat
cpConstraintGetMaxForce(const cpConstraint *constraint)
{
return constraint->maxForce;
}
void
cpConstraintSetMaxForce(cpConstraint *constraint, cpFloat maxForce)
{
cpAssertHard(maxForce >= 0.0f, "maxForce must be positive.");
cpConstraintActivateBodies(constraint);
constraint->maxForce = maxForce;
}
cpFloat
cpConstraintGetErrorBias(const cpConstraint *constraint)
{
return constraint->errorBias;
}
void
cpConstraintSetErrorBias(cpConstraint *constraint, cpFloat errorBias)
{
cpAssertHard(errorBias >= 0.0f, "errorBias must be positive.");
cpConstraintActivateBodies(constraint);
constraint->errorBias = errorBias;
}
cpFloat
cpConstraintGetMaxBias(const cpConstraint *constraint)
{
return constraint->maxBias;
}
void
cpConstraintSetMaxBias(cpConstraint *constraint, cpFloat maxBias)
{
cpAssertHard(maxBias >= 0.0f, "maxBias must be positive.");
cpConstraintActivateBodies(constraint);
constraint->maxBias = maxBias;
}
cpBool
cpConstraintGetCollideBodies(const cpConstraint *constraint)
{
return constraint->collideBodies;
}
void
cpConstraintSetCollideBodies(cpConstraint *constraint, cpBool collideBodies)
{
cpConstraintActivateBodies(constraint);
constraint->collideBodies = collideBodies;
}
cpConstraintPreSolveFunc
cpConstraintGetPreSolveFunc(const cpConstraint *constraint)
{
return constraint->preSolve;
}
void
cpConstraintSetPreSolveFunc(cpConstraint *constraint, cpConstraintPreSolveFunc preSolveFunc)
{
constraint->preSolve = preSolveFunc;
}
cpConstraintPostSolveFunc
cpConstraintGetPostSolveFunc(const cpConstraint *constraint)
{
return constraint->postSolve;
}
void
cpConstraintSetPostSolveFunc(cpConstraint *constraint, cpConstraintPostSolveFunc postSolveFunc)
{
constraint->postSolve = postSolveFunc;
}
cpDataPointer
cpConstraintGetUserData(const cpConstraint *constraint)
{
return constraint->userData;
}
void
cpConstraintSetUserData(cpConstraint *constraint, cpDataPointer userData)
{
constraint->userData = userData;
}
cpFloat
cpConstraintGetImpulse(cpConstraint *constraint)
{
return constraint->klass->getImpulse(constraint);
}

View File

@ -1,178 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "chipmunk/chipmunk_private.h"
static cpFloat
defaultSpringTorque(cpDampedRotarySpring *spring, cpFloat relativeAngle){
return (relativeAngle - spring->restAngle)*spring->stiffness;
}
static void
preStep(cpDampedRotarySpring *spring, cpFloat dt)
{
cpBody *a = spring->constraint.a;
cpBody *b = spring->constraint.b;
cpFloat moment = a->i_inv + b->i_inv;
cpAssertSoft(moment != 0.0, "Unsolvable spring.");
spring->iSum = 1.0f/moment;
spring->w_coef = 1.0f - cpfexp(-spring->damping*dt*moment);
spring->target_wrn = 0.0f;
// apply spring torque
cpFloat j_spring = spring->springTorqueFunc((cpConstraint *)spring, a->a - b->a)*dt;
spring->jAcc = j_spring;
a->w -= j_spring*a->i_inv;
b->w += j_spring*b->i_inv;
}
static void applyCachedImpulse(cpDampedRotarySpring *spring, cpFloat dt_coef){}
static void
applyImpulse(cpDampedRotarySpring *spring, cpFloat dt)
{
cpBody *a = spring->constraint.a;
cpBody *b = spring->constraint.b;
// compute relative velocity
cpFloat wrn = a->w - b->w;//normal_relative_velocity(a, b, r1, r2, n) - spring->target_vrn;
// compute velocity loss from drag
// not 100% certain this is derived correctly, though it makes sense
cpFloat w_damp = (spring->target_wrn - wrn)*spring->w_coef;
spring->target_wrn = wrn + w_damp;
//apply_impulses(a, b, spring->r1, spring->r2, cpvmult(spring->n, v_damp*spring->nMass));
cpFloat j_damp = w_damp*spring->iSum;
spring->jAcc += j_damp;
a->w += j_damp*a->i_inv;
b->w -= j_damp*b->i_inv;
}
static cpFloat
getImpulse(cpDampedRotarySpring *spring)
{
return spring->jAcc;
}
static const cpConstraintClass klass = {
(cpConstraintPreStepImpl)preStep,
(cpConstraintApplyCachedImpulseImpl)applyCachedImpulse,
(cpConstraintApplyImpulseImpl)applyImpulse,
(cpConstraintGetImpulseImpl)getImpulse,
};
cpDampedRotarySpring *
cpDampedRotarySpringAlloc(void)
{
return (cpDampedRotarySpring *)cpcalloc(1, sizeof(cpDampedRotarySpring));
}
cpDampedRotarySpring *
cpDampedRotarySpringInit(cpDampedRotarySpring *spring, cpBody *a, cpBody *b, cpFloat restAngle, cpFloat stiffness, cpFloat damping)
{
cpConstraintInit((cpConstraint *)spring, &klass, a, b);
spring->restAngle = restAngle;
spring->stiffness = stiffness;
spring->damping = damping;
spring->springTorqueFunc = (cpDampedRotarySpringTorqueFunc)defaultSpringTorque;
spring->jAcc = 0.0f;
return spring;
}
cpConstraint *
cpDampedRotarySpringNew(cpBody *a, cpBody *b, cpFloat restAngle, cpFloat stiffness, cpFloat damping)
{
return (cpConstraint *)cpDampedRotarySpringInit(cpDampedRotarySpringAlloc(), a, b, restAngle, stiffness, damping);
}
cpBool
cpConstraintIsDampedRotarySpring(const cpConstraint *constraint)
{
return (constraint->klass == &klass);
}
cpFloat
cpDampedRotarySpringGetRestAngle(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring.");
return ((cpDampedRotarySpring *)constraint)->restAngle;
}
void
cpDampedRotarySpringSetRestAngle(cpConstraint *constraint, cpFloat restAngle)
{
cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring.");
cpConstraintActivateBodies(constraint);
((cpDampedRotarySpring *)constraint)->restAngle = restAngle;
}
cpFloat
cpDampedRotarySpringGetStiffness(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring.");
return ((cpDampedRotarySpring *)constraint)->stiffness;
}
void
cpDampedRotarySpringSetStiffness(cpConstraint *constraint, cpFloat stiffness)
{
cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring.");
cpConstraintActivateBodies(constraint);
((cpDampedRotarySpring *)constraint)->stiffness = stiffness;
}
cpFloat
cpDampedRotarySpringGetDamping(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring.");
return ((cpDampedRotarySpring *)constraint)->damping;
}
void
cpDampedRotarySpringSetDamping(cpConstraint *constraint, cpFloat damping)
{
cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring.");
cpConstraintActivateBodies(constraint);
((cpDampedRotarySpring *)constraint)->damping = damping;
}
cpDampedRotarySpringTorqueFunc
cpDampedRotarySpringGetSpringTorqueFunc(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring.");
return ((cpDampedRotarySpring *)constraint)->springTorqueFunc;
}
void
cpDampedRotarySpringSetSpringTorqueFunc(cpConstraint *constraint, cpDampedRotarySpringTorqueFunc springTorqueFunc)
{
cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring.");
cpConstraintActivateBodies(constraint);
((cpDampedRotarySpring *)constraint)->springTorqueFunc = springTorqueFunc;
}

View File

@ -1,216 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "chipmunk/chipmunk_private.h"
static cpFloat
defaultSpringForce(cpDampedSpring *spring, cpFloat dist){
return (spring->restLength - dist)*spring->stiffness;
}
static void
preStep(cpDampedSpring *spring, cpFloat dt)
{
cpBody *a = spring->constraint.a;
cpBody *b = spring->constraint.b;
spring->r1 = cpTransformVect(a->transform, cpvsub(spring->anchorA, a->cog));
spring->r2 = cpTransformVect(b->transform, cpvsub(spring->anchorB, b->cog));
cpVect delta = cpvsub(cpvadd(b->p, spring->r2), cpvadd(a->p, spring->r1));
cpFloat dist = cpvlength(delta);
spring->n = cpvmult(delta, 1.0f/(dist ? dist : INFINITY));
cpFloat k = k_scalar(a, b, spring->r1, spring->r2, spring->n);
cpAssertSoft(k != 0.0, "Unsolvable spring.");
spring->nMass = 1.0f/k;
spring->target_vrn = 0.0f;
spring->v_coef = 1.0f - cpfexp(-spring->damping*dt*k);
// apply spring force
cpFloat f_spring = spring->springForceFunc((cpConstraint *)spring, dist);
cpFloat j_spring = spring->jAcc = f_spring*dt;
apply_impulses(a, b, spring->r1, spring->r2, cpvmult(spring->n, j_spring));
}
static void applyCachedImpulse(cpDampedSpring *spring, cpFloat dt_coef){}
static void
applyImpulse(cpDampedSpring *spring, cpFloat dt)
{
cpBody *a = spring->constraint.a;
cpBody *b = spring->constraint.b;
cpVect n = spring->n;
cpVect r1 = spring->r1;
cpVect r2 = spring->r2;
// compute relative velocity
cpFloat vrn = normal_relative_velocity(a, b, r1, r2, n);
// compute velocity loss from drag
cpFloat v_damp = (spring->target_vrn - vrn)*spring->v_coef;
spring->target_vrn = vrn + v_damp;
cpFloat j_damp = v_damp*spring->nMass;
spring->jAcc += j_damp;
apply_impulses(a, b, spring->r1, spring->r2, cpvmult(spring->n, j_damp));
}
static cpFloat
getImpulse(cpDampedSpring *spring)
{
return spring->jAcc;
}
static const cpConstraintClass klass = {
(cpConstraintPreStepImpl)preStep,
(cpConstraintApplyCachedImpulseImpl)applyCachedImpulse,
(cpConstraintApplyImpulseImpl)applyImpulse,
(cpConstraintGetImpulseImpl)getImpulse,
};
cpDampedSpring *
cpDampedSpringAlloc(void)
{
return (cpDampedSpring *)cpcalloc(1, sizeof(cpDampedSpring));
}
cpDampedSpring *
cpDampedSpringInit(cpDampedSpring *spring, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat restLength, cpFloat stiffness, cpFloat damping)
{
cpConstraintInit((cpConstraint *)spring, &klass, a, b);
spring->anchorA = anchorA;
spring->anchorB = anchorB;
spring->restLength = restLength;
spring->stiffness = stiffness;
spring->damping = damping;
spring->springForceFunc = (cpDampedSpringForceFunc)defaultSpringForce;
spring->jAcc = 0.0f;
return spring;
}
cpConstraint *
cpDampedSpringNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat restLength, cpFloat stiffness, cpFloat damping)
{
return (cpConstraint *)cpDampedSpringInit(cpDampedSpringAlloc(), a, b, anchorA, anchorB, restLength, stiffness, damping);
}
cpBool
cpConstraintIsDampedSpring(const cpConstraint *constraint)
{
return (constraint->klass == &klass);
}
cpVect
cpDampedSpringGetAnchorA(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring.");
return ((cpDampedSpring *)constraint)->anchorA;
}
void
cpDampedSpringSetAnchorA(cpConstraint *constraint, cpVect anchorA)
{
cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring.");
cpConstraintActivateBodies(constraint);
((cpDampedSpring *)constraint)->anchorA = anchorA;
}
cpVect
cpDampedSpringGetAnchorB(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring.");
return ((cpDampedSpring *)constraint)->anchorB;
}
void
cpDampedSpringSetAnchorB(cpConstraint *constraint, cpVect anchorB)
{
cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring.");
cpConstraintActivateBodies(constraint);
((cpDampedSpring *)constraint)->anchorB = anchorB;
}
cpFloat
cpDampedSpringGetRestLength(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring.");
return ((cpDampedSpring *)constraint)->restLength;
}
void
cpDampedSpringSetRestLength(cpConstraint *constraint, cpFloat restLength)
{
cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring.");
cpConstraintActivateBodies(constraint);
((cpDampedSpring *)constraint)->restLength = restLength;
}
cpFloat
cpDampedSpringGetStiffness(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring.");
return ((cpDampedSpring *)constraint)->stiffness;
}
void
cpDampedSpringSetStiffness(cpConstraint *constraint, cpFloat stiffness)
{
cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring.");
cpConstraintActivateBodies(constraint);
((cpDampedSpring *)constraint)->stiffness = stiffness;
}
cpFloat
cpDampedSpringGetDamping(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring.");
return ((cpDampedSpring *)constraint)->damping;
}
void
cpDampedSpringSetDamping(cpConstraint *constraint, cpFloat damping)
{
cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring.");
cpConstraintActivateBodies(constraint);
((cpDampedSpring *)constraint)->damping = damping;
}
cpDampedSpringForceFunc
cpDampedSpringGetSpringForceFunc(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring.");
return ((cpDampedSpring *)constraint)->springForceFunc;
}
void
cpDampedSpringSetSpringForceFunc(cpConstraint *constraint, cpDampedSpringForceFunc springForceFunc)
{
cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring.");
cpConstraintActivateBodies(constraint);
((cpDampedSpring *)constraint)->springForceFunc = springForceFunc;
}

View File

@ -1,145 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "chipmunk/chipmunk_private.h"
static void
preStep(cpGearJoint *joint, cpFloat dt)
{
cpBody *a = joint->constraint.a;
cpBody *b = joint->constraint.b;
// calculate moment of inertia coefficient.
joint->iSum = 1.0f/(a->i_inv*joint->ratio_inv + joint->ratio*b->i_inv);
// calculate bias velocity
cpFloat maxBias = joint->constraint.maxBias;
joint->bias = cpfclamp(-bias_coef(joint->constraint.errorBias, dt)*(b->a*joint->ratio - a->a - joint->phase)/dt, -maxBias, maxBias);
}
static void
applyCachedImpulse(cpGearJoint *joint, cpFloat dt_coef)
{
cpBody *a = joint->constraint.a;
cpBody *b = joint->constraint.b;
cpFloat j = joint->jAcc*dt_coef;
a->w -= j*a->i_inv*joint->ratio_inv;
b->w += j*b->i_inv;
}
static void
applyImpulse(cpGearJoint *joint, cpFloat dt)
{
cpBody *a = joint->constraint.a;
cpBody *b = joint->constraint.b;
// compute relative rotational velocity
cpFloat wr = b->w*joint->ratio - a->w;
cpFloat jMax = joint->constraint.maxForce*dt;
// compute normal impulse
cpFloat j = (joint->bias - wr)*joint->iSum;
cpFloat jOld = joint->jAcc;
joint->jAcc = cpfclamp(jOld + j, -jMax, jMax);
j = joint->jAcc - jOld;
// apply impulse
a->w -= j*a->i_inv*joint->ratio_inv;
b->w += j*b->i_inv;
}
static cpFloat
getImpulse(cpGearJoint *joint)
{
return cpfabs(joint->jAcc);
}
static const cpConstraintClass klass = {
(cpConstraintPreStepImpl)preStep,
(cpConstraintApplyCachedImpulseImpl)applyCachedImpulse,
(cpConstraintApplyImpulseImpl)applyImpulse,
(cpConstraintGetImpulseImpl)getImpulse,
};
cpGearJoint *
cpGearJointAlloc(void)
{
return (cpGearJoint *)cpcalloc(1, sizeof(cpGearJoint));
}
cpGearJoint *
cpGearJointInit(cpGearJoint *joint, cpBody *a, cpBody *b, cpFloat phase, cpFloat ratio)
{
cpConstraintInit((cpConstraint *)joint, &klass, a, b);
joint->phase = phase;
joint->ratio = ratio;
joint->ratio_inv = 1.0f/ratio;
joint->jAcc = 0.0f;
return joint;
}
cpConstraint *
cpGearJointNew(cpBody *a, cpBody *b, cpFloat phase, cpFloat ratio)
{
return (cpConstraint *)cpGearJointInit(cpGearJointAlloc(), a, b, phase, ratio);
}
cpBool
cpConstraintIsGearJoint(const cpConstraint *constraint)
{
return (constraint->klass == &klass);
}
cpFloat
cpGearJointGetPhase(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsGearJoint(constraint), "Constraint is not a ratchet joint.");
return ((cpGearJoint *)constraint)->phase;
}
void
cpGearJointSetPhase(cpConstraint *constraint, cpFloat phase)
{
cpAssertHard(cpConstraintIsGearJoint(constraint), "Constraint is not a ratchet joint.");
cpConstraintActivateBodies(constraint);
((cpGearJoint *)constraint)->phase = phase;
}
cpFloat
cpGearJointGetRatio(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsGearJoint(constraint), "Constraint is not a ratchet joint.");
return ((cpGearJoint *)constraint)->ratio;
}
void
cpGearJointSetRatio(cpConstraint *constraint, cpFloat ratio)
{
cpAssertHard(cpConstraintIsGearJoint(constraint), "Constraint is not a ratchet joint.");
cpConstraintActivateBodies(constraint);
((cpGearJoint *)constraint)->ratio = ratio;
((cpGearJoint *)constraint)->ratio_inv = 1.0f/ratio;
}

View File

@ -1,197 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "chipmunk/chipmunk_private.h"
static void
preStep(cpGrooveJoint *joint, cpFloat dt)
{
cpBody *a = joint->constraint.a;
cpBody *b = joint->constraint.b;
// calculate endpoints in worldspace
cpVect ta = cpTransformPoint(a->transform, joint->grv_a);
cpVect tb = cpTransformPoint(a->transform, joint->grv_b);
// calculate axis
cpVect n = cpTransformVect(a->transform, joint->grv_n);
cpFloat d = cpvdot(ta, n);
joint->grv_tn = n;
joint->r2 = cpTransformVect(b->transform, cpvsub(joint->anchorB, b->cog));
// calculate tangential distance along the axis of r2
cpFloat td = cpvcross(cpvadd(b->p, joint->r2), n);
// calculate clamping factor and r2
if(td <= cpvcross(ta, n)){
joint->clamp = 1.0f;
joint->r1 = cpvsub(ta, a->p);
} else if(td >= cpvcross(tb, n)){
joint->clamp = -1.0f;
joint->r1 = cpvsub(tb, a->p);
} else {
joint->clamp = 0.0f;
joint->r1 = cpvsub(cpvadd(cpvmult(cpvperp(n), -td), cpvmult(n, d)), a->p);
}
// Calculate mass tensor
joint->k = k_tensor(a, b, joint->r1, joint->r2);
// calculate bias velocity
cpVect delta = cpvsub(cpvadd(b->p, joint->r2), cpvadd(a->p, joint->r1));
joint->bias = cpvclamp(cpvmult(delta, -bias_coef(joint->constraint.errorBias, dt)/dt), joint->constraint.maxBias);
}
static void
applyCachedImpulse(cpGrooveJoint *joint, cpFloat dt_coef)
{
cpBody *a = joint->constraint.a;
cpBody *b = joint->constraint.b;
apply_impulses(a, b, joint->r1, joint->r2, cpvmult(joint->jAcc, dt_coef));
}
static inline cpVect
grooveConstrain(cpGrooveJoint *joint, cpVect j, cpFloat dt){
cpVect n = joint->grv_tn;
cpVect jClamp = (joint->clamp*cpvcross(j, n) > 0.0f) ? j : cpvproject(j, n);
return cpvclamp(jClamp, joint->constraint.maxForce*dt);
}
static void
applyImpulse(cpGrooveJoint *joint, cpFloat dt)
{
cpBody *a = joint->constraint.a;
cpBody *b = joint->constraint.b;
cpVect r1 = joint->r1;
cpVect r2 = joint->r2;
// compute impulse
cpVect vr = relative_velocity(a, b, r1, r2);
cpVect j = cpMat2x2Transform(joint->k, cpvsub(joint->bias, vr));
cpVect jOld = joint->jAcc;
joint->jAcc = grooveConstrain(joint, cpvadd(jOld, j), dt);
j = cpvsub(joint->jAcc, jOld);
// apply impulse
apply_impulses(a, b, joint->r1, joint->r2, j);
}
static cpFloat
getImpulse(cpGrooveJoint *joint)
{
return cpvlength(joint->jAcc);
}
static const cpConstraintClass klass = {
(cpConstraintPreStepImpl)preStep,
(cpConstraintApplyCachedImpulseImpl)applyCachedImpulse,
(cpConstraintApplyImpulseImpl)applyImpulse,
(cpConstraintGetImpulseImpl)getImpulse,
};
cpGrooveJoint *
cpGrooveJointAlloc(void)
{
return (cpGrooveJoint *)cpcalloc(1, sizeof(cpGrooveJoint));
}
cpGrooveJoint *
cpGrooveJointInit(cpGrooveJoint *joint, cpBody *a, cpBody *b, cpVect groove_a, cpVect groove_b, cpVect anchorB)
{
cpConstraintInit((cpConstraint *)joint, &klass, a, b);
joint->grv_a = groove_a;
joint->grv_b = groove_b;
joint->grv_n = cpvperp(cpvnormalize(cpvsub(groove_b, groove_a)));
joint->anchorB = anchorB;
joint->jAcc = cpvzero;
return joint;
}
cpConstraint *
cpGrooveJointNew(cpBody *a, cpBody *b, cpVect groove_a, cpVect groove_b, cpVect anchorB)
{
return (cpConstraint *)cpGrooveJointInit(cpGrooveJointAlloc(), a, b, groove_a, groove_b, anchorB);
}
cpBool
cpConstraintIsGrooveJoint(const cpConstraint *constraint)
{
return (constraint->klass == &klass);
}
cpVect
cpGrooveJointGetGrooveA(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsGrooveJoint(constraint), "Constraint is not a groove joint.");
return ((cpGrooveJoint *)constraint)->grv_a;
}
void
cpGrooveJointSetGrooveA(cpConstraint *constraint, cpVect value)
{
cpAssertHard(cpConstraintIsGrooveJoint(constraint), "Constraint is not a groove joint.");
cpGrooveJoint *g = (cpGrooveJoint *)constraint;
g->grv_a = value;
g->grv_n = cpvperp(cpvnormalize(cpvsub(g->grv_b, value)));
cpConstraintActivateBodies(constraint);
}
cpVect
cpGrooveJointGetGrooveB(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsGrooveJoint(constraint), "Constraint is not a groove joint.");
return ((cpGrooveJoint *)constraint)->grv_b;
}
void
cpGrooveJointSetGrooveB(cpConstraint *constraint, cpVect value)
{
cpAssertHard(cpConstraintIsGrooveJoint(constraint), "Constraint is not a groove joint.");
cpGrooveJoint *g = (cpGrooveJoint *)constraint;
g->grv_b = value;
g->grv_n = cpvperp(cpvnormalize(cpvsub(value, g->grv_a)));
cpConstraintActivateBodies(constraint);
}
cpVect
cpGrooveJointGetAnchorB(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsGrooveJoint(constraint), "Constraint is not a groove joint.");
return ((cpGrooveJoint *)constraint)->anchorB;
}
void
cpGrooveJointSetAnchorB(cpConstraint *constraint, cpVect anchorB)
{
cpAssertHard(cpConstraintIsGrooveJoint(constraint), "Constraint is not a groove joint.");
cpConstraintActivateBodies(constraint);
((cpGrooveJoint *)constraint)->anchorB = anchorB;
}

View File

@ -1,253 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "chipmunk/chipmunk_private.h"
#include "prime.h"
typedef struct cpHashSetBin {
void *elt;
cpHashValue hash;
struct cpHashSetBin *next;
} cpHashSetBin;
struct cpHashSet {
unsigned int entries, size;
cpHashSetEqlFunc eql;
void *default_value;
cpHashSetBin **table;
cpHashSetBin *pooledBins;
cpArray *allocatedBuffers;
};
void
cpHashSetFree(cpHashSet *set)
{
if(set){
cpfree(set->table);
cpArrayFreeEach(set->allocatedBuffers, cpfree);
cpArrayFree(set->allocatedBuffers);
cpfree(set);
}
}
cpHashSet *
cpHashSetNew(int size, cpHashSetEqlFunc eqlFunc)
{
cpHashSet *set = (cpHashSet *)cpcalloc(1, sizeof(cpHashSet));
set->size = next_prime(size);
set->entries = 0;
set->eql = eqlFunc;
set->default_value = NULL;
set->table = (cpHashSetBin **)cpcalloc(set->size, sizeof(cpHashSetBin *));
set->pooledBins = NULL;
set->allocatedBuffers = cpArrayNew(0);
return set;
}
void
cpHashSetSetDefaultValue(cpHashSet *set, void *default_value)
{
set->default_value = default_value;
}
static int
setIsFull(cpHashSet *set)
{
return (set->entries >= set->size);
}
static void
cpHashSetResize(cpHashSet *set)
{
// Get the next approximate doubled prime.
unsigned int newSize = next_prime(set->size + 1);
// Allocate a new table.
cpHashSetBin **newTable = (cpHashSetBin **)cpcalloc(newSize, sizeof(cpHashSetBin *));
// Iterate over the chains.
for(unsigned int i=0; i<set->size; i++){
// Rehash the bins into the new table.
cpHashSetBin *bin = set->table[i];
while(bin){
cpHashSetBin *next = bin->next;
cpHashValue idx = bin->hash%newSize;
bin->next = newTable[idx];
newTable[idx] = bin;
bin = next;
}
}
cpfree(set->table);
set->table = newTable;
set->size = newSize;
}
static inline void
recycleBin(cpHashSet *set, cpHashSetBin *bin)
{
bin->next = set->pooledBins;
set->pooledBins = bin;
bin->elt = NULL;
}
static cpHashSetBin *
getUnusedBin(cpHashSet *set)
{
cpHashSetBin *bin = set->pooledBins;
if(bin){
set->pooledBins = bin->next;
return bin;
} else {
// Pool is exhausted, make more
int count = CP_BUFFER_BYTES/sizeof(cpHashSetBin);
cpAssertHard(count, "Internal Error: Buffer size is too small.");
cpHashSetBin *buffer = (cpHashSetBin *)cpcalloc(1, CP_BUFFER_BYTES);
cpArrayPush(set->allocatedBuffers, buffer);
// push all but the first one, return it instead
for(int i=1; i<count; i++) recycleBin(set, buffer + i);
return buffer;
}
}
int
cpHashSetCount(cpHashSet *set)
{
return set->entries;
}
void *
cpHashSetInsert(cpHashSet *set, cpHashValue hash, void *ptr, cpHashSetTransFunc trans, void *data)
{
cpHashValue idx = hash%set->size;
// Find the bin with the matching element.
cpHashSetBin *bin = set->table[idx];
while(bin && !set->eql(ptr, bin->elt))
bin = bin->next;
// Create it if necessary.
if(!bin){
bin = getUnusedBin(set);
bin->hash = hash;
bin->elt = (trans ? trans(ptr, data) : data);
bin->next = set->table[idx];
set->table[idx] = bin;
set->entries++;
if(setIsFull(set)) cpHashSetResize(set);
}
return bin->elt;
}
void *
cpHashSetRemove(cpHashSet *set, cpHashValue hash, void *ptr)
{
cpHashValue idx = hash%set->size;
cpHashSetBin **prev_ptr = &set->table[idx];
cpHashSetBin *bin = set->table[idx];
// Find the bin
while(bin && !set->eql(ptr, bin->elt)){
prev_ptr = &bin->next;
bin = bin->next;
}
// Remove it if it exists.
if(bin){
// Update the previous linked list pointer
(*prev_ptr) = bin->next;
set->entries--;
void *elt = bin->elt;
recycleBin(set, bin);
return elt;
}
return NULL;
}
void *
cpHashSetFind(cpHashSet *set, cpHashValue hash, void *ptr)
{
cpHashValue idx = hash%set->size;
cpHashSetBin *bin = set->table[idx];
while(bin && !set->eql(ptr, bin->elt))
bin = bin->next;
return (bin ? bin->elt : set->default_value);
}
void
cpHashSetEach(cpHashSet *set, cpHashSetIteratorFunc func, void *data)
{
for(unsigned int i=0; i<set->size; i++){
cpHashSetBin *bin = set->table[i];
while(bin){
cpHashSetBin *next = bin->next;
func(bin->elt, data);
bin = next;
}
}
}
void
cpHashSetFilter(cpHashSet *set, cpHashSetFilterFunc func, void *data)
{
for(unsigned int i=0; i<set->size; i++){
// The rest works similarly to cpHashSetRemove() above.
cpHashSetBin **prev_ptr = &set->table[i];
cpHashSetBin *bin = set->table[i];
while(bin){
cpHashSetBin *next = bin->next;
if(func(bin->elt, data)){
prev_ptr = &bin->next;
} else {
(*prev_ptr) = next;
set->entries--;
recycleBin(set, bin);
}
bin = next;
}
}
}

View File

@ -1,172 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "chipmunk/chipmunk_private.h"
static void
preStep(cpPinJoint *joint, cpFloat dt)
{
cpBody *a = joint->constraint.a;
cpBody *b = joint->constraint.b;
joint->r1 = cpTransformVect(a->transform, cpvsub(joint->anchorA, a->cog));
joint->r2 = cpTransformVect(b->transform, cpvsub(joint->anchorB, b->cog));
cpVect delta = cpvsub(cpvadd(b->p, joint->r2), cpvadd(a->p, joint->r1));
cpFloat dist = cpvlength(delta);
joint->n = cpvmult(delta, 1.0f/(dist ? dist : (cpFloat)INFINITY));
// calculate mass normal
joint->nMass = 1.0f/k_scalar(a, b, joint->r1, joint->r2, joint->n);
// calculate bias velocity
cpFloat maxBias = joint->constraint.maxBias;
joint->bias = cpfclamp(-bias_coef(joint->constraint.errorBias, dt)*(dist - joint->dist)/dt, -maxBias, maxBias);
}
static void
applyCachedImpulse(cpPinJoint *joint, cpFloat dt_coef)
{
cpBody *a = joint->constraint.a;
cpBody *b = joint->constraint.b;
cpVect j = cpvmult(joint->n, joint->jnAcc*dt_coef);
apply_impulses(a, b, joint->r1, joint->r2, j);
}
static void
applyImpulse(cpPinJoint *joint, cpFloat dt)
{
cpBody *a = joint->constraint.a;
cpBody *b = joint->constraint.b;
cpVect n = joint->n;
// compute relative velocity
cpFloat vrn = normal_relative_velocity(a, b, joint->r1, joint->r2, n);
cpFloat jnMax = joint->constraint.maxForce*dt;
// compute normal impulse
cpFloat jn = (joint->bias - vrn)*joint->nMass;
cpFloat jnOld = joint->jnAcc;
joint->jnAcc = cpfclamp(jnOld + jn, -jnMax, jnMax);
jn = joint->jnAcc - jnOld;
// apply impulse
apply_impulses(a, b, joint->r1, joint->r2, cpvmult(n, jn));
}
static cpFloat
getImpulse(cpPinJoint *joint)
{
return cpfabs(joint->jnAcc);
}
static const cpConstraintClass klass = {
(cpConstraintPreStepImpl)preStep,
(cpConstraintApplyCachedImpulseImpl)applyCachedImpulse,
(cpConstraintApplyImpulseImpl)applyImpulse,
(cpConstraintGetImpulseImpl)getImpulse,
};
cpPinJoint *
cpPinJointAlloc(void)
{
return (cpPinJoint *)cpcalloc(1, sizeof(cpPinJoint));
}
cpPinJoint *
cpPinJointInit(cpPinJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB)
{
cpConstraintInit((cpConstraint *)joint, &klass, a, b);
joint->anchorA = anchorA;
joint->anchorB = anchorB;
// STATIC_BODY_CHECK
cpVect p1 = (a ? cpTransformPoint(a->transform, anchorA) : anchorA);
cpVect p2 = (b ? cpTransformPoint(b->transform, anchorB) : anchorB);
joint->dist = cpvlength(cpvsub(p2, p1));
cpAssertWarn(joint->dist > 0.0, "You created a 0 length pin joint. A pivot joint will be much more stable.");
joint->jnAcc = 0.0f;
return joint;
}
cpConstraint *
cpPinJointNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB)
{
return (cpConstraint *)cpPinJointInit(cpPinJointAlloc(), a, b, anchorA, anchorB);
}
cpBool
cpConstraintIsPinJoint(const cpConstraint *constraint)
{
return (constraint->klass == &klass);
}
cpVect
cpPinJointGetAnchorA(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsPinJoint(constraint), "Constraint is not a pin joint.");
return ((cpPinJoint *)constraint)->anchorA;
}
void
cpPinJointSetAnchorA(cpConstraint *constraint, cpVect anchorA)
{
cpAssertHard(cpConstraintIsPinJoint(constraint), "Constraint is not a pin joint.");
cpConstraintActivateBodies(constraint);
((cpPinJoint *)constraint)->anchorA = anchorA;
}
cpVect
cpPinJointGetAnchorB(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsPinJoint(constraint), "Constraint is not a pin joint.");
return ((cpPinJoint *)constraint)->anchorB;
}
void
cpPinJointSetAnchorB(cpConstraint *constraint, cpVect anchorB)
{
cpAssertHard(cpConstraintIsPinJoint(constraint), "Constraint is not a pin joint.");
cpConstraintActivateBodies(constraint);
((cpPinJoint *)constraint)->anchorB = anchorB;
}
cpFloat
cpPinJointGetDist(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsPinJoint(constraint), "Constraint is not a pin joint.");
return ((cpPinJoint *)constraint)->dist;
}
void
cpPinJointSetDist(cpConstraint *constraint, cpFloat dist)
{
cpAssertHard(cpConstraintIsPinJoint(constraint), "Constraint is not a pin joint.");
cpConstraintActivateBodies(constraint);
((cpPinJoint *)constraint)->dist = dist;
}

View File

@ -1,152 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "chipmunk/chipmunk_private.h"
static void
preStep(cpPivotJoint *joint, cpFloat dt)
{
cpBody *a = joint->constraint.a;
cpBody *b = joint->constraint.b;
joint->r1 = cpTransformVect(a->transform, cpvsub(joint->anchorA, a->cog));
joint->r2 = cpTransformVect(b->transform, cpvsub(joint->anchorB, b->cog));
// Calculate mass tensor
joint-> k = k_tensor(a, b, joint->r1, joint->r2);
// calculate bias velocity
cpVect delta = cpvsub(cpvadd(b->p, joint->r2), cpvadd(a->p, joint->r1));
joint->bias = cpvclamp(cpvmult(delta, -bias_coef(joint->constraint.errorBias, dt)/dt), joint->constraint.maxBias);
}
static void
applyCachedImpulse(cpPivotJoint *joint, cpFloat dt_coef)
{
cpBody *a = joint->constraint.a;
cpBody *b = joint->constraint.b;
apply_impulses(a, b, joint->r1, joint->r2, cpvmult(joint->jAcc, dt_coef));
}
static void
applyImpulse(cpPivotJoint *joint, cpFloat dt)
{
cpBody *a = joint->constraint.a;
cpBody *b = joint->constraint.b;
cpVect r1 = joint->r1;
cpVect r2 = joint->r2;
// compute relative velocity
cpVect vr = relative_velocity(a, b, r1, r2);
// compute normal impulse
cpVect j = cpMat2x2Transform(joint->k, cpvsub(joint->bias, vr));
cpVect jOld = joint->jAcc;
joint->jAcc = cpvclamp(cpvadd(joint->jAcc, j), joint->constraint.maxForce*dt);
j = cpvsub(joint->jAcc, jOld);
// apply impulse
apply_impulses(a, b, joint->r1, joint->r2, j);
}
static cpFloat
getImpulse(cpConstraint *joint)
{
return cpvlength(((cpPivotJoint *)joint)->jAcc);
}
static const cpConstraintClass klass = {
(cpConstraintPreStepImpl)preStep,
(cpConstraintApplyCachedImpulseImpl)applyCachedImpulse,
(cpConstraintApplyImpulseImpl)applyImpulse,
(cpConstraintGetImpulseImpl)getImpulse,
};
cpPivotJoint *
cpPivotJointAlloc(void)
{
return (cpPivotJoint *)cpcalloc(1, sizeof(cpPivotJoint));
}
cpPivotJoint *
cpPivotJointInit(cpPivotJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB)
{
cpConstraintInit((cpConstraint *)joint, &klass, a, b);
joint->anchorA = anchorA;
joint->anchorB = anchorB;
joint->jAcc = cpvzero;
return joint;
}
cpConstraint *
cpPivotJointNew2(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB)
{
return (cpConstraint *)cpPivotJointInit(cpPivotJointAlloc(), a, b, anchorA, anchorB);
}
cpConstraint *
cpPivotJointNew(cpBody *a, cpBody *b, cpVect pivot)
{
cpVect anchorA = (a ? cpBodyWorldToLocal(a, pivot) : pivot);
cpVect anchorB = (b ? cpBodyWorldToLocal(b, pivot) : pivot);
return cpPivotJointNew2(a, b, anchorA, anchorB);
}
cpBool
cpConstraintIsPivotJoint(const cpConstraint *constraint)
{
return (constraint->klass == &klass);
}
cpVect
cpPivotJointGetAnchorA(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsPivotJoint(constraint), "Constraint is not a pivot joint.");
return ((cpPivotJoint *)constraint)->anchorA;
}
void
cpPivotJointSetAnchorA(cpConstraint *constraint, cpVect anchorA)
{
cpAssertHard(cpConstraintIsPivotJoint(constraint), "Constraint is not a pivot joint.");
cpConstraintActivateBodies(constraint);
((cpPivotJoint *)constraint)->anchorA = anchorA;
}
cpVect
cpPivotJointGetAnchorB(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsPivotJoint(constraint), "Constraint is not a pivot joint.");
return ((cpPivotJoint *)constraint)->anchorB;
}
void
cpPivotJointSetAnchorB(cpConstraint *constraint, cpVect anchorB)
{
cpAssertHard(cpConstraintIsPivotJoint(constraint), "Constraint is not a pivot joint.");
cpConstraintActivateBodies(constraint);
((cpPivotJoint *)constraint)->anchorB = anchorB;
}

View File

@ -1,317 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "chipmunk/chipmunk_private.h"
#include "chipmunk_unsafe.h"
cpPolyShape *
cpPolyShapeAlloc(void)
{
return (cpPolyShape *)cpcalloc(1, sizeof(cpPolyShape));
}
static void
cpPolyShapeDestroy(cpPolyShape *poly)
{
if(poly->count > CP_POLY_SHAPE_INLINE_ALLOC){
cpfree(poly->planes);
}
}
static cpBB
cpPolyShapeCacheData(cpPolyShape *poly, cpTransform transform)
{
int count = poly->count;
struct cpSplittingPlane *dst = poly->planes;
struct cpSplittingPlane *src = dst + count;
cpFloat l = (cpFloat)INFINITY, r = -(cpFloat)INFINITY;
cpFloat b = (cpFloat)INFINITY, t = -(cpFloat)INFINITY;
for(int i=0; i<count; i++){
cpVect v = cpTransformPoint(transform, src[i].v0);
cpVect n = cpTransformVect(transform, src[i].n);
dst[i].v0 = v;
dst[i].n = n;
l = cpfmin(l, v.x);
r = cpfmax(r, v.x);
b = cpfmin(b, v.y);
t = cpfmax(t, v.y);
}
cpFloat radius = poly->r;
return (poly->shape.bb = cpBBNew(l - radius, b - radius, r + radius, t + radius));
}
static void
cpPolyShapePointQuery(cpPolyShape *poly, cpVect p, cpPointQueryInfo *info){
int count = poly->count;
struct cpSplittingPlane *planes = poly->planes;
cpFloat r = poly->r;
cpVect v0 = planes[count - 1].v0;
cpFloat minDist = INFINITY;
cpVect closestPoint = cpvzero;
cpVect closestNormal = cpvzero;
cpBool outside = cpFalse;
for(int i=0; i<count; i++){
cpVect v1 = planes[i].v0;
if(cpvdot(planes[i].n, cpvsub(p, v1)) > 0.0f) outside = cpTrue;
cpVect closest = cpClosetPointOnSegment(p, v0, v1);
cpFloat dist = cpvdist(p, closest);
if(dist < minDist){
minDist = dist;
closestPoint = closest;
closestNormal = planes[i].n;
}
v0 = v1;
}
cpFloat dist = (outside ? minDist : -minDist);
cpVect g = cpvmult(cpvsub(p, closestPoint), 1.0f/dist);
info->shape = (cpShape *)poly;
info->point = cpvadd(closestPoint, cpvmult(g, r));
info->distance = dist - r;
// Use the normal of the closest segment if the distance is small.
info->gradient = (minDist > MAGIC_EPSILON ? g : closestNormal);
}
static void
cpPolyShapeSegmentQuery(cpPolyShape *poly, cpVect a, cpVect b, cpFloat r2, cpSegmentQueryInfo *info)
{
struct cpSplittingPlane *planes = poly->planes;
int count = poly->count;
cpFloat r = poly->r;
cpFloat rsum = r + r2;
for(int i=0; i<count; i++){
cpVect n = planes[i].n;
cpFloat an = cpvdot(a, n);
cpFloat d = an - cpvdot(planes[i].v0, n) - rsum;
if(d < 0.0f) continue;
cpFloat bn = cpvdot(b, n);
cpFloat t = d/(an - bn);
if(t < 0.0f || 1.0f < t) continue;
cpVect point = cpvlerp(a, b, t);
cpFloat dt = cpvcross(n, point);
cpFloat dtMin = cpvcross(n, planes[(i - 1 + count)%count].v0);
cpFloat dtMax = cpvcross(n, planes[i].v0);
if(dtMin <= dt && dt <= dtMax){
info->shape = (cpShape *)poly;
info->point = cpvsub(cpvlerp(a, b, t), cpvmult(n, r2));
info->normal = n;
info->alpha = t;
}
}
// Also check against the beveled vertexes.
if(rsum > 0.0f){
for(int i=0; i<count; i++){
cpSegmentQueryInfo circle_info = {NULL, b, cpvzero, 1.0f};
CircleSegmentQuery(&poly->shape, planes[i].v0, r, a, b, r2, &circle_info);
if(circle_info.alpha < info->alpha) (*info) = circle_info;
}
}
}
static void
SetVerts(cpPolyShape *poly, int count, const cpVect *verts)
{
poly->count = count;
if(count <= CP_POLY_SHAPE_INLINE_ALLOC){
poly->planes = poly->_planes;
} else {
poly->planes = (struct cpSplittingPlane *)cpcalloc(2*count, sizeof(struct cpSplittingPlane));
}
for(int i=0; i<count; i++){
cpVect a = verts[(i - 1 + count)%count];
cpVect b = verts[i];
cpVect n = cpvnormalize(cpvrperp(cpvsub(b, a)));
poly->planes[i + count].v0 = b;
poly->planes[i + count].n = n;
}
}
static struct cpShapeMassInfo
cpPolyShapeMassInfo(cpFloat mass, int count, const cpVect *verts, cpFloat radius)
{
// TODO moment is approximate due to radius.
cpVect centroid = cpCentroidForPoly(count, verts);
struct cpShapeMassInfo info = {
mass, cpMomentForPoly(1.0f, count, verts, cpvneg(centroid), radius),
centroid,
cpAreaForPoly(count, verts, radius),
};
return info;
}
static const cpShapeClass polyClass = {
CP_POLY_SHAPE,
(cpShapeCacheDataImpl)cpPolyShapeCacheData,
(cpShapeDestroyImpl)cpPolyShapeDestroy,
(cpShapePointQueryImpl)cpPolyShapePointQuery,
(cpShapeSegmentQueryImpl)cpPolyShapeSegmentQuery,
};
cpPolyShape *
cpPolyShapeInit(cpPolyShape *poly, cpBody *body, int count, const cpVect *verts, cpTransform transform, cpFloat radius)
{
cpVect *hullVerts = (cpVect *)alloca(count*sizeof(cpVect));
// Transform the verts before building the hull in case of a negative scale.
for(int i=0; i<count; i++) hullVerts[i] = cpTransformPoint(transform, verts[i]);
unsigned int hullCount = cpConvexHull(count, hullVerts, hullVerts, NULL, 0.0);
return cpPolyShapeInitRaw(poly, body, hullCount, hullVerts, radius);
}
cpPolyShape *
cpPolyShapeInitRaw(cpPolyShape *poly, cpBody *body, int count, const cpVect *verts, cpFloat radius)
{
cpShapeInit((cpShape *)poly, &polyClass, body, cpPolyShapeMassInfo(0.0f, count, verts, radius));
SetVerts(poly, count, verts);
poly->r = radius;
return poly;
}
cpShape *
cpPolyShapeNew(cpBody *body, int count, const cpVect *verts, cpTransform transform, cpFloat radius)
{
return (cpShape *)cpPolyShapeInit(cpPolyShapeAlloc(), body, count, verts, transform, radius);
}
cpPolyShape *
cpBoxShapeInit(cpPolyShape *poly, cpBody *body, cpFloat width, cpFloat height, cpFloat radius)
{
cpFloat hw = width/2.0f;
cpFloat hh = height/2.0f;
return cpBoxShapeInit2(poly, body, cpBBNew(-hw, -hh, hw, hh), radius);
}
cpPolyShape *
cpBoxShapeInit2(cpPolyShape *poly, cpBody *body, cpBB box, cpFloat radius)
{
cpVect verts[] = {
cpv(box.r, box.b),
cpv(box.r, box.t),
cpv(box.l, box.t),
cpv(box.l, box.b),
};
return cpPolyShapeInitRaw(poly, body, 4, verts, radius);
}
cpShape *
cpBoxShapeNew(cpBody *body, cpFloat width, cpFloat height, cpFloat radius)
{
return (cpShape *)cpBoxShapeInit(cpPolyShapeAlloc(), body, width, height, radius);
}
cpShape *
cpBoxShapeNew2(cpBody *body, cpBB box, cpFloat radius)
{
return (cpShape *)cpBoxShapeInit2(cpPolyShapeAlloc(), body, box, radius);
}
int
cpPolyShapeGetCount(const cpShape *shape)
{
cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape.");
return ((cpPolyShape *)shape)->count;
}
cpVect
cpPolyShapeGetVert(const cpShape *shape, int i)
{
cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape.");
int count = cpPolyShapeGetCount(shape);
cpAssertHard(0 <= i && i < count, "Index out of range.");
return ((cpPolyShape *)shape)->planes[i + count].v0;
}
cpFloat
cpPolyShapeGetRadius(const cpShape *shape)
{
cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape.");
return ((cpPolyShape *)shape)->r;
}
// Unsafe API (chipmunk_unsafe.h)
void
cpPolyShapeSetVerts(cpShape *shape, int count, cpVect *verts, cpTransform transform)
{
cpVect *hullVerts = (cpVect *)alloca(count*sizeof(cpVect));
// Transform the verts before building the hull in case of a negative scale.
for(int i=0; i<count; i++) hullVerts[i] = cpTransformPoint(transform, verts[i]);
unsigned int hullCount = cpConvexHull(count, hullVerts, hullVerts, NULL, 0.0);
cpPolyShapeSetVertsRaw(shape, hullCount, hullVerts);
}
void
cpPolyShapeSetVertsRaw(cpShape *shape, int count, cpVect *verts)
{
cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape.");
cpPolyShape *poly = (cpPolyShape *)shape;
cpPolyShapeDestroy(poly);
SetVerts(poly, count, verts);
cpFloat mass = shape->massInfo.m;
shape->massInfo = cpPolyShapeMassInfo(shape->massInfo.m, count, verts, poly->r);
if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body);
}
void
cpPolyShapeSetRadius(cpShape *shape, cpFloat radius)
{
cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape.");
cpPolyShape *poly = (cpPolyShape *)shape;
poly->r = radius;
// TODO radius is not handled by moment/area
// cpFloat mass = shape->massInfo.m;
// shape->massInfo = cpPolyShapeMassInfo(shape->massInfo.m, poly->count, poly->verts, poly->r);
// if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body);
}

View File

@ -1,179 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "chipmunk/chipmunk_private.h"
static void
preStep(cpRatchetJoint *joint, cpFloat dt)
{
cpBody *a = joint->constraint.a;
cpBody *b = joint->constraint.b;
cpFloat angle = joint->angle;
cpFloat phase = joint->phase;
cpFloat ratchet = joint->ratchet;
cpFloat delta = b->a - a->a;
cpFloat diff = angle - delta;
cpFloat pdist = 0.0f;
if(diff*ratchet > 0.0f){
pdist = diff;
} else {
joint->angle = cpffloor((delta - phase)/ratchet)*ratchet + phase;
}
// calculate moment of inertia coefficient.
joint->iSum = 1.0f/(a->i_inv + b->i_inv);
// calculate bias velocity
cpFloat maxBias = joint->constraint.maxBias;
joint->bias = cpfclamp(-bias_coef(joint->constraint.errorBias, dt)*pdist/dt, -maxBias, maxBias);
// If the bias is 0, the joint is not at a limit. Reset the impulse.
if(!joint->bias) joint->jAcc = 0.0f;
}
static void
applyCachedImpulse(cpRatchetJoint *joint, cpFloat dt_coef)
{
cpBody *a = joint->constraint.a;
cpBody *b = joint->constraint.b;
cpFloat j = joint->jAcc*dt_coef;
a->w -= j*a->i_inv;
b->w += j*b->i_inv;
}
static void
applyImpulse(cpRatchetJoint *joint, cpFloat dt)
{
if(!joint->bias) return; // early exit
cpBody *a = joint->constraint.a;
cpBody *b = joint->constraint.b;
// compute relative rotational velocity
cpFloat wr = b->w - a->w;
cpFloat ratchet = joint->ratchet;
cpFloat jMax = joint->constraint.maxForce*dt;
// compute normal impulse
cpFloat j = -(joint->bias + wr)*joint->iSum;
cpFloat jOld = joint->jAcc;
joint->jAcc = cpfclamp((jOld + j)*ratchet, 0.0f, jMax*cpfabs(ratchet))/ratchet;
j = joint->jAcc - jOld;
// apply impulse
a->w -= j*a->i_inv;
b->w += j*b->i_inv;
}
static cpFloat
getImpulse(cpRatchetJoint *joint)
{
return cpfabs(joint->jAcc);
}
static const cpConstraintClass klass = {
(cpConstraintPreStepImpl)preStep,
(cpConstraintApplyCachedImpulseImpl)applyCachedImpulse,
(cpConstraintApplyImpulseImpl)applyImpulse,
(cpConstraintGetImpulseImpl)getImpulse,
};
cpRatchetJoint *
cpRatchetJointAlloc(void)
{
return (cpRatchetJoint *)cpcalloc(1, sizeof(cpRatchetJoint));
}
cpRatchetJoint *
cpRatchetJointInit(cpRatchetJoint *joint, cpBody *a, cpBody *b, cpFloat phase, cpFloat ratchet)
{
cpConstraintInit((cpConstraint *)joint, &klass, a, b);
joint->angle = 0.0f;
joint->phase = phase;
joint->ratchet = ratchet;
// STATIC_BODY_CHECK
joint->angle = (b ? b->a : 0.0f) - (a ? a->a : 0.0f);
return joint;
}
cpConstraint *
cpRatchetJointNew(cpBody *a, cpBody *b, cpFloat phase, cpFloat ratchet)
{
return (cpConstraint *)cpRatchetJointInit(cpRatchetJointAlloc(), a, b, phase, ratchet);
}
cpBool
cpConstraintIsRatchetJoint(const cpConstraint *constraint)
{
return (constraint->klass == &klass);
}
cpFloat
cpRatchetJointGetAngle(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsRatchetJoint(constraint), "Constraint is not a ratchet joint.");
return ((cpRatchetJoint *)constraint)->angle;
}
void
cpRatchetJointSetAngle(cpConstraint *constraint, cpFloat angle)
{
cpAssertHard(cpConstraintIsRatchetJoint(constraint), "Constraint is not a ratchet joint.");
cpConstraintActivateBodies(constraint);
((cpRatchetJoint *)constraint)->angle = angle;
}
cpFloat
cpRatchetJointGetPhase(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsRatchetJoint(constraint), "Constraint is not a ratchet joint.");
return ((cpRatchetJoint *)constraint)->phase;
}
void
cpRatchetJointSetPhase(cpConstraint *constraint, cpFloat phase)
{
cpAssertHard(cpConstraintIsRatchetJoint(constraint), "Constraint is not a ratchet joint.");
cpConstraintActivateBodies(constraint);
((cpRatchetJoint *)constraint)->phase = phase;
}
cpFloat
cpRatchetJointGetRatchet(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsRatchetJoint(constraint), "Constraint is not a ratchet joint.");
return ((cpRatchetJoint *)constraint)->ratchet;
}
void
cpRatchetJointSetRatchet(cpConstraint *constraint, cpFloat ratchet)
{
cpAssertHard(cpConstraintIsRatchetJoint(constraint), "Constraint is not a ratchet joint.");
cpConstraintActivateBodies(constraint);
((cpRatchetJoint *)constraint)->ratchet = ratchet;
}

View File

@ -1,160 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "chipmunk/chipmunk_private.h"
static void
preStep(cpRotaryLimitJoint *joint, cpFloat dt)
{
cpBody *a = joint->constraint.a;
cpBody *b = joint->constraint.b;
cpFloat dist = b->a - a->a;
cpFloat pdist = 0.0f;
if(dist > joint->max) {
pdist = joint->max - dist;
} else if(dist < joint->min) {
pdist = joint->min - dist;
}
// calculate moment of inertia coefficient.
joint->iSum = 1.0f/(a->i_inv + b->i_inv);
// calculate bias velocity
cpFloat maxBias = joint->constraint.maxBias;
joint->bias = cpfclamp(-bias_coef(joint->constraint.errorBias, dt)*pdist/dt, -maxBias, maxBias);
// If the bias is 0, the joint is not at a limit. Reset the impulse.
if(!joint->bias) joint->jAcc = 0.0f;
}
static void
applyCachedImpulse(cpRotaryLimitJoint *joint, cpFloat dt_coef)
{
cpBody *a = joint->constraint.a;
cpBody *b = joint->constraint.b;
cpFloat j = joint->jAcc*dt_coef;
a->w -= j*a->i_inv;
b->w += j*b->i_inv;
}
static void
applyImpulse(cpRotaryLimitJoint *joint, cpFloat dt)
{
if(!joint->bias) return; // early exit
cpBody *a = joint->constraint.a;
cpBody *b = joint->constraint.b;
// compute relative rotational velocity
cpFloat wr = b->w - a->w;
cpFloat jMax = joint->constraint.maxForce*dt;
// compute normal impulse
cpFloat j = -(joint->bias + wr)*joint->iSum;
cpFloat jOld = joint->jAcc;
if(joint->bias < 0.0f){
joint->jAcc = cpfclamp(jOld + j, 0.0f, jMax);
} else {
joint->jAcc = cpfclamp(jOld + j, -jMax, 0.0f);
}
j = joint->jAcc - jOld;
// apply impulse
a->w -= j*a->i_inv;
b->w += j*b->i_inv;
}
static cpFloat
getImpulse(cpRotaryLimitJoint *joint)
{
return cpfabs(joint->jAcc);
}
static const cpConstraintClass klass = {
(cpConstraintPreStepImpl)preStep,
(cpConstraintApplyCachedImpulseImpl)applyCachedImpulse,
(cpConstraintApplyImpulseImpl)applyImpulse,
(cpConstraintGetImpulseImpl)getImpulse,
};
cpRotaryLimitJoint *
cpRotaryLimitJointAlloc(void)
{
return (cpRotaryLimitJoint *)cpcalloc(1, sizeof(cpRotaryLimitJoint));
}
cpRotaryLimitJoint *
cpRotaryLimitJointInit(cpRotaryLimitJoint *joint, cpBody *a, cpBody *b, cpFloat min, cpFloat max)
{
cpConstraintInit((cpConstraint *)joint, &klass, a, b);
joint->min = min;
joint->max = max;
joint->jAcc = 0.0f;
return joint;
}
cpConstraint *
cpRotaryLimitJointNew(cpBody *a, cpBody *b, cpFloat min, cpFloat max)
{
return (cpConstraint *)cpRotaryLimitJointInit(cpRotaryLimitJointAlloc(), a, b, min, max);
}
cpBool
cpConstraintIsRotaryLimitJoint(const cpConstraint *constraint)
{
return (constraint->klass == &klass);
}
cpFloat
cpRotaryLimitJointGetMin(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsRotaryLimitJoint(constraint), "Constraint is not a rotary limit joint.");
return ((cpRotaryLimitJoint *)constraint)->min;
}
void
cpRotaryLimitJointSetMin(cpConstraint *constraint, cpFloat min)
{
cpAssertHard(cpConstraintIsRotaryLimitJoint(constraint), "Constraint is not a rotary limit joint.");
cpConstraintActivateBodies(constraint);
((cpRotaryLimitJoint *)constraint)->min = min;
}
cpFloat
cpRotaryLimitJointGetMax(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsRotaryLimitJoint(constraint), "Constraint is not a rotary limit joint.");
return ((cpRotaryLimitJoint *)constraint)->max;
}
void
cpRotaryLimitJointSetMax(cpConstraint *constraint, cpFloat max)
{
cpAssertHard(cpConstraintIsRotaryLimitJoint(constraint), "Constraint is not a rotary limit joint.");
cpConstraintActivateBodies(constraint);
((cpRotaryLimitJoint *)constraint)->max = max;
}

View File

@ -1,603 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "chipmunk/chipmunk_private.h"
#include "chipmunk_unsafe.h"
#define CP_DefineShapeGetter(struct, type, member, name) \
CP_DeclareShapeGetter(struct, type, name){ \
cpAssertHard(shape->klass == &struct##Class, "shape is not a "#struct); \
return ((struct *)shape)->member; \
}
cpShape *
cpShapeInit(cpShape *shape, const cpShapeClass *klass, cpBody *body, struct cpShapeMassInfo massInfo)
{
shape->klass = klass;
shape->body = body;
shape->massInfo = massInfo;
shape->sensor = 0;
shape->e = 0.0f;
shape->u = 0.0f;
shape->surfaceV = cpvzero;
shape->type = 0;
shape->filter.group = CP_NO_GROUP;
shape->filter.categories = CP_ALL_CATEGORIES;
shape->filter.mask = CP_ALL_CATEGORIES;
shape->userData = NULL;
shape->space = NULL;
shape->next = NULL;
shape->prev = NULL;
return shape;
}
void
cpShapeDestroy(cpShape *shape)
{
if(shape->klass && shape->klass->destroy) shape->klass->destroy(shape);
}
void
cpShapeFree(cpShape *shape)
{
if(shape){
cpShapeDestroy(shape);
cpfree(shape);
}
}
cpSpace *
cpShapeGetSpace(const cpShape *shape)
{
return shape->space;
}
cpBody *
cpShapeGetBody(const cpShape *shape)
{
return shape->body;
}
void
cpShapeSetBody(cpShape *shape, cpBody *body)
{
cpAssertHard(!cpShapeActive(shape), "You cannot change the body on an active shape. You must remove the shape from the space before changing the body.");
shape->body = body;
}
cpFloat cpShapeGetMass(cpShape *shape){ return shape->massInfo.m; }
void
cpShapeSetMass(cpShape *shape, cpFloat mass){
cpBody *body = shape->body;
cpBodyActivate(body);
shape->massInfo.m = mass;
cpBodyAccumulateMassFromShapes(body);
}
cpFloat cpShapeGetDensity(cpShape *shape){ return shape->massInfo.m/shape->massInfo.area; }
void cpShapeSetDensity(cpShape *shape, cpFloat density){ cpShapeSetMass(shape, density*shape->massInfo.area); }
cpFloat cpShapeGetMoment(cpShape *shape){ return shape->massInfo.m*shape->massInfo.i; }
cpFloat cpShapeGetArea(cpShape *shape){ return shape->massInfo.area; }
cpVect cpShapeGetCenterOfGravity(cpShape *shape) { return shape->massInfo.cog; }
cpBB
cpShapeGetBB(const cpShape *shape)
{
return shape->bb;
}
cpBool
cpShapeGetSensor(const cpShape *shape)
{
return shape->sensor;
}
void
cpShapeSetSensor(cpShape *shape, cpBool sensor)
{
cpBodyActivate(shape->body);
shape->sensor = sensor;
}
cpFloat
cpShapeGetElasticity(const cpShape *shape)
{
return shape->e;
}
void
cpShapeSetElasticity(cpShape *shape, cpFloat elasticity)
{
cpAssertHard(elasticity >= 0.0f, "Elasticity must be positive and non-zero.");
cpBodyActivate(shape->body);
shape->e = elasticity;
}
cpFloat
cpShapeGetFriction(const cpShape *shape)
{
return shape->u;
}
void
cpShapeSetFriction(cpShape *shape, cpFloat friction)
{
cpAssertHard(friction >= 0.0f, "Friction must be postive and non-zero.");
cpBodyActivate(shape->body);
shape->u = friction;
}
cpVect
cpShapeGetSurfaceVelocity(const cpShape *shape)
{
return shape->surfaceV;
}
void
cpShapeSetSurfaceVelocity(cpShape *shape, cpVect surfaceVelocity)
{
cpBodyActivate(shape->body);
shape->surfaceV = surfaceVelocity;
}
cpDataPointer
cpShapeGetUserData(const cpShape *shape)
{
return shape->userData;
}
void
cpShapeSetUserData(cpShape *shape, cpDataPointer userData)
{
shape->userData = userData;
}
cpCollisionType
cpShapeGetCollisionType(const cpShape *shape)
{
return shape->type;
}
void
cpShapeSetCollisionType(cpShape *shape, cpCollisionType collisionType)
{
cpBodyActivate(shape->body);
shape->type = collisionType;
}
cpShapeFilter
cpShapeGetFilter(const cpShape *shape)
{
return shape->filter;
}
void
cpShapeSetFilter(cpShape *shape, cpShapeFilter filter)
{
cpBodyActivate(shape->body);
shape->filter = filter;
}
cpBB
cpShapeCacheBB(cpShape *shape)
{
return cpShapeUpdate(shape, shape->body->transform);
}
cpBB
cpShapeUpdate(cpShape *shape, cpTransform transform)
{
return (shape->bb = shape->klass->cacheData(shape, transform));
}
cpFloat
cpShapePointQuery(const cpShape *shape, cpVect p, cpPointQueryInfo *info)
{
cpPointQueryInfo blank = {NULL, cpvzero, INFINITY, cpvzero};
if(info){
(*info) = blank;
} else {
info = &blank;
}
shape->klass->pointQuery(shape, p, info);
return info->distance;
}
cpBool
cpShapeSegmentQuery(const cpShape *shape, cpVect a, cpVect b, cpFloat radius, cpSegmentQueryInfo *info){
cpSegmentQueryInfo blank = {NULL, b, cpvzero, 1.0f};
if(info){
(*info) = blank;
} else {
info = &blank;
}
cpPointQueryInfo nearest;
shape->klass->pointQuery(shape, a, &nearest);
if(nearest.distance <= radius){
info->shape = shape;
info->alpha = 0.0;
info->normal = cpvnormalize(cpvsub(a, nearest.point));
} else {
shape->klass->segmentQuery(shape, a, b, radius, info);
}
return (info->shape != NULL);
}
cpContactPointSet
cpShapesCollide(const cpShape *a, const cpShape *b)
{
struct cpContact contacts[CP_MAX_CONTACTS_PER_ARBITER];
struct cpCollisionInfo info = cpCollide(a, b, 0, contacts);
cpContactPointSet set;
set.count = info.count;
// cpCollideShapes() may have swapped the contact order. Flip the normal.
cpBool swapped = (a != info.a);
set.normal = (swapped ? cpvneg(info.n) : info.n);
for(int i=0; i<info.count; i++){
// cpCollideShapesInfo() returns contacts with absolute positions.
cpVect p1 = contacts[i].r1;
cpVect p2 = contacts[i].r2;
set.points[i].pointA = (swapped ? p2 : p1);
set.points[i].pointB = (swapped ? p1 : p2);
set.points[i].distance = cpvdot(cpvsub(p2, p1), set.normal);
}
return set;
}
cpCircleShape *
cpCircleShapeAlloc(void)
{
return (cpCircleShape *)cpcalloc(1, sizeof(cpCircleShape));
}
static cpBB
cpCircleShapeCacheData(cpCircleShape *circle, cpTransform transform)
{
cpVect c = circle->tc = cpTransformPoint(transform, circle->c);
return cpBBNewForCircle(c, circle->r);
}
static void
cpCircleShapePointQuery(cpCircleShape *circle, cpVect p, cpPointQueryInfo *info)
{
cpVect delta = cpvsub(p, circle->tc);
cpFloat d = cpvlength(delta);
cpFloat r = circle->r;
info->shape = (cpShape *)circle;
info->point = cpvadd(circle->tc, cpvmult(delta, r/d)); // TODO: div/0
info->distance = d - r;
// Use up for the gradient if the distance is very small.
info->gradient = (d > MAGIC_EPSILON ? cpvmult(delta, 1.0f/d) : cpv(0.0f, 1.0f));
}
static void
cpCircleShapeSegmentQuery(cpCircleShape *circle, cpVect a, cpVect b, cpFloat radius, cpSegmentQueryInfo *info)
{
CircleSegmentQuery((cpShape *)circle, circle->tc, circle->r, a, b, radius, info);
}
static struct cpShapeMassInfo
cpCircleShapeMassInfo(cpFloat mass, cpFloat radius, cpVect center)
{
struct cpShapeMassInfo info = {
mass, cpMomentForCircle(1.0f, 0.0f, radius, cpvzero),
center,
cpAreaForCircle(0.0f, radius),
};
return info;
}
static const cpShapeClass cpCircleShapeClass = {
CP_CIRCLE_SHAPE,
(cpShapeCacheDataImpl)cpCircleShapeCacheData,
NULL,
(cpShapePointQueryImpl)cpCircleShapePointQuery,
(cpShapeSegmentQueryImpl)cpCircleShapeSegmentQuery,
};
cpCircleShape *
cpCircleShapeInit(cpCircleShape *circle, cpBody *body, cpFloat radius, cpVect offset)
{
circle->c = offset;
circle->r = radius;
cpShapeInit((cpShape *)circle, &cpCircleShapeClass, body, cpCircleShapeMassInfo(0.0f, radius, offset));
return circle;
}
cpShape *
cpCircleShapeNew(cpBody *body, cpFloat radius, cpVect offset)
{
return (cpShape *)cpCircleShapeInit(cpCircleShapeAlloc(), body, radius, offset);
}
cpVect
cpCircleShapeGetOffset(const cpShape *shape)
{
cpAssertHard(shape->klass == &cpCircleShapeClass, "Shape is not a circle shape.");
return ((cpCircleShape *)shape)->c;
}
cpFloat
cpCircleShapeGetRadius(const cpShape *shape)
{
cpAssertHard(shape->klass == &cpCircleShapeClass, "Shape is not a circle shape.");
return ((cpCircleShape *)shape)->r;
}
cpSegmentShape *
cpSegmentShapeAlloc(void)
{
return (cpSegmentShape *)cpcalloc(1, sizeof(cpSegmentShape));
}
static cpBB
cpSegmentShapeCacheData(cpSegmentShape *seg, cpTransform transform)
{
seg->ta = cpTransformPoint(transform, seg->a);
seg->tb = cpTransformPoint(transform, seg->b);
seg->tn = cpTransformVect(transform, seg->n);
cpFloat l,r,b,t;
if(seg->ta.x < seg->tb.x){
l = seg->ta.x;
r = seg->tb.x;
} else {
l = seg->tb.x;
r = seg->ta.x;
}
if(seg->ta.y < seg->tb.y){
b = seg->ta.y;
t = seg->tb.y;
} else {
b = seg->tb.y;
t = seg->ta.y;
}
cpFloat rad = seg->r;
return cpBBNew(l - rad, b - rad, r + rad, t + rad);
}
static void
cpSegmentShapePointQuery(cpSegmentShape *seg, cpVect p, cpPointQueryInfo *info)
{
cpVect closest = cpClosetPointOnSegment(p, seg->ta, seg->tb);
cpVect delta = cpvsub(p, closest);
cpFloat d = cpvlength(delta);
cpFloat r = seg->r;
cpVect g = cpvmult(delta, 1.0f/d);
info->shape = (cpShape *)seg;
info->point = (d ? cpvadd(closest, cpvmult(g, r)) : closest);
info->distance = d - r;
// Use the segment's normal if the distance is very small.
info->gradient = (d > MAGIC_EPSILON ? g : seg->n);
}
static void
cpSegmentShapeSegmentQuery(cpSegmentShape *seg, cpVect a, cpVect b, cpFloat r2, cpSegmentQueryInfo *info)
{
cpVect n = seg->tn;
cpFloat d = cpvdot(cpvsub(seg->ta, a), n);
cpFloat r = seg->r + r2;
cpVect flipped_n = (d > 0.0f ? cpvneg(n) : n);
cpVect seg_offset = cpvsub(cpvmult(flipped_n, r), a);
// Make the endpoints relative to 'a' and move them by the thickness of the segment.
cpVect seg_a = cpvadd(seg->ta, seg_offset);
cpVect seg_b = cpvadd(seg->tb, seg_offset);
cpVect delta = cpvsub(b, a);
if(cpvcross(delta, seg_a)*cpvcross(delta, seg_b) <= 0.0f){
cpFloat d_offset = d + (d > 0.0f ? -r : r);
cpFloat ad = -d_offset;
cpFloat bd = cpvdot(delta, n) - d_offset;
if(ad*bd < 0.0f){
cpFloat t = ad/(ad - bd);
info->shape = (cpShape *)seg;
info->point = cpvsub(cpvlerp(a, b, t), cpvmult(flipped_n, r2));
info->normal = flipped_n;
info->alpha = t;
}
} else if(r != 0.0f){
cpSegmentQueryInfo info1 = {NULL, b, cpvzero, 1.0f};
cpSegmentQueryInfo info2 = {NULL, b, cpvzero, 1.0f};
CircleSegmentQuery((cpShape *)seg, seg->ta, seg->r, a, b, r2, &info1);
CircleSegmentQuery((cpShape *)seg, seg->tb, seg->r, a, b, r2, &info2);
if(info1.alpha < info2.alpha){
(*info) = info1;
} else {
(*info) = info2;
}
}
}
static struct cpShapeMassInfo
cpSegmentShapeMassInfo(cpFloat mass, cpVect a, cpVect b, cpFloat r)
{
struct cpShapeMassInfo info = {
mass, cpMomentForBox(1.0f, cpvdist(a, b) + 2.0f*r, 2.0f*r), // TODO is an approximation.
cpvlerp(a, b, 0.5f),
cpAreaForSegment(a, b, r),
};
return info;
}
static const cpShapeClass cpSegmentShapeClass = {
CP_SEGMENT_SHAPE,
(cpShapeCacheDataImpl)cpSegmentShapeCacheData,
NULL,
(cpShapePointQueryImpl)cpSegmentShapePointQuery,
(cpShapeSegmentQueryImpl)cpSegmentShapeSegmentQuery,
};
cpSegmentShape *
cpSegmentShapeInit(cpSegmentShape *seg, cpBody *body, cpVect a, cpVect b, cpFloat r)
{
seg->a = a;
seg->b = b;
seg->n = cpvrperp(cpvnormalize(cpvsub(b, a)));
seg->r = r;
seg->a_tangent = cpvzero;
seg->b_tangent = cpvzero;
cpShapeInit((cpShape *)seg, &cpSegmentShapeClass, body, cpSegmentShapeMassInfo(0.0f, a, b, r));
return seg;
}
cpShape*
cpSegmentShapeNew(cpBody *body, cpVect a, cpVect b, cpFloat r)
{
return (cpShape *)cpSegmentShapeInit(cpSegmentShapeAlloc(), body, a, b, r);
}
cpVect
cpSegmentShapeGetA(const cpShape *shape)
{
cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape.");
return ((cpSegmentShape *)shape)->a;
}
cpVect
cpSegmentShapeGetB(const cpShape *shape)
{
cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape.");
return ((cpSegmentShape *)shape)->b;
}
cpVect
cpSegmentShapeGetNormal(const cpShape *shape)
{
cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape.");
return ((cpSegmentShape *)shape)->n;
}
cpFloat
cpSegmentShapeGetRadius(const cpShape *shape)
{
cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape.");
return ((cpSegmentShape *)shape)->r;
}
void
cpSegmentShapeSetNeighbors(cpShape *shape, cpVect prev, cpVect next)
{
cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape.");
cpSegmentShape *seg = (cpSegmentShape *)shape;
seg->a_tangent = cpvsub(prev, seg->a);
seg->b_tangent = cpvsub(next, seg->b);
}
// Unsafe API (chipmunk_unsafe.h)
// TODO setters should wake the shape up?
void
cpCircleShapeSetRadius(cpShape *shape, cpFloat radius)
{
cpAssertHard(shape->klass == &cpCircleShapeClass, "Shape is not a circle shape.");
cpCircleShape *circle = (cpCircleShape *)shape;
circle->r = radius;
cpFloat mass = shape->massInfo.m;
shape->massInfo = cpCircleShapeMassInfo(mass, circle->r, circle->c);
if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body);
}
void
cpCircleShapeSetOffset(cpShape *shape, cpVect offset)
{
cpAssertHard(shape->klass == &cpCircleShapeClass, "Shape is not a circle shape.");
cpCircleShape *circle = (cpCircleShape *)shape;
circle->c = offset;
cpFloat mass = shape->massInfo.m;
shape->massInfo = cpCircleShapeMassInfo(shape->massInfo.m, circle->r, circle->c);
if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body);
}
void
cpSegmentShapeSetEndpoints(cpShape *shape, cpVect a, cpVect b)
{
cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape.");
cpSegmentShape *seg = (cpSegmentShape *)shape;
seg->a = a;
seg->b = b;
seg->n = cpvperp(cpvnormalize(cpvsub(b, a)));
cpFloat mass = shape->massInfo.m;
shape->massInfo = cpSegmentShapeMassInfo(shape->massInfo.m, seg->a, seg->b, seg->r);
if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body);
}
void
cpSegmentShapeSetRadius(cpShape *shape, cpFloat radius)
{
cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape.");
cpSegmentShape *seg = (cpSegmentShape *)shape;
seg->r = radius;
cpFloat mass = shape->massInfo.m;
shape->massInfo = cpSegmentShapeMassInfo(shape->massInfo.m, seg->a, seg->b, seg->r);
if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body);
}

View File

@ -1,123 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "chipmunk/chipmunk_private.h"
static void
preStep(cpSimpleMotor *joint, cpFloat dt)
{
cpBody *a = joint->constraint.a;
cpBody *b = joint->constraint.b;
// calculate moment of inertia coefficient.
joint->iSum = 1.0f/(a->i_inv + b->i_inv);
}
static void
applyCachedImpulse(cpSimpleMotor *joint, cpFloat dt_coef)
{
cpBody *a = joint->constraint.a;
cpBody *b = joint->constraint.b;
cpFloat j = joint->jAcc*dt_coef;
a->w -= j*a->i_inv;
b->w += j*b->i_inv;
}
static void
applyImpulse(cpSimpleMotor *joint, cpFloat dt)
{
cpBody *a = joint->constraint.a;
cpBody *b = joint->constraint.b;
// compute relative rotational velocity
cpFloat wr = b->w - a->w + joint->rate;
cpFloat jMax = joint->constraint.maxForce*dt;
// compute normal impulse
cpFloat j = -wr*joint->iSum;
cpFloat jOld = joint->jAcc;
joint->jAcc = cpfclamp(jOld + j, -jMax, jMax);
j = joint->jAcc - jOld;
// apply impulse
a->w -= j*a->i_inv;
b->w += j*b->i_inv;
}
static cpFloat
getImpulse(cpSimpleMotor *joint)
{
return cpfabs(joint->jAcc);
}
static const cpConstraintClass klass = {
(cpConstraintPreStepImpl)preStep,
(cpConstraintApplyCachedImpulseImpl)applyCachedImpulse,
(cpConstraintApplyImpulseImpl)applyImpulse,
(cpConstraintGetImpulseImpl)getImpulse,
};
cpSimpleMotor *
cpSimpleMotorAlloc(void)
{
return (cpSimpleMotor *)cpcalloc(1, sizeof(cpSimpleMotor));
}
cpSimpleMotor *
cpSimpleMotorInit(cpSimpleMotor *joint, cpBody *a, cpBody *b, cpFloat rate)
{
cpConstraintInit((cpConstraint *)joint, &klass, a, b);
joint->rate = rate;
joint->jAcc = 0.0f;
return joint;
}
cpConstraint *
cpSimpleMotorNew(cpBody *a, cpBody *b, cpFloat rate)
{
return (cpConstraint *)cpSimpleMotorInit(cpSimpleMotorAlloc(), a, b, rate);
}
cpBool
cpConstraintIsSimpleMotor(const cpConstraint *constraint)
{
return (constraint->klass == &klass);
}
cpFloat
cpSimpleMotorGetRate(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsSimpleMotor(constraint), "Constraint is not a pin joint.");
return ((cpSimpleMotor *)constraint)->rate;
}
void
cpSimpleMotorSetRate(cpConstraint *constraint, cpFloat rate)
{
cpAssertHard(cpConstraintIsSimpleMotor(constraint), "Constraint is not a pin joint.");
cpConstraintActivateBodies(constraint);
((cpSimpleMotor *)constraint)->rate = rate;
}

View File

@ -1,195 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "chipmunk/chipmunk_private.h"
static void
preStep(cpSlideJoint *joint, cpFloat dt)
{
cpBody *a = joint->constraint.a;
cpBody *b = joint->constraint.b;
joint->r1 = cpTransformVect(a->transform, cpvsub(joint->anchorA, a->cog));
joint->r2 = cpTransformVect(b->transform, cpvsub(joint->anchorB, b->cog));
cpVect delta = cpvsub(cpvadd(b->p, joint->r2), cpvadd(a->p, joint->r1));
cpFloat dist = cpvlength(delta);
cpFloat pdist = 0.0f;
if(dist > joint->max) {
pdist = dist - joint->max;
joint->n = cpvnormalize(delta);
} else if(dist < joint->min) {
pdist = joint->min - dist;
joint->n = cpvneg(cpvnormalize(delta));
} else {
joint->n = cpvzero;
joint->jnAcc = 0.0f;
}
// calculate mass normal
joint->nMass = 1.0f/k_scalar(a, b, joint->r1, joint->r2, joint->n);
// calculate bias velocity
cpFloat maxBias = joint->constraint.maxBias;
joint->bias = cpfclamp(-bias_coef(joint->constraint.errorBias, dt)*pdist/dt, -maxBias, maxBias);
}
static void
applyCachedImpulse(cpSlideJoint *joint, cpFloat dt_coef)
{
cpBody *a = joint->constraint.a;
cpBody *b = joint->constraint.b;
cpVect j = cpvmult(joint->n, joint->jnAcc*dt_coef);
apply_impulses(a, b, joint->r1, joint->r2, j);
}
static void
applyImpulse(cpSlideJoint *joint, cpFloat dt)
{
if(cpveql(joint->n, cpvzero)) return; // early exit
cpBody *a = joint->constraint.a;
cpBody *b = joint->constraint.b;
cpVect n = joint->n;
cpVect r1 = joint->r1;
cpVect r2 = joint->r2;
// compute relative velocity
cpVect vr = relative_velocity(a, b, r1, r2);
cpFloat vrn = cpvdot(vr, n);
// compute normal impulse
cpFloat jn = (joint->bias - vrn)*joint->nMass;
cpFloat jnOld = joint->jnAcc;
joint->jnAcc = cpfclamp(jnOld + jn, -joint->constraint.maxForce*dt, 0.0f);
jn = joint->jnAcc - jnOld;
// apply impulse
apply_impulses(a, b, joint->r1, joint->r2, cpvmult(n, jn));
}
static cpFloat
getImpulse(cpConstraint *joint)
{
return cpfabs(((cpSlideJoint *)joint)->jnAcc);
}
static const cpConstraintClass klass = {
(cpConstraintPreStepImpl)preStep,
(cpConstraintApplyCachedImpulseImpl)applyCachedImpulse,
(cpConstraintApplyImpulseImpl)applyImpulse,
(cpConstraintGetImpulseImpl)getImpulse,
};
cpSlideJoint *
cpSlideJointAlloc(void)
{
return (cpSlideJoint *)cpcalloc(1, sizeof(cpSlideJoint));
}
cpSlideJoint *
cpSlideJointInit(cpSlideJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat min, cpFloat max)
{
cpConstraintInit((cpConstraint *)joint, &klass, a, b);
joint->anchorA = anchorA;
joint->anchorB = anchorB;
joint->min = min;
joint->max = max;
joint->jnAcc = 0.0f;
return joint;
}
cpConstraint *
cpSlideJointNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat min, cpFloat max)
{
return (cpConstraint *)cpSlideJointInit(cpSlideJointAlloc(), a, b, anchorA, anchorB, min, max);
}
cpBool
cpConstraintIsSlideJoint(const cpConstraint *constraint)
{
return (constraint->klass == &klass);
}
cpVect
cpSlideJointGetAnchorA(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint.");
return ((cpSlideJoint *)constraint)->anchorA;
}
void
cpSlideJointSetAnchorA(cpConstraint *constraint, cpVect anchorA)
{
cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint.");
cpConstraintActivateBodies(constraint);
((cpSlideJoint *)constraint)->anchorA = anchorA;
}
cpVect
cpSlideJointGetAnchorB(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint.");
return ((cpSlideJoint *)constraint)->anchorB;
}
void
cpSlideJointSetAnchorB(cpConstraint *constraint, cpVect anchorB)
{
cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint.");
cpConstraintActivateBodies(constraint);
((cpSlideJoint *)constraint)->anchorB = anchorB;
}
cpFloat
cpSlideJointGetMin(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint.");
return ((cpSlideJoint *)constraint)->min;
}
void
cpSlideJointSetMin(cpConstraint *constraint, cpFloat min)
{
cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint.");
cpConstraintActivateBodies(constraint);
((cpSlideJoint *)constraint)->min = min;
}
cpFloat
cpSlideJointGetMax(const cpConstraint *constraint)
{
cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint.");
return ((cpSlideJoint *)constraint)->max;
}
void
cpSlideJointSetMax(cpConstraint *constraint, cpFloat max)
{
cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint.");
cpConstraintActivateBodies(constraint);
((cpSlideJoint *)constraint)->max = max;
}

View File

@ -1,701 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdio.h>
#include <string.h>
#include "chipmunk/chipmunk_private.h"
//MARK: Contact Set Helpers
// Equal function for arbiterSet.
static cpBool
arbiterSetEql(cpShape **shapes, cpArbiter *arb)
{
cpShape *a = shapes[0];
cpShape *b = shapes[1];
return ((a == arb->a && b == arb->b) || (b == arb->a && a == arb->b));
}
//MARK: Collision Handler Set HelperFunctions
// Equals function for collisionHandlers.
static cpBool
handlerSetEql(cpCollisionHandler *check, cpCollisionHandler *pair)
{
return (
(check->typeA == pair->typeA && check->typeB == pair->typeB) ||
(check->typeB == pair->typeA && check->typeA == pair->typeB)
);
}
// Transformation function for collisionHandlers.
static void *
handlerSetTrans(cpCollisionHandler *handler, void *unused)
{
cpCollisionHandler *copy = (cpCollisionHandler *)cpcalloc(1, sizeof(cpCollisionHandler));
(*copy) = (*handler);
return copy;
}
//MARK: Misc Helper Funcs
// Default collision functions.
static cpBool
DefaultBegin(cpArbiter *arb, cpSpace *space, void *data){
cpBool retA = cpArbiterCallWildcardBeginA(arb, space);
cpBool retB = cpArbiterCallWildcardBeginB(arb, space);
return retA && retB;
}
static cpBool
DefaultPreSolve(cpArbiter *arb, cpSpace *space, void *data){
cpBool retA = cpArbiterCallWildcardPreSolveA(arb, space);
cpBool retB = cpArbiterCallWildcardPreSolveB(arb, space);
return retA && retB;
}
static void
DefaultPostSolve(cpArbiter *arb, cpSpace *space, void *data){
cpArbiterCallWildcardPostSolveA(arb, space);
cpArbiterCallWildcardPostSolveB(arb, space);
}
static void
DefaultSeparate(cpArbiter *arb, cpSpace *space, void *data){
cpArbiterCallWildcardSeparateA(arb, space);
cpArbiterCallWildcardSeparateB(arb, space);
}
// Use the wildcard identifier since the default handler should never match any type pair.
static cpCollisionHandler cpCollisionHandlerDefault = {
CP_WILDCARD_COLLISION_TYPE, CP_WILDCARD_COLLISION_TYPE,
DefaultBegin, DefaultPreSolve, DefaultPostSolve, DefaultSeparate, NULL
};
static cpBool AlwaysCollide(cpArbiter *arb, cpSpace *space, void *data){return cpTrue;}
static void DoNothing(cpArbiter *arb, cpSpace *space, void *data){}
cpCollisionHandler cpCollisionHandlerDoNothing = {
CP_WILDCARD_COLLISION_TYPE, CP_WILDCARD_COLLISION_TYPE,
AlwaysCollide, AlwaysCollide, DoNothing, DoNothing, NULL
};
// function to get the estimated velocity of a shape for the cpBBTree.
static cpVect ShapeVelocityFunc(cpShape *shape){return shape->body->v;}
// Used for disposing of collision handlers.
static void FreeWrap(void *ptr, void *unused){cpfree(ptr);}
//MARK: Memory Management Functions
cpSpace *
cpSpaceAlloc(void)
{
return (cpSpace *)cpcalloc(1, sizeof(cpSpace));
}
cpSpace*
cpSpaceInit(cpSpace *space)
{
#ifndef NDEBUG
static cpBool done = cpFalse;
if(!done){
printf("Initializing cpSpace - Chipmunk v%s (Debug Enabled)\n", cpVersionString);
printf("Compile with -DNDEBUG defined to disable debug mode and runtime assertion checks\n");
done = cpTrue;
}
#endif
space->iterations = 10;
space->gravity = cpvzero;
space->damping = 1.0f;
space->collisionSlop = 0.1f;
space->collisionBias = cpfpow(1.0f - 0.1f, 60.0f);
space->collisionPersistence = 3;
space->locked = 0;
space->stamp = 0;
space->shapeIDCounter = 0;
space->staticShapes = cpBBTreeNew((cpSpatialIndexBBFunc)cpShapeGetBB, NULL);
space->dynamicShapes = cpBBTreeNew((cpSpatialIndexBBFunc)cpShapeGetBB, space->staticShapes);
cpBBTreeSetVelocityFunc(space->dynamicShapes, (cpBBTreeVelocityFunc)ShapeVelocityFunc);
space->allocatedBuffers = cpArrayNew(0);
space->dynamicBodies = cpArrayNew(0);
space->staticBodies = cpArrayNew(0);
space->sleepingComponents = cpArrayNew(0);
space->rousedBodies = cpArrayNew(0);
space->sleepTimeThreshold = INFINITY;
space->idleSpeedThreshold = 0.0f;
space->arbiters = cpArrayNew(0);
space->pooledArbiters = cpArrayNew(0);
space->contactBuffersHead = NULL;
space->cachedArbiters = cpHashSetNew(0, (cpHashSetEqlFunc)arbiterSetEql);
space->constraints = cpArrayNew(0);
space->usesWildcards = cpFalse;
space->defaultHandler = cpCollisionHandlerDoNothing;
space->collisionHandlers = cpHashSetNew(0, (cpHashSetEqlFunc)handlerSetEql);
space->postStepCallbacks = cpArrayNew(0);
space->skipPostStep = cpFalse;
cpBody *staticBody = cpBodyInit(&space->_staticBody, 0.0f, 0.0f);
cpBodySetType(staticBody, CP_BODY_TYPE_STATIC);
cpSpaceSetStaticBody(space, staticBody);
return space;
}
cpSpace*
cpSpaceNew(void)
{
return cpSpaceInit(cpSpaceAlloc());
}
static void cpBodyActivateWrap(cpBody *body, void *unused){cpBodyActivate(body);}
void
cpSpaceDestroy(cpSpace *space)
{
cpSpaceEachBody(space, (cpSpaceBodyIteratorFunc)cpBodyActivateWrap, NULL);
cpSpatialIndexFree(space->staticShapes);
cpSpatialIndexFree(space->dynamicShapes);
cpArrayFree(space->dynamicBodies);
cpArrayFree(space->staticBodies);
cpArrayFree(space->sleepingComponents);
cpArrayFree(space->rousedBodies);
cpArrayFree(space->constraints);
cpHashSetFree(space->cachedArbiters);
cpArrayFree(space->arbiters);
cpArrayFree(space->pooledArbiters);
if(space->allocatedBuffers){
cpArrayFreeEach(space->allocatedBuffers, cpfree);
cpArrayFree(space->allocatedBuffers);
}
if(space->postStepCallbacks){
cpArrayFreeEach(space->postStepCallbacks, cpfree);
cpArrayFree(space->postStepCallbacks);
}
if(space->collisionHandlers) cpHashSetEach(space->collisionHandlers, FreeWrap, NULL);
cpHashSetFree(space->collisionHandlers);
}
void
cpSpaceFree(cpSpace *space)
{
if(space){
cpSpaceDestroy(space);
cpfree(space);
}
}
//MARK: Basic properties:
int
cpSpaceGetIterations(const cpSpace *space)
{
return space->iterations;
}
void
cpSpaceSetIterations(cpSpace *space, int iterations)
{
cpAssertHard(iterations > 0, "Iterations must be positive and non-zero.");
space->iterations = iterations;
}
cpVect
cpSpaceGetGravity(const cpSpace *space)
{
return space->gravity;
}
void
cpSpaceSetGravity(cpSpace *space, cpVect gravity)
{
space->gravity = gravity;
}
cpFloat
cpSpaceGetDamping(const cpSpace *space)
{
return space->damping;
}
void
cpSpaceSetDamping(cpSpace *space, cpFloat damping)
{
cpAssertHard(damping >= 0.0, "Damping must be positive.");
space->damping = damping;
}
cpFloat
cpSpaceGetIdleSpeedThreshold(const cpSpace *space)
{
return space->idleSpeedThreshold;
}
void
cpSpaceSetIdleSpeedThreshold(cpSpace *space, cpFloat idleSpeedThreshold)
{
space->idleSpeedThreshold = idleSpeedThreshold;
}
cpFloat
cpSpaceGetSleepTimeThreshold(const cpSpace *space)
{
return space->sleepTimeThreshold;
}
void
cpSpaceSetSleepTimeThreshold(cpSpace *space, cpFloat sleepTimeThreshold)
{
space->sleepTimeThreshold = sleepTimeThreshold;
}
cpFloat
cpSpaceGetCollisionSlop(const cpSpace *space)
{
return space->collisionSlop;
}
void
cpSpaceSetCollisionSlop(cpSpace *space, cpFloat collisionSlop)
{
space->collisionSlop = collisionSlop;
}
cpFloat
cpSpaceGetCollisionBias(const cpSpace *space)
{
return space->collisionBias;
}
void
cpSpaceSetCollisionBias(cpSpace *space, cpFloat collisionBias)
{
space->collisionBias = collisionBias;
}
cpTimestamp
cpSpaceGetCollisionPersistence(const cpSpace *space)
{
return space->collisionPersistence;
}
void
cpSpaceSetCollisionPersistence(cpSpace *space, cpTimestamp collisionPersistence)
{
space->collisionPersistence = collisionPersistence;
}
cpDataPointer
cpSpaceGetUserData(const cpSpace *space)
{
return space->userData;
}
void
cpSpaceSetUserData(cpSpace *space, cpDataPointer userData)
{
space->userData = userData;
}
cpBody *
cpSpaceGetStaticBody(const cpSpace *space)
{
return space->staticBody;
}
cpFloat
cpSpaceGetCurrentTimeStep(const cpSpace *space)
{
return space->curr_dt;
}
void
cpSpaceSetStaticBody(cpSpace *space, cpBody *body)
{
if(space->staticBody != NULL){
cpAssertHard(space->staticBody->shapeList == NULL, "Internal Error: Changing the designated static body while the old one still had shapes attached.");
space->staticBody->space = NULL;
}
space->staticBody = body;
body->space = space;
}
cpBool
cpSpaceIsLocked(cpSpace *space)
{
return (space->locked > 0);
}
//MARK: Collision Handler Function Management
static void
cpSpaceUseWildcardDefaultHandler(cpSpace *space)
{
// Spaces default to using the slightly faster "do nothing" default handler until wildcards are potentially needed.
if(!space->usesWildcards){
space->usesWildcards = cpTrue;
space->defaultHandler = cpCollisionHandlerDefault;
}
}
cpCollisionHandler *cpSpaceAddDefaultCollisionHandler(cpSpace *space)
{
cpSpaceUseWildcardDefaultHandler(space);
return &space->defaultHandler;
}
cpCollisionHandler *cpSpaceAddCollisionHandler(cpSpace *space, cpCollisionType a, cpCollisionType b)
{
cpHashValue hash = CP_HASH_PAIR(a, b);
// TODO should use space->defaultHandler values instead?
cpCollisionHandler temp = {a, b, DefaultBegin, DefaultPreSolve, DefaultPostSolve, DefaultSeparate, NULL};
cpHashSet *handlers = space->collisionHandlers;
cpCollisionHandler *handler = cpHashSetFind(handlers, hash, &temp);
return (handler ? handler : cpHashSetInsert(handlers, hash, &temp, (cpHashSetTransFunc)handlerSetTrans, NULL));
}
cpCollisionHandler *
cpSpaceAddWildcardHandler(cpSpace *space, cpCollisionType type)
{
cpSpaceUseWildcardDefaultHandler(space);
cpHashValue hash = CP_HASH_PAIR(type, CP_WILDCARD_COLLISION_TYPE);
cpCollisionHandler temp = {type, CP_WILDCARD_COLLISION_TYPE, AlwaysCollide, AlwaysCollide, DoNothing, DoNothing, NULL};
cpHashSet *handlers = space->collisionHandlers;
cpCollisionHandler *handler = cpHashSetFind(handlers, hash, &temp);
return (handler ? handler : cpHashSetInsert(handlers, hash, &temp, (cpHashSetTransFunc)handlerSetTrans, NULL));
}
//MARK: Body, Shape, and Joint Management
cpShape *
cpSpaceAddShape(cpSpace *space, cpShape *shape)
{
cpBody *body = shape->body;
cpAssertHard(shape->space != space, "You have already added this shape to this space. You must not add it a second time.");
cpAssertHard(!shape->space, "You have already added this shape to another space. You cannot add it to a second.");
// cpAssertHard(body->space == space, "The shape's body must be added to the space before the shape.");
cpAssertSpaceUnlocked(space);
cpBool isStatic = (cpBodyGetType(body) == CP_BODY_TYPE_STATIC);
if(!isStatic) cpBodyActivate(body);
cpBodyAddShape(body, shape);
shape->hashid = space->shapeIDCounter++;
cpShapeUpdate(shape, body->transform);
cpSpatialIndexInsert(isStatic ? space->staticShapes : space->dynamicShapes, shape, shape->hashid);
shape->space = space;
return shape;
}
cpBody *
cpSpaceAddBody(cpSpace *space, cpBody *body)
{
cpAssertHard(body->space != space, "You have already added this body to this space. You must not add it a second time.");
cpAssertHard(!body->space, "You have already added this body to another space. You cannot add it to a second.");
cpAssertSpaceUnlocked(space);
cpArrayPush(cpSpaceArrayForBodyType(space, cpBodyGetType(body)), body);
body->space = space;
return body;
}
cpConstraint *
cpSpaceAddConstraint(cpSpace *space, cpConstraint *constraint)
{
cpAssertHard(constraint->space != space, "You have already added this constraint to this space. You must not add it a second time.");
cpAssertHard(!constraint->space, "You have already added this constraint to another space. You cannot add it to a second.");
cpAssertSpaceUnlocked(space);
cpBody *a = constraint->a, *b = constraint->b;
cpAssertHard(a != NULL && b != NULL, "Constraint is attached to a NULL body.");
// cpAssertHard(a->space == space && b->space == space, "The constraint's bodies must be added to the space before the constraint.");
cpBodyActivate(a);
cpBodyActivate(b);
cpArrayPush(space->constraints, 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 = space;
return constraint;
}
struct arbiterFilterContext {
cpSpace *space;
cpBody *body;
cpShape *shape;
};
static cpBool
cachedArbitersFilter(cpArbiter *arb, struct arbiterFilterContext *context)
{
cpShape *shape = context->shape;
cpBody *body = context->body;
// Match on the filter shape, or if it's NULL the filter body
if(
(body == arb->body_a && (shape == arb->a || shape == NULL)) ||
(body == arb->body_b && (shape == arb->b || shape == NULL))
){
// Call separate when removing shapes.
if(shape && arb->state != CP_ARBITER_STATE_CACHED){
// Invalidate the arbiter since one of the shapes was removed.
arb->state = CP_ARBITER_STATE_INVALIDATED;
cpCollisionHandler *handler = arb->handler;
handler->separateFunc(arb, context->space, handler->userData);
}
cpArbiterUnthread(arb);
cpArrayDeleteObj(context->space->arbiters, arb);
cpArrayPush(context->space->pooledArbiters, arb);
return cpFalse;
}
return cpTrue;
}
void
cpSpaceFilterArbiters(cpSpace *space, cpBody *body, cpShape *filter)
{
cpSpaceLock(space); {
struct arbiterFilterContext context = {space, body, filter};
cpHashSetFilter(space->cachedArbiters, (cpHashSetFilterFunc)cachedArbitersFilter, &context);
} cpSpaceUnlock(space, cpTrue);
}
void
cpSpaceRemoveShape(cpSpace *space, cpShape *shape)
{
cpBody *body = shape->body;
cpAssertHard(cpSpaceContainsShape(space, shape), "Cannot remove a shape that was not added to the space. (Removed twice maybe?)");
cpAssertSpaceUnlocked(space);
cpBool isStatic = (cpBodyGetType(body) == CP_BODY_TYPE_STATIC);
if(isStatic){
cpBodyActivateStatic(body, shape);
} else {
cpBodyActivate(body);
}
cpBodyRemoveShape(body, shape);
cpSpaceFilterArbiters(space, body, shape);
cpSpatialIndexRemove(isStatic ? space->staticShapes : space->dynamicShapes, shape, shape->hashid);
shape->space = NULL;
shape->hashid = 0;
}
void
cpSpaceRemoveBody(cpSpace *space, cpBody *body)
{
cpAssertHard(body != cpSpaceGetStaticBody(space), "Cannot remove the designated static body for the space.");
cpAssertHard(cpSpaceContainsBody(space, body), "Cannot remove a body that was not added to the space. (Removed twice maybe?)");
// cpAssertHard(body->shapeList == NULL, "Cannot remove a body from the space before removing the bodies attached to it.");
// cpAssertHard(body->constraintList == NULL, "Cannot remove a body from the space before removing the constraints attached to it.");
cpAssertSpaceUnlocked(space);
cpBodyActivate(body);
// cpSpaceFilterArbiters(space, body, NULL);
cpArrayDeleteObj(cpSpaceArrayForBodyType(space, cpBodyGetType(body)), body);
body->space = NULL;
}
void
cpSpaceRemoveConstraint(cpSpace *space, cpConstraint *constraint)
{
cpAssertHard(cpSpaceContainsConstraint(space, constraint), "Cannot remove a constraint that was not added to the space. (Removed twice maybe?)");
cpAssertSpaceUnlocked(space);
cpBodyActivate(constraint->a);
cpBodyActivate(constraint->b);
cpArrayDeleteObj(space->constraints, constraint);
cpBodyRemoveConstraint(constraint->a, constraint);
cpBodyRemoveConstraint(constraint->b, constraint);
constraint->space = NULL;
}
cpBool cpSpaceContainsShape(cpSpace *space, cpShape *shape)
{
return (shape->space == space);
}
cpBool cpSpaceContainsBody(cpSpace *space, cpBody *body)
{
return (body->space == space);
}
cpBool cpSpaceContainsConstraint(cpSpace *space, cpConstraint *constraint)
{
return (constraint->space == space);
}
//MARK: Iteration
void
cpSpaceEachBody(cpSpace *space, cpSpaceBodyIteratorFunc func, void *data)
{
cpSpaceLock(space); {
cpArray *bodies = space->dynamicBodies;
for(int i=0; i<bodies->num; i++){
func((cpBody *)bodies->arr[i], data);
}
cpArray *otherBodies = space->staticBodies;
for(int i=0; i<otherBodies->num; i++){
func((cpBody *)otherBodies->arr[i], data);
}
cpArray *components = space->sleepingComponents;
for(int i=0; i<components->num; i++){
cpBody *root = (cpBody *)components->arr[i];
cpBody *body = root;
while(body){
cpBody *next = body->sleeping.next;
func(body, data);
body = next;
}
}
} cpSpaceUnlock(space, cpTrue);
}
typedef struct spaceShapeContext {
cpSpaceShapeIteratorFunc func;
void *data;
} spaceShapeContext;
static void
spaceEachShapeIterator(cpShape *shape, spaceShapeContext *context)
{
context->func(shape, context->data);
}
void
cpSpaceEachShape(cpSpace *space, cpSpaceShapeIteratorFunc func, void *data)
{
cpSpaceLock(space); {
spaceShapeContext context = {func, data};
cpSpatialIndexEach(space->dynamicShapes, (cpSpatialIndexIteratorFunc)spaceEachShapeIterator, &context);
cpSpatialIndexEach(space->staticShapes, (cpSpatialIndexIteratorFunc)spaceEachShapeIterator, &context);
} cpSpaceUnlock(space, cpTrue);
}
void
cpSpaceEachConstraint(cpSpace *space, cpSpaceConstraintIteratorFunc func, void *data)
{
cpSpaceLock(space); {
cpArray *constraints = space->constraints;
for(int i=0; i<constraints->num; i++){
func((cpConstraint *)constraints->arr[i], data);
}
} cpSpaceUnlock(space, cpTrue);
}
//MARK: Spatial Index Management
void
cpSpaceReindexStatic(cpSpace *space)
{
cpAssertHard(!space->locked, "You cannot manually reindex objects while the space is locked. Wait until the current query or step is complete.");
cpSpatialIndexEach(space->staticShapes, (cpSpatialIndexIteratorFunc)&cpShapeUpdateFunc, NULL);
cpSpatialIndexReindex(space->staticShapes);
}
void
cpSpaceReindexShape(cpSpace *space, cpShape *shape)
{
cpAssertHard(!space->locked, "You cannot manually reindex objects while the space is locked. Wait until the current query or step is complete.");
cpShapeCacheBB(shape);
// attempt to rehash the shape in both hashes
cpSpatialIndexReindexObject(space->dynamicShapes, shape, shape->hashid);
cpSpatialIndexReindexObject(space->staticShapes, shape, shape->hashid);
}
void
cpSpaceReindexShapesForBody(cpSpace *space, cpBody *body)
{
CP_BODY_FOREACH_SHAPE(body, shape) cpSpaceReindexShape(space, shape);
}
static void
copyShapes(cpShape *shape, cpSpatialIndex *index)
{
cpSpatialIndexInsert(index, shape, shape->hashid);
}
void
cpSpaceUseSpatialHash(cpSpace *space, cpFloat dim, int count)
{
cpSpatialIndex *staticShapes = cpSpaceHashNew(dim, count, (cpSpatialIndexBBFunc)cpShapeGetBB, NULL);
cpSpatialIndex *dynamicShapes = cpSpaceHashNew(dim, count, (cpSpatialIndexBBFunc)cpShapeGetBB, staticShapes);
cpSpatialIndexEach(space->staticShapes, (cpSpatialIndexIteratorFunc)copyShapes, staticShapes);
cpSpatialIndexEach(space->dynamicShapes, (cpSpatialIndexIteratorFunc)copyShapes, dynamicShapes);
cpSpatialIndexFree(space->staticShapes);
cpSpatialIndexFree(space->dynamicShapes);
space->staticShapes = staticShapes;
space->dynamicShapes = dynamicShapes;
}

View File

@ -1,349 +0,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.
*/
#include <string.h>
#include "chipmunk/chipmunk_private.h"
//MARK: Sleeping Functions
void
cpSpaceActivateBody(cpSpace *space, cpBody *body)
{
cpAssertHard(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC, "Internal error: Attempting to activate a non-dynamic body.");
if(space->locked){
// cpSpaceActivateBody() is called again once the space is unlocked
if(!cpArrayContains(space->rousedBodies, body)) cpArrayPush(space->rousedBodies, body);
} else {
cpAssertSoft(body->sleeping.root == NULL && body->sleeping.next == NULL, "Internal error: Activating body non-NULL node pointers.");
cpArrayPush(space->dynamicBodies, body);
CP_BODY_FOREACH_SHAPE(body, shape){
cpSpatialIndexRemove(space->staticShapes, shape, shape->hashid);
cpSpatialIndexInsert(space->dynamicShapes, shape, shape->hashid);
}
CP_BODY_FOREACH_ARBITER(body, arb){
cpBody *bodyA = arb->body_a;
// Arbiters are shared between two bodies that are always woken up together.
// You only want to restore the arbiter once, so bodyA is arbitrarily chosen to own the arbiter.
// The edge case is when static bodies are involved as the static bodies never actually sleep.
// If the static body is bodyB then all is good. If the static body is bodyA, that can easily be checked.
if(body == bodyA || cpBodyGetType(bodyA) == CP_BODY_TYPE_STATIC){
int numContacts = arb->count;
struct cpContact *contacts = arb->contacts;
// Restore contact values back to the space's contact buffer memory
arb->contacts = cpContactBufferGetArray(space);
memcpy(arb->contacts, contacts, numContacts*sizeof(struct cpContact));
cpSpacePushContacts(space, numContacts);
// Reinsert the arbiter into the arbiter cache
const cpShape *a = arb->a, *b = arb->b;
const cpShape *shape_pair[] = {a, b};
cpHashValue arbHashID = CP_HASH_PAIR((cpHashValue)a, (cpHashValue)b);
cpHashSetInsert(space->cachedArbiters, arbHashID, shape_pair, NULL, arb);
// Update the arbiter's state
arb->stamp = space->stamp;
cpArrayPush(space->arbiters, arb);
cpfree(contacts);
}
}
CP_BODY_FOREACH_CONSTRAINT(body, constraint){
cpBody *bodyA = constraint->a;
if(body == bodyA || cpBodyGetType(bodyA) == CP_BODY_TYPE_STATIC) cpArrayPush(space->constraints, constraint);
}
}
}
static void
cpSpaceDeactivateBody(cpSpace *space, cpBody *body)
{
cpAssertHard(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC, "Internal error: Attempting to deactivate a non-dynamic body.");
cpArrayDeleteObj(space->dynamicBodies, body);
CP_BODY_FOREACH_SHAPE(body, shape){
cpSpatialIndexRemove(space->dynamicShapes, shape, shape->hashid);
cpSpatialIndexInsert(space->staticShapes, shape, shape->hashid);
}
CP_BODY_FOREACH_ARBITER(body, arb){
cpBody *bodyA = arb->body_a;
if(body == bodyA || cpBodyGetType(bodyA) == CP_BODY_TYPE_STATIC){
cpSpaceUncacheArbiter(space, arb);
// Save contact values to a new block of memory so they won't time out
size_t bytes = arb->count*sizeof(struct cpContact);
struct cpContact *contacts = (struct cpContact *)cpcalloc(1, bytes);
memcpy(contacts, arb->contacts, bytes);
arb->contacts = contacts;
}
}
CP_BODY_FOREACH_CONSTRAINT(body, constraint){
cpBody *bodyA = constraint->a;
if(body == bodyA || cpBodyGetType(bodyA) == CP_BODY_TYPE_STATIC) cpArrayDeleteObj(space->constraints, constraint);
}
}
static inline cpBody *
ComponentRoot(cpBody *body)
{
return (body ? body->sleeping.root : NULL);
}
void
cpBodyActivate(cpBody *body)
{
if(body != NULL && cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC){
body->sleeping.idleTime = 0.0f;
cpBody *root = ComponentRoot(body);
if(root && cpBodyIsSleeping(root)){
// TODO should cpBodyIsSleeping(root) be an assertion?
cpAssertSoft(cpBodyGetType(root) == CP_BODY_TYPE_DYNAMIC, "Internal Error: Non-dynamic body component root detected.");
cpSpace *space = root->space;
cpBody *body = root;
while(body){
cpBody *next = body->sleeping.next;
body->sleeping.idleTime = 0.0f;
body->sleeping.root = NULL;
body->sleeping.next = NULL;
cpSpaceActivateBody(space, body);
body = next;
}
cpArrayDeleteObj(space->sleepingComponents, root);
}
CP_BODY_FOREACH_ARBITER(body, arb){
// Reset the idle timer of things the body is touching as well.
// That way things don't get left hanging in the air.
cpBody *other = (arb->body_a == body ? arb->body_b : arb->body_a);
if(cpBodyGetType(other) != CP_BODY_TYPE_STATIC) other->sleeping.idleTime = 0.0f;
}
}
}
void
cpBodyActivateStatic(cpBody *body, cpShape *filter)
{
cpAssertHard(cpBodyGetType(body) == CP_BODY_TYPE_STATIC, "cpBodyActivateStatic() called on a non-static body.");
CP_BODY_FOREACH_ARBITER(body, arb){
if(!filter || filter == arb->a || filter == arb->b){
cpBodyActivate(arb->body_a == body ? arb->body_b : arb->body_a);
}
}
// TODO: should also activate joints?
}
static inline void
cpBodyPushArbiter(cpBody *body, cpArbiter *arb)
{
cpAssertSoft(cpArbiterThreadForBody(arb, body)->next == NULL, "Internal Error: Dangling contact graph pointers detected. (A)");
cpAssertSoft(cpArbiterThreadForBody(arb, body)->prev == NULL, "Internal Error: Dangling contact graph pointers detected. (B)");
cpArbiter *next = body->arbiterList;
cpAssertSoft(next == NULL || cpArbiterThreadForBody(next, body)->prev == NULL, "Internal Error: Dangling contact graph pointers detected. (C)");
cpArbiterThreadForBody(arb, body)->next = next;
if(next) cpArbiterThreadForBody(next, body)->prev = arb;
body->arbiterList = arb;
}
static inline void
ComponentAdd(cpBody *root, cpBody *body){
body->sleeping.root = root;
if(body != root){
body->sleeping.next = root->sleeping.next;
root->sleeping.next = body;
}
}
static inline void
FloodFillComponent(cpBody *root, cpBody *body)
{
// Kinematic bodies cannot be put to sleep and prevent bodies they are touching from sleeping.
// Static bodies are effectively sleeping all the time.
if(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC){
cpBody *other_root = ComponentRoot(body);
if(other_root == NULL){
ComponentAdd(root, body);
CP_BODY_FOREACH_ARBITER(body, arb) FloodFillComponent(root, (body == arb->body_a ? arb->body_b : arb->body_a));
CP_BODY_FOREACH_CONSTRAINT(body, constraint) FloodFillComponent(root, (body == constraint->a ? constraint->b : constraint->a));
} else {
cpAssertSoft(other_root == root, "Internal Error: Inconsistency dectected in the contact graph.");
}
}
}
static inline cpBool
ComponentActive(cpBody *root, cpFloat threshold)
{
CP_BODY_FOREACH_COMPONENT(root, body){
if(body->sleeping.idleTime < threshold) return cpTrue;
}
return cpFalse;
}
void
cpSpaceProcessComponents(cpSpace *space, cpFloat dt)
{
cpBool sleep = (space->sleepTimeThreshold != INFINITY);
cpArray *bodies = space->dynamicBodies;
#ifndef NDEBUG
for(int i=0; i<bodies->num; i++){
cpBody *body = (cpBody*)bodies->arr[i];
cpAssertSoft(body->sleeping.next == NULL, "Internal Error: Dangling next pointer detected in contact graph.");
cpAssertSoft(body->sleeping.root == NULL, "Internal Error: Dangling root pointer detected in contact graph.");
}
#endif
// Calculate the kinetic energy of all the bodies.
if(sleep){
cpFloat dv = space->idleSpeedThreshold;
cpFloat dvsq = (dv ? dv*dv : cpvlengthsq(space->gravity)*dt*dt);
// update idling and reset component nodes
for(int i=0; i<bodies->num; i++){
cpBody *body = (cpBody*)bodies->arr[i];
// TODO should make a separate array for kinematic bodies.
if(cpBodyGetType(body) != CP_BODY_TYPE_DYNAMIC) continue;
// Need to deal with infinite mass objects
cpFloat keThreshold = (dvsq ? body->m*dvsq : 0.0f);
body->sleeping.idleTime = (cpBodyKineticEnergy(body) > keThreshold ? 0.0f : body->sleeping.idleTime + dt);
}
}
// Awaken any sleeping bodies found and then push arbiters to the bodies' lists.
cpArray *arbiters = space->arbiters;
for(int i=0, count=arbiters->num; i<count; i++){
cpArbiter *arb = (cpArbiter*)arbiters->arr[i];
cpBody *a = arb->body_a, *b = arb->body_b;
if(sleep){
// TODO checking cpBodyIsSleepin() redundant?
if(cpBodyGetType(b) == CP_BODY_TYPE_KINEMATIC || cpBodyIsSleeping(a)) cpBodyActivate(a);
if(cpBodyGetType(a) == CP_BODY_TYPE_KINEMATIC || cpBodyIsSleeping(b)) cpBodyActivate(b);
}
cpBodyPushArbiter(a, arb);
cpBodyPushArbiter(b, arb);
}
if(sleep){
// Bodies should be held active if connected by a joint to a kinematic.
cpArray *constraints = space->constraints;
for(int i=0; i<constraints->num; i++){
cpConstraint *constraint = (cpConstraint *)constraints->arr[i];
cpBody *a = constraint->a, *b = constraint->b;
if(cpBodyGetType(b) == CP_BODY_TYPE_KINEMATIC) cpBodyActivate(a);
if(cpBodyGetType(a) == CP_BODY_TYPE_KINEMATIC) cpBodyActivate(b);
}
// Generate components and deactivate sleeping ones
for(int i=0; i<bodies->num;){
cpBody *body = (cpBody*)bodies->arr[i];
if(ComponentRoot(body) == NULL){
// Body not in a component yet. Perform a DFS to flood fill mark
// the component in the contact graph using this body as the root.
FloodFillComponent(body, body);
// Check if the component should be put to sleep.
if(!ComponentActive(body, space->sleepTimeThreshold)){
cpArrayPush(space->sleepingComponents, body);
CP_BODY_FOREACH_COMPONENT(body, other) cpSpaceDeactivateBody(space, other);
// cpSpaceDeactivateBody() removed the current body from the list.
// Skip incrementing the index counter.
continue;
}
}
i++;
// Only sleeping bodies retain their component node pointers.
body->sleeping.root = NULL;
body->sleeping.next = NULL;
}
}
}
void
cpBodySleep(cpBody *body)
{
cpBodySleepWithGroup(body, NULL);
}
void
cpBodySleepWithGroup(cpBody *body, cpBody *group){
cpAssertHard(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC, "Non-dynamic bodies cannot be put to sleep.");
cpSpace *space = body->space;
cpAssertHard(!cpSpaceIsLocked(space), "Bodies cannot be put to sleep during a query or a call to cpSpaceStep(). Put these calls into a post-step callback.");
cpAssertHard(cpSpaceGetSleepTimeThreshold(space) < INFINITY, "Sleeping is not enabled on the space. You cannot sleep a body without setting a sleep time threshold on the space.");
cpAssertHard(group == NULL || cpBodyIsSleeping(group), "Cannot use a non-sleeping body as a group identifier.");
if(cpBodyIsSleeping(body)){
cpAssertHard(ComponentRoot(body) == ComponentRoot(group), "The body is already sleeping and it's group cannot be reassigned.");
return;
}
CP_BODY_FOREACH_SHAPE(body, shape) cpShapeCacheBB(shape);
cpSpaceDeactivateBody(space, body);
if(group){
cpBody *root = ComponentRoot(group);
body->sleeping.root = root;
body->sleeping.next = root->sleeping.next;
body->sleeping.idleTime = 0.0f;
root->sleeping.next = body;
} else {
body->sleeping.root = body;
body->sleeping.next = NULL;
body->sleeping.idleTime = 0.0f;
cpArrayPush(space->sleepingComponents, body);
}
cpArrayDeleteObj(space->dynamicBodies, body);
}

View File

@ -1,189 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "chipmunk/chipmunk_private.h"
#ifndef CP_SPACE_DISABLE_DEBUG_API
static void
cpSpaceDebugDrawShape(cpShape *shape, cpSpaceDebugDrawOptions *options)
{
cpBody *body = shape->body;
cpDataPointer *data = options->data;
cpSpaceDebugColor outline_color = options->shapeOutlineColor;
cpSpaceDebugColor fill_color = options->colorForShape(shape, data);
switch(shape->klass->type){
case CP_CIRCLE_SHAPE: {
cpCircleShape *circle = (cpCircleShape *)shape;
options->drawCircle(circle->tc, body->a, circle->r, outline_color, fill_color, data);
break;
}
case CP_SEGMENT_SHAPE: {
cpSegmentShape *seg = (cpSegmentShape *)shape;
options->drawFatSegment(seg->ta, seg->tb, seg->r, outline_color, fill_color, data);
break;
}
case CP_POLY_SHAPE: {
cpPolyShape *poly = (cpPolyShape *)shape;
int count = poly->count;
struct cpSplittingPlane *planes = poly->planes;
cpVect *verts = (cpVect *)alloca(count*sizeof(cpVect));
for(int i=0; i<count; i++) verts[i] = planes[i].v0;
options->drawPolygon(count, verts, poly->r, outline_color, fill_color, data);
break;
}
default: break;
}
}
static const cpVect spring_verts[] = {
{0.00f, 0.0f},
{0.20f, 0.0f},
{0.25f, 3.0f},
{0.30f,-6.0f},
{0.35f, 6.0f},
{0.40f,-6.0f},
{0.45f, 6.0f},
{0.50f,-6.0f},
{0.55f, 6.0f},
{0.60f,-6.0f},
{0.65f, 6.0f},
{0.70f,-3.0f},
{0.75f, 6.0f},
{0.80f, 0.0f},
{1.00f, 0.0f},
};
static const int spring_count = sizeof(spring_verts)/sizeof(cpVect);
static void
cpSpaceDebugDrawConstraint(cpConstraint *constraint, cpSpaceDebugDrawOptions *options)
{
cpDataPointer *data = options->data;
cpSpaceDebugColor color = options->constraintColor;
cpBody *body_a = constraint->a;
cpBody *body_b = constraint->b;
if(cpConstraintIsPinJoint(constraint)){
cpPinJoint *joint = (cpPinJoint *)constraint;
cpVect a = cpTransformPoint(body_a->transform, joint->anchorA);
cpVect b = cpTransformPoint(body_b->transform, joint->anchorB);
options->drawDot(5, a, color, data);
options->drawDot(5, b, color, data);
options->drawSegment(a, b, color, data);
} else if(cpConstraintIsSlideJoint(constraint)){
cpSlideJoint *joint = (cpSlideJoint *)constraint;
cpVect a = cpTransformPoint(body_a->transform, joint->anchorA);
cpVect b = cpTransformPoint(body_b->transform, joint->anchorB);
options->drawDot(5, a, color, data);
options->drawDot(5, b, color, data);
options->drawSegment(a, b, color, data);
} else if(cpConstraintIsPivotJoint(constraint)){
cpPivotJoint *joint = (cpPivotJoint *)constraint;
cpVect a = cpTransformPoint(body_a->transform, joint->anchorA);
cpVect b = cpTransformPoint(body_b->transform, joint->anchorB);
options->drawDot(5, a, color, data);
options->drawDot(5, b, color, data);
} else if(cpConstraintIsGrooveJoint(constraint)){
cpGrooveJoint *joint = (cpGrooveJoint *)constraint;
cpVect a = cpTransformPoint(body_a->transform, joint->grv_a);
cpVect b = cpTransformPoint(body_a->transform, joint->grv_b);
cpVect c = cpTransformPoint(body_b->transform, joint->anchorB);
options->drawDot(5, c, color, data);
options->drawSegment(a, b, color, data);
} else if(cpConstraintIsDampedSpring(constraint)){
cpDampedSpring *spring = (cpDampedSpring *)constraint;
cpDataPointer *data = options->data;
cpSpaceDebugColor color = options->constraintColor;
cpVect a = cpTransformPoint(body_a->transform, spring->anchorA);
cpVect b = cpTransformPoint(body_b->transform, spring->anchorB);
options->drawDot(5, a, color, data);
options->drawDot(5, b, color, data);
cpVect delta = cpvsub(b, a);
cpFloat cos = delta.x;
cpFloat sin = delta.y;
cpFloat s = 1.0f/cpvlength(delta);
cpVect r1 = cpv(cos, -sin*s);
cpVect r2 = cpv(sin, cos*s);
cpVect *verts = (cpVect *)alloca(spring_count*sizeof(cpVect));
for(int i=0; i<spring_count; i++){
cpVect v = spring_verts[i];
verts[i] = cpv(cpvdot(v, r1) + a.x, cpvdot(v, r2) + a.y);
}
for(int i=0; i<spring_count-1; i++){
options->drawSegment(verts[i], verts[i + 1], color, data);
}
}
}
void
cpSpaceDebugDraw(cpSpace *space, cpSpaceDebugDrawOptions *options)
{
if(options->flags & CP_SPACE_DEBUG_DRAW_SHAPES){
cpSpaceEachShape(space, (cpSpaceShapeIteratorFunc)cpSpaceDebugDrawShape, options);
}
if(options->flags & CP_SPACE_DEBUG_DRAW_CONSTRAINTS){
cpSpaceEachConstraint(space, (cpSpaceConstraintIteratorFunc)cpSpaceDebugDrawConstraint, options);
}
if(options->flags & CP_SPACE_DEBUG_DRAW_COLLISION_POINTS){
cpArray *arbiters = space->arbiters;
cpSpaceDebugColor color = options->collisionPointColor;
cpSpaceDebugDrawSegmentImpl draw_seg = options->drawSegment;
cpDataPointer *data = options->data;
for(int i=0; i<arbiters->num; i++){
cpArbiter *arb = (cpArbiter*)arbiters->arr[i];
cpVect n = arb->n;
for(int j=0; j<arb->count; j++){
cpVect p1 = cpvadd(arb->body_a->p, arb->contacts[j].r1);
cpVect p2 = cpvadd(arb->body_b->p, arb->contacts[j].r2);
cpFloat d = 2.0f;
cpVect a = cpvadd(p1, cpvmult(n, -d));
cpVect b = cpvadd(p2, cpvmult(n, d));
draw_seg(a, b, color, data);
}
}
}
}
#endif

View File

@ -1,634 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "chipmunk/chipmunk_private.h"
#include "prime.h"
typedef struct cpSpaceHashBin cpSpaceHashBin;
typedef struct cpHandle cpHandle;
struct cpSpaceHash {
cpSpatialIndex spatialIndex;
int numcells;
cpFloat celldim;
cpSpaceHashBin **table;
cpHashSet *handleSet;
cpSpaceHashBin *pooledBins;
cpArray *pooledHandles;
cpArray *allocatedBuffers;
cpTimestamp stamp;
};
//MARK: Handle Functions
struct cpHandle {
void *obj;
int retain;
cpTimestamp stamp;
};
static cpHandle*
cpHandleInit(cpHandle *hand, void *obj)
{
hand->obj = obj;
hand->retain = 0;
hand->stamp = 0;
return hand;
}
static inline void cpHandleRetain(cpHandle *hand){hand->retain++;}
static inline void
cpHandleRelease(cpHandle *hand, cpArray *pooledHandles)
{
hand->retain--;
if(hand->retain == 0) cpArrayPush(pooledHandles, hand);
}
static int handleSetEql(void *obj, cpHandle *hand){return (obj == hand->obj);}
static void *
handleSetTrans(void *obj, cpSpaceHash *hash)
{
if(hash->pooledHandles->num == 0){
// handle pool is exhausted, make more
int count = CP_BUFFER_BYTES/sizeof(cpHandle);
cpAssertHard(count, "Internal Error: Buffer size is too small.");
cpHandle *buffer = (cpHandle *)cpcalloc(1, CP_BUFFER_BYTES);
cpArrayPush(hash->allocatedBuffers, buffer);
for(int i=0; i<count; i++) cpArrayPush(hash->pooledHandles, buffer + i);
}
cpHandle *hand = cpHandleInit((cpHandle *)cpArrayPop(hash->pooledHandles), obj);
cpHandleRetain(hand);
return hand;
}
//MARK: Bin Functions
struct cpSpaceHashBin {
cpHandle *handle;
cpSpaceHashBin *next;
};
static inline void
recycleBin(cpSpaceHash *hash, cpSpaceHashBin *bin)
{
bin->next = hash->pooledBins;
hash->pooledBins = bin;
}
static inline void
clearTableCell(cpSpaceHash *hash, int idx)
{
cpSpaceHashBin *bin = hash->table[idx];
while(bin){
cpSpaceHashBin *next = bin->next;
cpHandleRelease(bin->handle, hash->pooledHandles);
recycleBin(hash, bin);
bin = next;
}
hash->table[idx] = NULL;
}
static void
clearTable(cpSpaceHash *hash)
{
for(int i=0; i<hash->numcells; i++) clearTableCell(hash, i);
}
// Get a recycled or new bin.
static inline cpSpaceHashBin *
getEmptyBin(cpSpaceHash *hash)
{
cpSpaceHashBin *bin = hash->pooledBins;
if(bin){
hash->pooledBins = bin->next;
return bin;
} else {
// Pool is exhausted, make more
int count = CP_BUFFER_BYTES/sizeof(cpSpaceHashBin);
cpAssertHard(count, "Internal Error: Buffer size is too small.");
cpSpaceHashBin *buffer = (cpSpaceHashBin *)cpcalloc(1, CP_BUFFER_BYTES);
cpArrayPush(hash->allocatedBuffers, buffer);
// push all but the first one, return the first instead
for(int i=1; i<count; i++) recycleBin(hash, buffer + i);
return buffer;
}
}
//MARK: Memory Management Functions
cpSpaceHash *
cpSpaceHashAlloc(void)
{
return (cpSpaceHash *)cpcalloc(1, sizeof(cpSpaceHash));
}
// Frees the old table, and allocate a new one.
static void
cpSpaceHashAllocTable(cpSpaceHash *hash, int numcells)
{
cpfree(hash->table);
hash->numcells = numcells;
hash->table = (cpSpaceHashBin **)cpcalloc(numcells, sizeof(cpSpaceHashBin *));
}
static inline cpSpatialIndexClass *Klass();
cpSpatialIndex *
cpSpaceHashInit(cpSpaceHash *hash, cpFloat celldim, int numcells, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex)
{
cpSpatialIndexInit((cpSpatialIndex *)hash, Klass(), bbfunc, staticIndex);
cpSpaceHashAllocTable(hash, next_prime(numcells));
hash->celldim = celldim;
hash->handleSet = cpHashSetNew(0, (cpHashSetEqlFunc)handleSetEql);
hash->pooledHandles = cpArrayNew(0);
hash->pooledBins = NULL;
hash->allocatedBuffers = cpArrayNew(0);
hash->stamp = 1;
return (cpSpatialIndex *)hash;
}
cpSpatialIndex *
cpSpaceHashNew(cpFloat celldim, int cells, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex)
{
return cpSpaceHashInit(cpSpaceHashAlloc(), celldim, cells, bbfunc, staticIndex);
}
static void
cpSpaceHashDestroy(cpSpaceHash *hash)
{
if(hash->table) clearTable(hash);
cpfree(hash->table);
cpHashSetFree(hash->handleSet);
cpArrayFreeEach(hash->allocatedBuffers, cpfree);
cpArrayFree(hash->allocatedBuffers);
cpArrayFree(hash->pooledHandles);
}
//MARK: Helper Functions
static inline cpBool
containsHandle(cpSpaceHashBin *bin, cpHandle *hand)
{
while(bin){
if(bin->handle == hand) return cpTrue;
bin = bin->next;
}
return cpFalse;
}
// The hash function itself.
static inline cpHashValue
hash_func(cpHashValue x, cpHashValue y, cpHashValue n)
{
return (x*1640531513ul ^ y*2654435789ul) % n;
}
// Much faster than (int)floor(f)
// Profiling showed floor() to be a sizable performance hog
static inline int
floor_int(cpFloat f)
{
int i = (int)f;
return (f < 0.0f && f != i ? i - 1 : i);
}
static inline void
hashHandle(cpSpaceHash *hash, cpHandle *hand, cpBB bb)
{
// Find the dimensions in cell coordinates.
cpFloat dim = hash->celldim;
int l = floor_int(bb.l/dim); // Fix by ShiftZ
int r = floor_int(bb.r/dim);
int b = floor_int(bb.b/dim);
int t = floor_int(bb.t/dim);
int n = hash->numcells;
for(int i=l; i<=r; i++){
for(int j=b; j<=t; j++){
cpHashValue idx = hash_func(i,j,n);
cpSpaceHashBin *bin = hash->table[idx];
// Don't add an object twice to the same cell.
if(containsHandle(bin, hand)) continue;
cpHandleRetain(hand);
// Insert a new bin for the handle in this cell.
cpSpaceHashBin *newBin = getEmptyBin(hash);
newBin->handle = hand;
newBin->next = bin;
hash->table[idx] = newBin;
}
}
}
//MARK: Basic Operations
static void
cpSpaceHashInsert(cpSpaceHash *hash, void *obj, cpHashValue hashid)
{
cpHandle *hand = (cpHandle *)cpHashSetInsert(hash->handleSet, hashid, obj, (cpHashSetTransFunc)handleSetTrans, hash);
hashHandle(hash, hand, hash->spatialIndex.bbfunc(obj));
}
static void
cpSpaceHashRehashObject(cpSpaceHash *hash, void *obj, cpHashValue hashid)
{
cpHandle *hand = (cpHandle *)cpHashSetRemove(hash->handleSet, hashid, obj);
if(hand){
hand->obj = NULL;
cpHandleRelease(hand, hash->pooledHandles);
cpSpaceHashInsert(hash, obj, hashid);
}
}
static void
rehash_helper(cpHandle *hand, cpSpaceHash *hash)
{
hashHandle(hash, hand, hash->spatialIndex.bbfunc(hand->obj));
}
static void
cpSpaceHashRehash(cpSpaceHash *hash)
{
clearTable(hash);
cpHashSetEach(hash->handleSet, (cpHashSetIteratorFunc)rehash_helper, hash);
}
static void
cpSpaceHashRemove(cpSpaceHash *hash, void *obj, cpHashValue hashid)
{
cpHandle *hand = (cpHandle *)cpHashSetRemove(hash->handleSet, hashid, obj);
if(hand){
hand->obj = NULL;
cpHandleRelease(hand, hash->pooledHandles);
}
}
typedef struct eachContext {
cpSpatialIndexIteratorFunc func;
void *data;
} eachContext;
static void eachHelper(cpHandle *hand, eachContext *context){context->func(hand->obj, context->data);}
static void
cpSpaceHashEach(cpSpaceHash *hash, cpSpatialIndexIteratorFunc func, void *data)
{
eachContext context = {func, data};
cpHashSetEach(hash->handleSet, (cpHashSetIteratorFunc)eachHelper, &context);
}
static void
remove_orphaned_handles(cpSpaceHash *hash, cpSpaceHashBin **bin_ptr)
{
cpSpaceHashBin *bin = *bin_ptr;
while(bin){
cpHandle *hand = bin->handle;
cpSpaceHashBin *next = bin->next;
if(!hand->obj){
// orphaned handle, unlink and recycle the bin
(*bin_ptr) = bin->next;
recycleBin(hash, bin);
cpHandleRelease(hand, hash->pooledHandles);
} else {
bin_ptr = &bin->next;
}
bin = next;
}
}
//MARK: Query Functions
static inline void
query_helper(cpSpaceHash *hash, cpSpaceHashBin **bin_ptr, void *obj, cpSpatialIndexQueryFunc func, void *data)
{
restart:
for(cpSpaceHashBin *bin = *bin_ptr; bin; bin = bin->next){
cpHandle *hand = bin->handle;
void *other = hand->obj;
if(hand->stamp == hash->stamp || obj == other){
continue;
} else if(other){
func(obj, other, 0, data);
hand->stamp = hash->stamp;
} else {
// The object for this handle has been removed
// cleanup this cell and restart the query
remove_orphaned_handles(hash, bin_ptr);
goto restart; // GCC not smart enough/able to tail call an inlined function.
}
}
}
static void
cpSpaceHashQuery(cpSpaceHash *hash, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data)
{
// Get the dimensions in cell coordinates.
cpFloat dim = hash->celldim;
int l = floor_int(bb.l/dim); // Fix by ShiftZ
int r = floor_int(bb.r/dim);
int b = floor_int(bb.b/dim);
int t = floor_int(bb.t/dim);
int n = hash->numcells;
cpSpaceHashBin **table = hash->table;
// Iterate over the cells and query them.
for(int i=l; i<=r; i++){
for(int j=b; j<=t; j++){
query_helper(hash, &table[hash_func(i,j,n)], obj, func, data);
}
}
hash->stamp++;
}
// Similar to struct eachPair above.
typedef struct queryRehashContext {
cpSpaceHash *hash;
cpSpatialIndexQueryFunc func;
void *data;
} queryRehashContext;
// Hashset iterator func used with cpSpaceHashQueryRehash().
static void
queryRehash_helper(cpHandle *hand, queryRehashContext *context)
{
cpSpaceHash *hash = context->hash;
cpSpatialIndexQueryFunc func = context->func;
void *data = context->data;
cpFloat dim = hash->celldim;
int n = hash->numcells;
void *obj = hand->obj;
cpBB bb = hash->spatialIndex.bbfunc(obj);
int l = floor_int(bb.l/dim);
int r = floor_int(bb.r/dim);
int b = floor_int(bb.b/dim);
int t = floor_int(bb.t/dim);
cpSpaceHashBin **table = hash->table;
for(int i=l; i<=r; i++){
for(int j=b; j<=t; j++){
cpHashValue idx = hash_func(i,j,n);
cpSpaceHashBin *bin = table[idx];
if(containsHandle(bin, hand)) continue;
cpHandleRetain(hand); // this MUST be done first in case the object is removed in func()
query_helper(hash, &bin, obj, func, data);
cpSpaceHashBin *newBin = getEmptyBin(hash);
newBin->handle = hand;
newBin->next = bin;
table[idx] = newBin;
}
}
// Increment the stamp for each object hashed.
hash->stamp++;
}
static void
cpSpaceHashReindexQuery(cpSpaceHash *hash, cpSpatialIndexQueryFunc func, void *data)
{
clearTable(hash);
queryRehashContext context = {hash, func, data};
cpHashSetEach(hash->handleSet, (cpHashSetIteratorFunc)queryRehash_helper, &context);
cpSpatialIndexCollideStatic((cpSpatialIndex *)hash, hash->spatialIndex.staticIndex, func, data);
}
static inline cpFloat
segmentQuery_helper(cpSpaceHash *hash, cpSpaceHashBin **bin_ptr, void *obj, cpSpatialIndexSegmentQueryFunc func, void *data)
{
cpFloat t = 1.0f;
restart:
for(cpSpaceHashBin *bin = *bin_ptr; bin; bin = bin->next){
cpHandle *hand = bin->handle;
void *other = hand->obj;
// Skip over certain conditions
if(hand->stamp == hash->stamp){
continue;
} else if(other){
t = cpfmin(t, func(obj, other, data));
hand->stamp = hash->stamp;
} else {
// The object for this handle has been removed
// cleanup this cell and restart the query
remove_orphaned_handles(hash, bin_ptr);
goto restart; // GCC not smart enough/able to tail call an inlined function.
}
}
return t;
}
// modified from http://playtechs.blogspot.com/2007/03/raytracing-on-grid.html
static void
cpSpaceHashSegmentQuery(cpSpaceHash *hash, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data)
{
a = cpvmult(a, 1.0f/hash->celldim);
b = cpvmult(b, 1.0f/hash->celldim);
int cell_x = floor_int(a.x), cell_y = floor_int(a.y);
cpFloat t = 0;
int x_inc, y_inc;
cpFloat temp_v, temp_h;
if (b.x > a.x){
x_inc = 1;
temp_h = (cpffloor(a.x + 1.0f) - a.x);
} else {
x_inc = -1;
temp_h = (a.x - cpffloor(a.x));
}
if (b.y > a.y){
y_inc = 1;
temp_v = (cpffloor(a.y + 1.0f) - a.y);
} else {
y_inc = -1;
temp_v = (a.y - cpffloor(a.y));
}
// Division by zero is *very* slow on ARM
cpFloat dx = cpfabs(b.x - a.x), dy = cpfabs(b.y - a.y);
cpFloat dt_dx = (dx ? 1.0f/dx : INFINITY), dt_dy = (dy ? 1.0f/dy : INFINITY);
// fix NANs in horizontal directions
cpFloat next_h = (temp_h ? temp_h*dt_dx : dt_dx);
cpFloat next_v = (temp_v ? temp_v*dt_dy : dt_dy);
int n = hash->numcells;
cpSpaceHashBin **table = hash->table;
while(t < t_exit){
cpHashValue idx = hash_func(cell_x, cell_y, n);
t_exit = cpfmin(t_exit, segmentQuery_helper(hash, &table[idx], obj, func, data));
if (next_v < next_h){
cell_y += y_inc;
t = next_v;
next_v += dt_dy;
} else {
cell_x += x_inc;
t = next_h;
next_h += dt_dx;
}
}
hash->stamp++;
}
//MARK: Misc
void
cpSpaceHashResize(cpSpaceHash *hash, cpFloat celldim, int numcells)
{
if(hash->spatialIndex.klass != Klass()){
cpAssertWarn(cpFalse, "Ignoring cpSpaceHashResize() call to non-cpSpaceHash spatial index.");
return;
}
clearTable(hash);
hash->celldim = celldim;
cpSpaceHashAllocTable(hash, next_prime(numcells));
}
static int
cpSpaceHashCount(cpSpaceHash *hash)
{
return cpHashSetCount(hash->handleSet);
}
static int
cpSpaceHashContains(cpSpaceHash *hash, void *obj, cpHashValue hashid)
{
return cpHashSetFind(hash->handleSet, hashid, obj) != NULL;
}
static cpSpatialIndexClass klass = {
(cpSpatialIndexDestroyImpl)cpSpaceHashDestroy,
(cpSpatialIndexCountImpl)cpSpaceHashCount,
(cpSpatialIndexEachImpl)cpSpaceHashEach,
(cpSpatialIndexContainsImpl)cpSpaceHashContains,
(cpSpatialIndexInsertImpl)cpSpaceHashInsert,
(cpSpatialIndexRemoveImpl)cpSpaceHashRemove,
(cpSpatialIndexReindexImpl)cpSpaceHashRehash,
(cpSpatialIndexReindexObjectImpl)cpSpaceHashRehashObject,
(cpSpatialIndexReindexQueryImpl)cpSpaceHashReindexQuery,
(cpSpatialIndexQueryImpl)cpSpaceHashQuery,
(cpSpatialIndexSegmentQueryImpl)cpSpaceHashSegmentQuery,
};
static inline cpSpatialIndexClass *Klass(){return &klass;}
//MARK: Debug Drawing
//#define CP_BBTREE_DEBUG_DRAW
#ifdef CP_BBTREE_DEBUG_DRAW
#include "OpenGL/gl.h"
#include "OpenGL/glu.h"
#include <GLUT/glut.h>
void
cpSpaceHashRenderDebug(cpSpatialIndex *index)
{
if(index->klass != &klass){
cpAssertWarn(cpFalse, "Ignoring cpSpaceHashRenderDebug() call to non-spatial hash spatial index.");
return;
}
cpSpaceHash *hash = (cpSpaceHash *)index;
cpBB bb = cpBBNew(-320, -240, 320, 240);
cpFloat dim = hash->celldim;
int n = hash->numcells;
int l = (int)floor(bb.l/dim);
int r = (int)floor(bb.r/dim);
int b = (int)floor(bb.b/dim);
int t = (int)floor(bb.t/dim);
for(int i=l; i<=r; i++){
for(int j=b; j<=t; j++){
int cell_count = 0;
int index = hash_func(i,j,n);
for(cpSpaceHashBin *bin = hash->table[index]; bin; bin = bin->next)
cell_count++;
GLfloat v = 1.0f - (GLfloat)cell_count/10.0f;
glColor3f(v,v,v);
glRectf(i*dim, j*dim, (i + 1)*dim, (j + 1)*dim);
}
}
}
#endif

View File

@ -1,246 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "chipmunk/chipmunk_private.h"
//MARK: Nearest Point Query Functions
struct PointQueryContext {
cpVect point;
cpFloat maxDistance;
cpShapeFilter filter;
cpSpacePointQueryFunc func;
};
static cpCollisionID
NearestPointQuery(struct PointQueryContext *context, cpShape *shape, cpCollisionID id, void *data)
{
if(
!cpShapeFilterReject(shape->filter, context->filter)
){
cpPointQueryInfo info;
cpShapePointQuery(shape, context->point, &info);
if(info.shape && info.distance < context->maxDistance) context->func(shape, info.point, info.distance, info.gradient, data);
}
return id;
}
void
cpSpacePointQuery(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpSpacePointQueryFunc func, void *data)
{
struct PointQueryContext context = {point, maxDistance, filter, func};
cpBB bb = cpBBNewForCircle(point, cpfmax(maxDistance, 0.0f));
cpSpaceLock(space); {
cpSpatialIndexQuery(space->dynamicShapes, &context, bb, (cpSpatialIndexQueryFunc)NearestPointQuery, data);
cpSpatialIndexQuery(space->staticShapes, &context, bb, (cpSpatialIndexQueryFunc)NearestPointQuery, data);
} cpSpaceUnlock(space, cpTrue);
}
static cpCollisionID
NearestPointQueryNearest(struct PointQueryContext *context, cpShape *shape, cpCollisionID id, cpPointQueryInfo *out)
{
if(
!cpShapeFilterReject(shape->filter, context->filter) && !shape->sensor
){
cpPointQueryInfo info;
cpShapePointQuery(shape, context->point, &info);
if(info.distance < out->distance) (*out) = info;
}
return id;
}
cpShape *
cpSpacePointQueryNearest(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpPointQueryInfo *out)
{
cpPointQueryInfo info = {NULL, cpvzero, maxDistance, cpvzero};
if(out){
(*out) = info;
} else {
out = &info;
}
struct PointQueryContext context = {
point, maxDistance,
filter,
NULL
};
cpBB bb = cpBBNewForCircle(point, cpfmax(maxDistance, 0.0f));
cpSpatialIndexQuery(space->dynamicShapes, &context, bb, (cpSpatialIndexQueryFunc)NearestPointQueryNearest, out);
cpSpatialIndexQuery(space->staticShapes, &context, bb, (cpSpatialIndexQueryFunc)NearestPointQueryNearest, out);
return (cpShape *)out->shape;
}
//MARK: Segment Query Functions
struct SegmentQueryContext {
cpVect start, end;
cpFloat radius;
cpShapeFilter filter;
cpSpaceSegmentQueryFunc func;
};
static cpFloat
SegmentQuery(struct SegmentQueryContext *context, cpShape *shape, void *data)
{
cpSegmentQueryInfo info;
if(
!cpShapeFilterReject(shape->filter, context->filter) &&
cpShapeSegmentQuery(shape, context->start, context->end, context->radius, &info)
){
context->func(shape, info.point, info.normal, info.alpha, data);
}
return 1.0f;
}
void
cpSpaceSegmentQuery(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSpaceSegmentQueryFunc func, void *data)
{
struct SegmentQueryContext context = {
start, end,
radius,
filter,
func,
};
cpSpaceLock(space); {
cpSpatialIndexSegmentQuery(space->staticShapes, &context, start, end, 1.0f, (cpSpatialIndexSegmentQueryFunc)SegmentQuery, data);
cpSpatialIndexSegmentQuery(space->dynamicShapes, &context, start, end, 1.0f, (cpSpatialIndexSegmentQueryFunc)SegmentQuery, data);
} cpSpaceUnlock(space, cpTrue);
}
static cpFloat
SegmentQueryFirst(struct SegmentQueryContext *context, cpShape *shape, cpSegmentQueryInfo *out)
{
cpSegmentQueryInfo info;
if(
!cpShapeFilterReject(shape->filter, context->filter) && !shape->sensor &&
cpShapeSegmentQuery(shape, context->start, context->end, context->radius, &info) &&
info.alpha < out->alpha
){
(*out) = info;
}
return out->alpha;
}
cpShape *
cpSpaceSegmentQueryFirst(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSegmentQueryInfo *out)
{
cpSegmentQueryInfo info = {NULL, end, cpvzero, 1.0f};
if(out){
(*out) = info;
} else {
out = &info;
}
struct SegmentQueryContext context = {
start, end,
radius,
filter,
NULL
};
cpSpatialIndexSegmentQuery(space->staticShapes, &context, start, end, 1.0f, (cpSpatialIndexSegmentQueryFunc)SegmentQueryFirst, out);
cpSpatialIndexSegmentQuery(space->dynamicShapes, &context, start, end, out->alpha, (cpSpatialIndexSegmentQueryFunc)SegmentQueryFirst, out);
return (cpShape *)out->shape;
}
//MARK: BB Query Functions
struct BBQueryContext {
cpBB bb;
cpShapeFilter filter;
cpSpaceBBQueryFunc func;
};
static cpCollisionID
BBQuery(struct BBQueryContext *context, cpShape *shape, cpCollisionID id, void *data)
{
if(
!cpShapeFilterReject(shape->filter, context->filter) &&
cpBBIntersects(context->bb, shape->bb)
){
context->func(shape, data);
}
return id;
}
void
cpSpaceBBQuery(cpSpace *space, cpBB bb, cpShapeFilter filter, cpSpaceBBQueryFunc func, void *data)
{
struct BBQueryContext context = {bb, filter, func};
cpSpaceLock(space); {
cpSpatialIndexQuery(space->dynamicShapes, &context, bb, (cpSpatialIndexQueryFunc)BBQuery, data);
cpSpatialIndexQuery(space->staticShapes, &context, bb, (cpSpatialIndexQueryFunc)BBQuery, data);
} cpSpaceUnlock(space, cpTrue);
}
//MARK: Shape Query Functions
struct ShapeQueryContext {
cpSpaceShapeQueryFunc func;
void *data;
cpBool anyCollision;
};
// Callback from the spatial hash.
static cpCollisionID
ShapeQuery(cpShape *a, cpShape *b, cpCollisionID id, struct ShapeQueryContext *context)
{
if(cpShapeFilterReject(a->filter, b->filter) || a == b) return id;
cpContactPointSet set = cpShapesCollide(a, b);
if(set.count){
if(context->func) context->func(b, &set, context->data);
context->anyCollision = !(a->sensor || b->sensor);
}
return id;
}
cpBool
cpSpaceShapeQuery(cpSpace *space, cpShape *shape, cpSpaceShapeQueryFunc func, void *data)
{
cpBody *body = shape->body;
cpBB bb = (body ? cpShapeUpdate(shape, body->transform) : shape->bb);
struct ShapeQueryContext context = {func, data, cpFalse};
cpSpaceLock(space); {
cpSpatialIndexQuery(space->dynamicShapes, shape, bb, (cpSpatialIndexQueryFunc)ShapeQuery, &context);
cpSpatialIndexQuery(space->staticShapes, shape, bb, (cpSpatialIndexQueryFunc)ShapeQuery, &context);
} cpSpaceUnlock(space, cpTrue);
return context.anyCollision;
}

View File

@ -1,444 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "chipmunk/chipmunk_private.h"
//MARK: Post Step Callback Functions
cpPostStepCallback *
cpSpaceGetPostStepCallback(cpSpace *space, void *key)
{
cpArray *arr = space->postStepCallbacks;
for(int i=0; i<arr->num; i++){
cpPostStepCallback *callback = (cpPostStepCallback *)arr->arr[i];
if(callback && callback->key == key) return callback;
}
return NULL;
}
static void PostStepDoNothing(cpSpace *space, void *obj, void *data){}
cpBool
cpSpaceAddPostStepCallback(cpSpace *space, cpPostStepFunc func, void *key, void *data)
{
cpAssertWarn(space->locked,
"Adding a post-step callback when the space is not locked is unnecessary. "
"Post-step callbacks will not called until the end of the next call to cpSpaceStep() or the next query.");
if(!cpSpaceGetPostStepCallback(space, key)){
cpPostStepCallback *callback = (cpPostStepCallback *)cpcalloc(1, sizeof(cpPostStepCallback));
callback->func = (func ? func : PostStepDoNothing);
callback->key = key;
callback->data = data;
cpArrayPush(space->postStepCallbacks, callback);
return cpTrue;
} else {
return cpFalse;
}
}
//MARK: Locking Functions
void
cpSpaceLock(cpSpace *space)
{
space->locked++;
}
void
cpSpaceUnlock(cpSpace *space, cpBool runPostStep)
{
space->locked--;
cpAssertHard(space->locked >= 0, "Internal Error: Space lock underflow.");
if(space->locked == 0){
cpArray *waking = space->rousedBodies;
for(int i=0, count=waking->num; i<count; i++){
cpSpaceActivateBody(space, (cpBody *)waking->arr[i]);
waking->arr[i] = NULL;
}
waking->num = 0;
if(space->locked == 0 && runPostStep && !space->skipPostStep){
space->skipPostStep = cpTrue;
cpArray *arr = space->postStepCallbacks;
for(int i=0; i<arr->num; i++){
cpPostStepCallback *callback = (cpPostStepCallback *)arr->arr[i];
cpPostStepFunc func = callback->func;
// Mark the func as NULL in case calling it calls cpSpaceRunPostStepCallbacks() again.
// TODO: need more tests around this case I think.
callback->func = NULL;
if(func) func(space, callback->key, callback->data);
arr->arr[i] = NULL;
cpfree(callback);
}
arr->num = 0;
space->skipPostStep = cpFalse;
}
}
}
//MARK: Contact Buffer Functions
struct cpContactBufferHeader {
cpTimestamp stamp;
cpContactBufferHeader *next;
unsigned int numContacts;
};
#define CP_CONTACTS_BUFFER_SIZE ((CP_BUFFER_BYTES - sizeof(cpContactBufferHeader))/sizeof(struct cpContact))
typedef struct cpContactBuffer {
cpContactBufferHeader header;
struct cpContact contacts[CP_CONTACTS_BUFFER_SIZE];
} cpContactBuffer;
static cpContactBufferHeader *
cpSpaceAllocContactBuffer(cpSpace *space)
{
cpContactBuffer *buffer = (cpContactBuffer *)cpcalloc(1, sizeof(cpContactBuffer));
cpArrayPush(space->allocatedBuffers, buffer);
return (cpContactBufferHeader *)buffer;
}
static cpContactBufferHeader *
cpContactBufferHeaderInit(cpContactBufferHeader *header, cpTimestamp stamp, cpContactBufferHeader *splice)
{
header->stamp = stamp;
header->next = (splice ? splice->next : header);
header->numContacts = 0;
return header;
}
void
cpSpacePushFreshContactBuffer(cpSpace *space)
{
cpTimestamp stamp = space->stamp;
cpContactBufferHeader *head = space->contactBuffersHead;
if(!head){
// No buffers have been allocated, make one
space->contactBuffersHead = cpContactBufferHeaderInit(cpSpaceAllocContactBuffer(space), stamp, NULL);
} else if(stamp - head->next->stamp > space->collisionPersistence){
// The tail buffer is available, rotate the ring
cpContactBufferHeader *tail = head->next;
space->contactBuffersHead = cpContactBufferHeaderInit(tail, stamp, tail);
} else {
// Allocate a new buffer and push it into the ring
cpContactBufferHeader *buffer = cpContactBufferHeaderInit(cpSpaceAllocContactBuffer(space), stamp, head);
space->contactBuffersHead = head->next = buffer;
}
}
struct 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.
cpSpacePushFreshContactBuffer(space);
}
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;
}
//MARK: Collision Detection Functions
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<count; i++) cpArrayPush(space->pooledArbiters, buffer + i);
}
return cpArbiterInit((cpArbiter *)cpArrayPop(space->pooledArbiters), shapes[0], shapes[1]);
}
static inline cpBool
QueryRejectConstraint(cpBody *a, cpBody *b)
{
CP_BODY_FOREACH_CONSTRAINT(a, constraint){
if(
!constraint->collideBodies && (
(constraint->a == a && constraint->b == b) ||
(constraint->a == b && constraint->b == a)
)
) return cpTrue;
}
return cpFalse;
}
static inline cpBool
QueryReject(cpShape *a, cpShape *b)
{
return (
// BBoxes must overlap
!cpBBIntersects(a->bb, b->bb)
// Don't collide shapes attached to the same body.
|| a->body == b->body
// Don't collide shapes that are filtered.
|| cpShapeFilterReject(a->filter, b->filter)
// Don't collide bodies if they have a constraint with collideBodies == cpFalse.
|| QueryRejectConstraint(a->body, b->body)
);
}
// Callback from the spatial hash.
cpCollisionID
cpSpaceCollideShapes(cpShape *a, cpShape *b, cpCollisionID id, cpSpace *space)
{
// Reject any of the simple cases
if(QueryReject(a,b)) return id;
// Narrow-phase collision detection.
struct cpCollisionInfo info = cpCollide(a, b, id, cpContactBufferGetArray(space));
if(info.count == 0) return info.id; // Shapes are not colliding.
cpSpacePushContacts(space, info.count);
// Get an arbiter from space->arbiterSet for the two shapes.
// This is where the persistant contact magic comes from.
const cpShape *shape_pair[] = {info.a, info.b};
cpHashValue arbHashID = CP_HASH_PAIR((cpHashValue)info.a, (cpHashValue)info.b);
cpArbiter *arb = (cpArbiter *)cpHashSetInsert(space->cachedArbiters, arbHashID, shape_pair, (cpHashSetTransFunc)cpSpaceArbiterSetTrans, space);
cpArbiterUpdate(arb, &info, space);
cpCollisionHandler *handler = arb->handler;
// Call the begin function first if it's the first step
if(arb->state == CP_ARBITER_STATE_FIRST_COLLISION && !handler->beginFunc(arb, space, handler->userData)){
cpArbiterIgnore(arb); // permanently ignore the collision until separation
}
if(
// Ignore the arbiter if it has been flagged
(arb->state != CP_ARBITER_STATE_IGNORE) &&
// Call preSolve
handler->preSolveFunc(arb, space, handler->userData) &&
// Check (again) in case the pre-solve() callback called cpArbiterIgnored().
arb->state != CP_ARBITER_STATE_IGNORE &&
// Process, but don't add collisions for sensors.
!(a->sensor || b->sensor) &&
// Don't process collisions between two infinite mass bodies.
!(a->body->m == INFINITY && b->body->m == INFINITY)
){
cpArrayPush(space->arbiters, arb);
} else {
cpSpacePopContacts(space, info.count);
arb->contacts = NULL;
arb->count = 0;
// 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 != CP_ARBITER_STATE_IGNORE) arb->state = CP_ARBITER_STATE_NORMAL;
}
// Time stamp the arbiter so we know it was used recently.
arb->stamp = space->stamp;
return info.id;
}
// Hashset filter func to throw away old arbiters.
cpBool
cpSpaceArbiterSetFilter(cpArbiter *arb, cpSpace *space)
{
cpTimestamp ticks = space->stamp - arb->stamp;
cpBody *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(
(cpBodyGetType(a) == CP_BODY_TYPE_STATIC || cpBodyIsSleeping(a)) &&
(cpBodyGetType(b) == CP_BODY_TYPE_STATIC || cpBodyIsSleeping(b))
){
return cpTrue;
}
// Arbiter was used last frame, but not this one
if(ticks >= 1 && arb->state != CP_ARBITER_STATE_CACHED){
arb->state = CP_ARBITER_STATE_CACHED;
cpCollisionHandler *handler = arb->handler;
handler->separateFunc(arb, space, handler->userData);
}
if(ticks >= space->collisionPersistence){
arb->contacts = NULL;
arb->count = 0;
cpArrayPush(space->pooledArbiters, arb);
return cpFalse;
}
return cpTrue;
}
//MARK: All Important cpSpaceStep() Function
void
cpShapeUpdateFunc(cpShape *shape, void *unused)
{
cpShapeCacheBB(shape);
}
void
cpSpaceStep(cpSpace *space, cpFloat dt)
{
// don't step if the timestep is 0!
if(dt == 0.0f) return;
space->stamp++;
cpFloat prev_dt = space->curr_dt;
space->curr_dt = dt;
cpArray *bodies = space->dynamicBodies;
cpArray *constraints = space->constraints;
cpArray *arbiters = space->arbiters;
// Reset and empty the arbiter lists.
for(int i=0; i<arbiters->num; i++){
cpArbiter *arb = (cpArbiter *)arbiters->arr[i];
arb->state = CP_ARBITER_STATE_NORMAL;
// If both bodies are awake, unthread the arbiter from the contact graph.
if(!cpBodyIsSleeping(arb->body_a) && !cpBodyIsSleeping(arb->body_b)){
cpArbiterUnthread(arb);
}
}
arbiters->num = 0;
cpSpaceLock(space); {
// Integrate positions
for(int i=0; i<bodies->num; i++){
cpBody *body = (cpBody *)bodies->arr[i];
body->position_func(body, dt);
}
// Find colliding pairs.
cpSpacePushFreshContactBuffer(space);
cpSpatialIndexEach(space->dynamicShapes, (cpSpatialIndexIteratorFunc)cpShapeUpdateFunc, NULL);
cpSpatialIndexReindexQuery(space->dynamicShapes, (cpSpatialIndexQueryFunc)cpSpaceCollideShapes, space);
} cpSpaceUnlock(space, cpFalse);
// Rebuild the contact graph (and detect sleeping components if sleeping is enabled)
cpSpaceProcessComponents(space, dt);
cpSpaceLock(space); {
// Clear out old cached arbiters and call separate callbacks
cpHashSetFilter(space->cachedArbiters, (cpHashSetFilterFunc)cpSpaceArbiterSetFilter, space);
// Prestep the arbiters and constraints.
cpFloat slop = space->collisionSlop;
cpFloat biasCoef = 1.0f - cpfpow(space->collisionBias, dt);
for(int i=0; i<arbiters->num; i++){
cpArbiterPreStep((cpArbiter *)arbiters->arr[i], dt, slop, biasCoef);
}
for(int i=0; i<constraints->num; i++){
cpConstraint *constraint = (cpConstraint *)constraints->arr[i];
cpConstraintPreSolveFunc preSolve = constraint->preSolve;
if(preSolve) preSolve(constraint, space);
constraint->klass->preStep(constraint, dt);
}
// Integrate velocities.
cpFloat damping = cpfpow(space->damping, dt);
cpVect gravity = space->gravity;
for(int i=0; i<bodies->num; i++){
cpBody *body = (cpBody *)bodies->arr[i];
body->velocity_func(body, gravity, damping, dt);
}
// Apply cached impulses
cpFloat dt_coef = (prev_dt == 0.0f ? 0.0f : dt/prev_dt);
for(int i=0; i<arbiters->num; i++){
cpArbiterApplyCachedImpulse((cpArbiter *)arbiters->arr[i], dt_coef);
}
for(int i=0; i<constraints->num; i++){
cpConstraint *constraint = (cpConstraint *)constraints->arr[i];
constraint->klass->applyCachedImpulse(constraint, dt_coef);
}
// Run the impulse solver.
for(int i=0; i<space->iterations; i++){
for(int j=0; j<arbiters->num; j++){
cpArbiterApplyImpulse((cpArbiter *)arbiters->arr[j]);
}
for(int j=0; j<constraints->num; j++){
cpConstraint *constraint = (cpConstraint *)constraints->arr[j];
constraint->klass->applyImpulse(constraint, dt);
}
}
// Run the constraint post-solve callbacks
for(int i=0; i<constraints->num; i++){
cpConstraint *constraint = (cpConstraint *)constraints->arr[i];
cpConstraintPostSolveFunc postSolve = constraint->postSolve;
if(postSolve) postSolve(constraint, space);
}
// run the post-solve callbacks
for(int i=0; i<arbiters->num; i++){
cpArbiter *arb = (cpArbiter *) arbiters->arr[i];
cpCollisionHandler *handler = arb->handler;
handler->postSolveFunc(arb, space, handler->userData);
}
} cpSpaceUnlock(space, cpTrue);
}

View File

@ -1,69 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "chipmunk/chipmunk_private.h"
void
cpSpatialIndexFree(cpSpatialIndex *index)
{
if(index){
cpSpatialIndexDestroy(index);
cpfree(index);
}
}
cpSpatialIndex *
cpSpatialIndexInit(cpSpatialIndex *index, cpSpatialIndexClass *klass, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex)
{
index->klass = klass;
index->bbfunc = bbfunc;
index->staticIndex = staticIndex;
if(staticIndex){
cpAssertHard(!staticIndex->dynamicIndex, "This static index is already associated with a dynamic index.");
staticIndex->dynamicIndex = index;
}
return index;
}
typedef struct dynamicToStaticContext {
cpSpatialIndexBBFunc bbfunc;
cpSpatialIndex *staticIndex;
cpSpatialIndexQueryFunc queryFunc;
void *data;
} dynamicToStaticContext;
static void
dynamicToStaticIter(void *obj, dynamicToStaticContext *context)
{
cpSpatialIndexQuery(context->staticIndex, obj, context->bbfunc(obj), context->queryFunc, context->data);
}
void
cpSpatialIndexCollideStatic(cpSpatialIndex *dynamicIndex, cpSpatialIndex *staticIndex, cpSpatialIndexQueryFunc func, void *data)
{
if(staticIndex && cpSpatialIndexCount(staticIndex) > 0){
dynamicToStaticContext context = {dynamicIndex->bbfunc, staticIndex, func, data};
cpSpatialIndexEach(dynamicIndex, (cpSpatialIndexIteratorFunc)dynamicToStaticIter, &context);
}
}

View File

@ -1,254 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "chipmunk/chipmunk_private.h"
static inline cpSpatialIndexClass *Klass();
//MARK: Basic Structures
typedef struct Bounds {
cpFloat min, max;
} Bounds;
typedef struct TableCell {
void *obj;
Bounds bounds;
} TableCell;
struct cpSweep1D
{
cpSpatialIndex spatialIndex;
int num;
int max;
TableCell *table;
};
static inline cpBool
BoundsOverlap(Bounds a, Bounds b)
{
return (a.min <= b.max && b.min <= a.max);
}
static inline Bounds
BBToBounds(cpSweep1D *sweep, cpBB bb)
{
Bounds bounds = {bb.l, bb.r};
return bounds;
}
static inline TableCell
MakeTableCell(cpSweep1D *sweep, void *obj)
{
TableCell cell = {obj, BBToBounds(sweep, sweep->spatialIndex.bbfunc(obj))};
return cell;
}
//MARK: Memory Management Functions
cpSweep1D *
cpSweep1DAlloc(void)
{
return (cpSweep1D *)cpcalloc(1, sizeof(cpSweep1D));
}
static void
ResizeTable(cpSweep1D *sweep, int size)
{
sweep->max = size;
sweep->table = (TableCell *)cprealloc(sweep->table, size*sizeof(TableCell));
}
cpSpatialIndex *
cpSweep1DInit(cpSweep1D *sweep, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex)
{
cpSpatialIndexInit((cpSpatialIndex *)sweep, Klass(), bbfunc, staticIndex);
sweep->num = 0;
ResizeTable(sweep, 32);
return (cpSpatialIndex *)sweep;
}
cpSpatialIndex *
cpSweep1DNew(cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex)
{
return cpSweep1DInit(cpSweep1DAlloc(), bbfunc, staticIndex);
}
static void
cpSweep1DDestroy(cpSweep1D *sweep)
{
cpfree(sweep->table);
sweep->table = NULL;
}
//MARK: Misc
static int
cpSweep1DCount(cpSweep1D *sweep)
{
return sweep->num;
}
static void
cpSweep1DEach(cpSweep1D *sweep, cpSpatialIndexIteratorFunc func, void *data)
{
TableCell *table = sweep->table;
for(int i=0, count=sweep->num; i<count; i++) func(table[i].obj, data);
}
static int
cpSweep1DContains(cpSweep1D *sweep, void *obj, cpHashValue hashid)
{
TableCell *table = sweep->table;
for(int i=0, count=sweep->num; i<count; i++){
if(table[i].obj == obj) return cpTrue;
}
return cpFalse;
}
//MARK: Basic Operations
static void
cpSweep1DInsert(cpSweep1D *sweep, void *obj, cpHashValue hashid)
{
if(sweep->num == sweep->max) ResizeTable(sweep, sweep->max*2);
sweep->table[sweep->num] = MakeTableCell(sweep, obj);
sweep->num++;
}
static void
cpSweep1DRemove(cpSweep1D *sweep, void *obj, cpHashValue hashid)
{
TableCell *table = sweep->table;
for(int i=0, count=sweep->num; i<count; i++){
if(table[i].obj == obj){
int num = --sweep->num;
table[i] = table[num];
table[num].obj = NULL;
return;
}
}
}
//MARK: Reindexing Functions
static void
cpSweep1DReindexObject(cpSweep1D *sweep, void *obj, cpHashValue hashid)
{
// Nothing to do here
}
static void
cpSweep1DReindex(cpSweep1D *sweep)
{
// Nothing to do here
// Could perform a sort, but queries are not accelerated anyway.
}
//MARK: Query Functions
static void
cpSweep1DQuery(cpSweep1D *sweep, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data)
{
// Implementing binary search here would allow you to find an upper limit
// but not a lower limit. Probably not worth the hassle.
Bounds bounds = BBToBounds(sweep, bb);
TableCell *table = sweep->table;
for(int i=0, count=sweep->num; i<count; i++){
TableCell cell = table[i];
if(BoundsOverlap(bounds, cell.bounds) && obj != cell.obj) func(obj, cell.obj, 0, data);
}
}
static void
cpSweep1DSegmentQuery(cpSweep1D *sweep, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data)
{
cpBB bb = cpBBExpand(cpBBNew(a.x, a.y, a.x, a.y), b);
Bounds bounds = BBToBounds(sweep, bb);
TableCell *table = sweep->table;
for(int i=0, count=sweep->num; i<count; i++){
TableCell cell = table[i];
if(BoundsOverlap(bounds, cell.bounds)) func(obj, cell.obj, data);
}
}
//MARK: Reindex/Query
static int
TableSort(TableCell *a, TableCell *b)
{
return (a->bounds.min < b->bounds.min ? -1 : (a->bounds.min > b->bounds.min ? 1 : 0));
}
static void
cpSweep1DReindexQuery(cpSweep1D *sweep, cpSpatialIndexQueryFunc func, void *data)
{
TableCell *table = sweep->table;
int count = sweep->num;
// Update bounds and sort
for(int i=0; i<count; i++) table[i] = MakeTableCell(sweep, table[i].obj);
qsort(table, count, sizeof(TableCell), (int (*)(const void *, const void *))TableSort); // TODO: use insertion sort instead
for(int i=0; i<count; i++){
TableCell cell = table[i];
cpFloat max = cell.bounds.max;
for(int j=i+1; table[j].bounds.min < max && j<count; j++){
func(cell.obj, table[j].obj, 0, data);
}
}
// Reindex query is also responsible for colliding against the static index.
// Fortunately there is a helper function for that.
cpSpatialIndexCollideStatic((cpSpatialIndex *)sweep, sweep->spatialIndex.staticIndex, func, data);
}
static cpSpatialIndexClass klass = {
(cpSpatialIndexDestroyImpl)cpSweep1DDestroy,
(cpSpatialIndexCountImpl)cpSweep1DCount,
(cpSpatialIndexEachImpl)cpSweep1DEach,
(cpSpatialIndexContainsImpl)cpSweep1DContains,
(cpSpatialIndexInsertImpl)cpSweep1DInsert,
(cpSpatialIndexRemoveImpl)cpSweep1DRemove,
(cpSpatialIndexReindexImpl)cpSweep1DReindex,
(cpSpatialIndexReindexObjectImpl)cpSweep1DReindexObject,
(cpSpatialIndexReindexQueryImpl)cpSweep1DReindexQuery,
(cpSpatialIndexQueryImpl)cpSweep1DQuery,
(cpSpatialIndexSegmentQueryImpl)cpSweep1DSegmentQuery,
};
static inline cpSpatialIndexClass *Klass(){return &klass;}

View File

@ -1,68 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// Used for resizing hash tables.
// Values approximately double.
// http://planetmath.org/encyclopedia/GoodHashTablePrimes.html
static int primes[] = {
5,
13,
23,
47,
97,
193,
389,
769,
1543,
3079,
6151,
12289,
24593,
49157,
98317,
196613,
393241,
786433,
1572869,
3145739,
6291469,
12582917,
25165843,
50331653,
100663319,
201326611,
402653189,
805306457,
1610612741,
0,
};
static inline int
next_prime(int n)
{
int i = 0;
while(n > primes[i]){
i++;
cpAssertHard(primes[i], "Tried to resize a hash table to a size greater than 1610612741 O_o"); // realistically this should never happen
}
return primes[i];
}

View File

@ -1,192 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 ChipmunkShape;
@class ChipmunkConstraint;
/**
Rigid bodies are the basic unit of simulation in Chipmunk.
They hold the physical properties of an object (mass, position, rotation, velocity, etc.). After creating a ChipmunkBody object, you can attach collision shapes (ChipmunkShape) and joints (ChipmunkConstraint) to it.
*/
@interface ChipmunkBody : NSObject <ChipmunkBaseObject>
/// Get the ChipmunkBody object associciated with a cpBody pointer.
/// Undefined if the cpBody wasn't created using Objective-Chipmunk.
+(ChipmunkBody *)bodyFromCPBody:(cpBody *)body;
/**
Create an autoreleased rigid body with the given mass and moment.
Guessing the moment of inertia is usually a bad idea. Use the moment estimation functions (cpMomentFor*()).
*/
+ (id)bodyWithMass:(cpFloat)mass andMoment:(cpFloat)moment;
/**
Create an autoreleased static body.
*/
+ (id)staticBody;
/**
Create an autoreleased kinematic body.
*/
+ (id)kinematicBody;
/**
Initialize a rigid body with the given mass and moment of inertia.
Guessing the moment of inertia is usually a bad idea. Use the moment estimation functions (cpMomentFor*()).
*/
- (id)initWithMass:(cpFloat)mass andMoment:(cpFloat)moment;
/// Type of the body (dynamic, kinematic, static).
@property(nonatomic, assign) cpBodyType type;
/// Mass of the rigid body. Mass does not have to be expressed in any particular units, but relative masses should be consistent.
@property(nonatomic, assign) cpFloat mass;
/// Moment of inertia of the body. The mass tells you how hard it is to push an object, the MoI tells you how hard it is to spin the object. Don't try to guess the MoI, use the cpMomentFor*() functions to try and estimate it.
@property(nonatomic, assign) cpFloat moment;
/// Location of the body's center of gravity relative to it's position. Defaults to @c cpvzero.
@property(nonatomic, assign) cpVect centerOfGravity;
/// The position of the rigid body's center of gravity.
@property(nonatomic, assign) cpVect position;
/// The linear velocity of the rigid body.
@property(nonatomic, assign) cpVect velocity;
/// The linear force applied to the rigid body. Unlike in some physics engines, the force does not reset itself during each step. Make sure that you are reseting the force between frames if that is what you intended.
@property(nonatomic, assign) cpVect force;
/// The rotation angle of the rigid body in radians.
@property(nonatomic, assign) cpFloat angle;
/// The angular velocity of the rigid body in radians per second.
@property(nonatomic, assign) cpFloat angularVelocity;
/// The torque being applied to the rigid body. Like force, this property is not reset every frame.
@property(nonatomic, assign) cpFloat torque;
/// The rigid transform of the body.
@property(nonatomic, readonly) cpTransform transform;
/// Returns a pointer to the underlying cpBody C struct.
@property(nonatomic, readonly) cpBody *body;
/**
An object that this constraint is associated with. You can use this get a reference to your game object or controller object from within callbacks.
@attention Like most @c delegate properties this is a weak reference and does not call @c retain. This prevents reference cycles from occuring.
*/
@property(nonatomic, assign) id userData;
/// Has the body been put to sleep by the space?
@property(nonatomic, readonly) bool isSleeping;
/// Get the kinetic energy of this body.
@property(nonatomic, readonly) cpFloat kineticEnergy;
/// Get the space the body is added to.
@property(nonatomic, readonly) ChipmunkSpace *space;
/**
Convert from body local to world coordinates.
Convert a point in world (absolute) coordinates to body local coordinates affected by the position and rotation of the rigid body.
*/
- (cpVect)localToWorld:(cpVect)v;
/**
Convert from world to body local Coordinates.
Convert a point in body local coordinates coordinates to world (absolute) coordinates.
*/
- (cpVect)worldToLocal:(cpVect)v;
/**
Get the velocity of a point on a body.
Get the world (absolute) velocity of a point on a rigid body specified in body local coordinates.
*/
- (cpVect)velocityAtLocalPoint:(cpVect)p;
/**
Get the velocity of a point on a body.
Get the world (absolute) velocity of a point on a rigid body specified in world coordinates.
*/
- (cpVect)velocityAtWorldPoint:(cpVect)p;
/**
Apply a force to a rigid body. An offset of cpvzero is equivalent to adding directly to the force property.
@param force A force in expressed in absolute (word) coordinates.
@param offset An offset expressed in world coordinates. Note that it is still an offset, meaning that it's position is relative, but the rotation is not.
*/
- (void)applyForce:(cpVect)force atLocalPoint:(cpVect)point;
- (void)applyForce:(cpVect)force atWorldPoint:(cpVect)point;
/**
Apply an impulse to a rigid body.
@param impulse An impulse in expressed in absolute (word) coordinates.
@param offset An offset expressed in world coordinates. Note that it is still an offset, meaning that it's position is relative, but the rotation is not.
*/
- (void)applyImpulse:(cpVect)impulse atLocalPoint:(cpVect)point;
- (void)applyImpulse:(cpVect)impulse atWorldPoint:(cpVect)point;
/// Wake up the body if it's sleeping, or reset the idle timer if it's active.
- (void)activate;
/// Wake up any bodies touching a static body through shape @c filter Pass @c nil for @c filter to away all touching bodies.
- (void)activateStatic:(ChipmunkShape *)filter;
/**
Force the body to sleep immediately. The body will be added to the same group as @c group. When any object in a group is woken up, all of the bodies are woken up with it.
If @c group is nil, then a new group is created and the body is added to it. It is an error pass a non-sleeping body as @c group.
This is useful if you want an object to be inactive until something hits it such as a pile of boxes you want the player to plow through or a stalactite hanging from a cave ceiling.
Make sure the body is fully set up before you call this. Adding this body or any shapes or constraints attached to it to a space, or modifying any of their properties automatically wake a body up.
*/
- (void)sleepWithGroup:(ChipmunkBody *)group;
/**
Equivalent to [ChipmunkBody sleepWithGroup:nil]. That is the object is forced to sleep immediately, but is not grouped with any other sleeping bodies.
*/
- (void)sleep;
/// Get a list of shapes that are attached to this body and currently added to a space.
- (NSArray *)shapes;
/// Get a list of constraints that are attached to this body and currently added to a space.
- (NSArray *)constraints;
/// Body/arbiter iterator callback block type.
typedef void (^ChipmunkBodyArbiterIteratorBlock)(cpArbiter *arbiter);
/// Call @c block once for each arbiter that is currently active on the body.
- (void)eachArbiter:(ChipmunkBodyArbiterIteratorBlock)block;
/// Implements the ChipmunkBaseObject protocol, not particularly useful outside of the library code
- (void)addToSpace:(ChipmunkSpace *)space;
/// Implements the ChipmunkBaseObject protocol, not particularly useful outside of the library code
- (void)removeFromSpace:(ChipmunkSpace *)space;
/// Override this to change the way that the body's velocity is integrated.
/// You should either understand how the cpBodyUpdateVelocity() function works, or use the super method.
-(void)updateVelocity:(cpFloat)dt gravity:(cpVect)gravity damping:(cpFloat)damping;
/// OVerride this to change the way that the body's position is intgrated.
/// You should either understand how the cpBodyUpdatePosition() function works, or use the super method.
-(void)updatePosition:(cpFloat)dt;
@end

View File

@ -1,405 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
Constraints connect two ChipmunkBody objects together. Most often constraints are simple joints, but can also be things like motors, friction generators or servos.
@htmlonly
<object width="425" height="344">
<param name="movie" value="http://www.youtube.com/v/ZgJJZTS0aMM?fs=1&amp;hl=en_US&amp;rel=0"></param>
<param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param>
<embed src="http://www.youtube.com/v/ZgJJZTS0aMM?fs=1&amp;hl=en_US&amp;rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed>
</object>
@endhtmlonly
*/
@interface ChipmunkConstraint : NSObject <ChipmunkBaseObject> {
@private
id _userData;
}
/// Returns a pointer to the underlying cpConstraint C struct.
@property(nonatomic, readonly) cpConstraint *constraint;
/// The first ChipmunkBody the constraint controls.
@property(nonatomic, readonly) ChipmunkBody *bodyA;
/// The second ChipmunkBody the constraint controls.
@property(nonatomic, readonly) ChipmunkBody *bodyB;
/// Get the ChipmunkConstraint object associciated with a cpConstraint pointer.
/// Undefined if the cpConstraint wasn't created using Objective-Chipmunk.
+(ChipmunkConstraint *)constraintFromCPConstraint:(cpConstraint *)constraint;
/**
Maximum force this constraint is allowed to use (defalts to infinity).
This allows joints to be pulled apart if too much force is applied to them.
It also allows you to use constraints as force or friction generators for controlling bodies.
*/
@property(nonatomic, assign) cpFloat maxForce;
/**
The rate at which joint error is corrected.
Defaults to pow(1.0 - 0.1, 60.0) meaning that it will correct 10% of the error every 1/60th of a second.
*/
@property(nonatomic, assign) cpFloat errorBias;
/**
Maximum rate (speed) that a joint can be corrected at (defaults to infinity).
Setting this value to a finite value allows you to control a joint like a servo motor.
*/
@property(nonatomic, assign) cpFloat maxBias;
/**
Whether or not the connected bodies should checked for collisions.
Collisions are filtered before calling callbacks.
Defaults to TRUE.
*/
@property(nonatomic, assign) BOOL collideBodies;
/// Get the most recent impulse applied by this constraint.
@property(nonatomic, readonly) cpFloat impulse;
/// Get the space the body is added to.
@property(nonatomic, readonly) ChipmunkSpace *space;
/**
An object that this constraint is associated with. You can use this get a reference to your game object or controller object from within callbacks.
@attention Like most @c delegate properties this is a weak reference and does not call @c retain. This prevents reference cycles from occuring.
*/
@property(nonatomic, assign) id userData;
/// Override this method to update a constraints parameters just before running the physics each step.
-(void)preSolve:(ChipmunkSpace *)space;
/// Override this method to poll values from a constraint each frame after the physics runs.
/// This can be used to implement breakable joints for instance.
-(void)postSolve:(ChipmunkSpace *)space;
@end
/**
Pin joints hold a set distance between points on two bodies.
Think of them as connecting a solid pin or rod between the two anchor points.
*/
@interface ChipmunkPinJoint : ChipmunkConstraint
/**
Create an autoreleased pin joint between the two bodies with the given anchor points.
The distance is calculated when the joint is initialized. It can be set explicitly using the property.
*/
+ (ChipmunkPinJoint *)pinJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB;
/**
Initialize a pin joint between the two bodies with the given anchor points.
The distance is calculated when the joint is initialized. It can be set explicitly using the property.
*/
- (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB;
/// The anchor point on the first body.
@property(nonatomic, assign) cpVect anchorA;
/// The anchor point on the second body.
@property(nonatomic, assign) cpVect anchorB;
/// The distance between the two anchor points that the joint keeps.
@property(nonatomic, assign) cpFloat dist;
@end
/**
Slide joints hold the distance between points on two bodies between a minimum and a maximum.
Think of them as a telescoping ChipmunkPinJoint.
*/
@interface ChipmunkSlideJoint : ChipmunkConstraint
/**
Create an autoreleased slide joint between the two bodies with the given anchor points and distance range.
*/
+ (ChipmunkSlideJoint *)slideJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB min:(cpFloat)min max:(cpFloat)max;
/**
Initialize a slide joint between the two bodies with the given anchor points and distance range.
*/
- (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB min:(cpFloat)min max:(cpFloat)max;
/// The anchor point on the first body.
@property(nonatomic, assign) cpVect anchorA;
/// The anchor point on the second body.
@property(nonatomic, assign) cpVect anchorB;
/// The minimum allowed distance between anchor points.
@property(nonatomic, assign) cpFloat min;
/// The maximum allowed distance between anchor points.
@property(nonatomic, assign) cpFloat max;
@end
/**
Pivot joints hold two points on two bodies together allowing them to rotate freely around the pivot.
*/
@interface ChipmunkPivotJoint : ChipmunkConstraint
/**
Create an autoreleased pivot joint between the two bodies with the two anchor points.
Make sure you have the bodies in the right place as the joint will fix itself as soon as you start simulating the space.
*/
+ (ChipmunkPivotJoint *)pivotJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB;
/**
Create an autoreleased pivot joint between the two bodies by calculating the anchor points from the pivot point given in absolute coordinates.
*/
+ (ChipmunkPivotJoint *)pivotJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b pivot:(cpVect)pivot;
/**
Initialize a pivot joint between the two bodies with the two anchor points.
Make sure you have the bodies in the right place as the joint will fix itself as soon as you start simulating the space.
*/
- (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB;
/**
Initialize a pivot joint between the two bodies by calculating the anchor points from the pivot point given in absolute coordinates.
*/
- (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b pivot:(cpVect)pivot;
/// The anchor point on the first body.
@property(nonatomic, assign) cpVect anchorA;
/// The anchor point on the second body.
@property(nonatomic, assign) cpVect anchorB;
@end
/**
Groove joints hold a pivot point on one body to line along a line segment on another like a pin in a groove.
*/
@interface ChipmunkGrooveJoint : ChipmunkConstraint
/**
Create an autoreleased groove joint between the two bodies.
Make sure you have the bodies in the right place as the joint will snap into shape as soon as you start simulating the space.
@param grooveA The start of the line segment on the first body.
@param grooveB The end of the line segment on the first body.
@param anchorB The anchor point on the second body that is held to the line segment on the first.
*/
+ (ChipmunkGrooveJoint *)grooveJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b grooveA:(cpVect)grooveA grooveB:(cpVect)grooveB anchorB:(cpVect)anchorB;
/**
Initialize a groove joint between the two bodies.
Make sure you have the bodies in the right place as the joint will snap into shape as soon as you start simulating the space.
@param grooveA The start of the line segment on the first body.
@param grooveB The end of the line segment on the first body.
@param anchorB The anchor point on the second body that is held to the line segment on the first.
*/
- (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b grooveA:(cpVect)grooveA grooveB:(cpVect)grooveB anchorB:(cpVect)anchorB;
/// The start point of the groove on the first body.
@property(nonatomic, assign) cpVect grooveA;
/// The end point of the groove on the first body.
@property(nonatomic, assign) cpVect grooveB;
/// The anchor point on the second body.
@property(nonatomic, assign) cpVect anchorB;
@end
/**
A spring with a damper.
While a spring is not technically a constraint, the damper is. The spring forces are simply a convenience.
*/
@interface ChipmunkDampedSpring : ChipmunkConstraint
/**
Create an autoreleased damped spring between two bodies at the given anchor points.
@param restLength The length the spring wants to contract or expand to.
@param stiffness The <a href="http://en.wikipedia.org/wiki/Young's_modulus">young's modulus</a> of the spring.
@param damping The amount of viscous damping to apply.
*/
+ (ChipmunkDampedSpring *)dampedSpringWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB restLength:(cpFloat)restLength stiffness:(cpFloat)stiffness damping:(cpFloat)damping;
/**
Initialize a damped spring between two bodies at the given anchor points.
@param restLength The length the spring wants to contract or expand to.
@param stiffness The <a href="http://en.wikipedia.org/wiki/Young's_modulus">young's modulus</a> of the spring.
@param damping The amount of viscous damping to apply.
*/
- (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB restLength:(cpFloat)restLength stiffness:(cpFloat)stiffness damping:(cpFloat)damping;
/// The anchor point on the first body.
@property(nonatomic, assign) cpVect anchorA;
/// The anchor point on the second body.
@property(nonatomic, assign) cpVect anchorB;
/// The length the spring wants to contract or expand to.
@property(nonatomic, assign) cpFloat restLength;
/// The <a href="http://en.wikipedia.org/wiki/Young's_modulus">young's modulus</a> of the spring.
@property(nonatomic, assign) cpFloat stiffness;
/// The amount of viscous damping to apply.
@property(nonatomic, assign) cpFloat damping;
@end
/**
Like a ChipmunkDampedSpring, but operates in a rotational fashion.
*/
@interface ChipmunkDampedRotarySpring : ChipmunkConstraint
/**
Create an autoreleased damped rotary spring between the given bodies.
@param restAngle The angular offset in radians the spring attempts to keep between the two bodies.
@param stiffness The <a href="http://en.wikipedia.org/wiki/Young's_modulus">young's modulus</a> of the spring.
@param damping The amount of viscous damping to apply.
*/
+ (ChipmunkDampedRotarySpring *)dampedRotarySpringWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b restAngle:(cpFloat)restAngle stiffness:(cpFloat)stiffness damping:(cpFloat)damping;
/**
Initialize a damped rotary spring between the given bodies.
@param restAngle The angular offset in radians the spring attempts to keep between the two bodies.
@param stiffness The <a href="http://en.wikipedia.org/wiki/Young's_modulus">young's modulus</a> of the spring.
@param damping The amount of viscous damping to apply.
*/
- (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b restAngle:(cpFloat)restAngle stiffness:(cpFloat)stiffness damping:(cpFloat)damping;
/// The angular offset the spring attempts to keep between the two bodies.
@property(nonatomic, assign) cpFloat restAngle;
/// The <a href="http://en.wikipedia.org/wiki/Young's_modulus">young's modulus</a> of the spring.
@property(nonatomic, assign) cpFloat stiffness;
/// The amount of viscous damping to apply.
@property(nonatomic, assign) cpFloat damping;
@end
/**
Constrains the angle between two bodies.
This joint is often used in conjuction with a separate ChipmunkPivotJoint in order to limit the rotation around the pivot.
*/
@interface ChipmunkRotaryLimitJoint : ChipmunkConstraint
/**
Create an autoreleased rotary limit joint between the two bodies and angular range in radians.
Make sure you have the bodies in the right place as the joint will snap into shape as soon as you start simulating the space.
*/
+ (ChipmunkRotaryLimitJoint *)rotaryLimitJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b min:(cpFloat)min max:(cpFloat)max;
/**
Create an autoreleased rotary limit joint between the two bodies and angular range in radians.
Make sure you have the bodies in the right place as the joint will snap into shape as soon as you start simulating the space.
*/
- (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b min:(cpFloat)min max:(cpFloat)max;
/// The minimum angular delta of the joint in radians.
@property(nonatomic, assign) cpFloat min;
/// The maximum angular delta of the joint in radians.
@property(nonatomic, assign) cpFloat max;
@end
/**
Simple motors make two objects spin relative to each other.
They are most often used with the ChipmunkConstraint.maxForce property set to a finite value.
*/
@interface ChipmunkSimpleMotor : ChipmunkConstraint
/// Create an autoreleased simple motor between the given bodies and relative rotation rate in radians per second.
+ (ChipmunkSimpleMotor *)simpleMotorWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b rate:(cpFloat)rate;
/// Initialize a simple motor between the given bodies and relative rotation rate in radians per second.
- (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b rate:(cpFloat)rate;
/// The relative rotation speed of the two bodies in radians per second.
@property(nonatomic, assign) cpFloat rate;
@end
/**
Gear joints constrain the rotational speed of one body to another.
A ratio of 1.0 will lock the rotation of two bodies together, and negative ratios will cause them to spin in opposite directions.
You can also use gear joints as rotary servos by setting ChipmunkConstraint.maxForce and ChipmunkConstraint.maxBias to finite values and changing the ChipmunkGearJoint.phase property.
*/
@interface ChipmunkGearJoint : ChipmunkConstraint
/**
Create an autoreleased gear joint between the given bodies.
@param phase The angular offset.
@param ratio The ratio of the rotational speeds.
*/
+ (ChipmunkGearJoint *)gearJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b phase:(cpFloat)phase ratio:(cpFloat)ratio;
/**
Initialize a gear joint between the given bodies.
@param phase The angular offset in radians.
@param ratio The ratio of the rotational speeds.
*/
- (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b phase:(cpFloat)phase ratio:(cpFloat)ratio;
/// The angular offset in radians.
@property(nonatomic, assign) cpFloat phase;
/// The ratio of the rotational speeds.
@property(nonatomic, assign) cpFloat ratio;
@end
/**
Ratchet joints create rotary ratches similar to a socket wrench.
*/
@interface ChipmunkRatchetJoint : ChipmunkConstraint
/**
Create an autoreleased ratchet joint between the given bodies.
@param phase The angular offset of the ratchet positions in radians.
@param ratchet The angle in radians of each ratchet position. Negative values cause the ratchet to operate in the opposite direction.
*/
+ (ChipmunkRatchetJoint *)ratchetJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b phase:(cpFloat)phase ratchet:(cpFloat)ratchet;
/**
Initialize a ratchet joint between the given bodies.
@param phase The angular offset of the ratchet positions in radians.
@param ratchet The angle in radians of each ratchet position. Negative values cause the ratchet to operate in the opposite direction.
*/
- (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b phase:(cpFloat)phase ratchet:(cpFloat)ratchet;
/// The current ratchet position in radians.
@property(nonatomic, assign) cpFloat angle;
/// The angular offset of the ratchet positions in radians
@property(nonatomic, assign) cpFloat phase;
/// The angle in radians of each ratchet position. Negative values cause the ratchet to operate in the opposite direction.
@property(nonatomic, assign) cpFloat ratchet;
@end

View File

@ -1,5 +0,0 @@
// Copyright 2013 Howling Moon Software. All rights reserved.
// See http://chipmunk2d.net/legal.php for more information.
//#define ChipmunkGetObject(s) s->data
//#define ChipmunkGetData(s) [s->data data]

View File

@ -1,136 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 "ObjectiveChipmunk/ObjectiveChipmunk.h"
@interface ChipmunkGrab : NSObject<ChipmunkObject> {
NSArray *_chipmunkObjects;
cpVect _pos;
cpFloat _smoothing;
ChipmunkShape *_grabbedShape;
id _data;
}
/// Last touch location of the grab.
@property(nonatomic, readonly) cpVect pos;
/// The ChipmunkShape that this grab was created for.
@property(nonatomic, readonly) ChipmunkShape *grabbedShape;
/// User definable pointer
@property(nonatomic, retain) id data;
@end
/// Simple class to implement multitouch grabbing of physics objects.
@interface ChipmunkMultiGrab : NSObject {
ChipmunkSpace *_space;
NSMutableArray *_grabs;
cpFloat _smoothing;
cpFloat _grabForce;
cpFloat _grabFriction;
cpFloat _grabRotaryFriction;
cpFloat _grabRadius;
cpShapeFilter filter;
bool (^_grabFilter)(ChipmunkShape *shape);
cpFloat (^_grabSort)(ChipmunkShape *shape, cpFloat depth);
bool _pushMode, _pullMode;
cpFloat _pushMass;
cpFloat _pushFriction;
cpFloat _pushElasticity;
cpCollisionType _pushCollisionType;
}
@property(nonatomic, assign) cpFloat smoothing;
@property(nonatomic, assign) cpFloat grabForce;
/// Layers used for the point query when grabbing objects.
@property(nonatomic, assign) cpShapeFilter filter;
/// Group used for the point query when grabbing objects
@property(nonatomic, assign) cpGroup group;
/// Gives you the opportunity to further filter shapes. Return FALSE to ignore a shape.
/// The default implementation always returns TRUE.
@property(nonatomic, copy) bool (^grabFilter)(ChipmunkShape *shape);
/// When clicking on a spot where two shapes overlap, the default behavior is to grab the shape that
/// overlaps the grab point the most. It's possible to use a custom sorting order instead however.
/// The block is called with each shape and the grab depth.
/// It should return a positive float. The shape with the highest value is grabbed.
/// The block is only called if the touch location is within a shape.
@property(nonatomic, copy) cpFloat (^grabSort)(ChipmunkShape *shape, cpFloat depth);
/// Amount of friction applied by the touch.
/// Should be less than the grabForce. Defaults to 0.0.
@property(nonatomic, assign) cpFloat grabFriction;
/// The amount torque to apply to the grab to keep it from spinning.
/// Defaults to 0.0.
@property(nonatomic, assign) cpFloat grabRotaryFriction;
/// On a touch screen, a single point query can make it really hard to grab small objects with a fat finger.
/// By providing a radius, it will make it much easier for users to grab objects.
/// Defaults to 0.0.
@property(nonatomic, assign) cpFloat grabRadius;
@property(nonatomic, assign) bool pullMode;
@property(nonatomic, assign) bool pushMode;
@property(nonatomic, assign) cpFloat pushMass;
@property(nonatomic, assign) cpFloat pushFriction;
@property(nonatomic, assign) cpFloat pushElasticity;
@property(nonatomic, assign) cpCollisionType pushCollisionType;
@property(nonatomic, readonly) NSArray *grabs;
/**
@c space is the space to grab shapes in.
@c smoothing is the amount of mouse smoothing to apply as percentage of remaining error per second.
cpfpow(0.8, 60) is a good starting point that provides fast response, but smooth mouse updates.
@c force is the force the grab points can apply.
*/
-(id)initForSpace:(ChipmunkSpace *)space withSmoothing:(cpFloat)smoothing withGrabForce:(cpFloat)grabForce;
/// Start tracking a new grab point
/// Returns the ChipmunkGrab that is tracking the touch, but only if a shape was grabbed.
/// Returns nil when creating a push shape (if push mode is enabled), or when no shape is grabbed.
-(ChipmunkGrab *)beginLocation:(cpVect)pos;
/// Update a grab point.
/// Returns the ChipmunkGrab that is tracking the touch, but only if the grab is tracking a shape.
-(ChipmunkGrab *)updateLocation:(cpVect)pos;
/// End a grab point.
/// Returns the ChipmunkGrab that was tracking the touch, but only if the grab was tracking a shape.
-(ChipmunkGrab *)endLocation:(cpVect)pos;
@end

View File

@ -1,249 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 ChipmunkPointQueryInfo;
@class ChipmunkSegmentQueryInfo;
/// Abstract base class for collsion shape types.
@interface ChipmunkShape : NSObject <ChipmunkBaseObject> {
@private
id _userData;
}
/// Get the ChipmunkShape object associciated with a cpShape pointer.
/// Undefined if the cpShape wasn't created using Objective-Chipmunk.
+(ChipmunkShape *)shapeFromCPShape:(cpShape *)shape;
/// Returns a pointer to the underlying cpShape C struct.
@property(nonatomic, readonly) cpShape *shape;
/// The ChipmunkBody that this shape is attached to.
@property(nonatomic, retain) ChipmunkBody *body;
// TODO doc
@property(nonatomic, assign) cpFloat mass;
@property(nonatomic, assign) cpFloat density;
@property(nonatomic, readonly) cpFloat moment;
@property(nonatomic, readonly) cpFloat area;
@property(nonatomic, readonly) cpVect centerOfGravity;
/// The axis-aligned bounding box for this shape.
@property(nonatomic, readonly) cpBB bb;
/// Sensor shapes send collision callback messages, but don't create a collision response.
@property(nonatomic, assign) BOOL sensor;
/// How bouncy this shape is.
@property(nonatomic, assign) cpFloat elasticity;
/// How much friction this shape has.
@property(nonatomic, assign) cpFloat friction;
/**
The velocity of the shape's surface.
This velocity is used in the collision response when calculating the friction only.
*/
@property(nonatomic, assign) cpVect surfaceVelocity;
/**
An object reference used as a collision type identifier. This is used when defining collision handlers.
@attention Like most @c delegate properties this is a weak reference and does not call @c retain.
*/
@property(nonatomic, assign) cpCollisionType collisionType;
/**
The collision filtering parameters of this shape.
*/
@property(nonatomic, assign) cpShapeFilter filter;
/// Get the space the body is added to.
@property(nonatomic, readonly) ChipmunkSpace *space;
/**
An object that this shape is associated with. You can use this get a reference to your game object or controller object from within callbacks.
@attention Like most @c delegate properties this is a weak reference and does not call @c retain. This prevents reference cycles from occuring.
*/
@property(nonatomic, assign) id userData;
/// Update and cache the axis-aligned bounding box for this shape.
- (cpBB)cacheBB;
- (ChipmunkPointQueryInfo *)pointQuery:(cpVect)point;
- (ChipmunkSegmentQueryInfo *)segmentQueryFrom:(cpVect)start to:(cpVect)end radius:(cpFloat)radius;
@end
@interface ChipmunkPointQueryInfo : NSObject {
@private
cpPointQueryInfo _info;
}
- (id)initWithInfo:(cpPointQueryInfo *)info;
/// Returns a pointer to the underlying cpNearestPointQueryInfo C struct.
@property(nonatomic, readonly) cpPointQueryInfo *info;
/// The ChipmunkShape found.
@property(nonatomic, readonly) ChipmunkShape *shape;
/// The closest point on the surface of the shape to the point.
@property(nonatomic, readonly) cpVect point;
/// The distance between the point and the surface of the shape.
/// Negative distances mean that the point is that depth inside the shape.
@property(nonatomic, readonly) cpFloat distance;
/// The gradient of the signed distance function.
/// The same as info.point/info.dist, but accurate even for very small values of info.dist.
@property(nonatomic, readonly) cpVect gradient;
@end
/// Holds collision information from segment queries. You should never need to create one.
@interface ChipmunkSegmentQueryInfo : NSObject {
@private
cpSegmentQueryInfo _info;
cpVect _start, _end;
}
- (id)initWithInfo:(cpSegmentQueryInfo *)info start:(cpVect)start end:(cpVect)end;
/// Returns a pointer to the underlying cpSegmentQueryInfo C struct.
@property(nonatomic, readonly) cpSegmentQueryInfo *info;
/// The ChipmunkShape found.
@property(nonatomic, readonly) ChipmunkShape *shape;
/// The percentage between the start and end points where the collision occurred.
@property(nonatomic, readonly) cpFloat t;
/// The normal of the collision with the shape.
@property(nonatomic, readonly) cpVect normal;
/// The point of the collision in absolute (world) coordinates.
@property(nonatomic, readonly) cpVect point;
/// The distance from the start point where the collision occurred.
@property(nonatomic, readonly) cpFloat dist;
/// The start point.
@property(nonatomic, readonly) cpVect start;
/// The end point.
@property(nonatomic, readonly) cpVect end;
@end
/// Holds collision information from segment queries. You should never need to create one.
@interface ChipmunkShapeQueryInfo : NSObject {
@private
ChipmunkShape *_shape;
cpContactPointSet _contactPoints;
}
- (id)initWithShape:(ChipmunkShape *)shape andPoints:(cpContactPointSet *)set;
@property(nonatomic, readonly) ChipmunkShape *shape;
@property(nonatomic, readonly) cpContactPointSet *contactPoints;
@end
/// A perfect circle shape.
@interface ChipmunkCircleShape : ChipmunkShape
/// Create an autoreleased circle shape with the given radius and offset from the center of gravity.
+ (id)circleWithBody:(ChipmunkBody *)body radius:(cpFloat)radius offset:(cpVect)offset;
/// Initialize a circle shape with the given radius and offset from the center of gravity.
- (id)initWithBody:(ChipmunkBody *)body radius:(cpFloat)radius offset:(cpVect)offset;
/// The radius of the circle.
@property(nonatomic, readonly) cpFloat radius;
/// The offset from the center of gravity.
@property(nonatomic, readonly) cpVect offset;
@end
/// A beveled (rounded) segment shape.
@interface ChipmunkSegmentShape : ChipmunkShape
/// Create an autoreleased segment shape with the given endpoints and radius.
+ (id)segmentWithBody:(ChipmunkBody *)body from:(cpVect)a to:(cpVect)b radius:(cpFloat)radius;
/// Initialize a segment shape with the given endpoints and radius.
- (id)initWithBody:(ChipmunkBody *)body from:(cpVect)a to:(cpVect)b radius:(cpFloat)radius;
/// Let Chipmunk know about the geometry of adjacent segments to avoid colliding with endcaps.
- (void)setPrevNeighbor:(cpVect)prev nextNeighbor:(cpVect)next;
/// The start of the segment shape.
@property(nonatomic, readonly) cpVect a;
/// The end of the segment shape.
@property(nonatomic, readonly) cpVect b;
/// The normal of the segment shape.
@property(nonatomic, readonly) cpVect normal;
/// The beveling radius of the segment shape.
@property(nonatomic, readonly) cpFloat radius;
@end
/// A convex polygon shape.
@interface ChipmunkPolyShape : ChipmunkShape
/// Create an autoreleased polygon shape from the given vertexes after applying the transform and with the given rounding radius.
+ (id)polyWithBody:(ChipmunkBody *)body count:(int)count verts:(const cpVect *)verts transform:(cpTransform)transform radius:(cpFloat)radius;
/// Create an autoreleased box shape centered on the center of gravity.
+ (id)boxWithBody:(ChipmunkBody *)body width:(cpFloat)width height:(cpFloat)height radius:(cpFloat)radius;
/// Create an autoreleased box shape with the given bounding box in body local coordinates and rounding radius.
+ (id)boxWithBody:(ChipmunkBody *)body bb:(cpBB)bb radius:(cpFloat)radius;
/// Initialize a polygon shape from the given vertexes after applying the transform and with the given rounding radius.
- (id)initWithBody:(ChipmunkBody *)body count:(int)count verts:(const cpVect *)verts transform:(cpTransform)transform radius:(cpFloat)radius;
/// Initialize a box shape centered on the center of gravity.
- (id)initBoxWithBody:(ChipmunkBody *)body width:(cpFloat)width height:(cpFloat)height radius:(cpFloat)radius;
/// Initialize a box shape with the given bounding box in body local coordinates and rounding radius.
- (id)initBoxWithBody:(ChipmunkBody *)body bb:(cpBB)bb radius:(cpFloat)radius;
/// The number of vertexes in this polygon.
@property(nonatomic, readonly) int count;
/// Get the rounding radius of the polygon.
@property(nonatomic, readonly) cpFloat radius;
/// Access the vertexes of this polygon.
- (cpVect)getVertex:(int)index;
@end

View File

@ -1,293 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 spaces are simulation containers. You add a bunch of physics objects to a space (rigid bodies, collision shapes, and joints) and step the entire space forward through time as a whole.
If you have Chipmunk Pro, you'll want to use the ChipmunkHastySpace subclass instead as it has iPhone specific optimizations.
Unfortunately because of how Objective-C code is linked I can't dynamically substitute a ChipmunkHastySpace from a static library.
*/
@interface ChipmunkSpace : NSObject {
@protected
struct cpSpace *_space;
ChipmunkBody *_staticBody;
NSMutableSet *_children;
NSMutableArray *_handlers;
id _userData;
}
/**
The iteration count is how many solver passes the space should use when solving collisions and joints (default is 10).
Fewer iterations mean less CPU usage, but lower quality (mushy looking) physics.
*/
@property(nonatomic, assign) int iterations;
/// Global gravity value to use for all rigid bodies in this space (default value is @c cpvzero).
@property(nonatomic, assign) cpVect gravity;
/**
Global viscous damping value to use for all rigid bodies in this space (default value is 1.0 which disables damping).
This value is the fraction of velocity a body should have after 1 second.
A value of 0.9 would mean that each second, a body would have 80% of the velocity it had the previous second.
*/
@property(nonatomic, assign) cpFloat damping;
/// If a body is moving slower than this speed, it is considered idle. The default value is 0, which signals that the space should guess a good value based on the current gravity.
@property(nonatomic, assign) cpFloat idleSpeedThreshold;
/**
Elapsed time before a group of idle bodies is put to sleep (defaults to infinity which disables sleeping).
If an entire group of touching or jointed bodies has been idle for at least this long, the space will put all of the bodies into a sleeping state where they consume very little CPU.
*/
@property(nonatomic, assign) cpFloat sleepTimeThreshold;
/**
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.
*/
@property(nonatomic, assign) cpFloat collisionSlop;
/**
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.
*/
@property(nonatomic, assign) cpFloat collisionBias;
/**
Number of frames that contact information should persist.
Defaults to 3. There is probably never a reason to change this value.
*/
@property(nonatomic, assign) cpTimestamp collisionPersistence;
/// Returns a pointer to the underlying cpSpace C struct
@property(nonatomic, readonly) cpSpace *space;
/**
The space's designated static body.
Collision shapes added to the body will automatically be marked as static shapes, and rigid bodies that come to rest while touching or jointed to this body will fall asleep.
*/
@property(nonatomic, readonly) ChipmunkBody *staticBody;
/**
Retrieves the current (if you are in a callback from [ChipmunkSpace step:]) or most recent (outside of a [ChipmunkSpace step:] call) timestep.
*/
@property(nonatomic, readonly) cpFloat currentTimeStep;
/**
Returns true if the space is currently executing a timestep.
*/
@property(nonatomic, readonly) BOOL locked;
/**
An object that this space is associated with. You can use this get a reference to your game state or controller object from within callbacks.
@attention Like most @c delegate properties this is a weak reference and does not call @c retain. This prevents reference cycles from occuring.
*/
@property(nonatomic, assign) id userData;
/// Get the ChipmunkSpace object associciated with a cpSpace pointer.
/// Undefined if the cpSpace wasn't created using Objective-Chipmunk.
+(ChipmunkSpace *)spaceFromCPSpace:(cpSpace *)space;
/**
Set the default collision handler.
The default handler is used for all collisions when a specific collision handler cannot be found.
The expected method selectors are as follows:
@code
- (bool)begin:(cpArbiter *)arbiter space:(ChipmunkSpace*)space
- (bool)preSolve:(cpArbiter *)arbiter space:(ChipmunkSpace*)space
- (void)postSolve:(cpArbiter *)arbiter space:(ChipmunkSpace*)space
- (void)separate:(cpArbiter *)arbiter space:(ChipmunkSpace*)space
@endcode
*/
- (void)setDefaultCollisionHandler:(id)delegate
begin:(SEL)begin
preSolve:(SEL)preSolve
postSolve:(SEL)postSolve
separate:(SEL)separate;
/**
Set a collision handler to handle specific collision types.
The methods are called only when shapes with the specified collisionTypes collide.
@c typeA and @c typeB should be the same object references set to ChipmunkShape.collisionType. They can be any uniquely identifying object.
Class and global NSString objects work well as collision types as they are easy to get a reference to and do not require you to allocate any objects.
The expected method selectors are as follows:
@code
- (bool)begin:(cpArbiter *)arbiter space:(ChipmunkSpace*)space
- (bool)preSolve:(cpArbiter *)arbiter space:(ChipmunkSpace*)space
- (void)postSolve:(cpArbiter *)arbiter space:(ChipmunkSpace*)space
- (void)separate:(cpArbiter *)arbiter space:(ChipmunkSpace*)space
@endcode
*/
- (void)addCollisionHandler:(id)delegate
typeA:(cpCollisionType)a typeB:(cpCollisionType)b
begin:(SEL)begin
preSolve:(SEL)preSolve
postSolve:(SEL)postSolve
separate:(SEL)separate;
/**
Add an object to the space.
This can be any object that implements the ChipmunkObject protocol.
This includes all the basic types such as ChipmunkBody, ChipmunkShape and ChipmunkConstraint as well as any composite game objects you may define that implement the protocol.
@warning This method may not be called from a collision handler callback. See smartAdd: or ChipmunkSpace.addPostStepCallback:selector:context: for information on how to do that.
*/
-(id)add:(NSObject<ChipmunkObject> *)obj;
/**
Remove an object from the space.
This can be any object that implements the ChipmunkObject protocol.
This includes all the basic types such as ChipmunkBody, ChipmunkShape and ChipmunkConstraint as well as any composite game objects you may define that implement the protocol.
@warning This method may not be called from a collision handler callback. See smartRemove: or ChipmunkSpace.addPostStepCallback:selector:context: for information on how to do that.
*/
-(id)remove:(NSObject<ChipmunkObject> *)obj;
/// Check if a space already contains a particular object:
-(BOOL)contains:(NSObject<ChipmunkObject> *)obj;
/// If the space is locked and it's unsafe to call add: it will call addPostStepAddition: instead.
- (id)smartAdd:(NSObject<ChipmunkObject> *)obj;
/// If the space is locked and it's unsafe to call remove: it will call addPostStepRemoval: instead.
- (id)smartRemove:(NSObject<ChipmunkObject> *)obj;
/// Handy utility method to add a border of collision segments around a box. See ChipmunkShape for more information on the other parameters.
/// Returns an NSArray of the shapes. Since NSArray implements the ChipmunkObject protocol, you can use the [ChipmunkSpace remove:] method to remove the bounds.
- (NSArray *)addBounds:(cpBB)bounds thickness:(cpFloat)radius
elasticity:(cpFloat)elasticity friction:(cpFloat)friction
filter:(cpShapeFilter)filter collisionType:(id)collisionType;
/**
Define a callback to be run just before [ChipmunkSpace step:] finishes.
The main reason you want to define post-step callbacks is to get around the restriction that you cannot call the add/remove methods from a collision handler callback.
Post-step callbacks run right before the next (or current) call to ChipmunkSpace.step: returns when it is safe to add and remove objects.
You can only schedule one post-step callback per key value, this prevents you from accidentally removing an object twice. Registering a second callback for the same key is a no-op.
The method signature of the method should be:
@code
- (void)postStepCallback:(id)key</code></pre>
@endcode
This makes it easy to call a removal method on your game controller to remove a game object that died or was destroyed as the result of a collision:
@code
[space addPostStepCallback:gameController selector:@selector(remove:) key:gameObject];
@endcode
@attention Not to be confused with post-solve collision handler callbacks.
@warning @c target and @c object cannot be retained by the ChipmunkSpace. If you need to release either after registering the callback, use autorelease to ensure that they won't be deallocated until after [ChipmunkSpace step:] returns.
@see ChipmunkSpace.addPostStepRemoval:
*/
- (BOOL)addPostStepCallback:(id)target selector:(SEL)selector key:(id)key;
/// Block type used with [ChipmunkSpace addPostStepBlock:]
typedef void (^ChipmunkPostStepBlock)(void);
/// Same as [ChipmunkSpace addPostStepCallback:] but with a block. The block is copied.
- (BOOL)addPostStepBlock:(ChipmunkPostStepBlock)block key:(id)key;
/// Add the Chipmunk Object to the space at the end of the step.
- (void)addPostStepAddition:(NSObject<ChipmunkObject> *)obj;
/// Remove the Chipmunk Object from the space at the end of the step.
- (void)addPostStepRemoval:(NSObject<ChipmunkObject> *)obj;
/// Return an array of ChipmunkNearestPointQueryInfo objects for shapes within @c maxDistance of @c point.
/// The point is treated as having the given group and layers.
- (NSArray *)pointQueryAll:(cpVect)point maxDistance:(cpFloat)maxDistance filter:(cpShapeFilter)filter;
/// Find the closest shape to a point that is within @c maxDistance of @c point.
/// The point is treated as having the given layers and group.
- (ChipmunkPointQueryInfo *)pointQueryNearest:(cpVect)point maxDistance:(cpFloat)maxDistance filter:(cpShapeFilter)filter;
/// Return a NSArray of ChipmunkSegmentQueryInfo objects for all the shapes that overlap the segment. The objects are unsorted.
- (NSArray *)segmentQueryAllFrom:(cpVect)start to:(cpVect)end radius:(cpFloat)radius filter:(cpShapeFilter)filter;
/// Returns the first shape that overlaps the given segment. The segment is treated as having the given group and layers.
- (ChipmunkSegmentQueryInfo *)segmentQueryFirstFrom:(cpVect)start to:(cpVect)end radius:(cpFloat)radius filter:(cpShapeFilter)filter;
/// Returns a NSArray of all shapes whose bounding boxes overlap the given bounding box. The box is treated as having the given group and layers.
- (NSArray *)bbQueryAll:(cpBB)bb filter:(cpShapeFilter)filter;
/// Returns a NSArray of ChipmunkShapeQueryInfo objects for all the shapes that overlap @c shape.
- (NSArray *)shapeQueryAll:(ChipmunkShape *)shape;
/// Returns true if the shape overlaps anything in the space.
- (BOOL)shapeTest:(ChipmunkShape *)shape;
/// Get a copy of the list of all the bodies in the space.
- (NSArray *)bodies;
/// Get a copy of the list of all the shapes in the space
- (NSArray *)shapes;
/// Get a copy of the list of all the constraints in the space
- (NSArray *)constraints;
/// Update all the static shapes.
- (void)reindexStatic;
/// Update the collision info for a single shape.
/// Can be used to update individual static shapes that were moved or active shapes that were moved that you want to query against.
- (void)reindexShape:(ChipmunkShape *)shape;
/// Update the collision info for all shapes attached to a body.
- (void)reindexShapesForBody:(ChipmunkBody *)body;
/// Step time forward. While variable timesteps may be used, a constant timestep will allow you to reduce CPU usage by using fewer iterations.
- (void)step:(cpFloat)dt;
@end
//MARK: Misc
/**
A macro that defines and initializes shape variables for you in a collision callback.
They are initialized in the order that they were defined in the collision handler associated with the arbiter.
If you defined the handler as:
@code
[space addCollisionHandler:target typeA:foo typeB:bar ...]
@endcode
You you will find that @code a->collision_type == 1 @endcode and @code b->collision_type == 2 @endcode.
*/
#define CHIPMUNK_ARBITER_GET_SHAPES(__arb__, __a__, __b__) ChipmunkShape *__a__, *__b__; { \
cpShape *__shapeA__, *__shapeB__; \
cpArbiterGetShapes(__arb__, &__shapeA__, &__shapeB__); \
__a__ = cpShapeGetUserData(__shapeA__); __b__ = cpShapeGetUserData(__shapeB__); \
}
#define CHIPMUNK_ARBITER_GET_BODIES(__arb__, __a__, __b__) ChipmunkBody *__a__, *__b__; { \
cpBody *__bodyA__, *__bodyB__; \
cpArbiterGetBodies(__arb__, &__bodyA__, &__bodyB__); \
__a__ = cpBodyGetUserData(__bodyA__); __b__ = cpBodyGetUserData(__bodyB__); \
}

View File

@ -1,77 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 <Foundation/Foundation.h>
#if __has_feature(objc_arc)
#define CP_DATA_POINTER_TYPE __unsafe_unretained id
#define CP_GROUP_TYPE __unsafe_unretained id
#define CP_COLLISION_TYPE_TYPE __unsafe_unretained id
#else
#define CP_DATA_POINTER_TYPE id
#define CP_GROUP_TYPE id
#define CP_COLLISION_TYPE_TYPE id
#endif
#ifdef CP_ALLOW_PRIVATE_ACCESS
#undef CP_ALLOW_PRIVATE_ACCESS
#import "chipmunk/chipmunk_private.h"
#else
#import "chipmunk/chipmunk.h"
#endif
/**
Allows you to add composite objects to a space in a single method call.
The easiest way to implement the ChipmunkObject protocol is to add a @c chipmunkObjects instance variable with a type of @c NSArray* to your class,
create a synthesized property for it, and initialize it with the ChipmunkObjectFlatten() function.
*/
@protocol ChipmunkObject
/// Returns a list of ChipmunkBaseObject objects.
- (id <NSFastEnumeration>)chipmunkObjects;
@end
/// A category to have NSArray implement the ChipmunkObject protocol.
/// They make for very easy containers.
@interface NSArray(ChipmunkObject) <ChipmunkObject>
@end
@class ChipmunkSpace;
/**
This protocol is implemented by objects that know how to add themselves to a space.
It's used internally as part of the ChipmunkObject protocol. You should never need to implement it yourself.
*/
@protocol ChipmunkBaseObject <ChipmunkObject>
- (void)addToSpace:(ChipmunkSpace *)space;
- (void)removeFromSpace:(ChipmunkSpace *)space;
@end
#import "ChipmunkBody.h"
#import "ChipmunkShape.h"
#import "ChipmunkConstraint.h"
#import "ChipmunkSpace.h"
#import "ChipmunkMultiGrab.h"

View File

@ -1,206 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 CP_ALLOW_PRIVATE_ACCESS 1
#import "ObjectiveChipmunk/ObjectiveChipmunk.h"
@interface ChipmunkSpace(DoubleDispatch)
- (ChipmunkBody *)addBody:(ChipmunkBody *)obj;
- (ChipmunkBody *)removeBody:(ChipmunkBody *)obj;
@end
@implementation ChipmunkBody {
cpBody _body;
id _userData;
}
// MARK: Integration Helpers
-(void)updateVelocity:(cpFloat)dt gravity:(cpVect)gravity damping:(cpFloat)damping
{
cpBodyUpdateVelocity(&_body, gravity, damping, dt);
}
-(void)updatePosition:(cpFloat)dt
{
cpBodyUpdatePosition(&_body, dt);
}
static void
VelocityFunction
(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt)
{
[(ChipmunkBody *)body->userData updateVelocity:dt gravity:gravity damping:damping];
}
static void
PositionFunction
(cpBody *body, cpFloat dt)
{
[(ChipmunkBody *)body->userData updatePosition:dt];
}
// Check if the method was overridden.
// No reason to add the extra method overhead if it's not needed.
-(BOOL)methodIsOverriden:(SEL)selector
{
return ([self methodForSelector:selector] != [[ChipmunkBody class] instanceMethodForSelector:selector]);
}
// MARK: Constructors
+(ChipmunkBody *)bodyFromCPBody:(cpBody *)body
{
ChipmunkBody *obj = body->userData;
cpAssertHard([obj isKindOfClass:[ChipmunkBody class]], "'body->data' is not a pointer to a ChipmunkBody object.");
return obj;
}
+ (id)bodyWithMass:(cpFloat)mass andMoment:(cpFloat)moment
{
return [[[self alloc] initWithMass:mass andMoment:moment] autorelease];
}
+ (id)staticBody
{
ChipmunkBody *body = [[self alloc] initWithMass:0.0f andMoment:0.0f];
body.type = CP_BODY_TYPE_STATIC;
return [body autorelease];
}
+ (id)kinematicBody
{
ChipmunkBody *body = [[self alloc] initWithMass:0.0f andMoment:0.0f];
body.type = CP_BODY_TYPE_KINEMATIC;
return [body autorelease];
}
- (id)initWithMass:(cpFloat)mass andMoment:(cpFloat)moment
{
if((self = [super init])){
cpBodyInit(&_body, mass, moment);
_body.userData = self;
// Setup integration callbacks if necessary.
if([self methodIsOverriden:@selector(updateVelocity:gravity:damping:)]){
_body.velocity_func = VelocityFunction;
}
if([self methodIsOverriden:@selector(updatePosition:)]){
_body.position_func = PositionFunction;
}
}
return self;
}
- (void) dealloc
{
cpBodyDestroy(&_body);
[super dealloc];
}
- (cpTransform)transform {return _body.transform;}
- (cpBody *)body {return &_body;}
@synthesize userData = _userData;
// accessor macros
#define getter(type, lower, upper) \
- (type)lower {return cpBodyGet##upper(&_body);}
#define setter(type, lower, upper) \
- (void)set##upper:(type)value {cpBodySet##upper(&_body, value);};
#define both(type, lower, upper) \
getter(type, lower, upper) \
setter(type, lower, upper)
both(cpBodyType, type, Type)
both(cpFloat, mass, Mass)
both(cpFloat, moment, Moment)
both(cpVect, centerOfGravity, CenterOfGravity)
both(cpVect, position, Position)
both(cpVect, velocity, Velocity)
both(cpVect, force, Force)
both(cpFloat, angle, Angle)
both(cpFloat, angularVelocity, AngularVelocity)
both(cpFloat, torque, Torque)
-(ChipmunkSpace *)space {
cpSpace *space = cpBodyGetSpace(&_body);
return (ChipmunkSpace *)(space ? cpSpaceGetUserData(space) : nil);
}
- (cpFloat)kineticEnergy {return cpBodyKineticEnergy(&_body);}
- (cpVect)localToWorld:(cpVect)v {return cpBodyLocalToWorld(&_body, v);}
- (cpVect)worldToLocal:(cpVect)v {return cpBodyWorldToLocal(&_body, v);}
- (cpVect)velocityAtLocalPoint:(cpVect)p {return cpBodyGetVelocityAtLocalPoint(&_body, p);}
- (cpVect)velocityAtWorldPoint:(cpVect)p {return cpBodyGetVelocityAtWorldPoint(&_body, p);}
- (void)applyForce:(cpVect)force atLocalPoint:(cpVect)point {cpBodyApplyForceAtLocalPoint(&_body, force, point);}
- (void)applyForce:(cpVect)force atWorldPoint:(cpVect)point {cpBodyApplyForceAtWorldPoint(&_body, force, point);}
- (void)applyImpulse:(cpVect)impulse atLocalPoint:(cpVect)point {cpBodyApplyImpulseAtLocalPoint(&_body, impulse, point);}
- (void)applyImpulse:(cpVect)impulse atWorldPoint:(cpVect)point {cpBodyApplyImpulseAtWorldPoint(&_body, impulse, point);}
- (bool)isSleeping {return cpBodyIsSleeping(&_body);}
- (void)activate {cpBodyActivate(&_body);}
- (void)activateStatic:(ChipmunkShape *)filter {cpBodyActivateStatic(&_body, filter.shape);}
- (void)sleepWithGroup:(ChipmunkBody *)group {cpBodySleepWithGroup(&_body, group.body);}
- (void)sleep {cpBodySleep(&_body);}
- (NSArray *)chipmunkObjects {return [NSArray arrayWithObject:self];}
- (void)addToSpace:(ChipmunkSpace *)space {[space addBody:self];}
- (void)removeFromSpace:(ChipmunkSpace *)space {[space removeBody:self];}
static void PushShape(cpBody *ignored, cpShape *shape, NSMutableArray *arr){[arr addObject:shape->userData];}
- (NSArray *)shapes
{
NSMutableArray *arr = [NSMutableArray array];
cpBodyEachShape(&_body, (cpBodyShapeIteratorFunc)PushShape, arr);
return arr;
}
static void PushConstraint(cpBody *ignored, cpConstraint *constraint, NSMutableArray *arr){[arr addObject:constraint->userData];}
- (NSArray *)constraints
{
NSMutableArray *arr = [NSMutableArray array];
cpBodyEachConstraint(&_body, (cpBodyConstraintIteratorFunc)PushConstraint, arr);
return arr;
}
static void CallArbiterBlock(cpBody *body, cpArbiter *arbiter, ChipmunkBodyArbiterIteratorBlock block){block(arbiter);}
- (void)eachArbiter:(ChipmunkBodyArbiterIteratorBlock)block
{
cpBodyEachArbiter(&_body, (cpBodyArbiterIteratorFunc)CallArbiterBlock, block);
}
@end

View File

@ -1,482 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 CP_ALLOW_PRIVATE_ACCESS 1
#import "ObjectiveChipmunk/ObjectiveChipmunk.h"
@interface ChipmunkSpace(DoubleDispatch)
- (ChipmunkConstraint *)addConstraint:(ChipmunkConstraint *)obj;
- (ChipmunkConstraint *)removeConstraint:(ChipmunkConstraint *)obj;
@end
@implementation ChipmunkConstraint
@synthesize userData = _userData;
+(ChipmunkConstraint *)constraintFromCPConstraint:(cpConstraint *)constraint
{
ChipmunkConstraint *obj = constraint->userData;
cpAssertHard([obj isKindOfClass:[ChipmunkConstraint class]], "'constraint->data' is not a pointer to a ChipmunkConstraint object.");
return obj;
}
- (void) dealloc
{
cpConstraint *constraint = self.constraint;
[self.bodyA release];
[self.bodyB release];
cpConstraintDestroy(constraint);
[super dealloc];
}
- (cpConstraint *)constraint
{
[self doesNotRecognizeSelector:_cmd];
return nil;
}
// accessor macros
#define getter(type, lower, upper) \
- (type)lower {return cpConstraintGet##upper(self.constraint);}
#define setter(type, lower, upper) \
- (void)set##upper:(type)value {cpConstraintSet##upper(self.constraint, value);};
#define both(type, lower, upper) \
getter(type, lower, upper) \
setter(type, lower, upper)
both(cpFloat, maxForce, MaxForce)
both(cpFloat, errorBias, ErrorBias)
both(cpFloat, maxBias, MaxBias)
both(BOOL, collideBodies, CollideBodies)
-(cpFloat)impulse {return cpConstraintGetImpulse(self.constraint);}
-(ChipmunkSpace *)space {
cpSpace *space = cpConstraintGetSpace(self.constraint);
return (ChipmunkSpace *)(space ? cpSpaceGetUserData(space) : nil);
}
- (ChipmunkBody *)bodyA
{
cpBody *body = cpConstraintGetBodyA(self.constraint);
return (body ? cpBodyGetUserData(body) : nil);
}
//- (void)setBodyA:(ChipmunkBody *)value {
// if(self.bodyA != value){
// [self.bodyA release];
// self.constraint->a = [[value retain] body];
// }
//}
- (ChipmunkBody *)bodyB
{
cpBody *body = cpConstraintGetBodyB(self.constraint);
return (body ? cpBodyGetUserData(body) : nil);
}
//- (void)setBodyB:(ChipmunkBody *)value {
// if(self.bodyB != value){
// [self.bodyB release];
// self.constraint->b = [[value retain] body];
// }
//}
- (NSArray *)chipmunkObjects {return [NSArray arrayWithObject:self];}
- (void)addToSpace:(ChipmunkSpace *)space {[space addConstraint:self];}
- (void)removeFromSpace:(ChipmunkSpace *)space {[space removeConstraint:self];}
-(void)preSolve:(ChipmunkSpace *)space {}
-(void)postSolve:(ChipmunkSpace *)space {}
// MARK: Callbacks
static void
PreSolve(cpConstraint *constraint, cpSpace *space)
{
[(ChipmunkConstraint *)constraint->userData preSolve:(ChipmunkSpace *)space->userData];
}
static void
PostSolve(cpConstraint *constraint, cpSpace *space)
{
[(ChipmunkConstraint *)constraint->userData postSolve:(ChipmunkSpace *)space->userData];
}
// Check if the method was overridden.
// No reason to add the extra method overhead if it's not needed.
-(BOOL)methodIsOverriden:(SEL)selector
{
return ([self methodForSelector:selector] != [[ChipmunkConstraint class] instanceMethodForSelector:selector]);
}
-(void)setupCallbacks
{
if([self methodIsOverriden:@selector(preSolve:)]){
cpConstraintSetPreSolveFunc(self.constraint, PreSolve);
}
if([self methodIsOverriden:@selector(postSolve:)]){
cpConstraintSetPostSolveFunc(self.constraint, PostSolve);
}
}
@end
// accessor macros
#define getter2(type, struct, lower, upper) \
- (type)lower {return struct##Get##upper((cpConstraint *)&_constraint);}
#define setter2(type, struct, lower, upper) \
- (void)set##upper:(type)value {struct##Set##upper((cpConstraint *)&_constraint, value);};
#define both2(type, struct, lower, upper) \
getter2(type, struct, lower, upper) \
setter2(type, struct, lower, upper)
@implementation ChipmunkPinJoint {
cpPinJoint _constraint;
}
+ (id)pinJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB
{
return [[[self alloc] initWithBodyA:a bodyB:b anchorA:anchorA anchorB:anchorB] autorelease];
}
- (cpConstraint *)constraint {return (cpConstraint *)&_constraint;}
- (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB
{
if((self = [super init])){
[a retain];
[b retain];
cpPinJointInit(&_constraint, a.body, b.body, anchorA, anchorB);
self.constraint->userData = self;
[self setupCallbacks];
}
return self;
}
both2(cpVect, cpPinJoint, anchorA, AnchorA)
both2(cpVect, cpPinJoint, anchorB, AnchorB)
both2(cpFloat, cpPinJoint, dist, Dist)
@end
@implementation ChipmunkSlideJoint {
cpSlideJoint _constraint;
}
+ (id)slideJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB min:(cpFloat)min max:(cpFloat)max
{
return [[[self alloc] initWithBodyA:a bodyB:b anchorA:anchorA anchorB:anchorB min:min max:max] autorelease];
}
- (cpConstraint *)constraint {return (cpConstraint *)&_constraint;}
- (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB min:(cpFloat)min max:(cpFloat)max
{
if((self = [super init])){
[a retain];
[b retain];
cpSlideJointInit(&_constraint, a.body, b.body, anchorA, anchorB, min, max);
self.constraint->userData = self;
[self setupCallbacks];
}
return self;
}
both2(cpVect, cpSlideJoint, anchorA, AnchorA)
both2(cpVect, cpSlideJoint, anchorB, AnchorB)
both2(cpFloat, cpSlideJoint, min, Min)
both2(cpFloat, cpSlideJoint, max, Max)
@end
@implementation ChipmunkPivotJoint {
cpPivotJoint _constraint;
}
+ (id)pivotJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB
{
return [[[self alloc] initWithBodyA:a bodyB:b anchorA:anchorA anchorB:anchorB] autorelease];
}
+ (id)pivotJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b pivot:(cpVect)pivot
{
return [[[self alloc] initWithBodyA:a bodyB:b pivot:pivot] autorelease];
}
- (cpConstraint *)constraint {return (cpConstraint *)&_constraint;}
- (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB
{
if((self = [super init])){
[a retain];
[b retain];
cpPivotJointInit(&_constraint, a.body, b.body, anchorA, anchorB);
self.constraint->userData = self;
[self setupCallbacks];
}
return self;
}
- (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b pivot:(cpVect)pivot
{
return [self initWithBodyA:a bodyB:b anchorA:[a worldToLocal:pivot] anchorB:[b worldToLocal:pivot]];
}
both2(cpVect, cpPivotJoint, anchorA, AnchorA)
both2(cpVect, cpPivotJoint, anchorB, AnchorB)
@end
@implementation ChipmunkGrooveJoint {
cpGrooveJoint _constraint;
}
+ (id)grooveJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b grooveA:(cpVect)grooveA grooveB:(cpVect)grooveB anchorB:(cpVect)anchorB
{
return [[[self alloc] initWithBodyA:a bodyB:b grooveA:grooveA grooveB:grooveB anchorB:anchorB] autorelease];
}
- (cpConstraint *)constraint {return (cpConstraint *)&_constraint;}
- (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b grooveA:(cpVect)grooveA grooveB:(cpVect)grooveB anchorB:(cpVect)anchorB
{
if((self = [super init])){
[a retain];
[b retain];
cpGrooveJointInit(&_constraint, a.body, b.body, grooveA, grooveB, anchorB);
self.constraint->userData = self;
[self setupCallbacks];
}
return self;
}
both2(cpVect, cpGrooveJoint, grooveA, GrooveA)
both2(cpVect, cpGrooveJoint, grooveB, GrooveB)
both2(cpVect, cpGrooveJoint, anchorB, AnchorB)
@end
@implementation ChipmunkDampedSpring {
cpDampedSpring _constraint;
}
+ (id)dampedSpringWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB restLength:(cpFloat)restLength stiffness:(cpFloat)stiffness damping:(cpFloat)damping
{
return [[[self alloc] initWithBodyA:a bodyB:b anchorA:anchorA anchorB:anchorB restLength:restLength stiffness:stiffness damping:damping] autorelease];
}
- (cpConstraint *)constraint {return (cpConstraint *)&_constraint;}
- (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b anchorA:(cpVect)anchorA anchorB:(cpVect)anchorB restLength:(cpFloat)restLength stiffness:(cpFloat)stiffness damping:(cpFloat)damping
{
if((self = [super init])){
[a retain];
[b retain];
cpDampedSpringInit(&_constraint, a.body, b.body, anchorA, anchorB, restLength, stiffness, damping);
self.constraint->userData = self;
[self setupCallbacks];
}
return self;
}
both2(cpVect, cpDampedSpring, anchorA, AnchorA)
both2(cpVect, cpDampedSpring, anchorB, AnchorB)
both2(cpFloat, cpDampedSpring, restLength, RestLength)
both2(cpFloat, cpDampedSpring, stiffness, Stiffness)
both2(cpFloat, cpDampedSpring, damping, Damping)
@end
@implementation ChipmunkDampedRotarySpring {
cpDampedRotarySpring _constraint;
}
+ (id)dampedRotarySpringWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b restAngle:(cpFloat)restAngle stiffness:(cpFloat)stiffness damping:(cpFloat)damping
{
return [[[self alloc] initWithBodyA:a bodyB:b restAngle:restAngle stiffness:stiffness damping:damping] autorelease];
}
- (cpConstraint *)constraint {return (cpConstraint *)&_constraint;}
- (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b restAngle:(cpFloat)restAngle stiffness:(cpFloat)stiffness damping:(cpFloat)damping
{
if((self = [super init])){
[a retain];
[b retain];
cpDampedRotarySpringInit(&_constraint, a.body, b.body, restAngle, stiffness, damping);
self.constraint->userData = self;
[self setupCallbacks];
}
return self;
}
both2(cpFloat, cpDampedRotarySpring, restAngle, RestAngle)
both2(cpFloat, cpDampedRotarySpring, stiffness, Stiffness)
both2(cpFloat, cpDampedRotarySpring, damping, Damping)
@end
@implementation ChipmunkRotaryLimitJoint {
cpRotaryLimitJoint _constraint;
}
+ (id)rotaryLimitJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b min:(cpFloat)min max:(cpFloat)max
{
return [[[self alloc] initWithBodyA:a bodyB:b min:min max:max] autorelease];
}
- (cpConstraint *)constraint {return (cpConstraint *)&_constraint;}
- (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b min:(cpFloat)min max:(cpFloat)max
{
if((self = [super init])){
[a retain];
[b retain];
cpRotaryLimitJointInit(&_constraint, a.body, b.body, min, max);
self.constraint->userData = self;
[self setupCallbacks];
}
return self;
}
both2(cpFloat, cpRotaryLimitJoint, min, Min)
both2(cpFloat, cpRotaryLimitJoint, max, Max)
@end
@implementation ChipmunkSimpleMotor {
cpSimpleMotor _constraint;
}
+ (id)simpleMotorWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b rate:(cpFloat)rate
{
return [[[self alloc] initWithBodyA:a bodyB:b rate:rate] autorelease];
}
- (cpConstraint *)constraint {return (cpConstraint *)&_constraint;}
- (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b rate:(cpFloat)rate
{
if((self = [super init])){
[a retain];
[b retain];
cpSimpleMotorInit(&_constraint, a.body, b.body, rate);
self.constraint->userData = self;
[self setupCallbacks];
}
return self;
}
both2(cpFloat, cpSimpleMotor, rate, Rate)
@end
@implementation ChipmunkGearJoint {
cpGearJoint _constraint;
}
+ (id)gearJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b phase:(cpFloat)phase ratio:(cpFloat)ratio
{
return [[[self alloc] initWithBodyA:a bodyB:b phase:phase ratio:ratio] autorelease];
}
- (cpConstraint *)constraint {return (cpConstraint *)&_constraint;}
- (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b phase:(cpFloat)phase ratio:(cpFloat)ratio
{
if((self = [super init])){
[a retain];
[b retain];
cpGearJointInit(&_constraint, a.body, b.body, phase, ratio);
self.constraint->userData = self;
[self setupCallbacks];
}
return self;
}
both2(cpFloat, cpGearJoint, phase, Phase)
both2(cpFloat, cpGearJoint, ratio, Ratio)
@end
@implementation ChipmunkRatchetJoint {
cpRatchetJoint _constraint;
}
+ (id)ratchetJointWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b phase:(cpFloat)phase ratchet:(cpFloat)ratchet
{
return [[[self alloc] initWithBodyA:a bodyB:b phase:phase ratchet:ratchet] autorelease];
}
- (cpConstraint *)constraint {return (cpConstraint *)&_constraint;}
- (id)initWithBodyA:(ChipmunkBody *)a bodyB:(ChipmunkBody *)b phase:(cpFloat)phase ratchet:(cpFloat)ratchet
{
if((self = [super init])){
[a retain];
[b retain];
cpRatchetJointInit(&_constraint, a.body, b.body, phase, ratchet);
self.constraint->userData = self;
[self setupCallbacks];
}
return self;
}
both2(cpFloat, cpRatchetJoint, angle, Angle)
both2(cpFloat, cpRatchetJoint, phase, Phase)
both2(cpFloat, cpRatchetJoint, ratchet, Ratchet)
@end

View File

@ -1,264 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 "ChipmunkMultiGrab.h"
// A constraint subclass that tracks a grab point
@interface ChipmunkGrab() <ChipmunkObject>
@property(nonatomic, readwrite) cpVect pos;
@property(nonatomic, readonly) NSArray *chipmunkObjects;
@end
@implementation ChipmunkGrab
@synthesize pos = _pos;
@synthesize chipmunkObjects = _chipmunkObjects;
@synthesize grabbedShape = _grabbedShape;
@synthesize data = _data;
static void
GrabPreSolve(cpConstraint *constraint, cpSpace *space)
{
cpBody *grabBody = cpConstraintGetBodyA(constraint);
ChipmunkGrab *grab = [ChipmunkConstraint constraintFromCPConstraint:constraint].userData;
cpFloat dt = cpSpaceGetCurrentTimeStep(space);
cpFloat coef = cpfpow(grab->_smoothing, dt);
// Smooth out the mouse position.
cpVect pos = cpvlerp(grab->_pos, cpBodyGetPosition(grabBody), coef);
cpBodySetVelocity(grabBody, cpvmult(cpvsub(pos, cpBodyGetPosition(grabBody)), 1.0/dt));
// cpBodySetPosition(grabBody, pos);
}
// Body will be nil if no object was grabbed.
-(id)initWithMultiGrab:(ChipmunkMultiGrab *)multiGrab pos:(cpVect)pos nearest:(cpVect)nearest
body:(ChipmunkBody *)body grabbedShape:(ChipmunkShape *)grabbedShape
chipmunkObjects:(NSArray *)chipmunkObjects
{
ChipmunkBody *grabBody = [ChipmunkBody kinematicBody];
grabBody.position = pos;
// TODO the repeated appending is a little silly here.
chipmunkObjects = [chipmunkObjects arrayByAddingObject:grabBody];
if((self = [super init])){
_pos = pos;
_smoothing = multiGrab.smoothing;
_grabbedShape = grabbedShape;
if(body){
ChipmunkPivotJoint *pivot = [ChipmunkPivotJoint pivotJointWithBodyA:grabBody bodyB:body anchorA:cpvzero anchorB:[body worldToLocal:nearest]];
pivot.maxForce = multiGrab.grabForce;
pivot.userData = self;
cpConstraintSetPreSolveFunc(pivot.constraint, GrabPreSolve);
chipmunkObjects = [chipmunkObjects arrayByAddingObject:pivot];
if(grabbedShape){
cpFloat frictionForce = multiGrab.grabFriction;
if(frictionForce > 0.0 && (1.0/body.mass + 1.0/grabBody.mass != 0.0)){
ChipmunkPivotJoint *friction = [ChipmunkPivotJoint pivotJointWithBodyA:grabBody bodyB:body anchorA:cpvzero anchorB:[body worldToLocal:nearest]];
friction.maxForce = frictionForce;
friction.maxBias = 0.0;
chipmunkObjects = [chipmunkObjects arrayByAddingObject:friction];
}
cpFloat rotaryFriction = multiGrab.grabRotaryFriction;
if(rotaryFriction > 0.0 && (1.0/body.moment + 1.0/grabBody.moment != 0.0)){
ChipmunkGearJoint *friction = [ChipmunkGearJoint gearJointWithBodyA:grabBody bodyB:body phase:0.0 ratio:1.0];
friction.maxForce = rotaryFriction;
friction.maxBias = 0.0;
chipmunkObjects = [chipmunkObjects arrayByAddingObject:friction];
}
}
_chipmunkObjects = [chipmunkObjects retain];
}
}
return self;
}
-(void)dealloc
{
[_chipmunkObjects release]; _chipmunkObjects = nil;
[super dealloc];
}
@end
@implementation ChipmunkMultiGrab
@synthesize grabForce = _grabForce;
@synthesize smoothing = _smoothing;
@synthesize filter = _filter;
@synthesize grabFilter = _grabFilter;
@synthesize grabSort = _grabSort;
@synthesize grabFriction = _grabFriction, grabRotaryFriction = _grabRotaryFriction;
@synthesize grabRadius = _grabRadius;
@synthesize pullMode = _pullMode, pushMode = _pushMode;
@synthesize pushMass = _pushMass;
@synthesize pushFriction = _pushFriction, pushElasticity = _pushElasticity;
@synthesize pushCollisionType = _pushCollisionType;
-(id)initForSpace:(ChipmunkSpace *)space withSmoothing:(cpFloat)smoothing withGrabForce:(cpFloat)grabForce
{
if((self = [super init])){
_space = [space retain];
_grabs = [[NSMutableArray alloc] init];
_smoothing = smoothing;
_grabForce = grabForce;
_filter = CP_SHAPE_FILTER_ALL;
_grabFilter = ^(ChipmunkShape *shape){return (bool)TRUE;};
_grabSort = ^(ChipmunkShape *shape, cpFloat depth){return depth;};
_pullMode = TRUE;
_pushMode = FALSE;
}
return self;
}
-(void)dealloc
{
[_space release];
[_grabs release];
[super dealloc];
}
// Don't integrate push bodies.
static void PushBodyVelocityUpdate(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt){}
-(ChipmunkGrab *)beginLocation:(cpVect)pos
{
__block cpFloat min = INFINITY;
__block cpVect nearest = pos;
__block ChipmunkShape *grabbedShape = nil;
if(_pullMode){
cpSpacePointQuery_b(_space.space, pos, _grabRadius, _filter, ^(cpShape *c_shape, cpVect point, cpFloat dist, cpVect gradient){
ChipmunkShape *shape = [ChipmunkShape shapeFromCPShape:c_shape];
cpFloat sort = dist;
// Call the sorting callback if dist is negative.
// Otherwise just take the nearest shape.
if(dist <= 0.0f){
sort = -_grabSort(shape, -dist);
cpAssertWarn(sort <= 0.0f, "You must return a positive value from the sorting callback.");
}
if(sort < min && cpBodyGetMass(cpShapeGetBody(c_shape)) != INFINITY){
if(_grabFilter(shape)){
min = sort;
nearest = (dist > 0.0 ? point : pos);
grabbedShape = shape;
}
}
});
}
ChipmunkBody *pushBody = nil;
NSArray *chipmunkObjects = [NSArray array];
if(!grabbedShape && _pushMode){
pushBody = [ChipmunkBody bodyWithMass:_pushMass andMoment:INFINITY];
pushBody.position = pos;
cpBodySetVelocityUpdateFunc(pushBody.body, PushBodyVelocityUpdate);
ChipmunkShape *pushShape = [ChipmunkCircleShape circleWithBody:pushBody radius:_grabRadius offset:cpvzero];
pushShape.friction = _pushFriction;
pushShape.elasticity = _pushElasticity;
pushShape.filter = _filter;
pushShape.collisionType = _pushCollisionType;
chipmunkObjects = [NSArray arrayWithObjects:pushBody, pushShape, nil];
}
ChipmunkBody *grabBody = (grabbedShape ? grabbedShape.body : pushBody);
ChipmunkGrab *grab = [[ChipmunkGrab alloc] initWithMultiGrab:self pos:pos nearest:nearest body:grabBody grabbedShape:grabbedShape chipmunkObjects:chipmunkObjects];
[_grabs addObject:grab];
[_space add:grab];
[grab release];
return (grab.grabbedShape ? grab : nil);
}
static ChipmunkGrab *
BestGrab(NSArray *grabs, cpVect pos)
{
ChipmunkGrab *match = nil;
cpFloat best = INFINITY;
for(ChipmunkGrab *grab in grabs){
cpFloat dist = cpvdistsq(pos, grab.pos);
if(dist < best){
match = grab;
best = dist;
}
}
return match;
}
-(ChipmunkGrab *)updateLocation:(cpVect)pos
{
ChipmunkGrab *grab = BestGrab(_grabs, pos);
grab.pos = pos;
return (grab.grabbedShape ? grab : nil);
}
-(ChipmunkGrab *)endLocation:(cpVect)pos
{
cpAssertHard([_grabs count] != 0, "Grab set is already empty!");
ChipmunkGrab *grab = BestGrab(_grabs, pos);
[grab retain];
[_space remove:grab];
[_grabs removeObject:grab];
[grab autorelease];
return (grab.grabbedShape ? grab : nil);
}
-(NSArray *)grabs
{
NSMutableArray *grabs = [NSMutableArray array];
for(ChipmunkGrab *grab in _grabs){
if(grab.grabbedShape) [grabs addObject:grab];
}
return grabs;
}
@end

View File

@ -1,331 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 CP_ALLOW_PRIVATE_ACCESS 1
#import "ObjectiveChipmunk/ObjectiveChipmunk.h"
@interface ChipmunkSpace(DoubleDispatch)
- (ChipmunkShape *)addShape:(ChipmunkShape *)obj;
- (ChipmunkShape *)removeShape:(ChipmunkShape *)obj;
@end
@implementation ChipmunkShape
@synthesize userData = _userData;
+(ChipmunkShape *)shapeFromCPShape:(cpShape *)shape
{
ChipmunkShape *obj = shape->userData;
cpAssertHard([obj isKindOfClass:[ChipmunkShape class]], "'shape->data' is not a pointer to a ChipmunkShape object.");
return obj;
}
- (void) dealloc {
[self.body release];
cpShapeDestroy(self.shape);
[super dealloc];
}
- (cpShape *)shape {
[self doesNotRecognizeSelector:_cmd];
return nil;
}
- (ChipmunkBody *)body {
cpBody *body = cpShapeGetBody(self.shape);
return (body ? cpBodyGetUserData(body) : nil);
}
- (void)setBody:(ChipmunkBody *)body {
if(self.body != body){
[self.body release];
cpShapeSetBody(self.shape, [body retain].body);
}
}
-(cpFloat)mass {return cpShapeGetMass(self.shape);}
-(void)setMass:(cpFloat)mass {cpShapeSetMass(self.shape, mass);}
-(cpFloat)density {return cpShapeGetDensity(self.shape);}
-(void)setDensity:(cpFloat)density {cpShapeSetDensity(self.shape, density);}
-(cpFloat)moment {return cpShapeGetMoment(self.shape);}
-(cpFloat)area {return cpShapeGetArea(self.shape);}
-(cpVect)centerOfGravity {return cpShapeGetCenterOfGravity(self.shape);}
// accessor macros
#define getter(type, lower, upper) \
- (type)lower {return cpShapeGet##upper(self.shape);}
#define setter(type, lower, upper) \
- (void)set##upper:(type)value {cpShapeSet##upper(self.shape, value);};
#define both(type, lower, upper) \
getter(type, lower, upper) \
setter(type, lower, upper)
getter(cpBB, bb, BB)
both(BOOL, sensor, Sensor)
both(cpFloat, elasticity, Elasticity)
both(cpFloat, friction, Friction)
both(cpVect, surfaceVelocity, SurfaceVelocity)
both(cpCollisionType, collisionType, CollisionType)
both(cpShapeFilter, filter, Filter)
-(ChipmunkSpace *)space {
cpSpace *space = cpShapeGetSpace(self.shape);
return (ChipmunkSpace *)(space ? cpSpaceGetUserData(space) : nil);
}
- (cpBB)cacheBB {return cpShapeCacheBB(self.shape);}
- (ChipmunkPointQueryInfo *)pointQuery:(cpVect)point
{
cpPointQueryInfo info;
cpShapePointQuery(self.shape, point, &info);
return (info.shape ? [[[ChipmunkPointQueryInfo alloc] initWithInfo:&info] autorelease] : nil);
}
- (ChipmunkSegmentQueryInfo *)segmentQueryFrom:(cpVect)start to:(cpVect)end radius:(cpFloat)radius
{
cpSegmentQueryInfo info;
if(cpShapeSegmentQuery(self.shape, start, end, radius, &info)){
return [[[ChipmunkSegmentQueryInfo alloc] initWithInfo:&info start:start end:end] autorelease];
} else {
return nil;
}
}
- (NSArray *)chipmunkObjects {return [NSArray arrayWithObject:self];}
- (void)addToSpace:(ChipmunkSpace *)space {[space addShape:self];}
- (void)removeFromSpace:(ChipmunkSpace *)space {[space removeShape:self];}
@end
@implementation ChipmunkPointQueryInfo
- (id)initWithInfo:(cpPointQueryInfo *)info
{
if((self = [super init])){
_info = (*info);
[self.shape retain];
}
return self;
}
- (cpPointQueryInfo *)info {return &_info;}
- (ChipmunkShape *)shape {return (_info.shape ? _info.shape->userData : nil);}
- (cpVect)point {return _info.point;}
- (cpFloat)distance {return _info.distance;}
- (cpVect)gradient {return _info.gradient;}
- (void)dealloc
{
[self.shape release];
[super dealloc];
}
@end
@implementation ChipmunkSegmentQueryInfo
- (id)initWithInfo:(cpSegmentQueryInfo *)info start:(cpVect)start end:(cpVect)end
{
if((self = [super init])){
_info = (*info);
_start = start;
_end = end;
[self.shape retain];
}
return self;
}
- (cpSegmentQueryInfo *)info {return &_info;}
- (ChipmunkShape *)shape {return (_info.shape ? _info.shape->userData : nil);}
- (cpFloat)t {return _info.alpha;}
- (cpVect)normal {return _info.normal;}
- (cpVect)point {return _info.point;}
- (cpFloat)dist {return cpvdist(_start, _end)*_info.alpha;}
- (cpVect)start {return _start;}
- (cpVect)end {return _end;}
- (void)dealloc
{
[self.shape release];
[super dealloc];
}
@end
@implementation ChipmunkShapeQueryInfo
@synthesize shape = _shape;
- (cpContactPointSet *)contactPoints {return &_contactPoints;}
- (id)initWithShape:(ChipmunkShape *)shape andPoints:(cpContactPointSet *)set
{
if((self = [super init])){
_shape = [shape retain];
_contactPoints = *set;
}
return self;
}
- (void)dealloc {
[_shape release];
[super dealloc];
}
@end
@implementation ChipmunkCircleShape {
cpCircleShape _shape;
}
+ (ChipmunkCircleShape *)circleWithBody:(ChipmunkBody *)body radius:(cpFloat)radius offset:(cpVect)offset
{
return [[[self alloc] initWithBody:body radius:radius offset:offset] autorelease];
}
- (cpShape *)shape {return (cpShape *)&_shape;}
- (id)initWithBody:(ChipmunkBody *)body radius:(cpFloat)radius offset:(cpVect)offset {
if((self = [super init])){
[body retain];
cpCircleShapeInit(&_shape, body.body, radius, offset);
self.shape->userData = self;
}
return self;
}
- (cpFloat)radius {return cpCircleShapeGetRadius((cpShape *)&_shape);}
- (cpVect)offset {return cpCircleShapeGetOffset((cpShape *)&_shape);}
@end
@implementation ChipmunkSegmentShape {
cpSegmentShape _shape;
}
+ (ChipmunkSegmentShape *)segmentWithBody:(ChipmunkBody *)body from:(cpVect)a to:(cpVect)b radius:(cpFloat)radius
{
return [[[self alloc] initWithBody:body from:a to:b radius:radius] autorelease];
}
- (cpShape *)shape {return (cpShape *)&_shape;}
- (id)initWithBody:(ChipmunkBody *)body from:(cpVect)a to:(cpVect)b radius:(cpFloat)radius {
if((self = [super init])){
[body retain];
cpSegmentShapeInit(&_shape, body.body, a, b, radius);
self.shape->userData = self;
}
return self;
}
- (void)setPrevNeighbor:(cpVect)prev nextNeighbor:(cpVect)next
{
cpSegmentShapeSetNeighbors((cpShape *)&_shape, prev, next);
}
- (cpVect)a {return cpSegmentShapeGetA((cpShape *)&_shape);}
- (cpVect)b {return cpSegmentShapeGetB((cpShape *)&_shape);}
- (cpVect)normal {return cpSegmentShapeGetNormal((cpShape *)&_shape);}
- (cpFloat)radius {return cpSegmentShapeGetRadius((cpShape *)&_shape);}
@end
@implementation ChipmunkPolyShape {
cpPolyShape _shape;
}
+ (id)polyWithBody:(ChipmunkBody *)body count:(int)count verts:(const cpVect *)verts transform:(cpTransform)transform radius:(cpFloat)radius
{
return [[[self alloc] initWithBody:body count:count verts:verts transform:transform radius:radius] autorelease];
}
+ (id)boxWithBody:(ChipmunkBody *)body width:(cpFloat)width height:(cpFloat)height radius:(cpFloat)radius
{
return [[[self alloc] initBoxWithBody:body width:width height:height radius:radius] autorelease];
}
+ (id)boxWithBody:(ChipmunkBody *)body bb:(cpBB)bb radius:(cpFloat)radius
{
return [[[self alloc] initBoxWithBody:body bb:bb radius:radius] autorelease];
}
- (cpShape *)shape {return (cpShape *)&_shape;}
- (id)initWithBody:(ChipmunkBody *)body count:(int)count verts:(const cpVect *)verts transform:(cpTransform)transform radius:(cpFloat)radius
{
if((self = [super init])){
[body retain];
cpPolyShapeInit(&_shape, body.body, count, verts, transform, radius);
self.shape->userData = self;
}
return self;
}
- (id)initBoxWithBody:(ChipmunkBody *)body width:(cpFloat)width height:(cpFloat)height radius:(cpFloat)radius
{
if((self = [super init])){
[body retain];
cpBoxShapeInit(&_shape, body.body, width, height, radius);
self.shape->userData = self;
}
return self;
}
- (id)initBoxWithBody:(ChipmunkBody *)body bb:(cpBB)bb radius:(cpFloat)radius
{
if((self = [super init])){
[body retain];
cpBoxShapeInit2(&_shape, body.body, bb, radius);
self.shape->userData = self;
}
return self;
}
- (int)count {return cpPolyShapeGetCount((cpShape *)&_shape);}
- (cpFloat)radius {return cpPolyShapeGetRadius((cpShape *)&_shape);}
- (cpVect)getVertex:(int)index {return cpPolyShapeGetVert((cpShape *)&_shape, index);}
@end

View File

@ -1,590 +0,0 @@
/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION 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 CP_ALLOW_PRIVATE_ACCESS
#import "ObjectiveChipmunk/ObjectiveChipmunk.h"
#import <objc/message.h>
#import <TargetConditionals.h>
#ifdef CHIPMUNK_PRO_TRIAL
#if TARGET_OS_IPHONE == 1
#import <UIKit/UIKit.h>
#else
#import <AppKit/AppKit.h>
#endif
#endif
// Just in case the user doesn't have -ObjC in their linker flags.
// Annoyingly, this is the case more often than not.
@interface NSArrayChipmunkObject : NSArray<ChipmunkObject>
@property(nonatomic, retain) NSArray *chipmunkObjects;
@end
@implementation NSArrayChipmunkObject
@synthesize chipmunkObjects = _chipmunkObjects;
-(id)initWithArray:(NSArray *)objects {
if((self = [super init])){
self.chipmunkObjects = objects;
}
return self;
}
-(NSUInteger)count
{
return [_chipmunkObjects count];
}
-(id)objectAtIndex:(NSUInteger)index
{
return [_chipmunkObjects objectAtIndex:index];
}
@end
@implementation NSArray(ChipmunkObject)
-(id<NSFastEnumeration>)chipmunkObjects
{
return self;
}
@end
// Private class used to wrap the statically allocated staticBody attached to each space.
@interface _ChipmunkStaticBodySingleton : ChipmunkBody {
cpBody *_bodyPtr;
ChipmunkSpace *space; // weak ref
}
@end
typedef struct HandlerContext {
ChipmunkSpace *space;
id delegate;
cpCollisionType typeA, typeB;
SEL beginSelector;
SEL preSolveSelector;
SEL postSolveSelector;
SEL separateSelector;
} HandlerContext;
@implementation ChipmunkSpace
#ifdef CHIPMUNK_PRO_TRIAL
static NSString *dialogTitle = @"Chipmunk Pro Trial";
static NSString *dialogMessage = @"This copy of Chipmunk Pro is a trial, please consider purchasing if you continue using it.";
+(void)initialize
{
[super initialize];
static BOOL done = FALSE;
if(done) return; else done = TRUE;
#if TARGET_OS_IPHONE == 1
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:dialogTitle
message:dialogMessage
delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil
];
[alert show];
[alert release];
#else
[self performSelectorOnMainThread:@selector(dialog) withObject:nil waitUntilDone:FALSE];
#endif
}
#if TARGET_OS_IPHONE != 1
+(void)dialog
{
[NSApplication sharedApplication];
[[NSAlert
alertWithMessageText:dialogTitle
defaultButton:@"OK"
alternateButton:nil
otherButton:nil
informativeTextWithFormat:dialogMessage
] runModal];
}
#endif
#endif
+(ChipmunkSpace *)spaceFromCPSpace:(cpSpace *)space
{
ChipmunkSpace *obj = space->userData;
cpAssertHard([obj isKindOfClass:[ChipmunkSpace class]], "'space->data' is not a pointer to a ChipmunkSpace object.");
return obj;
}
- (id)initWithSpace:(cpSpace *)space
{
if((self = [super init])){
_children = [[NSMutableSet alloc] init];
_handlers = [[NSMutableArray alloc] init];
_space = space;
cpSpaceSetUserData(_space, self);
_staticBody = [[ChipmunkBody alloc] initWithMass:0.0f andMoment:0.0f];
_staticBody.type = CP_BODY_TYPE_STATIC;
cpSpaceSetStaticBody(_space, _staticBody.body);
}
return self;
}
- (id)init {
// Use a fast space instead if the class is available.
// However if you don't specify -ObjC as a linker flag the dynamic substitution won't work.
Class hastySpace = NSClassFromString(@"ChipmunkHastySpace");
if(hastySpace && [self isMemberOfClass:[ChipmunkSpace class]]){
[self release];
return [[hastySpace alloc] init];
} else {
return [self initWithSpace:cpSpaceNew()];
}
}
-(void)freeSpace
{
cpSpaceFree(_space);
}
- (void) dealloc {
[self freeSpace];
[_staticBody release];
[_children release];
[_handlers release];
[super dealloc];
}
- (cpSpace *)space {return _space;}
@synthesize userData = _userData;
// accessor macros
#define getter(type, lower, upper) \
- (type)lower {return cpSpaceGet##upper(_space);}
#define setter(type, lower, upper) \
- (void)set##upper:(type)value {cpSpaceSet##upper(_space, value);};
#define both(type, lower, upper) \
getter(type, lower, upper) \
setter(type, lower, upper)
both(int, iterations, Iterations);
both(cpVect, gravity, Gravity);
both(cpFloat, damping, Damping);
both(cpFloat, idleSpeedThreshold, IdleSpeedThreshold);
both(cpFloat, sleepTimeThreshold, SleepTimeThreshold);
both(cpFloat, collisionSlop, CollisionSlop);
both(cpFloat, collisionBias, CollisionBias);
both(cpTimestamp, collisionPersistence, CollisionPersistence);
getter(cpFloat, currentTimeStep, CurrentTimeStep);
- (BOOL)isLocked {return cpSpaceIsLocked(_space);}
- (ChipmunkBody *)staticBody {return _staticBody;}
typedef BOOL (*BeginProto)(id, SEL, cpArbiter *, ChipmunkSpace *);
static bool Begin(cpArbiter *arb, struct cpSpace *space, HandlerContext *ctx){return ((BeginProto)objc_msgSend)(ctx->delegate, ctx->beginSelector, arb, ctx->space);}
typedef BOOL (*PreSolveProto)(id, SEL, cpArbiter *, ChipmunkSpace *);
static bool PreSolve(cpArbiter *arb, struct cpSpace *space, HandlerContext *ctx){return ((PreSolveProto)objc_msgSend)(ctx->delegate, ctx->preSolveSelector, arb, ctx->space);}
typedef void (*PostSolveProto)(id, SEL, cpArbiter *, ChipmunkSpace *);
static void PostSolve(cpArbiter *arb, struct cpSpace *space, HandlerContext *ctx){((PostSolveProto)objc_msgSend)(ctx->delegate, ctx->postSolveSelector, arb, ctx->space);}
typedef void (*SeparateProto)(id, SEL, cpArbiter *, ChipmunkSpace *);
static void Separate(cpArbiter *arb, struct cpSpace *space, HandlerContext *ctx){((SeparateProto)objc_msgSend)(ctx->delegate, ctx->separateSelector, arb, ctx->space);}
// TODO handlers are never filtered.
- (void)setDefaultCollisionHandler:(id)delegate
begin:(SEL)begin
preSolve:(SEL)preSolve
postSolve:(SEL)postSolve
separate:(SEL)separate
{
cpCollisionType sentinel = (cpCollisionType)@"DEFAULT";
HandlerContext context = {self, delegate, sentinel, sentinel, begin, preSolve, postSolve, separate};
NSData *data = [NSData dataWithBytes:&context length:sizeof(context)];
[_handlers addObject:data];
cpCollisionHandler *handler = cpSpaceAddDefaultCollisionHandler(_space);
if(begin) handler->beginFunc = (cpCollisionBeginFunc)Begin;
if(preSolve) handler->preSolveFunc = (cpCollisionPreSolveFunc)PreSolve;
if(postSolve) handler->postSolveFunc = (cpCollisionPostSolveFunc)PostSolve;
if(separate) handler->separateFunc = (cpCollisionSeparateFunc)Separate;
handler->userData = (void *)[data bytes];
}
- (void)addCollisionHandler:(id)delegate
typeA:(cpCollisionType)a typeB:(cpCollisionType)b
begin:(SEL)begin
preSolve:(SEL)preSolve
postSolve:(SEL)postSolve
separate:(SEL)separate
{
HandlerContext context = {self, delegate, a, b, begin, preSolve, postSolve, separate};
NSData *data = [NSData dataWithBytes:&context length:sizeof(context)];
[_handlers addObject:data];
cpCollisionHandler *handler = cpSpaceAddCollisionHandler(_space, a, b);
if(begin) handler->beginFunc = (cpCollisionBeginFunc)Begin;
if(preSolve) handler->preSolveFunc = (cpCollisionPreSolveFunc)PreSolve;
if(postSolve) handler->postSolveFunc = (cpCollisionPostSolveFunc)PostSolve;
if(separate) handler->separateFunc = (cpCollisionSeparateFunc)Separate;
handler->userData = (void *)[data bytes];
}
- (id)add:(NSObject<ChipmunkObject> *)obj
{
if([obj conformsToProtocol:@protocol(ChipmunkBaseObject)]){
[(NSObject<ChipmunkBaseObject> *)obj addToSpace:self];
} else if([obj conformsToProtocol:@protocol(ChipmunkObject)]){
for(NSObject<ChipmunkBaseObject> *child in [obj chipmunkObjects]) [self add:child];
} else {
[NSException raise:@"NSArgumentError" format:@"Attempted to add an object of type %@ to a ChipmunkSpace.", [obj class]];
}
[_children addObject:obj];
return obj;
}
- (id)remove:(NSObject<ChipmunkObject> *)obj
{
if([obj conformsToProtocol:@protocol(ChipmunkBaseObject)]){
[(NSObject<ChipmunkBaseObject> *)obj removeFromSpace:self];
} else if([obj conformsToProtocol:@protocol(ChipmunkObject)]){
for(NSObject<ChipmunkBaseObject> *child in [obj chipmunkObjects]) [self remove:child];
} else {
[NSException raise:@"NSArgumentError" format:@"Attempted to remove an object of type %@ from a ChipmunkSpace.", [obj class]];
}
[_children removeObject:obj];
return obj;
}
-(BOOL)contains:(NSObject<ChipmunkObject> *)obj
{
return [_children containsObject:obj];
}
- (NSObject<ChipmunkObject> *)smartAdd:(NSObject<ChipmunkObject> *)obj
{
if(cpSpaceIsLocked(_space)){
[self addPostStepAddition:obj];
} else {
[self add:obj];
}
return obj;
}
- (NSObject<ChipmunkObject> *)smartRemove:(NSObject<ChipmunkObject> *)obj
{
if(cpSpaceIsLocked(_space)){
[self addPostStepRemoval:obj];
} else {
[self remove:obj];
}
return obj;
}
struct PostStepTargetContext {
id target;
SEL selector;
};
static void
postStepPerform(cpSpace *unused, id key, struct PostStepTargetContext *context)
{
[context->target performSelector:context->selector withObject:key];
[context->target release];
cpfree(context);
[key release];
}
- (BOOL)addPostStepCallback:(id)target selector:(SEL)selector key:(id)key
{
if(!cpSpaceGetPostStepCallback(_space, key)){
struct PostStepTargetContext *context = cpcalloc(1, sizeof(struct PostStepTargetContext));
(*context) = (struct PostStepTargetContext){target, selector};
cpSpaceAddPostStepCallback(_space, (cpPostStepFunc)postStepPerform, key, context);
[target retain];
[key retain];
return TRUE;
} else {
return FALSE;
}
}
static void
postStepPerformBlock(cpSpace *unused, id key, ChipmunkPostStepBlock block)
{
block();
[block release];
[key release];
}
- (BOOL)addPostStepBlock:(ChipmunkPostStepBlock)block key:(id)key
{
if(!cpSpaceGetPostStepCallback(_space, key)){
cpSpaceAddPostStepCallback(_space, (cpPostStepFunc)postStepPerformBlock, key, [block copy]);
[key retain];
return TRUE;
} else {
return FALSE;
}
}
- (void)addPostStepAddition:(NSObject<ChipmunkObject> *)obj
{
[self addPostStepCallback:self selector:@selector(add:) key:obj];
}
- (void)addPostStepRemoval:(NSObject<ChipmunkObject> *)obj
{
[self addPostStepCallback:self selector:@selector(remove:) key:obj];
}
- (NSArray *)pointQueryAll:(cpVect)point maxDistance:(cpFloat)maxDistance filter:(cpShapeFilter)filter
{
NSMutableArray *array = [NSMutableArray array];
cpSpacePointQuery_b(_space, point, maxDistance, filter, ^(cpShape *shape, cpVect p, cpFloat d, cpVect g){
ChipmunkPointQueryInfo *info = [[ChipmunkPointQueryInfo alloc] initWithInfo:&(cpPointQueryInfo){shape, p, d, g}];
[array addObject:info];
[info release];
});
return array;
}
- (ChipmunkPointQueryInfo *)pointQueryNearest:(cpVect)point maxDistance:(cpFloat)maxDistance filter:(cpShapeFilter)filter
{
cpPointQueryInfo info;
cpSpacePointQueryNearest(_space, point, maxDistance, filter, &info);
return (info.shape ? [[[ChipmunkPointQueryInfo alloc] initWithInfo:&info] autorelease] : nil);
}
typedef struct segmentQueryContext {
cpVect start, end;
NSMutableArray *array;
} segmentQueryContext;
- (NSArray *)segmentQueryAllFrom:(cpVect)start to:(cpVect)end radius:(cpFloat)radius filter:(cpShapeFilter)filter
{
NSMutableArray *array = [NSMutableArray array];
cpSpaceSegmentQuery_b(_space, start, end, radius, filter, ^(cpShape *shape, cpVect p, cpVect n, cpFloat t){
// TODO point
ChipmunkSegmentQueryInfo *info = [[ChipmunkSegmentQueryInfo alloc] initWithInfo:&(cpSegmentQueryInfo){shape, p, n, t} start:start end:end];
[array addObject:info];
[info release];
});
return array;
}
- (ChipmunkSegmentQueryInfo *)segmentQueryFirstFrom:(cpVect)start to:(cpVect)end radius:(cpFloat)radius filter:(cpShapeFilter)filter
{
cpSegmentQueryInfo info;
cpSpaceSegmentQueryFirst(_space, start, end, radius, filter, &info);
return (info.shape ? [[[ChipmunkSegmentQueryInfo alloc] initWithInfo:&info start:start end:end] autorelease] : nil);
}
- (NSArray *)bbQueryAll:(cpBB)bb filter:(cpShapeFilter)filter
{
NSMutableArray *array = [NSMutableArray array];
cpSpaceBBQuery_b(_space, bb, filter, ^(cpShape *shape){
[array addObject:shape->userData];
});
return array;
}
//static void
//shapeQueryAll(cpShape *shape, cpContactPointSet *points, NSMutableArray *array)
//{
// ChipmunkShapeQueryInfo *info = [[ChipmunkShapeQueryInfo alloc] initWithShape:shape->userData andPoints:points];
// [array addObject:info];
// [info release];
//}
- (NSArray *)shapeQueryAll:(ChipmunkShape *)shape
{
NSMutableArray *array = [NSMutableArray array];
cpSpaceShapeQuery_b(_space, shape.shape, ^(cpShape *shape, cpContactPointSet *points){
ChipmunkShapeQueryInfo *info = [[ChipmunkShapeQueryInfo alloc] initWithShape:shape->userData andPoints:points];
[array addObject:info];
[info release];
});
return array;
}
- (BOOL)shapeTest:(ChipmunkShape *)shape
{
return cpSpaceShapeQuery(_space, shape.shape, NULL, NULL);
}
static void PushBody(cpBody *body, NSMutableArray *arr){[arr addObject:body->userData];}
- (NSArray *)bodies
{
NSMutableArray *arr = [NSMutableArray array];
cpSpaceEachBody(_space, (cpSpaceBodyIteratorFunc)PushBody, arr);
return arr;
}
static void PushShape(cpShape *shape, NSMutableArray *arr){[arr addObject:shape->userData];}
- (NSArray *)shapes
{
NSMutableArray *arr = [NSMutableArray array];
cpSpaceEachShape(_space, (cpSpaceShapeIteratorFunc)PushShape, arr);
return arr;
}
static void PushConstraint(cpConstraint *constraint, NSMutableArray *arr){[arr addObject:constraint->userData];}
- (NSArray *)constraints
{
NSMutableArray *arr = [NSMutableArray array];
cpSpaceEachConstraint(_space, (cpSpaceConstraintIteratorFunc)PushConstraint, arr);
return arr;
}
- (void)reindexStatic
{
cpSpaceReindexStatic(_space);
}
- (void)reindexShape:(ChipmunkShape *)shape
{
cpSpaceReindexShape(_space, shape.shape);
}
- (void)reindexShapesForBody:(ChipmunkBody *)body
{
cpSpaceReindexShapesForBody(_space, body.body);
}
- (void)step:(cpFloat)dt
{
cpSpaceStep(_space, dt);
}
//MARK: Extras
- (ChipmunkBody *)addBody:(ChipmunkBody *)obj {
cpSpaceAddBody(_space, obj.body);
[_children addObject:obj];
return obj;
}
- (ChipmunkBody *)removeBody:(ChipmunkBody *)obj {
cpSpaceRemoveBody(_space, obj.body);
[_children removeObject:obj];
return obj;
}
- (ChipmunkShape *)addShape:(ChipmunkShape *)obj {
cpSpaceAddShape(_space, obj.shape);
[_children addObject:obj];
return obj;
}
- (ChipmunkShape *)removeShape:(ChipmunkShape *)obj {
cpSpaceRemoveShape(_space, obj.shape);
[_children removeObject:obj];
return obj;
}
- (ChipmunkConstraint *)addConstraint:(ChipmunkConstraint *)obj {
cpSpaceAddConstraint(_space, obj.constraint);
[_children addObject:obj];
return obj;
}
- (ChipmunkConstraint *)removeConstraint:(ChipmunkConstraint *)obj {
cpSpaceRemoveConstraint(_space, obj.constraint);
[_children removeObject:obj];
return obj;
}
static ChipmunkSegmentShape *
boundSeg(ChipmunkBody *body, cpVect a, cpVect b, cpFloat radius, cpFloat elasticity,cpFloat friction, cpShapeFilter filter, cpCollisionType collisionType)
{
ChipmunkSegmentShape *seg = [ChipmunkSegmentShape segmentWithBody:body from:a to:b radius:radius];
seg.elasticity = elasticity;
seg.friction = friction;
seg.filter = filter;
seg.collisionType = collisionType;
return seg;
}
- (NSArray *)addBounds:(cpBB)bounds thickness:(cpFloat)radius
elasticity:(cpFloat)elasticity friction:(cpFloat)friction
filter:(cpShapeFilter)filter collisionType:(cpCollisionType)collisionType
{
cpFloat l = bounds.l - radius;
cpFloat b = bounds.b - radius;
cpFloat r = bounds.r + radius;
cpFloat t = bounds.t + radius;
NSArray *segs = [[NSArrayChipmunkObject alloc] initWithArray:[NSArray arrayWithObjects:
boundSeg(_staticBody, cpv(l,b), cpv(l,t), radius, elasticity, friction, filter, collisionType),
boundSeg(_staticBody, cpv(l,t), cpv(r,t), radius, elasticity, friction, filter, collisionType),
boundSeg(_staticBody, cpv(r,t), cpv(r,b), radius, elasticity, friction, filter, collisionType),
boundSeg(_staticBody, cpv(r,b), cpv(l,b), radius, elasticity, friction, filter, collisionType),
nil
]];
[self add:segs];
return [segs autorelease];
}
@end

View File

@ -1,24 +0,0 @@
CocosBuilder Reader: http://www.cocosbuilder.com
Copyright (c) 2010-2011 - Viktor Lidholt
Copyright (c) 2011-2012 - Zynga Inc. and contributors
(see each file to see the different copyright owners)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,19 +0,0 @@
Copyright (c) 2007-2014 Scott Lembcke and Howling Moon Software
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,24 +0,0 @@
cocos2d for iPhone: http://www.cocos2d-iphone.org
Copyright (c) 2008-2011 - Ricardo Quesada and contributors
Copyright (c) 2011-2012 - Zynga Inc. and contributors
(see each file to see the different copyright owners)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,23 +0,0 @@
//
// AppDelegate+OALAction_Private.h
// ObjectAL
//
// Created by Karl Stenerud on 3/18/12.
// Copyright (c) 2012 Stenerud. All rights reserved.
//
#import "OALAction.h"
/** \cond */
@interface OALAction ()
@property(nonatomic,readwrite,assign) id target;
@property(nonatomic,readwrite,assign) float duration;
@property(nonatomic,readwrite,assign) bool running;
@property(nonatomic,readwrite,assign) bool runningInManager;
@end
/** \endcond */

View File

@ -1,325 +0,0 @@
//
// OALAction.h
// ObjectAL
//
// Created by Karl Stenerud on 10-09-18.
//
// Copyright (c) 2009 Karl Stenerud. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall remain in place
// in this source code.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// Attribution is not required, but appreciated :)
//
#import <Foundation/Foundation.h>
#import "ObjectALConfig.h"
#if OBJECTAL_CFG_USE_COCOS2D_ACTIONS
#pragma mark Cocos2d Subclassing
#import "cocos2d.h"
/** Generates common code required to subclass from a cocos2d action
* while maintaining the functionality of an OALAction.
*/
#define COCOS2D_SUBCLASS_HEADER(CLASS_A,CLASS_B) \
@interface CLASS_A: CLASS_B \
{ \
bool started_; \
} \
\
@property(nonatomic,readonly,assign) bool running; \
- (void) runWithTarget:(id) target; \
- (void) prepareWithTarget:(id) target; \
- (void) stopAction; \
- (void) updateCompletion:(float) proportionComplete; \
\
@end
/** Generates common code required to subclass from a cocos2d action
* while maintaining the functionality of an OALAction.
*/
#define COCOS2D_SUBCLASS(CLASS_A) \
@implementation CLASS_A \
\
- (id) init \
{ \
return [self initWithDuration:0]; \
} \
\
-(void) startWithTarget:(id) targetIn \
{ \
[super startWithTarget:targetIn]; \
[self prepareWithTarget:targetIn]; \
started_ = YES; \
[self runWithTarget:targetIn]; \
} \
\
- (void) update:(float) proportionComplete \
{ \
[super update:proportionComplete]; \
[self updateCompletion:proportionComplete]; \
} \
\
- (bool) running \
{ \
return !self.isDone; \
} \
\
- (void) runWithTarget:(id) targetIn \
{ \
if(!started_) \
{ \
[[[CCDirector sharedDirector] actionManager] addAction:self target:targetIn paused:NO]; \
} \
} \
\
- (void) stopAction \
{ \
[[[CCDirector sharedDirector] actionManager] removeAction:self]; \
} \
#endif /* OBJECTAL_CFG_USE_COCOS2D_ACTIONS */
/* There are two versions of the actions which can be used: ObjectAL and Cocos2d.
* It's usually more convenient when using Cocos2d to have all actions as part of
* the Cocos2d action system. You can set this in ObjectALConfig.h
*/
#if !OBJECTAL_CFG_USE_COCOS2D_ACTIONS
#pragma mark -
#pragma mark OALAction (ObjectAL version)
/**
* Represents an action that can be performed on an object.
*/
@interface OALAction : NSObject
{
/** \cond */
float duration_;
float elapsed_;
bool running_;
/** \endcond */
/** If TRUE, this action is running via OALActionManager. */
bool runningInManager_;
}
#pragma mark Properties
/** The target to perform the action on. WEAK REFERENCE. */
@property(nonatomic,readonly,assign) id target;
/** The duration of the action, in seconds. */
@property(nonatomic,readonly,assign) float duration;
/** The amount of time that has elapsed for this action, in seconds. */
@property(nonatomic,readwrite,assign) float elapsed;
/** If true, the action is currently running. */
@property(nonatomic,readonly,assign) bool running;
#pragma mark Object Management
/** Initialize an action.
*
* @param duration The duration of this action in seconds.
* @return The initialized action.
*/
- (id) initWithDuration:(float) duration;
#pragma mark Functions
/** Run this action on a target.
*
* @param target The target to run the action on.
*/
- (void) runWithTarget:(id) target;
/** Called by runWithTraget to do any final preparations before running.
* Subclasses must ensure that duration is valid when this method returns.
*
* @param target The target to run the action on.
*/
- (void) prepareWithTarget:(id) target;
/** Called by runWithTarget to start the action running.
*/
- (void) startAction;
/** Called by OALActionManager to update this action's progress.
*
* @param proportionComplete The proportion of this action's duration that has elapsed.
*/
- (void) updateCompletion:(float) proportionComplete;
/** Stop this action.
*/
- (void) stopAction;
@end
#else /* !OBJECTAL_CFG_USE_COCOS2D_ACTIONS */
COCOS2D_SUBCLASS_HEADER(OALAction, CCActionInterval);
#endif /* !OBJECTAL_CFG_USE_COCOS2D_ACTIONS */
#pragma mark -
#pragma mark OALPropertyAction
@interface OALPropertyAction: OALAction
/** The value that the property in the target will hold at the start of the action. */
@property(nonatomic,readwrite,assign) float startValue;
/** The value that the property in the target will hold at the end of the action. */
@property(nonatomic,readwrite,assign) float endValue;
#pragma mark Object Management
/** Create a new action using the default function.
* The start value will be the current value of the target this action is applied to.
*
* @param duration The duration of this action in seconds.
* @param propertyKey The property to modify.
* @param endValue The "ending" value that this action will converge upon when setting the target's property.
* @return A new action.
*/
+ (id) actionWithDuration:(float) duration
propertyKey:(NSString*) propertyKey
endValue:(float) endValue;
/** Create a new action.
*
* @param duration The duration of this action in seconds.
* @param propertyKey The property to modify.
* @param startValue The "starting" value that this action will diverge from when setting the target's
* property. If NAN, use the current value from the target.
* @param endValue The "ending" value that this action will converge upon when setting the target's property.
* @return A new action.
*/
+ (id) actionWithDuration:(float) duration
propertyKey:(NSString*) propertyKey
startValue:(float) startValue
endValue:(float) endValue;
/** Initialize an action using the default function.
* The start value will be the current value of the target this action is applied to.
*
* @param duration The duration of this action in seconds.
* @param propertyKey The property to modify.
* @param endValue The "ending" value that this action will converge upon when setting the target's property.
* @return The initialized action.
*/
- (id) initWithDuration:(float) duration
propertyKey:(NSString*) propertyKey
endValue:(float) endValue;
/** Initialize an action.
*
* @param duration The duration of this action in seconds.
* @param propertyKey The property to modify.
* @param startValue The "starting" value that this action will diverge from when setting the target's
* property. If NAN, use the current value from the target.
* @param endValue The "ending" value that this action will converge upon when setting the target's property.
* @return The initialized action.
*/
- (id) initWithDuration:(float) duration
propertyKey:(NSString*) propertyKey
startValue:(float) startValue
endValue:(float) endValue;
@end
#pragma mark -
#pragma mark OALEaseAction
typedef float (*EaseFunctionPtr)(float);
typedef enum
{
kOALEaseIn,
kOALEaseOut,
kOALEaseInOut,
} OALEasePhase;
typedef enum
{
kOALEaseShapeSine,
kOALEaseShapeExponential,
} OALEaseShape;
/**
* Applies an easing function to another action.
* Normally, an action progresses at a linear rate. An ease changes that to a
* curve.
*/
@interface OALEaseAction: OALAction
{
OALAction* action_;
}
/** Create a new ease action.
*
* @param shape The shape of the curve to apply.
* @param phase What phase of the action to apply the curve to.
* @action The action to apple the curve to.
* @return A new action.
*/
+ (OALEaseAction*) actionWithShape:(OALEaseShape) shape
phase:(OALEasePhase) phase
action:(OALAction*) action;
/** Initialize an ease action.
*
* @param shape The shape of the curve to apply.
* @param phase What phase of the action to apply the curve to.
* @action The action to apple the curve to.
* @return The initialized action.
*/
- (id) initWithShape:(OALEaseShape) shape
phase:(OALEasePhase) phase
action:(OALAction*) action;
/** Get a pointer to an ease function of the specified shape and phase.
*
* @param shape The shape of the curve to apply.
* @param phase What phase of the action to apply the curve to.
* @return a pointer to the appropriate function.
*/
+ (EaseFunctionPtr) easeFunctionForShape:(OALEaseShape) shape
phase:(OALEasePhase) phase;
@end

Some files were not shown because too many files have changed in this diff Show More