如何在圆圈内绘制三角形指针

How to draw triangle pointers inside of circle

我知道这是一个简单的三角函数问题,但我的高中现在不及格。

给定一个角度,我已将其转换为弧度以获得第一个点。我如何计算三角形的下两个点绘制在 canvas 上,以便使小三角形始终指向圆圈外。所以假设我已经画了一个给定半径的圆。现在我想要一个函数来绘制一个三角形,该三角形位于圆圈内部的边缘上,无论角度如何,它都指向外部。 (可以这么说,沿着边缘)

function drawPointerTriangle(ctx, angle){
    var radians = angle * (Math.PI/180)
    var startX = this.radius + this.radius/1.34 * Math.cos(radians)
    var startY = this.radius - this.radius/1.34 * Math.sin(radians)
    // This gives me my starting point on the outer edge of the circle, plotted at the angle I need    
    ctx.moveTo(startX, startY);

   // HOW DO I THEN CALCULATE x1,y1 and x2, y2.  So that no matter what angle I enter into this function, the arrow/triangle always points outwards to the circle.
    ctx.lineTo(x1, y1);
    ctx.lineTo(x2, y2);


}

示例

减小半径,改变角度再调用cos/sin:

function drawPointerTriangle(ctx, angle)
{
    var radians = angle * (Math.PI/180);
    var radius = this.radius/1.34;
    var startX = this.center.x + radius * Math.cos(radians);
    var startY = this.center.y + radius * Math.sin(radians);

    ctx.moveTo(startX, startY);

    radius  *= 0.9;
    radians += 0.1;
    var x1  = this.center.x + radius * Math.cos(radians);
    var y1  = this.center.y + radius * Math.sin(radians);

    radians -= 0.2;
    var x1  = this.center.x + radius * Math.cos(radians);
    var y1  = this.center.y + radius * Math.sin(radians);

    ctx.lineTo(x1, y1);
    ctx.lineTo(x2, y2);
    ctx.lineTo(startX, startY);
}

生成的三角形的大小与圆的大小成正比。


如果您需要等边的、固定大小的三角形,请使用:

//get h by pythagoras
h = sqrt( a^2 - (a/2)^2 );)
//get phi using arcustangens:
phi = atan( a/2, radius-h );
//reduced radius h by pythagoras:
radius = sqrt( (radius-h)^2 + (a/2)^2 );

radians += phi;
...
radians -= 2*phi;
...

你没说要画什么三角形,我猜是等边三角形。

看看这张图片(来源 here

我会从右上到右下逆时针调用3个点p1,p2,p3。

在原点与三角形质心重合的坐标系中,可以很容易地计算出三角形三点的坐标。

给定一个点属于圆的边缘和我们刚刚计算的点p1,我们可以计算从我们的主坐标系到三角形坐标系的平移参数。然后,我们只需要将另外两个点的坐标转换回我们的主坐标系。即 (x1,y1) 和 (x2,y2)。

您可以看看下面基于您的代码的演示。

const w = 300;
const h = 300;

function calculateTrianglePoints(angle, width) {
  let r = width / Math.sqrt(3);
  let firstPoint = [
    r * Math.cos(angle),
    r * Math.sin(angle),
  ]

  let secondPoint = [
    r * Math.cos(angle + 2 * Math.PI / 3),
    r * Math.sin(angle + 2 * Math.PI / 3),
  ]

  let thirdPoint = [
    r * Math.cos(angle + 4 * Math.PI / 3),
    r * Math.sin(angle + 4 * Math.PI / 3),
  ]

  return [firstPoint, secondPoint, thirdPoint]
}

const radius = 100
const triangleWidth = 20;

function drawPointerTriangle(ctx, angle) {
  var radians = angle * (Math.PI / 180)
  var startX = radius * Math.cos(radians)
  var startY = radius * Math.sin(radians)
  var [pt0, pt1, pt2] = calculateTrianglePoints(radians, triangleWidth);
  var delta = [
    startX - pt0[0],
    startY - pt0[1],
  ]

  pt1[0] = pt1[0] + delta[0]
  pt1[1] = pt1[1] + delta[1]
  pt2[0] = pt2[0] + delta[0]
  pt2[1] = pt2[1] + delta[1]
  ctx.beginPath();
  // This gives me my starting point on the outer edge of the circle, plotted at the angle I need    
  ctx.moveTo(startX, startY);

  [x1, y1] = pt1;
  [x2, y2] = pt2;
  // HOW DO I THEN CALCULATE x1,y1 and x2, y2.  So that no matter what angle I enter into this function, the arrow/triangle always points outwards to the circle.
  ctx.lineTo(x1, y1);
  ctx.lineTo(x2, y2);
  ctx.closePath();
  ctx.fillStyle = '#FF0000';
  ctx.fill();
}

function drawCircle(ctx, radius) {
  ctx.beginPath();
  ctx.arc(0, 0, radius, 0, 2 * Math.PI);
  ctx.closePath();
  ctx.fillStyle = '#000';
  ctx.fill();
}

function clear(ctx) {
  ctx.fillStyle = '#fff';
  ctx.fillRect(-w / 2, -h / 2, w, h);
}

function normalizeAngle(pointCoordinate, angle) {
  const [x, y] = pointCoordinate;
  if (x > 0 && y > 0) return angle;
  else if (x > 0 && y < 0) return 360 + angle;
  else if (x < 0 && y < 0) return 180 - angle;
  else if (x < 0 && y > 0) return 180 - angle;
}

function getAngleFromPoint(point) {
  const [x, y] = point;
  if (x == 0 && y == 0) return 0;
  else if (x == 0) return 90 * (y > 0 ? 1 : -1);
  else if (y == 0) return 180 * (x >= 0 ? 0: 1);
  const radians = Math.asin(y / Math.sqrt(
    x ** 2 + y ** 2
  ))

  return normalizeAngle(point, radians / (Math.PI / 180))
}

document.addEventListener('DOMContentLoaded', function() {
  const canvas = document.querySelector('canvas');
  const angleText = document.querySelector('.angle');
  const ctx = canvas.getContext('2d');
  ctx.translate(w / 2, h / 2);
  drawCircle(ctx, radius);
  drawPointerTriangle(ctx, 0);
  canvas.addEventListener('mousemove', _.throttle(function(ev) {
    let mouseCoordinate = [
      ev.clientX - w / 2,
      ev.clientY - h / 2
    ]
    
    let degAngle = getAngleFromPoint(mouseCoordinate)

    clear(ctx);
    drawCircle(ctx, radius);
    drawPointerTriangle(ctx, degAngle)
    angleText.innerText = Math.floor((360 - degAngle)*100)/100;
  }, 15))
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js"></script>
<canvas width=300 height=300></canvas>
<div class="angle">0</div>