如何“捕捉”到罗盘的方向(2D)矢量(N、NE、E、SE、S、SW、W、NW)?

发布于 2024-08-05 04:40:04 字数 442 浏览 5 评论 0原文

我在 3D 建模软件中有一堆垂直于窗户表面的向量。投影到 XY 平面,我想知道它们面向哪个方向,转换为 8 罗盘坐标东北、东南西南西 > 和西北)。

向量的工作方式如下:

  • X 轴代表东西向(东为正),
  • y 轴代表南北(北为正),
  • 因此
    • (0, 1) == 北
    • (1, 0) == 东
    • (0,-1) == 南
    • (-1,0) == 西

给定一个向量 (x, y) 我正在寻找8 个罗盘坐标中最接近的一个。关于如何优雅地做到这一点有什么想法吗?

I have a bunch of vectors normal to window surfaces in a 3D modelling software. Projected to the XY-Plane, I would like to know in which direction they are facing, translated to the 8 compass coordinates (North, North-East, East, South-East, South, South-West, West and North-West).

The vectors work like this:

  • the X axis represents East-West (with East being positive)
  • the y axis represents North-South (with North being positive)
  • thus
    • (0, 1) == North
    • (1, 0) == East
    • (0,-1) == South
    • (-1,0) == West

Given a vector (x, y) I am looking for the closest of the 8 compass coordinates. Any ideas on how to do this elegantly?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

吻安 2024-08-12 04:40:04

这在 Java 中有效,计算八个方向的值 0...7:

import static java.lang.Math.*;    

int compass = (((int) round(atan2(y, x) / (2 * PI / 8))) + 8) % 8;

结果映射到罗盘,如下所示:

0 => E
1 => NE
2 => N
3 => NW
4 => W
5 => SW
6 => S
7 => SE

This works in Java, computing a value 0...7 for the eight directions:

import static java.lang.Math.*;    

int compass = (((int) round(atan2(y, x) / (2 * PI / 8))) + 8) % 8;

The result maps to the compass as follows:

0 => E
1 => NE
2 => N
3 => NW
4 => W
5 => SW
6 => S
7 => SE
舟遥客 2024-08-12 04:40:04

我可能只是调用 atan2() 来计算航向角度(“偏航"),然后使用一系列 if:s 或一些数学运算将其“捕捉”到 90 度的倍数。

I would probably just do a call to atan2() to figure out the heading angle ("yaw"), and then use either a sequence of if:s or some maths to "snap" it to a multiple of 90 degrees.

魄砕の薆 2024-08-12 04:40:04

不需要执行atan 函数。

如果你这样做: y/x 你会得到直线的斜率。根据得到的数字来判断,您可以确定角度/八分圆。

对于正 x (x>0)

  • (y/x) > 2.4-=> 90度(北)
  • 2.4> (y/x)> 0.4-=> 45度(西北)
  • 0.4> (y/x)> -0.4-=> 0度(西)
  • -0.4> (y/x)> -2.4 -=> -45度(西南)
  • -2.4> (y/x)-=> 90 度(南)

和负 x 的类似列表

,最后是例外情况:

  • (x==0 && y>0) -=> -90 度(南)
  • (x==0 && y<0) -=> 90 度(南)

附录:我只报告这种方法,因为当计算 atan 是不行的(例如在嵌入式系统上)时,

我必须挖掘一下。这是我使用的高度优化的例程(用于手机游戏)。

输入:x1,y1 = 向量的起点
x2,y2 = 向量的端点
输出 (0-7) = 0=north, 1=northwest, 2=west,...etc

 int CalcDir( int x1, int y1, int x2, int y2 )
 {
      int dx = x2 - x1, dy = y2 - y1;
      int adx = (dx<0)?-dx:dx, ady = (dy<0)?-dy:dy, r;
      r=(dy>0?4:0)+(dx>0?2:0)+(adx>ady?1:0);
      r=(int []){2,3,1,0,5,4,6,7}[r];
      return r;
 }

 void CalcDirTest(){
      int t = CalcDir(0, 0, 10, 1);
      printf("t = %d",t);
      t = CalcDir(0, 0, 9, 10);
      printf("t = %d",t);
      t = CalcDir(0, 0, -1, 10);
      printf("t = %d",t);
      t = CalcDir(0, 0, -10, 9);
      printf("t = %d",t);
      t = CalcDir(0, 0, -10, -1);
      printf("t = %d",t);
      t = CalcDir(0, 0, -9, -10);
      printf("t = %d",t);
      t = CalcDir(0, 0, 1, -10);
      printf("t = %d",t);
      t = CalcDir(0, 0, 10, -9);
      printf("t = %d",t);
 }

这将产生以下输出:(

 t = 7
 t = 6
 t = 5
 t = 4
 t = 3
 t = 2
 t = 1
 t = 0

测试的向量可能看起来很奇怪,但我对它们进行了一些调整明显位于一个八分圆内,而不是在确切的边界上)

no need to do an atan function.

if you do: y/x you'll get the slope of the line. Judging by the number you get you can determine the angle/octant.

for positive x's (x>0)

  • (y/x) > 2.4 -=> 90 degrees (north)
  • 2.4 > (y/x) > 0.4 -=> 45 degrees (northwest)
  • 0.4 > (y/x) > -0.4 -=> 0 degrees (west)
  • -0.4 > (y/x) > -2.4 -=> -45 degrees (southwest)
  • -2.4 > (y/x) -=> 90 degrees (south)

and a similar list for the negative x's

and finally the exception cases:

  • (x==0 && y>0) -=> -90 degrees (south)
  • (x==0 && y<0) -=> 90 degrees (south)

addendum: I only report this method for when calculating an atan is a no go (for instance on an embedded system))

