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

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

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

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

示例演示

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

结尾

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

相关推荐
子兮曰13 小时前
OpenClaw入门:从零开始搭建你的私有化AI助手
前端·架构·github
吴仰晖13 小时前
使用github copliot chat的源码学习之Chromium Compositor
前端
1024小神13 小时前
github发布pages的几种状态记录
前端
不像程序员的程序媛15 小时前
Nginx日志切分
服务器·前端·nginx
北原_春希15 小时前
如何在Vue3项目中引入并使用Echarts图表
前端·javascript·echarts
尽意啊15 小时前
echarts树图动态添加子节点
前端·javascript·echarts
吃面必吃蒜15 小时前
echarts 极坐标柱状图 如何定义柱子颜色
前端·javascript·echarts
O_oStayPositive15 小时前
Vue3使用ECharts
前端·javascript·echarts
竹秋…15 小时前
echarts自定义tooltip中的内容
前端·javascript·echarts
宝贝露.15 小时前
Axure引入Echarts图无法正常显示问题
前端·javascript·echarts