目前我们的系统使用带有 ILNumerics 表面组件的 ILNumerics 3D 绘图立方体类来显示 3D 网格表面。我们系统的一个目标是能够通过鼠标点击绘图来询问表面上的各个点。我们在绘图上设置了 MouseClick 事件,问题是我不确定如何获取已单击表面上特定点的值,有人可以帮助解决这个问题吗?
1 回答
从 2D 鼠标坐标到 3D '模型' 坐标的转换是可能的 - 在一些限制下:
转换不是明确的。鼠标事件仅提供 2 个维度:X 和 Y 屏幕坐标。在 3D 模型中,这个 2D 屏幕点的“后面”可能不止一个点。因此,你能得到的最好的结果是计算一条 3D 线,从相机开始,以无限深度结束。
虽然理论上至少可以尝试找到与 3D 对象相交的线,但 ILNumerics 目前还没有。即使在表面的简单情况下,也很容易构建一个在多个点与线相交的 3D 模型。
对于简化的情况,存在一种解决方案:如果 3D 中的 Z 坐标无关紧要,可以使用通用矩阵转换来获取 3D 中的 X 和 Y 坐标并仅使用它们。假设您的绘图是二维线图或曲面图 - 但只能从“上方”观看(即未旋转的 XY 平面)。单击的点的 Z 坐标可能不重要。让我们进一步假设,您已经使用 ILPanel 在一个常见的 Windows 应用程序中设置了一个 ILScene 场景:
私人无效ilPanel1_Load(对象发送者,EventArgs e){
var scene = new ILScene() { new ILPlotCube(twoDMode: true) { new ILSurface(ILSpecialData.sincf(20,30)) } }; scene.First<ILSurface>().MouseClick += (s,arg) => { // we start at the mouse event target -> this will be the // surface group node (the parent of "Fill" and "Wireframe") var group = arg.Target.Parent; if (group != null) { // walk up to the next camera node Matrix4 trans = group.Transform; while (!(group is ILCamera) && group != null) { group = group.Parent; // collect all nodes on the path up trans = group.Transform * trans; } if (group != null && (group is ILCamera)) { // convert args.LocationF to world coords // The Z coord is not provided by the mouse! -> choose arbitrary value var pos = new Vector3(arg.LocationF.X * 2 - 1, arg.LocationF.Y * -2 + 1, 0); // invert the matrix. trans = Matrix4.Invert(trans); // trans now converts from the world coord system (at the camera) to // the local coord system in the 'target' group node (surface). // In order to transform the mouse (viewport) position, we // left multiply the transformation matrix. pos = trans * pos; // view result in the window title Text = "Model Position: " + pos.ToString(); } } }; ilPanel1.Scene = scene;
}
它的作用:它在表面组节点上注册一个 MouseClick 事件处理程序。在处理程序中,它在从单击的目标(表面组节点)到表面是其子节点的下一个相机节点的路径上累积变换矩阵。渲染时,顶点的(模型)坐标由托管在每个组节点中的局部坐标变换矩阵进行变换。所有的变换都是累积的,因此顶点坐标最终位于每个相机建立的“世界坐标”系统中。所以渲染从 3D 模型顶点位置找到 2D 屏幕位置。
为了从 2D 屏幕坐标中找到 3D 位置 - 必须反过来。在示例中,我们获取每个组节点的变换矩阵,将它们全部相乘并反转得到的变换矩阵。这是必需的,因为这样的转换自然地描述了从子节点到父节点的转换。在这里,我们需要相反的方式 - 因此反转是必要的。
此方法在鼠标位置提供正确的 3D 坐标。但是,请记住这些限制!在这里,我们不考虑绘图立方体的任何旋转(绘图立方体必须保持不旋转)和投影变换(绘图立方体默认使用正交变换,这基本上是一个noop)。为了也识别这些变量,您可以相应地扩展示例。