Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Algorithm 4点凸包_Algorithm_Graphics_Computational Geometry_Convex Hull - Fatal编程技术网

Algorithm 4点凸包

Algorithm 4点凸包,algorithm,graphics,computational-geometry,convex-hull,Algorithm,Graphics,Computational Geometry,Convex Hull,我想要一个算法来计算4个2D点的凸包。我已经研究了广义问题的算法,但我想知道是否有一个简单的4点解决方案。这里有一个针对4点的更特别的算法: function Point (x, y) { this.x = x; this.y = y; } Point.prototype.equals = function (p) { return this.x == p.x && this.y == p.y; }; Point.prototype.distance

我想要一个算法来计算4个2D点的凸包。我已经研究了广义问题的算法,但我想知道是否有一个简单的4点解决方案。

这里有一个针对4点的更特别的算法:

function Point (x, y)
{
    this.x = x;
    this.y = y;
}

Point.prototype.equals = function (p)
{
    return this.x == p.x && this.y == p.y;
};

Point.prototype.distance = function (p)
{ 
    return Math.sqrt (Math.pow (this.x-p.x, 2) 
                    + Math.pow (this.y-p.y, 2));
};

function convex_hull (points)
{
    function left_oriented (p1, p2, candidate)
    {
        var det = (p2.x - p1.x) * (candidate.y - p1.y) 
                - (candidate.x - p1.x) * (p2.y - p1.y);
        if (det > 0) return true;  // left-oriented 
        if (det < 0) return false; // right oriented
        // select the farthest point in case of colinearity
        return p1.distance (candidate) > p1.distance (p2);
    }

    var N = points.length;
    var hull = [];

    // get leftmost point
    var min = 0;
    for (var i = 1; i != N; i++)
    {
        if (points[i].y < points[min].y) min = i;
    }
    hull_point = points[min];

    // walk the hull
    do
    {
        hull.push(hull_point);

        var end_point = points[0];
        for (var i = 1; i != N; i++) 
        {
            if (  hull_point.equals (end_point)
               || left_oriented (hull_point, 
                                 end_point, 
                                 points[i]))
            {
                end_point = points[i];
            }
        }
        hull_point = end_point;
    }
    /*
     * must compare coordinates values (and not simply objects)
     * for the case of 4 co-incident points
     */
    while (!end_point.equals (hull[0])); 
    return hull;
}
  • 找到具有最小-X、最大-X、最小-Y和最大-Y的点的索引,并从中获得唯一值。例如,索引可以是0,2,1,2,唯一值将是0,2,1
  • 如果有4个唯一值,则凸包由所有4个点组成
  • 如果有3个唯一值,则这3个点肯定位于凸包中。检查第4点是否位于该三角形内;如果不是,它也是凸包的一部分
  • 如果有两个唯一值,则这两个点位于外壳上。在其他两个点中,离连接这两个点的这条线更远的点肯定在船体上。进行三角形安全壳试验,检查另一点是否也在船体内
  • 如果有1个唯一值,则所有4个点都是共相关的

如果有4个点正确排列,则需要进行一些计算,以避免得到领结形状。六羟甲基三聚氰胺六甲醚。。。。看起来有足够的特殊情况来证明使用广义算法是正确的。但是,您可能会对其进行调优,使其比通用算法运行得更快

取三个点,确定它们的三角形是顺时针还是逆时针:

triangle_ABC= (A.y-B.y)*C.x + (B.x-A.x)*C.y + (A.x*B.y-B.x*A.y)
对于右手坐标系,如果ABC为逆时针,则该值为正值;如果ABC为顺时针,则该值为负值;如果ABC为共线,则该值为零。但是,对于左手坐标系,由于方向是相对的,下面的方法同样适用

计算包含第四个点的三个三角形的可比值:

triangle_ABD= (A.y-B.y)*D.x + (B.x-A.x)*D.y + (A.x*B.y-B.x*A.y)
triangle_BCD= (B.y-C.y)*D.x + (C.x-B.x)*D.y + (B.x*C.y-C.x*B.y)
triangle_CAD= (C.y-A.y)*D.x + (A.x-C.x)*D.y + (C.x*A.y-A.x*C.y)
如果{ABD,BCD,CAD}中的三个都与ABC有相同的符号,那么D在ABC内,船体是三角形ABC

