计算画布中线与圆的交点坐标与线的度数

时间:2017-08-10 08:53:03

标签: javascript canvas

好吧,我会称自己是一位非常优秀的程序员,从未真正遵循学术方式。因此,我的数学知识相当有限,这对我有时需要完成的事情来说是一个小负担。我知道你们中的一些人都非常聪明,喜欢计算这个(对你来说很容易)的事情:

我需要为客户实现一些图表和图表。我选择了OCanvas作为绘图库。它真的有帮助,我需要做的一切都很好,但有一点需要注意。

我有两个圆圈,并且可以通过对象OCanvas为我创建一个函数,在圆圈1的中心和圆圈2的中心之间画一条线。现在我想在线条上添加三角形它是一个箭头。

我知道我需要计算线的倾斜度,以正确对齐三角形。我还需要计算圆边和线之间的交点位置,以定位三角形。

我会为你提供一些代码,但老实说,我不知道如何计算这个。我的已知值包括:行 startX startY endX endY 以及圈子1 X Y 半径 /圈2 X Y radius (X和Y代表圆圈的中心)

提前致谢!

编辑:

视觉表示,需要红色三角形:

enter image description here

5 个答案:

答案 0 :(得分:2)

不完整但可能会给你一个良好的开端。



let c = document.getElementById("myCanvas"),
  ctx = c.getContext("2d"),
  startX = 50,
  startY = 50,
  endX = 250,
  endY = 150,
  rS = 30,
  rE = 50,
  alpha = Math.atan((startY - endY) / (startX - endX)),
  dir = 0, // start 0 end 1
  side = 10;

ctx.beginPath();
ctx.arc(startX, startY, rS, 0, 2 * Math.PI);
ctx.stroke();

ctx.beginPath();
ctx.arc(endX, endY, rE, 0, 2 * Math.PI);
ctx.stroke();

if (startX < endX) {
	endcx = endX + (rE + side/2) * Math.cos(alpha + Math.PI);
	endcy = endY + (rE + side/2) * Math.sin(alpha + Math.PI);
	startcx = startX - (rS + side/2) * Math.cos(alpha - Math.PI);
	startcy = startY - (rS + side/2) * Math.sin(alpha - Math.PI);
	dir = 0;
} else if (startX >= endX) {
	endcx = endX - (rE + side/2) * Math.cos(alpha + Math.PI);
	endcy = endY - (rE + side/2) * Math.sin(alpha + Math.PI);
	startcx = startX + (rS + side/2) * Math.cos(alpha - Math.PI);
	startcy = startY + (rS + side/2) * Math.sin(alpha - Math.PI);
	dir = 1;
}

ctx.beginPath();
ctx.moveTo(startcx, startcy);
ctx.lineTo(endcx, endcy);
ctx.lineWidth=3;
ctx.stroke();

drawEqTriangle(ctx, side, startcx, startcy, dir?0:1);
drawEqTriangle(ctx, side, endcx, endcy, dir);

function drawEqTriangle(ctx, side, cx, cy, dir) {
  var h = side * (Math.sqrt(3) / 2);

  ctx.save();
  ctx.strokeStyle = "#000";
  ctx.fillStyle = "#000";
  ctx.translate(cx, cy);
  ctx.rotate(alpha + Math.PI / 2);

  ctx.beginPath();
  ctx.lineWidth=1;
  
if (dir === 0) {
  ctx.moveTo(0, -h / 2);
  ctx.lineTo(-side / 2, h / 2);
  ctx.lineTo(side / 2, h / 2);
  ctx.lineTo(0, -h / 2);
} else if (dir === 1) {
  ctx.moveTo(0, h / 2);
  ctx.lineTo(-side / 2, -h / 2);
  ctx.lineTo(side / 2, -h / 2);
  ctx.lineTo(0, h / 2);
}

  ctx.stroke();
  ctx.fill();

  ctx.closePath();
  ctx.restore();
}
&#13;
<!DOCTYPE html>
<html>

<body>

  <canvas id="myCanvas" width="400" height="300" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>
</body>

</html>
&#13;
&#13;
&#13;

答案 1 :(得分:1)

要获得三角形的倾斜角度,您需要以下内容: 假设(x1,y1)是该行开头的坐标,而(x2,y2)是该行结尾的坐标,并且您想在该行的末尾绘制三角形:

alpha = arctan((y2-y2) / (x2-x1))

然后您需要创建以该角度(alpha)倾斜的三角形。

