计算一条三维线段与一个三角形所在的平面的交点
一、介绍
MapGIS Objects SDK : 是一款组件式地理信息开发平台,提供全空间数据存储、管理、显示、编辑、查询、分析、制图输出等二三维一体化核心 GIS 功能,提供 C++、.NET、Java、Python 等开发资源,接口简单易用,性能优越,具备跨平台开发能力。
本篇内容将引导您如何使用 MapGIS Objects SDK 实现如何在三维场景中根据一个三维点计算其到一条三维线段的垂足点。
二、开发环境
| 软件 | 版本 | 下载地址 | 说明 |
|---|---|---|---|
| MapGIS 10 x64 All In One SDK for Windows | 10.7 | 开发包下载地址 | MapGIS 提供的一款地理信息开发平台,包含 MapGIS Objects Java 面向 Java 开发环境的跨平台组件式 GIS 开发资源。 |
| MapGIS 开发授权 | \ | 开发授权下载地址 | MapGIS 针对开发者提供开发授权,下载开发包并安装后,还需要获取开发授权才能正常使用。 |
| IntelliJ IDEA | 2020.3 以上版本 | IDEA 下载地址 | 一款适用于 Java 专业开发的集成开发环境(IDE)。 |
| JDK | 1.8 | JDK 下载地址 | JDK 是 Java 语言的软件开发工具包,JDK 是整个 java 开发的核心,它包含了 JAVA 的运行环境(JVM+Java 系统类库)和 JAVA 工具。 |
三、几何原理

