第5周-任务1-三角形类中的构造函数

本文详细阐述了如何在C++中使用构造函数来初始化三角形类的对象,包括默认构造函数、带参数构造函数及使用参数初始化表对数据成员进行初始化。通过多个版本的程序实现,全面体现了构造函数的不同写法。

【题目】设计三角形类,通过增加构造函数,使对象在定义时能够进行初始化

特别要求:为全面体会构造函数的各种写法,本任务要求提交多个版本的程序,体现出你已经掌握下列知识点:(1)使用带参数构造函数,即Triangle(float x, float y, float z),三边长在调用时由实参直接给出;(2)设计默认构造函数,即不指定参数时,默认各边长为1;(3)使用默认参数的构造函数,不给定实参时,默认边长为1;(4)在构造函数中使用参数初始化表对数据成员初始化。

#include<iostream>
using namespace std;
class Triangle
{
public:
	float perimeter(void);//计算三角形的周长
	float area(void);//计算并返回三角形的面积
	void showMessage();
private:
	float a,b,c; //三边为私有成员数据
};

Void Triangle:: showMessage() //!!!!Void应为void,大小写是敏感的
{
	cout<<”三角形的三边长分别为:”<<a<<'\t'<<<<b<<<<'\t'<<<<c<<endl;//!!!!<<<<没有道理
	cout<<"该三角形的周长为:"<< Tri1.Perimeter()<<'\t'<<"面积为:"<< Tri1.Area()<<endl<<endl; //!!!!在类中扯入对象Tri1意欲何为?
}

void main(void)
{
	Triangle Tri1;	
	Tri1.showMessage();
	Triangle Tri2(7,8,9);	
	Tri1.showMessage();  //!!!!现在是要显示Tri2的信息
}

提供给同学们的程序中有几处低级错误,有的导致编译不畅,有的让输出莫名其妙。自行改正过的同学值得庆贺:坏程序也阻止不了你的脚步;而对于受困于老贺使坏的童鞋,请看代码中用!!!!标出的注释,修改你的代码,进步还要加快。


【解答过程】请点下面的视频链接



【参考解答】

解答1: 体现要求1和2

#include<iostream>
#include<Cmath>
using namespace std;
class Triangle
{
public:
    Triangle(){a=1;b=1;c=1;} //()设计默认构造函数,即不指定参数时,默认各边长为;
    Triangle(float x, float y, float z)    {a=x; b=y; c=z;} //使用带参数构造函数,即Triangle(floatx, float y, float z),三边长在调用时由实参直接给出
    float perimeter(void);//计算三角形的周长
    float area(void);//计算并返回三角形的面积
    void showMessage();
private:
    float a,b,c;//三边为私有成员数据
};
 
float Triangle::perimeter(void) 
{ 
    return (a +b + c); 
} 
 
float Triangle::area(void) 
{ 
    float s =(a + b + c) / 2;   
 
    return sqrt(s * (s - a) * (s - b) * (s - c));  
}
 
void Triangle::showMessage()
{
    cout<<"三角形的三边长分别为:"<<a<<' '<<b<<' '<<c<<endl;
    cout<<"该三角形的周长为:"<<perimeter()<<' '<<"面积为:"<<area()<<endl<<endl;
}
 
void main(void)
{
    Triangle Tri1;    //定义三角形类的一个实例(对象)
    Tri1.showMessage();
    Triangle Tri2(7,8,9);    //定义三角形类的一个实例(对象)
    Tri2.showMessage();
    system("pause");
}

解答2:体现要求(3)使用默认参数的构造函数,不给定实参时,默认边长为1。另外,将构造函数定义在了类体外。注意:由于全部参数都给出默认值,不能再重构默认构造函数。

class Triangle
{
public:
    Triangle(float x=1, float y=1, float z=1);//3)使用默认参数的构造函数,不给定实参时,默认边长为;
    float perimeter(void);//计算三角形的周长
    float area(void);//计算并返回三角形的面积
    void showMessage();
private:
    float a,b,c;//三边为私有成员数据
};
 
Triangle::Triangle(float x, float y, float z)
{
    a=x;
    b=y;
    c=z;
}

解答3:体现要求(4)在构造函数中使用参数初始化表对数据成员初始化。换个写法而已,不过,这是一种提倡的写法。

