【问题标题】:Get center points of a UIBezierpath获取 UIBezierpath 的中心点
【发布时间】:2016-12-25 01:42:06
【问题描述】:

无论选择何种字体和大小,我都能生成UIBezierPath 的字符。现在我想在贝塞尔路径之间制作一条蚀刻线。我可以得到贝塞尔路径的中心点吗?或者我可以制作中心虚线并遵循该路径的任何其他方式?

这是我如何做的代码

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;


}

【问题讨论】:

标签: ios objective-c uibezierpath nsbezierpath


【解决方案1】:

我可以得到贝塞尔路径的中心点吗?或任何其他方式 我可以制作中心虚线并沿着那条路走吗?

要知道用户的手指是否沿着路径移动,您必须在路径上进行命中检测

确定触摸事件是否发生在 a 的填充部分 路径,可以使用 UIBezierPath 的 containsPoint: 方法。这 方法针对所有封闭子路径测试指​​定点 path 对象,如果它位于其中任何一个之上或之内,则返回 YES 子路径。

如果您想对路径的描边部分进行命中测试 (而不是填充区域),您必须使用 Core Graphics。这 CGContextPathContainsPoint 函数可让您在任一 当前分配给图形的路径的填充或描边部分 上下文。

以下方法测试指定点是否与指定路径相交。 inFill 参数让调用者指定是否应针对路径的填充部分或描边部分测试该点。调用者传入的路径必须包含一个或多个闭合子路径才能成功检测。

针对路径对象的测试点。

- (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;
}

查看此链接以了解有关Hit-Detection on a Path的更多信息

获取路径中心

您可以将路径的宽度设为myPath.bounds.size.width;,而要获得路径的中心,只需将宽度除以 2。

要绘制虚线,请检查answer

在任何UIBezierPath 上制作虚线为:

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];

要冲刺CAShapeLayer,请使用属性lineDashPattern

shapeView.lineDashPattern = @[@2, @3];

【讨论】:

  • 我没有被这个方法击中,
  • 尝试使用CGPathContainsPoint(path, NULL, point, NO); 而不是CGContextPathContainsPoint(context, point, mode);
  • 要绘制虚线,您可以将绘制从起点链接到终点。我的角色是弯曲的。
  • 我已经更新了答案,显示将虚线添加到任何UIBezierPathCAShapeLayer
  • 嘿。正如您所说,我已经更新了代码,但仍然没有出现虚线。我已经更新了上面的代码
【解决方案2】:

虽然贝塞尔路径是简单绘图的方便选项,但它们是复杂的计算,因此为此目的使用它们会非常昂贵和复杂。那是因为您需要能够计算路径上的任意点并知道先前的点才能知道当前方向。

您需要对角色进行一些其他描述,以便更轻松地了解这些细节。这意味着使用字体的简单矢量(或笔画)描述(如 cmets 中的 rob mayoff 所述)。此描述将字符分解为许多易于使用的直线段。它们都由y=mx+c 计算控制,并且您始终知道每条线段上的 2 个点,因此很容易在它们之间进行插值并了解运动方向。

如果 rob (here) 提供的链接上的描述对于您想要显示字符的大小不够“准确”,您可以创建具有更多点的新版本以实现更接近的近似值到贝塞尔曲线选项。

现在,有了这个描述,你有很多选择......

要沿路径拖动手指,您可以在点之间进行插值,以找到路径上与当前触摸点最近的点,并确定触摸点何时偏离路径太远或与路径相交。这种交叉点处理也是您确定覆盖百分比的方式。您只需要选择插值间隙距离(选择合适的分辨率,而不会创建太多真正靠近的点)以及触摸点距离“下一个”路径点的距离的容差。

这还允许在您链接的视频中进行其他操作,例如在(插值的)路径上的每个点放置图像并为这些图像设置动画。

【讨论】:

    【解决方案3】:

    您可以使用此开源存储库中的代码沿UIBezierPath 计算点数:

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

    如果您将“a”字符表示为贝塞尔路径,则可以通过编写如下代码来计算上图中的红点:

    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
    }
    

    【讨论】:

    • 这是轮廓点。不是中心点。
    • 如果您的输入是来自CTFontCreatePathForGlyph 的字形的轮廓,并且您想要一种算法来输出表示如何绘制字形的路径,那将是非常具有挑战性的(如果可能的话) ) 使其 100% 正确工作。您的算法可能需要解释大量古怪的字形,因此在分析输入时几乎无法做出任何假设。在您链接到的视频中,我猜他们从中心路径开始,并使用该路径以一定宽度而不是使用轮廓来绘制角色。
    【解决方案4】:

    试试这段代码画红点线:

    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;
    

    【讨论】:

    • 如果您阅读问题,问题是在 UIBezierpath 的中心绘制虚线。
    【解决方案5】:

    你想要的比较复杂,但我相信可以通过以下步骤来实现:

    1) 使用你的方法获取字母轮廓路径

    2) 使用CGPathCreateCopyByStrokingPath 创建一个新路径,lineWidth 等于 1

    3)get all segments of the new path

    4)find all intersection points of all segments

    5) 确定哪些相交线是相邻的,并用它们的交点形成中心线

    6) 重复步骤 2-6,增加步骤 2 中的 lineWidth

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-28
      相关资源
      最近更新 更多