如果{ABD,BCD,CAD}中的两个具有与ABC相同的符号,而一个具有相反的符号,则所有四个点都是极值,并且外壳是四边形ABCD

如果{ABD,BCD,CAD}中的一个具有与ABC相同的符号,并且两个具有相反的符号,则凸包是具有相同符号的三角形;剩下的一点在它里面

如果任何三角形值为零,则三个点共线,中点不是极值。如果所有四个点都是共线的,则所有四个值都应为零,并且外壳将是一条直线或一个点。在这些情况下,请注意数值稳健性问题

对于ABC为正值的情况:

ABC  ABD  BCD  CAD  hull
------------------------
 +    +    +    +   ABC
 +    +    +    -   ABCD
 +    +    -    +   ABDC
 +    +    -    -   ABD
 +    -    +    +   ADBC
 +    -    +    -   BCD
 +    -    -    +   CAD
 +    -    -    -   [should not happen]
或者直接使用。

我是根据一个粗糙版本的礼品包装算法做的

一般情况下效率不高,但只够4分

function Point (x, y)
{
    this.x = x;
    this.y = y;
}

Point.prototype.equals = function (p)
{
    return this.x == p.x && this.y == p.y;
};

Point.prototype.distance = function (p)
{ 
    return Math.sqrt (Math.pow (this.x-p.x, 2) 
                    + Math.pow (this.y-p.y, 2));
};

function convex_hull (points)
{
    function left_oriented (p1, p2, candidate)
    {
        var det = (p2.x - p1.x) * (candidate.y - p1.y) 
                - (candidate.x - p1.x) * (p2.y - p1.y);
        if (det > 0) return true;  // left-oriented 
        if (det < 0) return false; // right oriented
        // select the farthest point in case of colinearity
        return p1.distance (candidate) > p1.distance (p2);
    }

    var N = points.length;
    var hull = [];

    // get leftmost point
    var min = 0;
    for (var i = 1; i != N; i++)
    {
        if (points[i].y < points[min].y) min = i;
    }
    hull_point = points[min];

    // walk the hull
    do
    {
        hull.push(hull_point);

        var end_point = points[0];
        for (var i = 1; i != N; i++) 
        {
            if (  hull_point.equals (end_point)
               || left_oriented (hull_point, 
                                 end_point, 
                                 points[i]))
            {
                end_point = points[i];
            }
        }
        hull_point = end_point;
    }
    /*
     * must compare coordinates values (and not simply objects)
     * for the case of 4 co-incident points
     */
    while (!end_point.equals (hull[0])); 
    return hull;
}
功能点(x,y)
{
这个.x=x;
这个。y=y;
}
Point.prototype.equals=函数(p)
{
返回this.x==p.x&&this.y==p.y;
};
Point.prototype.distance=函数(p)
{ 
返回Math.sqrt(Math.pow(this.x-p.x,2)
+Math.pow(this.y-p.y,2));
};
函数凸包(点)
{
面向左的功能(p1、p2、候选)
{
VarDet=(p2.x-p1.x)*(candidate.y-p1.y)
-(候选者.x-p1.x)*(p2.y-p1.y);
if(det>0)返回true;//向左
if(det<0)返回false;//右向
//选择共线情况下的最远点
返回p1.distance(候选者)>p1.distance(p2);
}
var N=点长度;
var外壳=[];
//得到最左边的点
var min=0;
对于(变量i=1;i!=N;i++)
{
如果(点[i].y<点[min].y)min=i;
}
赫尔_点=点[min];
//在船体上行走
做
{
赫尔。推(赫尔_点);
var end_point=点[0];
对于(变量i=1;i!=N;i++)
{
如果(赫尔点)等于(终点)
||向左(船体点),
终点,,
第[i]点)
{
终点=点[i];
}
}
赫尔点=终点;
}
/*
*必须比较坐标值(而不仅仅是对象)
*对于4个共有事件点的情况
*/
而(!end_point.equals(hull[0]);
返回船体;
}

