PDA

View Full Version : Problems retrieving a CGPath




RottenApple2
Aug 15, 2012, 11:04 PM
I am trying to create a path, then immediately retrieve it and modify it. The problem is that the path gets created and displayed correctly but then I am unable to retrieve it and modify it further.

The problem is that when I call CGContextClosePath in the second function the compiler returns this error: : CGContextClosePath: no current point. It basically does not "recognise" the old path and therefore it cannot close it. Any advice would be highly appreciated! Thanks!

Here is my code:

//Defined at the very beginning outside the functions
CGMutablePathRef _path;

//First function
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self];

UIGraphicsBeginImageContext(self.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();

[drawImage.image drawInRect:CGRectMake(0, 0, drawImage.frame.size.width, drawImage.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);

CGContextSetLineWidth(UIGraphicsGetCurrentContext(), BrushSize);

CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 1.0, 1.0, 1.0, 1.0); *
_path = CGPathCreateMutable();

CGContextAddPath (context, _path);

CGContextMoveToPoint(context, lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(context, currentPoint.x, currentPoint.y);

CGContextStrokePath(context);

drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();


//Second function
UIGraphicsBeginImageContext(self.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();

[drawImage.image drawInRect:CGRectMake(0, 0, drawImage.frame.size.width, drawImage.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound); //kCGLineCapSquare, kCGLineCapButt, kCGLineCapRound
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), BrushSize); //1.0); // Brush size

CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 1.0, 0.0, 0.0, 1.0);
CGContextSetRGBFillColor(context, 1.0, 0.4, 1.0, 1.0);

CGPathRef pathFill = CGPathCreateCopy (_path);
CGContextAddPath (context, pathFill);

CGContextClosePath(context);
CGContextFillPath(context);

drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();



chown33
Aug 16, 2012, 12:10 AM
Please post actual functions. With the function name, parameters, and { } around the code. Details are important.

If the functions are actually methods, then we need to see that they're methods, and that _path is or isn't an instance variable.

And if this is related to your earlier thread (http://forums.macrumors.com/showthread.php?t=1415578), please describe exactly what you've done differently.

RottenApple2
Aug 16, 2012, 08:10 PM
Ok, lets keep it simple. With the 2 functions below the aim is to draw a rectangle with a missing side with the first function, then with the second function retrieving the original shape, close the path and fill it with a solid color. However it does not work... I get: <Error>: CGContextClosePath: no current point. Here is the link to the original question (http://stackoverflow.com/questions/11915576/creating-a-path-in-multiple-steps).

-(void)AddPath_InitialPart
{
//DRAW 3 SIDES OF A RED RECTANGLE
UIGraphicsBeginImageContext(self.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();

[drawImage.image drawInRect:CGRectMake(0, 0, drawImage.frame.size.width, drawImage.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), BrushSize);

//LINE COLOR
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 1.0, 0.0, 0.0, 1.0);

CGContextAddPath (context, _path);
CGContextMoveToPoint(context, 30, 30);
CGContextAddLineToPoint(context, 320, 30);
CGContextAddLineToPoint(context, 320, 90);
CGContextAddLineToPoint(context, 30, 90);
CGContextStrokePath(context);

drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
// UIGraphicsEndImageContext();
}

-(void)FinalizeThePath
{
//CLOSE THE RECTANGLE (IT DOES NOT WORK)
UIGraphicsBeginImageContext(self.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();

[drawImage.image drawInRect:CGRectMake(0, 0, drawImage.frame.size.width, drawImage.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), BrushSize);

//LINE COLOR
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 1.0, 0.0, 0.0, 1.0);
//FILL COLOR
CGContextSetRGBFillColor(context, 1.0, 0.4, 1.0, 1.0);

CGPathRef pathFill = CGPathCreateCopy (_path);
CGContextAddPath (context, pathFill);

CGContextClosePath(context);
CGContextFillPath(context);

drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}

chown33
Aug 16, 2012, 09:31 PM
Post the code that shows _path being assigned a new CGMutablePathRef. If that doesn't happen, then _path is nil, and it contains no path, hence it has no points.

Also post the code that shows points or curves or something being added to the mutable-path in _path.


In this code, describe what you think the red-hilited statement is doing:
-(void)AddPath_InitialPart
{
//DRAW 3 SIDES OF A RED RECTANGLE
UIGraphicsBeginImageContext(self.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();

[drawImage.image drawInRect:CGRectMake(0, 0, drawImage.frame.size.width, drawImage.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), BrushSize);

//LINE COLOR
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 1.0, 0.0, 0.0, 1.0);

CGContextAddPath (context, _path);
CGContextMoveToPoint(context, 30, 30);
CGContextAddLineToPoint(context, 320, 30);
CGContextAddLineToPoint(context, 320, 90);
CGContextAddLineToPoint(context, 30, 90);
CGContextStrokePath(context);

drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
// UIGraphicsEndImageContext();
}

If _path does not have an actual path containing actual points, then it has no effect.

If _path does have an actual path, and that path contains points (or curves, etc.), then the code is adding the contents of _path to the context. Those points (or curves, etc.) will then be stroked when CGContextStrokePath() is called.


In this code, note the red-hilited statement:
-(void)FinalizeThePath
{
//CLOSE THE RECTANGLE (IT DOES NOT WORK)
UIGraphicsBeginImageContext(self.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();

[drawImage.image drawInRect:CGRectMake(0, 0, drawImage.frame.size.width, drawImage.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), BrushSize);

//LINE COLOR
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 1.0, 0.0, 0.0, 1.0);
//FILL COLOR
CGContextSetRGBFillColor(context, 1.0, 0.4, 1.0, 1.0);

CGPathRef pathFill = CGPathCreateCopy (_path);
CGContextAddPath (context, pathFill);

CGContextClosePath(context);
CGContextFillPath(context);

drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}

At the hilited point in the code, if _path is nil, or _path contains an actual path with no points, then the context at that point has no points in it. You haven't performed a move, draw, or any other action that would add any points to the context. Therefore, the immediately following CGContextClosePath() would have no path to close, because there would be no points.

So you need to look very closely at what's in _path, because that determines what will be in pathFill, since _path is being copied. If there's nothing in _path, then pathFill is also nothing, and the context has no points.


I'm unsure exactly what you're trying to accomplish. If you're trying to collect points of a red rectangle in _path, I'm pretty sure the code is wrong. I think you should be using the CG functions whose names start with CGPathAdd, not the functions with names CGContextAdd.

If you're trying to draw _path into a CGContext, then _path must be an actual path containing actual points or curves. Since you haven't shown anything that assigns a real path reference to _path, nor anything that adds points or curves to a path, no one knows what's really in _path.

RottenApple2
Aug 17, 2012, 11:25 AM
Dear chawn33, first of all thanks for your explanations.

I thought I could define an empty path (_path) at the very beginning by using CGMutablePathRef _path; and then later assign it to the context as an incomplete rectangle with


CGContextAddPath (context, _path);
CGContextMoveToPoint(context, 30, 30);
CGContextAddLineToPoint(context, 320, 30);
CGContextAddLineToPoint(context, 320, 90);
CGContextAddLineToPoint(context, 30, 90);


so that I could later retrieve this unfinished shape and complete it with the missing line. However I do now realise that what I am doing is actually assigning a void path to the context.. Isn't it possible to create this path and name it for later retrieval through the second function?

chown33
Aug 17, 2012, 12:56 PM
I thought I could define an empty path (_path) at the very beginning by using CGMutablePathRef _path;
That's not an empty path. It's a variable. The variable can hold a path, if you make one using a suitable CG function and assign it to the variable. If you don't assign anything to the variable, it remains nil (no path reference).

If you don't know the difference between a variable, which holds a value, and the value itself, you should study the language fundamentals more carefully.


and then later assign it to the context as an incomplete rectangle with


CGContextAddPath (context, _path);
CGContextMoveToPoint(context, 30, 30);
CGContextAddLineToPoint(context, 320, 30);
CGContextAddLineToPoint(context, 320, 90);
CGContextAddLineToPoint(context, 30, 90);


so that I could later retrieve this unfinished shape and complete it with the missing line. However I do now realise that what I am doing is actually assigning a void path to the context.. Isn't it possible to create this path and name it for later retrieval through the second function?
Instead of simply describing what you want to accomplish, you've posted code with no explanation.

The description of what you want to accomplish is simple: You want to build a path. You want it to contain a partial rectangle. You also want to add more points later, either by reopening the original path or building a new path that contains the original. If that's a clear and accurate description of what you want to accomplish, you should start by looking up how to create a mutable path reference, and how to build a path in it that reference.

Building a path is not at all the same as drawing into a CGContext. That's why it's important to have a simple yet accurate description of what you want to accomplish. If you had said in the beginning, "I want to build a path, by adding points of a partial rectangle", we would know how to help you. Posting code without an explanation, or posting incomplete code, makes it more difficult for everyone.


What have you read or studied to learn how to build paths? Book? Tutorial? Be specific: book title, author, and edition; tutorial URL. If you haven't studied anything, but instead have just pasted together parts you find on the internet, then you need to study the fundamentals first. The first thing you need to know is the difference between a path (CGPathRef) and a drawing context (CGContextRef).

Quartz 2D Programming Guide (http://developer.apple.com/library/ios/#DOCUMENTATION/GraphicsImaging/Conceptual/drawingwithquartz2d/Introduction/Introduction.html%23//apple_ref/doc/uid/TP30001066).

RottenApple2
Aug 19, 2012, 12:14 PM
Hi again, I have read about CGPathAdd functions as you suggested and I have made a successful attempt of creating an initial path (an incomplete circle), and then retrieve is as a copy, close it and then fill it. Here is the code for this.

-(void)AddPath_InitialPart
{
UIGraphicsBeginImageContext(self.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();

[drawImage.image drawInRect:CGRectMake(0, 0, drawImage.frame.size.width, drawImage.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), BrushSize);

//LINE COLOR
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 1.0, 0.0, 0.0, 1.0);

_path = CGPathCreateMutable();

//It works
CGPathAddArc(_path, NULL, 110, 50, 50, 0.2*M_PI, 1.3, YES);

//Add the path to the context
CGContextAddPath(context, _path);
CGContextStrokePath(context);

//Display the path on screen
drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
}

-(void)FinalizeThePath
{
//CLOSE THE RECTANGLE (IT DOES NOT WORK)
UIGraphicsBeginImageContext(self.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();

[drawImage.image drawInRect:CGRectMake(0, 0, drawImage.frame.size.width, drawImage.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), BrushSize);

//LINE COLOR
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 1.0, 0.0, 0.0, 1.0);
//FILL COLOR
CGContextSetRGBFillColor(context, 1.0, 0.4, 1.0, 1.0);

CGPathRef pathFill = CGPathCreateCopy (_path);
CGContextAddPath (context, pathFill);

//It works (not necessary?)
CGPathCloseSubpath(pathFill);

//Add the path to the context
CGContextAddPath(context, pathFill);
CGContextFillPath(context);

drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}


However I have then tried to apply the same principle for my actual needs (freehand drawing using Bezier curves) and no filling occurs. Any idea about what I am doing wrong here please? Many thanks.


CGPoint midPoint(CGPoint p1, CGPoint p2)
{
return CGPointMake((p1.x + p2.x) * 0.5, (p1.y + p2.y) * 0.5);
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

previousPoint1 = [touch previousLocationInView:self];
previousPoint2 = [touch previousLocationInView:self];
lastPoint = [touch locationInView:self];
FirstPoint = lastPoint;
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];

CGPoint currentPoint = [touch locationInView:self];

previousPoint2 = previousPoint1;
previousPoint1 = [touch previousLocationInView:self];
CGPoint mid1 = midPoint(previousPoint1, previousPoint2);
CGPoint mid2 = midPoint(currentPoint, previousPoint1);

UIGraphicsBeginImageContext(self.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();

[drawImage.image drawInRect:CGRectMake(0, 0, drawImage.frame.size.width, drawImage.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), BrushSize);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 0.0, 0.0, 1.0, 1.0);

_path = CGPathCreateMutable();

CGPathMoveToPoint(_path, NULL, mid1.x, mid1.y);
CGPathAddQuadCurveToPoint(_path, NULL, previousPoint1.x, previousPoint1.y, mid2.x, mid2.y);

//Add the path to the context
CGContextAddPath(context, _path);
CGContextStrokePath(context);

drawImage.image = UIGraphicsGetImageFromCurrentImageContext();

lastPoint = currentPoint;
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {

UITouch *touch = [touches anyObject];

UIGraphicsBeginImageContext(self.frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();

[drawImage.image drawInRect:CGRectMake(0, 0, drawImage.frame.size.width, drawImage.frame.size.height)];
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), BrushSize);

//LINE COLOR = RED
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 1.0, 0.0, 0.0, 1.0);
//FILL COLOR = PINK
CGContextSetRGBFillColor(context, 1.0, 0.4, 1.0, 1.0);

//Add a copy of the initial path
CGPathRef pathFill = CGPathCreateCopy (_path);
CGContextAddPath (context, pathFill);

//Close the copy of the path
CGPathCloseSubpath(pathFill);

//Add the path to the context
CGContextAddPath(context, pathFill);
//Apply the filling (does not work)
CGContextFillPath(context);

drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}

chown33
Aug 19, 2012, 12:30 PM
Does CGPathCreateCopy() return a mutable copy or an immutable copy?

If it returns an immutable copy, what should you expect to happen if you try to add to the path, such as by trying to close it?

If it returns an immutable copy, what is the function that returns a mutable copy?


Bonus points:
Are you managing memory correctly? Are the created objects being released when you're done working with them, or are they just leaking?