簡體   English   中英

如何從 cgpoints 數組中獲取中心點?

[英]How to get a centerpoint from an array of cgpoints?

我有一個 CGPoints 數組:

let points = [(1234.0, 1053.0), (1241.0, 1111.0), (1152.0, 1043.0)]

我要做的是找到 CGPoints 的中心。 所以我可以在所有點的中心放置一個 object。 如果這是一個讓我們說整數的數組,我會像這樣減少數組:

points.reduce(0, +)

然后除以總數組計數以獲得平均值。 但由於它的 CGPoints 這不起作用。 關於如何實現這一目標的任何想法?

Farhad 已經回答了如何平均所有這些數據點的問題。 (+1)

但這真的是你想要的嗎? 考慮:

這個圖片

這個凸形由藍點定義,但所有這些點的平均值是紅點。 但這並不是真正在形狀的中心。 它是向上傾斜的,因為頂部附近有五個數據點,而下方只有兩個。 這個凸的形狀很好地說明了這個問題,但它不需要是凸的來表現這個問題。 任何數據點分布不均勻的情況都可能表現出這種行為。

綠點是多邊形的質心 您可以看到它低於邊界框的中心(上圖中的十字准線),正如您所期望的那樣。 對於簡單的形狀,這可能是放置 label 的更好位置。 這可以計算如下:

extension Array where Element == CGPoint {
    /// Calculate signed area.
    ///
    /// See https://en.wikipedia.org/wiki/Centroid#Of_a_polygon
    ///
    /// - Returns: The signed area

    func signedArea() -> CGFloat {
        if isEmpty { return .zero }

        var sum: CGFloat = 0
        for (index, point) in enumerated() {
            let nextPoint: CGPoint
            if index < count-1 {
                nextPoint = self[index+1]
            } else {
                nextPoint = self[0]
            }

            sum += point.x * nextPoint.y - nextPoint.x * point.y
        }

        return sum / 2
    }

    /// Calculate centroid
    ///
    /// See https://en.wikipedia.org/wiki/Centroid#Of_a_polygon
    ///
    /// - Note: If the area of the polygon is zero (e.g. the points are collinear), this returns `nil`.
    ///
    /// - Parameter points: Unclosed points of polygon.
    /// - Returns: Centroid point.

    func centroid() -> CGPoint? {
        if isEmpty { return nil }

        let area = signedArea()
        if area == 0 { return nil }

        var sumPoint: CGPoint = .zero

        for (index, point) in enumerated() {
            let nextPoint: CGPoint
            if index < count-1 {
                nextPoint = self[index+1]
            } else {
                nextPoint = self[0]
            }

            let factor = point.x * nextPoint.y - nextPoint.x * point.y
            sumPoint.x += (point.x + nextPoint.x) * factor
            sumPoint.y += (point.y + nextPoint.y) * factor
        }

        return sumPoint / 6 / area
    }

    func mean() -> CGPoint? {
        if isEmpty { return nil }

        return reduce(.zero, +) / CGFloat(count)
    }
}

extension CGPoint {
    static func + (lhs: CGPoint, rhs: CGPoint) -> CGPoint {
        CGPoint(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
    }

    static func - (lhs: CGPoint, rhs: CGPoint) -> CGPoint {
        CGPoint(x: lhs.x - rhs.x, y: lhs.y - rhs.y)
    }

    static func / (lhs: CGPoint, rhs: CGFloat) -> CGPoint {
        CGPoint(x: lhs.x / rhs, y: lhs.y / rhs)
    }

    static func * (lhs: CGPoint, rhs: CGFloat) -> CGPoint {
        CGPoint(x: lhs.x * rhs, y: lhs.y * rhs)
    }
}

你會像這樣計算質心:

let points = [(1234.0, 1053.0), (1241.0, 1111.0), (1152.0, 1043.0)]
    .map(CGPoint.init)

guard let point = points.centroid() else { return }

FWIW 具有復雜的凹面形狀,即使質心也不是最佳的。 請參閱找到不規則形狀多邊形的“視覺”中心的最快方法是什么?

您可以重載 swift 中的操作,例如:

func +(lhs: CGPoint, rhs: CGPoint) -> CGPoint {
    CGPoint(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
}
func /(lhs: CGPoint, rhs: CGFloat) -> CGPoint {
    CGPoint(x: lhs.x / rhs, y: lhs.y / rhs)
}


let points = [(1234.0, 1053.0), (1241.0, 1111.0), (1152.0, 1043.0)].map({CGPoint(x: $0.0, y: $0.1)})

let m = points.reduce(.zero, +) / CGFloat(points.count)
print(m)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM