理论来自论文:https://www.doc88.com/p-7189543163840.html 如果看不了就搜一下我的这篇博客的标题吧,至少写博客的时候是能看的
用js实现了这篇论文,命名也是用ABCD比较容易对应上。红色是点,蓝色是包含所有点的最小圆
初始有5个点,和对应的圆,包含这5个点。
鼠标点击一个空位置可以生成一个新点,点击已有的点会删除这个点,每次点击都会重新渲染
下面是完整代码,复制到一个html文件里双击打开就可以用
<!DOCTYPE html> <html> <head> <style> canvas{ background-color: lightgrey; } </style> </head> <body> <canvas width="1600" height="1000"></canvas> </body> <script> var points=[ {x: 500, y: 500}, {x: 530, y: 500}, {x: 570, y: 530}, {x: 530, y: 560}, {x: 500, y: 560} ]; //两点距离 function getD(A,B){ return Math.sqrt((A.x-B.x)**2 + (A.y-B.y)**2); } //三角形外接圆 function circleCenter(A, B, C) { let yDelta_a = B.y - A.y; let xDelta_a = B.x - A.x; let yDelta_b = C.y - B.y; let xDelta_b = C.x - B.x; if(xDelta_a==0||xDelta_b==0||yDelta_a==0)//边界情况,把点换个位置 return circleCenter(B,C,A); let center = {}; let aSlope = yDelta_a/xDelta_a; let bSlope = yDelta_b/xDelta_b; center.x = (aSlope*bSlope*(A.y - C.y) + bSlope*(A.x + B.x) - aSlope*(B.x+C.x) )/(2* (bSlope-aSlope) ); center.y = -1*(center.x - (A.x+B.x)/2)/aSlope + (A.y+B.y)/2; center.r=getD(center,A);//求出圆心之后随便跟一个点算距离就是半径 return center; } //包含三点的最小圆 function minCircleOf3(A,B,C){ let edges=new Array(3); let points=[A,B,C]; for(let i=0;i<3;i++){ let cur=points[i],next=points[(i+1)%3]; edges[i]=(cur.x-next.x)**2 + (cur.y-next.y)**2; } let sum=edges.reduce((a,b)=>a+b); let max=Math.max(...edges); if(max<sum/2) return circleCenter(A,B,C); let index=edges.indexOf(max); let p1=points[index],p2=points[(index+1)%3]; let x=(p1.x+p2.x)/2; let y=(p1.y+p2.y)/2; let r=Math.sqrt(max)/2; return {x,y,r}; } //距离target的最远点 function getfarthest(target){ let d=points.map(p=>getD(p,target)); let res=0; for(let i=1;i<d.length;i++){ if(d[i]>d[res]) res=i; } return {index:res, d:d[res]}; } function minCircle(){ let ABC=[points[0],points[parseInt(points.length/3)],points[parseInt(points.length*2/3)]]; let circle=minCircleOf3(...ABC); let D=getfarthest(circle); let t=0; while(D.d-circle.r>0.1&&t++<10){//兼容误差 let next=new Array(3); for(let i=0;i<3;i++){ let cp=ABC.slice(); cp[i]=points[D.index]; next[i]=minCircleOf3(...cp); } let max=0; for(let i=1;i<3;i++) if(next[i].r>next[max].r) max=i; circle=next[max]; ABC[max]=points[D.index]; D=getfarthest(circle); } return circle; } function render(){ let canvas=document.getElementsByTagName('canvas')[0]; let ctx=canvas.getContext('2d'); canvas.width=canvas.width;//to clear ctx.fillStyle = 'red'; for(let point of points){ ctx.beginPath(); ctx.arc(point.x, point.y, 3, 0, 2*Math.PI); ctx.closePath(); ctx.fill(); } if(points.length<3) return; let circle=minCircle(); ctx.strokeStyle='#0000ff50'; ctx.lineWidth=6; ctx.beginPath(); ctx.arc(circle.x,circle.y,circle.r,0,2*Math.PI); ctx.stroke(); } render(); document.getElementsByTagName('canvas')[0].addEventListener('click',e=>{ let x=e.offsetX,y=e.offsetY; let newPoints=points.filter(u=>getD(u,{x,y})>6); if(newPoints.length==points.length){ points.push({x,y}); } else{ points=newPoints; } render(); }); </script> </html>