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);
}
答案 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;
}
为什么会这样?
p_a
和l_dir
基本上是您在p
为原点的坐标集中的点line.EndPoint2
和line.endPoint1
。l_dir.x * p_a.y - l_dir.y * p_a.x
基本上是l_dir
和p_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;
};
因此,阅读代码的人实际上会理解您的细分受众群有方向。