【问题标题】:how to use two coordinates draw an ellipse with javascript?如何使用两个坐标用javascript绘制一个椭圆?
【发布时间】:2016-08-23 10:12:29
【问题描述】:

就像图片显示的那样,我有两个坐标,我想画一个长轴边缘匹配这两个点的椭圆。

我已经尝试获取这两个坐标之间的中点,并在这一个坐标上绘制一个椭圆。像这样的代码,下面的函数返回我想要的椭圆点数组:

 function add_oval(centre, x, y) {
    var assemble = new Array();
    var angle;
    var dot;
    var tangent = x / y;
    for (i = 0; i < 36; i++) {
        angle = (2 * Math.PI / 36) * i;
        dot = [centre.lng + Math.sin(angle) * y * tangent, centre.lat + Math.cos(angle) * y];
        assemble.push(dot);
    }
    return assemble;
}

但问题是,这些只能画一个水平椭圆,我不知道如何改变角度。

有人知道如何解决我的问题吗?

【问题讨论】:

  • 您还必须定义短轴,以便绘制ellipse
  • @Teemu,我可以为短轴设置一个特定的值,没关系。

标签: javascript algorithm graphics 2d ellipse


【解决方案1】:

提示

如果 javascript 中没有旋转椭圆功能,解决方法是改用 Bezier 三次近似。请参阅帖子How to create circle with Bézier curves?,了解如何用四个贝塞尔弧近似圆。那么椭圆只是一个拉伸的圆,用同样的方式拉伸控制点就足够了。

从单位圆开始,按轴长度缩放控制点,应用所需的旋转并平移到所需的中心。缩放和旋转参数可以从给定的长轴(加上短轴的长度)得出。

另一种方法是使用椭圆的参数方程并将其绘制为折线。

【讨论】:

    【解决方案2】:

    我会为此使用基向量。

    1. 椭圆定义

      所以我们知道A,B 2D 点。您还需要定义标量次要半轴大小|b| 来定义您的椭圆。众所周知的是:

      A=!
      B=!
      |b|=!
      
    2. 中点 C

      这很容易,它是A,B之间的一半

      C.x=A.x + (B.x-A.x)/2
      C.y=A.y + (B.y-A.y)/2
      

      A,B的平均值

      C.x = (A.x+B.x)/2
      C.y = (A.y+B.y)/2
      

      选择你更喜欢哪个(哪个没关系)。此点C 将作为基向量原点。

    3. 基向量a,b

      主要半轴很容易,因为它由CBCA 定义(对于全椭圆也无关紧要)

      a.x = B.x-C.x
      a.y = B.y-C.y
      

      你也可以用一半AB

      a.x = (B.x-A.x)/2
      a.y = (B.y-A.y)/2
      

      次半轴b 更糟,我们知道它垂直于a,所以要么利用a 与向量(0,0,1) 在3D 广告重新缩放中的叉积,要么使用如果你交换@987654341 的事实@ 到 (y,-x)(-y,x) 你会在 2D 中获得 90 度旋转。并将大小从|a| 重新调整为大小|b| 所以:

      |a| = sqrt( a.x*a.x + a.y*a.y )
      b.x = a.y * |b|/|a|
      b.y =-a.x * |b|/|a|
      
    4. 椭圆

      现在我们终于得到了渲染所需的一切。我们椭圆上的任何点都由角度ang=&lt;0,2.0*M_PI&gt; 参数化,其中M_PI=3.1415926535897932384626433832795 很容易:

      x = C.x + a.x*cos(ang) + b.x*sin(ang)
      y = C.y + a.y*cos(ang) + b.y*sin(ang)
      

      所以要么将它添加到你的 for 循环中,然后像现在一样用点渲染你的椭圆。或者将椭圆渲染为带有线条的折线......例如在 VCL/GDI 中这样的东西(对不起,我不使用 javascript):

      bool e,e0;
      double ang,dang=2.0*M_PI/100.0; // 100 lines per 360 degree
      for (e=true,e0=true,ang=0.0;e;ang+=dang,e0=false)
       {
       if (ang>=2.0*M_PI) { ang=2.0*M_PI; e=false; } // reached end? 360 degree
       x = C.x + a.x*cos(ang) + b.x*sin(ang);
       y = C.y + a.y*cos(ang) + b.y*sin(ang);
       if (e0) Canvas->MoveTo(x,y); // first time is cursor moved to (x,y)
        else   Canvas->LineTo(x,y); // all the other iterations just draws a line from last cursor to (x,y) and also moves the cursor there
       }
      

    备注

    • 2D 中的 pointvector 定义为一组 2 个坐标 (x,y)
    • 标量是单值(标准数)
    • |a| 表示向量的大小 a
    • 以上计算不需要|a|&gt;|b|
    • 如果|b| 未知,您可以使用|a| 的缩放,例如让|b|/|a|=0.3

    【讨论】:

    • 如果我想获取椭圆的所有坐标而不​​是“绘制”它怎么办。实际上,draw方法不是我控制的。我只能提供该方法的点列表。
    • @JackZhang 然后您忽略 moveto/line to 而是将 (x,y) 添加到您的点列表中。您还需要更改dang 步骤,以便您有足够的积分。计算精确的相邻像素位置对于旋转来说确实是一个难题,因为它会导致超越问题......更容易和更快的是拥有更小的dang 步长并冒着重复像素的风险。为了更好地估计,您可以使用圆近似......所以如果|a|&gt;=|b|,那么如果我没记错dang&lt;=1/|a|应该非常接近像素步长。取决于偏心...我会使用dang=0.75/|a|或更小的步长
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    • 1970-01-01
    • 2020-03-29
    • 2011-10-08
    • 1970-01-01
    相关资源
    最近更新 更多