Three.js测量功能封装--距离、角度与面积

2022-07-31,,,,

模型距离角度、占地面积计算

1.获取屏幕坐标位置,转化为三维坐标位置。代码如下:

var Sx = event.clientX;//屏幕X轴坐标
var Sy = event.clientY;//屏幕Y轴坐标
var x = ( Sx / window.innerWidth ) * 2 - 1;
var
y = -( Sy / window.innerHeight ) * 2 + 1;
var
standardVector  = new THREE.Vector3(x, y, 0.5);//新建一个三维单位向量 假设z方向就是0.5

       2.需要将得到的三维向量坐标转为视点坐标系(参照是摄像机),并且创建射线,方向是选定模型点与摄像机两点的连线。代码如下:

var ray = worldVector.sub(camera.position).normalize();//在视点坐标系中形成射线,射线的起点向量是照相机, 射线的方向向量是照相机到点击的点,这个向量应该归一标准化。
var raycaster = new THREE.Raycaster(camera.position, ray);//射线和模型求交,选中直线
var intersects = raycaster.intersectObjects(name,true);

       3.依靠前两步封装测量点击事件方法。需声明一全局变量measure_distance_point=new Array()(以下称为点变量)以便保存点历史位置信息与measure_distance_draw=new Array()(以下称为模型模型变量)。当用户点击模型时,判断点变量是否为空,若为空记录点击时的x\y\z保存至点变量中,并且生成一条线段,该线段两端都为该点击点,并将生成线段添加至模型变量与场景中。当点变量不为空时 ,在测量距离中代码类似,不赘述。代码如下:

if (intersects.length > 0) {
       
if (measure_distance_point.length===0){
            measure_distance_point.
push(intersects[0].point.x);
           
measure_distance_point.push(intersects[0].point.y);
           
measure_distance_point.push(intersects[0].point.z);
           
//prative_tool_point.push(name[0].children[0].geometry.boundingBox.max.z);
           
var material = new THREE.LineBasicMaterial({color:0xFF1493,depthTest: false});//depthTest检测深度
            material.transparent=true;
           
material.opacity=0.5;
           
material.linewidth=100;
            var
geometry = new THREE.Geometry();
           
geometry.vertices.push(new THREE.Vector3(intersects[0].point.x,intersects[0].point.y,intersects[0].point.z));
           
geometry.vertices.push(new THREE.Vector3(intersects[0].point.x,intersects[0].point.y,intersects[0].point.z));
            var
line=new THREE.Line(geometry,material);
           
measure_distance_draw.push(line);
           
scene.add(line);
       
}else
           
{
                measure_distance_point.
push(intersects[0].point.x);
               
measure_distance_point.push(intersects[0].point.y);
               
measure_distance_point.push(intersects[0].point.z);
                var
material = new THREE.LineBasicMaterial({color:0xFF1493,depthTest: false});
               
material.transparent=true;
               
material.opacity=0.9;
                var
geometry = new THREE.Geometry();
               
geometry.vertices.push(new THREE.Vector3(intersects[0].point.x,intersects[0].point.y,intersects[0].point.z));
               
geometry.vertices.push(new THREE.Vector3(intersects[0].point.x,intersects[0].point.y,intersects[0].point.z));
                var
line=new THREE.Line(geometry,material);
               
measure_distance_draw.push(line);
             

          
    }
}

 

       4.需要封装测量鼠标移动事件方法。将第三步中生成的线段尾端可以实现随着鼠标移动而变化,此时需要判断模型变量是否为空,不为空就获取最后一次添加的模型的尾端点,让其跟随鼠标移动。注:此时需要打开geometry的verticesNeedUpdate为true。封装代码如下:

function Measure_Moveing(name) {
   
var Sx = event.clientX;//屏幕X轴坐标
    var Sy = event.clientY;//屏幕Y轴坐标
    var x = ( Sx / window.innerWidth ) * 2 - 1;
    var
y = -( Sy / window.innerHeight ) * 2 + 1;
    var
standardVector  = new THREE.Vector3(x, y, 0.5);//新建一个三维单位向量 假设z方向就是0.5
   
var worldVector = standardVector.unproject(camera); //根据照相机,把这个向量转换到视点坐标系
    var ray = worldVector.sub(camera.position).normalize();//在视点坐标系中形成射线,射线的起点向量是照相机, 射线的方向向量是照相机到点击的点,这个向量应该归一标准化。
    var raycaster = new THREE.Raycaster(camera.position, ray);//射线和模型求交,选中直线
    var intersects = raycaster.intersectObjects(name,true);
    if
(intersects.length > 0) {
       
if( measure_distance_draw.length>0){
           
var draw_Num=measure_distance_draw.length-1;
            var
point_Num=measure_distance_draw[draw_Num].geometry.vertices.length-1;
            
measure_distance_draw[draw_Num].geometry.vertices[point_Num].x=intersects[0].point.x;
           
measure_distance_draw[draw_Num].geometry.vertices[point_Num].y=intersects[0].point.y;
           
measure_distance_draw[draw_Num].geometry.vertices[point_Num].z=intersects[0].point.z;
           
measure_distance_draw[draw_Num].geometry.verticesNeedUpdate=true;
       
}
    }
}

 

5.算距离。直接上代码:

