如下图所示,黑色多边形是原多边形,绿色多边形是扩展多边形,红色多边形是收缩多边形。算法要实现的效果就是这样。
首先,我们用一个点的数组表示多边形,形如Point[] points。接下来是数学公式的推导,我们用向量法解决这个问题。
对于每一个点,它都有两个邻点,如下图所示,点P的邻点是P1和P2。那我们要求的,其实就是点Q。Q到PP1和PP2的距离都是L。
如图所示,明显向量PQ就是向量v1+v2,而v1的方向由PP1确定,v2的方向由PP2确定。易证明,v1、v2的长度是相等的。
向量PP1是(x1-x,y1-y),我们用(v1x,v1y)表示,
向量PP2是(x2-x,y2-y),我们用(v2x,v2y)表示。
我们把v1的长度归一化,设n1=norm(v1x,v2y),则v1为(v1x/n1,v1y/n1)。v2同理,不赘述。
那么向量PQ就是v1+v2,即(v1x/n1+v2x/n2+v1y/n1+v2y/n2),我们设其为(vx,vy)。
我们这里还要对PQ进行长度归一化,设n=norm(vx,vy),向量PQ为(vx/n,vy/n)。
好了,到现在为止,向量PQ的方向已经确定,剩下就是长度的问题了。
我们先求v1、v2的夹角。使用余弦公式,cosθ=(v1x*v2x+v1y*v2y)/(n1*n2)。
那|PQ|=L/sin(θ/2)。
根据半角公式,我们还能对上式进行化简,省去三角函数的计算。
|PQ|=L/sqrt(1-(v1x*v2x+v1y*v2y)/2)
经过上面的计算,我们已经求出了完整的PQ向量,现加上P点坐标,就得到了Q点的坐标。
C#完整代码如下:
/// <summary>
/// 扩展或收缩
/// </summary>
/// <param name="polygon">多边形顶点</param>
/// <param name="expand">扩展大小,为负则收缩</param>
/// <returns>扩展或收缩后的多边形</returns>
public static Point2D[] Expand(Point2D[] polygon, double expand)
{
Point2D[] new_polygon = new Point2D[polygon.Length];
int len = polygon.Length;
for (int i = 0; i < len; i++)
{
Point2D p = polygon[i];
Point2D p1 = polygon[i == 0 ? len - 1 : i - 1];
Point2D p2 = polygon[i == len - 1 ? 0 : i + 1];
double v1x = p1.X - p.X;
double v1y = p1.Y - p.Y;
double n1 = norm(v1x, v1y);
v1x /= n1;
v1y /= n1;
double v2x = p2.X - p.X;
double v2y = p2.Y - p.Y;
double n2 = norm(v2x, v2y);
v2x /= n2;
v2y /= n2;
double l = -expand / Math.Sqrt((1 - (v1x * v2x + v1y * v2y)) / 2);
double vx = v1x + v2x;
double vy = v1y + v2y;
double n = l / norm(vx, vy);
vx *= n;
vy *= n;
new_polygon[i] = new Point2D(vx + p.X, vy + p.Y);
}
return new_polygon;
}
private static double norm(double x, double y)
{
return Math.Sqrt(x * x + y * y);
}