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

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

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

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

示例演示

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

结尾

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

相关推荐
江城开朗的豌豆7 分钟前
小程序登录不迷路:一篇文章搞定用户身份验证
前端·javascript·微信小程序
aesthetician11 分钟前
React 19.2.0: 新特性与优化深度解析
前端·javascript·react.js
FIN666826 分钟前
射频技术领域的领航者,昂瑞微IPO即将上会审议
前端·人工智能·前端框架·信息与通信
U.2 SSD36 分钟前
ECharts漏斗图示例
前端·javascript·echarts
江城开朗的豌豆36 分钟前
我的小程序登录优化记:从短信验证到“一键获取”手机号
前端·javascript·微信小程序
excel39 分钟前
Vue Mixin 全解析:概念、使用与源码
前端·javascript·vue.js
IT_陈寒1 小时前
Java性能优化:这5个Spring Boot隐藏技巧让你的应用提速40%
前端·人工智能·后端
勇往直前plus1 小时前
CentOS 7 环境下 RabbitMQ 的部署与 Web 管理界面基本使用指南
前端·docker·centos·rabbitmq
北海-cherish7 小时前
vue中的 watchEffect、watchAsyncEffect、watchPostEffect的区别
前端·javascript·vue.js
2501_915909068 小时前
HTML5 与 HTTPS,页面能力、必要性、常见问题与实战排查
前端·ios·小程序·https·uni-app·iphone·html5