对简单多边形P而言,其小凸包pc是这样一个多边形,如果存在包含p的另一多边形p0,则pc是p0的子集,因此pc是包含p的最小的凸多边形。
首先介绍的算法称为硬币算法,具有逻辑简单,容易实现,并且效率较高的优点。其描述如下:
private void coin()//硬币算法实现 { myar.Clear();//每次进行对myar的数据进行清零,myar为存储硬币算法的点数据的ArrayList int p = 0; for (int i = 0; i < thear.Count; i++) { if (((zuobiao)thear[p]).y > ((zuobiao)thear[i]).y) //找出y最小的极限点,zuobiao为一个包含int x和int y字段的类 p = i; } myar.Add((zuobiao)thear[p]); //将这个点最先存储进myar中,thear为从文件中读取的点数据的Arraylist foreach (zuobiao zb in thear) { myar.Add(zb);//将其他数据加入myar中 } canshu.p0x = ((zuobiao)thear[p]).x;//记录极限点的数据,canshu为记录参数的公共变量 canshu.p0y = ((zuobiao)thear[p]).y; myar.RemoveAt(p + 1);//将极限点被重复存储的删去 CoinSort coinsort = new CoinSort();//对点按顺时针方向排序 myar.Sort(coinsort); int hou, zhong, qian;//hou,zhong,qian代表硬币“后”,“中”,“前” hou = 0; zhong = 1; qian = 2; while (qian < myar.Count)//“前”达到最末则停止 { if (isright(myar[hou], myar[zhong], myar[qian])) //判断是否在“前”是否与“后”“中”形成右拐 { hou++;//形成则全部向前移动 zhong++; qian++; } else { myar.RemoveAt(zhong); //否则删除“中”,因为队列中少了一项,因此原先“前”所表示的次序成为现在“前”的下一个,需要减回来 zhong--;//“后”“中”向后退 qian--; hou--; } } zuobiao temp = new zuobiao(); zuobiao temp2 = new zuobiao(); temp.x = canshu.p0x; temp.y = canshu.p0y; myar.Add(temp); temp2.x = ((zuobiao)myar[0]).x; temp2.y = ((zuobiao)myar[0]).y; myar.Add(temp2);//将极限点和最终点写在myar最后使绘图时构成闭合图形 }
private bool isright(object a, object b, object c)//判断点是否在右侧 { if (((((zuobiao)b).x - ((zuobiao)a).x) * (((zuobiao)c).y - ((zuobiao)a).y) - (((zuobiao)c).x - ((zuobiao)a).x) * (((zuobiao)b).y - ((zuobiao)a).y)) < 0) return true; else return false; }
上凸包算法如下:
private void serialup(int a, int b)//计算上凸包 { double maxdistence = -1;//初始化点到直线的最小距离为-1 int maxnum = 0;//到直线距离最大点的编号 int upnumber = 0;//在直线上的点数 for (int i = 1; i < b - a; i++) { if (!(((zuobiao)myar[a + i]).y < ((zuobiao)myar[a]).y && ((zuobiao)myar[a + i]).y < ((zuobiao)myar[b]).y) && !isright(myar[a], myar[b], myar[a + i])) //若点不在起终点下方并且在直线上方 { upnumber++;//计数器+1 double k = 1.0 * (((zuobiao)myar[b]).y - ((zuobiao)myar[a]).y) / (((zuobiao)myar[b]).x - ((zuobiao)myar[a]).x);//计算斜率 double nowdis = System.Math.Abs((((zuobiao)myar[a + i]).x) * k - ((zuobiao)myar[a + i]).y + ((zuobiao)myar[a]).y - k * ((zuobiao)myar[a]).x) / Math.Sqrt(k * k + 1); //计算点到直线距离距离 if (nowdis > maxdistence)//记录最大距离和最远点编号 { maxdistence = nowdis; maxnum = a + i; } } } if (upnumber > 1)//直线上不止一个点,则分割 { serialup(a, maxnum); serialup(maxnum, b); } else if (upnumber == 1)//若直线上只有一个点,直接存储该点和终点 { newar.Add((zuobiao)myar[maxnum]); newar.Add((zuobiao)myar[b]); return; } else//若直线上没有点,则直接存储终点 { newar.Add((zuobiao)myar[b]); return; } }
private void serialdown(int a, int b)//下凸包计算类似上凸包 { double maxdistence = -1; int maxnum = 0; int upnumber = 0; for (int i = 1; i < b - a; i++) { if (!(((zuobiao)myar[a + i]).y > ((zuobiao)myar[a]).y && ((zuobiao)myar[a + i]).y > ((zuobiao)myar[b]).y) && isright(myar[a], myar[b], myar[a + i])) { double k = 1.0 * (((zuobiao)myar[b]).y - ((zuobiao)myar[a]).y) / (((zuobiao)myar[b]).x - ((zuobiao)myar[a]).x); double nowdis = System.Math.Abs((((zuobiao)myar[a + i]).x) * k - ((zuobiao)myar[a + i]).y + ((zuobiao)myar[a]).y - k * ((zuobiao)myar[a]).x) / Math.Sqrt(k * k + 1); upnumber++; if (nowdis > maxdistence) { maxdistence = nowdis; maxnum = a + i; } } } if (upnumber > 1) { serialdown(maxnum, b);//存点顺序同上凸包相反 serialdown(a, maxnum); } else if (upnumber == 1) { newar.Add((zuobiao)myar[b]); newar.Add((zuobiao)myar[maxnum]); return; } else { newar.Add((zuobiao)myar[b]); return; } }