5
// Created by döme on 03.08.2009.
6
// Copyright 2009 __MyCompanyName__. All rights reserved.
11
#import "VertexArray.h"
14
#import "JigsawGenerator.h"
15
#import "Triangulation.h"
16
#import "JigsawPiece.h"
17
#import "MersenneTwister.h"
18
#import "SimplexNoise.h"
19
#import "ElAnimation.h"
20
#import "JigsAppDelegate.h"
22
@implementation JigView
29
cpResetShapeIdCounter();
32
space->iterations = 20;
33
cpSpaceResizeStaticHash(space, 40.0, 1000);
34
cpSpaceResizeActiveHash(space, 40.0, 1000);
35
// space->gravity = cpv(0, -GRAVITY);
36
space->damping = 0.01;
38
staticBody = cpBodyNew(INFINITY, INFINITY);
39
mouseBody = cpBodyNew(INFINITY, INFINITY);
40
mouseConstraint = cpPinJointAlloc();
42
cpShape *shape = NULL;
45
CGRect bounds = [self bounds];
47
// Create segments around the edge of the screen.
48
shape = cpSegmentShapeNew(staticBody, cpv(CGRectGetMinX(bounds) - 5.0f, CGRectGetMinY(bounds) - 5.0f), cpv(CGRectGetMaxX(bounds) + 5.0f, CGRectGetMinY(bounds) - 5.0f), 5.0f);
49
shape->e = 0.1; shape->u = 1.0;
50
cpSpaceAddStaticShape(space, shape);
52
shape = cpSegmentShapeNew(staticBody, cpv(CGRectGetMaxX(bounds) + 5.0f, CGRectGetMinY(bounds) - 5.0f), cpv(CGRectGetMaxX(bounds) + 5.0f, CGRectGetMaxY(bounds) + 5.0f), 5.0f);
53
shape->e = 0.1; shape->u = 1.0;
54
cpSpaceAddStaticShape(space, shape);
56
shape = cpSegmentShapeNew(staticBody, cpv(CGRectGetMaxX(bounds) + 5.0f, CGRectGetMaxY(bounds) + 5.0f), cpv(CGRectGetMinX(bounds) - 5.0f, CGRectGetMaxY(bounds) + 5.0f), 5.0f);
57
shape->e = 0.1; shape->u = 1.0;
58
cpSpaceAddStaticShape(space, shape);
60
shape = cpSegmentShapeNew(staticBody, cpv(CGRectGetMinX(bounds) - 5.0f, CGRectGetMaxY(bounds) + 5.0f), cpv(CGRectGetMinX(bounds) - 5.0f, CGRectGetMinY(bounds) - 5.0f), 5.0f);
61
shape->e = 0.1; shape->u = 1.0;
62
cpSpaceAddStaticShape(space, shape);
65
cpSpaceAddCollisionPairFunc(space->value, COLLTYPE_TOWER, COLLTYPE_NORMALBALL, towerBallCollisionFunc, space->value);
66
cpSpaceAddCollisionPairFunc(space->value, COLLTYPE_NORMALBALL, COLLTYPE_NORMALBALL, ballBallCollisionFunc, space->value);
67
cpSpaceAddCollisionPairFunc(space->value, COLLTYPE_NORMALBALL, COLLTYPE_SPLODEBALL, ballSplosionCollisionFunc, space->value);
68
cpSpaceAddCollisionPairFunc(space->value, COLLTYPE_DETACHEDBALL, COLLTYPE_SPLODEBALL, detachedBallSplosionCollisionFunc, space->value);
69
cpSpaceAddCollisionPairFunc(space->value, COLLTYPE_TOWER, COLLTYPE_SPLODEBALL, towerSplosionCollisionFunc, space->value);
70
cpSpaceAddCollisionPairFunc(space->value, COLLTYPE_SPLODEBALL, COLLTYPE_SPLODEBALL, voidCollisionFunc, space->value);
75
float diminishingBoxWeight(CGPoint p, CGSize size)
77
float wx = 1.0f - fclampf(fabsf(0.5f*size.width - p.x)/(0.5f*size.width), 0.0f, 1.0f);
78
float wy = 1.0f - fclampf(fabsf(0.5f*size.height - p.y)/(0.5f*size.height), 0.0f, 1.0f);
82
- (UIImage*) simplexImageWithSize: (CGSize) size frequency: (float) freq
84
SimplexNoise* noise = [[[SimplexNoise alloc] init] autorelease];
86
int width = size.width;
87
int height = size.height;
89
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
90
uint8_t* data = malloc(4*width*height);
93
for(int i = 0; i < height; ++i)
95
//float y = size.height - (float)i;
96
for(int j = 0; j < width; ++j)
98
//float x = size.width - (float)j;
99
//float n = diminishingBoxWeight(CGPointMake(x,y), size)*[noise noise3dWithX: (float)i*freq Y: (float)j*freq Z: 0.0f];
100
float n = [noise noise3dWithX: (float)(i-height/2)*freq Y: (float)(j-width/2)*freq Z: 0.0f];
101
// NSLog(@"%.2f", n);
102
data[(i*width+j)*4 + 0] = 127.0f + n*127.0f;
103
data[(i*width+j)*4 + 1] = 127.0f + n*127.0f;
104
data[(i*width+j)*4 + 2] = 127.0f + n*127.0f;
105
data[(i*width+j)*4 + 3] = 255;
108
CGContextRef bitmapContext = CGBitmapContextCreate(data, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedLast);
109
CGColorSpaceRelease(colorSpace);
120
CGImageRef image = CGBitmapContextCreateImage(bitmapContext);
121
CFRelease(bitmapContext);
123
id img = [UIImage imageWithCGImage: image];
124
CGImageRelease(image);
129
- (int) numCellsForImageSize: (float) imgSize density: (float) density
131
float maxSize = 200.0f;
132
float minSize = 50.0f;
134
float targetSize = minSize + (maxSize-minSize)*(1.0f - density);
136
return ceilf(imgSize/targetSize);
139
- (void) generateJigsaw
142
glDeleteTextures(1, &imageTexture);
145
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
147
[defaults setInteger: [NSDate timeIntervalSinceReferenceDate] forKey: JigsawSeedKey];
149
UIImage* image = [UIImage imageWithData: [defaults objectForKey: JigsawImageKey]];
151
imageSize = CGSizeFitIntoSize([image size], CGSizeMake(400.0f, 400.0f));
155
int hcells = [self numCellsForImageSize: imageSize.width density: [defaults floatForKey: JigsawDensityKey]];
156
int vcells = [self numCellsForImageSize: imageSize.height density: [defaults floatForKey: JigsawDensityKey]];
157
float jigWidth = imageSize.width;
158
float jigHeight = imageSize.height;
159
pieceSize = sqrtf(jigWidth*jigHeight/((float)(hcells*vcells)));
160
// float cellWidth = jigWidth/(float)hcells;
161
// float cellHeight = jigHeight/(float)vcells;
163
float noiseFreq = 1.0f/MAX(jigWidth, jigHeight);
165
JigsawGenerator* jgen = [[[JigsawGenerator alloc] init] autorelease];
166
[jgen setWidth: jigWidth];
167
[jgen setHeight: jigHeight];
168
[jgen setHcells: hcells];
169
[jgen setVcells: vcells];
170
[jgen setSeed: [defaults integerForKey: JigsawSeedKey]];
171
[jgen setDistortion: 0.1f*MIN(jigWidth, jigHeight)];
172
[jgen setFrequency: noiseFreq];
173
[jgen setOctaves: 0];
174
[jgen setNoiseType: 0];
176
[jgen setEdgeFiles: [NSMutableArray arrayWithArray: [NSBundle pathsForResourcesOfType: @"jigedge" inDirectory: [[NSBundle mainBundle] bundlePath]]]];
177
[jgen setEnabledEdges: [NSMutableDictionary dictionaryWithObject: [NSNumber numberWithInt: 1] forKey:[[jgen edgeFiles] objectAtIndex: 0]]];
178
[jgen setSmoothedEdges: [NSMutableDictionary dictionaryWithObject: [NSNumber numberWithInt: 1] forKey:[[jgen edgeFiles] objectAtIndex: 0]]];
180
JigPieces pieces = [jgen generateJigsaw];
185
// image = [self simplexImageWithSize: imageSize frequency: noiseFreq];
187
CGImageRef cgImage = image.CGImage;
188
[self textureFromCgImage: cgImage texName: &imageTexture width: 512 height: 512 repeat: NO mipmap: YES filter: YES];
192
//[pieceVertexArrays release];
193
[jigsawPieces release];
194
//pieceVertexArrays = [[NSMutableArray alloc] init];
195
jigsawPieces = [[NSMutableArray alloc] init];
198
CGPoint testPoints[6] = {
207
VertexArray* var = TriangulateOutline(testPoints, 6);
209
[pieceVertexArrays addObject: var];
212
// [self initChipmunk];
214
CGRect bounds = [self bounds];
216
//NSLog(@"bounds %f %f %f %f", bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height);
217
// CGPoint bc = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
219
// need to transform the vertices from puzzle space to piece local space
221
for (int i = 0; i < pieces.hcells; ++i)
223
for (int j = 0; j < pieces.vcells; ++j)
225
CGPoint* points = NULL;
226
size_t numPoints = 0;
228
SimpleBezierPathToPolyline(pieces.cells[i][j].outline, &points, &numPoints);
230
if (CGPointEqualToPoint(points[0], points[numPoints-1]))
233
CGPoint* outlinePoints = createOutline(points, numPoints, 4.0f);
235
JigsawPiece* jig = [[[JigsawPiece alloc] init] autorelease];
239
CGPoint drawpos = pieces.cells[i][j].pos;
240
CGPoint jigpos = pieces.cells[i][j].pos;
243
VertexArray* var = TriangulateOutline(points, numPoints);
244
[var applyVertexTransform: mtranslate(vcreate(-drawpos.x, -drawpos.y, 0.0f))];
245
[[jig fillDrawables] addObject: var];
248
CGAffineTransform cgm = CGAffineTransformMakeTranslation(-drawpos.x, -drawpos.y);
249
CGPathRef newPath = CreateTransformedPathFromCGPath(pieces.cells[i][j].outline, cgm);
252
//jigpos = CGPointMake(bc.x, bc.y);
253
//jigpos = CGPointMake(0.0, 0.0);
256
[[jig outlines] addObject: (id)newPath];
257
CGPathRelease(newPath);
259
[jig setInitialPos: jigpos];
260
[jig setPosition: jigpos];
261
//cpSpaceAddBody(space, [jig body]);
263
[jigsawPieces addObject: jig];
265
#ifdef OLDSCHOOL_OUTLINE
266
VertexArray* ovar = [[[VertexArray alloc] init] autorelease];
267
ovar->numVertices = 3*numPoints;
268
ovar->vertices = calloc(sizeof(*ovar->vertices), ovar->numVertices);
269
for (size_t ii = 0; ii < numPoints; ++ii)
272
CGPoint p0 = outlinePoints[ii];
273
CGPoint p1 = points[ii];
274
CGPoint p2 = outlinePoints[ii+numPoints];
275
ovar->vertices[vi+0].pos[0] = p0.x;
276
ovar->vertices[vi+0].pos[1] = p0.y;
277
ovar->vertices[vi+1].pos[0] = p1.x;
278
ovar->vertices[vi+1].pos[1] = p1.y;
279
ovar->vertices[vi+2].pos[0] = p2.x;
280
ovar->vertices[vi+2].pos[1] = p2.y;
282
ovar->vertices[vi+0].normal[2] = 1.0;
283
ovar->vertices[vi+1].normal[2] = 1.0;
284
ovar->vertices[vi+2].normal[2] = 1.0;
285
ovar->vertices[vi+0].texcoord[0] = 0.0f;
286
ovar->vertices[vi+1].texcoord[0] = 0.5f;
287
ovar->vertices[vi+2].texcoord[0] = 1.0f;
290
ovar->numIndices = 12*(numPoints);
291
ovar->indices = calloc(sizeof(*ovar->indices), ovar->numIndices);
295
for (size_t ii = 0; ii < numPoints; ++ii)
298
ovar->indices[k++] = (2*ii+0) % (numPoints*2);
299
ovar->indices[k++] = (2*ii+1) % (numPoints*2);
300
ovar->indices[k++] = (2*ii+2) % (numPoints*2);
301
ovar->indices[k++] = (2*ii+3) % (numPoints*2);
302
ovar->indices[k++] = (2*ii+2) % (numPoints*2);
303
ovar->indices[k++] = (2*ii+1) % (numPoints*2);
305
ovar->indices[k++] = (3*ii+0) % (numPoints*3);
306
ovar->indices[k++] = (3*ii+1) % (numPoints*3);
307
ovar->indices[k++] = (3*ii+3) % (numPoints*3);
308
ovar->indices[k++] = (3*ii+4) % (numPoints*3);
309
ovar->indices[k++] = (3*ii+3) % (numPoints*3);
310
ovar->indices[k++] = (3*ii+1) % (numPoints*3);
312
ovar->indices[k++] = (3*ii+1) % (numPoints*3);
313
ovar->indices[k++] = (3*ii+2) % (numPoints*3);
314
ovar->indices[k++] = (3*ii+4) % (numPoints*3);
315
ovar->indices[k++] = (3*ii+5) % (numPoints*3);
316
ovar->indices[k++] = (3*ii+4) % (numPoints*3);
317
ovar->indices[k++] = (3*ii+2) % (numPoints*3);
321
ovar->mode = GL_TRIANGLES;
323
ovar->mode = GL_LINES;
325
//ovar->numIndices = 0;
327
VertexArray* ovar = ThickenOutline(points, numPoints, 2.0f, kThickenNoSplitting | kThickenInside | kThickenOutside);
329
[ovar applyVertexTransform: mtranslate(vcreate(-drawpos.x, -drawpos.y, 0.0f))];
330
[[jig outlineDrawables] addObject: ovar];
342
for (int i = 0; i < pieces.hcells; ++i)
344
for (int j = 0; j < pieces.vcells; ++j)
346
JigsawPiece* jig = [jigsawPieces objectAtIndex: k];
350
JigsawPiece* leftn = [jigsawPieces objectAtIndex: k - vcells];
352
JigsawNeighbour* n = [[[JigsawNeighbour alloc] init] autorelease];
353
[n setAttachmentPoint: CGPointSub(pieces.cells[i][j].attachmentPoints[2], pieces.cells[i][j].pos)];
355
[[jig neighbours] setObject: [NSMutableArray arrayWithObject: n] forKey: [NSValue valueWithPointer: leftn]];
357
if (i+1 < pieces.hcells)
359
JigsawPiece* rightn = [jigsawPieces objectAtIndex: k + vcells];
361
JigsawNeighbour* n = [[[JigsawNeighbour alloc] init] autorelease];
362
[n setAttachmentPoint: CGPointSub(pieces.cells[i][j].attachmentPoints[0], pieces.cells[i][j].pos)];
364
[[jig neighbours] setObject: [NSMutableArray arrayWithObject: n] forKey: [NSValue valueWithPointer: rightn]];
368
JigsawPiece* bottomn = [jigsawPieces objectAtIndex: k - 1];
370
JigsawNeighbour* n = [[[JigsawNeighbour alloc] init] autorelease];
371
[n setAttachmentPoint: CGPointSub(pieces.cells[i][j].attachmentPoints[3], pieces.cells[i][j].pos)];
373
[[jig neighbours] setObject: [NSMutableArray arrayWithObject: n] forKey: [NSValue valueWithPointer: bottomn]];
375
if (j+1 < pieces.vcells)
377
JigsawPiece* topn = [jigsawPieces objectAtIndex: k + 1];
379
JigsawNeighbour* n = [[[JigsawNeighbour alloc] init] autorelease];
380
[n setAttachmentPoint: CGPointSub(pieces.cells[i][j].attachmentPoints[1], pieces.cells[i][j].pos)];
382
[[jig neighbours] setObject: [NSMutableArray arrayWithObject: n] forKey: [NSValue valueWithPointer: topn]];
390
MersenneTwister* rgen = [MersenneTwister sharedTwister];
391
for (JigsawPiece* jig in jigsawPieces)
393
CGPoint randomPos = CGPointMake([rgen randomFloatWithFloor: -0.5f*bounds.size.width ceiling: 0.5f*bounds.size.width], [rgen randomFloatWithFloor: -0.5f*bounds.size.height ceiling: 0.5f*bounds.size.height]);
394
[jig setPosition: randomPos];
395
[jig setOrientation: [rgen randomFloatWithFloor: 0.0f ceiling: (float)(2.0*M_PI)]];
398
FreeJigPieces(pieces);
400
piecesChangedSinceLastRedraw = YES;
403
- (void) setupGraphicsForApi: (EAGLRenderingAPI) glApi
405
glClearColor(0.38f,0.38f,0.38f,1.0f);
407
//CGImageRef image = [UIImage imageNamed: @"coding1.jpg"].CGImage;
408
//CGImageRef image = [UIImage imageNamed: @"white.png"].CGImage;
410
[self doubleGradientTexture: &blackGradientTexture red: 0.0f green: 0.0f blue: 0.0f];
411
[self doubleGradientTexture: &whiteGradientTexture red: 1.0f green: 1.0f blue: 1.0f];
413
[self loadTextureFromPDFNamed: @"move-icon.pdf" texName: &moveIconTexture width: 32 height: 32 repeat: NO mipmap: NO filter: NO];
414
[self loadTextureFromPDFNamed: @"rotate-icon.pdf" texName: &rotateIconTexture width: 32 height: 32 repeat: NO mipmap: NO filter: NO];
415
[self loadTextureFromPDFNamed: @"scale-icon.pdf" texName: &scaleIconTexture width: 32 height: 32 repeat: NO mipmap: NO filter: NO];
416
[self loadTextureFromBitmapNamed: @"white.png" texName: &whiteTexture repeat: NO mipmap: NO filter: NO];
418
if ([[EAGLContext currentContext] API] == kEAGLRenderingAPIOpenGLES2)
420
jigsawShader = [[[ESShader shader] initWithVertexShaderFile: @"jigsawPiece.vs" fragmentShaderFile: @"jigsawPiece.fs"] retain];
424
jigsawShader = [[[ESShader shader] init] retain];
427
//CGRect bounds = [self bounds];
428
oneTouchRotationThreshold = 10.0f;
429
oneTouchRotationFactor = 0.02f;
430
zoomTouchThreshold = 0.0f;
431
// zoomCenter = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
432
zoomCenter = CGPointZero;
435
targetGhostSize = 160.0f;
437
// [self generateJigsaw];
441
static inline CGPoint cpvToCGPoint(cpVect p)
443
return CGPointMake(p.x, p.y);
446
static inline cpVect CGPointToCpv(CGPoint p)
448
return cpv(p.x, p.y);
451
- (BOOL) checkPiece: (JigsawPiece*) piece0 againstPiece: (JigsawPiece*) piece1
453
NSArray* connections0 = [[piece0 neighbours] objectForKey: [NSValue valueWithPointer: piece1]];
455
{ // if the object exists, its established the piece can be connected
456
// next step is to find matching connection records
457
NSArray* connections1 = [[piece1 neighbours] objectForKey: [NSValue valueWithPointer: piece0]];
458
for (JigsawNeighbour* n0 in connections0)
460
for (JigsawNeighbour* n1 in connections1)
462
if (([n0 direction] != [n1 direction]) && (([n0 direction] + [n1 direction]) % 2 == 0))
464
CGAffineTransform R0 = CGAffineTransformMakeRotation([piece0 orientation]);
465
CGAffineTransform R1 = CGAffineTransformMakeRotation([piece1 orientation]);
467
float da = fmodf([piece1 orientation] - [piece0 orientation] + 2.0*M_PI, 2.0*M_PI);
471
CGPoint r0 = CGPointTransform([n0 attachmentPoint], R0);
472
CGPoint r1 = CGPointTransform([n1 attachmentPoint], R1);
475
CGPoint x0 = CGPointAdd([piece0 position], r0);
476
CGPoint x1 = CGPointAdd([piece1 position], r1);
478
float snapRR = 10.0*10.0/(zoomFactor*zoomFactor);
480
float realRR = CGPointDoubleDot(CGPointSub(x1,x0));
482
//if ((snapRR > realRR))
483
if ((fabsf(da) < 15.0f/180.0f*M_PI) && (snapRR > realRR))
493
- (void) animateUnhighlightPiece: (JigsawPiece*) piece withZoom: (BOOL) withZoom
495
float duration = 0.3f;
496
id anim = [[[ElV3Animation alloc] init] autorelease];
497
[anim setSetter: @selector(setOutlineColor:)];
498
[anim setTarget: piece];
499
[anim setStartv: vcreate(1.0f,1.0f,1.0f)];
500
[anim setEndv: vcreate(0.0f,0.0f,0.0f)];
501
[anim setDuration: duration];
503
[anim queueForKey: [NSString stringWithFormat: @"pieceAnimation %p", piece] atBeginning: NO cancelPending: NO];
505
anim = [[[ElCallbackAnimation alloc] init] autorelease];
506
[anim setCallback: @selector(piecesChanged)];
507
[anim setTarget: self];
508
[anim setDuration: duration];
510
[anim queueForKey: [NSString stringWithFormat: @"pieceAnimationRefresh %p", piece] atBeginning: NO cancelPending: NO];
514
anim = [[[ElFloatAnimation alloc] init] autorelease];
515
[anim setProperty: @"ghostZoom"];
516
[anim setTarget: piece];
517
[anim setStartValue: [piece ghostZoom]];
518
[anim setEndValue: 1.0f];
519
[anim setDuration: duration];
521
[anim queueForKey: [NSString stringWithFormat: @"pieceZoom %p", piece] atBeginning: NO cancelPending: NO];
524
- (void) animateHighlightPiece: (JigsawPiece*) piece withZoom: (BOOL) withZoom
526
float duration = 0.2f;
527
id anim = [[[ElV3Animation alloc] init] autorelease];
528
[anim setSetter: @selector(setOutlineColor:)];
529
[anim setTarget: piece];
530
[anim setStartv: vcreate(0.0f,0.0f,0.0f)];
531
[anim setEndv: vcreate(1.0f,1.0f,1.0f)];
532
[anim setDuration: duration];
534
[anim queueForKey: [NSString stringWithFormat: @"pieceAnimation %p", piece] atBeginning: NO cancelPending: NO];
538
anim = [[[ElFloatAnimation alloc] init] autorelease];
539
[anim setProperty: @"ghostZoom"];
540
[anim setTarget: piece];
541
[anim setStartValue: 1.0f];
542
[anim setEndValue: targetGhostSize/pieceSize/zoomFactor > 1.41f ? targetGhostSize/pieceSize/zoomFactor : 1.0f];
543
[anim setDuration: duration];
545
[anim queueForKey: [NSString stringWithFormat: @"pieceZoom %p", piece] atBeginning: NO cancelPending: NO];
548
anim = [[[ElCallbackAnimation alloc] init] autorelease];
549
[anim setCallback: @selector(piecesChanged)];
550
[anim setTarget: self];
551
[anim setDuration: duration];
553
[anim queueForKey: [NSString stringWithFormat: @"pieceAnimationRefresh %p", piece] atBeginning: NO cancelPending: NO];
557
- (void) snapPiece: (JigsawPiece*) piece toPiece: (JigsawPiece*) lump
559
CGPoint d = CGPointSub([piece initialPos], [lump initialPos]);
560
CGAffineTransform T = CGAffineTransformMakeTranslation(d.x, d.y);
561
m16 TM = mtranslate(vcreate(d.x, d.y, 0.0));
563
NSValue* lkey = [NSValue valueWithPointer: lump];
564
NSValue* pkey = [NSValue valueWithPointer: piece];
566
for (VertexArray* var in [piece fillDrawables])
567
[var applyVertexTransform: TM];
568
for (VertexArray* var in [piece outlineDrawables])
569
[var applyVertexTransform: TM];
571
[[lump fillDrawables] addObjectsFromArray: [piece fillDrawables]];
572
[[lump outlineDrawables] addObjectsFromArray: [piece outlineDrawables]];
574
for (id outline in [piece outlines])
576
CGPathRef path = CreateTransformedPathFromCGPath((CGPathRef)outline, T);
577
[[lump outlines] addObject: (id)path];
581
// update neighbours of old piece ot point to new, combined one
582
for (id nkey in [piece neighbours])
584
JigsawPiece* npiece = [nkey pointerValue];
588
// update neighbours of piece to point to lump
589
NSArray* oldNeighbourConnections = [[npiece neighbours] objectForKey: pkey];
590
[[npiece neighbours] setObject: oldNeighbourConnections forKey: lkey];
591
[[npiece neighbours] removeObjectForKey: pkey];
593
// update pointers from piece to its neighbours with new coordinates
594
NSMutableArray* neighbours = [[piece neighbours] objectForKey: nkey];
595
for (JigsawNeighbour* n in neighbours)
597
[n setAttachmentPoint: CGPointAdd([n attachmentPoint], d)];
602
NSMutableArray* existingNeighbours = [[lump neighbours] objectForKey: nkey];
603
if (!existingNeighbours)
604
[[lump neighbours] setObject: neighbours forKey: nkey];
606
[existingNeighbours addObjectsFromArray: neighbours];
609
[[lump neighbours] removeObjectForKey: pkey];
610
[jigsawPieces removeObject: piece];
611
if (selectedPiece == piece)
613
[self animateHighlightPiece: lump withZoom: YES];
614
selectedPiece = lump;
619
- (BOOL) snapPieceIfRequired: (JigsawPiece*) piece0
621
for (id key0 in [[[piece0 neighbours] copy] autorelease])
623
JigsawPiece* snappedPiece = [key0 pointerValue];
625
if ([self checkPiece: piece0 againstPiece: snappedPiece])
627
[self snapPiece: piece0 toPiece: snappedPiece];
634
- (m16) inverseCanvasTransform
636
//CGRect bounds = [self bounds];
637
v3 c = vcreate(zoomCenter.x, zoomCenter.y, 0.0f);
639
//m16 M = mmul(mtranslate(c), mmul(mscale(vcreate(zoomFactor, zoomFactor, 1.0)), mtranslate(vmul(c, -1.0f))));
640
m16 M = mmul(mtranslate(c), mmul(mscale(vcreate(1.0f/zoomFactor, 1.0f/zoomFactor, 1.0)), mtranslate(vmul(c, -1.0f))));
645
- (m16) canvasTransform
647
//CGRect bounds = [self bounds];
648
v3 c = vcreate(zoomCenter.x, zoomCenter.y, 0.0f);
650
m16 M = mmul(mtranslate(c), mmul(mscale(vcreate(zoomFactor, zoomFactor, 1.0)), mtranslate(vmul(c, -1.0f))));
656
- (CGAffineTransform) inverseCanvasTransformCG
658
m16 M = [self inverseCanvasTransform];
660
CGAffineTransform m = {M.m[0][0], M.m[0][1], M.m[1][0], M.m[1][1], M.m[3][0], M.m[3][1]};
665
- (CGAffineTransform) inverseViewTransformCG
667
CGRect bounds = [self bounds];
668
CGAffineTransform m = {1.0, 0.0, 0.0, -1.0, -0.5*bounds.size.width, 0.5*bounds.size.height};
673
- (void) drawGhostPiece: (JigsawPiece*) piece withZoom: (float) ghostZoom
678
- (void) drawPiece: (JigsawPiece*) piece tableMatrix: (m16*) tableM textureMatrix: (m16*) textureM gradientMatrix: (m16*) gradientM zoom: (float) pieceZoom alpha: (float) pieceAlpha
680
CGPoint ppos = [piece position];
681
float a = [piece orientation];
682
m16 R = mcreatefrombases(vcreate(cosf(a), sinf(a), 0.0), vcreate(-sinf(a), cosf(a), 0.0), vcreate(0.0,0.0,1.0));
683
m16 pieceTransform = mmul(*tableM, mmul(mtranslate(vcreate(ppos.x, ppos.y,0.0)), R));
684
//m16 pieceTransform = mtranslate(vcreate(0.0, 0.0,0.0));
686
pieceTransform = mmul(pieceTransform, mscale(vcreate(pieceZoom, pieceZoom, 1.0)));
689
[jigsawShader setModelviewMatrix: &pieceTransform];
691
glBindTexture(GL_TEXTURE_2D, imageTexture);
692
[jigsawShader setColorRed: pieceAlpha green: pieceAlpha blue: pieceAlpha alpha: pieceAlpha];
693
[jigsawShader setTextureMatrix: textureM];
694
for (VertexArray* var in [piece fillDrawables])
698
if (selectedPiece == piece)
699
glBindTexture(GL_TEXTURE_2D, whiteGradientTexture);
701
glBindTexture(GL_TEXTURE_2D, blackGradientTexture);
703
glBindTexture(GL_TEXTURE_2D, whiteGradientTexture);
704
v3 outlineColor = [piece outlineColor];
705
[jigsawShader setColorRed: outlineColor.v.x*pieceAlpha green: outlineColor.v.y*pieceAlpha blue: outlineColor.v.z*pieceAlpha alpha: pieceAlpha];
708
glBindTexture(GL_TEXTURE_2D, whiteTexture);
709
glDisable(GL_DEPTH_TEST);
712
[jigsawShader setTextureMatrix: gradientM];
713
for (VertexArray* var in [piece outlineDrawables])
721
CGRect bounds = [self bounds];
723
m16 tableM = [self canvasTransform];
724
m16 viewM = mortho(-0.5f*bounds.size.width, 0.5f*bounds.size.width, -0.5f*bounds.size.height, 0.5f*bounds.size.height, 1.0, -1.0);
725
//m16 viewM = mortho(CGRectGetMinX(bounds), CGRectGetMaxX(bounds), CGRectGetMinY(bounds), CGRectGetMaxY(bounds), 1.0, -1.0);
726
m16 textureM = mscale(vcreate(1.0/imageSize.width,-1.0/imageSize.height,1.0));
727
textureM.m[3][0] = 0.5f;
728
textureM.m[3][1] = 0.5f;
729
[self setupViewDrawing];
731
glDisable(GL_CULL_FACE);
732
glEnable(GL_TEXTURE_2D);
733
glDisable(GL_DEPTH_TEST);
735
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
737
glBindTexture(GL_TEXTURE_2D, imageTexture);
739
[jigsawShader bindShader];
741
[jigsawShader setColorRed: 1.0f green: 1.0f blue: 1.0f alpha: 1.0f];
743
[jigsawShader setTextureMatrix: &I];
744
[jigsawShader setProjectionMatrix: &I];
745
[jigsawShader setModelviewMatrix: &I];
747
// [[VertexArray sharedDisk] draw];
749
[jigsawShader setTextureMatrix: &textureM];
750
[jigsawShader setProjectionMatrix: &viewM];
752
// for (VertexArray* var in pieceVertexArrays)
755
m16 gradientM = mmul(mtranslate(vcreate(0.5f - 1.0f*zoomFactor, 0.0f, 0.0f)), mscale(vcreate(2.0f*zoomFactor, 1.0f, 1.0f)));
756
for (JigsawPiece* piece in [jigsawPieces reverseObjectEnumerator])
758
if ((selectedPiece == piece) && ([[piece outlines] count] < 2))
759
[self drawPiece: piece tableMatrix: &tableM textureMatrix: &textureM gradientMatrix: &gradientM zoom: [piece ghostZoom] alpha: 0.2f];
760
[self drawPiece: piece tableMatrix: &tableM textureMatrix: &textureM gradientMatrix: &gradientM zoom: 1.0f alpha: 1.0f];
764
piecesChangedSinceLastRedraw = NO;
768
m16 quadS = mscale(vcreate(16.0,16.0,1.0));
769
m16 quadT = mtranslate(vcreate(0.5*bounds.size.width+16.0, 0.5*bounds.size.height-16.0, 0.0));
771
m16 T = mtranslate(vcreate(-32.0f, 0.0f, 0.0f));
773
m16 quadM = mmul(quadT, quadS);
775
[jigsawShader setTextureMatrix: &I];
776
[jigsawShader setColorRed: 0.5f green: 0.5f blue: 0.5f alpha: 0.5f];
777
glBlendFunc(GL_ONE, GL_ONE);
781
quadM = mmul(T, quadM);
782
[jigsawShader setModelviewMatrix: &quadM];
783
glBindTexture(GL_TEXTURE_2D, moveIconTexture);
784
[[VertexArray sharedQuad] draw];
786
if (((fabsf(oneTouchRotationAccumulator) > oneTouchRotationThreshold) || secondaryTouch) && selectedPiece)
788
quadM = mmul(T, quadM);
789
//float strength = 0.5f*fminf(fabsf(oneTouchRotationAccumulator*0.01), 1.0);
790
float strength = 0.5f;
791
[jigsawShader setColorRed: strength green: strength blue: strength alpha: strength];
792
[jigsawShader setModelviewMatrix: &quadM];
793
glBindTexture(GL_TEXTURE_2D, rotateIconTexture);
794
[[VertexArray sharedQuad] draw];
796
if (initialTouch && secondaryTouch && !selectedPiece)
798
quadM = mmul(T, quadM);
799
//float strength = 0.5f*fminf(fabsf(oneTouchRotationAccumulator*0.01), 1.0);
800
float strength = 0.5f;
801
[jigsawShader setColorRed: strength green: strength blue: strength alpha: strength];
802
[jigsawShader setModelviewMatrix: &quadM];
803
glBindTexture(GL_TEXTURE_2D, scaleIconTexture);
804
[[VertexArray sharedQuad] draw];
808
[self finishViewDrawing];
811
- (void) animationCallback: (id) info
814
// cpSpaceStep(space, h);
816
float delta = [[info objectForKey: AnimationDeltaTimeKey] floatValue];
818
zoomTouchAccumulator *= 1.0f - 0.5f*delta;
819
nonZoomTouchAccumulator *= 1.0f - 0.5f*delta;
823
oneTouchRotationAccumulator *= 1.0f - fclampf(10.0*delta, 0.0, 1.0);
825
if ((fabsf(oneTouchRotationAccumulator) > oneTouchRotationThreshold) && (fabs(oneTouchRotationCirclelation) > 2.0*M_PI))
827
float touchy = oneTouchRotationAccumulator > 0 ? oneTouchRotationAccumulator - oneTouchRotationThreshold : oneTouchRotationAccumulator + oneTouchRotationThreshold;
828
[selectedPiece rotateByAngle: delta*touchy*oneTouchRotationFactor aroundPoint: oneTouchRotationCenter];
829
[self piecesChanged];
832
if (piecesChangedSinceLastRedraw)
836
- (void) secondaryTouch: (UITouch*) touch
838
CGAffineTransform vm = [self inverseViewTransformCG];
839
CGAffineTransform cm = [self inverseCanvasTransformCG];
840
CGPoint p0 = CGPointApplyAffineTransform([initialTouch locationInView: self], vm);
841
CGPoint cp0 = CGPointTransform(p0, cm);
843
CGPoint p1 = CGPointApplyAffineTransform([touch locationInView: self], vm);
844
CGPoint cp1 = CGPointTransform(p1, cm);
846
[secondaryTouch release];
847
secondaryTouch = [touch retain];
849
zoomTouchReference = CGPointScale(CGPointAdd(cp0,cp1), 0.5f);
854
rotationTouchLastVector = CGPointSub(p1, p0);
855
rotationTouchInitialAngle = [selectedPiece orientation];
860
- (void) initialTouch: (UITouch*) touch
862
CGAffineTransform vm = [self inverseViewTransformCG];
863
CGAffineTransform cm = [self inverseCanvasTransformCG];
864
CGPoint p = CGPointApplyAffineTransform([touch locationInView: self], vm);
865
CGPoint cp = CGPointTransform(p, cm);
867
// NSLog(@"touch down %.1f %.1f", p.x, p.y);
869
oneTouchRotationAccumulator = 0.0f;
870
oneTouchRotationDelta = CGPointZero;
872
oneTouchRotationCirclelation = 0.0f;
876
[self animateUnhighlightPiece: selectedPiece withZoom: YES];
881
[self piecesChanged];
884
for (JigsawPiece* piece in jigsawPieces)
886
CGPoint pos = [piece position];
887
float alpha = [piece orientation];
888
// NSLog(@"pos %.1f %.1f", pos.x, pos.y);
889
CGAffineTransform R = CGAffineTransformMakeRotation(-alpha);
890
CGAffineTransform T = CGAffineTransformMakeTranslation(-pos.x, -pos.y);
891
CGPoint lp = CGPointTransform(cp, T);
892
lp = CGPointTransform(lp, R);
894
for (id _outline in [piece outlines])
896
CGPathRef outline = (CGPathRef)_outline;
898
if (CGPathContainsPoint(outline, NULL, lp, NO))
900
selectedPiece = piece;
901
// NSLog(@"selected piece");
912
[jigsawPieces removeObject: [selectedPiece retain]];
913
[jigsawPieces insertObject: [selectedPiece autorelease] atIndex: 0];
914
//v3 white = vcreate(1.0f,1.0f,1.0f);
915
//[selectedPiece setOutlineColor: &white];
917
[self animateHighlightPiece: selectedPiece withZoom: YES];
919
//mouseConstraint = cpPinJointInit(mouseConstraint, mouseBody, [selectedPiece body], cpv(0.0,0.0), cpv(0.0,0.0));
920
//cpSpaceAddConstraint(space, (cpConstraint*)mouseConstraint);
923
[initialTouch release];
924
initialTouch = [touch retain];
927
- (void) touchesBegan: (NSSet*) touches withEvent: (UIEvent*) event
932
[self initialTouch: [touches anyObject]];
934
else if (!secondaryTouch)
936
NSMutableSet* secondaryTouches = [touches mutableCopy];
937
[secondaryTouches removeObject: initialTouch];
938
UITouch* touch = [secondaryTouches anyObject];
939
[self secondaryTouch: touch];
943
- (void) touchesMoved: (NSSet*) touches withEvent: (UIEvent*) event
945
CGAffineTransform vm = [self inverseViewTransformCG];
946
CGAffineTransform cm = [self inverseCanvasTransformCG];
947
CGPoint p0 = CGPointApplyAffineTransform([initialTouch locationInView: self], vm);
948
CGPoint cp0 = CGPointTransform(p0, cm);
949
CGPoint pp0 = CGPointApplyAffineTransform([initialTouch previousLocationInView: self], vm);
950
CGPoint cpp0 = CGPointTransform(pp0, cm);
952
CGPoint p1 = CGPointApplyAffineTransform([secondaryTouch locationInView: self], vm);
953
CGPoint cp1 = CGPointTransform(p1, cm);
954
CGPoint pp1 = CGPointApplyAffineTransform([secondaryTouch previousLocationInView: self], vm);
955
CGPoint cpp1 = CGPointTransform(pp1, cm);
957
CGPoint delta0 = CGPointSub(p0,pp0);
958
CGPoint delta1 = CGPointSub(p1,pp1);
961
CGPoint cdelta0 = CGPointSub(cp0,cpp0);
962
CGPoint cdelta1 = CGPointSub(cp1,cpp1);
964
if (initialTouch && selectedPiece)
967
oneTouchRotationCenter = cp0;
971
[selectedPiece setPosition: CGPointAdd([selectedPiece position], cdelta0)];
973
float sinDelta = CGPointCross(oneTouchRotationDelta, delta0);
974
float cosDelta = CGPointDot(oneTouchRotationDelta, delta0);
976
float length = (CGPointLength(delta0)*CGPointLength(oneTouchRotationDelta));
977
if (length > FLT_EPSILON)
979
float cs = 1.0f/length;
980
float angle = atan2(sinDelta/cs, cosDelta/cs);
984
//NSLog(@"angle %.2f", 180.0/M_PI*angle);
986
if (fabs(oneTouchRotationCirclelation) <= 4.0*M_PI)
987
oneTouchRotationCirclelation += angle;
989
//NSLog(@"buffer %.2f", 180.0/M_PI*oneTouchRotationCirclelation);
992
if (fabs(oneTouchRotationCirclelation) > 2.0*M_PI)
993
oneTouchRotationAccumulator += sinDelta;
996
oneTouchRotationDelta = delta0;
1004
CGPoint rv = CGPointSub(cp1, cp0);
1006
float alpha = atan2(rv.y,rv.x) - atan2(rotationTouchLastVector.y, rotationTouchLastVector.x);
1008
//[selectedPiece setPosition: CGPointAdd([selectedPiece position], cdelta0)];
1009
//[selectedPiece rotateByAngle: alpha aroundPoint: cp0];
1010
[selectedPiece setPosition: CGPointAdd([selectedPiece position], CGPointScale(CGPointAdd(cdelta0, cdelta1), 0.5f))];
1011
//[selectedPiece rotateByAngle: alpha aroundPoint: CGPointScale(CGPointAdd(cp0, cp1), 0.5f)];
1012
[selectedPiece rotateByAngle: alpha aroundPoint: [selectedPiece position]];
1015
rotationTouchLastVector = rv;
1019
if (initialTouch && secondaryTouch)
1021
CGPoint zc0 = CGPointScale(CGPointAdd(p0, p1), 0.5f);
1022
CGPoint zcp0 = CGPointScale(CGPointAdd(pp0, pp1), 0.5f);
1023
CGPoint zoomCenterMovement = CGPointSub(zc0, zcp0);
1025
CGPoint zoomDelta = CGPointSub(delta0, delta1);
1026
CGPoint zoomAxis = CGPointSub(pp0, pp1);
1027
CGPoint newZoomAxis = CGPointSub(p0, p1);
1029
float minZoomFactor = 0.5;
1030
float maxZoomFactor = 20.0;
1032
float zoomTouchFactor = fabsf(CGPointDot(zoomDelta, CGPointNormalize(zoomAxis)));
1033
float nonZoomTouchFactor = fabsf(CGPointCross(zoomDelta, CGPointNormalize(zoomAxis)));
1034
zoomTouchAccumulator += zoomTouchFactor;
1035
nonZoomTouchAccumulator += nonZoomTouchFactor;
1037
if (!selectedPiece || ((zoomTouchAccumulator > zoomTouchThreshold) && (zoomTouchAccumulator > 2.0*nonZoomTouchAccumulator)))
1040
[self animateUnhighlightPiece: selectedPiece withZoom: YES];
1042
selectedPiece = nil;
1043
float scaleFactor = CGPointLength(newZoomAxis)/(CGPointLength(zoomAxis));
1044
scaleFactor = fclampf(zoomFactor*scaleFactor, minZoomFactor, maxZoomFactor)/zoomFactor;
1046
float newZoomFactor = zoomFactor*scaleFactor;
1049
CGPoint newZoomCenter = CGPointScale(CGPointAdd(CGPointAdd(zoomCenterMovement, CGPointScale(zoomTouchReference, (1.0-scaleFactor)*zoomFactor)), CGPointScale(zoomCenter, 1.0-zoomFactor)),
1050
1.0f/(1.0-scaleFactor*zoomFactor));
1052
//CGPoint newZoomCenter = CGPointAdd(zoomCenter, CGPointScale(CGPointSub(zoomTouchCenter, zoomCenter), scaleFactor));
1054
//if (fabsf(1.0f-scaleFactor) > 10.0f*FLT_EPSILON);
1055
zoomCenter = newZoomCenter;
1056
zoomFactor = newZoomFactor;
1061
[self piecesChanged];
1065
- (void) touchesEnded: (NSSet*) touches withEvent: (UIEvent*) event
1069
if ([touches containsObject: initialTouch])
1071
[secondaryTouch release];
1072
secondaryTouch = nil;
1074
[initialTouch release];
1078
[self snapPieceIfRequired: selectedPiece];
1081
[self animateUnhighlightPiece: selectedPiece withZoom: YES];
1083
selectedPiece = nil;
1084
[self piecesChanged];
1086
else if ([touches containsObject: secondaryTouch])
1088
[secondaryTouch release];
1089
secondaryTouch = nil;
1095
- (void) touchesCancelled: (NSSet*) touches withEvent: (UIEvent*) event
1099
if ([touches containsObject: initialTouch])
1101
[secondaryTouch release];
1102
secondaryTouch = nil;
1104
[initialTouch release];
1108
[self animateUnhighlightPiece: selectedPiece withZoom: YES];
1109
selectedPiece = nil;
1111
else if ([touches containsObject: secondaryTouch])
1113
[secondaryTouch release];
1114
secondaryTouch = nil;
1117
[self piecesChanged];
1120
- (void) piecesChanged
1122
piecesChangedSinceLastRedraw = YES;
1125
- (void) saveJigsawState
1127
NSMutableData* data = [NSMutableData data];
1128
NSKeyedArchiver* archiver = [[[NSKeyedArchiver alloc] initForWritingWithMutableData: data] autorelease];
1130
[archiver encodeObject: jigsawPieces forKey: @"jigsawPieces"];
1131
[archiver encodeFloat: pieceSize forKey: @"pieceSize"];
1132
[archiver finishEncoding];
1134
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
1136
[defaults setObject: data forKey: @"jigsawGameState"];
1140
- (void) restoreJigsawState
1142
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
1143
NSData* data = [defaults objectForKey: @"jigsawGameState"];
1144
NSKeyedUnarchiver* archiver = [[[NSKeyedUnarchiver alloc] initForReadingWithData: data] autorelease];
1148
glDeleteTextures(1, &imageTexture);
1151
UIImage* image = [UIImage imageWithData: [defaults objectForKey: JigsawImageKey]];
1153
imageSize = CGSizeFitIntoSize([image size], CGSizeMake(400.0f, 400.0f));
1155
[self textureFromCgImage: [image CGImage] texName: &imageTexture width: 512 height: 512 repeat: NO mipmap: YES filter: YES];
1157
NSArray* pieces = [archiver decodeObjectForKey: @"jigsawPieces"];
1158
pieceSize = [archiver decodeFloatForKey: @"pieceSize"];
1160
[archiver finishDecoding];
1162
[jigsawPieces release];
1163
selectedPiece = nil;
1165
jigsawPieces = [pieces mutableCopy];
1167
piecesChangedSinceLastRedraw = YES;