我有一个CGPoints数组:
let points = [(1234.0, 1053.0), (1241.0, 1111.0), (1152.0, 1043.0)]
我想做的就是找到CGPoints的中心。因此,我可以将一个对象放置在所有点的中心。如果这是一个可以说整数的数组,则可以这样减少数组:
points.reduce(0, +)
然后除以总数组数即可得出平均值。但是由于它的CGPoints,这是行不通的。关于如何实现这一目标的任何想法?
Farhad回答了有关如何平均所有这些数据点的问题。(+1)
但这真的是您想要的吗?考虑:
This convex shape is defined by the blue points, but the mean of all of those points is the red point. But that’s not really in the center of the shape. It is skewed up because there are five data points near the top, and only two down below. This convex shape illustrates the problem well, but it doesn’t need to be convex to manifest this issue. Any situation where the data points are not relatively evenly distributed can manifest this behavior.
The green point is the centroid of the polygon. You can see that it falls below the center of the bounding box (the crosshairs in the above image), like you’d expect. For simple shapes, that might be a better place to place your label. That can be calculated as follows:
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)
}
}
And you’d calculate the centroid like so:
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具有复杂的凹形形状,即使质心也不是最佳的。请参见找到不规则形状的多边形的“可视”中心的最快方法是什么?
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句