Sort points by angle from given axis?

前端 未结 8 2874
無奈伤痛
無奈伤痛 2021-02-19 12:11

How can I sort an array of points/vectors by counter-clockwise increasing angle from a given axis vector?

For example:

8条回答
  •  情歌与酒
    2021-02-19 12:22

    I know this question is quite old, and the accepted answer helped me get to this, still I think I have a more elegant solution which also covers equality (so returns -1 for lowerThan, 0 for equals, and 1 for greaterThan).

    It is based on the division of the plane to 2 halves, one from the positive ref axis (inclusive) to the negative ref axis (exclusive), and the other is its complement.

    Inside each half, comparison can be done by right hand rule (cross product sign), or in other words - sign of sine of angle between the 2 vectors. If the 2 points come from different halves, then the comparison is trivial and is done between the halves themselves.

    For an adequately uniform distribution, this test should perform on average 4 comparisons, 1 subtraction, and 1 multiplication, besides the 4 subtractions done with ref, that in my opinion should be precalculated.

    int compareAngles(Point const & A, Point const & B, Point const & ref = Point(0,0)) {
      typedef decltype(Point::x) T;  // for generality. this would not appear in real code.
      const T sinA = A.y - ref.y; // |A-ref|.sin(angle between A and positive ref-axis)
      const T sinB = B.y - ref.y; // |B-ref|.sin(angle between B and positive ref-axis)
      const T cosA = A.x - ref.x; // |A-ref|.cos(angle between A and positive ref-axis)
      const T cosB = B.x - ref.x; // |B-ref|.cos(angle between B and positive ref-axis)
    
      bool hA = ( (sinA < 0) || ((sinA == 0) && (cosA < 0)) ); // 0 for [0,180). 1 for [180,360).
      bool hB = ( (sinB < 0) || ((sinB == 0) && (cosB < 0)) ); // 0 for [0,180). 1 for [180,360).
    
      if (hA == hB) {
        // |A-ref|.|B-ref|.sin(angle going from (B-ref) to (A-ref))
        T sinBA = sinA * cosB - sinB * cosA;
        // if T is int, or return value is changed to T, it can be just "return sinBA;"
        return ((sinBA > 0) ? 1 : ((sinBA < 0) ? (-1) : 0));
      }
      return (hA - hB);
    }
    

提交回复
热议问题