腾讯位置服务JavaScript API GL实现文本标记的碰撞避让
以下内容转载自Crape的文章《web页面上的旋转矩形碰撞》
作者:Crape
链接:https://juejin.im/post/5eede991e51d45740950c946
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
前言
本文主要是总结一下web页面中的旋转矩形的碰撞检测,碰撞算法本身并不难,只是需要注意web坐标系在计算中的影响。碰撞检测应该是在游戏等场景中很常见且基础的功能,本文记录了在JavaScript API GL遇到了这类碰撞问题的调研和实现的过程。
需求场景
用户在地图上实现MultiLabel文本标注覆盖物时,会由于两个label坐标过近,或者地图的旋转、缩放产生的变化而相互重叠。目前label的背景色均为透明且暂时还不支持配置,文字重叠之后识别度下降很多,就计划先实现label之间的避让功能。检测到两个label碰撞时,根据优先级选择隐藏其中的一个,保证文字的可读性。
确定算法
在JSAPI GL中,label并不是在三维空间中的,而是绘制在屏幕上的,只是会根据用户视角的移动实时计算出label在屏幕坐标中所处的位置,然后在每一帧中进行绘制。label实际上就是一行文字,我们可以把它用一个矩形包围起来,当做整体计算,因为每个字之间的相对位置并不会变,这样一来label的碰撞检测实际上可以转化为二维空间内的矩形碰撞。
一般的横平竖直的矩形检测碰撞很简单,只要想清楚有哪些情况即可,不在这里赘述。但是用户可以对label进行旋转和偏移操作,普通的检测方法就不适用了,如果强行把label用一个大的水平矩形包裹起来再计算,精度损失会很多,所以调研了一下旋转矩形的碰撞检测方法。
比较常见的一种方式是通过分离轴定律(SAT:Separating Axis Theorem)来计算,分离轴定义:两个凸多边形物体,如果能找到一个轴,使得两个物体在该轴上的投影互不重叠,那么这两个物体就没有发生碰撞,这条轴可以称为分离轴。
一般不会遍历所有角度的轴,而是检测垂直于多边形每条边的轴,因为在这些轴上我们可以取到极值。对于矩形来说可以进一步简化,因为一个矩形的4条轴内有2个是重复的,所以只需要检测矩形互相垂直的两条边对应的轴就可以了。
进行判断的具体方式有两种:一是把每个矩形的4个顶点投影到一个轴上,算出该矩形最长的连线距离,判断两个矩形的投影是否重叠;二是将两个矩形的半径距离投影到轴上,然后把两个矩形中心点的连线投影到通一个轴上,判断两个矩形的半径投影之和与中心点连线投影的大小。
本文采用第二种方式计算,首先搞清楚投影的概念,引入向量来进行计算:
我们可以用单位向量来表示垂直于边线的轴,这样一个向量在轴线上的投影长度可以用该向量与投影轴上的单位向量的点积来表示。如上图,A点坐标为(xa, ya),OB为线段OA在x轴上的投影,x轴的单位向量为(1, 0),OA · x轴单位向量 = (xa, ya) · (1, 0) = xa * 1 + ya * 0 = xa。
// 如果用数组[x ,y]表示一个向量,则两个向量的点积结果可以表示为
function dot(vectorA, vectorB) {return Math.abs(vectorA[0] * vectorB[0] + vectorA[1] * vectorB[1]);
}
然后就是如何表示矩形两个轴的单位向量,假设矩形以自身的中心点为原点,逆时针旋转θ,其两条相邻边的轴的单位向量如下图所示:
单位圆的半径为1,所以单位向量OA为 (cosθ, sinθ),另一条边的单位向量与OA垂直,为(-sinθ, cosθ),这两个单位向量的点积为0。但这里有一个非常重要的注意点:web页面中的坐标系与我们平时使用的坐标系不同,x轴正方向不变,y轴的正方向向下。我在最开始实现算法的过程中忽略了这个问题,导致碰撞结果不对,调试了半天才发现原因。在实际计算中,我们所使用的坐标都是web屏幕坐标系下的,轴的正方向与常用的不同,所以两个单位向量应该分别表示为 (cosθ, -sinθ), (sinθ, cosθ),如下图所示:
然后就是计算矩形的半径投影,首先明确下半径投影的概念,可以理解为矩形中心点到一个顶点的向量,在轴上的投影长度。其实就是,矩形在X轴上最远处的交点,数学上意义就是2条检测轴的投影之和。
两个矩形检测的过程中,以其中一个矩形的检测轴为坐标系,投影另外一个矩形的检测轴。如上图所示,蓝色线段为左边矩形的半径投影,黄色线段为右边矩形检测轴。我们需要把右边2条检测轴投影到蓝色线段所在X轴的单位向量(即左边矩形的检测轴单位向量),得到投影比例,然后乘以检测轴长度(即矩形长、宽的一半),可计算出右边矩形的半径投影。红色线段则是两个矩形中心点的连线,同样需要计算它在蓝色线段所在X轴的投影长度,如果中心点连线的投影长度大于两个矩形的半径投影之和,那么在这条轴上两个矩形没有碰撞,否则发生碰撞。
检测最终是否碰撞,需要对四个分离轴都检测一次,在任何一个轴上没有碰撞,则两个矩形就没有碰撞。
实现
实际实现的过程中进行了简单的旋转矩形类,可根据实际业务需求调整,例如添加缩放、偏移等参数
class Rect {constructor(options) {const {center, height, width, angle} = options;this.centerPoint = [center.x, center.y];this.halfHeight = height / 2;this.halfWidth = width / 2;this.setRotation(angle);}getProjectionRadius(axis) { // 计算半径投影 const projectionAxisX = this.dot(axis, this.axisX);const projectionAxisY = this.dot(axis, this.axisY);return this.halfWidth * projectionAxisX + this.halfHeight * projectionAxisY;}dot(vectorA, vectorB) { // 向量点积return Math.abs(vectorA[0] * vectorB[0] + vectorA[1] * vectorB[1]);}setRotation(angle) { // 计算两个检测轴的单位向量const deg = (angle / 180) * Math.PI;this.axisX = [Math.cos(deg), -Math.sin(deg)];this.axisY = [Math.sin(deg), Math.cos(deg)]; return this;}isCollision(check) {const centerDistanceVertor = [this.centerPoint[0] - check.centerPoint[0],this.centerPoint[1] - check.centerPoint[1]];const axes = [ // 两个矩形一共4条检测轴this.axisX,this.axisY,check.axisX,check.axisY];for (let i = 0, len = axes.length; i < len; i++) {if (this.getProjectionRadius(axes[i]) + check.getProjectionRadius(axes[i]) <= this.dot(centerDistanceVertor, axes[i])) {return false; // 任意一条轴没碰上,就是没碰撞}}return true;}
}
使用时每个矩形实例化一个Rect类,然后调用实例上的isCollision
方法,参数传入另一个矩形的实例,最后返回一个boolean
类型的碰撞结果。
总结
封装的这个类比较简单,没有涉及到里面参数改变的问题,有需要的话可以再完善。实现过程中注意下web坐标系的问题就可以了。矩形应该是最简单的一种,其他凸多边形的检测会复杂一些,有兴趣的话可以自己尝试一下。
本文参考以下blog:
https://blog.csdn.net/tom_221x/article/details/38457757
https://aotu.io/notes/2017/02/16/2d-collision-detection/index.html
画图工具为 GeoGebra sketch
实际效果可以在腾讯位置服务官网的示例中尝试https://lbs.qq.com/webDemoCenter/glAPI/glMarker/labelCollision
腾讯位置服务JavaScript API GL实现文本标记的碰撞避让相关推荐
- 腾讯地图JavaScript API GL实现文本标记的碰撞避让
以下内容转载自Crape的文章<web页面上的旋转矩形碰撞> 作者:Crape 链接:https://juejin.im/post/5eede991e51d45740950c946 来源: ...
- 腾讯地图api_腾讯位置服务JavaScript API GL正式版发布 免费向开发者开放
近日,腾讯位置服务推出JavaScript API GL正式版,并免费向开发者开放.JavaScript API GL是新一代基于WebGL实现的高性能三维渲染引擎而封装的一套3D版本地图API,是腾 ...
- 腾讯位置服务JSAPI GL实现文本标记的碰撞避让
以下内容转载自Crape的文章<web页面上的旋转矩形碰撞> 作者:Crape 链接:https://juejin.im/post/5eede991e51d45740950c946 来源: ...
- 腾讯地图JSAPI GL实现文本标记的碰撞避让
以下内容转载自Crape的文章<web页面上的旋转矩形碰撞> 作者:Crape 链接:https://juejin.im/post/5eede991e51d45740950c946 来源: ...
- “技术让生活更便捷”之腾讯位置服务JavaScript API2.0前端定位和位置标注的开发实录
文章目录 前言 一.项目需求 1.项目需求 2.需求分解 二.开发实战 1.引入功能库和附件库 2.构建腾讯地图容器 3.调用前端定位组件 1.定位成功回调函数 获取位置坐标显示地图 定义当前位置ma ...
- 腾讯地图Javascript API GL
1. 介绍 腾讯位置服务在多平台为开发者提供了丰富的地图展现形式,帮助从属于不同领域的开发人员轻松完成构建地图并在其基础上打造专属内容的工作.同时配合海量数据.个性化定制.可视化等能力满足各个行业场景 ...
- 关于腾讯位置服务的地址解析与路径规划两个api的坑!!!debug了一个晚上终于找出来了
小程序的地图定位与导航以及非常常见且在很多小程序上都已在使用, 下面让我讲讲当我在使用腾讯位置服务这款微信小程序JavaScript SDK时,遇到的天坑! 重要!!! 地址解析(地址转坐标) geo ...
- qq位置如何用启动百度地图定位服务器,腾讯位置服务API快速入门
前言 之前项目有个需求,在网页上显示微信发送过来的位置信息,该开始想用百度地图,后来发现腾讯地图相对简单一点 快速入门 申请Key 引入js 把###替换成你申请的key 定义容器 设置好宽高度 设置 ...
- 发布地图服务属性表_Web3D地图来了!腾讯位置服务正式版发布!
公众号关注 "菜鸟要飞" 设为 "星标",和10万程序员一起成长! 原文转载自公众号[开发者技术前线] 腾讯位置服务在半年前推出JavaScript API G ...
最新文章
- linux监控目录变动
- PHP 标准AES加密算法类
- (转载)NSOperation and NSOperationQueue教程(翻译)
- eap aka_使用API​​密钥(aka身份验证令牌)部署到Maven Central
- 信息学奥赛一本通 1118:铺地毯 | 1863:【11NOIP提高组】铺地毯 | OpenJudge NOI 1.9 14 | 洛谷 P1003 [NOIP2011 提高组] 铺地毯
- 《深入理解 Spring Cloud 与微服务构建》第十七章 使用 Spring Cloud OAuth2 保护微服务系统
- python列表去重_python 字典列表/列表套字典 去重重复的字典数据
- CentOs6.5安装使用数据恢复软件extundelete
- html =拼接dom,在js代码拼接dom对象到页面上去的模板总结(必看)
- 计算机显示无法格式化,电脑提示windows无法完成格式化怎么办
- 【网络通信 -- 直播】ZLMediaServer -- 环境搭建与测试
- win10计算机图片,Win10使用系统自带的电子相册整理电脑中照片方法图解
- #foxpro(VFP) 入门(一) 常用命令
- 灯神动态规划(Dynamic Programing)学习笔记 打劫问题 凑整问题 背包问题 例题+原理+源码超详细讲解
- cocos creator 加载微信云端图片
- 贝壳DMP平台建设实践
- 承认多元化,保持宽容
- 100% 搭建你自己的匿名网站(暗网网站)
- VS编译失败,找不到源文件!!!
- 日本社交游戏郭锋:CA子公司Cygames在美国AppStore的Rage of Bahamut进入TopSells钱10位,该游戏在play上也排在前面。其日文版“神击Bahamut”在日本Moba