How to cut up image based on coordinates - ios

I have a question related to cutting up an image to know which part is tapped by the user. It's a rather simple exercise for some, I think, but I've been cracking my head over this and can't find a proper way.
What I would like to do is tap a body part on this image and have the device tell me what body part it is:
But I have absolutely no clue on how to program this.
I've been thinking on setting an array of outline CGPoints for every body part, but how to get those? Photoshop coordinates? And then calculate the touched point onto the closest CGPoint?
Somebody gave me the idea to cut up the image in the different zones, different images for every body part. But here the problem is that every image is a rectangle, which makes it very hard to know what exactly got touched. Would also need an array of points, especially for the overlapping images.
Another person told me this would only be possible using SpriteKit.
Let me know, smart peeps :)
UPDATE:
I figured it out! I'm making a lot of arrays that contain the points to the hand, the legs, ... An example:
CGPoint p1 = CGPointMake(x + 432, y + 200);
CGPoint p2 = CGPointMake(x + 523, y + 188);
CGPoint p3 = CGPointMake(x + 530, y + 277);
CGPoint p4 = CGPointMake(x + 432, y + 277);
CGPoint p5 = CGPointMake(x + 523, y + 367);
CGPoint p6 = CGPointMake(x + 432, y + 354);
CGPoint p7 = CGPointMake(x + 325, y + 355);
CGPoint p8 = CGPointMake(x + 296, y + 362);
NSArray *handL = [NSArray arrayWithObjects:
[NSValue valueWithCGPoint:p1],
[NSValue valueWithCGPoint:p2],
[NSValue valueWithCGPoint:p3],
[NSValue valueWithCGPoint:p4], nil];
[shapes addObject:#{#"name":NSLocalizedString(#"Left Hand", #""), #"points":handL}];
NSArray *handR = [NSArray arrayWithObjects:
[NSValue valueWithCGPoint:p5],
[NSValue valueWithCGPoint:p6],
[NSValue valueWithCGPoint:p7],
[NSValue valueWithCGPoint:p8], nil];
[shapes addObject:#{#"name":NSLocalizedString(#"Right Hand", #""), #"points": handR}];
Afterwards in I read these values and layer them:
NSArray *shapes = [[NumbersHelper sharedNumbersHelper] getScreenThreeShapes];
for (int outside = 0; outside < [shapes count]; outside++) {
CAShapeLayer *shape = [[CAShapeLayer alloc] init];
[self.scrollView.layer addSublayer:shape];
shape.opacity = 0.5;
shape.lineWidth = 2;
shape.lineJoin = kCALineJoinRound;
NSArray *points = [[[shapes valueForKey:#"points"] allObjects] objectAtIndex:outside];
UIBezierPath *path = [[UIBezierPath alloc] init];
for (int inside = 0; inside < points.count; inside++) {
if (inside == 0) {
[path moveToPoint:[[points objectAtIndex:inside] CGPointValue]];
} else {
[path addLineToPoint:[[points objectAtIndex:inside] CGPointValue]];
}
}
[path closePath];
shape.path = [path CGPath];
shape.name = [[[shapes valueForKey:#"name"] allObjects] objectAtIndex:outside];
}
When the user taps the view I do this:
- (void)tappedOnView:(UITapGestureRecognizer *)sender
{
if ([sender locationInView:self.scrollView].x > 2048 && [sender locationInView:self.scrollView].x < 3072) {
screenTwoTouchedPoint = [sender locationInView:self.scrollView];
CGPoint touchPoint = [sender locationInView:self.scrollView];
NSString *name;
for (id sublayers in self.scrollView.layer.sublayers) {
if ([sublayers isKindOfClass:[CAShapeLayer class]]) {
CAShapeLayer *shape = sublayers;
if (CGPathContainsPoint(shape.path, 0, touchPoint, YES)) {
name = shape.name;
break;
}
}
}
[self tappedBody:sender onPart:name];
}
}

A crude but simple way would be to have transparent buttons over various body parts. In case a body part is not covered properly by a single button, add multiple smaller buttons.
You can add tag to the buttons to identify which button/body part got tapped or connect them to unique Actions.

A solution that worked for me. Create a 2d array for the entire size of the image. If the image is 250x250, your array would be the same.
Fill this array with NSObjects, having and int bodyInteger
Then drag your finger on the hand of the image, and in your touchesMoved take the co-ordinates from location, and pick the object from your 2d array by using the co-ordinates and setting its bodyInteger to 1.
Now when someone puts finger on hand, you take the respective object from 2d array and see what bodyInteger it has.
If you have memory constraints, you can create a structure instead of an NSObject and save only for the points that are within the body.
Alternatively, you can create a boundary for each body part and save it. When user touches with finger, you can see if the point is within a saved polygon or not.

Related

Draw Bubble Graph in iOS? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
hope you guys are doing well.
How can I draw graph similar to below image?
To be more specific, my graph will have low, normal and high risk with orange, green and red colours respectively. Each containing bubble with info in x- axis.
I wanna make exactly like above image any help would be appreciated and also please be specific while posting some third party libs and all.
How can I achieve the above graph?
Note- In X- axis (dates) and Y- axis [(values) in bubbles] are there.
EDIT
y-axis is fixed with some range by x-axis is from server took in NSMutableArray
Thanks in advance.
First step in drawing graph is to create X-Y axis layout. You can draw X and Y axis line using UIBezierPath and CAShapeLayer.
UIBezierPath *graphPath = [[UIBezierPath alloc]init];
[graphPath setLineWidth:GRAPH_LAYOUT_LINE_WIDTH];
[[UIColor blackColor] setStroke];
[graphPath moveToPoint:CGPointMake(ORIGN_X, ORIGN_Y)];
[graphPath addLineToPoint:CGPointMake((ORIGN_X + TOTAL_X_DIST),ORIGN_Y)];
[graphPath stroke]; // drawing path
//Creating graph layout
CAShapeLayer *graphLayout = [CAShapeLayer layer];
graphLayout.fillColor = [[UIColor clearColor] CGColor];
graphLayout.strokeColor = [GRAPH_LAYOUT_COLOR CGColor];
graphLayout.lineWidth = GRAPH_LAYOUT_LINE_THICKNESS;
graphLayout.path = [graphPath CGPath];
[self.layer addSublayer:graphLayout];
Repeat the above code for drawing Y axis
Second you need to create X and Y axis scale
float minX = 0;
float maxX = 10;
float minY = 0;
float maxY = 100;
float x1Unit = 1;
float y1Unit = 1;
self.spacingX = TOTAL_X_DIST/((maxX - minX)/x1Unit);
self.spacingY = TOTAL_Y_DIST/((maxY - minY+1)/y1Unit);
Now you need to get the values(cartesian coordinate) of X and Y axis in an array, here i am just hard coding the points
//Enter the co-ordinates
[self.pointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(0, 50)]];
[self.pointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(1, 20)]];
[self.pointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(2, 35)]];
[self.pointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(3, 55)]];
[self.pointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(4, 80)]];
[self.pointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(5, 60)]];
[self.pointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(6, 85)]];
[self.pointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(7, 50)]];
[self.pointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(8, 30)]];
[self.pointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(9, 10)]];
[self.pointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(10, 05)]];
Next we need to convert cartesian coordinate values to values of x and y with respect to parent view.
// Creating (x,y) points based on superview coordinate system
for (NSValue *point in self.pointsArray)
{
CGPoint coordinate;
coordinate.x = (STARTING_X*x1Unit + ([point CGPointValue].x * self.spacingX) + self.spacingX/1.5)/x1Unit;
coordinate.y = (STARTING_Y*y1Unit - ([point CGPointValue].y * self.spacingY))/y1Unit;
[self.coordinateArray addObject:[NSValue valueWithCGPoint:coordinate]];
}
Now it's time to add beautiful bubbles
for (int a = 0; a < [self.coordinateArray count]; a++)
{
CGPoint point = [[self.coordinateArray objectAtIndex:a] CGPointValue];
//Creating bubble for points on the graph.
UIView *bubble = [[UIView alloc] initWithFrame:BUBBLE_FRAME];
bubble.Layer.cornerRadius = BUBBLE_FRAME_WIDTH/2;
//Giving the color for the bubble based on the Y-Axis value
if (point.y <= 50)
{
bubble.backgroundColor = RED_COLOR;
}
else if (point.y > 50 && point.y <= 80)
{
bubble.backgroundColor = YELLOW_COLOR;
}
else
{
bubble.backgroundColor = GREEN_COLOR;
}
[self addSubview:bubble];
}
Hope this helps you