Math.sqrt( Math.pow(measure_distance_draw[0].geometry.vertices[0].x - measure_distance_draw[0].geometry.vertices[1].x, 2) +
       
Math.pow(measure_distance_draw[0].geometry.vertices[0].y -measure_distance_draw[0].geometry.vertices[1].y, 2)+
       
Math.pow(measure_distance_draw[0].geometry.vertices[0].z -measure_distance_draw[0].geometry.vertices[1].z, 2)),

6.效果展示

  • 角度测量

1.与距离测量类似,只是计算的是角度。以下是计算角度的代码:

var lengthAB = Math.sqrt( Math.pow(measure_distance_draw[0].geometry.vertices[0].x - measure_distance_draw[0].geometry.vertices[1].x, 2) +
       
Math.pow(measure_distance_draw[0].geometry.vertices[0].y -measure_distance_draw[0].geometry.vertices[1].y, 2)+
       
Math.pow(measure_distance_draw[0].geometry.vertices[0].z -measure_distance_draw[0].geometry.vertices[1].z, 2)),
   
lengthAC = Math.sqrt( Math.pow(measure_distance_draw[1].geometry.vertices[0].x - measure_distance_draw[1].geometry.vertices[1].x, 2) +
       
Math.pow(measure_distance_draw[1].geometry.vertices[0].y -measure_distance_draw[1].geometry.vertices[1].y, 2)+
       
Math.pow(measure_distance_draw[1].geometry.vertices[0].z -measure_distance_draw[1].geometry.vertices[1].z, 2)),
   
lengthBC = Math.sqrt( Math.pow(measure_distance_draw[0].geometry.vertices[0].x - measure_distance_draw[1].geometry.vertices[1].x, 2) +
       
Math.pow(measure_distance_draw[0].geometry.vertices[0].y - measure_distance_draw[1].geometry.vertices[1].y, 2)+
       
Math.pow(measure_distance_draw[0].geometry.vertices[0].z - measure_distance_draw[1].geometry.vertices[1].z, 2));
var
cosA = (Math.pow(lengthAB, 2) + Math.pow(lengthAC, 2) - Math.pow(lengthBC, 2)) /
    (
2 * lengthAB * lengthAC);
var
angleA = Math.round( Math.acos(cosA) * 180 / Math.PI );

2.效果展示

  • 占地面积测量

1.需要实时显示我们在屏幕上画出的面积,这就要实时生成面,我采用的是三角面逐一生成的方式,主要采用几个for循环去实现它,第一个循环点变量,添加到THREE.Geometry()的vertices中,最后添加我们这次点击的点,第二个循环就是要算出三点一面的法线位置了,并且基于这些点的THREE.Face3面,也将其添加到geometry的faces中,至此geometry的工作完成了。生成网格Mesh,添加到场景中即可。(鼠标移动封装方法相同)代码如下:

if ( measure_distance_point.length>0){
    scene.
remove(measure_distance_draw[0]);
   
measure_distance_point.push(intersects[0].point.x);
   
measure_distance_point.push(intersects[0].point.y);
   
measure_distance_point.push(intersects[0].point.z);
    var
material = new THREE.LineBasicMaterial({color:0xFF1493, side:THREE.DoubleSide, depthTest: false});//深度测试关闭
    material.transparent=true;
   
material.opacity=0.5;
    var
geometry = new THREE.Geometry();
   
console.log( measure_distance_point);
    for
(var i=0;i<measure_distance_point.length/3;i++){
        geometry.
vertices.push(new THREE.Vector3( measure_distance_point[3*i], measure_distance_point[3*i+1], measure_distance_point[3*i+2]));
   
}
    geometry.
vertices.push(new THREE.Vector3(intersects[0].point.x,intersects[0].point.y,intersects[0].point.z));
   
console.log(geometry.vertices);
    for
(var j=0;j<measure_distance_point.length/3-1;j++)
    {
       
var normal = Get_normal(geometry.vertices[0],geometry.vertices[j+1],geometry.vertices[j+2]);
        var
face = new THREE.Face3( 0, j+1, j+2, normal);
       
geometry.faces.push( face );
   
}
   
var mesh=new THREE.Mesh(geometry,material);
   
measure_distance_draw[0]=mesh;
   
scene.add(mesh);

}

function Get_normal(p1,p2,p3) {
   
var   a = ( (p2.y-p1.y)*(p3.z-p1.z)-(p2.z-p1.z)*(p3.y-p1.y) );
    var  
b = ( (p2.z-p1.z)*(p3.x-p1.x)-(p2.x-p1.x)*(p3.z-p1.z) );
    var   
c = ( (p2.x-p1.x)*(p3.y-p1.y)-(p2.y-p1.y)*(p3.x-p1.x) );

    return THREE.Vector3(a,b,c);

}

 

 

2.算做出来图形的占地面积了,以z轴向上,代码如下:

var area=0.0;
for
(var j=0;j<measure_distance_draw[0].geometry.vertices.length-2;j++)
{
   
var point_1=measure_distance_draw[0].geometry.vertices[0];
    var
point_2=measure_distance_draw[0].geometry.vertices[j+1];
    var
point_3=measure_distance_draw[0].geometry.vertices[j+2];
    
area+=0.5*Math.abs(point_2.x*point_3.y+point_1.x*point_2.y+point_3.x*point_1.y-point_3.x*point_2.y-point_2.x*point_1.y-point_1.x*point_3.y);
}

3.效果展示

 

本文地址:https://blog.csdn.net/weixin_40676050/article/details/107774649

《Three.js测量功能封装--距离、角度与面积.doc》

下载本文的Word格式文档,以方便收藏与打印。