🎊 平面几何:三点确定唯一圆

平面几何:三点确定唯一圆

大家好,我是前端西瓜哥。

今天我们来学习一个简单平面几何算法。

给出三个点,求出对应的唯一圆形。

示例演示

codesandbox.io/p/sandbox/c...

原理

原理很简单,都是初中的知识点。

相邻两个点形成的两条线段,求它们的中垂线;

求两条中垂线的交点,此点即为圆心;

圆点到 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);

};

结尾

我是前端西瓜哥,关注我,学习更多平面几何知识。

🎈 相关推荐

“莱州海鲜甲天下, 世上无人不爱它。”﹌莱州海鲜就是鲜鲜鲜
《无畏契约》亚海悬城封烟攻略 进攻防守烟位置推荐
🏷️ Bet体育365提款验证

《无畏契约》亚海悬城封烟攻略 进攻防守烟位置推荐

📅 07-03 👀 4933
QQ炫舞梦幻精灵多少钱 怎么样
🏷️ office365桌面应用

QQ炫舞梦幻精灵多少钱 怎么样

📅 08-26 👀 5596
世界杯中国女足1-6不敌英格兰,首次无缘小组出线
🏷️ office365桌面应用

世界杯中国女足1-6不敌英格兰,首次无缘小组出线

📅 10-08 👀 273
顶级翡翠直播平台有哪些
🏷️ beat365登录平台

顶级翡翠直播平台有哪些

📅 11-14 👀 9811
蒲的成语
🏷️ Bet体育365提款验证

蒲的成语

📅 10-07 👀 5381