Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to draw a perfect squiggle in set card game with objective-c?

I'm trying to draw squiggle in Set Card Game. The output is not even close to what I want. How should I improve the drawing?

  • Please ignore the shading in "What I want" picture. I have figured out how to do that. The question is how to drawing the shape of squiggle
  • The purpose of this is to practice drawing with objective-c. So just using png picture is not a solution here...

..

#define SQUIGGLE_CURVE_FACTOR 0.5
 - (UIBezierPath *)drawSquiggleAtPoint:(CGPoint)point
{
    CGFloat dx = self.bounds.size.width * SYMBOL_WIDTH_RATIO / 2;
    CGFloat dy = self.bounds.size.height * SYMBOL_HEIGHT_RATIO / 2;

    UIBezierPath *path = [[UIBezierPath alloc] init];
    CGFloat dsqx = dx * SQUIGGLE_CURVE_FACTOR;
    CGFloat dsqy = dy * SQUIGGLE_CURVE_FACTOR;
    [path moveToPoint:CGPointMake(point.x - dx, point.y)];
    [path addQuadCurveToPoint:CGPointMake(point.x - dsqx, point.y + dsqy) controlPoint:CGPointMake(point.x - dx, point.y + dy + dsqy)];
    [path addCurveToPoint:CGPointMake(point.x + dx, point.y) controlPoint1:point controlPoint2:CGPointMake(point.x + dx, point.y + dy * 2)];
    [path addQuadCurveToPoint:CGPointMake(point.x + dsqx, point.y - dsqy) controlPoint:CGPointMake(point.x + dx, point.y - dy - dsqy)];
    [path addCurveToPoint:CGPointMake(point.x - dx, point.y) controlPoint1:point controlPoint2:CGPointMake(point.x - dx, point.y - dy * 2)];

    return path;
}
  • What I got:

What I got

  • What I want:

What I want


final version

- (UIBezierPath *)drawSquiggleAtPoint:(CGPoint)point
{
    CGSize size = CGSizeMake(self.bounds.size.width * SYMBOL_WIDTH_RATIO, self.bounds.size.height * SYMBOL_HEIGHT_RATIO);

    UIBezierPath *path = [[UIBezierPath alloc] init];
    [path moveToPoint:CGPointMake(104, 15)];
    [path addCurveToPoint:CGPointMake(63, 54) controlPoint1:CGPointMake(112.4, 36.9) controlPoint2:CGPointMake(89.7, 60.8)];
    [path addCurveToPoint:CGPointMake(27, 53) controlPoint1:CGPointMake(52.3, 51.3) controlPoint2:CGPointMake(42.2, 42)];
    [path addCurveToPoint:CGPointMake(5, 40) controlPoint1:CGPointMake(9.6, 65.6) controlPoint2:CGPointMake(5.4, 58.3)];
    [path addCurveToPoint:CGPointMake(36, 12) controlPoint1:CGPointMake(4.6, 22) controlPoint2:CGPointMake(19.1, 9.7)];
    [path addCurveToPoint:CGPointMake(89, 14) controlPoint1:CGPointMake(59.2, 15.2) controlPoint2:CGPointMake(61.9, 31.5)];
    [path addCurveToPoint:CGPointMake(104, 15) controlPoint1:CGPointMake(95.3, 10) controlPoint2:CGPointMake(100.9, 6.9)];

    [path applyTransform:CGAffineTransformMakeScale(0.9524*size.width/100, 0.9524*size.height/50)];
    [path applyTransform:CGAffineTransformMakeTranslation(point.x - size.width/2 - 3 * size.width /100, point.y - size.height/2 - 8 * size.height/50)];

    return path;
}
like image 484
dumduke Avatar asked Jan 10 '23 00:01

dumduke


2 Answers

Here's some code that generates a fairly decent rendition of a squiggle

- (void)drawSquiggle
{
    CGContextRef context = UIGraphicsGetCurrentContext();

    // translate and scale the squiggle
    CGContextSaveGState( context );
    CGContextTranslateCTM( context, 0, 100 );
    CGContextScaleCTM( context, 2.0, 2.0 );

    // create the squiggle path
    CGContextMoveToPoint( context, 104.0, 15.0 );
    CGContextAddCurveToPoint( context, 112.4 , 36.9,   89.7,  60.8,   63.0,  54.0 );
    CGContextAddCurveToPoint( context,  52.3 , 51.3,   42.2,  42.0,   27.0,  53.0 );
    CGContextAddCurveToPoint( context,   9.6 , 65.6,    5.4,  58.3,    5.0,  40.0 );
    CGContextAddCurveToPoint( context,   4.6 , 22.0,   19.1,   9.7,   36.0,  12.0 );
    CGContextAddCurveToPoint( context,  59.2 , 15.2,   61.9,  31.5,   89.0,  14.0 );
    CGContextAddCurveToPoint( context,  95.3 , 10.0,  100.9,   6.9,  104.0,  15.0 );

    // draw the squiggle
    CGContextSetLineCap( context, kCGLineCapRound );
    CGContextSetLineWidth( context, 2.0 );
    CGContextStrokePath( context );

    // restore the graphics state
    CGContextRestoreGState( context );
}

This is what it produces

enter image description here


Scaling and Translating the Squiggle

There are two ways to change the size and position of the squiggle.

