不规则多边形等间距等比例缩小
等间距缩小算法
原理参考:https://blog.csdn.net/leon_zeng0/article/details/73500174
多边形的重心点为
G
(
x
,
y
)
G(x,y)
G(x,y),重心的求解方法见我的另外一篇博客不规则多边形重心求解。设需要向内缩进的距离为
L
L
L,则
∣
v
1
∣
=
∣
v
2
∣
\left |\boldsymbol{v_1}\right |=\left |\boldsymbol{v_2} \right |
∣v1∣=∣v2∣
A
′
A’
A′的坐标可以表示为
A
′
=
A
+
(
v
1
+
v
2
)
=
A
+
L
sin
θ
(
v
1
∣
v
1
∣
+
v
2
∣
v
2
∣
)
A’=A+(\boldsymbol{v_1}+\boldsymbol{v_2})=A+\frac{L}{\sin{\theta}}\left ( \frac{\boldsymbol{v_1}}{\left |\boldsymbol{v_1}\right |} + \frac{\boldsymbol{v_2}}{\left |\boldsymbol{v_2}\right |}\right )
A′=A+(v1+v2)=A+sinθL(∣v1∣v1+∣v2∣v2)
其中,
sin
θ
=
∣
v
1
×
v
2
∣
∣
v
1
∣
∣
v
2
∣
\sin{\theta}=\frac{\left |\boldsymbol{v_1} \times \boldsymbol{v_2}\right |}{\left |\boldsymbol{v_1}\right |\left |\boldsymbol{v_2}\right |}
sinθ=∣v1∣∣v2∣∣v1×v2∣
- 创建内部等间距点
def create_equal_dist_points(points, L, gravity_point):
"""
@brief 创建等间距的边界内点
@param points The points
@param L 间距
@param gravity_point 重心
@return 创建等间距点
"""
# 判断输入条件
if len(points) <= 2 or not gravity_point:
return list()
length = len(points)
# 获取边的单位向量
normal_vector = list()
for i in range(length):
vector_x = points[(i + 1) % length][0] - points[i][0]
vector_y = points[(i + 1) % length][1] - points[i][1]
normal_vector_x = vector_x / math.sqrt(vector_x ** 2 + vector_y ** 2)
normal_vector_y = vector_y / math.sqrt(vector_x ** 2 + vector_y ** 2)
normal_vector.append([normal_vector_x, normal_vector_y])
# 获取夹角
theta = list()
for i in range(length):
x1 = normal_vector[i][0]
y1 = normal_vector[i][1]
x2 = normal_vector[(i - 1) % len(points)][0]
y2 = normal_vector[(i - 1) % len(points)][1]
sin_theta = abs(x1 * y2 - x2 * y1)
theta.append(sin_theta)
# 计算向内扩展的点坐标
new_points = list()
for i in range(length):
point = points[i]
x1 = -normal_vector[(i - 1) % len(points)][0]
y1 = -normal_vector[(i - 1) % len(points)][1]
x2 = normal_vector[i][0]
y2 = normal_vector[i][1]
add_x = L / theta[i] * (x1 + x2)
add_y = L / theta[i] * (y1 + y2)
new_point_x = point[0] + add_x
new_point_y = point[1] + add_y
new_point = [new_point_x, new_point_y]
# 判断点是否在里面,如果不在减去2倍的矢量增量
if get_distance(new_point, gravity_point) > get_distance(point, gravity_point):
new_point[0] -= 2 * add_x
new_point[1] -= 2 * add_y
new_points.append(new_point)
return new_points
其中的 get_distance
函数定义如下
def get_distance(point1, point2):
"""
@brief 计算两点之间的距离
@param point1 The point 1
@param point2 The point 2
@return The distance.
"""
diff = [(point1[x] - point2[x]) for x in range(2)]
dist = math.sqrt(diff[0] ** 2 + diff[1] ** 2)
return dist
等比例缩小算法
多边形的重心点位置为
G
(
x
,
y
)
G(x,y)
G(x,y),设缩小比例为
λ
\lambda
λ,那么根据向量的比例关系可以得到
λ
G
A
⃗
=
G
A
′
⃗
\lambda \vec{GA}= \vec{G{A}’}
λGA=GA′
设
A
A
A的坐标为
A
(
x
1
,
y
1
)
A(x_1,y_1)
A(x1,y1),
A
’
A’
A’的坐标为
A
′
(
x
′
,
y
′
)
A'(x’,y’)
A′(x′,y′),那么根据上述公式即可求得
{
x
′
=
λ
(
x
1
−
x
)
+
x
y
′
=
λ
(
y
1
−
y
)
+
y
\left\{\begin{matrix} x’ =\lambda(x_1-x)+x\\ y’ =\lambda(y_1-y)+y \end{matrix}\right.
{x′=λ(x1−x)+xy′=λ(y1−y)+y
- 创建内部等比例点
def create_equal_ratio_points(points, ratio, gravity_point):
"""
@brief 创建等比例的点
@param points The points
@param ratio The ratio
@param gravity_point The gravity point
@return { description_of_the_return_value }
"""
# 判断输入条件
if len(points) <= 2 or not gravity_point:
return list()
new_points = list()
length = len(points)
for i in range(length):
vector_x = points[i][0] - gravity_point[0]
vector_y = points[i][1] - gravity_point[1]
new_point_x = ratio * vector_x + gravity_point[0]
new_point_y = ratio * vector_y + gravity_point[1]
new_point = [new_point_x, new_point_y]
new_points.append(new_point)
return new_points
判断点是否位于多边形内部
参考W. Randolph Franklin 提出的PNPoly算法https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html ,总体来说就是使用射线法,代码很简单。
假设条件:
- 射线经过的点属于射线以上的一侧
- 点与多边形顶点重合直接算内部点
判断准则:
如果以测试点为起点发出一条射线,经过多边形的边数为奇数,那么该点就位于多边形内,否则位于多边形外面。
在上图中,点X经过了两条边,属于外部点,点Y经过了BC这条边,所以属于内部点,点Z没有经过任何边(边数为0),所以属于外部点。
对于不规则的多边形,判断算法步骤为:
- 如果测试点是顶点,则返回True
- 如果测试点位于多边形的AABB最小包围盒之外,则直接返回False
- 开始依次遍历所有的顶点
- 如果被测试点位于本次循环的两个相邻点纵坐标范围之内并且位于两个相邻点拟合直线的下方,则对返回的布尔量取反
- 返回布尔量
- 射线法判断点是否位于多边形内部
def is_inner_point(points, test_point):
"""
@brief 判断一个点是否在多边形的内部
@param points 多边形外围点,按顺序存储
@param test_point 测试点
@return True if inner point, False otherwise.
"""
# 输入是否合理
if not test_point or not points or len(points) <= 2:
return False
# 是否为边界顶点
if test_point in points:
return True
x = test_point[0]
y = test_point[1]
# 判断是否在多边形的AABB包围盒之内,如果不在,则不需要进行后续的判断
xmin = np.min([index[0] for index in points])
xmax = np.max([index[0] for index in points])
ymin = np.min([index[1] for index in points])
ymax = np.max([index[1] for index in points])
if (x < xmin) or (x > xmax) or (y < ymin) or (y > ymax):
return False
# 根据射线法判断
res = False
length = len(points)
for i in range(length):
x1 = points[i][0]
y1 = points[i][1]
x2 = points[(i - 1) % len(points)][0]
y2 = points[(i - 1) % len(points)][1]
# 判断纵坐标y是否在本次循环所测试的两个相邻点纵坐标范围之内
if (y2 > y) != (y1 > y):
# 判断点是否在两点直线下方
if x < ((x2 - x1) * (y - y1) / (y2 - y1) + x1):
res = not res
return res