js过圆外一点的直线与圆相切的切点坐标计算 您所在的位置:网站首页 圆外一点作圆切线的方程 js过圆外一点的直线与圆相切的切点坐标计算

js过圆外一点的直线与圆相切的切点坐标计算

2024-03-20 16:43| 来源: 网络整理| 查看: 265

由圆外一点P1(x1,y1)向圆(x - a)2 + (y - b)2 = R2作切线,切线与圆相切的切点是P0(x0,y0) image.png

方法一: 公式法

先求直线P0P1直线方程 因为P0P1⊥OP0 向量OP0 = (x0 - a,y0 - b ) 向量P0P1 = (x1 - x0, y1 - y0) 所以 向量P0P1 点乘 向量OP0 = 0 所以 (x0 - a)* (x1 - x0) + (y0 - b)(y1 - y0) = 0 并且 因为点P0是圆上的点,=> (x0 - a)2 + (y0 - b)2 = R2

解方程就可以求出P0(x0,y0)坐标,当然这样求法太复杂,我没有耐心解下去了,我们换个思维求我们把圆换成这样,相当于把整个系统做了一次相对位移,移动到0点 x2 + y2 = R2 image.png 那么经过点P0(x0,y0)这个圆的的切线方程就是 x0x1 + y0y1 = R2 并且x20 + y20 = R2 所以 (x21 + y21)x20 -2R2x1x0 + R4 - y21R2 = 0 这样就是求解一元二次方程 m = (x21 + y21) / R2

x0 = (-b±√(b2 - 4ac)) / (2a) = (x1 ± y1√(m-1)) / m y0 = (y1 ± x1√(m-1)) / m 至此切点是找到了, 然后再位移下就是真正的点了

p

let canvas = document.getElementById("target"); canvas.width = document.body.clientWidth; canvas.height = document.body.clientHeight; let ctx = canvas.getContext("2d"); let isDrawing = false; canvas.addEventListener('mousedown', e => { let x = e.offsetX; let y = e.offsetY; isDrawing = true; pointP = {x:x,y:y}; update(); }); canvas.addEventListener('mousemove', e => { if (isDrawing === true) { let x = e.offsetX; let y = e.offsetY; pointP = {x:x,y:y}; update(); } }); canvas.addEventListener('mouseup', e => { if (isDrawing === true) { isDrawing = false; } }); let pointP = {x: 300,y: 100}; //圆心坐标 let pointCenter ={x:250,y:250}; //圆点半径 let radius = 100; function update(){ ctx.clearRect(0,0,canvas.width,canvas.height); //圆心 drawPoint(pointCenter.x,pointCenter.y,"#986923"); //圆 ctx.beginPath(); ctx.arc(pointCenter.x,pointCenter.y,radius,0,2*Math.PI,true); ctx.stroke(); ctx.closePath(); drawPoint(pointP.x,pointP.y,"#986923"); let arrayQieDian = calcQieDian(pointCenter.x,pointCenter.y,radius,pointP); arrayQieDian.forEach((point)=>{ drawPoint(point.x,point.y,"#f00"); drawLine(pointP,point); drawLine(pointCenter,point); }); } update(); function drawPoint(x,y,color){ ctx.beginPath(); ctx.fillStyle=color; ctx.arc(x,y,5,0,2*Math.PI,true); ctx.fill(); } function drawLine(pointStart,pointEnd) { ctx.beginPath(); ctx.moveTo(pointStart.x,pointStart.y); ctx.lineTo(pointEnd.x,pointEnd.y); ctx.stroke(); } function calcQieDian(cx,cy,radius,point) { //将实际的点做一次转换,因为下面的计算都是暗转圆心都是在圆点计算的 let outsideX = point.x - cx; let outsideY = point.y - cy; let m = (Math.pow(outsideX,2)+Math.pow(outsideY,2))/Math.pow(radius,2); //求出的结果将会有4种排列 let pointA = { x:((outsideX+outsideY*Math.sqrt(m-1))/m), y:((outsideY+outsideX*Math.sqrt(m-1))/m) }; let pointB = { x:((outsideX-outsideY*Math.sqrt(m-1))/m), y:((outsideY-outsideX*Math.sqrt(m-1))/m) }; let pointC = { x:((outsideX+outsideY*Math.sqrt(m-1))/m), y:((outsideY-outsideX*Math.sqrt(m-1))/m) }; let pointD = { x:((outsideX-outsideY*Math.sqrt(m-1))/m), y:((outsideY+outsideX*Math.sqrt(m-1))/m) }; let array = []; //实际上只会有2个切点,利用向量垂直,点乘结果是0来判断哪个是有效的 //因为浮点数不能精确到0,所以这里用了1e-10 if(Math.abs(pointA.x*(outsideX -pointA.x ) + pointA.y * (outsideY - pointA.y)) { if (isDrawing === true) { isDrawing = false; eventCircle = false; } }); let pointP = {x: 300,y: 100}; //圆心坐标 let pointCenter ={x:250,y:250}; //圆点半径 let radius = 100; function angle(p1,p2) { return Math.atan2(p1.y - p2.y, p1.x - p2.x); } function getVector(cx, cy, a, r) { return {x:cx + r * Math.cos(a), y:cy + r * Math.sin(a)}; } function update(){ ctx.clearRect(0,0,canvas.width,canvas.height); //圆心 drawPoint(pointCenter.x,pointCenter.y,"#986923"); //圆 ctx.beginPath(); ctx.arc(pointCenter.x,pointCenter.y,radius,0,2*Math.PI,true); ctx.stroke(); ctx.closePath(); drawPoint(pointP.x,pointP.y,"#986923"); let p = calcQieDian3(pointCenter,radius,pointP); drawText("P1",p.p1.x,p.p1.y); drawText("P2",p.p2.x,p.p2.y); drawText("M",pointP.x,pointP.y); drawText("O",pointCenter.x,pointCenter.y); drawPoint(p.p1.x,p.p1.y,"#f00"); drawPoint(p.p2.x,p.p2.y,"#f00"); drawLine(pointCenter,p.p1); drawLine(pointCenter,p.p2); drawLine(pointCenter,pointP); drawLine(pointP,p.p1); drawLine(pointP,p.p2); drawLine({x:0,y:pointCenter.y},{x:canvas.width,y:pointCenter.y}); } update(); function drawPoint(x,y,color){ ctx.beginPath(); ctx.fillStyle=color; ctx.arc(x,y,5,0,2*Math.PI,true); ctx.fill(); } function drawLine(pointStart,pointEnd) { ctx.beginPath(); ctx.moveTo(pointStart.x,pointStart.y); ctx.lineTo(pointEnd.x,pointEnd.y); ctx.stroke(); } function calcQieDian3(pointCircle,radius,point) { let d = Math.sqrt(Math.pow(pointCircle.x - point.x,2) + Math.pow(pointCircle.y - point.y,2)); const angleBetweenCenters = angle(point,pointCircle); const spread = Math.acos(radius/ d); const angle1 = angleBetweenCenters + spread; const angle2 = angleBetweenCenters - spread; const p1 = getVector(pointCircle.x,pointCircle.y, angle1, radius); const p2 = getVector(pointCircle.x,pointCircle.y, angle2, radius); return {p1:p1,p2:p2}; } function drawText(text,x,y) { ctx.beginPath(); ctx.font="30px Arial"; ctx.fillText(text,x,y); ctx.stroke(); }


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有