Skip to content

Instantly share code, notes, and snippets.

@kazukitanaka0611
Created July 18, 2014 09:30
Show Gist options
  • Save kazukitanaka0611/0345947b53bff7899b93 to your computer and use it in GitHub Desktop.
Save kazukitanaka0611/0345947b53bff7899b93 to your computer and use it in GitHub Desktop.
Edge* MakeEdge(NSUInteger i, NSUInteger j);
@interface Edge : NSObject <NSCopying>
@property (nonatomic, assign, readonly) NSUInteger i;
@property (nonatomic, assign, readonly) NSUInteger j;
+ (instancetype)edgeWithI:(NSUInteger)i J:(NSUInteger)j;
@end
@interface Edge()
@end
Edge* MakeEdge(NSUInteger i, NSUInteger j)
{
return [Edge edgeWithI:i J:j];
}
@implementation Edge
+ (instancetype)edgeWithI:(NSUInteger)i J:(NSUInteger)j
{
return [[self alloc] initWithI:i J:j];
}
- (instancetype)initWithI:(NSUInteger)i J:(NSUInteger)j
{
if (self = [super init])
{
_i = i;
_j = j;
}
return self;
}
- (BOOL)isEqual:(id)object
{
if (object == self)
{
return YES;
}
if (!object || ![[object class] isEqual:[self class]])
{
return NO;
}
return [self isEqualToEdge:object];
}
- (BOOL)isEqualToEdge:(Edge *)edge
{
if (self == edge)
{
return YES;
}
if (edge == nil)
{
return NO;
}
if (self.i == edge.i && self.j == edge.j)
{
return YES;
}
if (self.i == edge.j && self.j == edge.i)
{
return YES;
}
return NO;
}
- (NSUInteger)hash
{
NSUInteger hash = self.i;
hash = hash * 31u + self.j;
return hash;
}
- (id)copyWithZone:(NSZone *)zone
{
Edge *copy = [[[self class] allocWithZone:zone] init];
if (copy != nil)
{
copy->_i = _i;
copy->_j = _j;
}
return copy;
}
@end
#import "MyScene.h"
#import "Edge.h"
@interface MyScene()
@property (nonatomic, strong) NSMutableDictionary *vertexes;
@property (nonatomic, strong) NSMutableDictionary *connections;
@property (nonatomic, strong) NSMutableArray *mutableEdges;
@property (nonatomic, assign) CGFloat repulsion;
@property (nonatomic, assign) CGFloat attraction;
@property (nonatomic, assign) BOOL contentCreated;
@property (nonatomic, assign) BOOL stable;
@property (nonatomic, strong) SKNode *touchedNode;
@end
@implementation MyScene
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
/* Setup your scene here */
self.connections = [NSMutableDictionary dictionary];
self.vertexes = [NSMutableDictionary dictionary];
self.mutableEdges = [NSMutableArray array];
self.repulsion = 600.f;
self.attraction = 0.1f;
[self addEdge];
}
return self;
}
#pragma mark - touch event
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = touches.anyObject;
CGPoint positionInScene = [touch locationInNode:self];
NSArray *nodes = [self nodesAtPoint:positionInScene];
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(SKNode *node, NSDictionary *bindings) {
return [node.name isEqualToString:@"circle"];
}];
SKNode *node = [nodes filteredArrayUsingPredicate:predicate].firstObject;
self.touchedNode = node;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = touches.anyObject;
CGPoint positionInScene = [touch locationInNode:self];
self.touchedNode.position = positionInScene;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
self.touchedNode = nil;
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
self.touchedNode = nil;
}
#pragma mark - update
-(void)update:(CFTimeInterval)currentTime
{
if (self.stable)
{
return;
}
for (NSUInteger i = 0; i < self.vertexes.count; i++)
{
SKShapeNode *v = self.vertexes[@(i)];
SKShapeNode *u;
CGFloat vForceX = 0;
CGFloat vForceY = 0;
for (NSUInteger j = 0; j < self.vertexes.count; j++)
{
if (i == j) continue;
u = self.vertexes[@(j)];
double rsq = pow(v.position.x - u.position.x, 2) +
pow(v.position.y - u.position.y, 2);
vForceX += self.repulsion * (v.position.x - u.position.x) / rsq;
vForceY += self.repulsion * (v.position.y - u.position.y) / rsq;
}
for (NSUInteger j = 0; j < self.vertexes.count; j++)
{
if (![self.mutableEdges containsObject:MakeEdge(i, j)])
{
continue;
}
u = self.vertexes[@(j)];
vForceX += self.attraction * (u.position.x - v.position.x);
vForceY += self.attraction * (u.position.y - v.position.y);
}
v.physicsBody.linearDamping = 0.95;
v.physicsBody.velocity = CGVectorMake((v.physicsBody.velocity.dx + vForceX),
(v.physicsBody.velocity.dy + vForceY));
[v.physicsBody applyForce:CGVectorMake(vForceX, vForceY)];
v.physicsBody.angularVelocity = 0;
}
[self updateConnections];
}
#pragma mark didMoveToView
- (void)didMoveToView:(SKView *)view
{
if (!self.contentCreated)
{
self.backgroundColor = [SKColor blueColor];
self.physicsWorld.gravity = CGVectorMake(0, 0);
self.contentCreated = YES;
}
}
#pragma mark didChangeSize
- (void)didChangeSize:(CGSize)oldSize
{
[super didChangeSize:oldSize];
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
}
#pragma mark - private method
- (void)updateConnections
{
[self.connections enumerateKeysAndObjectsUsingBlock:^(Edge *key, SKShapeNode *connection, BOOL *stop) {
CGMutablePathRef pathToDraw = CGPathCreateMutable();
SKNode *vertexA = self.vertexes[@(key.i)];
SKNode *vertexB = self.vertexes[@(key.j)];
CGPathMoveToPoint(pathToDraw, NULL, vertexA.position.x, vertexA.position.y);
CGPathAddLineToPoint(pathToDraw, NULL, vertexB.position.x, vertexB.position.y);
connection.path = pathToDraw;
CGPathRelease(pathToDraw);
}];
}
- (void)addEdge
{
NSArray *edges = @[
MakeEdge(0, 1),
MakeEdge(1, 2),
MakeEdge(2, 3),
MakeEdge(3, 0),
MakeEdge(3, 4),
MakeEdge(4, 5),
MakeEdge(4, 6),
MakeEdge(4, 7),
MakeEdge(4, 8),
];
for (Edge *edge in edges)
{
[self.mutableEdges addObject:edge];
[self createVertexIfNeeded:edge.i];
[self createVertexIfNeeded:edge.j];
[self createConnectionForEdge:edge];
}
}
- (void)createConnectionForEdge:(Edge *)edge
{
SKShapeNode *connection = [SKShapeNode node];
connection.strokeColor = [SKColor redColor];
connection.fillColor = [SKColor redColor];
connection.lineWidth = 3.f;
[self addChild:connection];
self.connections[edge] = connection;
}
- (void)createVertexIfNeeded:(NSUInteger)index
{
if (self.vertexes[@(index)] == nil)
{
SKShapeNode *circle = [self createVertexNode];
NSInteger maxWidth = (NSInteger)(self.size.width ? : 1);
NSInteger maxHeight = (NSInteger)(self.size.height ? : 1);
circle.position = CGPointMake(arc4random() % maxWidth,
arc4random() % maxHeight);
[self addChild:circle];
self.vertexes[@(index)] = circle;
}
}
- (SKShapeNode *)createVertexNode
{
SKShapeNode *node = [SKShapeNode node];
node.zPosition = 10;
node.physicsBody.allowsRotation = NO;
node.name = @"circle";
CGFloat diameter = 40;
CGRect circleRect = CGRectMake(- diameter / 2, -diameter / 2,
diameter, diameter);
node.path = CGPathCreateWithEllipseInRect(circleRect, nil);
node.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:diameter / 2];
return node;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment