RaphBlanchet
RaphBlanchet

Reputation: 575

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 : Slow swipe

Fast 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!

Upvotes: 4

Views: 5988

Answers (2)

RaphBlanchet
RaphBlanchet

Reputation: 575

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 : Smooth Lines

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!

Upvotes: 4

HalR
HalR

Reputation: 11083

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)
    }
}

Upvotes: 0

Related Questions