在二维图中,判断鼠标是否选中线段,是根据鼠标点的位置与线段的距离,如果和线段的距离小于阈值,那么就认为鼠标已经选中的线段。
计算过程如下:
- 第一步,计算鼠标点到线段所在的直线的距离。
- 第二步,计算鼠标点到线段两个端点的距离。
- 选择上面计算出的三个值,如果存在小于阈值的,那么就认为鼠标选择了线段。
类似的,在三维中,判断鼠标是否选中线段也是类似的方法,但是计算更加复杂。首先是不存在鼠标点的在空间的位置,鼠标点是二维的。其次,二维图本身就是一个平面系统,而在三维计算中需要进行投影,才能将鼠标点的信息转换为空间信息。
具体计算过程如下:
第一步,将鼠标点的信息进行投影,把鼠标模拟成一个光源,起点是鼠标点在平面的位置,光源的方向是三维
视锥体的顶点到鼠标点的射线。
Vector3 nearVector = new Vector3(LastMousePosition.X, LastMousePosition.Y, Camera.Viewport.MinZ);
Vector3 farVector = new Vector3(LastMousePosition.X, LastMousePosition.Y, Camera.Viewport.MaxZ);
nearVector.Unproject(device.Viewport, device.Transform.Projection, device.Transform.View, renderWorldMatrix);
farVector.Unproject(device.Viewport, device.Transform.Projection, device.Transform.View, renderWorldMatrix);
farVector.Subtract(nearVector);
上述代码中,
renderWorldMatrix是线段在场景中的Transform.World值,如果没有Transform那么就用Matrix.Identity表示。而
Camera.Viewport.MinZ和Camera.Viewport.MaxZ是三维视锥体的前后裁剪面。最后鼠标表示的射线存放在
farVector中。
第二步,求的射线与线段之间的距离。关于空间中两条直线的空间距离,就是计算直线到一个平行平面的距离,而这个平面的法向是垂直于两条直线,平面所在的位置在一条直线上。
第三步,求的射线与线段端点之间的距离
第四步,将上面求得的值与阈值判断,如果存在小于阈值,那么就说明鼠标选中了线段。
Vector3 v1 = transformedLineVertex[i];
Vector3 v2 = transformedLineVertex[i + 1];
Vector3 v = v2 - v1;
float a11 = Vector3.Dot(v, farVector);
float a21 = v.LengthSq();
float a22 = -a11;
float d1 = Vector3.Dot((nearVector - v1), farVector);
float d2 = Vector3.Dot((nearVector - v1), v);
float x = (d1 * a22 - d2 * a12) / (a11 * a22 - a21 * a12);
float y = (d1 * a21 - d2 * a11) / (a12 * a21 - a22 * a11);
//求得射线中与线段A最近的点
Vector3 p1 = new Vector3(nearVector.X + y * farVector.X,
nearVector.Y + y * farVector.Y,
nearVector.Z + y * farVector.Z);
//线段A与射线最近的点
Vector3 p2 = new Vector3(v1.X + x * v.X,
v1.Y + x * v.Y,
v1.Z + x * v.Z);
//下面是判断是否是否与端点的位置最近
Vector3 disLine;
if (x <= 0)
{
disLine = p2 - v1;
}
else if (x >= 1)
{
disLine = p2 - v2;
}
else
{
disLine = p2 - p1;
}
<完>