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

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

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

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

示例演示

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

结尾

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

相关推荐
weixin_382395236 小时前
为小工厂量身打造:本地部署的物料管理系统带缺料计算
前端·制造
__zRainy__6 小时前
解决pnpm v10+不自动构建
前端·pnpm·工程化
猫猫不是喵喵.7 小时前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~8 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华9 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子11 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅11 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni11 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学13 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端