判断一条三维线段是否穿过某个三角形,可以通过先判断这条线段是否与三角形所在的平面是否相交,如果相交则计算其交点,然后再判断该交点是否在三角形的边界或者内部。计算线段与平面交点的算法在前面的文章中已经详细进行了说明,在这里不再赘述,而判断线段与平面的交点是否在三角形内,可以通过面积法来解决,面积法的主要方案如上图所示,如果线段与平面的交点 P 在三角形 ABC 内部或者三角形的边界上,需要满足大三角形的面积等于三个小三角形面积之和,即 S/∆ABC=S/∆PAB+S/∆PAC+S/∆PBC(S/∆ABC 表示三角形 ABC 的面积,S/∆PAB、S/∆PBC、S/∆PAC 类似),即 1=S/∆PAB/S/∆ABC+S/∆PAC/S/∆ABC+S/∆PBC/S/∆ABC,即可推导出=> α=S/∆PAB/S/∆ABC,β=S/∆PAC/S/∆ABC,γ=S/∆PBC/S/∆PAB,α+β+γ=1 。其中三角形的面积可根据向量的叉乘得到,α=|PA×PB|/2,β 和 γ 也可同样计算出,因此要确定一个点是否在三角形的边界或里面,只需要保证 α>0、β>0、γ>0,且 α+β+γ=1 即可。
四、算法实现
本篇以 MapGIS Objects Java 实现算法的基本思想,对于 MapGIS Objects Java 的开发入门在此不做赘述,详情可参考MapGIS Objects Java 的开发入门文档,api 文档参考 MapGIS Objects Java API。
1.实现向量叉乘
public Dot3D crossMultiVector(Dot3D dot1,Dot3D dot2){
Dot3D rDot = new Dot3D();
rDot.setX(dot1.getY() * dot2.getZ() - dot1.getZ() * dot2.getY());
rDot.setY(dot1.getZ() * dot2.getX() - dot1.getX() * dot2.getZ());
rDot.setZ(dot1.getX() * dot2.getY() - dot1.getY() * dot2.getX());
return rDot;
}
2.计算单位法向量
public Dot3D computerNormal(Dot3D dotA,Dot3D dotB,Dot3D dotC){
//向量AB
Dot3D ab = new Dot3D();
ab.setX(dotB.getX()-dotA.getX());
ab.setY(dotB.getY()-dotA.getY());
ab.setZ(dotB.getZ()-dotA.getZ());
//向量AC
Dot3D ac = new Dot3D();
ac.setX(dotC.getX()-dotA.getX());
ac.setY(dotC.getY()-dotA.getY());
ac.setZ(dotC.getZ()-dotA.getZ());
//向量叉乘计算法向量
Dot3D pNormal=crossMultiVector(ab,ac);
//计算法向量模长
double len = Math.sqrt(Math.pow(pNormal.getX(),2)+Math.pow(pNormal.getY(),2)+Math.pow(pNormal.getZ(),2));
//计算单位法向量
pNormal.setX(pNormal.getX()/len);
pNormal.setY(pNormal.getY()/len);
pNormal.setZ(pNormal.getZ()/len);
return pNormal;
}
3.计算点到平面的距离
//通过点法式构建平面方程计算点到平面距离
public double computerDistanceToPlane(Dot3D pNormal,Dot3D dotPlane,Dot3D dotP){
double nx = pNormal.getX();
double ny = pNormal.getY();
double nz = pNormal.getZ();
double len =nx*dotP.getX()+ny*dotP.getY()+nz*dotP.getZ()-(nx*dotPlane.getX()+ny*dotPlane.getY()+nz*dotPlane.getZ());
return len;
}
4.判断线段是否与平面相交
public boolean isIntersectWithPlane(Dot3D sDot,Dot3D eDot,Dot3D planeDot1,Dot3D planeDot2,Dot3D planeDot3){
//1、计算平面单位法向量
Dot3D pNormal = computerNormal(planeDot1,planeDot2,planeDot3);
//2、计算线段的两个端点到平面距离
double len1 = computerDistanceToPlane(pNormal,planeDot1,sDot);
double len2 = computerDistanceToPlane(pNormal,planeDot1,eDot);
//3、判断线段是否与平面相交
if(len1*len2>0){
return false;
}
else{
return true;
}
}
5.计算线段与平面的交点
public Dot3D ComputerIntersectDotInPlane(Dot3D sDot,Dot3D eDot,Dot3D planeDot1,Dot3D planeDot2,Dot3D planeDot3){
//1、计算平面单位法向量
Dot3D pNormal = computerNormal(planeDot1,planeDot2,planeDot3);
//2、计算线段的两个端点到平面距离
double len1 = computerDistanceToPlane(pNormal,planeDot1,sDot);
double len2 = computerDistanceToPlane(pNormal,planeDot1,eDot);
//3、线性插值计算交点
if(len1<1e-3){
return sDot;
}
if(len2<1e-3){
return eDot;
}
if(len1*len2<0){
double t = -len1 / (len2 - len1);
Dot3D pDot = new Dot3D();
pDot.setX(sDot.getX()+t*(eDot.getX()-sDot.getX()));
pDot.setY(sDot.getY()+t*(eDot.getY()-sDot.getY()));
pDot.setZ(sDot.getZ()+t*(eDot.getZ()-sDot.getZ()));
return pDot;
}
else{
return null;
}
}
6.判断交点是否在三角形内
public double ComputerTriangleArea(Dot3D dot1,Dot3D dot2,Dot3D dot3){
Dot3D ab = new Dot3D();
Dot3D ac = new Dot3D();
ab.setX(dot2.getX()-dot1.getX());
ab.setY(dot2.getY()-dot1.getY());
ab.setZ(dot2.getZ()-dot1.getZ());
ac.setX(dot3.getX()-dot1.getX());
ac.setY(dot3.getY()-dot1.getY());
ac.setZ(dot3.getZ()-dot1.getZ());
Dot3D rDot = crossMultiVector(ab,ac);
double len = Math.sqrt(rDot.getX()*rDot.getX()+rDot.getY()*rDot.getY()+rDot.getZ()*rDot.getZ());
return len/2.0;
}
public boolean isInTriangle(Dot3D dotA,Dot3D dotB,Dot3D dotC,Dot3D dotP){
//1.计算三角形ABC的面积
double Sabc = ComputerTriangleArea(dotA,dotB,dotC);
//2.计算三角形PAB的面积
double Spab = ComputerTriangleArea(dotP,dotA,dotB);
//3.计算三角形PBC的面积
double Spbc = ComputerTriangleArea(dotP,dotB,dotC);
//4.计算三角形PAC的面积
double Spac = ComputerTriangleArea(dotP,dotA,dotC);
double α = Spab/Sabc;
double β = Spbc/Sabc;
double γ = Spac/Sabc;
return α>=0 && β>=0 && γ>=0 && Math.abs(α+β+γ-1)<1e-3;
}
ab.setZ(dot2.getZ()-dot1.getZ());
ac.setX(dot3.getX()-dot1.getX());
ac.setY(dot3.getY()-dot1.getY());
ac.setZ(dot3.getZ()-dot1.getZ());
Dot3D rDot = crossMul
708

被折叠的 条评论
为什么被折叠?



