大家好,我是前端西瓜哥。
今天我们来学习一个简单平面几何算法。
给出三个点,求出对应的唯一圆形。
示例演示

原理
原理很简单,都是初中的知识点。
-
相邻两个点形成的两条线段,求它们的中垂线;
-
求两条中垂线的交点,此点即为圆心;
-
圆点到 3 个点的任意一个点的距离,即为半径。
这样圆就确定好了。
另外需要注意的是,当 点发生重合,或是三点重合是无法构成圆的,此时我们的算法会返回一个 null。
下面看看用到的各种基础几何算法。
先定义下点的类型 Point。
typescript
interface Point {
x: number;
y: number;
}
求两条直线交点
用到了求两条直线交点的算法 ,这个算是很基础的算法,我 之前的文章 有讲过。
实现为:
ini
/** 计算两条线段的交点 */
const getLineIntersection = (line1: Point[], line2: Point[]): Point | null => {
const { x: x1, y: y1 } = line1[0];
const { x: x2, y: y2 } = line1[1];
const { x: x3, y: y3 } = line2[0];
const { x: x4, y: y4 } = line2[1];
const a = y2 - y1;
const b = x1 - x2;
const c = x1 * y2 - x2 * y1;
const d = y4 - y3;
const e = x3 - x4;
const f = x3 * y4 - x4 * y3;
// 计算分母
const denominator = a * e - b * d;
// 判断分母是否为 0(代表平行)
if (Math.abs(denominator) < 0.000000001) {
// 这里有个特殊的重叠但只有一个交点的情况,可以考虑处理一下
returnnull;
}
const px = (c * e - f * b) / denominator;
const py = (a * f - c * d) / denominator;
return { x: px, y: py };
};
求中垂线
先求出线段的中点,此为中垂线的第一个中点。
然后求出线段对应的向量,然后旋转 90 度,方向随意。这里有个公式,就是把这个向量的 x 和 y 对调一下,然后将其中一个取反。
然后把旋转 90 度的向量加到中点上,得到中垂线的另一个端点。
ini
/** 计算中垂线 */
const getMidPerpendicular = (p1: Point, p2: Point) => {
const mid = {
x: (p1.x + p2.x) / 2,
y: (p1.y + p2.y) / 2,
};
const vec = {
x: p2.x - p1.x,
y: p2.y - p1.y,
};
const perpVec = {
x: -vec.y,
y: vec.x,
};
return [mid, { x: mid.x + perpVec.x, y: mid.y + perpVec.y }];
};
完整代码实现
ini
export interface Point {
x: number;
y: number;
}
exportconst getCircleWith3Pt = (p1: Point, p2: Point, p3: Point) => {
const prep1 = getMidPerpendicular(p1, p2);
const prep2 = getMidPerpendicular(p2, p3);
const center = getLineIntersection(prep1, prep2);
if (!center) returnnull;
const radius = distance(center, p1);
return { center, radius };
};
/** 计算中垂线 */
const getMidPerpendicular = (p1: Point, p2: Point) => {
const mid = {
x: (p1.x + p2.x) / 2,
y: (p1.y + p2.y) / 2,
};
const vec = {
x: p2.x - p1.x,
y: p2.y - p1.y,
};
const perpVec = {
x: -vec.y,
y: vec.x,
};
return [mid, { x: mid.x + perpVec.x, y: mid.y + perpVec.y }];
};
/** 计算两条线段的交点 */
const getLineIntersection = (line1: Point[], line2: Point[]): Point | null => {
const { x: x1, y: y1 } = line1[0];
const { x: x2, y: y2 } = line1[1];
const { x: x3, y: y3 } = line2[0];
const { x: x4, y: y4 } = line2[1];
const a = y2 - y1;
const b = x1 - x2;
const c = x1 * y2 - x2 * y1;
const d = y4 - y3;
const e = x3 - x4;
const f = x3 * y4 - x4 * y3;
// 计算分母
const denominator = a * e - b * d;
// 判断分母是否为 0(代表平行)
if (Math.abs(denominator) < 0.000000001) {
// 这里有个特殊的重叠但只有一个交点的情况,可以考虑处理一下
returnnull;
}
const px = (c * e - f * b) / denominator;
const py = (a * f - c * d) / denominator;
return { x: px, y: py };
};
/** 计算两点距离 */
exportconst distance = (p1: Point, p2: Point) => {
const dx = p2.x - p1.x;
const dy = p2.y - p1.y;
returnMath.sqrt(dx * dx + dy * dy);
};
结尾
我是前端西瓜哥,关注我,学习更多平面几何知识。