如何在椭圆内绑定一个圆圈?

时间:2016-06-03 19:23:23

标签: c# winforms math graphics

这篇文章的标题很难想到,所以如果你能想到更具描述性的标题,请告诉我。无论如何,我的问题非常具体,需要一些简单的数学知识。我正在编写一个C#WinForms应用程序,它有点像旧的'xeyes'Linux应用程序。它基本上是一组跟随鼠标光标的眼睛。这听起来可能很容易,但是如果你像我这样的完美主义者会变得相当复杂:P。这是我到目前为止的代码(只有paint方法,间隔为16)。

int lx = 35;
int ly = 50;
int rx;
int ry;

int wx = Location.X + Width / 2;
int wy = Location.Y + Height / 2;

Rectangle bounds = Screen.FromControl(this).Bounds;

// Calculate X

float tempX = (mx - wx) / (float)(bounds.Width / 2);

// Calculate Y

float tempY = (my - wy) / (float)(bounds.Height / 2);

// Draw eyes

e.Graphics.FillEllipse(Brushes.LightGray, 10, 10, 70, 100);
e.Graphics.FillEllipse(Brushes.LightGray, 90, 10, 70, 100);

// Draw pupils (this only draws the left one)

e.Graphics.FillEllipse(Brushes.Black, lx += (int)(25 * tempX), ly += (int)(40 * tempY), 20, 20);

现在这在基本级别上有效,但是如果用户将光标置于0,0,有时会发生这种情况。

The pupil can exit the main eye.

现在我的问题是如何解决这个问题? IF语句将检查鼠标指针的位置,然后根据它减少瞳孔X?

感谢。

修改:这是我获取鼠标位置的位置(mymx):

private void timer_Tick(object sender, EventArgs e)
{
    mx = Cursor.Position.X;
    my = Cursor.Position.Y;

    Invalidate();
}

计时器在eyes_Load事件中启动,间隔为16。

编辑2:最终解决方案:http://pastebin.com/fT5HfiQR

1 个答案:

答案 0 :(得分:15)

将眼球建模为以下椭圆:

enter image description here

它的等式是:

enter image description here

加入其中心和光标的那条线:

enter image description here

(不要担心奇点)

然后我们可以求解得到交点:

enter image description here

其中

enter image description here

现在,您可以通过西格玛将距离中心到光标的距离除以眼球边缘的距离来计算。剩下的只是内插以限制学生的位置:

enter image description here

enter image description here

您想要的if语句是

enter image description here

(N.B。对于数学模型,上面是一个略微的简化,假设你的椭圆不是太窄; 完全解决方案是非分析性的)

编辑:我在VB.NET中的测试:

enter image description here

enter image description here

enter image description here

编辑2:C#端口

PointF Bound(double xc, double yc, double w, double h, double xm, double ym, double r)
{
   double dx = xm - xc, dy = ym - yc;
   if (Math.Abs(dx) > 0.001 && Math.Abs(dy) > 0.001) 
   {
      double dx2 = dx * dx, dy2 = dy * dy;
      double sig = 1.0 / Math.Sqrt(dx2 / (w * w * 0.25) + dy2 / (h * h * 0.25));
      double d = Math.Sqrt(dx2 + dy2), e = d * sig;
      if (d > e - r)
      {
         double ratio = (e - r) / d;
         return new PointF((float)(xc + dx * ratio),
                        (float)(yc + dy * ratio));
      }
   }
   return new PointF((float)xm, (float)ym);
}
  • xcyc:椭圆的中心坐标
  • wh:椭圆的宽度和高度
  • xmym:鼠标坐标
  • r:你想要约束的圆的半径(瞳孔)
  • 返回:您想要放置圆心的位置

编辑3:非常感谢Quinchilion进行了以下优化(gawd该死的,这让我脸红了)

PointF Bound(double xc, double yc, double w, double h, double xm, double ym, double r)
{
    double x = (xm - xc) / (w - r);
    double y = (ym - yc) / (h - r);
    double dot = x*x + y*y;
    if (dot > 1) {
        double mag = 1.0 / Math.Sqrt(dot);
        x *= mag; y *= mag;
    }
    return new PointF((float)(x * (w - r) + xc), (float)(y * (h - r) + yc));
}