这很有趣:)

我已经用查找表编写了comingstorm答案的快速实现。由于我的应用程序不需要这四个点,因此不处理所有四个点都是共线的情况。如果点共线,算法将第一个指针点[0]设置为null。如果点[3]是空指针,则外壳包含3个点,否则外壳包含4个点。对于y轴指向顶部、x轴指向右侧的坐标系,外壳按逆时针顺序排列

const char hull4_table[] = {        
    1,2,3,0,1,2,3,0,1,2,4,3,1,2,3,0,1,2,3,0,1,2,4,0,1,2,3,4,1,2,4,0,1,2,4,0,
    1,2,3,0,1,2,3,0,1,4,3,0,1,2,3,0,0,0,0,0,0,0,0,0,2,3,4,0,0,0,0,0,0,0,0,0,
    1,4,2,3,1,4,3,0,1,4,3,0,2,3,4,0,0,0,0,0,0,0,0,0,2,3,4,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,2,4,3,0,0,0,0,0,0,0,0,0,1,2,4,0,1,3,4,0,1,2,4,0,1,2,4,0,
    0,0,0,0,0,0,0,0,1,4,3,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,0,0,0,0,0,0,0,0,0,
    1,4,2,0,1,4,2,0,1,4,3,0,1,4,2,0,0,0,0,0,0,0,0,0,2,3,4,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,2,4,3,0,0,0,0,0,0,0,0,0,2,4,3,0,1,3,4,0,1,3,4,0,1,3,2,4,
    0,0,0,0,0,0,0,0,2,4,3,0,0,0,0,0,0,0,0,0,1,3,2,0,1,3,4,0,1,3,2,0,1,3,2,0,
    1,4,2,0,1,4,2,0,1,4,3,2,1,4,2,0,1,3,2,0,1,3,2,0,1,3,4,2,1,3,2,0,1,3,2,0
};
struct Vec2i {
    int x, y;
};
typedef long long int64;    
inline int sign(int64 x) {
    return (x > 0) - (x < 0);
}
inline int64 orientation(const Vec2i& a, const Vec2i& b, const Vec2i& c) {
    return (int64)(b.x - a.x) * (c.y - b.y) - (b.y - a.y) * (c.x - b.x);
}