Opposite of CGPathCreateCopyByStrokingPath?

Are there any core or other framework methods to return a CGPath or collection of some type that represents the Fill portion of a path object?
I am looking for something that would basically do the opposite of CGPathCreateCopyByStrokingPath.
With the help of an article on low-level-text-rendering I was able to come up with a path representing each letter of the alphabet.
The problem is that the path is the outline of the letter and what I would actually like to have is a path that represents only the fill portion of the letter instead of the stroke. For example the letter "A" would just consist of 3 distinct lines.
I am currently trying to work around this problem with class that acts much like an over complicated 16 segment display. Here is some sample code from that effort for anyone who is interested:
const NSUInteger segmentsPerDimension = 4;
const CGFloat oneOverTheNumberOfSegments = 1.0 / segmentsPerDimension;
#implementation PathSegments
- (instancetype)initWithRect:(CGRect)rect {
self = [super init];
[self setSegmentBounds:rect];
_segments = [[NSMutableArray alloc] init];
...
[self addVerticalSegments];
...
return self;
}
- (void)addVerticalSegments {
for (NSUInteger i = 0; i <= segmentsPerDimension; i++) {
[self createColumnSegmentsForRow:i];
}
}
- (void)createColumnSegmentsForRow:(NSUInteger)row {
for (NSUInteger i = 0; i < segmentsPerDimension; i++) {
CGFloat x = _segmentBounds.size.width * oneOverTheNumberOfSegments * row;
CGFloat yEnd = _segmentBounds.size.height * oneOverTheNumberOfSegments * i;
CGFloat yStart = _segmentBounds.size.height * oneOverTheNumberOfSegments * (i + 1);
[self addLineSegmentWithXStart:x YStart:yStart XEnd:x YEnd:yEnd];
}
}
...
#end
I have determined that it does not make sense to return the fill portion of a path. While a path can be defined in such a way to draw a circle, rectangle or custom shape by filling an outline, it would be ambiguous to copy the fill portion to another path because it would simply be the same path you started with.
A segmented path which is then combined into one full path using GPathAddPath(...) seems to be the way to go on this one. With a series lines and curves you can roll your own letter shapes.
A snippet based on the assumption you have defined a set of points to use as segments of the path:
_generatedSegmentPath = CGPathCreateMutable();
CGMutablePathRef subPath = CGPathCreateMutable();
...
if (CGPathIsEmpty(subPath)) {
CGPoint start = [[points objectAtIndex:startIndex] CGPointValue];
CGPathMoveToPoint(subPath, nil, start.x, start.y);
}
if (points.count == 2) {
CGPoint point = [[points objectAtIndex:startIndex + indexChange] CGPointValue];
CGPathAddLineToPoint(subPath, nil, point.x, point.y);
}
else {
CGPoint controlPoint = [[points objectAtIndex:startIndex + indexChange] CGPointValue];
CGPoint endPoint = [[points objectAtIndex:startIndex + (2 * indexChange)] CGPointValue];
CGPathAddQuadCurveToPoint(subPath, nil, controlPoint.x, controlPoint.y, endPoint.x, endPoint.y);
}
Then to combined the subpaths together:
CGPathAddPath(_generatedSegmentPath, nil, subPath);

