范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

快速检索碰撞图形四叉树碰撞检测

  大家好,我是前端西瓜哥。
  在上篇文章我们讨论了使用  脏矩形渲染 ,通过重渲染局部的图形来提优化 Canvas 的性能,将 GPU 密集转换为 CPU 密集。 看这篇文章
  《Canvas 性能优化:脏矩形渲染》
  CPU 密集在哪?
  在需要遍历 所有的图形,判断它们是否和脏矩形发生相交(碰撞),保存发生碰抓给你的图形,将它们在局部进行重绘。
  有没有办法减少需要遍历的图形,不要遍历全部的图形,而是少量的图形呢?有一个办法是使用 四叉树。四叉树碰撞检测原理我们将区域的分割表述为 "节点",因为是四叉树;
  将画布上的真实图形就叫做 "图形"。
  四叉树本质使用了 空间分割,给图形加 索引,将视口界面分割成多个区域,每个区域记住自己包含了哪些图形。
  然后移动目标图形时,判断它落在哪个区域,取出所在区域的图形,这些图形集合就是和目标图形发生碰撞图形的超集。
  这些区域外的图形就被我们排除了。
  算法实现的要点:
  创建根节点,根节点保存区域的信息 x、y、width 和 height。
  添加图形时,当一个节点内的节点数量大于阀值,就将整个区域均等切割为 4 等份的子节点,将图形从当前区域取出,重新放入到这些子节点内,从而将节点的归属划分为更小的区域。
  (原来的区域转换为索引层,真正保存节点的地方放到了它的子区域上)
  当我们提供一个碰撞矩形,我们从四叉树顶节点往下找,看是否有子节点。如果有,使用矩形碰撞算法找出它所在的子节点有哪些(可能有多个)。对这些子节点重复前面的操作,进行递归,找到所有的图形。
  这些图形就是碰撞矩形可能相交的矩形,但相对所有图形,又不至于太多。四叉树碰撞检测算法
  先看看经典算法实现。
  算法我就不自己实现了,这里展示 quadtree-js 库的代码实现。
  构造函数:function Quadtree(bounds, max_objects, max_levels, level) {   this.max_objects = max_objects || 10; // 节点内最大对象数量   this.max_levels = max_levels || 4; // 树的最大深度   this.level = level || 0;   this.bounds = bounds; // 区域,结构为 {x, y, width, height}   this.objects = []; // 保存图形的地方   this.nodes = []; // 4 个子节点,到达阀值时才创建 }
  这是一个内部私有方法,当节点内图形过多,超过阀值,就将当前节点分裂成 4 个子节点:// 切割:生成 4 个子节点 Quadtree.prototype.split = function () {   var nextLevel = this.level + 1,     subWidth = this.bounds.width / 2,     subHeight = this.bounds.height / 2,     x = this.bounds.x,     y = this.bounds.y;   // 右上   this.nodes[0] = new Quadtree(     {       x: x + subWidth,       y: y,       width: subWidth,       height: subHeight,     },     this.max_objects,     this.max_levels,     nextLevel   );   // 左上   this.nodes[1] = new Quadtree(     {       x: x,       y: y,       width: subWidth,       height: subHeight,     },     this.max_objects,     this.max_levels,     nextLevel   );   // 左下   this.nodes[2] = new Quadtree(     {       x: x,       y: y + subHeight,       width: subWidth,       height: subHeight,     },     this.max_objects,     this.max_levels,     nextLevel   );   // 右下   this.nodes[3] = new Quadtree(     {       x: x + subWidth,       y: y + subHeight,       width: subWidth,       height: subHeight,     },     this.max_objects,     this.max_levels,     nextLevel   ); };
  计算某个图形落在当前节点的哪个子节点,拿到对应节点索引值数组:// 找到某个 box 落在在哪个区域 Quadtree.prototype.getIndex = function (pRect) {   var indexes = [],     verticalMidpoint = this.bounds.x + this.bounds.width / 2,     horizontalMidpoint = this.bounds.y + this.bounds.height / 2;   var startIsNorth = pRect.y < horizontalMidpoint,     startIsWest = pRect.x < verticalMidpoint,     endIsEast = pRect.x + pRect.width > verticalMidpoint,     endIsSouth = pRect.y + pRect.height > horizontalMidpoint;   // top-right quad   if (startIsNorth && endIsEast) {     indexes.push(0);   }   // top-left quad   if (startIsWest && startIsNorth) {     indexes.push(1);   }   // bottom-left quad   if (startIsWest && endIsSouth) {     indexes.push(2);   }   // bottom-right quad   if (endIsEast && endIsSouth) {     indexes.push(3);   }   return indexes; };
  插入一个图形,先看是否存在子节点,有的话说明当前节点变成了索引层,通过矩形碰撞算法找到所在的子节点,对这些子节点做插入操作:Quadtree.prototype.insert = function (pRect) {   var i = 0,     indexes;   // 存在子节点,则插入到子节点   if (this.nodes.length) {      indexes = this.getIndex(pRect); // 找到索引位置     for (i = 0; i < indexes.length; i++) {       this.nodes[indexes[i]].insert(pRect); // 递归 insert     }     return;   }   // 没有子节点,不是索引层,图形放到前节点下   // (有个小 BUG,就是不在范围内的图形也加上去了)   this.objects.push(pRect);   // 如果对象太多,构建子节点   if (     this.objects.length > this.max_objects &&     this.level < this.max_levels   ) {     if (!this.nodes.length) {       this.split();     }     // 将 objects 取出,放入这些子节点中     for (i = 0; i < this.objects.length; i++) {       indexes = this.getIndex(this.objects[i]);       for (var k = 0; k < indexes.length; k++) {         this.nodes[indexes[k]].insert(this.objects[i]);       }     }     this.objects = [];   } };
  返回目标图形所在节点下的所有图形:// 传入一个矩形,取出它所在节点的所有矩形 // 这个方法能返回 Quadtree.prototype.retrieve = function (pRect) {   // 提取目标矩形所在区间下的所有矩形   var indexes = this.getIndex(pRect),     returnObjects = this.objects;   // 可能需要递归。   //if we have subnodes, retrieve their objects   if (this.nodes.length) {     for (var i = 0; i < indexes.length; i++) {       returnObjects = returnObjects.concat(         this.nodes[indexes[i]].retrieve(pRect)       );     }   }   // 移除重复的节点   returnObjects = returnObjects.filter(function (item, index) {     return returnObjects.indexOf(item) >= index;   });   return returnObjects; };
  非常简单,一些可以改善的能力。没有添加映射功能 ,最后返回的图形都是 box 对象信息,我们可以考虑改造为 insert(rect, data)  ,保存额外的信息,比如实际形状。动态收缩 :移除某个图形后更新树结构,并在发现图形数量低于阀值时,取出图形放到父节点上,销毁子节点;修改根节点范围  后,需要重置整棵树,如何高效重置等;四叉树的图形类型 ,常见的是矩形,但还可以是点、直线、曲线等,如果需要可以考虑支持。
  请根据实际需要进行扩展。一些权衡
  处于节点内分割线上的图形,它是归属于多个子节点的,所以最终会同时放到它的多个子节点下,会花费内存。
  极端情况下,一个非常大的图形,会保存在所有的节点下。
  如果想节省内存,可以直接保存到当前节点上,不放到子节点上,可以减少内存使用,只是最后返回的被碰撞图形会多一点。因为图形可能只压在了两个子节点的交界线上,比如 A、 B ,但目标矩形是在其他的子节点 C 上,但因为它们来自同一个父节点,所以拿到了这个不可能在 C 的图形。
  后者会更好一些,但如果一个图形刚好在画布中心,那每次取出的碰撞图形都会有它(这点可以通过松散四叉树解决)。松散四叉树
  经典四叉树有个问题,就是如果图形的物理信息是比较动态的,当总是在边界附近时,就会发生频繁地将图形从一个节点取出并放到另个节点下。
  对此我们可以额外设置一个出口边界。这个出口边界要比入口边界要大,只有当图形离开这个出口边界,才会更新提取图形到新的节点。
  这样,当图形划分到另一个节点上时,就 需要移动较长的距离才能回到原来节点下,轻微地移动不会导致剧烈的更新。
  通常出口边界边长为入口边界的两倍最佳,为什么不知道,经验之谈。
  其他空间分割思想的算法
  简单介绍一些也使用了 空间分割 思想的算法。跳表 :一种有序链表,通过叠加大量的索引层,可以进行链表形式的 "二分查找",达到高效的 O(logn)   时间复杂度,但也带来了内存的消耗。Redis 中的有序集合(Sorted Set)底层使用了跳表,一个原因是可以高效地获取区间内的数据集;B+ 树 :一种平衡二叉树,有点像跳表,但树的层数最多为三层,MySQL 的索引实现使用了 B+ 树,因为层数较少,可以减少 IO 操作;R 树 :R 表示矩形的意思。相比前面两种单维的范围查找,R 树能做高效的高维查找。比如地图中,我们可以通过 R 树将 距离 相近的高维图形合并为一个大节点,当搜索 "2km 内的药店" 时,如果你落到某个大节点上,我们只要遍历一个大节点下的所有节点,而不是要遍历整个市。
  R 树的思路是最接近四叉树的,它其实是另一种 减少图形遍历的方案,可以适用于高效剔除视口范围之外的图形。
  R 树有个 star 数很多的库,叫做 RBush,感兴趣可以看看。结尾
  我是前端西瓜哥,欢迎关注我,学习更多前端知识。

现在不投资新能源,等于二十年前不买房,这句话对吗?中国改革开放40多年,有四次大的致富机会,第一次是改革开放初期的七八十年代,是物资短缺时代,那时候只要敢辞职经商,摆个小摊甚至买个汽水就能挣大钱,产生了像娃哈哈等名企。第二次是九十没有勇士就是loser!杜兰特最大的底牌被看轻,难怪他敏感玻璃心杜兰特的闹剧持续一整个夏天,戏剧性地官宣留队后,他也仍是联盟的流量密码,关于他的讨论还在继续,这其中自然多是嘲讽。杜兰特和巴克利的骂战沸沸扬扬,巴克利称杜兰特离开勇士后的生涯是悲惨天文学家在一颗失败恒星的大气层中发现了沙云詹姆斯韦伯太空望远镜的最新观测结果直接证实了一些外星世界有岩石云。根据一个国际天文学家小组的说法,该望远镜直接探测到了棕矮星大气中的硅酸盐云,这是首次在太阳系外的行星质量伴星中进行零刻SRE5迷你主机能玩游戏吗?我测试了十多款3A大作一说玩游戏,很多人首先想到的就是炫酷的游戏主机,但是很多时候大主机太占空间,而且来回搬运也是件麻烦事。尤其是对于学生党来说,开学了要带电脑去学校,弄个大电脑来回托运都是一笔不小的开iPhone14Pro或支持新款30W充电器!分析师称苹果已完成卫星通信硬件测试,网友终于升级了中国经济周刊经济网讯在iPhone14Pro发布前一周,有传言称这款智能手机将配备新的30W充电器。对此,网友们纷纷表示终于升级了!推特用户DuanRui发文称,由于某充电器品牌开寄往日本邮件中,上海海关查获807盒未申报新型冠状病毒抗原检测试剂新民晚报讯(记者郭剑烽)上海海关今天透露,上海邮局海关于8月24日在寄往日本的出境邮件中,查获一批未申报的新型冠状病毒抗原检测试剂盒,共计807盒,因未能提供对应的特殊物品审批单,WAIC2022丨20余家汽车自动驾驶相关企业在沪开放体验,上海金桥今年开放测试道路已达29。3公里21世纪经济报道记者郑植文张梓桐上海报道智能网联汽车无人行驶停车已照进现实,8月31日上午,21世纪经济报道记者在上海金鼎智慧城市生活体验馆观察到不同品牌的智能网联汽车,或是在测试猴痘病毒核酸检测试剂临床试验的必要性截至2022年8月30日美国东部时间下午500,全球累计猴痘确诊病例49974例,猴痘死亡病例15例,猴痘疫情蔓延全球99个国家和地区。美国确认首例猴痘死亡病例当地时间8月29日,2022款比亚迪唐EV日常实用性测试报告中期改款的唐EV通过设计革新配置调整等一系列动作,整车科技含量进一步提升,在30万元级纯电动SUV市场中保持着较强的竞争力优势。作为比亚迪王朝家族的旗舰SUV车型,2022款比亚迪科克马兹谈退场时走廊内遭遇我们挥拳相向就像街头斗殴一样直播吧9月5日讯今日欧锦赛,土耳其男篮8388不敌格鲁吉亚。比赛中,科克马兹与格鲁吉亚球员发生冲突,之后科克马兹被罚出场。而在被逐出赛场后,科克马兹在球馆更衣室的走廊上遭到格鲁吉亚欧冠直播巴黎圣日耳曼vs尤文,凯尔特人vs皇马,塞维利亚vs曼城速球直播9月6日讯9月7日凌晨有8场欧冠直播焦点比赛观赛入口Loading(点开看)周三2022欧冠杯赛程时间表如下0045E组第1轮萨格勒布迪纳摩vs切尔西0045G组第1轮多特
富士康大逃离背后小人物的感悟一句我想回家让多少人瞬间破防落泪!郑州的疫情还很严峻,如今又上演了几万人徒步大逃亡的场景。看着跋山涉水的身影,鼻子一酸,竟落泪了!疫情三年,感慨自己的渺小,感慨底层人民的艰辛和不易生活中值得坚持的8件事1hr葆有爱心有人说,爱心绽放的地方,生命便能欣欣向荣。公交车上的一次让座,出行中的一次帮扶,处事时的一个小提醒当我们用真诚的爱去守护他人,那种帮助别人的快乐也会滋润我们自己的心田三月流焱今天和未来一样,充满无限可能今天和未来一样,充满无限可能!愿我们保持自律也充满自由,在岁月的长河里乘风破浪,活出自己的精彩。Today,likethefuture,isfullofinfinitepossib荣耀5G手机备战双11天玑90066W闪充10GB,跌至1111元第三季度手机中国市场出货量排行,荣耀排名第三,取得了不错的成绩,仅次于vivo和OPPO,超越苹果和小米,荣耀出货量如此之大,离不开用户的信赖,我身边有不少朋友,以前用荣耀手机,更全球变暖有什么影响?全球变暖的影响预计将是深远的,在许多情况下,是毁灭性的。2021年8月15日,在摩洛哥北部的舍夫chaouen地区,一名妇女看着大火吞噬森林。全球变暖的影响在全球各地都能看到和感受保姆级教程注册Ai智能绘图Midjourney使用Midjourney首先需要创建Discord账号,Midjourney和Discord的关系就像小程序和微信的关系一样。国内网络无法直接访问Discord,这就需要你家网络会自动驾驶时代还需要驾校吗?东方时尚董事长徐雄驾校核心价值是培养未知险情应对能力每经记者可杨每经编辑文多东方时尚董事长徐雄图片来源每经记者李少婷摄你的孙辈将能通过一条新型高速公路在24小时内横穿整个大陆。他们乘坐的是一种全靠按钮操作而不需要人驾驶的新式小汽车美立冬吃羊肉,炖羊肉切忌加料酒,全程只需放5样,味道鲜美不腥膻立冬吃羊肉,炖羊肉切忌加料酒,全程只需放5样,味道鲜美不腥膻还有四天,就是立冬时节了,是不是感觉过得非常快,秋天才刚刚过了多长时间,就已经要立冬了?不过从今天开始,的确出现了一轮大五粮液永不分梨酒,鸭梨占了酒瓶三分之一,怎么放进去的?我国有四大名酒,八大名酒等各种白酒排名,但要说名气最大的,无疑就是茅五剑。茅五剑每次推出的新品或新系列,都能在白酒圈内引起热议。这场面就像每年的苹果发布会。要说最近白酒圈内最大的动不管腌什么酸菜,牢记这4点,酸菜不烂不发霉,而且又酸又脆又香大家好,我是小欣。又到了腌制酸菜的季节,不少家庭都会选择自己在家腌制酸菜,相信你也会遇到一些问题,比如自己腌制的酸菜很容易腐烂发霉了,这是为什么呢?虽然腌制酸菜很容易,但是过程也是你有没有一个朋友,总是会突然玩失踪?如果问你世界上男女之间有没有纯友谊,你估计会抄起旁边的板凳猛K我一顿吧。但是我还是想说,有的,但是前提就是这两个人要很自恋,那就没问题。毕竟自恋的人都看不上对方。最近流行这么一个段