代码之家  ›  专栏  ›  技术社区  ›  Senseful

在绘图应用程序(例如草图)中指定具有角半径的UIBezierPath点

  •  -1
  • Senseful  · 技术社区  · 6 年前

    在草图等绘图应用程序中,绘制向量时,可以分别为每个点指定自定义角半径。

    例如,这里是一个5点向量,中点的角半径设置为17:

    enter image description here

    (左上角和右下角的点也有自定义半径。)

    在Swift中,我可以使用UIBezierPath绘制路径,但当我指定 addLine ,我只能选择指定一个点。我没有指定角半径的选项。

    我该如何给予 addLine 拐角半径?

    似乎我应该能够使用addCurve或addArc来实现我想要的结果,但我不确定应该为那些获得期望结果的人提供什么值。

    1 回复  |  直到 6 年前
        1
  •  3
  •   Senseful    6 年前

    您必须使用addLine,而不是addLine addArc 。addArc的值取决于上一个/下一个点。因此,任何辅助函数都应该包含要使用的整个点数组。

    如果你能想出如何在两条线之间画出一条曲线,那么同样的算法可以在整个形状上重复。

    为了让事情更容易理解,请参考此图(代码也将引用这些要点):

    enter image description here

    目标是找出圆的中心和开始/结束角度。

    首先,你应该 grab the CGFloat and CGPoint extensions from here .

    接下来添加以下帮助函数:

    extension Collection where Index == Int {
        func items(at index: Index) -> (previous: Element, current: Element, next: Element) {
            precondition(count > 2)
            let previous = self[index == 0 ? count - 1 : index - 1]
            let current = self[index]
            let next = self[(index + 1) % count]
            return (previous, current, next)
        }
    }
    
    /// Returns ∠abc (i.e. clockwise degrees from ba to bc)
    //
    //  b - - - a
    //   \
    //    \
    //     \
    //      c
    //
    func angleBetween3Points(_ a: CGPoint, _ b: CGPoint, _ c: CGPoint) -> CGFloat {
        let xbaAngle = (a - b).angle 
        let xbcAngle = (c - b).angle // if you were to put point b at the origin, `xbc` refers to the angle formed from the x-axis to the bc line (clockwise)
        let abcAngle = xbcAngle - xbaAngle
        return CGPoint(angle: abcAngle).angle // normalize angle between -π to π
    }
    
    func arcInfo(
        previous: CGPoint,
        current: CGPoint,
        next: CGPoint,
        radius: CGFloat)
        -> (center: CGPoint, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool)
    {
        let a = previous
        let b = current
        let bCornerRadius: CGFloat = radius
        let c = next
    
        let abcAngle: CGFloat = angleBetween3Points(a, b, c)
        let xbaAngle = (a - b).angle
        let abeAngle = abcAngle / 2
    
        let deLength: CGFloat = bCornerRadius
        let bdLength = bCornerRadius / tan(abeAngle)
        let beLength = sqrt(deLength*deLength + bdLength*bdLength)
    
        let beVector: CGPoint = CGPoint(angle: abcAngle/2 + xbaAngle)
    
        let e: CGPoint = b + beVector * beLength
    
        let xebAngle = (b - e).angle
        let bedAngle = (π/2 - abs(abeAngle)) * abeAngle.sign() * -1
    
        return (
            center: e,
            startAngle: xebAngle - bedAngle,
            endAngle: xebAngle + bedAngle,
            clockwise: abeAngle < 0)
    }
    
    func addArcs(to path: UIBezierPath, pointsAndRadii: [(point: CGPoint, radius: CGFloat)]) {
        precondition(pointsAndRadii.count > 2)
    
        for i in 0..<pointsAndRadii.count {
            let (previous, current, next) = pointsAndRadii.items(at: i)
            let (center, startAngle, endAngle, clockwise) = arcInfo(
                previous: previous.point,
                current: current.point,
                next: next.point,
                radius: current.radius)
    
            path.addArc(withCenter: center, radius: current.radius, startAngle: startAngle, endAngle: endAngle, clockwise: clockwise)
        }
    }
    
    

    最后,您现在可以使用这些函数轻松渲染绘图应用程序中定义的任何向量:

    override func draw(_ rect: CGRect) {
        let grayPath = UIBezierPath()
        addArcs(
            to: grayPath,
            pointsAndRadii: [
                (point: CGPoint(x: 100, y: 203), radius: 0),
                (point: CGPoint(x: 100, y: 138.62), radius: 33),
                (point: CGPoint(x: 173.78, y: 100), radius: 0),
                (point: CGPoint(x: 139.14, y: 172.51), radius: 17),
                (point: CGPoint(x: 231, y: 203), radius: 3),
            ])
        grayPath.close()
        grayPath.lineWidth = 5
        UIColor.gray.setStroke()
        grayPath.stroke()
    }
    
    

    这将产生精确的复制品:

    enter image description here