另一个三角形是alpha + 180 degreesalpha - 180 degrees(产生相同的)。

答案 2 :(得分:1)

尝试帮助您解决问题:

编辑:

通过整合@bluehipy答案,也许这会更接近

//Objects
Circle = function(oX, oY, r){
  this.oX = oX;
  this.oY = oY;
  this.r = r;
  context.beginPath();
  context.arc(oX, oY, r, 0, 2 * Math.PI, false);
  context.fillStyle = 'red';
  context.fill();
  context.lineWidth = 2;
  context.strokeStyle = '#003300';
  context.stroke();
}

//Functions
drawArrowFromCirclesOrigins = function(side,c1, c2){
  context.beginPath();
  //line
  context.moveTo(c1.oX, c1.oY);
  context.lineTo(c2.oX, c2.oY);     
  context.stroke();
  //triangle
  var alpha = Math.tan((c2.oY-c1.oY) / (c2.oX-c1.oX));
  var h = side * (Math.sqrt(3) / 2);
  
  context.translate(c2.oX/alpha+c2.r-(-(side/2)), c2.oY/alpha+c2.r-(-(side/2)));
  context.rotate(alpha + Math.PI / 2);

  context.beginPath();

  context.moveTo(0, -h / 2);
  context.lineTo(-side / 2, h / 2);
  context.lineTo(side / 2, h / 2);
  context.lineTo(0, -h / 2);
  context.fillStyle = 'green';
  context.fill();
  
  context.stroke();
}

//Variables
canvas = document.getElementById('canvas');
context = canvas.getContext('2d');

//Program
var cir1 = new Circle(20, 20, 10);
var cir2 = new Circle(40, 40, 10);
drawArrowFromCirclesOrigins(-11,cir1, cir2);
canvas {
  border: 2px solid black;
}
<canvas id="canvas" width="200" height="100"></canvas>

答案 3 :(得分:1)

在一行

上渲染箭头

如果你使用的是纯帆布,那就不那么繁琐了。

首先是两个圈子

const circle1 = {
    x : ?,
    y : ?,
    r : ?, // Radius
    lineWidth : ?,
}
const circle2 = {
    x : ?,
    y : ?,
    r : ?,
    lineWidth : ?,
}

然后是箭头描述

const arrow = {
   width : ?,
   depth : ?, // tip to back of arrow head
}

数学。

从circle1到circle2获取矢量

var vx = circle2.x - circle1.x;
var vy = circle2.y - circle1.y;

他们之间的距离

var dist = Math.sqrt(vx * vx + vy * vy);

现在需要通过将矢量除以长度来标准化矢量。这使得向量长一个单位

vx /= dist;
vy /= dist;

现在你可以渲染箭头了。尖端是圆2的边缘。要找到该点,从距离

减去半径和线宽的一半
const aDist = dist - (circle2.r + circle2.lineWidth / 2);

标准化矢量可以乘以距离来找到位置,或者如果使用画布2D API,则使用法线来设置变换,然后它全部与x轴对齐。

ctx.beginPath(); // drawing the arrow head
ctx.lineTo(circle1.x + vx * aDist, circle1.y + vy * aDist); // tip of arrow

我们需要从尖端向后移动并从90度的线向外移动才能执行此操作,您可以通过交换x,y部分并取消新x来旋转90度的矢量。

ctx.lineTo(
     circle1.x + vx * (aDist - arrow.depth) - vy * arrow.width, 
     circle1.y + vy * (aDist - arrow.depth) + vx * arrow.width
     //          ^------- along line -----^ ^--Out from line--^ 
);
// and the other side
ctx.lineTo(
     circle1.x + vx * (aDist - arrow.depth) + vy * arrow.width, 
     circle1.y + vy * (aDist - arrow.depth) - vx * arrow.width
     //          ^------- along line -----^ ^--Out from line--^ 
);

// ctx.closePath();
ctx.fill();

或者如果您使用转换

ctx.setTransform(vx,vy,-vy,vx,circle1.x,circle1.y); // set the circle1 as origin
                                                    // and use the normal to
                                                    // align the x axis
ctx.beginPath(); // drawing the arrow head
ctx.lineTo(aDist,0); // tip of arrow
ctx.lineTo(aDist - arrow.depth, arrow.width);
ctx.lineTo(aDist - arrow.depth, -arrow.width);
// ctx.closePath();
ctx.fill();
ctx.setTransform(1,0,0,1,0,0); // restore default transform.

实施例

const ctx = canvas.getContext("2d");   