class Triangle
{
public:
    Triangle(floatx=1, float y=1, float z=1);
    float perimeter(void);//计算三角形的周长
    float area(void);//计算并返回三角形的面积
    void showMessage();
private:
    float a,b,c;//三边为私有成员数据
};
Triangle::Triangle(float x, float y, float z):a(x),b(y),c(z){} //(4)在构造函数中使用参数初始化表对数据成员初始化。
还可以将构造函数在类体内定义。这样,构造函数是内置函数。代码如下:

class Triangle
{
public:
	Triangle(float x=1, float y=1, float z=1) :a(x),b(y),c(z){}//内置的构造函数
	float perimeter(void);//计算三角形的周长
	float area(void);//计算并返回三角形的面积
	void showMessage();
private:
	float a,b,c; //三边为私有成员数据
};

【拓展(选做)】请自行设计一个矩形类,可以计算长方形的面积、周长、对象线,判断是否是正方形。请用上构造函数。

<think> 根据用户需求,我们需要实现一个平面坐标系统中的三角形相交检测程序。由于是在平面坐标系(2D),我们可以简化问题。判断两个三角形是否相交,可以通过检查两个三角形是否有边相交或一个三角形完全包含另一个三角形来实现。 方法: 1. 判断两个三角形是否有边相交(线段相交) 2. 判断一个三角形是否完全位于另一个三角形内部(通过判断点是否在三角形内) 步骤: 1. 定义点类(Point) 2. 定义三角形类(Triangle),包含三个点 3. 在三角形类中实现方法: a. 判断线段(边)是否相交(使用叉积判断) b. 判断一个点是否在三角形内部(使用重心坐标法或面积法) c. 判断两个三角形是否相交(先判断边相交,再判断包含关系) 注意:两个三角形相交的情况包括: - 边相交(至少一对边相交) - 一个三角形完全包含另一个三角形(没有边相交,但一个三角形的所有点都在另一个三角形内) 因此,判断两个三角形A和B是否相交: - 检查A的任意一条边与B的任意一条边是否相交(包括线段相交) - 或者检查A的所有点是否在B的内部,或者B的所有点是否在A的内部(包含关系) 由于是平面问题,我们可以使用2D几何方法。 具体实现: 1. 点类(Point)包含x和y坐标。 2. 三角形类(Triangle)包含三个Point对象。 3. 实现辅助方法: - 叉积(cross product):用于判断点与线段的位置关系 - 判断两个线段是否相交(使用跨立试验,通过判断两个线段的两个端点是否在另一线段的两侧) - 判断点是否在三角形内:使用面积法(或重心坐标法)这里我们使用面积法:如果点P与三角形三个顶点组成的三个子三角形的面积和等于原三角形面积,则点在三角形内(注意浮点误差) 但是注意,面积法可能因为浮点精度问题导致误差,我们使用一个很小的误差值。 另一种方法是使用重心坐标法:计算点P相对于三角形ABC的重心坐标,如果三个坐标都在[0,1]之间,且在三角形内(包括边界),则点在三角形内。 我们选择使用重心坐标法(面积法的一种等价形式)来判断点是否在三角形内。 步骤: 1. 计算三角形ABC的面积(使用叉积) 2. 计算子三角形PBC, PCA, PAB的面积 3. 重心坐标:u = 面积(PBC)/面积(ABC), v = 面积(PCA)/面积(ABC), w = 面积(PAB)/面积(ABC) 4. 如果u,v,w >=0 且 u+v+w≈1(考虑浮点误差),则点在三角形内。 注意:由于是2D,叉积就是两个向量的叉积(标量),公式为:cross = (Bx-Ax)*(Cy-Ay) - (By-Ay)*(Cx-Ax) 但是,这里我们也可以使用一个更高效的判断点是否在三角形内的方法:使用叉积判断点是否在三条边的同侧(只适用于凸多边形,三角形是凸的)。 方法:分别计算点P与每条边构成的向量与边的向量的叉积,如果三个叉积的符号相同(或者有0,表示在边上),则点在三角形内。 具体步骤(以三角形ABC和点P为例): 1. 计算向量AB、BC、CA 2. 计算向量AP、BP、CP 3. 计算AB×AP, BC×BP, CA×CP(叉积,结果是一个标量) 4. 如果三个叉积同号(同为正或同为负,或者有0),则点在三角形内;否则在三角形外。 注意:叉积的符号表示方向(顺时针或逆时针)。如果三角形顶点顺序是逆时针,则点P在内部时三个叉积都应该大于等于0(或小于等于0,取决于方向)。为了统一,我们可以先保证三角形的顶点顺序(例如逆时针)。如果用户输入的顶点顺序不定,我们可以先调整顺序。 但是,判断两个三角形相交并不要求三角形顶点的顺序,所以我们可以使用线段相交和点包含两种方法。 因此,我们采用以下步骤: 方法1:判断两个线段是否相交(使用跨立试验) 方法2:判断点是否在三角形内(使用叉积符号判断) 实现跨立试验: 判断线段AB和CD是否相交: 1. 快速排斥:以AB和CD为对角线的矩形是否有重叠(可省略,但可以快速排除) 2. 跨立试验: - 点C和点D在线段AB的两侧:即(向量AB×向量AC)与(向量AB×向量AD)异号(或一个为0) - 点A和点B在线段CD的两侧:即(向量CD×向量CA)与(向量CD×向量CB)异号(或一个为0) 同时满足这两个条件则相交。 由于是2D,叉积计算:对于向量a=(a1,a2), b=(b1,b2),叉积为:a1*b2 - a2*b1。 因此,我们可以这样实现: 步骤总览: 1. 创建Point类(x, y) 2. 创建Triangle类(三个Point) 3. 在Triangle类中实现方法: - 获取三条边(返回三个线段,每个线段用两个点表示) - 判断一个点是否在三角形内(使用叉积符号法) - 判断两个三角形是否相交(方法:先检查所有边对是否相交,再检查是否有包含关系) 注意:由于两个三角形可能有一个点在另一个三角形内部,但没有边相交(比如一个三角形包含另一个三角形的顶点),所以必须检查包含关系:即检查三角形A的任意一个顶点是否在三角形B内,或者三角形B的任意一个顶点是否在三角形A内。 因此,判断两个三角形A和B相交: if (A的任意一条边与B的任意一条边相交) -> 相交 else if (A的任意一个顶点在B的内部 或者 B的任意一个顶点在A的内部) -> 相交 else -> 不相交 注意:这里没有考虑两个三角形共线的情况(但题目是平面坐标系,且三角形不相交于边的情况?),但按照通常的几何关系,共线且重叠的边也会被判断为线段相交,所以没问题。 程序结构: 1. Point.java 2. Triangle.java 3. Main.java(包含main方法,读取用户输入,创建两个三角形,判断相交并输出结果) 用户输入:每个三角形的三个顶点的坐标(x,y),可以按顺序输入。 注意:输入的顺序可能是顺时针或逆时针,但不会影响线段相交的判断和点是否在三角形内的判断(因为叉积判断点是否在三角形内需要顶点顺序一致,但我们可以通过叉积的符号是否同号(忽略正负,只要同号)来判断。因此,我们不需要调整顺序。 但是,对于点是否在三角形内的判断,如果三角形的顶点顺序是顺时针的,那么三个叉积的符号应该是同为负(或0)才表明点在内部;如果是逆时针,则三个叉积的符号应该是同为正(或0)。为了统一,我们可以在计算叉积后,判断三个叉积是否同号(都大于等于0或都小于等于0)即可。 实现步骤: 1. Point类: public class Point { public double x; public double y; // 构造方法 } 2. Triangle类: public class Triangle { private Point p0, p1, p2; // 构造方法 // 获取三条边:返回一个线段数组(每个线段用度为2的Point数组表示) // 判断点p是否在三角形内:使用叉积符号法 // 判断与另一个三角形是否相交 } 3. 实现叉积工具方法(静态方法,计算两个向量的叉积,向量由两个点构成:从a到b的向量为(b.x-a.x, b.y-a.y)) public static double crossProduct(Point a, Point b, Point c) { // 向量ab = (b.x - a.x, b.y - a.y) // 向量ac = (c.x - a.x, c.y - a.y) // 叉积: ab.x * ac.y - ab.y * ac.x return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x); } 4. 判断点p是否在三角形内: public boolean contains(Point p) { // 计算三个叉积(注意顶点顺序) double d1 = crossProduct(p0, p1, p); double d2 = crossProduct(p1, p2, p); double d3 = crossProduct(p2, p0, p); // 判断三个叉积是否同号(都大于等于0 或 都小于等于0) if ((d1 >= 0 && d2 >= 0 && d3 >= 0) || (d1 <= 0 && d2 <= 0 && d3 <= 0)) { return true; } return false; } 5. 判断两条线段是否相交(静态方法): public static boolean segmentsIntersect(Point a, Point b, Point c, Point d) { // 快速排斥(可选,提高效率) if (Math.max(a.x, b.x) < Math.min(c.x, d.x) || Math.max(c.x, d.x) < Math.min(a.x, b.x) || Math.max(a.y, b.y) < Math.min(c.y, d.y) || Math.max(c.y, d.y) < Math.min(a.y, b.y)) { return false; } // 跨立试验 double cross1 = crossProduct(a, b, c); // 向量ab与ac的叉积 double cross2 = crossProduct(a, b, d); // 向量ab与ad的叉积 double cross3 = crossProduct(c, d, a); // 向量cd与ca的叉积 double cross4 = crossProduct(c, d, b); // 向量cd与cb的叉积 // 如果两个叉积异号(一个正一个负),则说明两点在线段两侧(或者有0) // 注意:0表示共线,这里我们考虑共线情况(如果共线,则可能重叠或部分重叠,但在快速排斥中矩形重叠,并且这里共线,我们视为相交?但实际需要进一步判断重叠?) // 但题目要求是判断两个三角形是否相交,所以即使是共线重叠也算相交。 // 因此,我们只需要cross1和cross2符号相反(或0)且cross3和cross4符号相反(或0)则相交 if (cross1 * cross2 <= 0 && cross3 * cross4 <= 0) { return true; } return false; } 6. 在Triangle类中实现判断与另一个三角形是否相交: public boolean intersects(Triangle t) { // 获取当前三角形的三条边 Point[] edge1 = {this.p0, this.p1}; Point[] edge2 = {this.p1, this.p2}; Point[] edge3 = {this.p2, this.p0}; // 获取另一个三角形的三条边 Point[] tEdge1 = {t.p0, t.p1}; Point[] tEdge2 = {t.p1, t.p2}; Point[] tEdge3 = {t.p2, t.p0}; // 检查所有边对是否相交 if (segmentsIntersect(edge1[0], edge1[1], tEdge1[0], tEdge1[1]) || segmentsIntersect(edge1[0], edge1[1], tEdge2[0], tEdge2[1]) || segmentsIntersect(edge1[0], edge1[1], tEdge3[0], tEdge3[1]) || segmentsIntersect(edge2[0], edge2[1], tEdge1[0], tEdge1[1]) || segmentsIntersect(edge2[0], edge2[1], tEdge2[0], tEdge2[1]) || segmentsIntersect(edge2[0], edge2[1], tEdge3[0], tEdge3[1]) || segmentsIntersect(edge3[0], edge3[1], tEdge1[0], tEdge1[1]) || segmentsIntersect(edge3[0], edge3[1], tEdge2[0], tEdge2[1]) || segmentsIntersect(edge3[0], edge3[1], tEdge3[0], tEdge3[1])) { return true; } // 检查包含关系:当前三角形是否包含另一个三角形的任意一个顶点 if (this.contains(t.p0) || this.contains(t.p1) || this.contains(t.p2) || t.contains(this.p0) || t.contains(this.p1) || t.contains(this.p2)) { return true; } return false; } 7. 主程序(Main.java): 使用Scanner读取用户输入,输入两个三角形的6个点(每个点输入x和y),然后创建两个三角形对象,调用intersects方法判断并输出结果。 注意:输入顺序,用户输入第一个三角形的三个点,然后第二个三角形的三个点。 由于浮点数的精度问题,在比较叉积时,我们使用一个很小的容差值(例如1e-10)?但上面的判断相交的方法中,我们直接使用了乘法和小于等于0(包含了0)来处理共线的情况,所以没有显式的容差。但在实际应用中,浮点误差可能会导致判断错误。因此,我们可以引入一个容差值,但这里为了简单,先不考虑。 但是,在点是否在三角形内的判断中,我们使用叉积等于0表示在边上,所以包含边界。而线段相交判断中,叉积为0表示点在线上,符合要求。 因此,我们按照上述方法实现。 输入示例: 请输入第一个三角形的三个点坐标(每行一个点的x y,用空格分隔): 0 0 1 0 0 1 请输入第二个三角形的三个点坐标: 0.5 0.5 1.5 0.5 0.5 1.5 输出:两个三角形相交。 现在,我们编写代码。 注意:由于用户输入的点可能不是按顺序(顺时针或逆时针)排列,我们的点是否在三角形内的判断方法(叉积符号法)是否仍然有效? 答案是有效的,因为即使三角形顶点顺序是顺时针,三个叉积的符号就会同时为负;如果是逆时针,三个叉积的符号就会同时为正。所以只要三个叉积同号(包括0)就表示点在三角形内。 但是,如果三角形的顶点顺序是混乱的(比如一个顺时针排列,一个逆时针排列),那么我们的方法仍然有效吗? 实际上,我们的contains方法依赖于三角形的顶点顺序(要么都是顺时针,要么都是逆时针)。如果用户输入的点是乱序(比如不是按顺序连接形成三角形),那么我们的方法就会出错。 因此,我们需要在构造三角形时,将顶点调整为顺时针或逆时针顺序?或者不调整,而是使用一个与顶点顺序无关的方法(例如重心坐标法)? 我们这里选择使用重心坐标法(面积法)来避免顶点顺序问题。 修改contains方法:使用重心坐标法(通过面积计算) 步骤: 计算整个三角形的面积(使用叉积绝对值除以2) 计算三个子三角形(P和三角形两个顶点)的面积 三个子三角形的面积和等于原三角形面积(在容差范围内),则点在内部。 但是,面积法需要面积非负,所以取叉积的绝对值。 方法: double area = Math.abs(crossProduct(p0, p1, p2)) / 2.0; // 原三角形面积 double area1 = Math.abs(crossProduct(p, p0, p1)) / 2.0; // 三角形P p0 p1 double area2 = Math.abs(crossProduct(p, p1, p2)) / 2.0; // 三角形P p1 p2 double area3 = Math.abs(crossProduct(p, p2, p0)) / 2.0; // 三角形P p2 p0 double sumArea = area1 + area2 + area3; // 如果sumArea和area在容差范围内相等,则点在三角形内 if (Math.abs(area - sumArea) < 1e-10) { return true; } return false; 但是注意,这种方法在点在边上时,某个子三角形面积为0,但总和仍然等于原面积,所以点在边界上算在内部。符合要求。 另外,这种方法与顶点顺序无关,因为面积取了绝对值。 因此,我们修改contains方法为: public boolean contains(Point p) { double area = Math.abs(crossProduct(p0, p1, p2)) / 2.0; double area1 = Math.abs(crossProduct(p, p0, p1)) / 2.0; double area2 = Math.abs(crossProduct(p, p1, p2)) / 2.0; double area3 = Math.abs(crossProduct(p, p2, p0)) / 2.0; double sumArea = area1 + area2 + area3; // 考虑浮点误差,使用容差值 if (Math.abs(area - sumArea) < 1e-10) { return true; } return false; } 但是,注意:如果原三角形面积为0(即三个点共线),那么它不是一个有效的三角形,但我们这里假设用户输入的是有效的三角形(非退化)。在输入时我们可以检查,但题目没有要求,所以我们先不检查。 因此,我们使用面积法作为点是否在三角形内的判断。 现在,我们编写完整的代码。 注意:线段相交判断还是用叉积符号法(跨立试验),因为线段相交判断与方向无关(叉积的符号相反即表示在两侧,与顺序无关?实际上,跨立试验要求线段的方向,但是无论线段方向如何,叉积的符号关系都能正确判断两点是否在另一线段的两侧。所以跨立试验不需要调整顺序。 所以,最终代码结构: Point.java: public class Point { double x, y; public Point(double x, double y) { this.x = x; this.y = y; } } Triangle.java: public class Triangle { private Point p0, p1, p2; public Triangle(Point p0, Point p1, Point p2) { this.p0 = p0; this.p1 = p1; this.p2 = p2; } // 计算叉积 (a->b) 与 (a->c) 的叉积 private static double crossProduct(Point a, Point b, Point c) { return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x); } // 判断点p是否在三角形内(使用面积法) public boolean contains(Point p) { double area = Math.abs(crossProduct(p0, p1, p2)); // 注意这里没有除以2,因为后面比较时可以约掉 double area1 = Math.abs(crossProduct(p, p0, p1)); double area2 = Math.abs(crossProduct(p, p1, p2)); double area3 = Math.abs(crossProduct(p, p2, p0)); double sumArea = area1 + area2 + area3; // 考虑浮点误差,比较比例(也可以直接比较差) if (Math.abs(area - sumArea) < 1e-10) { return true; } return false; } // 判断两条线段是否相交 private static boolean segmentsIntersect(Point A, Point B, Point C, Point D) { // 快速排斥 if (Math.max(A.x, B.x) < Math.min(C.x, D.x) || Math.max(C.x, D.x) < Math.min(A.x, B.x) || Math.max(A.y, B.y) < Math.min(C.y, D.y) || Math.max(C.y, D.y) < Math.min(A.y, B.y)) { return false; } double cross1 = crossProduct(A, B, C); double cross2 = crossProduct(A, B, D); double cross3 = crossProduct(C, D, A); double cross4 = crossProduct(C, D, B); // 判断符号是否相反(包括0) if (cross1 * cross2 <= 0 && cross3 * cross4 <= 0) { return true; } return false; } // 判断当前三角形与另一个三角形t是否相交 public boolean intersects(Triangle t) { // 检查所有边对 if (segmentsIntersect(this.p0, this.p1, t.p0, t.p1) || segmentsIntersect(this.p0, this.p1, t.p1, t.p2) || segmentsIntersect(this.p0, this.p1, t.p2, t.p0) || segmentsIntersect(this.p1, this.p2, t.p0, t.p1) || segmentsIntersect(this.p1, this.p2, t.p1, t.p2) || segmentsIntersect(this.p1, this.p2, t.p2, t.p0) || segmentsIntersect(this.p2, this.p0, t.p0, t.p1) || segmentsIntersect(this.p2, this.p0, t.p1, t.p2) || segmentsIntersect(this.p2, this.p0, t.p2, t.p0)) { return true; } // 检查包含关系 if (this.contains(t.p0) || this.contains(t.p1) || this.contains(t.p2) || t.contains(this.p0) || t.contains(this.p1) || t.contains(this.p2)) { return true; } return false; } } Main.java: import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.println("请输入第一个三角形的三个点坐标(每行一个点的x y,用空格分隔):"); Point[] points1 = new Point[3]; for (int i = 0; i < 3; i++) { double x = scanner.nextDouble(); double y = scanner.nextDouble(); points1[i] = new Point(x, y); } System.out.println("请输入第二个三角形的三个点坐标:"); Point[] points2 = new Point[3]; for (int i = 0; i < 3; i++) { double x = scanner.nextDouble(); double y = scanner.nextDouble(); points2[i] = new Point(x, y); } Triangle tri1 = new Triangle(points1[0], points1[1], points1[2]); Triangle tri2 = new Triangle(points2[0], points2[1], points2[2]); if (tri1.intersects(tri2)) { System.out.println("两个三角形相交。"); } else { System.out.println("两个三角形不相交。"); } scanner.close(); } } 注意:上面的快速排斥试验可以减少计算量。 测试:可以测试两个相交的三角形(边相交、包含)、两个不相交的三角形。 注意:浮点精度问题,在面积法中,我们比较面积和时使用了1e-10的容差。 至此,我们完成了程序的设计。 但是,注意:面积法中,我们计算了叉积的绝对值,然后相加。而原三角形的面积也是叉积的绝对值。这样,即使三角形是顺时针或逆时针,面积都是正值。 因此,这个方法是有效的。 最后,我们将代码组织成三个类。 </think> 以下是Java实现的平面坐标系中三角形相交检测程序。程序包含`Point`、`Triangle`和`Main`三个类,通过键盘输入两个三角形的顶点坐标,使用**边相交检测**和**点包含检测**两种方法判断三角形是否相交。 ### 完整代码 #### 1. Point.java(点类) ```java public class Point { public double x; public double y; public Point(double x, double y) { this.x = x; this.y = y; } } ``` #### 2. Triangle.java(三角形类) ```java public class Triangle { private Point p0, p1, p2; public Triangle(Point p0, Point p1, Point p2) { this.p0 = p0; this.p1 = p1; this.p2 = p2; } // 计算向量叉积(用于判断相对位置) private static double crossProduct(Point a, Point b, Point c) { return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x); } // 判断点是否在三角形内部(使用面积法) public boolean contains(Point p) { double area = Math.abs(crossProduct(p0, p1, p2)); double area1 = Math.abs(crossProduct(p, p0, p1)); double area2 = Math.abs(crossProduct(p, p1, p2)); double area3 = Math.abs(crossProduct(p, p2, p0)); double sumArea = area1 + area2 + area3; return Math.abs(area - sumArea) < 1e-10; // 浮点误差容差 } // 判断两条线段是否相交 private static boolean segmentsIntersect(Point A, Point B, Point C, Point D) { // 快速排斥试验(矩形重叠检查) if (Math.max(A.x, B.x) < Math.min(C.x, D.x) || Math.max(C.x, D.x) < Math.min(A.x, B.x) || Math.max(A.y, B.y) < Math.min(C.y, D.y) || Math.max(C.y, D.y) < Math.min(A.y, B.y)) { return false; } // 跨立试验(叉积符号判断) double cross1 = crossProduct(A, B, C); double cross2 = crossProduct(A, B, D); double cross3 = crossProduct(C, D, A); double cross4 = crossProduct(C, D, B); return (cross1 * cross2 <= 0) && (cross3 * cross4 <= 0); } // 判断两个三角形是否相交 public boolean intersects(Triangle t) { // 检查边交叉(9种组合) if (segmentsIntersect(p0, p1, t.p0, t.p1) || segmentsIntersect(p0, p1, t.p1, t.p2) || segmentsIntersect(p0, p1, t.p2, t.p0) || segmentsIntersect(p1, p2, t.p0, t.p1) || segmentsIntersect(p1, p2, t.p1, t.p2) || segmentsIntersect(p1, p2, t.p2, t.p0) || segmentsIntersect(p2, p0, t.p0, t.p1) || segmentsIntersect(p2, p0, t.p1, t.p2) || segmentsIntersect(p2, p0, t.p2, t.p0)) { return true; } // 检查点包含(6种情况) return contains(t.p0) || contains(t.p1) || contains(t.p2) || t.contains(p0) || t.contains(p1) || t.contains(p2); } } ``` #### 3. Main.java(主程序) ```java import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); // 输入第一个三角形 System.out.println("输入第一个三角形的三个顶点坐标 (x y):"); Point[] tri1Points = readTrianglePoints(scanner); // 输入第二个三角形 System.out.println("输入第二个三角形的三个顶点坐标 (x y):"); Point[] tri2Points = readTrianglePoints(scanner); // 创建三角形对象 Triangle tri1 = new Triangle(tri1Points[0], tri1Points[1], tri1Points[2]); Triangle tri2 = new Triangle(tri2Points[0], tri2Points[1], tri2Points[2]); // 检测相交并输出结果 System.out.println("两个三角形是否相交: " + tri1.intersects(tri2)); scanner.close(); } private static Point[] readTrianglePoints(Scanner scanner) { Point[] points = new Point[3]; for (int i = 0; i < 3; i++) { System.out.print("顶点" + (i + 1) + ": "); double x = scanner.nextDouble(); double y = scanner.nextDouble(); points[i] = new Point(x, y); } return points; } } ``` ### 算法说明 1. **边相交检测**: - 通过**跨立试验**判断两线段是否相交,检查叉积符号是否相反或为零[^1]。 - 先进行**快速排斥试验**(矩形重叠检查)提升效率。 2. **点包含检测**: - 若一个三角形的顶点在另一个三角形内,则相交。 - 使用**面积法**计算:若点与三角形三顶点构成的三个子三角形面积之和等于原三角形面积,则点在内部[^4]。 3. **浮点处理**: - 使用容差值 `1e-10` 处理浮点计算误差。 ### 使用示例 ```text 输入第一个三角形的三个顶点坐标 (x y): 顶点1: 0 0 顶点2: 1 0 顶点3: 0 1 输入第二个三角形的三个顶点坐标 (x y): 顶点1: 0.5 0.5 顶点2: 1.5 0.5 顶点3: 0.5 1.5 两个三角形是否相交: true ``` ### 相关问题 1. 如何优化三角形相交检测的性能? **答**:使用空间划分结构(如BVH树)减少检测次数;添加快速排斥盒(AABB)预筛选[^3]。 2. 如何处理三维空间中的三角形相交检测? **答**:增加投影到二维平面的步骤,选择法向量绝对值最大的坐标平面进行降维计算[^3]。 3. 浮点精度误差会导致哪些误判?如何解决? **答**:可能将相邻三角形误判为相交。可采用相对容差(如`ε = 1e-10 * max_side_length`)或精确算术库。 4. 如何扩展程序检测多个三角形的相交关系? **答**:使用嵌套循环遍历所有三角形对;或结合扫描线算法优化大规模检测。 [^1]: 通过叉积判断点与线段相对位置(跨立试验) [^3]: 三维问题通过投影降维处理 [^4]: 面积法判断点是否在三角形
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

迂者-贺利坚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值