I had to dig a bit. Here's a highly optimized routine I use (used in mobile games).

input: x1,y1 = startpoint of vector
x2,y2 = endpoint of vector
output (0-7) = 0=north, 1=northwest, 2=west,...etc

 int CalcDir( int x1, int y1, int x2, int y2 )
 {
      int dx = x2 - x1, dy = y2 - y1;
      int adx = (dx<0)?-dx:dx, ady = (dy<0)?-dy:dy, r;
      r=(dy>0?4:0)+(dx>0?2:0)+(adx>ady?1:0);
      r=(int []){2,3,1,0,5,4,6,7}[r];
      return r;
 }

 void CalcDirTest(){
      int t = CalcDir(0, 0, 10, 1);
      printf("t = %d",t);
      t = CalcDir(0, 0, 9, 10);
      printf("t = %d",t);
      t = CalcDir(0, 0, -1, 10);
      printf("t = %d",t);
      t = CalcDir(0, 0, -10, 9);
      printf("t = %d",t);
      t = CalcDir(0, 0, -10, -1);
      printf("t = %d",t);
      t = CalcDir(0, 0, -9, -10);
      printf("t = %d",t);
      t = CalcDir(0, 0, 1, -10);
      printf("t = %d",t);
      t = CalcDir(0, 0, 10, -9);
      printf("t = %d",t);
 }

This will result in the following output:

 t = 7
 t = 6
 t = 5
 t = 4
 t = 3
 t = 2
 t = 1
 t = 0

(The vectors for the test might look oddly chosen, but I tweaked them all a bit to be clearly in one octant and not on the exact border)

£烟消云散 2024-08-12 04:40:04

这个不使用atan2,并且每次调用最多进行 4 次比较和 2 个产品。在 4 个内部块中比较 x 与 y(我仅在第一个块中编辑它),可以将其减少到正好 4 次比较和每次调用 1 个产品。

int compass(double x,double y)
{
  double t = 0.392699082; // tan(M_PI/8.0);

  if (x>=0)
  {
    if (y>=0)
    {
      if (x>y) { if (y<t*x) return E_COMPASS; }
      else { if (x<t*y) return N_COMPASS; }
      return NE_COMPASS;
    }
    else
    {
      if (-y<t*x) return E_COMPASS;
      if (x<-t*y) return S_COMPASS;
      return SE_COMPASS;
    }
  }
  else
  {
    if (y>=0)
    {
      if (y<-t*x) return W_COMPASS;
      if (-x<t*y) return N_COMPASS;
      return NW_COMPASS;
    }
    else
    {
      if (-y<-t*x) return W_COMPASS;
      if (-x<-t*y) return S_COMPASS;
      return SW_COMPASS;
    }
  }
  return E_COMPASS;
}

This one does not use atan2, and does at worst 4 comparisons and 2 products per call. Comparing x to y in the 4 inner blocks (I edited it in the first block only), it can be reduced to exactly 4 comparisons and 1 product per call.

int compass(double x,double y)
{
  double t = 0.392699082; // tan(M_PI/8.0);

  if (x>=0)
  {
    if (y>=0)
    {
      if (x>y) { if (y<t*x) return E_COMPASS; }
      else { if (x<t*y) return N_COMPASS; }
      return NE_COMPASS;
    }
    else
    {
      if (-y<t*x) return E_COMPASS;
      if (x<-t*y) return S_COMPASS;
      return SE_COMPASS;
    }
  }
  else
  {
    if (y>=0)
    {
      if (y<-t*x) return W_COMPASS;
      if (-x<t*y) return N_COMPASS;
      return NW_COMPASS;
    }
    else
    {
      if (-y<-t*x) return W_COMPASS;
      if (-x<-t*y) return S_COMPASS;
      return SW_COMPASS;
    }
  }
  return E_COMPASS;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文