void convex_hull4(const Vec2i** points) {
    const Vec2i* p[5] = {(Vec2i*)0, points[0], points[1], points[2], points[3]};
    char abc = (char)1 - sign(orientation(*points[0], *points[1], *points[2]));
    char abd = (char)1 - sign(orientation(*points[0], *points[1], *points[3]));
    char cad = (char)1 - sign(orientation(*points[2], *points[0], *points[3]));
    char bcd = (char)1 - sign(orientation(*points[1], *points[2], *points[3]));

    const char* t = hull4_table + (int)4 * (bcd + 3*cad + 9*abd + 27*abc);
    points[0] = p[t[0]];
    points[1] = p[t[1]];
    points[2] = p[t[2]];
    points[3] = p[t[3]];
}
const char hull4_表[]={
1,2,3,0,1,2,3,0,1,2,4,3,1,2,3,0,1,2,3,0,1,2,4,0,1,2,3,4,1,2,4,0,1,2,4,0,
1,2,3,0,1,2,3,0,1,4,3,0,1,2,3,0,0,0,0,0,0,0,0,0,2,3,4,0,0,0,0,0,0,0,0,0,
1,4,2,3,1,4,3,0,1,4,3,0,2,3,4,0,0,0,0,0,0,0,0,0,2,3,4,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,2,4,3,0,0,0,0,0,0,0,0,0,1,2,4,0,1,3,4,0,1,2,4,0,1,2,4,0,
0,0,0,0,0,0,0,0,1,4,3,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,0,0,0,0,0,0,0,0,0,
1,4,2,0,1,4,2,0,1,4,3,0,1,4,2,0,0,0,0,0,0,0,0,0,2,3,4,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,2,4,3,0,0,0,0,0,0,0,0,0,2,4,3,0,1,3,4,0,1,3,4,0,1,3,2,4,
0,0,0,0,0,0,0,0,2,4,3,0,0,0,0,0,0,0,0,0,1,3,2,0,1,3,4,0,1,3,2,0,1,3,2,0,
1,4,2,0,1,4,2,0,1,4,3,2,1,4,2,0,1,3,2,0,1,3,2,0,1,3,4,2,1,3,2,0,1,3,2,0
};
结构Vec2i{
int x,y;
};
typedef long long int64;
内联整数符号(int64 x){
返回(x>0)-(x<0);
}
内联int64方向(常量向量2i&a、常量向量2i&b、常量向量2i&c){
报税表(int64)(b.x-a.x)*(c.y-b.y)-(b.y-a.y)*(c.x-b.x);
}
空心凸面(常数向量2**点){
常数Vec2i*
  public static IntVector2[] ConvexHull3(IntVector2 a, IntVector2 b, IntVector2 c) {
     var abc = Clockness(a, b, c);
     if (abc == Clk.Neither) {
        var (s, t) = FindCollinearBounds(a, b, c);
        return s == t ? new[] { s } : new[] { s, t };
     }
     if (abc == Clk.Clockwise) {
        return new[] { c, b, a };
     }
     return new[] { a, b, c };
  }

  public static (IntVector2, IntVector2) FindCollinearBounds(IntVector2 a, IntVector2 b, IntVector2 c) {
     var ab = a.To(b).SquaredNorm2();
     var ac = a.To(c).SquaredNorm2();
     var bc = b.To(c).SquaredNorm2();
     if (ab > ac) {
        return ab > bc ? (a, b) : (b, c);
     } else {
        return ac > bc ? (a, c) : (b, c);
     }
  }

  // See https://stackoverflow.com/questions/2122305/convex-hull-of-4-points
  public static IntVector2[] ConvexHull4(IntVector2 a, IntVector2 b, IntVector2 c, IntVector2 d) {
     var abc = Clockness(a, b, c);

     if (abc == Clk.Neither) {
        var (s, t) = FindCollinearBounds(a, b, c);
        return ConvexHull3(s, t, d);
     }

     // make abc ccw
     if (abc == Clk.Clockwise) (a, c) = (c, a);

     var abd = Clockness(a, b, d);
     var bcd = Clockness(b, c, d);
     var cad = Clockness(c, a, d);

     if (abd == Clk.Neither) {
        var (s, t) = FindCollinearBounds(a, b, d);
        return ConvexHull3(s, t, c);
     }

     if (bcd == Clk.Neither) {
        var (s, t) = FindCollinearBounds(b, c, d);
        return ConvexHull3(s, t, a);
     }

     if (cad == Clk.Neither) {
        var (s, t) = FindCollinearBounds(c, a, d);
        return ConvexHull3(s, t, b);
     }

     if (abd == Clk.CounterClockwise) {
        if (bcd == Clk.CounterClockwise && cad == Clk.CounterClockwise) return new[] { a, b, c };
        if (bcd == Clk.CounterClockwise && cad == Clk.Clockwise) return new[] { a, b, c, d };
        if (bcd == Clk.Clockwise && cad == Clk.CounterClockwise) return new[] { a, b, d, c };
        if (bcd == Clk.Clockwise && cad == Clk.Clockwise) return new[] { a, b, d };
        throw new InvalidStateException();
     } else {
        if (bcd == Clk.CounterClockwise && cad == Clk.CounterClockwise) return new[] { a, d, b, c };
        if (bcd == Clk.CounterClockwise && cad == Clk.Clockwise) return new[] { d, b, c };
        if (bcd == Clk.Clockwise && cad == Clk.CounterClockwise) return new[] { a, d, c };
        // 4th state impossible
        throw new InvalidStateException();
     }
  }
  // relative to screen coordinates, so top left origin, x+ right, y+ down.
  // clockwise goes from origin to x+ to x+/y+ to y+ to origin, like clockwise if
  // you were to stare at a clock on your screen
  //
  // That is, if you draw an angle between 3 points on your screen, the clockness of that
  // direction is the clockness this would return.
  public enum Clockness {
     Clockwise = -1,
     Neither = 0,
     CounterClockwise = 1
  }
  public static Clockness Clockness(IntVector2 a, IntVector2 b, IntVector2 c) => Clockness(b - a, b - c);
  public static Clockness Clockness(IntVector2 ba, IntVector2 bc) => Clockness(ba.X, ba.Y, bc.X, bc.Y);
  public static Clockness Clockness(cInt ax, cInt ay, cInt bx, cInt by, cInt cx, cInt cy) => Clockness(bx - ax, by - ay, bx - cx, by - cy);
  public static Clockness Clockness(cInt bax, cInt bay, cInt bcx, cInt bcy) => (Clockness)Math.Sign(Cross(bax, bay, bcx, bcy));
#   positions for d:
#
#           abc > 0                               abc < 0
#         (+-+- doesn't exist)               (-+-+ doesn't exist)
#
#
#                     |        /       ---+ \   --++    | -+++
#                     |       /        bdc   \  acbd    | acd
#                     | +-++ /                \         |
#                     | abd /         ---------A--------B---------
#                     |    /                    \  --+- |
#                     |   /                      \ acb  |
#                     |  /                        \     |
#                     | /                          \    |
#                     |/                  ----      \   | -++-
#                     C                   adcb       \  | acdb
#                    /|                               \ |
#                   / |                                \|
#         ++++     /  |                                 C
#         abcd    /   |                                 |\
#                /    | +--+                            | \
#               /     | abdc                            |  \
#              / ++-+ |                                 |   \
#             /  abc  |                                 |    \
#   ---------A--------B---------                        |     \
#     +++-  /         |                                 |      \
#     bcd  /   ++--   | +---                            | -+--  \
#         /    adbc   | adc                             | adb    \
#
#   or as table
#
#   ++++ abcd       -+++ acd
#   +++- bcd        -++- acdb
#   ++-+ abc        -+-+ XXXX
#   ++-- adbc       -+-- adb
#   +-++ abd        --++ acbd
#   +-+- XXXX       --+- acb
#   +--+ abdc       ---+ bdc
#   +--- adc        ---- adcb
#
# if there are some collinear points, the hull will be nil (for the moment)
#
def point_point_point_orientation(p, q, r)
  (q.x - p.x) * (r.y - q.y) - (q.y - p.y) * (r.x - q.x)
end



def convex_hull_4_points(a, b, c, d)
  abc = point_point_point_orientation(a, b, c)
  if abc.zero?
    # todo
    return nil
  end

  bcd = point_point_point_orientation(b, c, d)
  if bcd.zero?
    # todo
    return nil
  end

  cda = point_point_point_orientation(c, d, a)
  if cda.zero?
    # todo
    return nil
  end

  dab = point_point_point_orientation(d, a, b)
  if dab.zero?
    # todo
    return nil
  end

  if abc.positive?
    if bcd.positive?
      if cda.positive?
        if dab.positive?
          [a, b, c, d] # ++++
        else
          [b, c, d]    # +++-
        end
      else
        if dab.positive?
          [a, b, c]    # ++-+
        else
          [a, d, b, c] # ++--
        end
      end
    else
      if cda.positive?
        if dab.positive?
          [a, b, d]    # +-++
        else
          raise        # +-+-
        end
      else
        if dab.positive?
          [a, b, d, c] # +--+
        else
          [a, d, c]    # +---
        end
      end
    end
  else
    if bcd.positive?
      if cda.positive?
        if dab.positive?
          [a, c, d]    # -+++
        else
          [a, c, d, b] # -++-
        end
      else
        if dab.positive?
          raise        # -+-+
        else
          [a, d, b]    # -+--
        end
      end
    else
      if cda.positive?
        if dab.positive?
          [a, c, b, d] # --++
        else
          [a, c, b]    # --+-
        end
      else
        if dab.positive?
          [b, d, c]    # ---+
        else
          [a, d, c, b] # ----
        end
      end
    end
  end
end