解析几何——求点A到点B和C组成直线的垂线的距离,以及点到线段的距离
数学原理
给定三点 A, B, C,点A到直线BC的距离可以通过以下公式计算:
[d=∣(C−B)×(A−B)∣∣C−B∣][ d = \frac{|(C-B) \times (A-B)|}{|C-B|} ][d=∣C−B∣∣(C−B)×(A−B)∣]
其中:
- × 表示二维向量的叉积
- |v| 表示向量的模长
C++ 实现
#include <cmath>
#include <iostream>struct Point {
double x;
double y;Point(double x = 0, double y = 0) : x(x), y(y) {}
};/**
* @brief 计算点A到直线BC的垂直距离
* @param A 目标点
* @param B 直线上的一点
* @param C 直线上的另一点
* @return 点A到直线BC的垂直距离
*/
double pointToLineDistance(const Point& A, const Point& B, const Point& C) {
// 计算向量BC和BA
double bc_x = C.x - B.x;
double bc_y = C.y - B.y;double ba_x = A.x - B.x;
double ba_y = A.y - B.y;// 计算叉积的绝对值 |BC × BA|
double cross_product = std::abs(bc_x * ba_y - bc_y * ba_x);// 计算向量BC的模长
double bc_length = std::sqrt(bc_x * bc_x + bc_y * bc_y);// 避免除零错误
if (bc_length < 1e-10) {
// B和C重合,退化为两点间距离
return std::sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y));
}// 距离 = 平行四边形面积 / 底边长
return cross_product / bc_length;
}/**
* @brief 计算点A到线段BC的最近距离(可能不是垂直距离)
* @param A 目标点
* @param B 线段端点
* @param C 线段另一端点
* @return 点A到线段BC的最近距离
*/
double pointToSegmentDistance(const Point& A, const Point& B, const Point& C) {
// 计算向量
double ab_x = B.x - A.x;
double ab_y = B.y - A.y;
double ac_x = C.x - A.x;
double ac_y = C.y - A.y;
double bc_x = C.x - B.x;
double bc_y = C.y - B.y;// 计算点积来确定投影点在线段上的位置
double t = ((A.x - B.x) * (C.x - B.x) + (A.y - B.y) * (C.y - B.y));// 如果投影点在B的外侧
if (t <= 0) {
return std::sqrt(ab_x * ab_x + ab_y * ab_y);
}double cb_length_sq = bc_x * bc_x + bc_y * bc_y;// 避免除零错误
if (cb_length_sq < 1e-10) {
return std::sqrt(ab_x * ab_x + ab_y * ab_y);
}t /= cb_length_sq;// 如果投影点在C的外侧
if (t >= 1) {
return std::sqrt(ac_x * ac_x + ac_y * ac_y);// 投影点在线段中间,返回垂直距离
return pointToLineDistance(A, B, C);
}// 测试函数
int main() {
// 测试用例
Point A(1, 1);// 目标点
Point B(0, 0);// 直线上的一点
Point C(3, 0);// 直线上的另一点double distance = pointToLineDistance(A, B, C);std::cout << "点A(" << A.x << "," << A.y << ") ";
std::cout << "到直线BC(B:" << B.x << "," << B.y;
std::cout << " -> C:" << C.x << "," << C.y << ") 的距离为: ";
std::cout << distance << std::endl;// 更多测试用例
Point testCases[][3] = {
{{0, 1}, {0, 0}, {1, 0}},// 期望距离为1
{{2, 2}, {0, 0}, {1, 1}},// 共线情况,期望距离为0
{{1, 0}, {0, 0}, {0, 1}}// 期望距离为√2/2 ≈ 0.707
};for (int i = 0; i < 3; ++i) {
double dist = pointToLineDistance(testCases[i][0],
testCases[i][1],
testCases[i][2]);
std::cout << "测试用例 " << i+1 << ": 距离 = " << dist << std::endl;
}return 0;
}
更简洁的实现版本
如果你想要一个更加紧凑的函数:
#include <cmath>struct Point {
double x, y;
};double pointToLineDistance(const Point& A, const Point& B, const Point& C) {
// 向量BC和BA的叉积的绝对值除以BC的模长
return std::abs((C.x - B.x) * (A.y - B.y) - (C.y - B.y) * (A.x - B.x)) /
std::sqrt((C.x - B.x) * (C.x - B.x) + (C.y - B.y) * (C.y - B.y));
}
使用标准库的增强版本
#include <cmath>
#include <algorithm>struct Point {
double x, y;
};double pointToLineDistance(const Point& A, const Point& B, const Point& C) {
double numerator = std::abs((C.x - B.x) * (A.y - B.y) - (C.y - B.y) * (A.x - B.x));
double denominator = std::sqrt(std::pow(C.x - B.x, 2) + std::pow(C.y - B.y, 2));// 处理B和C重合的特殊情况
if (denominator < 1e-10) {
return std::hypot(A.x - B.x, A.y - B.y);return numerator / denominator;
}
关键要点
-
数学基础:使用向量叉积计算平行四边形的面积,然后除以底边长度得到高度
-
边界情况处理:
- B和C重合时退化为两点间距离
- 浮点数精度考虑
-
时间复杂度:O(1) 常数时间
-
适用场景:
- 计算机图形学
- 路径规划
- 碰撞检测
- 地理信息系统