One way is to change all the number that are passed to the CGContextAddCurveToPoint function. For example, if you adjust the x values with x = (x-3) * 0.9524 and adjust all the y values with y = (y-8) * 0.9524, then the squiggle fits nicely into a 100x50 rectangle.

The other way is to change the affine transform of your drawing context before drawing the squiggle. You can apply translation and scale transformations to place/size the squiggle wherever you want it. Note that the order in which the transformations are applied is important. Also, you can save and restore the graphics state so that the transformations are only applied to the squiggle, and not other items that you are drawing. The code above moves the squiggle 100 pixels down, and makes it twice as big.

like image 158
user3386109 Avatar answered Feb 02 '23 17:02

user3386109


My squiggle looks like this. Unfortunately I can't insert image directly

This is my method for this. And squiggle scale with bounds of CGrect

typedef enum : NSUInteger
{
solidShading=1,
stripedShading=2,
openShading=3,
numShading=4,
} cardShading;


-(void)drawSquiggleInBounds:(CGRect)bounds withColor:(UIColor *)color withShading:(cardShading)shading
{
if (shading > 3) {return;}

UIBezierPath *path = [[UIBezierPath alloc] init];

[color setStroke];

path.lineWidth = 2;

[path moveToPoint:CGPointMake(bounds.origin.x + bounds.size.width*0.05, bounds.origin.y + bounds.size.height*0.40)];

[path addCurveToPoint:CGPointMake(bounds.origin.x + bounds.size.width*0.35, bounds.origin.y + bounds.size.height*0.25)
        controlPoint1:CGPointMake(bounds.origin.x + bounds.size.width*0.09, bounds.origin.y + bounds.size.height*0.15)
        controlPoint2:CGPointMake(bounds.origin.x + bounds.size.width*0.18, bounds.origin.y + bounds.size.height*0.10)];

[path addCurveToPoint:CGPointMake(bounds.origin.x + bounds.size.width*0.75, bounds.origin.y + bounds.size.height*0.30)
        controlPoint1:CGPointMake(bounds.origin.x + bounds.size.width*0.40, bounds.origin.y + bounds.size.height*0.30)
        controlPoint2:CGPointMake(bounds.origin.x + bounds.size.width*0.60, bounds.origin.y + bounds.size.height*0.45)];

[path addCurveToPoint:CGPointMake(bounds.origin.x + bounds.size.width*0.97, bounds.origin.y + bounds.size.height*0.35)
        controlPoint1:CGPointMake(bounds.origin.x + bounds.size.width*0.87, bounds.origin.y + bounds.size.height*0.15)
        controlPoint2:CGPointMake(bounds.origin.x + bounds.size.width*0.98, bounds.origin.y + bounds.size.height*0.00)];

[path addCurveToPoint:CGPointMake(bounds.origin.x + bounds.size.width*0.45, bounds.origin.y + bounds.size.height*0.85)
        controlPoint1:CGPointMake(bounds.origin.x + bounds.size.width*0.95, bounds.origin.y + bounds.size.height*1.10)
        controlPoint2:CGPointMake(bounds.origin.x + bounds.size.width*0.50, bounds.origin.y + bounds.size.height*0.95)];

[path addCurveToPoint:CGPointMake(bounds.origin.x + bounds.size.width*0.25, bounds.origin.y + bounds.size.height*0.85)
        controlPoint1:CGPointMake(bounds.origin.x + bounds.size.width*0.40, bounds.origin.y + bounds.size.height*0.80)
        controlPoint2:CGPointMake(bounds.origin.x + bounds.size.width*0.35, bounds.origin.y + bounds.size.height*0.75)];

[path addCurveToPoint:CGPointMake(bounds.origin.x + bounds.size.width*0.05, bounds.origin.y + bounds.size.height*0.40)
        controlPoint1:CGPointMake(bounds.origin.x + bounds.size.width*0.00, bounds.origin.y + bounds.size.height*1.10)
        controlPoint2:CGPointMake(bounds.origin.x + bounds.size.width*0.005, bounds.origin.y + bounds.size.height*0.60)];

[path closePath];

[self drawShading:shading withColor:color inPath:path];

[path stroke];
}

Also my methods for shading:

#define DISTANCE_BETWEEN_STRIPES 4

-(void)drawShading:(cardShading)shading withColor:(UIColor *)color inPath:(UIBezierPath *)beziePath
{
switch (shading)
{
    case 1:
        [color setFill];
        [beziePath fill];
        break;
    case 2:
        [self drawStripedShadingForPath:beziePath];
        break;
    default:
        break;
}
}

-(void)drawStripedShadingForPath:(UIBezierPath *)pathOfSymbol
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);

CGRect bounds = [pathOfSymbol bounds];
UIBezierPath *path = [[UIBezierPath alloc] init];

for (int i = 0; i < bounds.size.width; i += DISTANCE_BETWEEN_STRIPES)
{
    [path moveToPoint:CGPointMake(bounds.origin.x + i, bounds.origin.y)];
    [path addLineToPoint:CGPointMake(bounds.origin.x + i, bounds.origin.y + bounds.size.height)];
}

[pathOfSymbol addClip];

[path stroke];

CGContextRestoreGState(UIGraphicsGetCurrentContext());
}
like image 26
Great_Beaver Avatar answered Feb 02 '23 17:02

Great_Beaver