text labels drawn in CGRect form an elliptical arc when images drawn using the same coordinates form a circular arc

I am new to Core Graphics and trying to understand why text labels I draw in CGRect form an elliptical arc when images I draw using the same coordinates form a circular arc.
The original code by Arthur Knopper creates circles wherever the screen is touched. By removing the touches method, I have been able to generate a series of small circles (dots) along a circular arc (uber circle). Each dot is centred on the perimeter of the uber circle (as shown below).
In order to label each dot I use the same point coordinates I used for placing the dot. However text labels form an elliptical arc even though dots form a circular arc (as shown below). Labels are also hidden by the dots when dots are filled. The reason for this is a complete mystery.
As a novice I am probably missing something basic in Core Graphics. If anyone could explain what that is and what I need to do to make both arcs circular and place labels on top of the dots I’d be most grateful.
Thanks.
Here is the code.
circleView.h
NSMutableArray *totalCircles;
int dotCount, limit;
float uberX, uberY, uberRadius, uberAngle, labelX,
labelY,dotRadius, dotsFilled, sectors, x, y;
CGPoint dotPosition;
CGRect boxBoundary;
CGContextRef context;
}
- (void)demo;
#end
And ...
-#implementation iOSCircleView
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code
totalCircles = [[NSMutableArray alloc] init];
// Set background color
self.backgroundColor = [UIColor whiteColor];
}
return self;
} // frame a view for drawing
- (void)drawRect:(CGRect)rect {
[self demo];
}
- (void)demo {
context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 0.5);
uberX = 120;
uberY = 160;
uberRadius = 30;
sectors = 16;
uberAngle = (2.0 * PI) / sectors;
dotRadius = 20;
dotsFilled = FALSE;
for (dotCount = 1; dotCount <= sectors; dotCount++)
{
// Create a new iOSCircle Object
iOSCircle *newCircle = [[iOSCircle alloc] init];
newCircle.circleRadius = dotRadius;
[self setSectorDotCoordinates]; // make new point for each dot
dotPosition = CGPointMake(x,y); // create each dot
NSLog(#"Circle%i: %#", dotCount, NSStringFromCGPoint(dotPosition));
[self autoLabel]; // text hides behind the dots
newCircle.circleCentre = dotPosition; // place each dot on the frame
[totalCircles addObject:newCircle];
[self setNeedsDisplay];
}
CGContextSetShadowWithColor(context, CGSizeMake(-3 , 2), 4.0, [UIColor clearColor].CGColor);
dotCount = 1;
for (iOSCircle *circle in totalCircles) {
CGContextAddArc(context, circle.circleCentre.x, circle.circleCentre.y, circle.circleRadius, 0.0, M_PI * 2.0, YES); // draw the circles
NSLog(#"Dot %i Filled %i ", dotCount, dotsFilled);
switch (dotsFilled) {
case 1:
CGContextSetFillColorWithColor(context, [[UIColor cyanColor] CGColor]);
//CGContextDrawPath(context, kCGPathFillStroke);
break;
default:
//CGContextStrokePath(context); // draw dot outline
break;
}
dotCount++;
}
} // draw circular dots in circular patterns
- (void)setSectorDotCoordinates {
x = uberX + (uberRadius * cos(uberAngle *dotCount) * 2);
y = uberY + (uberRadius * sin(uberAngle *dotCount) * 2);
} // calculate dot coordinates along a circular arc
- (void)autoLabel {
CGContextSetStrokeColorWithColor(context, [[UIColor blackColor] CGColor]);
boxBoundary = CGRectMake(x-dotRadius, y-dotRadius, x+dotRadius, y+dotRadius);
[[NSString stringWithFormat:#"%i",dotCount] drawInRect:boxBoundary withFont:[UIFont systemFontOfSize:24] lineBreakMode:NSLineBreakByCharWrapping alignment:NSTextAlignmentCenter];
}
Change the boxBoundary in autoLabel, CGRectMake creates a rectangle with one point coordinates and width and height, not two points:
(void)autoLabel {
CGContextSetStrokeColorWithColor(context, [[UIColor blackColor] CGColor]);
boxBoundary = CGRectMake(x-dotRadius, y-dotRadius, dotRadius*2, dotRadius*2);
[[NSString stringWithFormat:#"%i",dotCount] drawInRect:boxBoundary
withFont:[UIFont systemFontOfSize:24]
lineBreakMode:NSLineBreakByCharWrapping alignment:NSTextAlignmentCenter];
}
In your code the "boxes" containing the texts where bigger and bigger when you where going to the right. (the width and height were not fixed)
My updated code show labels that match the drawing order but text is still hidden when dots are filled.
I suspect I need to construct a path to write text in front of the dots and it’s already apparent that something like CGPathMoveToPoint is needed to start drawing from the 12 O'clock position.
Here’s the updated code. The first part draws and renders the dots
context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 0.5);
uberX = 160;
uberY = 240;
uberRadius = 52;
sectors = 16;
uberAngle = ((2.0 * PI) / sectors);
NSLog(#"%f %f %f %f", uberX, uberY, uberRadius, uberAngle);
dotRadius = 20;
dotsFilled = FALSE;
textOffset = 4; // add to y to centre the label
for (dotCount = 1; dotCount <= 4 /*sectors*/; dotCount++)
{
// Create a new iOSCircle Object
iOSCircle *newCircle = [[iOSCircle alloc] init];
newCircle.circleRadius = dotRadius;
[self setSectorDotCoordinates]; // create a new point for each dot
dotPosition = CGPointMake(x,y); // create each dot
NSLog(#"Circle%i: %#", dotCount, NSStringFromCGPoint(dotPosition));
newCircle.circleCentre = dotPosition; // place each dot on the frame
[totalCircles addObject:newCircle];
[self setNeedsDisplay];
}
CGContextSetShadowWithColor(context, CGSizeMake(-3 , 2), 4.0, [UIColor clearColor].CGColor);
dotCount = 1;
for (iOSCircle *circle in totalCircles) {
CGContextAddArc(context, circle.circleCentre.x, circle.circleCentre.y, circle.circleRadius, 0.0, M_PI * 2.0, YES);
// draw the circles
NSLog(#"Dot %i Filled %i ", dotCount, dotsFilled);
switch (dotsFilled) {
case 1:
CGContextSetFillColorWithColor(context, [[UIColor cyanColor] CGColor]);
CGContextDrawPath(context, kCGPathFillStroke);
break;
default:
CGContextStrokePath(context); // draw dot outline
break;
}
[self setSectorDotCoordinates]; // find point coordinates for each dot
dotCount++;
}
The code that draws the labels follow immediately afterwards.
// draw labels
for (dotCount = 1; dotCount <= sectors; dotCount++)
{
// Create a new iOSCircle Object
iOSCircle *newCircle = [[iOSCircle alloc] init];
newCircle.circleRadius = dotRadius;
[self setSectorDotCoordinates]; // find point coordinates for each dot
dotPosition = CGPointMake(x,y); // use point coordinates for label
[self autoLabel]; // THIS SHOWS TEXT BEHIND THE DOTS
}

Cocos2D & ccDrawLine - Drawing smooth lines

I have some troubles when I try to draw lines with cocos2d! I store points, got from touchMoved method, in a NSMutableArray, and pass that array to a subclass of CCNode called Lines that I use to draw the lines from the array of points. The problem is that the line is not smooth when I swipe slowly, but when I swipe faster, the line is way much smoother. See the pictures below :
Slow Swipe :
Fast Swipe :
I tried to solve the problem with ccpDistance, which calculate the distance between the last saved point and if it's not far enough I don't save it. I also tried to draw little circles at each saved positions, but this isn't really nice neither. Here's my code :
In my GameScene :
- (void) ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
if (ccpDistance(lastPoint, location) > 10) {
//SAVE THE POINT
[linePoints addObject:[NSValue valueWithCGPoint:location]];
[line updatePoints:linePoints];
lastPoint = location;
}
}
And my Line Class :
- (void) updatePoints:(NSMutableArray *)_point
{
points = _point;
}
- (void) draw
{
if ([points count] > 0) {
ccGLEnable(GL_LINE_STRIP);
ccDrawColor4B(209, 75, 75, 255);
float lineWidth = 6.0 * CC_CONTENT_SCALE_FACTOR();
glLineWidth(lineWidth);
int count = [points count];
for (int i = 0; i < (count - 1); i++){
CGPoint pos1 = [[points objectAtIndex:i] CGPointValue];
CGPoint pos2 = [[points objectAtIndex:i+1] CGPointValue];
ccDrawLine(pos1, pos2);
ccDrawSolidCircle(pos2, 2.5, 20);
}
}
}
Also, is there something in my code that could be done better to improve performance? Right now I don't have any problems even with 1000+ points, but just in case...
Any help will be greatly appreciated! Thanks in advance!
Ok, I found a website explaining really clearly how to do smooth lines, and it worked wonderfully! There's still anti-aliasing that's left to do, but maybe I will never do, since it looks really great on retina devices. Here's the website : Drawing Smooth Lines with Cocos2D
And here's the result :
Also, for those interested in the finished code, here it is :
Line.m
- (void) drawCurPoint:(CGPoint)curPoint PrevPoint:(CGPoint)prevPoint
{
float lineWidth = 6.0;
ccColor4F red = ccc4f(209.0/255.0, 75.0/255.0, 75.0/255.0, 1.0);
//These lines will calculate 4 new points, depending on the width of the line and the saved points
CGPoint dir = ccpSub(curPoint, prevPoint);
CGPoint perpendicular = ccpNormalize(ccpPerp(dir));
CGPoint A = ccpAdd(prevPoint, ccpMult(perpendicular, lineWidth / 2));
CGPoint B = ccpSub(prevPoint, ccpMult(perpendicular, lineWidth / 2));
CGPoint C = ccpAdd(curPoint, ccpMult(perpendicular, lineWidth / 2));
CGPoint D = ccpSub(curPoint, ccpMult(perpendicular, lineWidth / 2));
CGPoint poly[4] = {A, C, D, B};
//Then draw the poly, and a circle at the curPoint to get smooth corners
ccDrawSolidPoly(poly, 4, red);
ccDrawSolidCircle(curPoint, lineWidth/2.0, 20);
}
- (void) draw
{
if ([points count] > 0) {
ccGLEnable(GL_LINE_STRIP);
ccColor4F red = ccc4f(209.0/255.0, 75.0/255.0, 75.0/255.0, 1.0);
ccDrawColor4F(red.r, red.g, red.b, red.a);
float lineWidth = 6.0 * CC_CONTENT_SCALE_FACTOR();
glLineWidth(lineWidth);
int count = [points count];
for (int i = 0; i < (count - 1); i++){
CGPoint pos1 = [[points objectAtIndex:i] CGPointValue];
CGPoint pos2 = [[points objectAtIndex:i+1] CGPointValue];
[self drawCurPoint:pos2 PrevPoint:pos1];
}
}
}
As for the GameScene, nothing changed there (See the question for the code)! Note that you can change the line if (ccpDistance(lastPoint, location) > X), where X is the minimum distance between two points before the game saves another one. The lower X is, the smoother the line will be, but you will have way more points in your array, which could affect performance!
Anyway, thank you guys for your suggestions and your help, it helped me to get in the right way!
I think that you could smooth up your line drawing with some averaging.
- (void) updatePoints:(NSMutableArray *)_point
{
points = _point;
int count = [points count];
for (int i = 3; i < (count - 4); i++) {
CGPoint pos1 = [[points objectAtIndex:i - 2] CGPointValue];
CGPoint pos2 = [[points objectAtIndex:i - 1] CGPointValue];
CGPoint pos3 = [[points objectAtIndex:i] CGPointValue];
CGPoint pos4 = [[points objectAtIndex:i + 1] CGPointValue];
CGPoint pos5 = [[points objectAtIndex:i + 2] CGPointValue];
CGFloat xpos = (pos1.x + pos2.x + 2 * pos3.x + pos4.x + pos5.x)/6;
...
(now calcuclate ypos similarly and store the point into an array)
}
}

Smoothing hand drawn Bezier Curve

I am trying to smoothen my hand drawn Bezier curves, but not able to achieve, it, I got the code of smoothing the curve from the book written by erica sudan , which I tried to implement , but dont know why I am not able to produce a smooth curve, below is my code , I request all of you to please go through it ..
- (void)drawRect:(CGRect)rect
{
myPath.pathColor = [UIColor redColor];
for (BezeirPath *_path in m_pathArray)
{
[_path.pathColor setStroke];
[_path.bezeirPath strokeWithBlendMode:kCGBlendModeNormal alpha:1.0];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
m_previousPoint1 = [mytouch previousLocationInView:self];
m_previousPoint2 = [mytouch previousLocationInView:self];
m_currentPoint = [mytouch locationInView:self];
CGPoint controlPoint = CGPointMake(m_previousPoint1.x+(m_previousPoint1.x - m_previousPoint2.x), m_previousPoint1.y +(m_previousPoint1.y - m_previousPoint2.y));
[myPath.bezeirPath addQuadCurveToPoint:m_currentPoint controlPoint:controlPoint];
self.previousPoint3 = controlPoint;
[myPath.bezeirPath smoothedPath:myPath.bezeirPath :40];//Called the smoothing function.
[self setNeedsDisplay];
}
//Smoothing function
-(UIBezierPath*)smoothedPath:(UIBezierPath*)bpath: (NSInteger) granularity
{
NSArray *points = pointsFromBezierPath(bpath);
if (points.count < 4) return bpath;
// This only copies lineWidth. You may want to copy more
UIBezierPath *smoothedPath = [UIBezierPath bezierPath];
smoothedPath.lineWidth = bpath.lineWidth;
// Draw out the first 3 points (0..2)
[smoothedPath moveToPoint:POINT(0)];
for (int index = 1; index < 3; index++)
[smoothedPath addLineToPoint:POINT(index)];
// Points are interpolated between p1 and p2,
// starting with 2..3, and moving from there
for (int index = 4; index < points.count; index++)
{
CGPoint p0 = POINT(index - 3);
CGPoint p1 = POINT(index - 2);
CGPoint p2 = POINT(index - 1);
CGPoint p3 = POINT(index);
// now add n points starting at p1 + dx/dy up until p2
// using Catmull-Rom splines
for (int i = 1; i < granularity; i++)
{
float t = (float) i * (1.0f / (float) granularity);
float tt = t * t;
float ttt = tt * t;
CGPoint pi; // intermediate point
pi.x = 0.5 * (2*p1.x+(p2.x-p0.x)*t +
(2*p0.x-5*p1.x+4*p2.x-p3.x)*tt +
(3*p1.x-p0.x-3*p2.x+p3.x)*ttt);
pi.y = 0.5 * (2*p1.y+(p2.y-p0.y)*t +
(2*p0.y-5*p1.y+4*p2.y-p3.y)*tt +
(3*p1.y-p0.y-3*p2.y+p3.y)*ttt);
[smoothedPath addLineToPoint:pi];
}
[smoothedPath addLineToPoint:p2];
}
// finish by adding the last point
[smoothedPath addLineToPoint:POINT(points.count - 1)];
return smoothedPath;
}
I suggest watching "Building Advanced Gesture Recognizers" from WWDC 2012. You can skip the beginning - the part you are most interested in is at 38:23.

Resources