日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

腾讯位置服务JavaScript API GL实现文本标记的碰撞避让

發(fā)布時(shí)間:2024/1/1 javascript 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 腾讯位置服务JavaScript API GL实现文本标记的碰撞避让 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

以下內(nèi)容轉(zhuǎn)載自Crape的文章《web頁面上的旋轉(zhuǎn)矩形碰撞》

作者:Crape

鏈接:https://juejin.im/post/5eede991e51d45740950c946

來源:掘金

著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。

前言

本文主要是總結(jié)一下web頁面中的旋轉(zhuǎn)矩形的碰撞檢測,碰撞算法本身并不難,只是需要注意web坐標(biāo)系在計(jì)算中的影響。碰撞檢測應(yīng)該是在游戲等場景中很常見且基礎(chǔ)的功能,本文記錄了在JavaScript API GL遇到了這類碰撞問題的調(diào)研和實(shí)現(xiàn)的過程。

需求場景

用戶在地圖上實(shí)現(xiàn)MultiLabel文本標(biāo)注覆蓋物時(shí),會由于兩個(gè)label坐標(biāo)過近,或者地圖的旋轉(zhuǎn)、縮放產(chǎn)生的變化而相互重疊。目前l(fā)abel的背景色均為透明且暫時(shí)還不支持配置,文字重疊之后識別度下降很多,就計(jì)劃先實(shí)現(xiàn)label之間的避讓功能。檢測到兩個(gè)label碰撞時(shí),根據(jù)優(yōu)先級選擇隱藏其中的一個(gè),保證文字的可讀性。

確定算法

在JSAPI GL中,label并不是在三維空間中的,而是繪制在屏幕上的,只是會根據(jù)用戶視角的移動(dòng)實(shí)時(shí)計(jì)算出label在屏幕坐標(biāo)中所處的位置,然后在每一幀中進(jìn)行繪制。label實(shí)際上就是一行文字,我們可以把它用一個(gè)矩形包圍起來,當(dāng)做整體計(jì)算,因?yàn)槊總€(gè)字之間的相對位置并不會變,這樣一來label的碰撞檢測實(shí)際上可以轉(zhuǎn)化為二維空間內(nèi)的矩形碰撞。

一般的橫平豎直的矩形檢測碰撞很簡單,只要想清楚有哪些情況即可,不在這里贅述。但是用戶可以對label進(jìn)行旋轉(zhuǎn)和偏移操作,普通的檢測方法就不適用了,如果強(qiáng)行把label用一個(gè)大的水平矩形包裹起來再計(jì)算,精度損失會很多,所以調(diào)研了一下旋轉(zhuǎn)矩形的碰撞檢測方法。

比較常見的一種方式是通過分離軸定律(SAT:Separating Axis Theorem)來計(jì)算,分離軸定義:兩個(gè)凸多邊形物體,如果能找到一個(gè)軸,使得兩個(gè)物體在該軸上的投影互不重疊,那么這兩個(gè)物體就沒有發(fā)生碰撞,這條軸可以稱為分離軸。

一般不會遍歷所有角度的軸,而是檢測垂直于多邊形每條邊的軸,因?yàn)樵谶@些軸上我們可以取到極值。對于矩形來說可以進(jìn)一步簡化,因?yàn)橐粋€(gè)矩形的4條軸內(nèi)有2個(gè)是重復(fù)的,所以只需要檢測矩形互相垂直的兩條邊對應(yīng)的軸就可以了。

進(jìn)行判斷的具體方式有兩種:一是把每個(gè)矩形的4個(gè)頂點(diǎn)投影到一個(gè)軸上,算出該矩形最長的連線距離,判斷兩個(gè)矩形的投影是否重疊;二是將兩個(gè)矩形的半徑距離投影到軸上,然后把兩個(gè)矩形中心點(diǎn)的連線投影到通一個(gè)軸上,判斷兩個(gè)矩形的半徑投影之和與中心點(diǎn)連線投影的大小。

本文采用第二種方式計(jì)算,首先搞清楚投影的概念,引入向量來進(jìn)行計(jì)算:

我們可以用單位向量來表示垂直于邊線的軸,這樣一個(gè)向量在軸線上的投影長度可以用該向量與投影軸上的單位向量的點(diǎn)積來表示。如上圖,A點(diǎn)坐標(biāo)為(xa, ya),OB為線段OA在x軸上的投影,x軸的單位向量為(1, 0),OA · x軸單位向量 = (xa, ya) · (1, 0) = xa * 1 + ya * 0 = xa。

// 如果用數(shù)組[x ,y]表示一個(gè)向量,則兩個(gè)向量的點(diǎn)積結(jié)果可以表示為 function dot(vectorA, vectorB) {return Math.abs(vectorA[0] * vectorB[0] + vectorA[1] * vectorB[1]); }

