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

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

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

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

示例演示

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);
};

结尾

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

相关推荐
不吃鱼的羊28 分钟前
ISOLAR软件生成报错处理(七)
java·前端·javascript
TE-茶叶蛋1 小时前
React-props
前端·javascript·react.js
安分小尧1 小时前
[特殊字符] 超强 Web React版 PDF 阅读器!支持分页、缩放、旋转、全屏、懒加载、缩略图!
前端·javascript·react.js
EndingCoder1 小时前
React从基础入门到高级实战:React 高级主题 - React Concurrent 特性:深入探索与实践指南
前端·javascript·react.js·前端框架
EndingCoder1 小时前
React从基础入门到高级实战:React 生态与工具 - React Query:异步状态管理
前端·javascript·react.js·前端框架
TE-茶叶蛋1 小时前
ReactJS 中的 JSX工作原理
前端·react.js·前端框架
水煮白菜王1 小时前
React 编译器
前端·react.js·前端框架
霸王蟹1 小时前
React 项目中封装 Excel 导入导出组件:技术分享与实践
前端·笔记·学习·react.js·typescript·excel·vite
wl_1 小时前
react-color-palette源码解析
前端·react.js·调色板
集成显卡1 小时前
图片压缩工具 | Electron+Vue3+Rsbuild开发桌面应用
前端·javascript·electron·vue