Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get center points of a UIBezierpath

I am able to generate the UIBezierPath of characters with whatever selected font and size. Now I want to make an etched line in between the bezier path. Can I get the center points of the bezier path along? Or any other way that I can make the center dotted line and follow that path?

Here is the code how I do so

Reference Link. I want something like this. :

 UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 568, 568) cornerRadius:0];
UIBezierPath *circlePath = [self createArcPath];
[path appendPath:circlePath];
[path setUsesEvenOddFillRule:YES];





shapeView = [CAShapeLayer layer];
shapeView.geometryFlipped = false;
shapeView.path = path.CGPath;
shapeView.fillRule = kCAFillRuleEvenOdd;
shapeView.fillColor = [UIColor grayColor].CGColor;
shapeView.opacity = 1.0;
shapeView.lineDashPattern = @[@2, @3];
[self.view.layer addSublayer:shapeView];

CGFloat dashes[] = {2, 3};
[path setLineDash:dashes count:2 phase:0];


- (UIBezierPath *)createArcPath
{
// Create path from text
// See: http://www.codeproject.com/KB/iPhone/Glyph.aspx
// License: The Code Project Open License (CPOL) 1.02 http://www.codeproject.com/info/cpol10.aspx
letters = CGPathCreateMutable();

CTFontRef font = CTFontCreateWithName(CFSTR("Helvetica-Bold"),80, NULL);
NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:
                       (__bridge id)font, kCTFontAttributeName,//د
                       nil];//ج
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:@"H"
                                                                 attributes:attrs];
CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)attrString);
CFArrayRef runArray = CTLineGetGlyphRuns(line);

// for each RUN
for (CFIndex runIndex = 0; runIndex < CFArrayGetCount(runArray); runIndex++)
{
    // Get FONT for this run
    CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runArray, runIndex);
    CTFontRef runFont = CFDictionaryGetValue(CTRunGetAttributes(run), kCTFontAttributeName);

    // for each GLYPH in run
    for (CFIndex runGlyphIndex = 0; runGlyphIndex < CTRunGetGlyphCount(run); runGlyphIndex++)
    {
        // get Glyph & Glyph-data
        CFRange thisGlyphRange = CFRangeMake(runGlyphIndex, 1);
        CGGlyph glyph;
        CGPoint position;
        CTRunGetGlyphs(run, thisGlyphRange, &glyph);
        CTRunGetPositions(run, thisGlyphRange, &position);

        // Get PATH of outline
        {
            CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyph, NULL);
            CGAffineTransform t = CGAffineTransformMakeTranslation(position.x+200, position.y+80);
            CGPathAddPath(letters, &t, letter);
            CGPathRelease(letter);
        }
    }
}
CFRelease(line);

self.path = [UIBezierPath bezierPath];
[self.path appendPath:[UIBezierPath bezierPathWithCGPath:letters]];

return self.path;


}

The center dots points I want to draw which is in the cenyter of the bezierPath

like image 654
NiKKi Avatar asked Aug 18 '16 08:08

NiKKi


3 Answers

Can I get the center points of the bezier path along? Or any other way that I can make the center dotted line and follow that path?

To know that user's finger is going along the path or not you have to Hit-Detection on a Path.

To determine whether a touch event occurred on the filled portion of a path, you can use the containsPoint: method of UIBezierPath. This method tests the specified point against all closed subpaths in the path object and returns YES if it lies on or inside any of those subpaths.

If you want to do hit-testing on the stroked portion of the path (instead of the fill area), you must use Core Graphics. The CGContextPathContainsPoint function lets you test points on either the fill or stroke portion of the path currently assigned to the graphics context.

Below method tests to see whether the specified point intersects the specified path. The inFill parameter lets the caller specify whether the point should be tested against the filled or stroked portion of the path. The path passed in by the caller must contain one or more closed subpaths for the hit detection to succeed.

Testing points against a path object.

- (BOOL)containsPoint:(CGPoint)point onPath:(UIBezierPath *)path inFillArea:(BOOL)inFill
{
   CGContextRef context = UIGraphicsGetCurrentContext();
   CGPathRef cgPath = path.CGPath;
   BOOL    isHit = NO;

   // Determine the drawing mode to use. Default to
   // detecting hits on the stroked portion of the path.
   CGPathDrawingMode mode = kCGPathStroke;
   if (inFill)
   {
      // Look for hits in the fill area of the path instead.
      if (path.usesEvenOddFillRule)
         mode = kCGPathEOFill;
      else
         mode = kCGPathFill;
   }

   // Save the graphics state so that the path can be removed later.
   CGContextSaveGState(context);
   CGContextAddPath(context, cgPath);

   // Do the hit detection.
   isHit = CGContextPathContainsPoint(context, point, mode);

   CGContextRestoreGState(context);

   return isHit;
}

Check this link to know more about Hit-Detection on a Path

Getting center of path

You can get the width of path as myPath.bounds.size.width; and to get the center of the path just divide width by 2.

And to draw dashed line check this answer

To make dashed line on any UIBezierPath as:

CGFloat dashes[] = {2, 3};
//passing an array with the values {2,3} sets a dash pattern that alternates between a 2 space-unit-long painted segment and a 3 space-unit-long unpainted segment.
UIBezierPath *path = [UIBezierPath bezierPath];
[path setLineDash:dashes count:2 phase:0];

and to dash on CAShapeLayer use the property lineDashPattern as:

shapeView.lineDashPattern = @[@2, @3];
like image 52
Sunil Sharma Avatar answered Nov 14 '22 07:11

Sunil Sharma


While bezier paths are a convenient option for simple drawing, they are complex calculations, so it will be prohibitively expensive and complicated to use them for this purpose. That's because you need to be able to calculate arbitrary points along the path and to know the prior point in order to know the current direction.

You need some other description of the characters to allow you to know these details more easily. That means using a simple vector (or stroke) description of the fonts (as mentioned by rob mayoff in the comments). This description breaks the characters down into a number of straight line segments which are easy to work with. They are all governed by a y=mx+c calculation and you always know 2 points on each line segment so it's easy to interpolate between them and to know the direction of movement.

If the descriptions at the link provided by rob (here) aren't 'accurate' enough for the size at which you want to display the characters you can create new versions with more points in order to achieve a closer approximation to bezier curve options.

Now, with this description you have a lot of options...

For dragging a finger along the path you can interpolate between points to find the closest point on the path to the current touch point and determine when the touch point has strayed too far from the path, or has intersected it. This intersection processing is also how you can determine the coverage percentage. You just need to choose the interpolation gap distance (chosen for a suitable resolution without creating too many points that are really close together) and a tolerance for how far touch points can be from the 'next' path point.

This also allows for other things in the video you link, like dropping images at each point along the (interpolated) path and animating those images around.

like image 2
Wain Avatar answered Nov 14 '22 07:11

Wain


You can calculate points along a UIBezierPath with the code in this open source repo:

https://github.com/ImJCabus/UIBezierPath-Length

If you have your 'a' character represented as a bezier path, you can calculate the red points in the image above by writing something like:

UIBezierPath *path = /* ... */;
NSUInteger subdivisions = 100;//however precise you want to be
for(NSUInteger i = 0; i < subdivisions; i++) {
    CGFloat percent = (CGFloat)i/subdivisions;
    CGPoint point = [path pointAtPercentOfLength:percent];
    //draw a dot at this point, or trace it, or whatever you want to do
}
like image 2
BradB Avatar answered Nov 14 '22 07:11

BradB