然后就是如何表示矩形兩個(gè)軸的單位向量,假設(shè)矩形以自身的中心點(diǎn)為原點(diǎn),逆時(shí)針旋轉(zhuǎn)θ,其兩條相鄰邊的軸的單位向量如下圖所示:

單位圓的半徑為1,所以單位向量OA為 (cosθ, sinθ),另一條邊的單位向量與OA垂直,為(-sinθ, cosθ),這兩個(gè)單位向量的點(diǎn)積為0。但這里有一個(gè)非常重要的注意點(diǎn):web頁面中的坐標(biāo)系與我們平時(shí)使用的坐標(biāo)系不同,x軸正方向不變,y軸的正方向向下。我在最開始實(shí)現(xiàn)算法的過程中忽略了這個(gè)問題,導(dǎo)致碰撞結(jié)果不對,調(diào)試了半天才發(fā)現(xiàn)原因。在實(shí)際計(jì)算中,我們所使用的坐標(biāo)都是web屏幕坐標(biāo)系下的,軸的正方向與常用的不同,所以兩個(gè)單位向量應(yīng)該分別表示為 (cosθ, -sinθ), (sinθ, cosθ),如下圖所示:

然后就是計(jì)算矩形的半徑投影,首先明確下半徑投影的概念,可以理解為矩形中心點(diǎn)到一個(gè)頂點(diǎn)的向量,在軸上的投影長度。其實(shí)就是,矩形在X軸上最遠(yuǎn)處的交點(diǎn),數(shù)學(xué)上意義就是2條檢測軸的投影之和。

兩個(gè)矩形檢測的過程中,以其中一個(gè)矩形的檢測軸為坐標(biāo)系,投影另外一個(gè)矩形的檢測軸。如上圖所示,藍(lán)色線段為左邊矩形的半徑投影,黃色線段為右邊矩形檢測軸。我們需要把右邊2條檢測軸投影到藍(lán)色線段所在X軸的單位向量(即左邊矩形的檢測軸單位向量),得到投影比例,然后乘以檢測軸長度(即矩形長、寬的一半),可計(jì)算出右邊矩形的半徑投影。紅色線段則是兩個(gè)矩形中心點(diǎn)的連線,同樣需要計(jì)算它在藍(lán)色線段所在X軸的投影長度,如果中心點(diǎn)連線的投影長度大于兩個(gè)矩形的半徑投影之和,那么在這條軸上兩個(gè)矩形沒有碰撞,否則發(fā)生碰撞。

檢測最終是否碰撞,需要對四個(gè)分離軸都檢測一次,在任何一個(gè)軸上沒有碰撞,則兩個(gè)矩形就沒有碰撞。

實(shí)現(xiàn)

實(shí)際實(shí)現(xiàn)的過程中進(jìn)行了簡單的旋轉(zhuǎn)矩形類,可根據(jù)實(shí)際業(yè)務(wù)需求調(diào)整,例如添加縮放、偏移等參數(shù)

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) { // 計(jì)算半徑投影 const projectionAxisX = this.dot(axis, this.axisX);const projectionAxisY = this.dot(axis, this.axisY);return this.halfWidth * projectionAxisX + this.halfHeight * projectionAxisY;}dot(vectorA, vectorB) { // 向量點(diǎn)積return Math.abs(vectorA[0] * vectorB[0] + vectorA[1] * vectorB[1]);}setRotation(angle) { // 計(jì)算兩個(gè)檢測軸的單位向量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 = [ // 兩個(gè)矩形一共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;} }

使用時(shí)每個(gè)矩形實(shí)例化一個(gè)Rect類,然后調(diào)用實(shí)例上的isCollision方法,參數(shù)傳入另一個(gè)矩形的實(shí)例,最后返回一個(gè)boolean類型的碰撞結(jié)果。

總結(jié)

封裝的這個(gè)類比較簡單,沒有涉及到里面參數(shù)改變的問題,有需要的話可以再完善。實(shí)現(xiàn)過程中注意下web坐標(biāo)系的問題就可以了。矩形應(yīng)該是最簡單的一種,其他凸多邊形的檢測會復(fù)雜一些,有興趣的話可以自己嘗試一下。

本文參考以下blog:
https://blog.csdn.net/tom_221x/article/details/38457757
https://aotu.io/notes/2017/02/16/2d-collision-detection/index.html

畫圖工具為 GeoGebra sketch

實(shí)際效果可以在騰訊位置服務(wù)官網(wǎng)的示例中嘗試https://lbs.qq.com/webDemoCenter/glAPI/glMarker/labelCollision

總結(jié)

以上是生活随笔為你收集整理的腾讯位置服务JavaScript API GL实现文本标记的碰撞避让的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。