Three.js测量功能封装--距离、角度与面积
模型距离、角度、占地面积计算
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