【问题标题】:How to "snap" a directional (2D) vector to a compass (N, NE, E, SE, S, SW, W, NW)?如何将方向(2D)矢量“捕捉”到指南针(N、NE、E、SE、S、SW、W、NW)?
【发布时间】:2010-11-29 02:18:57
【问题描述】:

我在 3D 建模软件中有一堆垂直于窗口表面的向量。投影到 XY 平面,我想知道他们面对的方向,转换为 8 罗盘坐标NorthNorth-East em>, East, South-East, South, South-West, West西北)。

向量的工作方式如下:

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

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

【问题讨论】:

  • 关于如何正确标记的任何想法?随意...
  • 不,在我的回答中 (1, 0) 是东边,它是逆时针方向的,因为我在这里做数学运算。这与导航中角度的标准使用不一致(0 = 北,顺时针)。您可以通过在 +8 上添加适当的偏移量并在 atan2 前面使用 - 来调整它。
  • 感谢您指出starblue,我在这里的草图让自己感到困惑-您的答案是正确的!

标签: algorithm math trigonometry vector


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    这适用于 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
    

    【讨论】:

      【解决方案3】:

      无需执行 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 90 度(南)

      附录:我只在计算 atan 不可行(例如在嵌入式系统上)时报告此方法)

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

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

       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
      

      (测试的向量可能看起来很奇怪,但我稍微调整了一下,使其清楚地在一个八分圆中,而不是在确切的边界上)

      【讨论】:

      • 我看不出最终的优化例程如何工作,因为 r 值很容易超过匿名数组的大小。
      • 很好看。修复了错误
      【解决方案4】:

      这个不使用 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;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-08-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-07
        相关资源
        最近更新 更多