const circle1 = {
    x : 50,
    y : 100,
    r : 45, // Radius
    lineWidth : 3,
}
const circle2 = {
    x : 250,
    y : 50,
    r : 45,
    lineWidth : 3,
}
const arrow = {
   width : 10,
   depth : 20, // tip to back of arrow head
}
var vx = circle2.x - circle1.x;
var vy = circle2.y - circle1.y;
var dist = Math.sqrt(vx * vx + vy * vy);
vx /= dist;
vy /= dist;
const aDist = dist - (circle2.r + circle2.lineWidth / 2);
ctx.lineWidth = circle1.lineWidth;
ctx.beginPath();
ctx.arc(circle1.x, circle1.y, circle1.r, 0, 2 * Math.PI);
ctx.moveTo(circle2.x + circle2.r, circle2.y);
ctx.arc(circle2.x, circle2.y, circle2.r, 0, 2 * Math.PI);
ctx.moveTo(circle1.x, circle1.y);
ctx.lineTo(circle2.x, circle2.y);
ctx.stroke();    

ctx.beginPath(); // drawing the arrow head
ctx.lineTo(circle1.x + vx * aDist, circle1.y + vy * aDist); // tip of arrow
ctx.lineTo(
     circle1.x + vx * (aDist - arrow.depth) - vy * arrow.width, 
     circle1.y + vy * (aDist - arrow.depth) + vx * arrow.width
     //          ^------- along line -----^ ^--Out from line--^ 
);
// and the other side
ctx.lineTo(
     circle1.x + vx * (aDist - arrow.depth) + vy * arrow.width, 
     circle1.y + vy * (aDist - arrow.depth) - vx * arrow.width
     //          ^------- along line -----^ ^--Out from line--^ 
);

// ctx.closePath();
ctx.fill();
canvas { border : 2px solid black; }


const ctx = canvas.getContext("2d");
<canvas id="canvas"></canvas>

使用setTransform

的示例2

这使用setTransform来减少数学量。此示例也使用线宽但箭头指向圆

const ctx = canvas.getContext("2d");   


const circle1 = {
    x : 50,
    y : 100,
    r : 45, // Radius
    lineWidth : 3,
}
const circle2 = {
    x : 250,
    y : 50,
    r : 45,
    lineWidth : 3,
}
const arrow = {
   width : 10,
   depth : 20, // tip to back of arrow head
}
var vx = circle2.x - circle1.x;
var vy = circle2.y - circle1.y;
var dist = Math.sqrt(vx * vx + vy * vy);
vx /= dist;
vy /= dist;
const aDist = dist - (circle2.r + circle2.lineWidth / 2);
ctx.lineWidth = circle1.lineWidth;
ctx.beginPath();
ctx.arc(circle1.x, circle1.y, circle1.r, 0, 2 * Math.PI);
ctx.moveTo(circle2.x + circle2.r, circle2.y);
ctx.arc(circle2.x, circle2.y, circle2.r, 0, 2 * Math.PI);
ctx.moveTo(circle1.x, circle1.y);
ctx.lineTo(circle2.x, circle2.y);
ctx.stroke();    

ctx.beginPath(); // drawing the arrow head
ctx.setTransform(vx, vy, -vy, vx, circle1.x, circle1.y); // set the circle1 as origin
                                                    // and use the normal to
                                                    // align the x axis
ctx.beginPath(); // drawing the arrow head
ctx.lineTo(aDist,-circle1.lineWidth/2); // tip of arrow circle1.linewidth is same for line between circles
ctx.lineTo(aDist,circle1.lineWidth/2); // tip of arrow circle1.linewidth is same for line between circles
ctx.lineTo(aDist - arrow.depth, arrow.width + circle1.lineWidth/2);
ctx.lineTo(aDist - arrow.depth, -arrow.width - circle1.lineWidth/2);
// ctx.closePath();
ctx.fill();
ctx.setTransform(1,0,0,1,0,0); // restore default transform.
ctx.fill();
canvas { border : 2px solid black; }


const ctx = canvas.getContext("2d");
<canvas id="canvas"></canvas>

答案 4 :(得分:0)

你可以用来简化事情的另一个想法(也许是上面实现的@ Blindman67但我不理解它)就是这样想:

无论在哪里放置圆圈,您都可以旋转上下文,使其中心位于水平或垂直(如果您愿意),这样所有兴趣点都位于单个坐标上,即线性。画出你需要的东西然后再旋转上下文。