Problems retrieving a CGPath

Discussion in 'iPhone/iPad Programming' started by RottenApple2, Aug 15, 2012.

  1. RottenApple2, Aug 15, 2012
    Last edited by a moderator: Aug 15, 2012

    macrumors newbie

    Joined:
    Nov 4, 2009
    #1
    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:

    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();
     
  2. macrumors 603

    Joined:
    Aug 9, 2009
    #2
    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, please describe exactly what you've done differently.
     
  3. thread starter macrumors newbie

    Joined:
    Nov 4, 2009
    #3
    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.

    Code:
    -(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();
    }
     
  4. chown33, Aug 16, 2012
    Last edited: Aug 16, 2012

    macrumors 603

    Joined:
    Aug 9, 2009
    #4
    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:
    Code:
    -(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);
        
    [COLOR="Red"]    CGContextAddPath (context, _path);
    [/COLOR]    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:
    Code:
    -(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);
    [COLOR="Red"]    CGContextAddPath (context, pathFill);
    [/COLOR]    
        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.
     
  5. thread starter macrumors newbie

    Joined:
    Nov 4, 2009
    #5
    Dear chawn33, first of all thanks for your explanations.

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

    Code:
    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?
     
  6. macrumors 603

    Joined:
    Aug 9, 2009
    #6
    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.


    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.
     
  7. thread starter macrumors newbie

    Joined:
    Nov 4, 2009
    #7
    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.

    Code:
    -(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.


    Code:
    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();
    }
    
    
     
  8. macrumors 603

    Joined:
    Aug 9, 2009
    #8
    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?
     

Share This Page