【问题标题】:Drawing arc to fit angle in html canvas by Circle tangles point with known radius 2D通过圆形缠结点,绘图弧形以圆形缠结点与已知半径2d的缠结点
【发布时间】:2015-07-25 10:06:48
【问题描述】:

很难解释这个问题。但我会尽力而为。 首先我做了2行,一行包含一个起点和一个终点,像这样

line = {
   startPoint{x: , y:}
   endPoint{x: , y:}
}

然后我在画布上绘制两条线,形成这样的三角形的角。

我现在将线条彼此分开,长度为 Radius*2,如下所示

那么我现在如何使用两个端点作为切点来绘制圆弧,如下所示

我需要使用 arc 还是可以使用 arcto 来做到这一点?如果是弧线;然后我如何给它开始绘图和结束点,以便它像上图中的图像所示那样绘制它。感谢您的宝贵时间,任何输入都会有所帮助。再次抱歉对问题的错误描述

  • 更新 - 看来我没有完全解释我的问题。所以这里有一点更新。使用此处给出的示例。我最终得到一个椭圆形的圆圈。我想要得到的是线条之间的圆形。

【问题讨论】:

  • 首先 moveTo 然后 lineTo (line1) 然后可能 quadraticCurveTo 使用线的交点作为中点和第 2 行的开始作为结束,最后 lineTo (Line 2 ) 可能是足够好的解决方案?跨度>
  • @TeroTolonen 我试过了,但我认为总是出错的部分是如何计算中间点 X,Y
  • 互联网上应该有很多解决方案jsfiddle.net/justin_c_rounds/Gd2S2
  • @TeroTolonen 主要是我想要的,我唯一的问题是控制点离圆心更远,它不应该像半径这样有意义吗?跨度>
  • 好吧,你可以移动控制点,但也请检查其他解决方案,它们都有不同的形状,取决于你在看什么是最好的,贝塞尔曲线将保证线条的连续性端点

标签: javascript html canvas html5-canvas trigonometry


【解决方案1】:

是的,你可以使用arcTo()

  • 使用moveTo() 设置您的第一行起点
  • 然后将两条线之间的交点作为第一对
  • 然后是第二行的终点(您称之为“起点线 2”)最后一对。
  • 提供半径
  • 要实际绘制最后一条线(仅用于与arcTo() 一起计算),请为弧线中的最后一个点对添加lineTo(),描边/填充。

如果您想将线条分开但不知道交点,则必须先将线条插值超出其原始长度,然后才能manually calculate(请参阅该答案中的getIntersection())。

var ctx = document.querySelector("canvas").getContext("2d");

ctx.moveTo(0, 0);                 // start point
ctx.arcTo(50, 150, 100, 0, 20);   // intersection, outpoint, radius
ctx.lineTo(100, 0);               // line from arc-end to outpoint

ctx.translate(130, 0);
ctx.moveTo(0, 0);                 // start point
ctx.arcTo(50, 150, 80, 50, 8);   // intersection, outpoint, radius
ctx.lineTo(80, 50);               // line from arc-end to outpoint

ctx.stroke();
<canvas></canvas>

如果在不知道交点的情况下将它们分开,如何延长线以找到交点:

function extendLine(line, scale) {
  var sx = line.startPoint.x,
      sy = line.startPoint.y,
      ex = line.endPoint.x,
      ey = line.endPoint.y;

   return {
     startPoint: {x: sx, y: sy},
     endPoint: {
       x: sx + (ex - sx) * scale,
       y: sy + (ey - sy) * scale
     }
   }
}

比例可能是一个荒谬的值,因为我们需要确保在非常陡峭的角度线会在某个地方相交。不影响计算速度。

然后使用两条线(确保第二条线从第一条线的端点继续,这意味着您可能必须反转坐标 - 如果您想动态执行此操作,您可以测量每个点的距离第二行到第一行的终点,最短的距离首先作为起点):

然后执行步骤是:

var line1 = ...,
    line2 = ...,
    line1tmp = extendLine(line1, 10000),
    line2tmp = extendLine(line2, 10000),
    ipoint = getIntersection(line1, line2); // see link above

// define the line + arcTo
ctx.moveTo(line1.startPoint.x, line1.startPoint.y);
ctx.arcTo(ipoint.x, ipoint.y, 
          line2.endPoint.x, line2.endPoint.y,
          Math.abs(line2.startPoint.x - line1.endPoint.x) / 2);
