结构化数据:嵌套三角形(使用结构并进行一些几何计算)

时间:2013-09-08 21:55:58

标签: c++ geometry

  

nestedTriangles.cpp(附)说明:

     

程序将一对三角形作为输入,指定为给定   每个trangle顶点的坐标。然后它确定是否   三角形是“嵌套”在另一个中,意味着一个   三角形完全位于另一个的内部。

     

伪代码:

     

一个三角形位于另一个三角形内,当且仅当所有三个顶点都是   第一个三角形位于第二个三角形的内部。   假设我们有一个带有顶点A,B和C的三角形,如上所述   分别通过坐标(xA,yA),(xB,yB)和(xC,yC)。该   三角形的边是线段AB,BC和CA.一条线   通过两个点(x1,y1)和(x2,y2)可以考虑   是满足方程f(x,y)= 0的点(x,y)的集合   f(x,y)给出为f(x,y)=(x-x1)(y2-y1) - (y-y1)(x2-x1)   关于f(x,y)的一个有趣的事情是我们可以使用它   确定行的点(x,y)在哪一行的“侧”:

     

如果f(x,y)= 0,则该点正好在该行上。所有要点   f(x,y)> 0位于该行的一侧,而All位于其中   f(x,y)< 0是在另一边所以确定是否的问题   一个点(x,y)在一个trangle的内部可以检查标志   f(x,y)对于构成三角形的三条线中的每一条线。一个   对于任何给定的三角形,复杂因素是我们不知道的   这三个标志是否应该是积极的,都是消极的,或者   两者的混合物。

     

三角形的质心可以计算为x的“平均值”   和顶点的y坐标:xcen =(xA + xB + xC)/ 3 ycen =(yA   + yB + yC)/ 3此点(xcen,ycen)肯定在trangle内(除非三角形是“退化”并且没有内部点)。该   确定(x,y)是否在三角形内部的问题   因此可以通过检查它是否在同一侧来解决   每个trangle的线段为(xcen,ycen)。

     

我需要什么:

     

为LineSegment填写缺少的结构类型,我需要每一行   段有两个字段,分别称为“endPoint1”和“endPoint2”   Point类型。 Point类型已经声明。然后我想填补   在函数eval和areOnSameSideOf的缺失主体中,   操纵线段。我认为从内部调用eval   areOnSameSideOf将简化后者的实现。

    #include <iostream>
    using namespace std;

        /**
         * 2D Cartesian coordinates
         */
        struct Point {
          double x;
          double y;
        };

    /**
     * A simple triangle, modeled as a collection of 3 vertices
     */
    struct Triangle {
      Point vertices[3];
    };

    /**
     * A line segment, indicated by its two endpoints
     */
    struct LineSegment
    {
      Point endPoint1;
      Point endPoint2; 
    }; 

    /**
     * Read a trangle from the input stream.
     *
     * A triangle is presented as a sequence of 6 floating point values,
     * each successive pair of numbers being the x,y coordinates of one vertex.
     *
     * @param t  the triangle being read in
     * @param in the input from which it should be read
     */

    void readTriangle (Triangle& t, istream& in)
    {
      for (int i = 0; i < 3; ++i)
        {
          double x, y;
          in >> x >> y;
          Point p = {x, y};
          t.vertices[i] = p;
        }
    }


    /**
     * Evaluate a point with respect to the equation defining the line
     * passing through a line segment.
     *
     *  A line is defined as the set of points satisfying f(x,y) = 0
     *  where f(x,y) is a function of the form ax + by + c. This function
     *  computes that f(x,y) for an arbitrary point, which might not be
     *  on that line.
     *
     * @param line a line segment
     * @param p a point at which we ant the line function evaluated
     * @return A value that is zero for points on the line, positive for all
     *          points on one side of the line, and negative for all points
     *          on the other side of the line.
     */

    double eval (LineSegment line, Point p)
    {
         //*** see "What I need"
 }




    /**
     * Check two points to see if they lie on the same side of a line
     *
     * @param p1 a point
     * @param p2 another point
     * @param line a line segment
     * @return true iff p1 and p2 are on the same side of the line
     *                  passing through the given line segment.
     *                  If either or both points lie exactly on the line,
     *                  return false.
     */

    bool areOnSameSideOf (Point p1, Point p2, LineSegment line)
    {
      //*** see "What I need"
    }

    /**
     * Check to see if a point lies on the interior of a triangle.
     *
     * @param p a point
     * @param t a triangle
     * @return true iff p lies in the interior of the trangle t
     */

    bool isWithin (Point p, Triangle t)
    {
      Point centroid = {0.0, 0.0};
      for (int i = 0; i < 3; ++i)
        {
          centroid.x += t.vertices[i].x;
          centroid.y += t.vertices[i].y;

        }
      centroid.x /= 3.0;
      centroid.y /= 3.0;

      for (int i = 0; i < 3; ++i)
        {
          LineSegment side = {t.vertices[i], t.vertices[(i+1)%3]};
          if (!areOnSameSideOf (p, centroid, side))
        return false;
        }
      return true;
    }

    /**
     * Check to see if one triangle lies entirely within the
     * interior of the other.
     *
     * @param outer
     * @param inner
     * @return true iff inner lies entirely within the interior of outer
     */

    bool contains (Triangle outer, Triangle inner)
    {
      for (int i = 0; i < 3; ++i)
        {
          if (!isWithin(inner.vertices[i], outer))
        return false;
        }
      return true;
    }

    /**
     * Check to see if either of two triangles is
     * entirely contained within the other.
     *
     * @param t1
     * @param t2
     */

    void checkForNesting (Triangle t1, Triangle t2)
    {
      if (contains(t1, t2) || contains(t2, t1))
        cout << "These triangles nest." << endl;
      else
        cout << "These triangles do not nest." << endl;
    }

    int main (int argc, char** argv)
    {
      cout << "Enter the x,y coordinates of three vertices of a triangle: " << flush;
      Triangle t1;
      readTriangle (t1, cin);
      cout << "Enter the x,y coordinates of three vertices of another triangle: " << flush;
      Triangle t2;
      readTriangle (t2, cin);
      checkForNesting (t1, t2);
    }

