Get center points of a UIBezierpath
Asked Answered
M

5

10

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

Millpond answered 18/8, 2016 at 8:4 Comment(11)
https://mcmap.net/q/137067/-draw-dotted-not-dashed-line-with-ibdesignable-in-2017Illusory
Its not working. I have a curved path for different alphabets. Also I want to draw inside the path, not the outline.Millpond
Did u try this #34694151 ?Adactylous
Does your path outline the white area in your image? Or does your path run along the center of the white area?Melonymelos
@robmayoff - my path outline the white area. I want to draw line in the center of the white area. I have added the code as well, how I am doing it.Millpond
There is no easy way to do what you're asking. Maybe you should look into getting a stroke-based font, also called an engraving font. Hershey Simplex is the easiest to obtain.Melonymelos
@robmayoff - Any way out that I can draw the coler over the text and can come to know how much area is covered by drawing and how much is left. Also I want to point the hand in which direction to go for drawing. I want something like this : youtube.com/watch?v=LxS1vq8h35gMillpond
@Nikki Visit this link may be it help you bignerdranch.com/blog/core-graphics-part-three-linesLuciferin
can you fix the question picture? it is broken. Upload the picture to stackoverflow.Percipient
@Millpond did you got the solution?Tranche
any solution for this problem? I am also stuck with this issue ... I got bezierPath of character with given font. I found if user touch inside the path or not. I want to draw along to touch the path.Overwhelming
W
3

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];
Werth answered 24/8, 2016 at 7:18 Comment(7)
I am not getting hit using this method,Millpond
try to use CGPathContainsPoint(path, NULL, point, NO); instead of CGContextPathContainsPoint(context, point, mode);Werth
To draw a dashed line , you link draws from start to end point. My characters are curved.Millpond
I have update the answer, showing to add dash line to any UIBezierPath and CAShapeLayerWerth
Hey. I have updated the code as you told but still dashed line is not appearing. I have updated the code aboveMillpond
And also I guess the dashed line from your code will be drawn in the outline. Not in the center as my requirement. Please if you can check the image attached i the questionMillpond
@Millpond i am also trying the same. Please upload the solution if you were able to do it.Keelson
B
2

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.

Bazil answered 23/8, 2016 at 15:13 Comment(0)
L
2

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
}
Lonnielonny answered 26/8, 2016 at 19:23 Comment(2)
This comes out to be the outline points. Not the center points.Millpond
If your input is the outline of a glyph from CTFontCreatePathForGlyph and you want an algorithm that outputs a path that represents how one would draw the glyph, that's going to be very challenging (if possible at all) to get it working 100% correctly. There are a ton of wacky glyphs that your algorithm would potentially need to interpret, so there are hardly any assumptions you can make when analyzing your input. In the video you linked to, I would guess they started with the center path and drew the character using that path at a certain width instead of using an outline.Lonnielonny
P
0

Try this code to draw red dot line:

CAShapeLayer* dotLayer   = [CAShapeLayer layer];
dotLayer.path            = path.CGPath;
dotLayer.strokeColor     = [UIColor redColor].CGColor;
dotLayer.lineWidth       = 2;//or the diameter of the dots
dotLayer.lineDashPattern = @[@0, @10];//change 10 to the distance between dots
dotLayer.lineJoin        = kCALineJoinRound;
dotLayer.lineCap         = kCALineCapRound;
Parathion answered 25/8, 2016 at 17:27 Comment(1)
If you read the question, problem is to draw the dotted line in the center of the UIBezierpath.Camilacamile
I
0

What you want is rather complicated, but I believe it can be achieved with the following steps:

1) get the letter outline path using your method

2) create a new path using CGPathCreateCopyByStrokingPath with lineWidth equal 1

3) get all segments of the new path

4) find all intersection points of all segments

5) determine which intersecting lines are adjacent and use their intersection points to form a center line

6) repeat steps 2-6 increasing the lineWidth value in step 2

Ingmar answered 29/8, 2016 at 21:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.