如何从 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 的中心。所以我可以把一个物体放在所有点的中心。如果这是一个让我们说整数的数组,我会像这样减少数组:
points.reduce(0, +)
然后除以数组总数得到平均值。但由于它的 CGPoints 这不起作用。关于如何实现这一点有什么想法吗?
您可以在 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: [=10=].0, y: [=10=].1)})
let m = points.reduce(.zero, +) / CGFloat(points.count)
print(m)
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?
我有一个 CGPoints 数组:
let points = [(1234.0, 1053.0), (1241.0, 1111.0), (1152.0, 1043.0)]
我要做的是找到 CGPoints 的中心。所以我可以把一个物体放在所有点的中心。如果这是一个让我们说整数的数组,我会像这样减少数组:
points.reduce(0, +)
然后除以数组总数得到平均值。但由于它的 CGPoints 这不起作用。关于如何实现这一点有什么想法吗?
您可以在 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: [=10=].0, y: [=10=].1)})
let m = points.reduce(.zero, +) / CGFloat(points.count)
print(m)
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?