前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >你知道吗?圆弧有3种表达方式

你知道吗?圆弧有3种表达方式

作者头像
前端西瓜哥
发布2024-06-03 15:26:35
740
发布2024-06-03 15:26:35
举报

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

圆弧是一条平面曲线,它是圆上两点间的一段,包含两个端点。

在做图形渲染的时候,我们需要设计好对应的数据结构,目前观测的常见的有三种表达。

这篇文章会对它们一一讲解分析。

圆心、半径 、起始角、结束角、方向

使用到的参数:

  • center: 圆心;
  • radius:半径;
  • starAngle:起始角;
  • endAngle:结束角;
  • sweep:是否为正方向(起点到终点走顺逆时针)。该参数可以去掉,因为可以通过交换 startAngle 和 endAngle 来做等价。

圆弧可以视作一个只绘制了部分线段的圆。

所以我们在原来圆形的圆心、半径参数的基础上,加上极坐标弧度表示的起点和终点,就能表达一段圆弧。

特别注意的是,我们需要提前定义好 图形所在画布的极坐标

  • angleStart:角度为 0 时对应哪个方向,通常为向右方向;
  • angleDir:极坐标的正方向。true 为顺时针,false 为逆时针。

Canvas 2D 使用了这种表达方式:

代码语言:javascript
复制
const center = { x: 150, y: 150 };
const radius = 100;
const startAngle = 0;
const endAngle = Math.PI * 2 * (5 / 7);

ctx.arc(center.x, center.y, radius, startAngle, endAngle);

绘制结果为:

起点、终点、半径、优弧、方向

用到的参数:

  • start:起点位置;
  • end:终点位置;
  • radius:半径;
  • largeArc:是否使用优弧(更长的那条弧);
  • sweep:正方向是顺逆方向,是否朝则正方向移动(起点到终点)。同样,这个 sweep 也是可要可不要,交换 start 和 end 也能表达。

已知起点、终点、半径,我们可以确定圆弧落在这两个圆的路径上。

起点和终点把圆分成两部分,接着我们需要看看是大弧还是小弧,确定走哪一部分。

最后是方向,起点到终点,应该走正方向(假设为顺时针方向)还是反方向。

至此,圆弧就确定好了。

SVG 的 Path 使用了这种表达方式。

代码语言:javascript
复制
const start = { x: 100, y: 100 };
const end = { x: 250, y: 200 };
const radius = 95;
const sweep = true;
const largeArc = true;

const d = `M ${start.x} ${start.y} A ${radius} ${radius} 0 ${
  largeArc ? 1 : 0
} ${sweep ? 1 : 0} ${end.x} ${end.y}`;

顺带一提,Path 还能表达椭圆弧。

渲染效果:

表达 2 这种方式没有圆心参数,但圆心位置还是要经常要用到的,比如渲染的时候还是要算出来,作为矩阵的参数的一部分。

求圆心的代码实现为:

代码语言:javascript
复制
const getArc2Center = (
  start: Point,
  end: Point,
  radius: number,
  sweep: boolean,
  largeArc: boolean,
) => {
  const dist = distance(start, end);
  // 半径太小,无法构成圆。
  // 做特殊处理,radius 替换为 start 到 end 的距离除以 2
  // 此时圆心就是两点的中点
  if (radius * 2 < dist) {
    return {
      x: start.x / 2 + end.x / 2,
      y: start.y / 2 + end.y / 2,
    };
  }
  const cos = Math.min(dist / 2 / radius, 1);
  const dAngle = Math.acos(cos);
  const startToEndAngle = Math.atan((end.y - start.y) / (end.x - start.x));

  let angle = 0;
  if (sweep === largeArc) {
    angle = startToEndAngle - dAngle;
  } else {
    angle = startToEndAngle + dAngle;
  }

  return {
    x: start.x + radius * Math.cos(angle),
    y: start.y + radius * Math.sin(angle),
  };
};

原理是通过三角函数求起点到圆心对应的角,然后基于这个角度、起点位置、半径求出圆心位置。

起点、终点、凸度

使用到的参数:

  • start:起点位置;
  • end:结尾位置;
  • bulge:凸度,线条的凸出程度,对应 圆弧扫过的圆心角的 1/4 的正弦值

bulge 的符号表示方向,正数表示逆时针,负数表示顺时针。

我们知道,tan(45°) 的值为 1,所以当圆心角为 180度,它的 1/4 就是 45度,正弦值就是 1,即 180 度对应的凸度为 1。

然后正弦函数在 (-PI/2, PI/2) 区间是单调递增的,所以我们有:

凸度的绝对值小于 1 时,圆弧为劣弧;绝对值大于 1 时,圆弧为优弧;特别的,凸度为 0 时,表示的是直线

接着我们求圆弧的半径 radius。

根据凸度,我们通过反正弦求出圆心角 delta,然后我们作出下图。

半径 radius 的值为起点到终点距的一半,除以圆心角的一半(dist<start, end>/2) / sin(delta/2)

至此,我们把这种表达方式转换为了第二种表达方式,圆弧就表达出来了。

代码语言:javascript
复制
const start = { x: 100, y: 100 };
const end = { x: 250, y: 200 };
const bulge: number = -1.6;

if (bulge === 0) {
  console.log('表达的是直线');
  return;
}

const sweep = bulge > 0 ? false : true;
const largeArc = Math.abs(bulge) > 1 ? true : false;
const sweepAngle = Math.atan(Math.abs(bulge)) * 4;
const radius = distance(start, end) / 2 / Math.sin(sweepAngle / 2);

渲染结果为:

这种表达方式我目前只在 AutoCAD 的多段线上见过。

优点:

  1. 同时表达圆弧和直线(凸度为 0);
  2. 参数更少,相对其它两种方式只要三个参数。

结尾

如果你想要改改参数调试代码,可以关注公众号,后台回复 “圆弧表达”,获取在线 demo 地址。

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-06-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端西瓜哥 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 圆心、半径 、起始角、结束角、方向
  • 起点、终点、半径、优弧、方向
  • 起点、终点、凸度
  • 结尾
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档