0

nestedTriangles.cpp(附)说明:

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

伪代码:

当且仅当第一个三角形的所有三个顶点都位于第二个三角形的内部时,一个三角形位于另一个三角形内。假设我们有一个三角形,其顶点为 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 的所有点都在直线的一侧,而 f(x,y) < 0 的所有点都在另一侧 所以判断一个点 (x,y) 是否存在的问题) 位于三角形的内部,可以检查构成三角形的三条线中的每条线的 f(x,y) 的符号。一个复杂的因素是,对于任何给定的三角形,我们不知道这三个符号是否应该全部为正、全部为负,或两者兼而有之。

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

我需要的:

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

    #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);
    }
4

1 回答 1

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_a并且l_dir基本上是您的点p,并且line.EndPoint2在原点所在的坐标集中line.endPoint1
  2. 您可以检查该表达式l_dir.x * p_a.y - l_dir.y * p_a.x基本上是 和 之间的叉l_dirp_a。因此,如果p_a在 的“左侧” l_dir,即如果p在 的左侧,则为正line

现在, 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;
};

因此,阅读您的代码的人会真正理解您的细分是有方向的。

于 2013-09-08T22:57:50.230 回答