空间点、线和面:
实现三维解析几何中的点、直线和平面类
(1) 能够实现直线的不同创建方式(例如,两个点确定一条直 线,两个相交的平面确定一条直线,空间曲线的点斜式)和平面的 不同创建方式(例如,三个不共线的点确定一个平面,一个点和一 个法向量确定一个平面); (2) 能够计算相应的距离:两点之间的距离,点到直线的距离, 点到平面的距离; (3) 能够计算空间直线的单位方向向量(长度为1),空间平面的 单位法向量(长度为1); (4) 能够判断点和线的关系,线和线的关系,点和平面的关系, 线和平面的关系,平面和平面的关系。 (5)注意:要考虑计算机中实数计算的精度误差(不同精度的数 值相等判断不能使用==)。
提示: 1.点、直线和平面分别用类来封装,每个类的数据为该表示该类 需要的参数,例如,对于空间平面的一般方程为ax+by+cz+d = 0,那么私有数据成员为a, b, c和d; 2.空间直线和平面的不同创建方式定义为对应类的成员函数; 3.空间点和点、点和直线、点和平面、直线和直线、直线和平 面、平面和平面位置关系的判断定义为一般的函数; 4. 要在main函数中对上述功能进行调用和验证
分别定义Point类,Plane类,Line类MyVector类(向量类),以及tools(函数类)
point.h
#pragma once
#include<iostream>
using namespace std;
class Line;
class plane;
class Point {
private:
double x;
double y;
double z;
public:
//friend double pointToPlaneDis(Point pt, Plane p);
//friend double pointToLineDis(Point p, Line l);
friend class Plane;
friend void lineAndPlane(Line l, Plane p);
friend void lineAndLine(Line l1, Line l2);
friend void pointAndPlane(Point pt, Plane p);
friend void pointAndLine(Point p, Line l);
friend double pointDistance(Point p1, Point p2);
double getX() { return x; }
double getY() { return y; }
double getZ() { return z; }
void setX(double t) { x = t;}
void setY(double t) { y = t;}
void setZ(double t) { z = t;}
Point() = default;
Point(double x, double y, double z)
: x(x), y(y), z(z)
{
}
bool operator==(const Point& other) const
{
return x == other.x && y == other.y && z == other.z;
}
};
point.cpp
#include"point.h"
line.h
#pragma once
#include "Plane.h"
#include "myVector.h"
using namespace std;
class Point;
class Line
{
private:
//(x-x₀)/a = (y-y₀)/b = (z-z₀)/c
double a;
double b;
double c;
double x0;
double y0;
double z0;
//Point p1, p2;
public:
friend void lineAndLine(Line l1,Line l2);
friend void pointAndLine(Point p, Line l);
friend double pointToLineDis(Point p, Line l);
friend void lineAndPlane(Line l, Plane p);
friend MyVector lineUnitDirVector(Line l);
//两个点确定一条直线
Line TwoPointToLine(Point p1, Point p2);
//两个相交的平面确定一条直线
Line TwoPlaneToLine(Plane p1, Plane p2);
double getA() {
return a;
}
void setA(double a) {
this->a = a;
}
double getB() {
return b;
}
void setB(double b) {
this->b = b;
}
double getC() {
return c;
}
void setC(double c) {
this->c = c;
}
double getX0() {
return x0;
}
void setX0(double x0) {
this->x0 = x0;
}
double getY0() {
return y0;
}
void setY0(double y0) {
this->y0 = y0;
}
double getZ0() {
return z0;
}
void setZ0(double z0) {
this->z0 = z0;
}
Line() = default;
Line(double a, double b, double c, double x0, double y0, double z0)
: a(a), b(b), c(c), x0(x0), y0(y0), z0(z0){}
};
ostream& operator<<(ostream& out,Line& l);
line.cpp
#include "line.h"
Line Line::TwoPointToLine(Point p1, Point p2){
return Line(p1.getX() - p2.getX(), p1.getY() - p2.getY(), p1.getZ() - p2.getZ(),p1.getX(),p1.getY(),p1.getZ());
}
Line Line::TwoPlaneToLine(Plane p1, Plane p2){
MyVector v(p1.b*p2.c-p2.b*p1.c, p1.c * p2.a - p2.a * p1.c, p1.a * p2.b - p2.a * p1.b);
double x = (p2.b * p1.d - p1.b * p2.d) / (p2.a * p1.b - p1.a * p2.b);
double y = (p2.d * p1.a - p1.d * p2.a) / (p2.a * p1.b - p1.a * p2.b);
double z = 0.0;
return Line(v.getX(), v.getY(), v.getZ(), x, y, z);
}
ostream& operator<<(ostream& out, Line& l)
{
//(x-x₀)/a = (y-y₀)/b = (z-z₀)/c
Line t(l);
l.setY0(l.getY0()<0?-1.0 * l.getY0():l.getY0());
l.setX0(l.getX0() < 0 ? -1.0 * l.getX0() : l.getX0());
out << (t.getY0() > 0 ? "(x-" : "(x+") << l.getX0() << ")/" << l.getA()
<< " = " << (t.getY0()>0?"(y-":"(y+") << l.getY0() << ")/" << l.getB()
<< " = " << "(z-" << l.getZ0() << ")/" << l.getC() << endl;
return out;
}
plane.h
#pragma once
#include "point.h"
#include <cmath>
class MyVector;
class Plane{
private:
double a;
double b;
double c;
double d;
public:
friend class Line;
friend double pointToPlaneDis(Point pt, Plane p);
friend void pointAndPlane(Point pt, Plane p);
friend void lineAndPlane(Line l, Plane p);
friend MyVector planeUnitfVector(Plane p);
friend void planeAndPlane(Plane p1, Plane p2);
friend ostream& operator<<(ostream& out, Plane p);
//三个不共线的点确定一个平面
Plane threePointToPlane(Point p1,Point p2,Point p3);
//一个点和一个法向量确定一个平面
Plane pointAndVectorToPlane(Point p, MyVector v);
double getA() {
return a;
}
void setA(double a) {
this->a = a;
}
double getB() {
return b;
}
void setB(double b) {
this->b = b;
}
double getC() {
return c;
}
void setC(double c) {
this->c = c;
}
double getD() {
return d;
}
void setD(double d) {
this->d = d;
}
Plane() = default;
Plane(double a, double b, double c, double d)
: a(a), b(b), c(c), d(d)
{
}
};
ostream& operator<<(ostream& out, Plane p);
plane.cpp
#include "plane.h"
#include "myVector.h"
//已知3点坐标,求平面ax+by+cz+d=0;
Plane Plane::threePointToPlane(Point p1, Point p2, Point p3){
double a, b, c, d;
a = (p2.y - p1.y) * (p3.z - p1.z) - (p2.z - p1.z) * (p3.y - p1.y);
b = (p2.z - p1.z) * (p3.x - p1.x) - (p2.x - p1.x) * (p3.z - p1.z);
c = (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x);
d = 0 - (a * p1.x + b * p1.y + c * p1.z);
return Plane(a, b, c, d);
}
Plane Plane::pointAndVectorToPlane(Point p, MyVector v)
{
double d = -(p.x * v.x + p.y * v.y + p.z * v.z);
return Plane(v.x, v.y, v.z, d);
}
ostream& operator<<(ostream& out, Plane p) {
out << p.a << "x+" << p.b << "y+" << p.c << "z+" << p.d << " = 0" << endl;
return out;
}
// 已知三点坐标,求法向量
/*Vec3 get_Normal(Point p1, Point p2, Point p3) {
a = (p2.y - p1.y) * (p3.z - p1.z) - (p2.z - p1.z) * (p3.y - p1.y);
b = (p2.z - p1.z) * (p3.x - p1.x) - (p2.x - p1.x) * (p3.z - p1.z);
c = (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x);
return Vec3(a, b, c);
}*/
myVector.h
#pragma once
class MyVector{
public:
double x;
double y;
double z;
public:
double getX() { return x; }
double getY() { return y; }
double getZ() { return z; }
void setX(double t) { x = t; }
void setY(double t) { y = t; }
void setZ(double t) { z = t; }
MyVector() = default;
MyVector(double x, double y, double z)
: x(x), y(y), z(z)
{
}
};
myVector.cpp
#include "myVector.h"
tool.h
#pragma once
#include "line.h"
#include "myVector.h"
#include "plane.h"
#include "point.h"
using namespace std;
//两点之间的距离
double pointDistance(Point p1, Point p2) {
return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)
+ (p1.z - p2.z) * (p1.z - p2.z));
}
//点到平面距离
double pointToPlaneDis(Point pt, Plane p) {
return abs(p.a * pt.getX() + p.b * pt.getY() + p.c * pt.getZ() + p.d) / sqrt(p.a * p.a + p.b * p.b + p.c * p.c);
}
//点到直线的距离
double pointToLineDis(Point p, Line l) {
MyVector mv(l.x0 - p.getX(), l.y0 - p.getY(), l.z0 - p.getZ());
MyVector f(mv.y * l.c - l.b * mv.z, l.a * mv.z - mv.x * l.c, mv.x * l.b - l.a * mv.y);
return sqrt(mv.x * mv.x + mv.y * mv.y + mv.z * mv.z) / sqrt(f.x * f.x + f.y * f.y + f.z * f.z);
}
//计算空间直线的单位方向向量
MyVector lineUnitDirVector(Line l) {
//(1,1,1)
double su = sqrt(l.a * l.a + l.b * l.b + l.c * l.c);
return MyVector(l.a / su, l.b / su, l.c / su);
}
//计算平面的单位法向量
MyVector planeUnitfVector(Plane p) {
double su = sqrt(p.a * p.a + p.b * p.b + p.c * p.c);
return MyVector(p.a / su, p.b / su, p.c / su);
}
//点和点的关系
void pointAndPoint(Point p1, Point p2) {
if (p1 == p2) {
cout << "同一点" << endl;
}
else cout << "不为同一点" << endl;
}
//点和直线的关系
void pointAndLine(Point p, Line l) {
if ((p.x - l.x0) / l.a - (p.y - l.y0) / l.b < 0.001 && (p.z - l.z0) / l.c - (p.y - l.y0) / l.b < 0.001) {
cout << "点在直线上" << endl;
}
else cout << "点在直线外" << endl;
}
//点和平面的关系
void pointAndPlane(Point pt, Plane p) {
//ax+by+cz+d = 0
if (p.a * pt.x + p.b * pt.y + p.c * pt.z + p.d - 0 < 0.001) {
cout << "点在平面上" << endl;
}
else cout << "点在平面外" << endl;
}
//计算向量×积
MyVector crossProduct(const MyVector& v1, const MyVector& v2) {
MyVector result;
result.x = v1.y * v2.z - v1.z * v2.y;
result.y = v1.z * v2.x - v1.x * v2.z;
result.z = v1.x * v2.y - v1.y * v2.x;
return result;
}
//直线和直线的关系
void lineAndLine(Line l1, Line l2) {
Point p1(l1.x0, l1.y0, l1.z0);
Point p2(l2.x0, l2.y0, l2.z0);
MyVector v1(l1.a, l1.b, l1.c), v2(l2.a, l2.b, l2.c);
MyVector diff = { p2.x - p1.x, p2.y - p1.y, p2.z - p1.z };
MyVector cross = crossProduct(v1,v2);
if (cross.x == 0 && cross.y == 0 && cross.z == 0) {
// 方向向量平行,再判断是否共线
if ((diff.x * v2.y - diff.y * v2.x) == 0 && (diff.x * v2.z - diff.z * v2.x) == 0 && (diff.y * v2.z - diff.z * v2.y) == 0) {
cout << "两直线平行或重合" << endl;
}
else {
cout << "两直线平行" << endl;
}
}
else {
// 计算联立方程判断是否相交
double t1 = 0.0, t2;
if (v1.x != 0) {
t2 = (p1.x + t1 * v1.x - p2.x) / v2.x;
}
else if (v1.y != 0) {
t2 = (p1.y + t1 * v1.y - p2.y) / v2.y;
}
else if (v1.z != 0) {
t2 = (p1.z + t1 * v1.z - p2.z) / v2.z;
}
// 代入直线方程求解 t1
if ((p1.x + t1 * v1.x == p2.x + t2 * v2.x) && (p1.y + t1 * v1.y == p2.y + t2 * v2.y) && (p1.z + t1 * v1.z == p2.z + t2 * v2.z)) {
cout << "两直线相交" << endl;
}
else {
cout << "两直线异面" << endl;
}
}
}
// 计算两个向量的点积
double dotProduct(const MyVector& v1, const MyVector& v2) {
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
}
//得到平面上一点
Point getPointOnPlane(Plane& p) {
//ax+by+cz+d=0;
//另x = 0, y = 0;
double x = 0.0, y = 0.0;
double z = -1.0 * p.getD() / p.getC();
return Point(0, 0, z);
}
//直线和平面的关系
void lineAndPlane(Line l, Plane p) {
//ax+by+cz+d=0;
MyVector f(p.a, p.b, p.c);
MyVector dir(l.a, l.b, l.c);
double dot = dotProduct(f, dir);
Point pointOnLine(l.x0, l.y0, l.z0);
Point pointOnPlane = getPointOnPlane(p);
double D = -(p.a * pointOnPlane.x + p.b * pointOnPlane.y + p.c * pointOnPlane.z);
double result = p.a * pointOnLine.x + p.b * pointOnLine.y + p.c * pointOnLine.z + D;
/*cerr << p.a << " " << p.b << " " << p.c << endl;
cerr << l.a << " " << l.b << " " << l.c << endl;
cerr << dot << "***" << result << endl;*/
if (dot == 0 && result == 0) {
cout << "直线在平面内" << endl;
}
else if (dot == 0 && result != 0) {
cout << "直线与平面平行" << endl;
}
else {
cout << "直线与平面相交" << endl;
}
}
//平面和平面的关系
void planeAndPlane(Plane p1, Plane p2) {
if (p1.a == p2.a && p1.b == p2.b && p1.c == p2.c && p1.b == p2.b && p1.d == p2.d) {
cout << "重合" << endl;
}
else if (p1.a / p2.a - p1.b / p2.b < 0.001 && p1.b / p2.b - p1.c / p2.c < 0.001) {
cout << "平行" << endl;
}
else {
cout << "相交" << endl;
}
}
测试代码main类
#include "tool.h"
int main() {
Point p1(1, -1, -1);
Point p2(0, 0, 5);
Line l1 = (new Line())->TwoPointToLine(p1,p2); //两点确定一条直线
cout <<"l1的方程为:" << l1; //
Plane pl1(1, 1, 1, -1), pl2(1,-1,1,1);
Line l2 = (new Line())->TwoPlaneToLine(pl1, pl2); //两个相交的平面确定一条直线
cout << "l2的方程为:" << l2;
Line l3(2, -2, -12, 0, 0, 0); //空间曲线的点斜式
cout << "l3的方程为:" << l3;
cout << endl;
Point p3(1, 1, 1);
Plane pl3 = (new Plane())->threePointToPlane(p1, p2, p3); //三个不共线的点确定一个平面
cout << "pl3的方程为:" << pl3;
MyVector v(1, 1, 1);
Plane pl4 = (new Plane())->pointAndVectorToPlane(p1, v); //一个点和一个法向量确定一个平面
cout << "pl4的方程为:" << pl4;
cout << endl;
//两点之间的距离,点到直线的距离,点到平面的距离
cout <<"点p1到面pl1的距离:"<< pointToPlaneDis(p2, pl1) << endl;
cout << "点p1到直线l2的距离为:" << pointToLineDis(p1, l2) << endl;
cout << "点p1和点p2的距离:" << pointDistance(p2, p1) << endl;
cout << endl;
//空间直线的单位方向向量
cout << "线l1的单位方向向量为:";
MyVector v1 = lineUnitDirVector(l1);
cout << "(" << v1.x << "," << v1.y << "," << v1.z << ")" << endl;
//空间平面的单位法向量
cout << "面pl1的单位法向量为:";
MyVector v2 = planeUnitfVector(pl1);
cout << "(" << v2.x << "," << v2.y << "," << v2.z << ")" << endl;
cout << endl;
//空间点和点、点和直线、点和平面、直线和直线、直线和平面、平面和平面位置关系的判断
Point p4(1, -1, -1);
cout << "点p1和点p2的关系是:";
pointAndPoint(p1, p2);
cout << "点p1和点p4的关系是:";
pointAndPoint(p1, p4);
cout << "点p1和线l1的关系是:";
pointAndLine(p1, l1);
cout << "点p1和线l2的关系是:";
pointAndLine(p1, l2);
cout << "点p3和面pl3的关系:";
pointAndPlane(p3, pl3);
cout << "点p2和面pl1的关系:";
pointAndPlane(p2, pl1);
cout << "线l1和线l2的关系:";
lineAndLine(l1, l2);
cout << "线l1和线l3的关系:";
lineAndLine(l1, l3);
cout << "线l2和线l2的关系:";
lineAndLine(l2, l3);
//cerr << "*************************" << endl;
//pl4:1x+1y+1z+1 = 0
Line l4(2,-1,-1,0,0,0);
cout << "线l2和平面pl2之间的关系:";
lineAndPlane(l2, pl2); //pl1:x + y + z + 1 = 0(1,1,1)(2,0,1) l2: (x+3)/2 = z
cout << "线l1和平面pl2之间的关系:";
lineAndPlane(l1, pl2);
cout << "线l4和平面pl4之间的关系:";
lineAndPlane(l4, pl4);
//cerr << "*************************" << endl;
Plane pl5(2, 2, 2, 5);
Plane pl6(2, 2, 2, 5);
cout << "面pl1和面pl2的关系:";
planeAndPlane(pl1, pl2);
cout << "面pl1和面pl5的关系:";
planeAndPlane(pl1, pl5);
cout << "面pl5和面pl6的关系:";
planeAndPlane(pl6, pl5);
return 0;
}
仅供参考,代码在测试中有些小问题,祝大家学习进步
总结
本文的难点
1.友元函数:在类的内部使用 “friend” 关键字声明一个非成员函数为友元函数,这个函数可以访问该类的私有成员和保护成员。
2.友元类:一个类可以将另一个类声明为友元类,友元类的成员函数可以访问该类的私有成员和保护成员。
3.封装:在面向对象编程中,封装是指将数据和操作数据的方法绑定在一起,形成一个类,并通过访问控制机制来限制对类内部数据的直接访问,只允许通过特定的方法来访问和修改这些数据。
4.引用:引用是一个对象的别名,它为一个已存在的对象起了另外一个名字。引用并非一个独立的对象,它必须在创建时被初始化,并且一旦初始化后就不能再指向其他对象。