UIBezierPath 点的长度百分比

UIBezierPath Percent of Length at Point

我正在构建一个具有一些图形操作功能的应用程序。我将形状存储为 UIBezierPaths,并且我希望允许用户沿线触摸点以创建保存的位置。使用 wonderful answer to this question, and more specifically, this project,我可以在知道点所在长度的百分比的线上放置一个点。这是我的问题的一半。

我想要一种方法来获取路径上的一个点,并得出其长度的百分比。

我的数学功底极差。我已经 studied bezier curves 但我只是没有数学来理解它。

我会谦虚地提出 "go back and learn geometry and trigonometry" 是一个正确的答案,但遗憾的是我目前没有时间。我需要的是一个方法来填写这个方法:

- (CGFloat)percentOfLengthAtPoint:(CGPoint)point onPath:(UIBezierPath*)path

感谢任何帮助!

我有解决我问题的工作代码。我并不为此感到特别自豪;整体技术本质上是对 UIBezierPath 的暴力攻击,如果你仔细想想,这有点有趣。 (请不要胡思乱想)

正如我提到的,我可以使用 allows me to get a point from a given percentage of a line 的方法。我利用这种能力通过 运行 通过 1000 个百分比值找到最接近给定点 的百分比。 也就是说:

从代表用户触摸线上位置的 CGPoint 开始。

let pointA = // the incoming CGPoint

运行 通过 0-1 范围以千计。这是一组我们将要进行暴力破解的百分比,看看我们是否有匹配项。对于每个,我们 运行 pointAtPercentOfLength,来自上面的链接项目。

var pointArray:[[String:Any]] = []
for (var i:Int = 0; i <= 1000; i++) {
    let value = CGFloat(round((CGFloat(i) / CGFloat(1000)) * 1000) / 1000)
    let testPoint = path.pointAtPercentOfLength(value)

    let pointB = CGPoint(x: floor(testPoint.x), y: floor(testPoint.y))
    pointArray.append(["point" : pointB, "percent" : value])
}

这是困难的部分。现在我们获取返回值并计算每个点与触摸点之间的距离。最接近的是我们的获胜者。

// sort the damned array by distance so we find the closest
var distanceArray:[[String:Any]] = []
for point in pointArray {
    distanceArray.append([
        "distance" : self.distanceFrom(point["point"] as! CGPoint, point2: pointA),
        "point" : point["point"],
        "percent" : point["percent"] as! CGFloat
        ])
}

如果你感兴趣,这里是排序函数:

func distanceFrom(point1:CGPoint, point2:CGPoint) -> CGFloat {
    let xDist = (point2.x - point1.x);
    let yDist = (point2.y - point1.y);
    return sqrt((xDist * xDist) + (yDist * yDist));
}

最后,我按值的距离对数组进行排序,并选出最接近的百分比。

let ordered = distanceArray.sort { return CGFloat([=14=]["distance"] as! CGFloat) < CGFloat(["distance"] as! CGFloat) }

ordered 是一个包含 percent 的小字典,它是一行长度百分比的正确值。

我知道这不是漂亮的代码。 我知道。但它完成了工作,而且在计算上似乎并不昂贵。

作为附言,我应该指出似乎适合执行此操作的资源。在我的研究过程中,我阅读了 David Rönnqvist 的 this beautiful article,其中包含一个用于计算沿路径的百分比距离的方程式:

start⋅(1-t)3 + 3⋅c1⋅t(1-t)2 + 3⋅c2⋅t2(1-t) + end⋅t3

在我想到最终解决方案之前,我正要尝试实施它。 数学,伙计。我什至想不通。但如果你比我更有野心,希望用五行代码来替代我的 30 行代码,那么大家将不胜感激!

我认为你的方法很合理,但你可以far更有效率。

无需创建两个字典数组(每个数组包含一千个元素)然后对数组进行排序 - 只需使用 while 循环从 0.0 移动到 1.0,计算距离触摸点并跟踪最小距离。

例如:

    var t:CGFloat = 0.0
    let step:CGFloat = 0.001
    var minDistance:CGFloat = -1.0
    var minPoint:CGPoint = CGPointZero
    var minT:CGFloat = -1;
    while (t<1.0) {

        let point = pointAtPercentOfLength(t)
        let distance:CGFloat = self.distanceFrom(point, point2: pointA)

        if (minDistance == -1.0 || distance < minDistance) {
            minDistance = distance
            minPoint = point
            minT = t
        }
        t += step
    }
    print("minDistance: \(minDistance) minPoint: \(minPoint.x) \(minPoint.y) t\(minT)\n")