已知正方形顶点、周长上一点及长度,如何确定周长上目标点?
正方形周长上按长度偏移点的最优解法
问题背景
给定正方形的四个顶点向量v1、v2、v3、v4,周长上任意一点p,以及偏移长度n,需要确定周长上经过偏移后的点r。示例代码如下:
let v1 = CGPoint(x: 0.0, y: 0.0) let v2 = CGPoint(x: 10.0, y: 0.0) let v3 = CGPoint(x: 10.0, y: 10.0) let v4 = CGPoint(x: 0.0, y: 10.0) let corners = [v1, v2, v3, v4] let p = CGPoint(x: 5.0, y: 0.0) func add(length n: CGFloat, to p: CGPoint, constrainedTo corners: [CGPoint]) -> CGPoint { // …magic!🪄 return r } var n: CGFloat = 17.0 let r = add(length: n, to: p, constrainedTo: corners) // 结果:CGPoint(x: 8.0, y: 10.0) n = -17.0 let r = add(length: n, to: p, constrainedTo: corners) // 结果:CGPoint(x: 2.0, y: 10.0) n = -32.8 let r = add(length: n, to: p, constrainedTo: corners) // 结果:CGPoint(x: 10.0, y: 2.2)
疑问
最有效的方式是循环遍历边,逐步增减长度直到n=0吗?还是存在对应的数学公式可以直接计算?
最优解法:数学公式直接计算
循环遍历边逐步消耗长度的方法虽然直观,但效率远不如直接用数学公式计算——尤其是当偏移长度远大于正方形周长时,循环会重复绕圈遍历多轮边,完全是性能浪费。用数学公式可以一步到位,核心步骤如下:
- 计算总周长:正方形四条边等长,算出单条边的长度后乘4得到
perimeter。 - 压缩偏移量:因为周长是循环的,把
n对perimeter取模,得到normalizedN,直接把超大的偏移量压缩到一个周长范围内。注意负数的处理:取模后要保持负方向的一致性,避免方向错乱。 - 定位p在周长上的位置:遍历正方形的边(最后一个顶点要和第一个顶点相连形成闭合周长),判断
p落在哪条边上,再计算p到这条边起点的距离,累计得到p从周长起始点开始的总长度totalDistanceFromStart。 - 计算目标点的总位置:把
totalDistanceFromStart加上normalizedN得到targetTotalDistance。如果这个值小于0就加perimeter,大于perimeter就减perimeter,确保它落在0到perimeter的范围内。 - 算出目标点坐标:再次遍历边的累计长度,找到
targetTotalDistance对应的边,计算该边上的相对位置,最终得到目标点r的坐标。
两种方法对比
循环遍历的思路是从p所在边开始,每次用当前边的剩余长度抵消n,直到n被耗完。这种方法逻辑简单,但当n是周长的几十上百倍时,会重复遍历很多轮边,效率极低。而数学公式的方法通过偏移量归一化,只需要两次遍历边(一次找p的位置,一次找目标点的边),不管n多大,都能在固定时间内完成计算,性能优势很明显。
内容的提问来源于stack exchange,提问作者forgot
相关产品推荐
相关产品推荐

