【问题标题】:How to get a centerpoint from an array of cgpoints?如何从 cgpoints 数组中获取中心点?
【发布时间】:2020-09-04 20:38:08
【问题描述】:

我有一个 CGPoints 数组:

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

我要做的是找到 CGPoints 的中心。所以我可以在所有点的中心放置一个对象。如果这是一个整数数组,我会像这样减少数组:

points.reduce(0, +)

然后除以数组总数得到平均值。但由于它的 CGPoints 这不起作用。关于如何实现这一点的任何想法?

【问题讨论】:

    标签: ios arrays swift cgpoint


    【解决方案1】:

    Farhad 已经回答了有关如何平均所有这些数据点的问题。 (+1)

    但这真的是你想要的吗?考虑:

    这个凸形由蓝点定义,但所有这些点的平均值是红点。但这并不是真的在形状的中心。它是向上倾斜的,因为顶部附近有五个数据点,而下方只有两个。这个凸的形状很好地说明了这个问题,但它不需要是凸的来表现这个问题。任何数据点分布不均的情况都可能表现出这种行为。

    绿点是多边形的centroid。您可以看到它落在边界框的中心下方(上图中的十字准线),正如您所期望的那样。对于简单的形状,这可能是放置标签的更好位置。可以这样计算:

    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,具有复杂的凹形形状,即使质心也不是最佳的。见What is the fastest way to find the "visual" center of an irregularly shaped polygon?

    【讨论】:

    【解决方案2】:

    您可以快速重载操作,例如:

    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)
    
    

    【讨论】:

    • map(CGPoint.init)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多