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

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

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

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

示例演示

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

原理

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

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

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

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

结尾

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

相关推荐
阿四5 小时前
【Nextjs】为什么server action中在try/catch内写redirect操作会跳转失败?
前端·next.js
申阳5 小时前
Day 6:04. 基于Nuxt开发博客项目-LOGO生成以及ICON图标引入
前端·后端·程序员
中国lanwp5 小时前
npm中@your-company:registry 和 registry 的区别
前端·npm·node.js
Bacon5 小时前
Electron 应用商店:开箱即用工具集成方案
前端·github
行走的陀螺仪5 小时前
uni-app + Vue3 实现折叠文本(超出省略 + 展开收起)
前端·javascript·css·uni-app·vue3
冴羽5 小时前
JavaScript 异步循环踩坑指南
前端·javascript·node.js
jump6805 小时前
commonjs 和 ES Module
前端
旧曲重听16 小时前
前端需要掌握多少Node.js?
前端·node.js
Mr.Jessy6 小时前
Web APIs 学习第四天:DOM事件进阶
开发语言·前端·javascript·学习·ecmascript
云枫晖6 小时前
前端工程化实战:手把手教你构建项目脚手架
前端·前端工程化