GLKMathUnproject : detecting a tap on an object - ios

So, it seems that GLKit has GLKMathUnproject which can be used to work out where you clicked in 3D space (awesome)
However, I cant get it to work, I have copied a few different examples in and it still does not detect me clicking on my cube at 0,0,0. I basically do a for next loop and see if my ray hits my cube.
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSSet* allTouches = [event allTouches];
UITouch*touch1 = [[allTouches allObjects] objectAtIndex:0];
CGPoint touch1Point = [touch1 locationInView:self.view];
GLKVector3 window_coord = GLKVector3Make(touch1Point.x,touch1Point.y, 0.0f);
bool result;
GLint viewport[4] = {};
glGetIntegerv(GL_VIEWPORT, viewport);
GLKVector3 near_pt = GLKMathUnproject(window_coord, _baseModelViewMatrix, _projectionMatrix, &viewport[0], &result);
window_coord = GLKVector3Make(touch1Point.x,touch1Point.y, 1.0f);
GLKVector3 far_pt = GLKMathUnproject(window_coord, _baseModelViewMatrix, _projectionMatrix, &viewport[0], &result);
//need to get z=0 from
//assumes near and far are on opposite sides of z=0
float z_magnitude = fabs(far_pt.z-near_pt.z);
float near_pt_factor = fabs(near_pt.z)/z_magnitude;
float far_pt_factor = fabs(far_pt.z)/z_magnitude;
GLKVector3 final_pt = GLKVector3Add( GLKVector3MultiplyScalar(near_pt, far_pt_factor), GLKVector3MultiplyScalar(far_pt, near_pt_factor));
float xDif = (final_pt.x - near_pt.x) / 1000;
float yDif = (final_pt.y - near_pt.y) / 1000;
float zDif = (final_pt.z - near_pt.z) / 1000;
for (int i = 0; i < 100; i ++)
{
if ((near_pt.x + (xDif * i)) > self.cube.position.x - self.cube.scale.x && (near_pt.x + (xDif * i)) < self.cube.position.x + self.cube.scale.x &&
(near_pt.y + (yDif * i)) > self.cube.position.y - self.cube.scale.y && (near_pt.y + (yDif * i)) < self.cube.position.y + self.cube.scale.y &&
(near_pt.z + (zDif * i)) > self.cube.position.z - self.cube.scale.z && (near_pt.z + (zDif * i)) < self.cube.position.z + self.cube.scale.z)
{
NSLog(#"%f %f %f", final_pt.x, final_pt.y, final_pt.z);
NSLog(#"Hit cube");
}
}
}

I found the answer here
Updating OpenGL ES Touch Detection (Ray Tracing) for iPad Retina?
- (void)handleTap: (UITapGestureRecognizer *)recognizer
{
CGPoint tapLoc = [recognizer locationInView:self.view];
tapLoc.x *= [UIScreen mainScreen].scale;
tapLoc.y *= [UIScreen mainScreen].scale;
bool testResult;
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
float uiKitOffset = 113; //Need to factor in the height of the nav bar + the height of the tab bar at the bottom in the storyboard.
GLKVector3 nearPt = GLKMathUnproject(GLKVector3Make(tapLoc.x, (tapLoc.y-viewport[3]+uiKitOffset)*-1, 0.0), modelViewMatrix, projectionMatrix, &viewport[0] , &testResult);
GLKVector3 farPt = GLKMathUnproject(GLKVector3Make(tapLoc.x, (tapLoc.y-viewport[3]+uiKitOffset)*-1, 1.0), modelViewMatrix, projectionMatrix, &viewport[0] , &testResult);
farPt = GLKVector3Subtract(farPt, nearPt);
....
}

Related

Rotating UIView (Again)

I'm encountering some problems with rotating a uiview again. This time, im trying to rotate a uiview 1/12 the speed of another uiview I'm rotating at normal speed. However, when I try to accomplish this task, the uiview I'm trying to move slower moves like this:
1st Update
https://www.youtube.com/watch?v=wj3nRJo5CMM&feature=youtu.be
2nd Update
https://www.youtube.com/watch?v=YLRkUzXSDtQ&feature=youtu.be
Here's my code:
- (void)rotateHand:(UIPanGestureRecognizer *)panGesture {
if ([(UIPanGestureRecognizer*)panGesture state] == UIGestureRecognizerStateBegan) {
CGPoint touchPoint = [panGesture locationInView:[self view]];
float dx = touchPoint.x - minHandContainer.center.x;
float dy = touchPoint.y - minHandContainer.center.y;
arcTanMin = atan2(dy,dx);
arcTanHour = atan2(hourHand.center.x - minHandContainer.center.x, hourHand.center.y - minHandContainer.center.y);
if (arcTanMin < 0) {
arcTanMin = 2 * M_PI + arcTanMin;
}
if (arcTanHour < 0) {
arcTanHour = 2 * M_PI + arcTanMin;
}
NSLog(#"arcTanMin %f", arcTanMin);
startTransformMin = minHandContainer.transform;
startTransformHour = hourHandContainer.transform;
}
if ([(UIPanGestureRecognizer*)panGesture state] == UIGestureRecognizerStateChanged) {
CGPoint pt = [panGesture locationInView:[self view]];
float dx = pt.x - minHandContainer.center.x;
float dy = pt.y - minHandContainer.center.y;
float ang = atan2(dy,dx);
if (ang < 0) {
ang = 2 * M_PI + ang;
}
float angleDifferenceM = arcTanMin - ang;
float angleDifferenceH = arcTanHour + angleDifferenceM * (1.0/12.0);
NSLog(#"angleDiffM %f", angleDifferenceM);
NSLog(#"angleDiffH %f", angleDifferenceH);
minHandContainer.transform = CGAffineTransformRotate(startTransformMin, -angleDifferenceM);
hourHandContainer.transform = CGAffineTransformRotate(startTransformHour, -angleDifferenceH);
}
}
It appears that you're using arcTanMin as the starting reference angle for both the minute hand and hour hand. So when you make the jump across the x-axis, both angleDifferenceM and angleDifferenceH are making the jump (which is why at the moment of the jump the angle of the hour hand to the y-axis is the same as the angle of the minute hand to the x-axis), but angleDifferenceH doesn't need to make the jump. Change this:
float angleDifferenceH = angleDifferenceM * (1.0/12.0);
to
float angleDifferenceH = arcTanHour + angleDifferenceM * (1.0/12.0);
with an appropriate starting value for arcTanHour.

Set angle of sprite (rotate) in cocos2d

I am new in cocos2d game development. I've set a Turret on the screen. When we touch the screen, the turrets fire bullets, but my turret's angle is not set correctly. This code is taken from the raywenderlich tutorial series. Also, bullets are not passing by the touched point.
Below is my code.
// Determine offset of location to projectile
CGPoint offset = ccpSub(cointouchLocation,turretArms.position);
NSLog(#"offset: %#",NSStringFromCGPoint(offset));
// Bail out if you are shooting down or backwards
if (offset.x <= 0) return;
int realX = winSize.width +(turretArms.contentSize.width/2);
float ratio = (float) offset.y/(float) offset.x;
int realY =(realX * ratio) +turretArms.position.y;
CGPoint realDest = ccp(realX, realY);
// Determine the length of how far you're shooting
int offRealX = realX - turretArms.position.x;
int offRealY = realY - turretArms.position.y;
float length = sqrtf((offRealX*offRealX)+(offRealY*offRealY));
float velocity = 480/1;
float realMoveDuration = length/velocity;
// Determine angle to face
float angleRadians = atanf((float)offRealY / (float)offRealX);
// NSLog(#"angleRadians : %f",angleRadians);
float angleDegrees = CC_RADIANS_TO_DEGREES(angleRadians);
float cocosAngle = -1 * angleDegrees;
float rotateDegreesPerSecond = 180 / 0.5; // Would take 0.5 seconds to rotate 180 degrees, or half a circle
float degreesDiff = turret.rotation - cocosAngle;
float rotateDuration = fabs(degreesDiff / rotateDegreesPerSecond);
[turret runAction:
[CCSequence actions:
[CCRotateTo actionWithDuration:rotateDuration angle:cocosAngle],
[CCCallBlock actionWithBlock:^{
// OK to add now - rotation is finished!
[self addChild:turretArms];
[turretsAry addObject:turretArms];
// Release
[turretArms release];
turretArms = nil;
}],
nil]];
// Move projectile to actual endpoint
[turretArms runAction:
[CCSequence actions:
[CCMoveTo actionWithDuration:realMoveDuration position:realDest],
[CCCallBlockN actionWithBlock:^(CCNode *node) {
[turretsAry removeObject:node];
[node removeFromParentAndCleanup:YES];
}],
nil]];
turretArms.tag =2;
//coin delete from window
for (CCSprite *cmonster in coinmonstersToDelete) {
// [monster stopAllActions];
[coinmonstersOnScreen removeObject:cmonster];
[self removeChild:cmonster cleanup:YES];
}
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if (_nextProjectile != nil) return;
// Choose one of the touches to work with
UITouch *touch = [touches anyObject];
CGPoint location = [self convertTouchToNodeSpace:touch];
// Set up initial location of projectile
CGSize winSize = [[CCDirector sharedDirector] winSize];
_nextProjectile = [[CCSprite spriteWithFile:#"projectile2-Hd.png"] retain];
_nextProjectile.position = ccp(winSize.width/2,20);
// Determine offset of location to projectile
CGPoint offset = ccpSub(location, _nextProjectile.position);
NSLog(#"offset.x : %f",offset.x);
NSLog(#"offset.x : %f",offset.y);
// Bail out if you are shooting down or backwards
if (offset.y <= 0) return;
int realY = winSize.height - (_nextProjectile.contentSize.width/2);
float ratio = (float) offset.x / (float) offset.y;
int realX = (realY * ratio) + _nextProjectile.position.x;
CGPoint realDest = ccp(realX, realY);
// Determine the length of how far you're shooting
int offRealX = realX - _nextProjectile.position.x;
int offRealY = realY - _nextProjectile.position.y;
float length = sqrtf((offRealX*offRealX)+(offRealY*offRealY));
float velocity = 480/1; // 480pixels/1sec
float realMoveDuration = length/velocity;
// Determine angle to face
// float angleRadians = atanf((float)offRealY / (float)offRealX);
float angleRadians = atanf((float)offRealX / (float)offRealY);
float angleDegrees = CC_RADIANS_TO_DEGREES(angleRadians);
// float cocosAngle = -1 * angleDegrees;
float cocosAngle = angleDegrees;
float rotateDegreesPerSecond = 180 / 0.5; // Would take 0.5 seconds to rotate 180 degrees, or half a circle
float degreesDiff = _player.rotation - cocosAngle;
float rotateDuration = fabs(degreesDiff / rotateDegreesPerSecond);
[_player runAction:
[CCSequence actions:
[CCRotateTo actionWithDuration:rotateDuration angle:cocosAngle],
[CCCallBlock actionWithBlock:^{
// OK to add now - rotation is finished!
[self addChild:_nextProjectile];
[_projectiles addObject:_nextProjectile];
// Release
[_nextProjectile release];
_nextProjectile = nil;
}],
nil]];
// Move projectile to actual endpoint
[_nextProjectile runAction:
[CCSequence actions:
[CCMoveTo actionWithDuration:realMoveDuration position:realDest],
[CCCallBlockN actionWithBlock:^(CCNode *node) {
[_projectiles removeObject:node];
[node removeFromParentAndCleanup:YES];
}],
nil]];
_nextProjectile.tag = 2;
// [[SimpleAudioEngine sharedEngine] playEffect:#"pew-pew-lei.caf"];
}

Draw shape of land on view by taking latitude and longitude in iOS?

I want to draw shape of small land on view by taking latitude and longitude at the corner of land.
I have wrote following code. For now I took hard core values.
- (void)drawRect:(CGRect)rect {
CGSize screenSize = [UIScreen mainScreen].applicationFrame.size;
SCALE = MIN(screenSize.width, screenSize.height) / (2.0 * EARTH_RADIUS);
OFFSET = MIN(screenSize.width, screenSize.height) / 2.0;
CGPoint latLong1 = {18.626103, 73.805023};
CGPoint latLong2 = {18.626444, 73.804884};
CGPoint latLong3 = {18.626226, 73.804969};
CGPoint latLong4 = {18.626103, 73.805023};
NSMutableArray *points=[NSMutableArray arrayWithObjects:[NSValue valueWithCGPoint:[self convertLatLongCoord:latLong1]],[NSValue valueWithCGPoint:[self convertLatLongCoord:latLong2]], [NSValue valueWithCGPoint:[self convertLatLongCoord:latLong3]],[NSValue valueWithCGPoint:[self convertLatLongCoord:latLong4]],nil];
CGContextRef ctx = UIGraphicsGetCurrentContext();
for(int i=0;i<points.count;i++)
{
// CGPoint newCoord = [self convertLatLongCoord:latLong];
NSValue *val = [points objectAtIndex:i];
CGPoint newCoord = [val CGPointValue];
if(i == 0)
{
// move to the first point
CGContextMoveToPoint(ctx, newCoord.x, newCoord.y);
}
else
{
CGContextAddLineToPoint(ctx, newCoord.x, newCoord.y);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 1);
CGContextSetStrokeColorWithColor(ctx, [[UIColor redColor] CGColor]);
}
}
CGContextStrokePath(ctx);
}
Below is method which converts lat long into x,y co-ordinates.
- (CGPoint)convertLatLongCoord:(CGPoint)latLong
{
CGFloat x = EARTH_RADIUS * cos(latLong.x) * cos(latLong.y) * SCALE + OFFSET;
CGFloat y = EARTH_RADIUS * cos(latLong.x) * sin(latLong.y) * SCALE + OFFSET;
return CGPointMake(x, y);
}
My problem is when I took small land(e.g house land) area lat long its shape is not visible on view after draw. How I can show maximise shape of land on view.
Thanks in advance.

Moving the 3d object in iOS application?

I am working on rotating the 3d object in my application by using the application downloaded from here.I succeed on stopping the rotating object as below, but I can't rotate the object again from the stopped position.Below is the piece of code I've worked from the downloaded project
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
{
    if (aRotate) {
        [self startOrStopAutorotation:nil];
    }
    if ([[event touchesForView:self.view] count] > 1) // Pinch gesture, possibly two-finger movement
{
//CGPoint directionOfPanning = CGPointZero;
}
    else{
        CGPoint currentMovementPosition = [[touches anyObject] locationInView:glView_];
        [self renderByRotatingAroundX:(lastMovementPosition.x - currentMovementPosition.x) rotatingAroundY: (lastMovementPosition.y - currentMovementPosition.y) scaling:1.0f translationInX:0.0f translationInY:0.0f];
        lastMovementPosition = currentMovementPosition;
    }
}
}
- (void)renderByRotatingAroundX:(float)xRotation rotatingAroundY:(float)yRotation scaling:(float)scaleF translationInX:(float)xTranslation translationInY:(float)yTranslation{
    
    glView_ = [[REGLView alloc] initWithFrame:CGRectMake(0, 0, 320, 320) colorFormat:kEAGLColorFormatRGBA8 multisampling:YES];
    [self.view addSubview:glView_];
    
    
    camera_ = [[RECamera alloc] initWithProjection:kRECameraProjectionOrthographic];
    camera_.position = CC3VectorMake(0, 0, 320);
    camera_.upDirection = CC3VectorMake(0, 1, 0);
    camera_.lookDirection = CC3VectorMake(0, 0, -1);
    camera_.frustumNear = 10;
    camera_.frustumFar = 640;
    camera_.frustumLeft = -glView_.frame.size.width / 2.0;
    camera_.frustumRight = glView_.frame.size.width / 2.0;
    camera_.frustumBottom = -glView_.frame.size.height / 2.0;
    camera_.frustumTop = glView_.frame.size.height / 2.0;
    
    scene_ = [[REScene alloc] init];
    scene_.camera = camera_;
    
    world_ = [[REWorld alloc] init];
    [scene_ addChild:world_];
    
    director_ = [[REDirector alloc] init];
    director_.view = glView_;
    director_.scene = scene_;
    
//    currentCalculatedMatrix = CATransform3DIdentity;
    GLfloat currentModelViewMatrix[16]  = {0.402560,0.094840,0.910469,0.000000, 0.913984,-0.096835,-0.394028,0.000000, 0.050796,0.990772,-0.125664,0.000000, 0.000000,0.000000,0.000000,1.000000};
    currentCalculatedMatrix = CATransform3DScale(currentCalculatedMatrix, 0.5, 0.5 * (320.0/480.0), 0.5);
    
////    if ((xRotation != 0.0) || (yRotation != 0.0))
//{
GLfloat totalRotation = sqrt(xRotation*xRotation + yRotation*yRotation);
CATransform3D temporaryMatrix = CATransform3DRotate(currentCalculatedMatrix, totalRotation * M_PI / 180.0,
((xRotation/totalRotation) * currentCalculatedMatrix.m12 + (yRotation/totalRotation) * currentCalculatedMatrix.m11),
((xRotation/totalRotation) * currentCalculatedMatrix.m22 + (yRotation/totalRotation) * currentCalculatedMatrix.m21),
((xRotation/totalRotation) * currentCalculatedMatrix.m32 + (yRotation/totalRotation) * currentCalculatedMatrix.m31));
if ((temporaryMatrix.m11 >= -100.0) && (temporaryMatrix.m11 <= 100.0))
currentCalculatedMatrix = temporaryMatrix;
    float currentScaleFactor = sqrt(pow(currentCalculatedMatrix.m11, 2.0f) + pow(currentCalculatedMatrix.m12, 2.0f) + pow(currentCalculatedMatrix.m13, 2.0f));
xTranslation = xTranslation / (currentScaleFactor * currentScaleFactor);
yTranslation = yTranslation / (currentScaleFactor * currentScaleFactor);
// Use the (0,4,8) components to figure the eye's X axis in the model coordinate system, translate along that
temporaryMatrix = CATransform3DTranslate(currentCalculatedMatrix, xTranslation * currentCalculatedMatrix.m11, xTranslation * currentCalculatedMatrix.m21, xTranslation * currentCalculatedMatrix.m31);
// Use the (1,5,9) components to figure the eye's Y axis in the model coordinate system, translate along that
temporaryMatrix = CATransform3DTranslate(temporaryMatrix, yTranslation * currentCalculatedMatrix.m12, yTranslation * currentCalculatedMatrix.m22, yTranslation * currentCalculatedMatrix.m32);
if ((temporaryMatrix.m11 >= -100.0) && (temporaryMatrix.m11 <= 100.0))
currentCalculatedMatrix = temporaryMatrix;
[self convert3DTransform:&currentCalculatedMatrix toMatrix:currentModelViewMatrix];    
   
// Black background, with depth buffer enabled
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
And below is the code I've worked for stopping and stopping
- (void)startOrStopAutorotation:(id)sender;
{
    id dLink=nil;
    if (aRotate){
        dLink=[[REDisplayLink sharedDisplayLink] observers];
        [[REDisplayLink sharedDisplayLink] removeObserver:[dLink objectAtIndex:0]];
    }
    else{
        CADisplayLink *aDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(handleAutorotationTimer)];
[aDisplayLink setFrameInterval:2];
[aDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        //          [[REDisplayLink sharedDisplayLink] addObserver:[[REDirector alloc]init]];
    }
    aRotate = !aRotate;
}

iOS OpenGLES2.0 drawing point at correct size

Using the code below I am drawing lines from triangle strips of varying sizes. At the point of the final triangle I would like to add a GLpoint primitive so it looks like the line has a rounded end. How would I calculate the correct diameter for the GLpoint based upon the size of the final triangle? Please see attached image demonstrating what I have at the moment (the point is much too large).
- (void)pan:(UIPanGestureRecognizer *)p {
CGPoint v = [p velocityInView:self.view];
CGPoint l = [p locationInView:self.view];
float distance = sqrtf((l.x - previousPoint.x) * (l.x - previousPoint.x) + (l.y - previousPoint.y) * (l.y - previousPoint.y));
float velocityMagnitude = sqrtf(v.x*v.x + v.y*v.y);
float clampedVelocityMagnitude = clamp(VELOCITY_CLAMP_MIN, VELOCITY_CLAMP_MAX, velocityMagnitude);
float normalizedVelocity = (clampedVelocityMagnitude - VELOCITY_CLAMP_MIN) / (VELOCITY_CLAMP_MAX - VELOCITY_CLAMP_MIN);
float lowPassFilterAlpha = STROKE_WIDTH_SMOOTHING;
float newThickness = (STROKE_WIDTH_MAX - STROKE_WIDTH_MIN) * normalizedVelocity + STROKE_WIDTH_MIN;
penThickness = penThickness * lowPassFilterAlpha + newThickness * (1 - lowPassFilterAlpha);
glBindVertexArrayOES(vertexArrayTriangles);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferTriangles);
if ([p state] == UIGestureRecognizerStateBegan)
{
previousPoint = l;
previousMidPoint = l;
NISignaturePoint startPoint = {
{ (l.x / self.view.bounds.size.width * 2. - 1), ((l.y / self.view.bounds.size.height) * 2.0 - 1) * -1, 0}, {0,0,0}
};
previousVertex = startPoint;
previousThickness = penThickness;
addVertexTriangles(&lengthTriangles, startPoint);
addVertexTriangles(&lengthTriangles, previousVertex);
} else if ([p state] == UIGestureRecognizerStateChanged) {
CGPoint mid = CGPointMake((l.x + previousPoint.x) / 2.0, (l.y + previousPoint.y) / 2.0);
if (distance > QUADRATIC_DISTANCE_TOLERANCE) {
// Plot quadratic bezier instead of line
unsigned int i;
int segments = (int) distance / 1.5;
float startPenThickness = previousThickness;
float endPenThickness = penThickness;
previousThickness = penThickness;
for (i = 0; i < segments; i++)
{
penThickness = startPenThickness + ((endPenThickness - startPenThickness) / segments) * i;
double t = (double)i / (double)segments;
double a = pow((1.0 - t), 2.0);
double b = 2.0 * t * (1.0 - t);
double c = pow(t, 2.0);
double x = a * previousMidPoint.x + b * previousPoint.x + c * mid.x;
double y = a * previousMidPoint.y + b * previousPoint.y + c * mid.y;
NISignaturePoint v = {
{
(x / self.view.bounds.size.width * 2. - 1),
((y / self.view.bounds.size.height) * 2.0 - 1) * -1,
0
},
strokeColor
};
[self addTriangleStripPointsForPrevious:previousVertex next:v];
previousVertex = v;
}
}
previousPoint = l;
previousMidPoint = mid;
}
else if (p.state == UIGestureRecognizerStateEnded | p.state == UIGestureRecognizerStateCancelled)
{
addVertexTriangles(&lengthTriangles, previousVertex);
addVertexTriangles(&lengthTriangles, previousVertex);
glBindVertexArrayOES(vertexArrayPoints);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferPoints);
NISignaturePoint startPoint = {
{previousVertex.vertex.x, previousVertex.vertex.y, 0}, strokeColor, ???
};
addVertexPoints(&lengthPoints, startPoint);
penThickness = STROKE_WIDTH_MIN;
previousThickness = penThickness;
}
}
- (void)addTriangleStripPointsForPrevious:(NISignaturePoint)previous next:(NISignaturePoint)next {
float toTravel = penThickness / 2.0;
//NSLog(#"add tri");
for (int i = 0; i < 2; i++) {
GLKVector3 p = perpendicular(previous, next);
GLKVector3 p1 = next.vertex;
GLKVector3 ref = GLKVector3Add(p1, p);
float distance = GLKVector3Distance(p1, ref);
float difX = p1.x - ref.x;
float difY = p1.y - ref.y;
float ratio = -1.0 * (toTravel / distance);
difX = difX * ratio;
difY = difY * ratio;
NISignaturePoint stripPoint = {
{ p1.x + difX, p1.y + difY, 0.0 },
strokeColor
};
addVertexTriangles(&lengthTriangles, stripPoint);
toTravel *= -1;
}
}
You might include UIGestureRecognizerStateChanged, UIGestureRecognizerStateEnded, and UIGestureRecognizerStateCancelled as one case to draw the line, but then detect the later two cases to add the ending point with diameter equal to 0.5+endPenThickness
- (void)pan:(UIPanGestureRecognizer *)p {
CGPoint v = [p velocityInView:self.view];
CGPoint l = [p locationInView:self.view];
float distance = sqrtf((l.x - previousPoint.x) * (l.x - previousPoint.x) + (l.y - previousPoint.y) * (l.y - previousPoint.y));
float velocityMagnitude = sqrtf(v.x*v.x + v.y*v.y);
float clampedVelocityMagnitude = clamp(VELOCITY_CLAMP_MIN, VELOCITY_CLAMP_MAX, velocityMagnitude);
float normalizedVelocity = (clampedVelocityMagnitude - VELOCITY_CLAMP_MIN) / (VELOCITY_CLAMP_MAX - VELOCITY_CLAMP_MIN);
float lowPassFilterAlpha = STROKE_WIDTH_SMOOTHING;
float newThickness = (STROKE_WIDTH_MAX - STROKE_WIDTH_MIN) * normalizedVelocity + STROKE_WIDTH_MIN;
penThickness = penThickness * lowPassFilterAlpha + newThickness * (1 - lowPassFilterAlpha);
glBindVertexArrayOES(vertexArrayTriangles);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferTriangles);
if ([p state] == UIGestureRecognizerStateBegan)
{
previousPoint = l;
previousMidPoint = l;
NISignaturePoint startPoint = {
{ (l.x / self.view.bounds.size.width * 2. - 1), ((l.y / self.view.bounds.size.height) * 2.0 - 1) * -1, 0}, {0,0,0}
};
previousVertex = startPoint;
previousThickness = penThickness;
addVertexTriangles(&lengthTriangles, startPoint);
addVertexTriangles(&lengthTriangles, previousVertex);
} else {
// UIGestureRecognizerStateChanged, UIGestureRecognizerStateEnded, and UIGestureRecognizerStateCancelled
CGPoint mid = CGPointMake((l.x + previousPoint.x) / 2.0, (l.y + previousPoint.y) / 2.0);
if (distance > QUADRATIC_DISTANCE_TOLERANCE) {
// Plot quadratic bezier instead of line
unsigned int i;
int segments = (int) distance / 1.5;
float startPenThickness = previousThickness;
float endPenThickness = penThickness;
previousThickness = penThickness;
for (i = 0; i < segments; i++)
{
penThickness = startPenThickness + ((endPenThickness - startPenThickness) / segments) * i;
double t = (double)i / (double)segments;
double a = pow((1.0 - t), 2.0);
double b = 2.0 * t * (1.0 - t);
double c = pow(t, 2.0);
double x = a * previousMidPoint.x + b * previousPoint.x + c * mid.x;
double y = a * previousMidPoint.y + b * previousPoint.y + c * mid.y;
NISignaturePoint v = {
{
(x / self.view.bounds.size.width * 2. - 1),
((y / self.view.bounds.size.height) * 2.0 - 1) * -1,
0
},
strokeColor
};
[self addTriangleStripPointsForPrevious:previousVertex next:v];
previousVertex = v;
}
}
previousPoint = l;
previousMidPoint = mid;
if (p.state == UIGestureRecognizerStateEnded | p.state == UIGestureRecognizerStateCancelled)
{
glBindVertexArrayOES(vertexArrayPoints);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferPoints);
NISignaturePoint startPoint = {
{previousVertex.vertex.x, previousVertex.vertex.y, 0}, strokeColor, endPenThickness / 2.
};
addVertexPoints(&lengthPoints, startPoint);
penThickness = STROKE_WIDTH_MIN;
previousThickness = penThickness;
}
}
}

Resources