图形模块化3

网格选择,顾名思义,就是把多边形变成网格后选择(此方法只适用于多边形,若是曲线,我们就得将其分段)。
在这里插入图片描述
这样,网格选择就分成了两步:

  1. 将多边形分解为多个三角形。
  2. 判断鼠标点是否在三角形中。

来吧,我们先从最基础的判断鼠标点是否在三角形中开始说。

一、网格选择与三角函数

我们可以用鼠标点和三角形其它顶点的夹角之和来判断。
在这里插入图片描述
点D 在▲ABC 中:
∠ADB+∠BDC+∠CDA=360°

点D 不在▲ABC 中:
∠ADB+∠BDC+∠CDA<360°

接下来我们先说一下一下如何基于三个点计算夹角,如∠mon

先根据三个点画一个角↓
在这里插入图片描述
在这里插入图片描述
poly.draw(ctx);

把∠mon 的顶点o 归零:

m.x-=o.x
m.y-=o.y
n.x-=o.x
n.y-=o.y

根据余弦定理,可以基于点m乘以点n 的点积,om的长度乘以on 的长度,计算∠mon 的余弦值
在这里插入图片描述
根据反余弦方法acos() 求∠mon,也就是下面的theta

const theta=Math.acos(cosTheta);

Math.acos() 可以自动把根据余弦取得的弧度限制在[0,Math.PI]之内。

如果我们使用Math.atan2(y,x),会得到基于x 轴正方向的弧度;

而且y 值为负的时候,atan2(y,x) 的值也是负数,这是不适合夹角求和的。

至于这里面涉及的点积公式,这是个纯数学的知识,大家先知道其用法即可。

感兴趣的同学可以评论区扣1,我后面为它再起一章:点积公式详解

我们知道了一个夹角的求法之后,那就可以去求∠ADB+∠BDC+∠CDA 的夹角和了。

其和若小于360°,那就在三角之外,否则在三角之中。

我把这样的方法封装在了Vector2d 类里:
在这里插入图片描述
注:0.01 是一个用于容差的数。

电脑对浮点数的运算不是绝对精确的,所以我没有直接用Math.PI*2===sum来做判断;

而且是让鼠标点只要靠近了三角形一定范围,就算选择上了。

p1.includedAngleTo(p2,p3) 是求∠p1p2p3 的方法:
在这里插入图片描述
p1.includedAngle(p2) 求的是角的顶点归零后夹角:
在这里插入图片描述
dot() 就是点积方法:
在这里插入图片描述
length() 是求向量长度的方法
在这里插入图片描述
inTriangle() 的使用方法:
在这里插入图片描述
若上面的bool为true,那就说明点在三角形中。

关于判断判断点位是否在三角形中的方法我们就说到这。

接下来,我们聊聊:图形选择-多边形网格化

在说多边形网格化之前,我们先画个复杂点的多边形出来。

准备一组用于逆时针绘图的顶点:
在这里插入图片描述
基于这组顶点绘制一个多边形:
在这里插入图片描述
在这里插入图片描述

二、图形选择的思路解析

接下来,我们就说一下网格化思路:

  1. 先判断points 中的顶点数量,若等于3,那就直接将这三个点存储起来。否则,下一步。
  2. 把多边形里的第一个点作为起点
    在这里插入图片描述
  3. 从起点连接下下个点,构成一个三角形
    在这里插入图片描述
  4. 对上面的三角形做两个判断:

三角形是否包含了其它顶点
三角形是否为凹三角形
在这里插入图片描述
若出现上面任何一种情况,就把起点的下一个点做为起点,执行第1步。

若上面的情况都不存在:

那就把构成这个三角形的顶点存储起来,把下一个点从points 中删除,把下下个点做为起点,执行第1步。
在这里插入图片描述
到这里,网格化的思路就说完了,这是一个递归方法,最终效果如下:
在这里插入图片描述
接下来,咱们就说一下这整个逻辑中涉及的两个知识点:

  1. 三角形是否包含了其它顶点。

这个方法我们上一章说过,遍历其它顶点,逐一判断即可,在此便不再赘述。

  1. 判断三角形的凹凸。

首先我们在定点的时候要遵守一个规则,这里是逆时针定点。

这样我们在用叉乘的方法求三角形的面积的时候,面积为正,那就是凹三角形;面积为负,那就是凸三角形。

求三角形面积的方法:
在这里插入图片描述
这个求面积方法用到的是一个叉乘算法。

整体代码的实现步骤

  1. 绘制多边形
    在这里插入图片描述
  2. 声明用于存储三角形的数组triangles[] 和起点

const triangles=[];
let start=0;

  1. 建立网格
    在这里插入图片描述
    len 是points 顶点长度

i0,i1,i2 是通过取余的方法获取三角形顶点的索引,因为网格化可能会对points 循环遍历。

len===3 是如果points 长度为三,就直接将三角形顶点放到triangles 集合里;

这也是个终止递归的条件,后面points 会不断删除三角形的第2个顶点,从而不断变小。

area>=0||hasOtherPointInTriangle(p0,p1,p2) 便是思路4中的两个情况:

若满足其中之一,就把下一个顶点作为参数,递归执行crtMesh(i1) 方法。

若都不满足,就存储三角形,冲points 中删除三角形的第二个顶点,将三角形的第二个顶点作为起点参数,执行crtMesh(i1) 方法。

好了,整个网格化的实现过程就是这样。

三、多边形怎么选择

要选择多边形的话,那就遍历triangles里的三角形即可,鼠标点只要在一个三角形中,那也就在多边形中了。

最后我把上面的过程进行了封装,建立了一个Mesh 类:

下滑查看全部代码
☟☟☟

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
网格实例化和绘制方法:
在这里插入图片描述
图形选择方法:
在这里插入图片描述
效果:
在这里插入图片描述
写 在 最 后

关于图形选择的三篇前端干货就讲到这啦~
需要复习的小伙伴可以随时去看~

=== 图形模块化2:https://blog.csdn.net/qq_30071415/article/details/106895989

=== 图形模块化1
https://blog.csdn.net/qq_30071415/article/details/106895394


摘至开课吧前端团队,阅读后颇有收获分享至此,希望对大家有所帮助~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章