ctx.lineTo(line2.endPoint.x, line2.endPoint.y);
ctx.stroke();

【讨论】:

  • 如果我使用离第一线更近的交点,然后线到,这个圆会变成椭圆形吗?
  • @DaCh 不,它将始终保持循环(用另一个示例更新答案演示)
  • 经过几天的测试,我发现这个解决方案非常有效。但我有一个问题。为什么如果我有一个大约 5 度的角度并且我使用一个大半径,那么圆弧就会以相反的方式绘制。这是一个半径为 5 的,它是Working。这里是一个半径为 45 的半径为Not working。两者都有 9 度角
  • @DaCh 它是它工作方式的一部分。如果线条的角度和位置无法在线条之间形成圆弧,则圆弧被“强制”到可以产生的点。您将不得不对其中一条线进行一些扩展(使用 f.ex. 插值)以迫使弧线向下。您可以计算端点之间的角度并使用长度,以便线条是反射角度。然后找到最短线的交点。 For example
【解决方案2】:

给定在一个公共点相交的 2 条线段,您可以使用三次贝塞尔曲线对它们应用圆角交点:

方法如下...

  • 给定一个点 p1 和从 p1 延伸到 p0 (P10) 和从 p1 延伸到 p2 (P12) 的线段:

    var p0={x:50,y:50};
    var p1={x:100,y:150};
    var p2={x:250,y:100};
    
  • 计算 P10 和 P12 上的点,它们是从公共点 (p1) 回到各自起点 (p0 和 p2) 的指定百分比:

    var lerp=function(a,b,x){ return(a+x*(b-a)); };
    var dx,dy,length;
    var offsetPct=0.15;
    
    // calc a point on P10 that is 15% of the way from p1 to p0
    dx=p1.x-p0.x;
    dy=p1.y-p0.y;
    p00={ x:lerp(p1.x,p0.x,offsetPct), y:lerp(p1.y,p0.y,offsetPct) }
    
    // calc a point on P12 that is 15% of the way from p1 to p2
    dx=p1.x-p2.x;
    dy=p1.y-p2.y;
    p22={ x:lerp(p1.x,p2.x,offsetPct), y:lerp(p1.y,p2.y,offsetPct) }
    
  • 然后您可以使用缩短的线段和三次贝塞尔曲线绘制圆形交点:

示例代码和演示:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");

var p0={x:50,y:50};
var p1={x:100,y:150};
var p2={x:150,y:50};


roundedIntersection(p0,p1,p2,0.15);


function roundedIntersection(p0,p1,p2,offsetPct){

  var lerp=function(a,b,x){ return(a+x*(b-a)); };
  var dx,dy,length;
  dx=p1.x-p0.x;
  dy=p1.y-p0.y;
  p00={ x:lerp(p1.x,p0.x,offsetPct), y:lerp(p1.y,p0.y,offsetPct) }
  dx=p1.x-p2.x;
  dy=p1.y-p2.y;
  p22={ x:lerp(p1.x,p2.x,offsetPct), y:lerp(p1.y,p2.y,offsetPct) }

  ctx.beginPath();
  ctx.moveTo(p0.x,p0.y);
  ctx.lineTo(p00.x,p00.y);
  ctx.bezierCurveTo( p1.x,p1.y,  p1.x,p1.y  ,p22.x,p22.y);
  ctx.lineTo(p2.x,p2.y);
  ctx.stroke();

  dot(p0.x,p0.y);
  dot(p1.x,p1.y);
  dot(p2.x,p2.y);
  dot(p00.x,p00.y);
  dot(p22.x,p22.y);

  function dot(x,y){
    ctx.beginPath();
    ctx.arc(x,y,2,0,Math.PI*2);
    ctx.closePath();
    ctx.fill();
  }

}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=300 height=300></canvas>

【讨论】:

  • 我认为 bezierCurveTo 是我需要的东西。感谢您的回答。我现在只需要找到一个“控制点”就可以了。
  • 您可以通过使贝塞尔曲线开始更接近线段的起点来使您的顶点连接更加“圆润”。例如,将offsetPct 更改为 0.50 并且连接将非常圆润:roundedIntersection(p0,p1,p2,0.50); 您可以使用 arc / arcTo 使连接完美圆润,但这是以将弧拉离原始连接点更远为代价的2 段。
猜你喜欢
  • 1970-01-01
  • 2019-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-08
  • 1970-01-01
相关资源
最近更新 更多