当前位置: 首页 > Web前端 > HTML5

分享HT实战技巧:实现指南针和3D魔方导航

时间:2023-04-06 00:08:42 HTML5

前言一个3D场景往往需要一个导航标记来确定场景的位置。一般有两种表现形式:罗盘、小方块(方位魔方)。参考百度百科的maya界面,可以看到右上角有一个小方框标识方向,就是这个意思:Hightopo的HTforWeb产品可以轻松构建轻量级的3D可视化场景。在web端,我们可以使用HT2D引擎和3D渲染引擎来实现这个功能,构建一个简单的类似Maya的操作界面。预览地址:https://www.hightopo.com/demo/compass-and-directionbox/界面介绍及效果预览在这个界面中,我们使用了一个2D场景和两个3D场景。具体效果如下:先实现功能先说一下页面布局:指南针是通过在ht.graph.GraphView中为一个图形元素设置一个预绘制的图标来实现的,只需将其放在绘图的左上角即可(也就是下图中的位置1)。OrientationRubik'sCube的实现是在一个小场景(ht.graph3d.Graph3dView)中放置一个Rubik'sobj模型,然后将这个小场景放置在绘图的右上角(即下图中位置2)。主要的3D场景(ht.graph3d.Graph3dView)作为背景放置在整个2D页面下方(下图中的位置3)。代码示例:constg3d=newht.graph3d.Graph3dView();g3d.setOriginAxisVisible(true);g3d.setGridVisible(true);g3d.addToDOM();constg2d=newht.graph.GraphView();g2d.deserialize('displays/test.json',json=>{g2d.addToDOM(g3d.getView());});位置关系:罗盘同步首先约定方位,我们以Z轴负半轴方向为北,Z轴正向为南,X轴正向为东,X轴负向是西方。由于指南针的作用是在鸟瞰图中指示方位,与Y轴无关,我们可以将整个计算过程放在一个二维空间中。代码示例:consteye=this.g3d.getEye();constcenter=this.g3d.getCenter();constv=newht.Math.Vector2(eye[0],eye[2]);constv2=newht.Math.Vector2(center[0],center[2]);constangle=v.sub(v2).angle()-Math.PI/2;compass.setRotation(-angle);罗盘.a('角度',角度);compass.a('角度2',角度);在这段代码中,我们使用eye(相机)和center(观察点)构造两个二维向量(ht.Math.Vector2),Y轴上的分量被丢弃。用向量减法得到中心到眼睛的向量,存入变量v中。用angle()方法得到当前向量与x的正半轴(即东方向)。为什么?减去Math.PI/2,因为我们计算的是与x轴的夹角,罗盘的正方向(北)对应z轴的负半轴。获取旋转角度后,我们可以通过setRotation()方法设置罗盘图元的旋转角度。为什么取负值(-角)?因为当视线逆时针转动时,坐标轴和罗盘相对于人眼的移动方向相反,即顺时针转动。利用HT2D引擎提供的数据绑定功能,通过为罗盘节点设置一个属性值,可以实时动态改变轮盘图标和角度图标的旋转角度。每次视线变化时,都需要进行上述计算和设置。我们可以为3D场景组件添加一个属性监听器:graph3dView.addPropertyChangeListener(e=>{if(e.property==='eye'||e.property==='center'){changeCompass();//...}});图例参考:OrientationRubik'sCubeSynchronization首先约定好方位,X的正半轴在右,负半轴在左;Y为正半轴为上,负半轴为下;Z正半轴在前,负半轴在后。魔方不同于指南针,它是用来表示三维空间中视线的方位。同时它还是一个交互式的方位摇杆,可以方便快捷的改变当前视角为顶视图、侧视图等。视线变化触发魔方变换代码示例:graph3dView.addPropertyChangeListener(e=>{if(e.property==='eye'){constnewValue=e.newValue;constvEye=newht.Math.Vector3(newValue[0],newValue[1],newValue[2]).normalize();graph3dView2.setEye([300*vEye.x,300*vEye.y,300*vEye.z]);}});上面代码中我们通过监听主3D场景(graph3dView)中眼睛属性的变化,动态改变小场景(graph3dView2)中眼睛的位置,达到联动效果。其中e.newValue会获取场景视角变化后的值。我们用这个值构造一个三维向量(ht.Math.Vector3),调用normalize()方法进行归一化,这样就可以得到任何角度和位置的距离都保持不变。之所以将得到的分量乘以300,是因为观察小方块的距离刚刚好,当然也可以根据需要改成其他值。效果示例:点击魔方改变场景视角要实现在主场景点击魔方改变视线,需要一个非常关键的信息,即场景的哪一边鼠标点击小魔方。这里我们需要用到一个找到交点的方法:graph3dView.intersectObject(event,data),这个方法会返回一个对象,用来描述点击的位置信息,其中world属性用来表示点击位置的世界坐标。代码示例:graph3dView2.addInteractorListener(event=>{if(event.kind==='clickData'){constobj=graph3dView2.intersectObject(event.event,event.data);if(obj){constworld=obj.世界;//...}}});有了描述点击位置的world属性,我们就可以很容易的判断出点击的是哪个面,因为我们的小方块是放在原点的,是一个正六面体,这两个关键信息决定了无论点击哪个面,被点击面对应的轴的分量的值一定要大于它在其他两个轴上的分量,所以我们可以简单的判断三个分量中哪个值大就可以判断视线更靠近哪个轴,然后通过判断分量的符号来判断是在正半轴上还是在负半轴上。判断点击的是哪一张脸后,只需要在两个3D场景中设置各自视点(眼睛)的位置即可。代码示例:constworld=obj.world;constx=world.x;consty=世界.y;constz=world.z;如果(Math.abs(x)-Math.abs(y)>0&&Math.abs(x)-Math.abs(z)>0){if(x>0){graph3dView2.setEye([300,0,0]);graph3dView.setEye([this._distance,0,0]);graph3dView2.setCenter([0,0,0]);this._g3d.setCenter([0,0,0]);}else{graph3dView2.setEye([-300,0,0]);graph3dView.setEye([-this._distance,0,0]);graph3dView2.setCenter([0,0,0]);graph3dView.setCenter([0,0,0]);}}elseif(Math.abs(y)-Math.abs(x)>0&&Math.abs(y)-Math.abs(z)>0){//...}其中,this._distance用于描述主场景中视线与原点的距离,可以根据需要进行调整。300与前面的描述一致。在小场景下是比较合适的视角位置,也可以根据需要进行调整。最后,我们还需要处理点击时小方块颜色变化的问题(这个也可以不是问题,看需求),可以在点击事件监听的最后做如下设置:1constsm=graph3dView2.dm().getSelectionModel();2sm.setSelection(null);点击魔方各边演示效果:总结直观的方位指示广泛应用于室内定位、GIS、车站、机场等诸多场景。使用HT提供的2D和3D引擎可以轻松完成。Web3D有着无限的想象空间,非常丰富的数据呈现方式,还有很多吸引眼球的可视化效果,等待着我们在各个行业去实现这些数据呈现方式。HT在这方面做了很多探索和尝试,比如这个好玩的太阳系监控系统:https://www.hightopo.com/demo/solar-system/2019我们也更新了上百个工业互联网2D/3D可视化案例集锦,在这里你可以找到很多新奇的例子,也可以发现不一样的工业互联网:《分享数百个 HT 工业互联网 2D 3D 可视化应用案例之 2019 篇》。更多行业应用实例请参考官网案例链接:https://www.hightopo.com/demo...