1 个答案:

答案 0 :(得分:0)

让我们从你的“eval”功能开始吧。

double eval (LineSegment line, Point p)
{
   Point p_a = p - line.endPoint1;
   Point l_dir = line.EndPoint2 - line.EndPoint1;

   return p_a.x * l_dir.y - p_a.y * l_dir.x;
}

为什么会这样?

  1. p_al_dir基本上是您在p为原点的坐标集中的点line.EndPoint2line.endPoint1
  2. 您可以检查表达式l_dir.x * p_a.y - l_dir.y * p_a.x基本上是l_dirp_a之间的交叉产品。因此,如果p_a位于l_dir的“左侧”,即p位于line的左侧,则会为正。
  3. 现在,areOnSameSideOf非常简单,只需检查eval(line,p1)eval(line,p2)是否具有相同的符号。

    bool areOnSameSideOf (Point p1, Point p2, LineSegment line)
    {
        return eval(line,p1) * eval(line,p2) > 0.0; //Use >= if you want to consider "inside" points which are exactly on the segment "line"
    }
    

    你的方法并不差,但有一种“标准”方式来做这样的事情,这样做更有效率。

    核心思想是始终逆时针(或顺时针,只选择一个)定义三角形顶点的顺序。

    为了做到这一点,我们需要的是makeTriangleCounterclockwise函数;我们可以重复eval来执行此操作。

    void makeTriangleCounterclockwise(Triangle & t)
    {
        LineSegment ab = {t.vertices[1], t.vertices[0]};
        if ( eval(ab,t.vertices[2]) < 0.0) {
                /** vertices[2] is on the "right" of ab. Notice that if this eval returns
    exactly zero that means that your triangle is singular**/
            swap(t.vertices[0],t.vertices[1]);
        }
    }
    

    现在你不再需要三角形的质心,因为eval将永远返回 “左侧”的正值和“右侧”的负值,您知道三角形是按逆时针顺序定义的

    bool isWithin (Point p, Triangle t)
    {
       for (int i = 0; i < 3; ++i)
       {
          LineSegment side = {t.vertices[i], t.vertices[(i+1)%3]};
          if (eval(side, p) < 0.0)
                return false;
       }
       return true;
    }
    

    附注:更好地将LineSegment定义为:

    struct LineSegment {
        Point startPoint,endPoint;
    };
    

    因此,阅读代码的人实际上会理解您的细分受众群有方向